import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area' import { useNoteStatsById } from '@/hooks/useNoteStatsById' import { createReactionDraftEvent } from '@/lib/draft-event' import { cn } from '@/lib/utils' import { useNostr } from '@/providers/NostrProvider' import noteStatsService from '@/services/note-stats.service' import { TEmoji } from '@/types' import { Loader } from 'lucide-react' import { Event } from 'nostr-tools' import { useMemo, useRef, useState } from 'react' import Emoji from '../Emoji' export default function Likes({ event }: { event: Event }) { const { pubkey, checkLogin, publish } = useNostr() const noteStats = useNoteStatsById(event.id) const [liking, setLiking] = useState(null) const longPressTimerRef = useRef(null) const [isLongPressing, setIsLongPressing] = useState(null) const [isCompleted, setIsCompleted] = useState(null) const likes = useMemo(() => { const _likes = noteStats?.likes if (!_likes) return [] const stats = new Map }>() _likes.forEach((item) => { const key = typeof item.emoji === 'string' ? item.emoji : item.emoji.url if (!stats.has(key)) { stats.set(key, { key, pubkeys: new Set(), emoji: item.emoji }) } stats.get(key)?.pubkeys.add(item.pubkey) }) return Array.from(stats.values()).sort((a, b) => b.pubkeys.size - a.pubkeys.size) }, [noteStats, event]) if (!likes.length) return null const like = async (key: string, emoji: TEmoji | string) => { checkLogin(async () => { if (liking || !pubkey) return setLiking(key) const timer = setTimeout(() => setLiking((prev) => (prev === key ? null : prev)), 5000) try { const reaction = createReactionDraftEvent(event, emoji) const evt = await publish(reaction) noteStatsService.updateNoteStatsByEvents([evt]) } catch (error) { console.error('like failed', error) } finally { setLiking(null) clearTimeout(timer) } }) } const handleMouseDown = (key: string) => { if (pubkey && likes.find((l) => l.key === key)?.pubkeys.has(pubkey)) { return } setIsLongPressing(key) longPressTimerRef.current = setTimeout(() => { setIsCompleted(key) setIsLongPressing(null) }, 800) } const handleMouseUp = () => { if (longPressTimerRef.current) { clearTimeout(longPressTimerRef.current) longPressTimerRef.current = null } if (isCompleted) { const completedKey = isCompleted const completedEmoji = likes.find((l) => l.key === completedKey)?.emoji if (completedEmoji) { like(completedKey, completedEmoji) } } setIsLongPressing(null) setIsCompleted(null) } const handleMouseLeave = () => { if (longPressTimerRef.current) { clearTimeout(longPressTimerRef.current) longPressTimerRef.current = null } setIsLongPressing(null) setIsCompleted(null) } const handleTouchMove = (e: React.TouchEvent) => { const touch = e.touches[0] const rect = (e.currentTarget as HTMLElement).getBoundingClientRect() const isInside = touch.clientX >= rect.left && touch.clientX <= rect.right && touch.clientY >= rect.top && touch.clientY <= rect.bottom if (!isInside) { handleMouseLeave() } } return (
{likes.map(({ key, emoji, pubkeys }) => (
e.stopPropagation()} onMouseDown={() => handleMouseDown(key)} onMouseUp={handleMouseUp} onMouseLeave={handleMouseLeave} onTouchStart={() => handleMouseDown(key)} onTouchMove={handleTouchMove} onTouchEnd={handleMouseUp} onTouchCancel={handleMouseLeave} > {(isLongPressing === key || isCompleted === key) && (
)}
{liking === key ? ( ) : (
)}
{pubkeys.size}
))}
) }