import { Link } from 'react-router-dom'; import { formatDistanceToNow } from 'date-fns'; import { Heart, Repeat2, Zap, AtSign, MessageCircle, CalendarCheck, Megaphone } from 'lucide-react'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { useAuthor } from '@/hooks/useAuthor'; import { genUserName } from '@/lib/genUserName'; import { cn } from '@/lib/utils'; import { nip19 } from 'nostr-tools'; import { activityVerb, type ActivityNotification, type ActivityType, } from '@/hooks/useActivityNotifications'; const TYPE_META: Record< ActivityType, { icon: typeof Heart; color: string; ring: string } > = { reaction: { icon: Heart, color: 'text-pink-400', ring: 'bg-pink-500/15' }, repost: { icon: Repeat2, color: 'text-green-400', ring: 'bg-green-500/15' }, zap: { icon: Zap, color: 'text-yellow-400', ring: 'bg-yellow-500/15' }, mention: { icon: AtSign, color: 'text-sky-400', ring: 'bg-sky-500/15' }, reply: { icon: MessageCircle, color: 'text-teal-400', ring: 'bg-teal-500/15' }, rsvp: { icon: CalendarCheck, color: 'text-emerald-400', ring: 'bg-emerald-500/15' }, 'event-update': { icon: Megaphone, color: 'text-[#ccff00]', ring: 'bg-[#ccff00]/15' }, }; /** * A single "social" activity notification (reaction / repost / zap / mention / * reply). Links to the actor's profile and — when there's a target event — to * that note so the user can jump straight to the context. */ export function ActivityNotificationCard({ notification, unread, onNavigate, }: { notification: ActivityNotification; unread?: boolean; onNavigate?: () => void; }) { const author = useAuthor(notification.pubkey); const name = author.data?.metadata?.name ?? genUserName(notification.pubkey); const pic = author.data?.metadata?.picture; const meta = TYPE_META[notification.type]; const Icon = meta.icon; const npub = (() => { try { return nip19.npubEncode(notification.pubkey); } catch { return null; } })(); const timeAgo = formatDistanceToNow(new Date(notification.createdAt * 1000), { addSuffix: true, }); const snippet = (notification.type === 'reply' || notification.type === 'mention' || notification.type === 'event-update') && notification.content ? notification.content.slice(0, 120) : null; const body = (
{name}{' '} {activityVerb(notification)}
{snippet && ("{snippet}"
)}{timeAgo}