Initial commit: SyncWatch Chrome extension + WebSocket server
This commit is contained in:
127
extension/background.js
Normal file
127
extension/background.js
Normal file
@@ -0,0 +1,127 @@
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user