From 1c404a7f118bd0f34f6878543e58a6cb8d35abc7 Mon Sep 17 00:00:00 2001 From: Kaia-Alenia Date: Fri, 19 Jun 2026 14:11:47 -0600 Subject: [PATCH] refactor(extension): extract tab management logic into independent module --- extension/background.js | 83 ++++++------------------- extension/modules/tab-manager.js | 94 +++++++++++++++++++++++++++++ website/assets/NewLogoIcon_64.webp | Bin 1282 -> 1282 bytes 3 files changed, 111 insertions(+), 66 deletions(-) create mode 100644 extension/modules/tab-manager.js diff --git a/extension/background.js b/extension/background.js index 6e13bf8..14abb73 100644 --- a/extension/background.js +++ b/extension/background.js @@ -2,6 +2,7 @@ import { EVENTS, PROTOCOL_VERSION, OFFICIAL_SERVER_URL, OFFICIAL_SERVER_TOKEN, E import { generateUsername } from './shared/names.js'; import { loadLocale, getMessage, getSystemLanguage } from './i18n.js'; import { sameEpisode } from './episode-utils.js'; +import { initTabManager } from './modules/tab-manager.js'; // --- Uninstall URL Initialization --- chrome.runtime.onInstalled.addListener((details) => { @@ -1899,72 +1900,22 @@ async function handleAsyncMessage(message, sender, sendResponse) { } } -chrome.storage.onChanged.addListener(async (changes, area) => { - if (area !== 'local' || !changes.audioSettings) return; - await ensureState(); - if (!currentTabId) return; - - chrome.tabs.sendMessage(currentTabId, { - action: 'APPLY_AUDIO_SETTINGS', - settings: changes.audioSettings.newValue - }).catch(() => {}); -}); - -// Tab removal listener -chrome.tabs.onRemoved.addListener(async (tabId) => { - await ensureState(); - if (tabId === currentTabId) { - const wasInRoom = !!currentRoom; - currentTabId = null; - currentTabTitle = null; - lastContentHeartbeatAt = null; - roomIdleSince = Date.now(); - chrome.storage.session.set({ currentTabId: null, currentTabTitle: null, roomIdleSince, lastContentHeartbeatAt }); - updateBadgeStatus(); - addLog('Target tab closed.', 'warn'); - - if (wasInRoom) { - const roomAtClose = currentRoom; - getSettings().then(settings => { - if (currentRoom !== roomAtClose) return; - - emit(EVENTS.PEER_STATUS, { - peerId, - playbackState: 'paused', - currentTime: null, - mediaTitle: null, - username: settings.username, - tabTitle: null - }); - - if (currentRoom && Array.isArray(currentRoom.peers)) { - const me = currentRoom.peers.find(p => (p.peerId || p) === peerId); - if (me && typeof me === 'object') { - me.playbackState = 'paused'; - me.currentTime = null; - me.mediaTitle = null; - me.tabTitle = null; - me.lastHeartbeat = Date.now(); - if (storageInitialized) chrome.storage.session.set({ currentRoom }); - chrome.runtime.sendMessage({ type: 'PEER_UPDATE', peers: currentRoom.peers }).catch(() => {}); - } - } - }).catch(() => {}); - } - } -}); - -// Re-inject on full page refresh -chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, _tab) => { - await ensureState(); - if (currentTabId && tabId === parseInt(currentTabId) && changeInfo.status === 'complete') { - chrome.scripting.executeScript({ - target: { tabId }, - files: ['content.js'] - }) - .then(() => applyAudioSettingsToTab(tabId)) - .catch(() => {}); - } +initTabManager({ + getCurrentTabId: () => currentTabId, + setCurrentTabId: (val) => { currentTabId = val; }, + setCurrentTabTitle: (val) => { currentTabTitle = val; }, + setLastContentHeartbeatAt: (val) => { lastContentHeartbeatAt = val; }, + setRoomIdleSince: (val) => { roomIdleSince = val; }, + getCurrentRoom: () => currentRoom, + getPeerId: () => peerId, + getStorageInitialized: () => storageInitialized, + updateBadgeStatus, + addLog, + getSettings, + emit, + applyAudioSettingsToTab, + ensureState, + EVENTS }); // Initial Connect — only if user has an active room configuration diff --git a/extension/modules/tab-manager.js b/extension/modules/tab-manager.js new file mode 100644 index 0000000..063a7be --- /dev/null +++ b/extension/modules/tab-manager.js @@ -0,0 +1,94 @@ +export function initTabManager({ + getCurrentTabId, + setCurrentTabId, + setCurrentTabTitle, + setLastContentHeartbeatAt, + setRoomIdleSince, + getCurrentRoom, + getPeerId, + getStorageInitialized, + updateBadgeStatus, + addLog, + getSettings, + emit, + applyAudioSettingsToTab, + ensureState, + EVENTS +}) { + chrome.storage.onChanged.addListener(async (changes, area) => { + if (area !== 'local' || !changes.audioSettings) return; + await ensureState(); + const tabId = getCurrentTabId(); + if (!tabId) return; + + chrome.tabs.sendMessage(tabId, { + action: 'APPLY_AUDIO_SETTINGS', + settings: changes.audioSettings.newValue + }).catch(() => {}); + }); + + chrome.tabs.onRemoved.addListener(async (tabId) => { + await ensureState(); + if (tabId === getCurrentTabId()) { + const wasInRoom = !!getCurrentRoom(); + setCurrentTabId(null); + setCurrentTabTitle(null); + setLastContentHeartbeatAt(null); + const now = Date.now(); + setRoomIdleSince(now); + chrome.storage.session.set({ + currentTabId: null, + currentTabTitle: null, + roomIdleSince: now, + lastContentHeartbeatAt: null + }); + updateBadgeStatus(); + addLog('Target tab closed.', 'warn'); + + if (wasInRoom) { + const roomAtClose = getCurrentRoom(); + getSettings().then(settings => { + if (getCurrentRoom() !== roomAtClose) return; + + emit(EVENTS.PEER_STATUS, { + peerId: getPeerId(), + playbackState: 'paused', + currentTime: null, + mediaTitle: null, + username: settings.username, + tabTitle: null + }); + + const room = getCurrentRoom(); + if (room && Array.isArray(room.peers)) { + const me = room.peers.find(p => (p.peerId || p) === getPeerId()); + if (me && typeof me === 'object') { + me.playbackState = 'paused'; + me.currentTime = null; + me.mediaTitle = null; + me.tabTitle = null; + me.lastHeartbeat = Date.now(); + if (getStorageInitialized()) { + chrome.storage.session.set({ currentRoom: room }); + } + chrome.runtime.sendMessage({ type: 'PEER_UPDATE', peers: room.peers }).catch(() => {}); + } + } + }).catch(() => {}); + } + } + }); + + chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, _tab) => { + await ensureState(); + const curTabId = getCurrentTabId(); + if (curTabId && tabId === parseInt(curTabId) && changeInfo.status === 'complete') { + chrome.scripting.executeScript({ + target: { tabId }, + files: ['content.js'] + }) + .then(() => applyAudioSettingsToTab(tabId)) + .catch(() => {}); + } + }); +} diff --git a/website/assets/NewLogoIcon_64.webp b/website/assets/NewLogoIcon_64.webp index 660ffe28987821948744dafb8543121d3c9924ee..b1f4e43ede2c8f7304360593b419303eb296c0d2 100644 GIT binary patch delta 175 zcmV;g08sye3W5r-j{*uP3wTE6hMStHV6&M5(*b{}TcN{oo+V6*L_a%Yrc!p2D?t#c zOKs7r7~J4m$Dv1=mJaDq77GQQQ>rw7XYD@K>HoHNx9RHcyI5?-@+?sg|AU>&-G4md zD$=Kt1fjo2eO@GCUGUCe6T6+N5TM#$rGCu%%0C_&{K4-ZrketIuEE5-AGFN%Jt!V_ dA-)%z8o>$A#3xK?o>SG_`v#;TD2(v9006+KRrvq_ delta 175 zcmV;g08sye3W5r-j{*uRbh!h)p{C}lShJY|(*b{`0tbHB-=W1lvs8JmTg?5P$6NU| zO#PNrF}c9Ck3x?#EFIFIEEWqqr&MVE&)R*f)BkMkZ`0M?cCguv