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
This commit is contained in:
Koala
2026-05-25 12:26:21 +02:00
parent c621685aae
commit eb5515fc1b
2 changed files with 25 additions and 4 deletions
+15 -2
View File
@@ -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(() => {});
+10 -2
View File
@@ -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 ---