From ef86d858c253f120882ff78ef36c6169eb3afeb4 Mon Sep 17 00:00:00 2001 From: "shakespeare.diy" Date: Thu, 24 Sep 2026 19:55:38 +0700 Subject: [PATCH] fix: make relay-probe timers and effect hooks lint-clean across ESLint versions - Replace single-assignment `let timer` with a `timers[]` array in citrine.ts and relayProbe.ts so stricter prefer-const builds pass (the newer ESLint pulled in by `npm audit fix` flagged the let as never-reassigned). - Drop two now-redundant `eslint-disable react-hooks/exhaustive-deps` directives in NostrProvider and MapPage. Newer react-hooks no longer reports those lines, so the directives were flagged as unused errors under reportUnusedDisableDirectives; removing them is safe in both old (warning only) and new (silent) ESLint. Co-authored-by: shakespeare.diy --- src/components/NostrProvider.tsx | 1 - src/lib/citrine.ts | 8 ++++---- src/lib/relayProbe.ts | 8 ++++---- src/pages/MapPage.tsx | 1 - 4 files changed, 8 insertions(+), 10 deletions(-) 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