import { useQuery } from '@tanstack/react-query'; import { probeRelay, type RelayProbeResult } from '@/lib/relayProbe'; export type RelayStatus = 'connecting' | 'connected' | 'offline'; /** * useRelayStatus — live reachability of a single relay URL. * * Backed by react-query so every row that probes the same URL shares one result * and we don't open redundant sockets. Returns a coarse status the UI can map to * a coloured dot: * - 'connecting': first probe in flight (no result yet). * - 'connected': the relay answered a Nostr handshake. * - 'offline': the probe finished and the relay did not answer. * * Reconnected relays are picked up because we keep polling while offline, and * stop polling once connected to avoid churn (a healthy relay rarely vanishes * silently, and any real read failure elsewhere is a separate concern). */ export function useRelayStatus(url: string): { status: RelayStatus; probing: boolean; reason: string | null; recheck: () => void; } { const { data, isFetching, isFetched, refetch } = useQuery({ queryKey: ['relay-status', url], queryFn: () => probeRelay(url), // Consider a result fresh for a minute so switching sub-screens doesn't // re-probe every relay each time. staleTime: 60_000, // Poll only while still offline so a relay that comes back is detected; // once connected, stop the background churn. refetchInterval: (query) => (query.state.data?.ok ? false : 30_000), refetchOnWindowFocus: (query) => !query.state.data?.ok, retry: false, }); const status: RelayStatus = !isFetched ? 'connecting' : data?.ok ? 'connected' : isFetching ? 'connecting' : 'offline'; return { status, probing: isFetching, reason: data?.reason ?? null, recheck: () => { refetch(); }, }; }