import { useMemo } from 'react'; import { useFollowing } from './useFollowing'; import { useTrustAssertions } from './useTrustAssertions'; import { useLocalStorage } from './useLocalStorage'; import { FLAGGED_REPORTERS_THRESHOLD } from '@/lib/trustLadder'; /** A muted-by-trusted-people count at/above this also raises an alert. */ const MUTERS_THRESHOLD = FLAGGED_REPORTERS_THRESHOLD; export interface NetworkAlert { pubkey: string; reporters: number; muters: number; followers?: number; hops?: number; } /** * Accounts the viewer follows that trusted people have reported or muted, * according to the active NIP-85 provider. * * This is a safety notice, not an automatic action: the page offers * ignore / unfollow / mute / report, and the user decides. Dismissed accounts * are remembered device-locally. */ export function useNetworkAlerts() { const { data: following } = useFollowing(); const { data: assertions, isLoading } = useTrustAssertions(following ?? []); const [ignored, setIgnored] = useLocalStorage('skatespots:trust:alerts-ignored', []); const alerts = useMemo(() => { const out: NetworkAlert[] = []; for (const pubkey of following ?? []) { if (ignored.includes(pubkey)) continue; const assertion = assertions?.get(pubkey); if (!assertion) continue; const reporters = assertion.reporters ?? 0; const muters = assertion.muters ?? 0; if (reporters >= FLAGGED_REPORTERS_THRESHOLD || muters >= MUTERS_THRESHOLD) { out.push({ pubkey, reporters, muters, followers: assertion.followers, hops: assertion.hops, }); } } // Strongest negative signal first. out.sort((a, b) => b.reporters + b.muters - (a.reporters + a.muters)); return out; }, [following, assertions, ignored]); return { alerts, alertCount: alerts.length, isLoading, ignore: (pubkey: string) => setIgnored((prev) => (prev.includes(pubkey) ? prev : [...prev, pubkey])), }; }