initial commit
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* common.js – Stato condiviso tra i widget overlay via WebSocket
|
||||
*
|
||||
* Ogni widget si connette al server Express+WS e ascolta gli aggiornamenti.
|
||||
* Il pannello di controllo invia comandi/aggiornamenti, il server li
|
||||
* propaga a tutti i widget connessi.
|
||||
*/
|
||||
|
||||
// ---- Config ----
|
||||
const WS_URL = (() => {
|
||||
const loc = window.location;
|
||||
const protocol = loc.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
return `${protocol}//${loc.host}/ws`;
|
||||
})();
|
||||
|
||||
let ws = null;
|
||||
let currentState = {};
|
||||
let subscribers = [];
|
||||
let reconnectTimer = null;
|
||||
let connectAttempts = 0;
|
||||
|
||||
// ---- WebSocket connection ----
|
||||
function connect() {
|
||||
if (ws && (ws.readyState === 0 || ws.readyState === 1)) return;
|
||||
|
||||
ws = new WebSocket(WS_URL);
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('🟢 WS connected');
|
||||
connectAttempts = 0;
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
const msg = JSON.parse(event.data);
|
||||
if (msg.type === 'STATE_UPDATE' && msg.state) {
|
||||
currentState = msg.state;
|
||||
notifySubscribers();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('❌ WS message error:', err);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('🔴 WS disconnected, reconnecting...');
|
||||
scheduleReconnect();
|
||||
};
|
||||
|
||||
ws.onerror = (err) => {
|
||||
console.error('❌ WS error:', err);
|
||||
ws.close();
|
||||
};
|
||||
}
|
||||
|
||||
function scheduleReconnect() {
|
||||
if (reconnectTimer) clearTimeout(reconnectTimer);
|
||||
const delay = Math.min(1000 * 2 ** connectAttempts, 10000);
|
||||
connectAttempts++;
|
||||
reconnectTimer = setTimeout(connect, delay);
|
||||
}
|
||||
|
||||
// ---- Subscribe to state updates ----
|
||||
/**
|
||||
* Si iscrive agli aggiornamenti di stato.
|
||||
* @param {(state: object) => void} callback
|
||||
* @returns {() => void} unsubscribe function
|
||||
*/
|
||||
function onStateUpdate(callback) {
|
||||
subscribers.push(callback);
|
||||
// Chiamata immediata con stato corrente (se già presente)
|
||||
if (Object.keys(currentState).length > 0) {
|
||||
callback({ ...currentState });
|
||||
}
|
||||
return () => {
|
||||
subscribers = subscribers.filter((cb) => cb !== callback);
|
||||
};
|
||||
}
|
||||
|
||||
function notifySubscribers() {
|
||||
const state = { ...currentState };
|
||||
for (const cb of subscribers) {
|
||||
try { cb(state); } catch (e) { console.error(e); }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invia un comando al server (ADD_POINT, RESET_GAME, RESET_MATCH, SET_DEUCE, SET_SERVER)
|
||||
*/
|
||||
function sendCommand(command, player = null) {
|
||||
const msg = { type: 'COMMAND', command };
|
||||
if (player !== null) msg.player = player;
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invia un aggiornamento parziale di stato al server
|
||||
*/
|
||||
function sendStateUpdate(partial) {
|
||||
sendMessage({ type: 'STATE_UPDATE', state: partial });
|
||||
}
|
||||
|
||||
function sendMessage(msg) {
|
||||
if (ws && ws.readyState === 1) {
|
||||
ws.send(JSON.stringify(msg));
|
||||
} else {
|
||||
console.warn('⚠️ WS not connected, cannot send');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility: mappa punti → label tennis
|
||||
*/
|
||||
function pointLabel(state) {
|
||||
// Tiebreak: mostra punti numerici
|
||||
if (state.tiebreak) {
|
||||
return [String(state.tiebreakPoints[0]), String(state.tiebreakPoints[1])];
|
||||
}
|
||||
const p1 = state.points[0];
|
||||
const p2 = state.points[1];
|
||||
if (state.deuce) {
|
||||
if (state.advantage === 1) return ['AD', '40'];
|
||||
if (state.advantage === 2) return ['40', 'AD'];
|
||||
return ['40', '40'];
|
||||
}
|
||||
const map = ['0', '15', '30', '40'];
|
||||
return [map[p1] || '40', map[p2] || '40'];
|
||||
}
|
||||
|
||||
// ---- Auto-connect ----
|
||||
connect();
|
||||
Reference in New Issue
Block a user