From 44d6fcac24fb8623056de202d5885c53a7b199c6 Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Mon, 30 Mar 2026 21:15:42 -0700 Subject: [PATCH] Add missing abort controller --- frontend/src/api.ts | 6 ++++-- frontend/src/components/ContactInfoPane.tsx | 16 ++++++++-------- frontend/src/test/contactInfoPane.test.tsx | 5 ++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/frontend/src/api.ts b/frontend/src/api.ts index ace9789..8872cff 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -137,11 +137,13 @@ export const api = { fetchJson( `/contacts/repeaters/advert-paths?limit_per_repeater=${limitPerRepeater}` ), - getContactAnalytics: (params: { publicKey?: string; name?: string }) => { + getContactAnalytics: (params: { publicKey?: string; name?: string }, signal?: AbortSignal) => { const searchParams = new URLSearchParams(); if (params.publicKey) searchParams.set('public_key', params.publicKey); if (params.name) searchParams.set('name', params.name); - return fetchJson(`/contacts/analytics?${searchParams.toString()}`); + return fetchJson(`/contacts/analytics?${searchParams.toString()}`, { + signal, + }); }, deleteContact: (publicKey: string) => fetchJson<{ status: string }>(`/contacts/${publicKey}`, { diff --git a/frontend/src/components/ContactInfoPane.tsx b/frontend/src/components/ContactInfoPane.tsx index 59587ce..3f145fa 100644 --- a/frontend/src/components/ContactInfoPane.tsx +++ b/frontend/src/components/ContactInfoPane.tsx @@ -10,7 +10,7 @@ import { ResponsiveContainer, Legend, } from 'recharts'; -import { api } from '../api'; +import { api, isAbortError } from '../api'; import { formatTime } from '../utils/messageParser'; import { getContactDisplayName, @@ -110,29 +110,29 @@ export function ContactInfoPane({ return; } - let cancelled = false; + const controller = new AbortController(); setAnalytics(null); setLoading(true); const request = isNameOnly && nameOnlyValue - ? api.getContactAnalytics({ name: nameOnlyValue }) - : api.getContactAnalytics({ publicKey: contactKey }); + ? api.getContactAnalytics({ name: nameOnlyValue }, controller.signal) + : api.getContactAnalytics({ publicKey: contactKey }, controller.signal); request .then((data) => { - if (!cancelled) setAnalytics(data); + if (!controller.signal.aborted) setAnalytics(data); }) .catch((err) => { - if (!cancelled) { + if (!isAbortError(err)) { console.error('Failed to fetch contact analytics:', err); toast.error('Failed to load contact info'); } }) .finally(() => { - if (!cancelled) setLoading(false); + if (!controller.signal.aborted) setLoading(false); }); return () => { - cancelled = true; + controller.abort(); }; }, [contactKey, isNameOnly, nameOnlyValue]); diff --git a/frontend/src/test/contactInfoPane.test.tsx b/frontend/src/test/contactInfoPane.test.tsx index e518694..58df85c 100644 --- a/frontend/src/test/contactInfoPane.test.tsx +++ b/frontend/src/test/contactInfoPane.test.tsx @@ -181,7 +181,10 @@ describe('ContactInfoPane', () => { await screen.findByText('Mystery'); await waitFor(() => { - expect(getContactAnalytics).toHaveBeenCalledWith({ name: 'Mystery' }); + expect(getContactAnalytics).toHaveBeenCalledWith( + { name: 'Mystery' }, + expect.any(AbortSignal) + ); expect(screen.getByText('Messages')).toBeInTheDocument(); expect(screen.getByText('Channel Messages')).toBeInTheDocument(); expect(screen.getByText('4', { selector: 'p' })).toBeInTheDocument();