feat: post side actions
This commit is contained in:
parent
c1c66d709b
commit
00e6022e4c
|
|
@ -151,6 +151,9 @@ export default async function SitePagePage({
|
|||
className="mt-10"
|
||||
content={page?.metadata?.content?.content}
|
||||
toc={true}
|
||||
page={page}
|
||||
site={site}
|
||||
withActions={true}
|
||||
/>
|
||||
</article>
|
||||
{!onlyContent && (
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
import { MutableRefObject, useEffect, useMemo } from "react"
|
||||
|
||||
import { PostActions } from "~/components/site/PostActions"
|
||||
import { PostToc } from "~/components/site/PostToc"
|
||||
import { useCodeCopy } from "~/hooks/useCodeCopy"
|
||||
import { ExpandedCharacter, ExpandedNote } from "~/lib/types"
|
||||
import { calculateElementTop, cn } from "~/lib/utils"
|
||||
import { renderPageContent } from "~/markdown"
|
||||
|
||||
|
|
@ -16,6 +18,9 @@ export const PageContent: React.FC<{
|
|||
onMouseEnter?: () => void
|
||||
parsedContent?: ReturnType<typeof renderPageContent>
|
||||
isComment?: boolean
|
||||
page?: ExpandedNote
|
||||
site?: ExpandedCharacter
|
||||
withActions?: boolean
|
||||
}> = ({
|
||||
className,
|
||||
content,
|
||||
|
|
@ -25,6 +30,9 @@ export const PageContent: React.FC<{
|
|||
onMouseEnter,
|
||||
parsedContent,
|
||||
isComment,
|
||||
page,
|
||||
site,
|
||||
withActions,
|
||||
}) => {
|
||||
useCodeCopy()
|
||||
|
||||
|
|
@ -75,6 +83,7 @@ export const PageContent: React.FC<{
|
|||
{inParsedContent?.element}
|
||||
</div>
|
||||
{toc && inParsedContent?.toc && <PostToc data={inParsedContent?.toc} />}
|
||||
{withActions && <PostActions page={page} site={site} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ export const ReactionLike: React.FC<{
|
|||
size?: "sm" | "base"
|
||||
characterId?: number
|
||||
noteId?: number
|
||||
}> = ({ size, characterId, noteId }) => {
|
||||
vertical?: boolean
|
||||
}> = ({ size, characterId, noteId, vertical }) => {
|
||||
const toggleLikePage = useToggleLikePage()
|
||||
const { t, i18n } = useTranslation("common")
|
||||
|
||||
|
|
@ -94,6 +95,8 @@ export const ReactionLike: React.FC<{
|
|||
}
|
||||
}, [toggleLikePage.isSuccess])
|
||||
|
||||
const showAvatarStack = size !== "sm" && !vertical
|
||||
|
||||
const avatars = useMemo(
|
||||
() =>
|
||||
likes
|
||||
|
|
@ -112,7 +115,13 @@ export const ReactionLike: React.FC<{
|
|||
<div className={cn("xlog-reactions-like flex items-center sm:mb-0")}>
|
||||
<Button
|
||||
variant="like"
|
||||
className={`flex items-center mr-2 ${likeStatus.isLiked && "active"}`}
|
||||
className={cn(
|
||||
"flex items-center",
|
||||
{
|
||||
active: likeStatus.isLiked,
|
||||
},
|
||||
vertical ? "!h-auto flex-col" : "mr-2",
|
||||
)}
|
||||
isAutoWidth={true}
|
||||
onClick={like}
|
||||
isLoading={toggleLikePage.isPending}
|
||||
|
|
@ -122,8 +131,13 @@ export const ReactionLike: React.FC<{
|
|||
const inner = (
|
||||
<span
|
||||
className={cn(
|
||||
"icon-[mingcute--thumb-up-2-fill] mr-1",
|
||||
size === "sm" ? "text-base" : "text-[38px]",
|
||||
"icon-[mingcute--thumb-up-2-fill]",
|
||||
size === "sm"
|
||||
? "text-base"
|
||||
: vertical
|
||||
? "text-[33px]"
|
||||
: "text-[38px]",
|
||||
vertical ? "" : "mr-1",
|
||||
)}
|
||||
></span>
|
||||
)
|
||||
|
|
@ -135,9 +149,9 @@ export const ReactionLike: React.FC<{
|
|||
inner
|
||||
)
|
||||
})()}
|
||||
<span>{likeCount}</span>
|
||||
<span className="leading-snug">{likeCount}</span>
|
||||
</Button>
|
||||
{size !== "sm" && (
|
||||
{showAvatarStack && (
|
||||
<AvatarStack
|
||||
avatars={avatars}
|
||||
count={likeCount}
|
||||
|
|
@ -196,14 +210,16 @@ export const ReactionLike: React.FC<{
|
|||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
<CharacterList
|
||||
open={isLikeListOpen}
|
||||
setOpen={setIsLikeListOpen}
|
||||
title={t("Like List")}
|
||||
loadMore={likesMutation.fetchNextPage}
|
||||
hasMore={!!likesMutation.hasNextPage}
|
||||
list={likesMutation.data?.pages || []}
|
||||
></CharacterList>
|
||||
{showAvatarStack && (
|
||||
<CharacterList
|
||||
open={isLikeListOpen}
|
||||
setOpen={setIsLikeListOpen}
|
||||
title={t("Like List")}
|
||||
loadMore={likesMutation.fetchNextPage}
|
||||
hasMore={!!likesMutation.hasNextPage}
|
||||
list={likesMutation.data?.pages || []}
|
||||
></CharacterList>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ export const ReactionMint: React.FC<{
|
|||
size?: "sm" | "base"
|
||||
noteId?: number
|
||||
characterId?: number
|
||||
}> = ({ size, noteId, characterId }) => {
|
||||
vertical?: boolean
|
||||
}> = ({ size, noteId, characterId, vertical }) => {
|
||||
const mintPage = useMintPage()
|
||||
const { t, i18n } = useTranslation("common")
|
||||
|
||||
|
|
@ -83,6 +84,8 @@ export const ReactionMint: React.FC<{
|
|||
}
|
||||
}, [mintPage.isSuccess])
|
||||
|
||||
const showAvatarStack = size !== "sm" && !vertical
|
||||
|
||||
const avatars = useMemo(
|
||||
() =>
|
||||
mints.data?.pages?.[0]?.list
|
||||
|
|
@ -102,9 +105,13 @@ export const ReactionMint: React.FC<{
|
|||
<div className="xlog-reactions-mint flex items-center">
|
||||
<Button
|
||||
variant="collect"
|
||||
className={`flex items-center mr-2 ${
|
||||
isMint.isSuccess && isMint.data.count && "active"
|
||||
}`}
|
||||
className={cn(
|
||||
"flex items-center",
|
||||
{
|
||||
active: isMint.isSuccess && isMint.data.count,
|
||||
},
|
||||
vertical ? "!h-auto flex-col" : "mr-2",
|
||||
)}
|
||||
isAutoWidth={true}
|
||||
onClick={mint}
|
||||
isLoading={mintPage.isLoading}
|
||||
|
|
@ -112,7 +119,15 @@ export const ReactionMint: React.FC<{
|
|||
>
|
||||
{(() => {
|
||||
const inner = (
|
||||
<MintIcon className={cn(size === "sm" ? "w-3 h-3" : "w-8 h-8")} />
|
||||
<MintIcon
|
||||
className={cn(
|
||||
size === "sm"
|
||||
? "w-3 h-3"
|
||||
: vertical
|
||||
? "w-[33px] h-[33px] p-[2px]"
|
||||
: "w-[38px] h-[38px] p-[3px]",
|
||||
)}
|
||||
/>
|
||||
)
|
||||
return size !== "sm" ? (
|
||||
<Tooltip label={t("Mint to an NFT")} placement="top">
|
||||
|
|
@ -122,9 +137,11 @@ export const ReactionMint: React.FC<{
|
|||
inner
|
||||
)
|
||||
})()}
|
||||
<span className="ml-2">{mints.data?.pages?.[0]?.count || 0}</span>
|
||||
<span className={cn("leading-snug", vertical ? "" : "ml-2")}>
|
||||
{mints.data?.pages?.[0]?.count || 0}
|
||||
</span>
|
||||
</Button>
|
||||
{size !== "sm" && (
|
||||
{showAvatarStack && (
|
||||
<AvatarStack
|
||||
avatars={avatars}
|
||||
onClick={() => setIsMintListOpen(true)}
|
||||
|
|
@ -161,14 +178,16 @@ export const ReactionMint: React.FC<{
|
|||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
<CharacterList
|
||||
open={isMintListOpen}
|
||||
setOpen={setIsMintListOpen}
|
||||
title={t("Mint List")}
|
||||
loadMore={mints.fetchNextPage}
|
||||
hasMore={!!mints.hasNextPage}
|
||||
list={mints.data?.pages || []}
|
||||
></CharacterList>
|
||||
{showAvatarStack && (
|
||||
<CharacterList
|
||||
open={isMintListOpen}
|
||||
setOpen={setIsMintListOpen}
|
||||
title={t("Mint List")}
|
||||
loadMore={mints.fetchNextPage}
|
||||
hasMore={!!mints.hasNextPage}
|
||||
list={mints.data?.pages || []}
|
||||
></CharacterList>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { Tooltip } from "~/components/ui/Tooltip"
|
|||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { noopArr } from "~/lib/noop"
|
||||
import { ExpandedCharacter, ExpandedNote } from "~/lib/types"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { useGetTips } from "~/queries/site"
|
||||
|
||||
import { AvatarStack } from "../ui/AvatarStack"
|
||||
|
|
@ -19,7 +20,8 @@ export const ReactionTip: React.FC<{
|
|||
noteId?: number
|
||||
site?: ExpandedCharacter
|
||||
page?: ExpandedNote
|
||||
}> = ({ characterId, noteId, site, page }) => {
|
||||
vertical?: boolean
|
||||
}> = ({ characterId, noteId, site, page, vertical }) => {
|
||||
const { t } = useTranslation("common")
|
||||
|
||||
const account = useAccountState((s) => s.computed.account)
|
||||
|
|
@ -43,6 +45,8 @@ export const ReactionTip: React.FC<{
|
|||
}
|
||||
}
|
||||
|
||||
const showAvatarStack = !vertical
|
||||
|
||||
const avatars = useMemo(
|
||||
() =>
|
||||
tips.data?.pages?.[0]?.list
|
||||
|
|
@ -62,16 +66,25 @@ export const ReactionTip: React.FC<{
|
|||
<div className="xlog-reactions-tip flex items-center">
|
||||
<Button
|
||||
variant="tip"
|
||||
className={`flex items-center mr-2 ${
|
||||
isTip.isSuccess && isTip.data.pages?.[0].count && "active"
|
||||
}`}
|
||||
className={cn(
|
||||
"flex items-center",
|
||||
{
|
||||
active: isTip.isSuccess && isTip.data.pages?.[0].count,
|
||||
},
|
||||
vertical ? "!h-auto flex-col" : "mr-2",
|
||||
)}
|
||||
isAutoWidth={true}
|
||||
onClick={tip}
|
||||
ref={tipRef}
|
||||
>
|
||||
{(() => {
|
||||
const inner = (
|
||||
<i className="icon-[mingcute--heart-fill] text-[40px]" />
|
||||
<i
|
||||
className={cn(
|
||||
"icon-[mingcute--heart-fill]",
|
||||
vertical ? "text-[33px]" : "text-[38px]",
|
||||
)}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<Tooltip label={t("Tip")} placement="top">
|
||||
|
|
@ -79,14 +92,17 @@ export const ReactionTip: React.FC<{
|
|||
</Tooltip>
|
||||
)
|
||||
})()}
|
||||
<span className="ml-2">{tips.data?.pages?.[0]?.count || 0}</span>
|
||||
<span className={cn("leading-snug", vertical ? "" : "ml-2")}>
|
||||
{tips.data?.pages?.[0]?.count || 0}
|
||||
</span>
|
||||
</Button>
|
||||
|
||||
<AvatarStack
|
||||
avatars={avatars}
|
||||
count={tips.data?.pages?.[0]?.count || 0}
|
||||
onClick={tip}
|
||||
/>
|
||||
{showAvatarStack && (
|
||||
<AvatarStack
|
||||
avatars={avatars}
|
||||
count={tips.data?.pages?.[0]?.count || 0}
|
||||
onClick={tip}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<PatronModal
|
||||
site={site}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
import React, { useEffect, useRef, useState } from "react"
|
||||
|
||||
import { ReactionLike } from "~/components/common/ReactionLike"
|
||||
import { ReactionMint } from "~/components/common/ReactionMint"
|
||||
import { ReactionTip } from "~/components/common/ReactionTip"
|
||||
import { ExpandedCharacter, ExpandedNote } from "~/lib/types"
|
||||
import { calculateElementTop } from "~/lib/utils"
|
||||
import { useGetComments } from "~/queries/page"
|
||||
|
||||
export const PostActions: React.FC<{
|
||||
page?: ExpandedNote
|
||||
site?: ExpandedCharacter
|
||||
}> = ({ page, site }) => {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const [maxWidth, setMaxWidth] = useState(0)
|
||||
useEffect(() => {
|
||||
if (containerRef?.current) {
|
||||
setMaxWidth(
|
||||
(window.innerWidth -
|
||||
(containerRef.current?.parentElement?.clientWidth || 0)) /
|
||||
2 -
|
||||
40,
|
||||
)
|
||||
}
|
||||
}, [containerRef])
|
||||
|
||||
const comments = useGetComments({
|
||||
characterId: page?.characterId,
|
||||
noteId: page?.noteId,
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
className="xlog-post-actions absolute right-full pr-14 h-full top-0"
|
||||
style={{
|
||||
display: maxWidth > 60 ? "block" : "none",
|
||||
}}
|
||||
ref={containerRef}
|
||||
>
|
||||
<div className="sticky top-[calc(100vh-313px)] text-sm leading-loose whitespace-nowrap flex flex-col justify-center items-center space-y-4">
|
||||
<ReactionLike
|
||||
characterId={page?.characterId}
|
||||
noteId={page?.noteId}
|
||||
vertical={true}
|
||||
/>
|
||||
<ReactionMint
|
||||
characterId={page?.characterId}
|
||||
noteId={page?.noteId}
|
||||
vertical={true}
|
||||
/>
|
||||
<ReactionTip
|
||||
characterId={page?.characterId}
|
||||
noteId={page?.noteId}
|
||||
site={site}
|
||||
page={page}
|
||||
vertical={true}
|
||||
/>
|
||||
<div
|
||||
className="cursor-pointer text-gray-500 flex flex-col items-center leading-snug"
|
||||
onClick={() => {
|
||||
const targetElement = document.querySelector(
|
||||
"#comments",
|
||||
) as HTMLElement
|
||||
window.scrollTo({
|
||||
top: calculateElementTop(targetElement) - 20,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}}
|
||||
>
|
||||
<i className="icon-[mingcute--comment-fill] text-[33px]" />
|
||||
<span className="text-gray-500">
|
||||
{comments.data?.pages?.[0]?.count}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -154,8 +154,8 @@ export const PostToc: React.FC<{
|
|||
<div
|
||||
className="xlog-post-toc absolute left-full pl-10 h-full top-0"
|
||||
style={{
|
||||
maxWidth: maxWidth > 40 ? maxWidth : 0,
|
||||
display: maxWidth > 40 ? "block" : "none",
|
||||
maxWidth: maxWidth > 60 ? maxWidth : 0,
|
||||
display: maxWidth > 60 ? "block" : "none",
|
||||
}}
|
||||
ref={containerRef}
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in New Issue