diff --git a/extension/background.js b/extension/background.js index d213403..f44df41 100644 --- a/extension/background.js +++ b/extension/background.js @@ -12,6 +12,8 @@ let currentTabTitle = null; // New: for Smart Matching let logs = []; let history = []; // New: for Action History let reconnectTimer = null; +let reconnectStartTime = null; // New: track when reconnection started +let reconnectFailed = false; // New: true if we hit the 5-min cap // Force Sync Coordination let isForceSyncInitiator = false; @@ -58,8 +60,11 @@ async function connect() { if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) return; if (!navigator.onLine) { addLog('Browser is offline. Waiting...', 'warn'); + broadcastConnectionStatus('offline'); return; } + + if (reconnectFailed) return; // Wait for manual retry if (!peerId) peerId = await getPeerId(); const settings = await getSettings(); @@ -110,6 +115,8 @@ async function connect() { reconnectDelay = 1000; addLog('WebSocket Connection Opened', 'success'); broadcastConnectionStatus('connected'); + reconnectStartTime = null; + reconnectFailed = false; // Socket.IO Handshake: Send "40" to join default namespace socket.send('40'); @@ -180,7 +187,15 @@ function broadcastConnectionStatus(status) { } function updateBadgeStatus() { - if (currentTabId) { + const status = socket ? (socket.readyState === WebSocket.OPEN ? 'connected' : (isConnecting || socket.readyState === WebSocket.CONNECTING ? 'connecting' : 'disconnected')) : 'disconnected'; + + if (reconnectFailed) { + chrome.action.setBadgeText({ text: 'ERR' }); + chrome.action.setBadgeBackgroundColor({ color: '#ef4444' }); + } else if (status === 'connecting') { + chrome.action.setBadgeText({ text: '...' }); + chrome.action.setBadgeBackgroundColor({ color: '#fbbf24' }); + } else if (currentTabId) { chrome.action.setBadgeText({ text: 'ON' }); chrome.action.setBadgeBackgroundColor({ color: '#22c55e' }); } else { @@ -204,7 +219,17 @@ function showNotification(senderName, action) { } function scheduleReconnect() { - if (reconnectTimer) return; // Already scheduled + if (reconnectTimer || reconnectFailed) return; + + if (!reconnectStartTime) reconnectStartTime = Date.now(); + + // Check 5 minute cap (300,000ms) + if (Date.now() - reconnectStartTime > 300000) { + reconnectFailed = true; + addLog('Reconnection failed after 5 minutes. Please try again manually.', 'error'); + broadcastConnectionStatus('reconnect_failed'); + return; + } reconnectTimer = setTimeout(() => { reconnectTimer = null; @@ -378,9 +403,17 @@ chrome.alarms.onAlarm.addListener((alarm) => { // --- Extension Message Listeners --- chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === 'CONNECT') { + reconnectFailed = false; + reconnectStartTime = null; + connect(); + } else if (message.type === 'RETRY_CONNECT') { + reconnectFailed = false; + reconnectStartTime = null; + reconnectDelay = 1000; connect(); } else if (message.type === 'GET_STATUS') { - const status = socket ? (socket.readyState === WebSocket.OPEN ? 'connected' : (isConnecting || socket.readyState === WebSocket.CONNECTING ? 'connecting' : 'disconnected')) : 'disconnected'; + let status = socket ? (socket.readyState === WebSocket.OPEN ? 'connected' : (isConnecting || socket.readyState === WebSocket.CONNECTING ? 'connecting' : 'disconnected')) : 'disconnected'; + if (reconnectFailed) status = 'reconnect_failed'; sendResponse({ status, peerId, peers: currentRoom ? currentRoom.peers : [] }); // Global return true at the end handles this } else if (message.type === 'LEAVE_ROOM') { diff --git a/extension/popup.html b/extension/popup.html index 3b657b6..8015819 100644 --- a/extension/popup.html +++ b/extension/popup.html @@ -277,9 +277,10 @@ -
+
Disconnected +
diff --git a/extension/popup.js b/extension/popup.js index 39ed01a..eeb971d 100644 --- a/extension/popup.js +++ b/extension/popup.js @@ -35,7 +35,8 @@ const elements = { createRoomBtn: document.getElementById('createRoomBtn'), publicRooms: document.getElementById('publicRooms'), refreshRooms: document.getElementById('refreshRooms'), - roomError: document.getElementById('roomError') + roomError: document.getElementById('roomError'), + retryBtn: document.getElementById('retryBtn') }; let localPeerId = null; @@ -183,8 +184,17 @@ async function populateTabs(providedPeers = null) { function applyConnectionStatus(status) { const connected = status === 'connected'; const connecting = status === 'connecting'; - elements.connDot.className = 'status-dot ' + (connected ? 'status-online' : 'status-offline'); - elements.connText.textContent = connected ? 'Connected' : (connecting ? 'Connecting...' : 'Disconnected'); + const failed = status === 'reconnect_failed'; + + elements.connDot.className = 'status-dot ' + (connected ? 'status-online' : (failed ? 'status-offline' : (connecting ? 'status-online' : 'status-offline'))); + + // Custom colors for states + if (connecting) elements.connDot.style.background = '#fbbf24'; + else if (failed) elements.connDot.style.background = '#ef4444'; + else elements.connDot.style.background = ''; + + elements.connText.textContent = connected ? 'Connected' : (connecting ? 'Connecting...' : (failed ? 'Failed' : 'Disconnected')); + elements.retryBtn.style.display = failed ? 'block' : 'none'; } function updateHistory(history) { @@ -349,6 +359,10 @@ elements.refreshRooms.addEventListener('click', () => { chrome.runtime.sendMessage({ type: 'GET_ROOM_LIST' }); }); +elements.retryBtn.addEventListener('click', () => { + chrome.runtime.sendMessage({ type: 'RETRY_CONNECT' }); +}); + elements.targetTab.addEventListener('change', async () => { await chrome.storage.sync.set({ targetTabId: elements.targetTab.value }); });