import { useQuery } from '@tanstack/react-query'; import { useNostr } from '@nostrify/react'; import { verifyNip05 } from '@/lib/nip05'; /** * Batch-verify NIP-05 for a set of skater pubkeys. * * NIP-05 is a *soft* trust signal, never a gate — the whole directory stays open * to everyone. This hook simply fetches each skater's kind-0 metadata, then * confirms their `nip05` handle actually resolves to their own pubkey via the * domain's `.well-known/nostr.json`. The result powers the "Verified only" * filter and the verified badge, letting people *opt in* to a higher-trust view * without excluding anyone. * * Returns a Set of pubkeys that are genuinely NIP-05 verified. */ export function useSkaterVerifications(pubkeys: string[]) { const { nostr } = useNostr(); // Stable key so the query only refires when the actual set changes. const sortedKey = [...pubkeys].sort().join(','); return useQuery({ queryKey: ['skater-verifications', sortedKey], enabled: pubkeys.length > 0, staleTime: 10 * 60 * 1000, // 10 min — verification rarely changes gcTime: 30 * 60 * 1000, queryFn: async (c): Promise> => { const verified = new Set(); if (pubkeys.length === 0) return verified; const signal = AbortSignal.any([c.signal, AbortSignal.timeout(12000)]); // 1. Fetch kind-0 metadata for all skaters in one query. const profiles = await nostr.query([{ kinds: [0], authors: pubkeys }], { signal }); // Map pubkey -> nip05 handle (newest metadata wins). const nip05ByPubkey = new Map(); const seenAt = new Map(); for (const ev of profiles) { const prev = seenAt.get(ev.pubkey) ?? -1; if (ev.created_at <= prev) continue; seenAt.set(ev.pubkey, ev.created_at); try { const meta = JSON.parse(ev.content) as { nip05?: string }; if (meta.nip05) nip05ByPubkey.set(ev.pubkey, meta.nip05); else nip05ByPubkey.delete(ev.pubkey); } catch { // ignore malformed metadata } } // 2. Verify each handle against its own pubkey, in parallel. const entries = [...nip05ByPubkey.entries()]; const results = await Promise.all( entries.map(async ([pubkey, nip05]) => { const res = await verifyNip05(nip05, pubkey, signal).catch(() => ({ status: 'unreachable' as const })); return { pubkey, ok: res.status === 'verified' }; }), ); for (const { pubkey, ok } of results) { if (ok) verified.add(pubkey); } return verified; }, }); }