import { useMemo, useState } from 'react'; import { Link } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; import { ShieldCheck, Users } from 'lucide-react'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { useAuthor } from '@/hooks/useAuthor'; import { useBatchTrustScores } from '@/hooks/useBatchTrustScores'; import { genUserName } from '@/lib/genUserName'; import { cn } from '@/lib/utils'; import { useTranslation } from '@/contexts/language'; const TRUSTED_THRESHOLD = 60; function AttendeeRow({ pubkey, trusted }: { pubkey: string; trusted: boolean }) { const { t } = useTranslation(); const author = useAuthor(pubkey); const name = author.data?.metadata?.name ?? genUserName(pubkey); const pic = author.data?.metadata?.picture; const npub = (() => { try { return nip19.npubEncode(pubkey); } catch { return null; } })(); const inner = (
{name.slice(0, 2).toUpperCase()} {name} {trusted && ( {t('placesUi.attendees.trusted')} )}
); if (!npub) return inner; return {inner}; } /** * Full attendee list for a host: tabbed Going / Interested, each row showing a * face + name, plus a "trust signal" summarizing how many of the Going crowd * are people the viewer follows or trusted mappers (skate events live or die on * whether real locals show up). */ export function AttendeeList({ going, tentative, className, }: { going: string[]; tentative: string[]; className?: string; }) { const { t } = useTranslation(); const [tab, setTab] = useState<'going' | 'tentative'>('going'); const allPubkeys = useMemo(() => [...new Set([...going, ...tentative])], [going, tentative]); const { data: trustScores } = useBatchTrustScores(allPubkeys); const trustedGoing = useMemo( () => going.filter((pk) => (trustScores?.get(pk) ?? 0) >= TRUSTED_THRESHOLD).length, [going, trustScores], ); if (going.length === 0 && tentative.length === 0) { return (

{t('placesUi.attendees.noRsvps')}

); } const list = tab === 'going' ? going : tentative; return (
{/* Trust signal */} {going.length > 0 && (

{going.length} {t('placesUi.attendees.goingCount')} {trustedGoing > 0 && ( <> {' ยท '} {trustedGoing} {t('placesUi.attendees.youTrust')} )}

)} {/* Tabs */}
{/* List */} {list.length > 0 ? (
{list.map((pk) => ( = TRUSTED_THRESHOLD} /> ))}
) : (

{tab === 'going' ? t('placesUi.attendees.nobodyGoing') : t('placesUi.attendees.nobodyInterested')}

)}
); }