You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

88 lines
3.7 KiB

import { useNoteStatsById } from '@/hooks/useNoteStatsById'
import { useUserTrust } from '@/contexts/user-trust-context'
import { cn } from '@/lib/utils'
import noteStatsService from '@/services/note-stats.service'
import { useRssUrlThreadQueryRelays } from '@/hooks/useRssUrlThreadQueryRelays'
import { useNostr } from '@/providers/NostrProvider'
import { Bookmark, Highlighter, MessageCircle, ThumbsUp } from 'lucide-react'
import type { Event } from 'nostr-tools'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
/** Compact reply / reaction / bookmark / highlight counts for RSS + Web URL threads. */
export default function RssUrlThreadStatsBar({
event,
className
}: {
event: Event
className?: string
}) {
const { t } = useTranslation()
const { pubkey } = useNostr()
const { relayUrls: statsRelays, key: statsRelaysKey } = useRssUrlThreadQueryRelays()
const { hideUntrustedInteractions, isUserTrusted } = useUserTrust()
const noteStats = useNoteStatsById(event.id)
const [loading, setLoading] = useState(false)
useEffect(() => {
setLoading(true)
noteStatsService
.fetchNoteStats(event, pubkey, statsRelays, { foreground: true })
.finally(() => setLoading(false))
}, [event.id, event.kind, event.created_at, event.sig, pubkey, statsRelaysKey])
const fmt = (n: number) => (n >= 100 ? '99+' : String(n))
const { replyCount, reactionCount, highlightCount, bookmarkCount } = useMemo(() => {
const replies = noteStats?.replies ?? []
const likes = noteStats?.likes ?? []
const highlights = noteStats?.highlights ?? []
const trustedReplyCount = hideUntrustedInteractions
? replies.filter((r) => isUserTrusted(r.pubkey)).length
: replies.length
const trustedReactionCount = hideUntrustedInteractions
? likes.filter((l) => isUserTrusted(l.pubkey)).length
: likes.length
const trustedHighlightCount = hideUntrustedInteractions
? highlights.filter((h) => isUserTrusted(h.pubkey)).length
: highlights.length
const bookmarkCountInner = noteStats?.bookmarkPubkeySet?.size ?? 0
return {
replyCount: trustedReplyCount,
reactionCount: trustedReactionCount,
highlightCount: trustedHighlightCount,
bookmarkCount: bookmarkCountInner
}
}, [noteStats, hideUntrustedInteractions, isUserTrusted])
return (
<div
className={cn(
'flex flex-wrap items-center gap-x-3 gap-y-1 border-t border-border/50 bg-muted/20 px-3 py-2 text-xs text-muted-foreground',
loading ? 'animate-pulse' : '',
className
)}
data-rss-url-thread-stats
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
role="group"
aria-label={t('URL thread activity')}
>
<span className="inline-flex items-center gap-1" title={t('Comments')}>
<MessageCircle className="size-3.5 shrink-0 opacity-90" strokeWidth={2} aria-hidden />
<span className="tabular-nums">{fmt(replyCount)}</span>
</span>
<span className="inline-flex items-center gap-1" title={t('Reactions')}>
<ThumbsUp className="size-3.5 shrink-0 opacity-90" strokeWidth={2} aria-hidden />
<span className="tabular-nums">{fmt(reactionCount)}</span>
</span>
<span className="inline-flex items-center gap-1" title={t('Bookmarks')}>
<Bookmark className="size-3.5 shrink-0 opacity-90" strokeWidth={2} aria-hidden />
<span className="tabular-nums">{fmt(bookmarkCount)}</span>
</span>
<span className="inline-flex items-center gap-1" title={t('Highlights')}>
<Highlighter className="size-3.5 shrink-0 opacity-90" strokeWidth={2} aria-hidden />
<span className="tabular-nums">{fmt(highlightCount)}</span>
</span>
</div>
)
}