Files
syncwatch/extension/background.js

128 lines
3.0 KiB
JavaScript

let ws = null;
let roomCode = null;
let serverUrl = null;
let connectState = 'disconnected'; // 'disconnected' | 'connecting' | 'connected'
function send(msg) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(msg));
}
}
function notifyPopup(msg) {
chrome.runtime.sendMessage(msg).catch(() => {});
}
function notifyAllTabs(msg) {
chrome.tabs.query({}, (tabs) => {
for (const tab of tabs) {
chrome.tabs.sendMessage(tab.id, msg).catch(() => {});
}
});
}
function connect(url) {
if (ws) {
ws.close();
ws = null;
}
serverUrl = url;
connectState = 'connecting';
try {
ws = new WebSocket(url);
} catch {
connectState = 'disconnected';
notifyPopup({ type: 'ws_status', status: 'error' });
return;
}
ws.onopen = () => {
connectState = 'connected';
notifyPopup({ type: 'ws_status', status: 'connected' });
};
ws.onclose = () => {
connectState = 'disconnected';
roomCode = null;
notifyPopup({ type: 'ws_status', status: 'disconnected' });
};
ws.onerror = () => {
connectState = 'disconnected';
notifyPopup({ type: 'ws_status', status: 'error' });
};
ws.onmessage = (event) => {
let msg;
try { msg = JSON.parse(event.data); } catch { return; }
switch (msg.type) {
case 'created':
case 'joined':
roomCode = msg.code;
notifyPopup(msg);
break;
case 'error':
case 'peer_joined':
case 'peer_left':
notifyPopup(msg);
break;
case 'sync':
notifyAllTabs({ type: 'apply_sync', action: msg.action, time: msg.time });
break;
}
};
}
// Keep service worker alive and reconnect if needed
chrome.alarms.create('keepalive', { periodInMinutes: 0.4 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name !== 'keepalive') return;
if (ws && ws.readyState === WebSocket.OPEN) {
send({ type: 'ping' });
} else if (serverUrl && connectState !== 'connecting') {
connect(serverUrl);
}
});
chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
switch (msg.type) {
case 'connect':
chrome.storage.local.set({ serverUrl: msg.url });
connect(msg.url);
sendResponse({ ok: true });
break;
case 'disconnect':
serverUrl = null;
roomCode = null;
if (ws) ws.close();
sendResponse({ ok: true });
break;
case 'create_room':
send({ type: 'create' });
sendResponse({ ok: true });
break;
case 'join_room':
send({ type: 'join', code: msg.code });
sendResponse({ ok: true });
break;
case 'media_event':
if (roomCode) {
send({ type: 'sync', action: msg.action, time: msg.time });
}
sendResponse({ ok: true });
break;
case 'get_state':
sendResponse({ connectState, roomCode });
break;
}
return true;
});
// Restore WS connection on service worker restart
chrome.storage.local.get(['serverUrl'], ({ serverUrl: saved }) => {
if (saved) connect(saved);
});