208 lines
5.7 KiB
TypeScript
208 lines
5.7 KiB
TypeScript
import confetti from "canvas-confetti"
|
|
import { Trans, useTranslation } from "next-i18next"
|
|
import { useEffect, useMemo, useRef, useState } from "react"
|
|
|
|
import { CharacterList } from "~/components/common/CharacterList"
|
|
import { Modal } from "~/components/ui/Modal"
|
|
import { Tooltip } from "~/components/ui/Tooltip"
|
|
import { UniLink } from "~/components/ui/UniLink"
|
|
import { CSB_SCAN } from "~/lib/env"
|
|
import { cn } from "~/lib/utils"
|
|
import {
|
|
useCheckLike,
|
|
useGetLikeCounts,
|
|
useGetLikes,
|
|
useToggleLikePage,
|
|
} from "~/queries/page"
|
|
|
|
import { AvatarStack } from "../ui/AvatarStack"
|
|
import { Button } from "../ui/Button"
|
|
|
|
export const ReactionLike: React.FC<{
|
|
size?: "sm" | "base"
|
|
characterId?: number
|
|
noteId?: number
|
|
}> = ({ size, characterId, noteId }) => {
|
|
const toggleLikePage = useToggleLikePage()
|
|
const { t } = useTranslation("common")
|
|
|
|
const [isLikeOpen, setIsLikeOpen] = useState(false)
|
|
const [isLikeListOpen, setIsLikeListOpen] = useState(false)
|
|
const likeRef = useRef<HTMLButtonElement>(null)
|
|
|
|
const [likes, likesMutation] = useGetLikes({
|
|
characterId,
|
|
noteId,
|
|
})
|
|
const [likeStatus] = useCheckLike({
|
|
characterId,
|
|
noteId,
|
|
})
|
|
const { data: likeCount = 0 } = useGetLikeCounts({
|
|
characterId,
|
|
noteId,
|
|
})
|
|
|
|
const [isUnlikeOpen, setIsUnlikeOpen] = useState(false)
|
|
|
|
const like = () => {
|
|
if (characterId && noteId) {
|
|
if (likeStatus.isLiked) {
|
|
setIsLikeOpen(true)
|
|
} else {
|
|
toggleLikePage.mutate({
|
|
characterId,
|
|
noteId,
|
|
action: "link",
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
const unlike = () => {
|
|
if (characterId && noteId) {
|
|
setIsUnlikeOpen(false)
|
|
if (likeStatus.isLiked) {
|
|
toggleLikePage.mutate({
|
|
noteId,
|
|
characterId,
|
|
action: "unlink",
|
|
})
|
|
} // else do nothing
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (toggleLikePage.isSuccess) {
|
|
if (likeRef.current?.getBoundingClientRect() && likeStatus.isLiked) {
|
|
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 avatars = useMemo(
|
|
() =>
|
|
likes
|
|
?.sort((a, b) =>
|
|
b.character?.metadata?.content?.avatars?.[0] ? 1 : -1,
|
|
)
|
|
.slice(0, 3)
|
|
.map(($) => ({
|
|
images: $.character?.metadata?.content?.avatars,
|
|
name: $.character?.metadata?.content?.name,
|
|
})),
|
|
[likes],
|
|
)
|
|
return (
|
|
<>
|
|
<div className={cn("xlog-reactions-like flex items-center sm:mb-0")}>
|
|
<Button
|
|
variant="like"
|
|
className={`flex items-center mr-2 ${likeStatus.isLiked && "active"}`}
|
|
isAutoWidth={true}
|
|
onClick={like}
|
|
isLoading={toggleLikePage.isPending}
|
|
ref={likeRef}
|
|
>
|
|
{(() => {
|
|
const inner = (
|
|
<span
|
|
className={cn(
|
|
"icon-[mingcute--thumb-up-2-fill] mr-1",
|
|
size === "sm" ? "text-base" : "text-[38px]",
|
|
)}
|
|
></span>
|
|
)
|
|
return size !== "sm" ? (
|
|
<Tooltip label={t("Like")} placement="top">
|
|
{inner}
|
|
</Tooltip>
|
|
) : (
|
|
inner
|
|
)
|
|
})()}
|
|
<span>{likeCount}</span>
|
|
</Button>
|
|
{size !== "sm" && (
|
|
<AvatarStack
|
|
avatars={avatars}
|
|
count={likeCount}
|
|
onClick={() => setIsLikeListOpen(true)}
|
|
/>
|
|
)}
|
|
</div>
|
|
<Modal
|
|
open={isLikeOpen}
|
|
setOpen={setIsLikeOpen}
|
|
title={t("Like successfully") || ""}
|
|
>
|
|
<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="border-t flex flex-col md:flex-row gap-4 items-center px-5 py-4">
|
|
<Button isBlock onClick={() => setIsLikeOpen(false)}>
|
|
{t("Got it, thanks!")}
|
|
</Button>
|
|
<Button
|
|
variant="secondary"
|
|
isBlock
|
|
onClick={() => {
|
|
setIsUnlikeOpen(true)
|
|
setIsLikeOpen(false)
|
|
}}
|
|
>
|
|
{t("Revert")}
|
|
</Button>
|
|
</div>
|
|
</Modal>
|
|
<Modal
|
|
open={isUnlikeOpen}
|
|
setOpen={setIsUnlikeOpen}
|
|
title={t("Confirm to revert")}
|
|
>
|
|
<div className="p-5">
|
|
<Trans i18nKey="like revert">
|
|
Do you really want to revert this like action?
|
|
</Trans>
|
|
</div>
|
|
<div className="border-t flex flex-col md:flex-row gap-4 items-center px-5 py-4">
|
|
<Button isBlock onClick={() => setIsUnlikeOpen(false)}>
|
|
{t("Cancel")}
|
|
</Button>
|
|
<Button variant="secondary" isBlock onClick={() => unlike()}>
|
|
{t("Confirm")}
|
|
</Button>
|
|
</div>
|
|
</Modal>
|
|
<CharacterList
|
|
open={isLikeListOpen}
|
|
setOpen={setIsLikeListOpen}
|
|
title={t("Like List")}
|
|
loadMore={likesMutation.fetchNextPage}
|
|
hasMore={!!likesMutation.hasNextPage}
|
|
list={likesMutation.data?.pages || []}
|
|
></CharacterList>
|
|
</>
|
|
)
|
|
}
|