128 lines
3.0 KiB
JavaScript
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);
|
|
});
|