refactor: extract reaction AvatarStack (#454)

This commit is contained in:
Innei 2023-04-29 03:58:37 +08:00 committed by GitHub
parent 7e6702ae32
commit d51b713d2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 118 additions and 98 deletions

View File

@ -4,12 +4,12 @@ import { CharacterFloatCard } from "~/components/common/CharacterFloatCard"
import { BlockchainIcon } from "~/components/icons/BlockchainIcon"
import { CSB_SCAN } from "~/lib/env"
import { getSiteLink } from "~/lib/helpers"
import { noopArr } from "~/lib/noop"
import { Avatar } from "../ui/Avatar"
import { UniLink } from "../ui/UniLink"
import { FollowingButton } from "./FollowingButton"
const noopArr = [] as any[]
const CharacterListItem: React.FC<{
character: any
sub: any

View File

@ -1,6 +1,6 @@
import confetti from "canvas-confetti"
import { Trans, useTranslation } from "next-i18next"
import { useEffect, useRef, useState } from "react"
import { useEffect, useMemo, useRef, useState } from "react"
import { CharacterList } from "~/components/common/CharacterList"
import { Modal } from "~/components/ui/Modal"
@ -16,7 +16,7 @@ import {
useToggleLikePage,
} from "~/queries/page"
import { Avatar } from "../ui/Avatar"
import { AvatarStack } from "../ui/AvatarStack"
import { Button } from "../ui/Button"
export const ReactionLike: React.FC<{
@ -77,6 +77,19 @@ export const ReactionLike: React.FC<{
}
}, [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")}>
@ -108,33 +121,11 @@ export const ReactionLike: React.FC<{
<span>{likeCount}</span>
</Button>
{size !== "sm" && (
<ul
className="-space-x-4 cursor-pointer hidden sm:inline-block"
<AvatarStack
avatars={avatars}
count={likeCount}
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>
<Modal

View File

@ -1,6 +1,6 @@
import confetti from "canvas-confetti"
import { Trans, useTranslation } from "next-i18next"
import { useEffect, useRef, useState } from "react"
import { useEffect, useMemo, useRef, useState } from "react"
import { useAccountState } from "@crossbell/connect-kit"
@ -10,11 +10,12 @@ import { Modal } from "~/components/ui/Modal"
import { Tooltip } from "~/components/ui/Tooltip"
import { UniLink } from "~/components/ui/UniLink"
import { CSB_SCAN, CSB_XCHAR } from "~/lib/env"
import { noopArr } from "~/lib/noop"
import { cn } from "~/lib/utils"
import { parsePageId } from "~/models/page.model"
import { useCheckMint, useGetMints, useMintPage } from "~/queries/page"
import { Avatar } from "../ui/Avatar"
import { AvatarStack } from "../ui/AvatarStack"
import { Button } from "../ui/Button"
export const ReactionMint: React.FC<{
@ -73,6 +74,20 @@ export const ReactionMint: React.FC<{
}
}, [mintPage.isSuccess])
const avatars = useMemo(
() =>
mints.data?.pages?.[0]?.list
?.sort((a, b: any) =>
b.character?.metadata?.content?.avatars?.[0] ? 1 : -1,
)
.slice(0, 3)
.map((mint: any) => ({
images: mint.character?.metadata?.content?.avatars,
name: mint.character?.metadata?.content?.name,
})) || noopArr,
[mints],
)
return (
<>
<div className="xlog-reactions-mint flex items-center">
@ -101,33 +116,11 @@ export const ReactionMint: React.FC<{
<span className="ml-2">{mints.data?.pages?.[0]?.count || 0}</span>
</Button>
{size !== "sm" && (
<ul
className="-space-x-4 cursor-pointer hidden sm:inline-block"
<AvatarStack
avatars={avatars}
onClick={() => setIsMintListOpen(true)}
>
{mints.data?.pages?.[0]?.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?.pages?.[0]?.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!.pages?.[0]?.count - 3}
</div>
</li>
)}
</ul>
count={mints.data?.pages?.[0]?.count || 0}
/>
)}
</div>
<Modal

View File

@ -1,15 +1,16 @@
import { useTranslation } from "next-i18next"
import { useRef, useState } from "react"
import { useMemo, useRef, useState } from "react"
import { useAccountState } from "@crossbell/connect-kit"
import { PatronModal } from "~/components/common/PatronModal"
import { Tooltip } from "~/components/ui/Tooltip"
import { noopArr } from "~/lib/noop"
import { Note, Profile } from "~/lib/types"
import { parsePageId } from "~/models/page.model"
import { useGetTips } from "~/queries/site"
import { Avatar } from "../ui/Avatar"
import { AvatarStack } from "../ui/AvatarStack"
import { Button } from "../ui/Button"
export const ReactionTip: React.FC<{
@ -41,6 +42,14 @@ export const ReactionTip: React.FC<{
}
}
const avatars = useMemo(
() =>
tips.data?.pages?.[0]?.list?.sort((a, b: any) =>
b.character?.metadata?.content?.avatars?.[0] ? 1 : -1,
) || noopArr,
[tips.data?.pages],
)
return (
<>
<div className="xlog-reactions-tip flex items-center">
@ -63,35 +72,12 @@ export const ReactionTip: React.FC<{
})()}
<span className="ml-2">{tips.data?.pages?.[0]?.count || 0}</span>
</Button>
{
<ul
className="-space-x-4 cursor-pointer hidden sm:inline-block"
onClick={tip}
>
{tips.data?.pages?.[0]?.list
?.sort((a, b: any) =>
b.character?.metadata?.content?.avatars?.[0] ? 1 : -1,
)
.slice(0, 3)
.map((tip: any, index) => (
<li className="inline-block" key={index}>
<Avatar
className="relative align-middle border-2 border-white"
images={tip.character?.metadata?.content?.avatars || []}
name={tip.character?.metadata?.content?.name}
size={40}
/>
</li>
))}
{(tips.data?.pages?.[0]?.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">
+{tips.data!.pages?.[0]?.count - 3}
</div>
</li>
)}
</ul>
}
<AvatarStack
avatars={avatars}
count={tips.data?.pages?.[0]?.count || 0}
onClick={tip}
/>
</div>
<PatronModal
site={site}

View File

@ -0,0 +1,48 @@
import { FC } from "react"
import { noopArr } from "~/lib/noop"
import { Avatar } from "./Avatar"
interface AvatarStackProps {
onClick?: () => void
avatars: {
name: string | null | undefined
images: string[] | null | undefined
}[]
count: number
/**
* @default 3
*/
showCount?: number
}
export const AvatarStack: FC<AvatarStackProps> = (props) => {
const { avatars, count, onClick, showCount = 3 } = props
return (
<ul
className="-space-x-4 cursor-pointer hidden sm:inline-flex"
onClick={onClick}
>
{avatars.slice(0, showCount).map((avatar) => {
return (
<li className="inline-block" key={avatar.name}>
<Avatar
className="relative align-middle border-2 border-white"
images={avatar.images || noopArr}
name={avatar.name || ""}
size={40}
/>
</li>
)
})}
{count > showCount && (
<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">
+{count - showCount}
</div>
</li>
)}
</ul>
)
}

View File

@ -1,6 +1,7 @@
import { useEffect, useRef, useState } from "react"
import { create } from "zustand"
import { noop } from "~/lib/noop"
import { delStorage, getStorage, setStorage } from "~/lib/storage"
import { isServerSide } from "~/lib/utils"
@ -150,8 +151,6 @@ const useDarkModeInternal = (
}
}
const noop = () => {}
const mockElement = {
classList: {
add: noop,

View File

@ -3,3 +3,8 @@ export const IS_BROWSER = typeof document !== "undefined"
export const IS_PROD = IS_BROWSER
? !location.hostname.endsWith("localhost")
: process.env.NODE_ENV === "production"
export const IS_DEV = process.env.NODE_ENV === "development"
export const IS_VERCEL_PREVIEW =
process.env.NEXT_PUBLIC_VERCEL_ENV === "preview" ||
process.env.VERCEL_ENV === "preview"

View File

@ -20,8 +20,3 @@ export const IPFS_GATEWAY =
process.env.NEXT_PUBLIC_IPFS_GATEWAY || "https://ipfs.4everland.xyz/ipfs/"
export const MIRA_LINK =
process.env.NEXT_PUBLIC_MIRA_LINK || "https://mira.crossbell.io"
export const IS_VERCEL_PREVIEW =
process.env.NEXT_PUBLIC_VERCEL_ENV === "preview" ||
process.env.VERCEL_ENV === "preview"
export const IS_DEV = process.env.NODE_ENV === "development"

View File

@ -2,8 +2,8 @@ import { NoteEntity } from "crossbell.js"
import { Note, Profile } from "~/lib/types"
import { IS_PROD } from "./constants"
import { IS_VERCEL_PREVIEW, OUR_DOMAIN } from "./env"
import { IS_PROD, IS_VERCEL_PREVIEW } from "./constants"
import { OUR_DOMAIN } from "./env"
import { isServerSide } from "./utils"
export const getSiteLink = ({

3
src/lib/noop.ts Normal file
View File

@ -0,0 +1,3 @@
export const noop = () => void 0
export const noopArr = [] as any[]

View File

@ -1,8 +1,8 @@
import { NoteEntity } from "crossbell.js"
import { NextApiRequest, NextApiResponse } from "next"
import { IS_VERCEL_PREVIEW } from "~/lib/constants"
import { getDefaultSlug } from "~/lib/default-slug"
import { IS_VERCEL_PREVIEW } from "~/lib/env"
import { getSiteLink } from "~/lib/helpers"
import { checkDomainServer } from "~/models/site.model"