From b4a0b1c515f279f83d4503285dfb80642e067b3d Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Tue, 24 Feb 2026 20:45:47 -0800 Subject: [PATCH] Add refresh prompt after WS loss --- frontend/src/App.tsx | 6 ++++++ frontend/src/test/messageCache.test.ts | 1 - frontend/src/useWebSocket.ts | 7 ++++++- frontend/src/utils/radioPresets.ts | 1 - frontend/src/utils/visualizerUtils.ts | 4 +++- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e8b8b66..bf60bca 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -220,6 +220,12 @@ export function App() { description: success.details, }); }, + onReconnect: () => { + toast.warning('Unstable connection', { + description: 'Please refresh the page to ensure all messages are correctly loaded.', + duration: 10000, + }); + }, onMessage: (msg: Message) => { const activeConv = activeConversationRef.current; diff --git a/frontend/src/test/messageCache.test.ts b/frontend/src/test/messageCache.test.ts index 3dc9f65..a7be93a 100644 --- a/frontend/src/test/messageCache.test.ts +++ b/frontend/src/test/messageCache.test.ts @@ -433,5 +433,4 @@ describe('messageCache', () => { expect(messageCache.reconcile(current, fetched)).toBeNull(); }); }); - }); diff --git a/frontend/src/useWebSocket.ts b/frontend/src/useWebSocket.ts index 98b1d66..4a4128a 100644 --- a/frontend/src/useWebSocket.ts +++ b/frontend/src/useWebSocket.ts @@ -24,12 +24,14 @@ interface UseWebSocketOptions { onMessageAcked?: (messageId: number, ackCount: number, paths?: MessagePath[]) => void; onError?: (error: ErrorEvent) => void; onSuccess?: (success: SuccessEvent) => void; + onReconnect?: () => void; } export function useWebSocket(options: UseWebSocketOptions) { const wsRef = useRef(null); const reconnectTimeoutRef = useRef(null); const shouldReconnectRef = useRef(true); + const hasConnectedRef = useRef(false); // Store options in ref to avoid stale closures in WebSocket handlers. // The onmessage callback captures this ref, and we keep the ref updated @@ -61,6 +63,10 @@ export function useWebSocket(options: UseWebSocketOptions) { clearTimeout(reconnectTimeoutRef.current); reconnectTimeoutRef.current = null; } + if (hasConnectedRef.current) { + optionsRef.current.onReconnect?.(); + } + hasConnectedRef.current = true; }; ws.onclose = () => { @@ -156,5 +162,4 @@ export function useWebSocket(options: UseWebSocketOptions) { } }; }, [connect]); - } diff --git a/frontend/src/utils/radioPresets.ts b/frontend/src/utils/radioPresets.ts index fcdb597..636bdd1 100644 --- a/frontend/src/utils/radioPresets.ts +++ b/frontend/src/utils/radioPresets.ts @@ -24,4 +24,3 @@ export const RADIO_PRESETS: RadioPreset[] = [ { name: 'Portugal 868MHz', freq: 869.618, bw: 62.5, sf: 7, cr: 6 }, { name: 'Vietnam', freq: 920.25, bw: 250, sf: 11, cr: 5 }, ]; - diff --git a/frontend/src/utils/visualizerUtils.ts b/frontend/src/utils/visualizerUtils.ts index 8e60060..898a704 100644 --- a/frontend/src/utils/visualizerUtils.ts +++ b/frontend/src/utils/visualizerUtils.ts @@ -174,7 +174,9 @@ export function getPacketLabel(payloadType: number): PacketLabel { } export function generatePacketKey(parsed: ParsedPacket, rawPacket: RawPacket): string { - const contentHash = (parsed.messageHash || hashString(rawPacket.data).toString(16).padStart(8, '0')).slice(0, 8); + const contentHash = ( + parsed.messageHash || hashString(rawPacket.data).toString(16).padStart(8, '0') + ).slice(0, 8); if (parsed.payloadType === PayloadType.Advert && parsed.advertPubkey) { return `ad:${parsed.advertPubkey.slice(0, 12)}`;