feat: show reaction counts and hide top zap/likes in feed

This commit is contained in:
codytseng
2025-05-16 15:17:13 +08:00
parent ee6c5c6f03
commit 81b6462b63
4 changed files with 31 additions and 18 deletions

View File

@@ -5,7 +5,6 @@ import {
DropdownMenuTrigger DropdownMenuTrigger
} from '@/components/ui/dropdown-menu' } from '@/components/ui/dropdown-menu'
import { createReactionDraftEvent } from '@/lib/draft-event' import { createReactionDraftEvent } from '@/lib/draft-event'
import { cn } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider' import { useNostr } from '@/providers/NostrProvider'
import { useNoteStats } from '@/providers/NoteStatsProvider' import { useNoteStats } from '@/providers/NoteStatsProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider'
@@ -16,6 +15,7 @@ import { useTranslation } from 'react-i18next'
import Emoji from '../Emoji' import Emoji from '../Emoji'
import EmojiPicker from '../EmojiPicker' import EmojiPicker from '../EmojiPicker'
import SuggestedEmojis from '../SuggestedEmojis' import SuggestedEmojis from '../SuggestedEmojis'
import { formatCount } from './utils'
export default function LikeButton({ event }: { event: Event }) { export default function LikeButton({ event }: { event: Event }) {
const { t } = useTranslation() const { t } = useTranslation()
@@ -25,10 +25,10 @@ export default function LikeButton({ event }: { event: Event }) {
const [liking, setLiking] = useState(false) const [liking, setLiking] = useState(false)
const [isEmojiReactionsOpen, setIsEmojiReactionsOpen] = useState(false) const [isEmojiReactionsOpen, setIsEmojiReactionsOpen] = useState(false)
const [isPickerOpen, setIsPickerOpen] = useState(false) const [isPickerOpen, setIsPickerOpen] = useState(false)
const myLastEmoji = useMemo(() => { const { myLastEmoji, likeCount } = useMemo(() => {
const stats = noteStatsMap.get(event.id) || {} const stats = noteStatsMap.get(event.id) || {}
const like = stats.likes?.find((like) => like.pubkey === pubkey) const like = stats.likes?.find((like) => like.pubkey === pubkey)
return like?.emoji return { myLastEmoji: like?.emoji, likeCount: stats.likes?.length }
}, [noteStatsMap, event, pubkey]) }, [noteStatsMap, event, pubkey])
const like = async (emoji: string) => { const like = async (emoji: string) => {
@@ -58,10 +58,7 @@ export default function LikeButton({ event }: { event: Event }) {
const trigger = ( const trigger = (
<button <button
className={cn( className="flex items-center enabled:hover:text-primary gap-1 px-3 h-full text-muted-foreground"
'flex items-center enabled:hover:text-primary gap-1 px-3 h-full',
!myLastEmoji ? 'text-muted-foreground' : ''
)}
title={t('Like')} title={t('Like')}
onClick={() => { onClick={() => {
if (isSmallScreen) { if (isSmallScreen) {
@@ -72,11 +69,17 @@ export default function LikeButton({ event }: { event: Event }) {
{liking ? ( {liking ? (
<Loader className="animate-spin" /> <Loader className="animate-spin" />
) : myLastEmoji ? ( ) : myLastEmoji ? (
<>
<div className="h-5 w-5 flex items-center justify-center"> <div className="h-5 w-5 flex items-center justify-center">
<Emoji emoji={myLastEmoji} /> <Emoji emoji={myLastEmoji} />
</div> </div>
{!!likeCount && <div className="text-sm">{formatCount(likeCount)}</div>}
</>
) : ( ) : (
<>
<SmilePlus /> <SmilePlus />
{!!likeCount && <div className="text-sm">{formatCount(likeCount)}</div>}
</>
)} )}
</button> </button>
) )

View File

@@ -16,7 +16,8 @@ export default function NoteStats({
event, event,
className, className,
classNames, classNames,
fetchIfNotExisting = false fetchIfNotExisting = false,
displayTopZapsAndLikes = false
}: { }: {
event: Event event: Event
className?: string className?: string
@@ -24,6 +25,7 @@ export default function NoteStats({
buttonBar?: string buttonBar?: string
} }
fetchIfNotExisting?: boolean fetchIfNotExisting?: boolean
displayTopZapsAndLikes?: boolean
}) { }) {
const { isSmallScreen } = useScreenSize() const { isSmallScreen } = useScreenSize()
const { fetchNoteStats } = useNoteStats() const { fetchNoteStats } = useNoteStats()
@@ -38,8 +40,12 @@ export default function NoteStats({
if (isSmallScreen) { if (isSmallScreen) {
return ( return (
<div className={cn('select-none', className)}> <div className={cn('select-none', className)}>
{displayTopZapsAndLikes && (
<>
<TopZaps event={event} /> <TopZaps event={event} />
<Likes event={event} /> <Likes event={event} />
</>
)}
<div <div
className={cn( className={cn(
'flex justify-between items-center h-5 [&_svg]:size-5', 'flex justify-between items-center h-5 [&_svg]:size-5',
@@ -61,8 +67,12 @@ export default function NoteStats({
return ( return (
<div className={cn('select-none', className)}> <div className={cn('select-none', className)}>
{displayTopZapsAndLikes && (
<>
<TopZaps event={event} /> <TopZaps event={event} />
<Likes event={event} /> <Likes event={event} />
</>
)}
<div className="flex justify-between h-5 [&_svg]:size-4"> <div className="flex justify-between h-5 [&_svg]:size-4">
<div <div
className={cn('flex items-center', loading ? 'animate-pulse' : '')} className={cn('flex items-center', loading ? 'animate-pulse' : '')}

View File

@@ -66,7 +66,7 @@ export default function ReplyNote({
{show ? ( {show ? (
<> <>
<Content className="mt-2" event={event} /> <Content className="mt-2" event={event} />
<NoteStats className="mt-2" event={event} /> <NoteStats className="mt-2" event={event} displayTopZapsAndLikes />
</> </>
) : ( ) : (
<Button <Button

View File

@@ -77,7 +77,7 @@ const NotePage = forwardRef(({ id, index }: { id?: string; index?: number }, ref
className="select-text" className="select-text"
hideParentNotePreview hideParentNotePreview
/> />
<NoteStats className="mt-3" event={event} fetchIfNotExisting /> <NoteStats className="mt-3" event={event} fetchIfNotExisting displayTopZapsAndLikes />
</div> </div>
<Separator className="mt-4" /> <Separator className="mt-4" />
{event.kind === kinds.ShortTextNote ? ( {event.kind === kinds.ShortTextNote ? (