const Cloud = { status: null, socket: null, reconnectTimeout: null, reconnectAttempts: 0, reconnecting: false, start() { Cloud.status = 'sync'; const uuid = localStorage.getItem("uuid"); if (Cloud.socket && Cloud.socket.readyState <= 1) return; const ws = new WebSocket(CONFIG.wss, uuid); Cloud.socket = ws; ws.onopen = () => { console.log("[WebSocket] З'єднання встановлено"); Cloud.status = 'ok'; ws.send(JSON.stringify({ event: 'connection', user: { name: USER.name, id: USER.id } })); if (Cloud.reconnecting == true) { Cloud.reconnect(); } Cloud.reconnecting = true; Cloud.reconnectAttempts = 0; clearTimeout(Cloud.reconnectTimeout); }; ws.onmessage = (e) => { const data = JSON.parse(e.data); if (data.event === 'user_connected' && data.user.id !== USER.id) { console.log(`[WebSocket] Новий користувач: ${data.user.name}`); } if (data.event === 'message') { switch (data.type) { case "apartment": Territory_card.cloud.update(data); break; case "building": Territory_card.cloud.update(data); break; case "stand_locking": case "stand_unlocking": case "stand_update": Stand_card.cloud.update(data); break; default: break; } } }; ws.onclose = () => { console.warn("[WebSocket] З'єднання розірвано"); Cloud.status = 'err'; if (!Cloud.reconnecting) return; // захист від дублювання if (Cloud.reconnectAttempts < 5) { Cloud.reconnectAttempts++; console.log(`[WebSocket] Спроба перепідключення ${Cloud.reconnectAttempts}/5`); Cloud.reconnectTimeout = setTimeout(() => { Cloud.start(); }, 500); } else { Cloud.reconnecting = false; Notifier.click({ title: `З'єднання розірвано!`, text: `Натисніть, щоб перепідключитись!` }, { type: 'warn', f: () => { Cloud.reconnecting = true; Cloud.reconnectAttempts = 0; Cloud.start(); }, timeout: 0 }); } }; ws.onerror = (err) => { console.error("[WebSocket] Помилка", err); Cloud.status = 'err'; ws.close(); }; }, async reconnect(){ switch (page) { case "Territory_card": Territory_card.reload(); break; default: Router.navigate(location.pathname); break; } } }