337 lines
11 KiB
TypeScript
337 lines
11 KiB
TypeScript
import {
|
|
useMintPage,
|
|
useGetLikes,
|
|
useCheckLike,
|
|
useGetMints,
|
|
useCheckMint,
|
|
useToggleLikePage,
|
|
useGetLikeCounts,
|
|
} from "~/queries/page"
|
|
import { useAccountSites } from "~/queries/site"
|
|
import { useState, useEffect, useRef } from "react"
|
|
import { Button } from "../ui/Button"
|
|
import { CSB_SCAN, CSB_XCHAR } 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 { getMints, parsePageId } from "~/models/page.model"
|
|
import { CharacterList } from "~/components/common/CharacterList"
|
|
import { cn } from "~/lib/utils"
|
|
import { Tooltip } from "~/components/ui/Tooltip"
|
|
import confetti from "canvas-confetti"
|
|
import { Trans, useTranslation } from "next-i18next"
|
|
|
|
export const Reactions: React.FC<{
|
|
className?: string
|
|
size?: "sm" | "base"
|
|
pageId?: string
|
|
}> = ({ className, size, pageId }) => {
|
|
const toggleLikePage = useToggleLikePage()
|
|
const mintPage = useMintPage()
|
|
const { t } = useTranslation("common")
|
|
|
|
const userSite = useAccountSites()
|
|
const [isLikeOpen, setIsLikeOpen] = useState(false)
|
|
const [isMintOpen, setIsMintOpen] = useState(false)
|
|
const [isLikeListOpen, setIsLikeListOpen] = useState(false)
|
|
const [isMintListOpen, setIsMintListOpen] = useState(false)
|
|
const likeRef = useRef<HTMLButtonElement>(null)
|
|
const mintRef = useRef<HTMLButtonElement>(null)
|
|
|
|
const [likes, likesMutation] = useGetLikes({ pageId })
|
|
const [likeStatus] = useCheckLike({
|
|
pageId,
|
|
})
|
|
const { data: likeCount = 0 } = useGetLikeCounts({ pageId })
|
|
|
|
const loadMoreLikes = async () => {
|
|
if (likesMutation.hasNextPage && !likesMutation.isFetchingNextPage) {
|
|
await likesMutation.fetchNextPage()
|
|
}
|
|
}
|
|
|
|
const mints = useGetMints({
|
|
pageId: pageId,
|
|
includeCharacter: size !== "sm",
|
|
})
|
|
const isMint = useCheckMint(pageId)
|
|
|
|
const like = () => {
|
|
if (pageId) {
|
|
if (likeStatus.isLiked) {
|
|
setIsLikeOpen(true)
|
|
} else {
|
|
toggleLikePage.mutate({ ...parsePageId(pageId), action: "link" })
|
|
}
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (toggleLikePage.isSuccess) {
|
|
if (likeRef.current?.getBoundingClientRect()) {
|
|
confetti({
|
|
particleCount: 150,
|
|
spread: 70,
|
|
origin: {
|
|
x:
|
|
(likeRef.current.getBoundingClientRect().left +
|
|
likeRef.current.getBoundingClientRect().width / 2 || 0.5) /
|
|
window.innerWidth,
|
|
y:
|
|
(likeRef.current.getBoundingClientRect().top || 0.5) /
|
|
window.innerHeight,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}, [toggleLikePage.isSuccess])
|
|
|
|
const mint = () => {
|
|
if (pageId) {
|
|
if (isMint.data?.count) {
|
|
setIsMintOpen(true)
|
|
} else {
|
|
mintPage.mutate(parsePageId(pageId))
|
|
}
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (mintPage.isSuccess) {
|
|
if (mintRef.current?.getBoundingClientRect()) {
|
|
confetti({
|
|
particleCount: 150,
|
|
spread: 360,
|
|
ticks: 50,
|
|
gravity: 0,
|
|
decay: 0.94,
|
|
startVelocity: 30,
|
|
origin: {
|
|
x:
|
|
(mintRef.current.getBoundingClientRect().left +
|
|
mintRef.current.getBoundingClientRect().width / 2 || 0.5) /
|
|
window.innerWidth,
|
|
y:
|
|
(mintRef.current.getBoundingClientRect().top || 0.5) /
|
|
window.innerHeight,
|
|
},
|
|
shapes: ["star"],
|
|
colors: ["FFE400", "FFBD00", "E89400", "FFCA6C", "FDFFB8"],
|
|
})
|
|
}
|
|
}
|
|
}, [mintPage.isSuccess])
|
|
|
|
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={cn(
|
|
"xlog-reactions flex fill-gray-400 text-gray-500 items-center",
|
|
size === "sm" ? "text-sm" : "",
|
|
className,
|
|
)}
|
|
>
|
|
<div
|
|
className={cn(
|
|
"xlog-reactions-like flex items-center",
|
|
size === "sm" ? "mr-3" : "mr-10",
|
|
)}
|
|
>
|
|
<Button
|
|
variant="like"
|
|
className={`flex items-center mr-2 ${
|
|
likeStatus.isLiked && "active"
|
|
}`}
|
|
isAutoWidth={true}
|
|
onClick={like}
|
|
isLoading={userSite.isLoading || toggleLikePage.isPending}
|
|
ref={likeRef}
|
|
>
|
|
<span
|
|
className={cn(
|
|
"i-mingcute:thumb-up-2-fill mr-1",
|
|
size === "sm" ? "text-base" : "text-[40px]",
|
|
)}
|
|
></span>
|
|
<span>{likeCount}</span>
|
|
</Button>
|
|
{size !== "sm" && (
|
|
<ul
|
|
className="-space-x-4 cursor-pointer"
|
|
onClick={() => setIsLikeListOpen(true)}
|
|
>
|
|
{likes
|
|
?.sort((a, b) =>
|
|
b.character?.metadata?.content?.avatars?.[0] ? 1 : -1,
|
|
)
|
|
.slice(0, 3)
|
|
?.map((like, index) => (
|
|
<li className="inline-block" key={index}>
|
|
<Avatar
|
|
className="relative align-middle border-2 border-white"
|
|
images={like.character?.metadata?.content?.avatars || []}
|
|
name={like.character?.metadata?.content?.name}
|
|
size={40}
|
|
/>
|
|
</li>
|
|
))}
|
|
{likeCount > 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">
|
|
+{likeCount - 3}
|
|
</div>
|
|
</li>
|
|
)}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
<div className="xlog-reactions-mint flex items-center">
|
|
{(() => {
|
|
const inner = (
|
|
<Button
|
|
variant="collect"
|
|
className={`flex items-center mr-2 ${
|
|
isMint.isSuccess && isMint.data.count && "active"
|
|
}`}
|
|
isAutoWidth={true}
|
|
onClick={mint}
|
|
isLoading={mintPage.isLoading}
|
|
ref={mintRef}
|
|
>
|
|
<MintIcon
|
|
className={cn("mr-2", size === "sm" ? "w-3 h-3" : "w-8 h-8")}
|
|
/>
|
|
<span>{mints.data?.count || 0}</span>
|
|
</Button>
|
|
)
|
|
return size !== "sm" ? (
|
|
<Tooltip label={t("Mint to an NFT")} placement="top">
|
|
{inner}
|
|
</Tooltip>
|
|
) : (
|
|
inner
|
|
)
|
|
})()}
|
|
{size !== "sm" && (
|
|
<ul
|
|
className="-space-x-4 cursor-pointer"
|
|
onClick={() => setIsMintListOpen(true)}
|
|
>
|
|
{mints.data?.list
|
|
?.sort((a, b: any) =>
|
|
b.character?.metadata?.content?.avatars?.[0] ? 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={t("Like successfull") || ""}
|
|
>
|
|
<div className="p-5">
|
|
<Trans i18nKey="like stored">
|
|
Your like has been stored on the blockchain, view it on{" "}
|
|
<UniLink
|
|
className="text-accent"
|
|
href={`${CSB_SCAN}/tx/${likeStatus.transactionHash}`}
|
|
>
|
|
Crossbell Scan
|
|
</UniLink>
|
|
</Trans>
|
|
</div>
|
|
<div className="h-16 border-t flex items-center px-5">
|
|
<Button isBlock onClick={() => setIsLikeOpen(false)}>
|
|
{t("Got it, thanks!")}
|
|
</Button>
|
|
</div>
|
|
</Modal>
|
|
<Modal
|
|
open={isMintOpen}
|
|
setOpen={setIsMintOpen}
|
|
title={t("Mint successfull") || ""}
|
|
>
|
|
<div className="p-5">
|
|
<Trans i18nKey="mint stored">
|
|
This post has been minted to NFT by you, view it on{" "}
|
|
<UniLink
|
|
className="text-accent"
|
|
href={`${CSB_XCHAR}/${userSite.data?.[0]?.username}/collections`}
|
|
>
|
|
xChar
|
|
</UniLink>{" "}
|
|
or{" "}
|
|
<UniLink
|
|
className="text-accent"
|
|
href={`${CSB_SCAN}/tx/${isMint.data?.list?.[0]?.transactionHash}`}
|
|
>
|
|
Crossbell Scan
|
|
</UniLink>
|
|
</Trans>
|
|
</div>
|
|
<div className="h-16 border-t flex items-center px-5">
|
|
<Button isBlock onClick={() => setIsMintOpen(false)}>
|
|
{t("Got it, thanks!")}
|
|
</Button>
|
|
</div>
|
|
</Modal>
|
|
<CharacterList
|
|
open={isLikeListOpen}
|
|
setOpen={setIsLikeListOpen}
|
|
title={t("Like List")}
|
|
loadMore={loadMoreLikes}
|
|
hasMore={!!likesMutation.hasNextPage}
|
|
list={likes}
|
|
></CharacterList>
|
|
<CharacterList
|
|
open={isMintListOpen}
|
|
setOpen={setIsMintListOpen}
|
|
title={t("Mint List")}
|
|
loadMore={loadMoreMints}
|
|
hasMore={!!mintCursor}
|
|
list={mintList}
|
|
></CharacterList>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|