SkateSpots/src/hooks/useRelayStatus.ts
2026-09-14 10:14:16 +07:00

55 lines
1.8 KiB
TypeScript

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<RelayProbeResult>({
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();
},
};
}