From eb5515fc1b39362d36f074c914e0118f96feefde Mon Sep 17 00:00:00 2001 From: Koala <6156589+Shik3i@users.noreply.github.com> Date: Mon, 25 May 2026 12:26:21 +0200 Subject: [PATCH] fix(extension): ensureState timeout guard, skip queued LEAVE_ROOM, persist onclose state, cleanup forceSync timer - Add 10s timeout to ensureState() so extension doesn't hang forever if chrome.storage.session.get() never calls back (storage API failure) - Skip emit(LEAVE_ROOM) in leaveOldRoomIfSwitching when socket is down; server already cleaned up via disconnect handler, avoid queued no-op - Persist cleared peers to storage in socket.onclose to prevent stale peer list restoration on service worker restart - Store and clean up forceSyncReset setTimeout in popup unload handler and when force_sync_execute completes --- extension/background.js | 17 +++++++++++++++-- extension/popup.js | 12 ++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/extension/background.js b/extension/background.js index d448bd7..25e555a 100644 --- a/extension/background.js +++ b/extension/background.js @@ -22,12 +22,22 @@ let restorationTask = null; function ensureState() { if (!restorationTask) { restorationTask = new Promise(resolve => { + let resolved = false; + const done = () => { if (!resolved) { resolved = true; resolve(); } }; + + const storageTimeout = setTimeout(() => { + addLog('Storage restoration timed out, continuing with defaults', 'warn'); + storageInitialized = true; + done(); + }, 10000); + chrome.storage.session.get([ 'logs', 'history', 'currentRoom', 'lastActionState', 'eventQueue', 'isForceSyncInitiator', 'forceSyncAcks', 'forceSyncDeadline', 'reconnectFailed', 'reconnectStartTime', 'currentTabId', 'currentTabTitle', 'episodeLobby' ], (data) => { + clearTimeout(storageTimeout); if (data.currentTabId !== undefined) currentTabId = data.currentTabId; if (data.currentTabTitle !== undefined) currentTabTitle = data.currentTabTitle; // Merge data from storage with any early-arriving state @@ -88,7 +98,7 @@ function ensureState() { pendingHistory = []; } - resolve(); + done(); }); }); } @@ -369,6 +379,7 @@ async function connect() { if (currentRoom) { currentRoom.peers = []; + if (storageInitialized) chrome.storage.session.set({ currentRoom }); chrome.runtime.sendMessage({ type: 'PEER_UPDATE', peers: [] }).catch(() => {}); } broadcastConnectionStatus('disconnected'); @@ -959,7 +970,9 @@ chrome.alarms.onAlarm.addListener(async (alarm) => { function leaveOldRoomIfSwitching(newRoomId) { if (currentRoom && currentRoom.roomId !== newRoomId) { addLog(`Switching rooms: leaving ${currentRoom.roomId} to join ${newRoomId}`, 'info'); - emit(EVENTS.LEAVE_ROOM, { peerId }); + if (socket && socket.readyState === WebSocket.OPEN && isNamespaceJoined) { + emit(EVENTS.LEAVE_ROOM, { peerId }); + } currentRoom = null; if (storageInitialized) chrome.storage.session.set({ currentRoom: null }); chrome.runtime.sendMessage({ type: 'PEER_UPDATE', peers: [] }).catch(() => {}); diff --git a/extension/popup.js b/extension/popup.js index fe58668..fdbad03 100644 --- a/extension/popup.js +++ b/extension/popup.js @@ -54,6 +54,7 @@ let lastPeersJson = null; let lastKnownPeers = []; let isDevTabVisible = false; let joinBtnTimeout = null; +let forceSyncResetTimer = null; let popupIntervals = []; let populateTabsToken = null; let forceSyncDone = false; @@ -974,8 +975,7 @@ elements.forceSyncBtn.addEventListener('click', async () => { elements.forceSyncBtn.textContent = originalText; } }; - setTimeout(forceSyncReset, 12000); - + forceSyncResetTimer = setTimeout(forceSyncReset, 12000); const tabId = parseInt(status.targetTabId); const sendForceSync = (time) => { @@ -1096,6 +1096,10 @@ chrome.runtime.onMessage.addListener((msg) => { } if (state && state.action === 'force_sync_execute') { forceSyncDone = true; + if (forceSyncResetTimer) { + clearTimeout(forceSyncResetTimer); + forceSyncResetTimer = null; + } if (elements.forceSyncBtn) { elements.forceSyncBtn.disabled = false; elements.forceSyncBtn.textContent = '⚡ Force Sync'; @@ -1259,6 +1263,10 @@ window.addEventListener('unload', () => { clearTimeout(joinBtnTimeout); joinBtnTimeout = null; } + if (forceSyncResetTimer) { + clearTimeout(forceSyncResetTimer); + forceSyncResetTimer = null; + } }); // --- Episode Lobby UI ---