feat: post side actions

This commit is contained in:
DIYgod 2023-05-19 02:33:29 +01:00
parent c1c66d709b
commit 00e6022e4c
No known key found for this signature in database
7 changed files with 185 additions and 43 deletions

View File

@ -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 && (

View File

@ -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>
)
}

View File

@ -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>
)}
</>
)
}

View File

@ -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>
)}
</>
)
}

View File

@ -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}

View File

@ -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>
)
}

View File

@ -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}
>