feat: comment reactions and posting subcomment
This commit is contained in:
parent
cc071ceafd
commit
42611ef635
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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 & {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -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} />
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
])
|
||||
},
|
||||
},
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue