feat: comment reactions and posting subcomment

This commit is contained in:
DIYgod 2022-10-22 01:57:17 +01:00
parent cc071ceafd
commit 42611ef635
No known key found for this signature in database
GPG Key ID: D159328F47A80DCA
6 changed files with 557 additions and 469 deletions

View File

@ -1,73 +1,17 @@
import clsx from "clsx"
import { Note, Profile } from "~/lib/types"
import { useAccount } from "wagmi"
import { useGetUserSites } from "~/queries/site"
import { Avatar } from "~/components/ui/Avatar"
import { Input } from "~/components/ui/Input"
import { Button } from "~/components/ui/Button"
import { useForm } from "react-hook-form"
import { useConnectModal } from "@rainbow-me/rainbowkit"
import { useState, useEffect } from "react"
import { useCommentPage, useGetComments } from "~/queries/page"
import { useRouter } from "next/router"
// @ts-ignore
import Picker from "@emoji-mart/react"
import { Popover } from "@headlessui/react"
import { FaceSmileIcon } from "@heroicons/react/24/outline"
import { Note } from "~/lib/types"
import { useGetComments } from "~/queries/page"
import { CommentItem } from "~/components/common/CommentItem"
import { CommentInput } from "~/components/common/CommentInput"
export const Comment: React.FC<{
page?: Note | null
className?: string
}> = ({ page, className }) => {
const { address } = useAccount()
const userSites = useGetUserSites(address)
const { openConnectModal } = useConnectModal()
const commentPage = useCommentPage()
const router = useRouter()
const [viewer, setViewer] = useState<Profile | null>(null)
const [addressIn, setAddressIn] = useState("")
const comments = useGetComments({
pageId: page?.id,
})
useEffect(() => {
setAddressIn(address || "")
}, [address])
useEffect(() => {
if (userSites.isSuccess && userSites.data?.length) {
setViewer(userSites.data[0])
}
}, [userSites, router])
const form = useForm({
defaultValues: {
content: "",
},
})
const handleSubmit = form.handleSubmit(async (values) => {
if (!address) {
openConnectModal?.()
} else if (userSites.isSuccess && !userSites.data?.length) {
router.push(`/dashboard/new-site`)
} else {
commentPage.mutate({
address,
pageId: page!.id,
content: values.content,
externalUrl: window.location.href,
})
}
})
useEffect(() => {
if (commentPage.isSuccess) {
form.reset()
}
}, [commentPage.isSuccess, form])
return (
<div className={clsx("xlog-comment", "comment", className)}>
<div className="xlog-comment-count border-b pb-2 mb-6">
@ -76,73 +20,14 @@ export const Comment: React.FC<{
{comments.data?.count && comments.data.count > 1 ? "s" : ""}
</span>
</div>
<div className="xlog-comment-input flex">
<Avatar
className="align-middle mr-3"
images={viewer?.avatars || []}
name={viewer?.name}
size={45}
/>
<form className="w-full" onSubmit={handleSubmit}>
<div>
<Input
id="content"
isBlock
required={
!!addressIn && userSites.isSuccess && !!userSites.data?.length
}
disabled={
!addressIn || !userSites.isSuccess || !userSites.data?.length
}
multiline
maxLength={300}
className="mb-2"
placeholder="Write a comment on the blockchain"
{...form.register("content", {})}
/>
</div>
<div className="flex justify-between">
<Popover className="relative flex justify-center">
{({ open }: { open: boolean }) => (
<>
<Popover.Button className="group inline-flex items-center rounded-md px-2 text-xl">
<FaceSmileIcon className="w-6 h-6 text-zinc-400 hover:text-zinc-500" />
</Popover.Button>
<Popover.Panel className="absolute left-0 top-full z-10">
<Picker
data={async () => {
const response = await fetch(
"https://cdn.jsdelivr.net/npm/@emoji-mart/data",
)
return response.json()
}}
onEmojiSelect={(e: any) =>
form.setValue(
"content",
form.getValues("content") + e.native,
)
}
/>
</Popover.Panel>
</>
)}
</Popover>
<Button
type="submit"
isLoading={userSites.isLoading || commentPage.isLoading}
>
{addressIn
? userSites.isSuccess && !userSites.data?.length
? "Create Character"
: "Submit"
: "Connect"}
</Button>
</div>
</form>
</div>
<CommentInput pageId={page?.id} />
<div className="xlog-comment-list space-y-6 pt-6">
{comments.data?.list?.map((comment) => (
<CommentItem comment={comment} key={comment.transactionHash} />
<CommentItem
originalId={page?.id}
comment={comment}
key={comment.transactionHash}
/>
))}
</div>
</div>

View File

@ -0,0 +1,134 @@
import clsx from "clsx"
import { Note, Profile } from "~/lib/types"
import { useAccount } from "wagmi"
import { useGetUserSites } from "~/queries/site"
import { Avatar } from "~/components/ui/Avatar"
import { Input } from "~/components/ui/Input"
import { Button } from "~/components/ui/Button"
import { useForm } from "react-hook-form"
import { useConnectModal } from "@rainbow-me/rainbowkit"
import { useState, useEffect } from "react"
import { useCommentPage } from "~/queries/page"
import { useRouter } from "next/router"
// @ts-ignore
import Picker from "@emoji-mart/react"
import { Popover } from "@headlessui/react"
import { FaceSmileIcon } from "@heroicons/react/24/outline"
export const CommentInput: React.FC<{
pageId?: string
originalId?: string
}> = ({ pageId, originalId }) => {
const { address } = useAccount()
const userSites = useGetUserSites(address)
const { openConnectModal } = useConnectModal()
const commentPage = useCommentPage()
const router = useRouter()
const [viewer, setViewer] = useState<Profile | null>(null)
const [addressIn, setAddressIn] = useState("")
useEffect(() => {
setAddressIn(address || "")
}, [address])
useEffect(() => {
if (userSites.isSuccess && userSites.data?.length) {
setViewer(userSites.data[0])
}
}, [userSites, router])
const form = useForm({
defaultValues: {
content: "",
},
})
const handleSubmit = form.handleSubmit(async (values) => {
if (!address) {
openConnectModal?.()
} else if (userSites.isSuccess && !userSites.data?.length) {
router.push(`/dashboard/new-site`)
} else if (pageId) {
commentPage.mutate({
address,
pageId: pageId,
content: values.content,
externalUrl: window.location.href,
originalId: originalId,
})
}
})
useEffect(() => {
if (commentPage.isSuccess) {
form.reset()
}
}, [commentPage.isSuccess, form])
return (
<div className="xlog-comment-input flex">
<Avatar
className="align-middle mr-3"
images={viewer?.avatars || []}
name={viewer?.name}
size={45}
/>
<form className="w-full" onSubmit={handleSubmit}>
<div>
<Input
id="content"
isBlock
required={
!!addressIn && userSites.isSuccess && !!userSites.data?.length
}
disabled={
!addressIn || !userSites.isSuccess || !userSites.data?.length
}
multiline
maxLength={300}
className="mb-2"
placeholder="Write a comment on the blockchain"
{...form.register("content", {})}
/>
</div>
<div className="flex justify-between">
<Popover className="relative flex justify-center">
{({ open }: { open: boolean }) => (
<>
<Popover.Button className="group inline-flex items-center rounded-md px-2 text-xl">
<FaceSmileIcon className="w-6 h-6 text-zinc-400 hover:text-zinc-500" />
</Popover.Button>
<Popover.Panel className="absolute left-0 top-full z-10">
<Picker
data={async () => {
const response = await fetch(
"https://cdn.jsdelivr.net/npm/@emoji-mart/data",
)
return response.json()
}}
onEmojiSelect={(e: any) =>
form.setValue(
"content",
form.getValues("content") + e.native,
)
}
/>
</Popover.Panel>
</>
)}
</Popover>
<Button
type="submit"
isLoading={userSites.isLoading || commentPage.isLoading}
>
{addressIn
? userSites.isSuccess && !userSites.data?.length
? "Create Character"
: "Submit"
: "Connect"}
</Button>
</div>
</form>
</div>
)
}

View File

@ -8,6 +8,10 @@ import { CSB_SCAN } from "~/lib/env"
import { getSiteLink } from "~/lib/helpers"
import { PageContent } from "~/components/common/PageContent"
import { NoteEntity, CharacterEntity } from "crossbell.js"
import { Button } from "~/components/ui/Button"
import { Reactions } from "~/components/common/Reactions"
import { useState } from "react"
import { CommentInput } from "~/components/common/CommentInput"
dayjs.extend(duration)
dayjs.extend(relativeTime)
@ -17,13 +21,16 @@ export const CommentItem: React.FC<{
character?: CharacterEntity | null
}
isSub?: boolean
}> = ({ comment, isSub }) => {
originalId?: string
}> = ({ comment, isSub, originalId }) => {
const [replyOpen, setReplyOpen] = useState(false)
return (
<div
key={comment.transactionHash}
className={isSub ? "" : "border-b border-dashed pb-6"}
>
<div className="flex">
<div className="flex group">
<UniLink
href={
comment?.character?.handle &&
@ -67,10 +74,32 @@ export const CommentItem: React.FC<{
<PageContent
content={comment.metadata?.content?.content}
></PageContent>
<div className="mt-1 flex items-center">
<Reactions
className="inline-flex"
size="sm"
pageId={`${comment.characterId}-${comment.noteId}`}
/>
<Button
className="text-gray-500 text-[13px] ml-2 mt-[-1px]"
variant="text"
onClick={() => setReplyOpen(!replyOpen)}
>
{replyOpen ? "Cancel" : "Reply"}
</Button>
</div>
{replyOpen && (
<div className="pt-6">
<CommentInput
originalId={originalId}
pageId={`${comment.characterId}-${comment.noteId}`}
/>
</div>
)}
</div>
</div>
{(comment as any)?.fromNotes?.list?.length > 0 && (
<div className="pl-11 space-y-6 pt-6">
<div className="pl-[57px] space-y-6 pt-6">
{(comment as any)?.fromNotes?.list?.map(
(
subcomment: NoteEntity & {

View File

@ -0,0 +1,371 @@
import { LikeIcon } from "~/components/icons/LikeIcon"
import {
useLikePage,
useUnlikePage,
useMintPage,
useGetLikes,
useCheckLike,
useGetMints,
useCheckMint,
} from "~/queries/page"
import { useGetUserSites } from "~/queries/site"
import { useAccount } from "wagmi"
import { useConnectModal } from "@rainbow-me/rainbowkit"
import { useState, useEffect } from "react"
import { Button } from "../ui/Button"
import { useRouter } from "next/router"
import { SITE_URL, CSB_SCAN } from "~/lib/env"
import { UniLink } from "~/components/ui/UniLink"
import { Modal } from "~/components/ui/Modal"
import { Avatar } from "../ui/Avatar"
import { MintIcon } from "~/components/icons/MintIcon"
import { getLikes, getMints } from "~/models/page.model"
import { CharacterList } from "~/components/common/CharacterList"
import clsx from "clsx"
export const Reactions: React.FC<{
className?: string
size?: "sm" | "base"
pageId?: string
}> = ({ className, size, pageId }) => {
const likePage = useLikePage()
const unlikePage = useUnlikePage()
const mintPage = useMintPage()
const { address } = useAccount()
const { openConnectModal } = useConnectModal()
const [likeProgress, setLikeProgress] = useState(false)
const [mintProgress, setMintProgress] = useState(false)
const userSite = useGetUserSites(address)
const router = useRouter()
let [isLikeOpen, setIsLikeOpen] = useState(false)
let [isMintOpen, setIsMintOpen] = useState(false)
let [isLikeListOpen, setIsLikeListOpen] = useState(false)
let [isMintListOpen, setIsMintListOpen] = useState(false)
const likes = useGetLikes({
pageId: pageId,
})
const isLike = useCheckLike({
address,
pageId: pageId,
})
const mints = useGetMints({
pageId: pageId,
})
const isMint = useCheckMint({
address,
pageId: pageId,
})
const like = async () => {
if (!address) {
setLikeProgress(true)
openConnectModal?.()
} else if (!userSite.data) {
router.push(`${SITE_URL}/dashboard/new-site`)
} else if (pageId) {
if (isLike.data?.count) {
setIsLikeOpen(true)
} else {
likePage.mutate({
address,
pageId: pageId,
})
}
}
}
const mint = async () => {
if (!address) {
setMintProgress(true)
openConnectModal?.()
} else if (!userSite.data) {
router.push(`${SITE_URL}/dashboard/new-site`)
} else if (pageId) {
if (isMint.data?.count) {
setIsMintOpen(true)
} else {
mintPage.mutate({
address,
pageId: pageId,
})
}
}
}
useEffect(() => {
if (
mintProgress &&
address &&
isMint.isSuccess &&
pageId &&
userSite.isSuccess
) {
if (!userSite.data) {
router.push(`${SITE_URL}/dashboard/new-site`)
}
if (!isMint.data.count) {
mintPage.mutate({
address,
pageId: pageId,
})
}
setMintProgress(false)
}
}, [
userSite.isSuccess,
userSite.data,
router,
mintProgress,
address,
isMint.isSuccess,
isMint.data?.count,
mintPage,
pageId,
])
useEffect(() => {
if (
likeProgress &&
address &&
isLike.isSuccess &&
pageId &&
userSite.isSuccess
) {
if (!userSite.data) {
router.push(`${SITE_URL}/dashboard/new-site`)
}
if (!isLike.data.count) {
likePage.mutate({
address,
pageId: pageId,
})
}
setLikeProgress(false)
}
}, [
userSite.isSuccess,
userSite.data,
router,
likeProgress,
address,
isLike.isSuccess,
isLike.data?.count,
likePage,
pageId,
])
const [likeList, setLikeList] = useState<any[]>([])
const [likeCursor, setLikeCursor] = useState<string>()
useEffect(() => {
if (likes.isSuccess && likes.data) {
setLikeList(likes.data.list || [])
setLikeCursor(likes.data.cursor || "")
}
}, [likes.isSuccess, likes.data])
const loadMoreLikes = async () => {
if (likeCursor && pageId) {
const subs = await getLikes({
pageId: pageId,
cursor: likeCursor,
})
setLikeList((prev) => [...prev, ...(subs?.list || [])])
setLikeCursor(subs?.cursor || "")
}
}
const [mintList, setMintList] = useState<any[]>([])
const [mintCursor, setMintCursor] = useState<string>()
useEffect(() => {
if (mints.isSuccess && mints.data) {
setMintList(mints.data.list || [])
setMintCursor(mints.data.cursor || "")
}
}, [mints.isSuccess, mints.data])
const loadMoreMints = async () => {
if (mintCursor && pageId) {
const subs = await getMints({
pageId: pageId,
cursor: mintCursor,
})
setMintList((prev) => [...prev, ...(subs?.list || [])])
setMintCursor(subs?.cursor || "")
}
}
return (
<>
<div
className={clsx(
"xlog-reactions flex fill-gray-400 text-gray-500 items-center",
size === "sm" ? "text-sm" : "",
className,
)}
>
<div
className={clsx(
"xlog-reactions-like flex items-center",
size === "sm" ? "mr-3" : "mr-10",
)}
>
<Button
variant="like"
className={`flex items-center mr-2 ${
isLike.isSuccess && isLike.data.count && "active"
}`}
isAutoWidth={true}
onClick={like}
isLoading={
userSite.isLoading ||
likePage.isLoading ||
unlikePage.isLoading ||
likeProgress
}
>
<LikeIcon
className={"mr-1 " + (size === "sm" ? "w-4 h-4" : "w-10 h-10")}
/>
<span>{likes.data?.count || 0}</span>
</Button>
{size !== "sm" && (
<ul
className="-space-x-4 cursor-pointer"
onClick={() => setIsLikeListOpen(true)}
>
{likes.data?.list
?.sort((a, b) =>
b.fromCharacter?.metadata?.content?.avatars ? 1 : -1,
)
.slice(0, 3)
?.map((like: any, index) => (
<li className="inline-block" key={index}>
<Avatar
className="relative align-middle border-2 border-white"
images={
like.fromCharacter?.metadata?.content?.avatars || []
}
name={like.fromCharacter?.metadata?.content?.name}
size={40}
/>
</li>
))}
{(likes.data?.count || 0) > 3 && (
<li className="inline-block">
<div className="relative align-middle border-2 border-white w-10 h-10 rounded-full inline-flex bg-gray-100 items-center justify-center text-gray-400 font-medium">
+{likes.data!.count - 3}
</div>
</li>
)}
</ul>
)}
</div>
<div className="xlog-reactions-mint flex items-center">
<Button
variant="collect"
className={`flex items-center mr-2 ${
isMint.isSuccess && isMint.data.count && "active"
}`}
isAutoWidth={true}
onClick={mint}
isLoading={mintPage.isLoading || likeProgress}
>
<MintIcon
className={clsx("mr-2", size === "sm" ? "w-3 h-3" : "w-8 h-8")}
/>
<span>{mints.data?.count || 0}</span>
</Button>
{size !== "sm" && (
<ul
className="-space-x-4 cursor-pointer"
onClick={() => setIsMintListOpen(true)}
>
{mints.data?.list
?.sort((a, b: any) =>
b.character?.metadata?.content?.avatars ? 1 : -1,
)
.slice(0, 3)
.map((mint: any, index) => (
<li className="inline-block" key={index}>
<Avatar
className="relative align-middle border-2 border-white"
images={mint.character?.metadata?.content?.avatars || []}
name={mint.character?.metadata?.content?.name}
size={40}
/>
</li>
))}
{(mints.data?.count || 0) > 3 && (
<li className="inline-block">
<div className="relative align-middle border-2 border-white w-10 h-10 rounded-full inline-flex bg-gray-100 items-center justify-center text-gray-400 font-medium">
+{mints.data!.count - 3}
</div>
</li>
)}
</ul>
)}
</div>
<Modal
open={isLikeOpen}
setOpen={setIsLikeOpen}
title="Like successfull"
>
<div className="p-5">
Your like has been permanently stored on the blockchain, view them{" "}
<UniLink
className="text-accent"
href={`${CSB_SCAN}/tx/${isLike.data?.list?.[0]?.transactionHash}`}
>
here
</UniLink>
</div>
<div className="h-16 border-t flex items-center px-5">
<Button isBlock onClick={() => setIsLikeOpen(false)}>
Got it, thanks!
</Button>
</div>
</Modal>
<Modal
open={isMintOpen}
setOpen={setIsMintOpen}
title="Mint successfull"
>
<div className="p-5">
Your minting has been permanently stored on the blockchain, view
them{" "}
<UniLink
className="text-accent"
href={`${CSB_SCAN}/tx/${isMint.data?.list?.[0]?.transactionHash}`}
>
here
</UniLink>
</div>
<div className="h-16 border-t flex items-center px-5">
<Button isBlock onClick={() => setIsMintOpen(false)}>
Got it, thanks!
</Button>
</div>
</Modal>
<CharacterList
open={isLikeListOpen}
setOpen={setIsLikeListOpen}
title="Like List"
loadMore={loadMoreLikes}
hasMore={!!likeCursor}
list={likeList}
></CharacterList>
<CharacterList
open={isMintListOpen}
setOpen={setIsMintListOpen}
title="Mint List"
loadMore={loadMoreMints}
hasMore={!!mintCursor}
list={mintList}
></CharacterList>
</div>
</>
)
}

View File

@ -1,351 +1,13 @@
import { Note } from "~/lib/types"
import { LikeIcon } from "~/components/icons/LikeIcon"
import {
useLikePage,
useUnlikePage,
useMintPage,
useGetLikes,
useCheckLike,
useGetMints,
useCheckMint,
} from "~/queries/page"
import { useGetUserSites } from "~/queries/site"
import { useAccount } from "wagmi"
import { useConnectModal } from "@rainbow-me/rainbowkit"
import { useState, useEffect } from "react"
import { Button } from "../ui/Button"
import { useRouter } from "next/router"
import { SITE_URL, CSB_SCAN } from "~/lib/env"
import { Comment } from "~/components/common/Comment"
import { UniLink } from "~/components/ui/UniLink"
import { Modal } from "~/components/ui/Modal"
import { Avatar } from "../ui/Avatar"
import { MintIcon } from "~/components/icons/MintIcon"
import { getLikes, getMints } from "~/models/page.model"
import { CharacterList } from "~/components/common/CharacterList"
import { Reactions } from "~/components/common/Reactions"
export const PostFooter: React.FC<{
page?: Note | null
}> = ({ page }) => {
const likePage = useLikePage()
const unlikePage = useUnlikePage()
const mintPage = useMintPage()
const { address } = useAccount()
const { openConnectModal } = useConnectModal()
const [likeProgress, setLikeProgress] = useState(false)
const [mintProgress, setMintProgress] = useState(false)
const userSite = useGetUserSites(address)
const router = useRouter()
let [isLikeOpen, setIsLikeOpen] = useState(false)
let [isMintOpen, setIsMintOpen] = useState(false)
let [isLikeListOpen, setIsLikeListOpen] = useState(false)
let [isMintListOpen, setIsMintListOpen] = useState(false)
const likes = useGetLikes({
pageId: page?.id,
})
const isLike = useCheckLike({
address,
pageId: page?.id,
})
const mints = useGetMints({
pageId: page?.id,
})
const isMint = useCheckMint({
address,
pageId: page?.id,
})
const like = async () => {
if (!address) {
setLikeProgress(true)
openConnectModal?.()
} else if (!userSite.data) {
router.push(`${SITE_URL}/dashboard/new-site`)
} else if (page?.id) {
if (isLike.data?.count) {
setIsLikeOpen(true)
} else {
likePage.mutate({
address,
pageId: page?.id,
})
}
}
}
const mint = async () => {
if (!address) {
setMintProgress(true)
openConnectModal?.()
} else if (!userSite.data) {
router.push(`${SITE_URL}/dashboard/new-site`)
} else if (page?.id) {
if (isMint.data?.count) {
setIsMintOpen(true)
} else {
mintPage.mutate({
address,
pageId: page?.id,
})
}
}
}
useEffect(() => {
if (
mintProgress &&
address &&
isMint.isSuccess &&
page?.id &&
userSite.isSuccess
) {
if (!userSite.data) {
router.push(`${SITE_URL}/dashboard/new-site`)
}
if (!isMint.data.count) {
mintPage.mutate({
address,
pageId: page?.id,
})
}
setMintProgress(false)
}
}, [
userSite.isSuccess,
userSite.data,
router,
mintProgress,
address,
isMint.isSuccess,
isMint.data?.count,
mintPage,
page?.id,
])
useEffect(() => {
if (
likeProgress &&
address &&
isLike.isSuccess &&
page?.id &&
userSite.isSuccess
) {
if (!userSite.data) {
router.push(`${SITE_URL}/dashboard/new-site`)
}
if (!isLike.data.count) {
likePage.mutate({
address,
pageId: page?.id,
})
}
setLikeProgress(false)
}
}, [
userSite.isSuccess,
userSite.data,
router,
likeProgress,
address,
isLike.isSuccess,
isLike.data?.count,
likePage,
page?.id,
])
const [likeList, setLikeList] = useState<any[]>([])
const [likeCursor, setLikeCursor] = useState<string>()
useEffect(() => {
if (likes.isSuccess && likes.data) {
setLikeList(likes.data.list || [])
setLikeCursor(likes.data.cursor || "")
}
}, [likes.isSuccess, likes.data])
const loadMoreLikes = async () => {
if (likeCursor && page?.id) {
const subs = await getLikes({
pageId: page.id,
cursor: likeCursor,
})
setLikeList((prev) => [...prev, ...(subs?.list || [])])
setLikeCursor(subs?.cursor || "")
}
}
const [mintList, setMintList] = useState<any[]>([])
const [mintCursor, setMintCursor] = useState<string>()
useEffect(() => {
if (mints.isSuccess && mints.data) {
setMintList(mints.data.list || [])
setMintCursor(mints.data.cursor || "")
}
}, [mints.isSuccess, mints.data])
const loadMoreMints = async () => {
if (mintCursor && page?.id) {
const subs = await getMints({
pageId: page.id,
cursor: mintCursor,
})
setMintList((prev) => [...prev, ...(subs?.list || [])])
setMintCursor(subs?.cursor || "")
}
}
return (
<>
<div className="xlog-post-footer flex fill-gray-400 text-gray-500 mt-14 mb-12 items-center">
<div className="xlog-post-like flex items-center">
<Button
variant="like"
className={`flex items-center mr-2 ${
isLike.isSuccess && isLike.data.count && "active"
}`}
isAutoWidth={true}
onClick={like}
isLoading={
userSite.isLoading ||
likePage.isLoading ||
unlikePage.isLoading ||
likeProgress
}
>
<LikeIcon className="mr-1 w-10 h-10" />
<span>{likes.data?.count || 0}</span>
</Button>
<ul
className="-space-x-4 mr-10 cursor-pointer"
onClick={() => setIsLikeListOpen(true)}
>
{likes.data?.list
?.sort((a, b) =>
b.fromCharacter?.metadata?.content?.avatars ? 1 : -1,
)
.slice(0, 3)
?.map((like: any, index) => (
<li className="inline-block" key={index}>
<Avatar
className="relative align-middle border-2 border-white"
images={
like.fromCharacter?.metadata?.content?.avatars || []
}
name={like.fromCharacter?.metadata?.content?.name}
size={40}
/>
</li>
))}
{(likes.data?.count || 0) > 3 && (
<li className="inline-block">
<div className="relative align-middle border-2 border-white w-10 h-10 rounded-full inline-flex bg-gray-100 items-center justify-center text-gray-400 font-medium">
+{likes.data!.count - 3}
</div>
</li>
)}
</ul>
</div>
<div className="xlog-post-mint flex items-center">
<Button
variant="collect"
className={`flex items-center mr-2 ${
isMint.isSuccess && isMint.data.count && "active"
}`}
isAutoWidth={true}
onClick={mint}
isLoading={mintPage.isLoading || likeProgress}
>
<MintIcon className="mr-2 w-8 h-8" />
<span>{mints.data?.count || 0}</span>
</Button>
<ul
className="-space-x-4 cursor-pointer"
onClick={() => setIsMintListOpen(true)}
>
{mints.data?.list
?.sort((a, b: any) =>
b.character?.metadata?.content?.avatars ? 1 : -1,
)
.slice(0, 3)
.map((mint: any, index) => (
<li className="inline-block" key={index}>
<Avatar
className="relative align-middle border-2 border-white"
images={mint.character?.metadata?.content?.avatars || []}
name={mint.character?.metadata?.content?.name}
size={40}
/>
</li>
))}
{(mints.data?.count || 0) > 3 && (
<li className="inline-block">
<div className="relative align-middle border-2 border-white w-10 h-10 rounded-full inline-flex bg-gray-100 items-center justify-center text-gray-400 font-medium">
+{mints.data!.count - 3}
</div>
</li>
)}
</ul>
</div>
<Modal
open={isLikeOpen}
setOpen={setIsLikeOpen}
title="Like successfull"
>
<div className="p-5">
Your like has been permanently stored on the blockchain, view them{" "}
<UniLink
className="text-accent"
href={`${CSB_SCAN}/tx/${isLike.data?.list?.[0]?.transactionHash}`}
>
here
</UniLink>
</div>
<div className="h-16 border-t flex items-center px-5">
<Button isBlock onClick={() => setIsLikeOpen(false)}>
Got it, thanks!
</Button>
</div>
</Modal>
<Modal
open={isMintOpen}
setOpen={setIsMintOpen}
title="Mint successfull"
>
<div className="p-5">
Your minting has been permanently stored on the blockchain, view
them{" "}
<UniLink
className="text-accent"
href={`${CSB_SCAN}/tx/${isMint.data?.list?.[0]?.transactionHash}`}
>
here
</UniLink>
</div>
<div className="h-16 border-t flex items-center px-5">
<Button isBlock onClick={() => setIsMintOpen(false)}>
Got it, thanks!
</Button>
</div>
</Modal>
<CharacterList
open={isLikeListOpen}
setOpen={setIsLikeListOpen}
title="Like List"
loadMore={loadMoreLikes}
hasMore={!!likeCursor}
list={likeList}
></CharacterList>
<CharacterList
open={isMintListOpen}
setOpen={setIsMintListOpen}
title="Mint List"
loadMore={loadMoreMints}
hasMore={!!mintCursor}
list={mintList}
></CharacterList>
</div>
<Reactions className="mt-14 mb-12" pageId={page?.id} />
<Comment page={page} />
</>
)

View File

@ -179,12 +179,19 @@ export function useCommentPage() {
const contract = useContract()
const queryClient = useQueryClient()
return useMutation(
async (input: Parameters<typeof pageModel.commentPage>[0]) => {
async (
input: Parameters<typeof pageModel.commentPage>[0] & {
originalId?: string
},
) => {
return pageModel.commentPage(input, contract)
},
{
onSuccess: (data, variables) => {
queryClient.invalidateQueries(["getComments", variables.pageId])
queryClient.invalidateQueries([
"getComments",
variables.originalId || variables.pageId,
])
},
},
)