diff --git a/src/components/NostrProvider.tsx b/src/components/NostrProvider.tsx index bace358..b4a7aae 100644 --- a/src/components/NostrProvider.tsx +++ b/src/components/NostrProvider.tsx @@ -103,7 +103,6 @@ const NostrProvider: React.FC = (props) => { } // Intentionally run once on mount; relay changes are handled by the // invalidation effects above and lazy-open on demand. - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Initialize NPool only once diff --git a/src/lib/citrine.ts b/src/lib/citrine.ts index 6c6ad4d..20c55df 100644 --- a/src/lib/citrine.ts +++ b/src/lib/citrine.ts @@ -132,12 +132,12 @@ export function probeCitrine( let settled = false; let opened = false; let ws: WebSocket | undefined; - let timer: ReturnType | undefined; + const timers: ReturnType[] = []; const done = (ok: boolean, reason: string) => { if (settled) return; settled = true; - if (timer) clearTimeout(timer); + for (const t of timers) clearTimeout(t); try { ws?.close(); } catch { @@ -160,10 +160,10 @@ export function probeCitrine( const subId = `citrine-probe-${Math.random().toString(36).slice(2, 10)}`; - timer = setTimeout(() => { + timers.push(setTimeout(() => { // Distinguish "never opened" (blocked/refused) from "opened but silent". done(false, opened ? 'opened-but-no-nostr-reply' : 'timeout-no-open'); - }, timeoutMs); + }, timeoutMs)); ws.onopen = () => { opened = true; diff --git a/src/lib/relayProbe.ts b/src/lib/relayProbe.ts index 3925265..89c45d3 100644 --- a/src/lib/relayProbe.ts +++ b/src/lib/relayProbe.ts @@ -29,12 +29,12 @@ export function probeRelay(url: string, timeoutMs = 4000): Promise | undefined; + const timers: ReturnType[] = []; const done = (ok: boolean, reason: string) => { if (settled) return; settled = true; - if (timer) clearTimeout(timer); + for (const t of timers) clearTimeout(t); try { ws?.close(); } catch { @@ -55,9 +55,9 @@ export function probeRelay(url: string, timeoutMs = 4000): Promise { + timers.push(setTimeout(() => { done(false, opened ? 'opened-but-no-nostr-reply' : 'timeout-no-open'); - }, timeoutMs); + }, timeoutMs)); ws.onopen = () => { opened = true; diff --git a/src/pages/MapPage.tsx b/src/pages/MapPage.tsx index 639c7ef..e404cfb 100644 --- a/src/pages/MapPage.tsx +++ b/src/pages/MapPage.tsx @@ -813,7 +813,6 @@ export default function MapPage() { return () => { cancelled = true; }; - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Combine spots and shops into unified list for map display