refactor: enhance session management and room joining logic in WebSocket handling
All checks were successful
Build and Push Docker Image / docker (push) Successful in 9s

This commit is contained in:
2025-09-04 18:22:30 +02:00
parent a63c5858f7
commit 33aa410c09
8 changed files with 199 additions and 18 deletions

View File

@@ -45,3 +45,13 @@ export function applySync(startAt, serverNow) {
if (Math.abs($audio.playbackRate - 1) > 0.001) { $audio.playbackRate = 1.0; }
}
}
export function stopAudioPlayback() {
try { $audio.pause(); } catch {}
try { $audio.currentTime = 0; } catch {}
try { $audio.src = ''; } catch {}
try { $audio.playbackRate = 1.0; } catch {}
try { if ($recordDisc) $recordDisc.classList.remove('spin-record'); } catch {}
try { if ($progressFill) $progressFill.style.width = '0%'; } catch {}
try { if ($bufferBadge) $bufferBadge.classList.add('hidden'); } catch {}
}

View File

@@ -1,8 +1,8 @@
import { $audio, $copyRoomCode, $leaveRoom, $nameDisplay, $nameLobby, $npArtist, $npTitle, $npYear, $pauseBtn, $placeBtn, $readyChk, $roomId, $roomCode, $slotSelect, $startGame, $volumeSlider, $playBtn, $nextBtn, $createRoom, $joinRoom, $lobby, $room, $setNameLobby, $guessTitle, $guessArtist, $answerResult } from './dom.js';
import { state } from './state.js';
import { connectWS, sendMsg } from './ws.js';
import { connectWS, sendMsg, cacheSessionId, cacheLastRoomId } from './ws.js';
import { renderRoom } from './render.js';
import { initAudioUI, applySync } from './audio.js';
import { initAudioUI, applySync, stopAudioPlayback } from './audio.js';
function showToast(msg) {
const el = document.getElementById('toast');
@@ -18,6 +18,9 @@ function showToast(msg) {
function handleConnected(msg) {
state.playerId = msg.playerId;
if (msg.sessionId) {
cacheSessionId(msg.sessionId);
}
const stored = localStorage.getItem('playerName');
if (stored) {
if ($nameLobby && $nameLobby.value !== stored) {
@@ -28,12 +31,14 @@ function handleConnected(msg) {
}
sendMsg({ type: 'set_name', name: stored });
}
if (state.room?.id) {
sendMsg({ type: 'join_room', code: state.room.id });
const last = state.room?.id || localStorage.getItem('lastRoomId');
if (last && !localStorage.getItem('sessionId')) {
sendMsg({ type: 'join_room', code: last });
}
}
function handleRoomUpdate(msg) {
if (msg?.room?.id) cacheLastRoomId(msg.room.id);
renderRoom(msg.room);
}
@@ -134,6 +139,16 @@ function handleGameEnded(msg) {
function onMessage(ev) {
const msg = JSON.parse(ev.data);
switch (msg.type) {
case 'resume_result':
if (msg.ok) {
if (msg.playerId) state.playerId = msg.playerId;
const code = msg.roomId || state.room?.id || localStorage.getItem('lastRoomId');
if (code) sendMsg({ type: 'join_room', code });
} else {
const code = state.room?.id || localStorage.getItem('lastRoomId');
if (code) sendMsg({ type: 'join_room', code });
}
break;
case 'connected':
handleConnected(msg);
break;
@@ -211,6 +226,7 @@ function wireUi() {
});
wire($leaveRoom, 'click', () => {
sendMsg({ type: 'leave_room' });
stopAudioPlayback();
state.room = null;
$lobby.classList.remove('hidden');
$room.classList.add('hidden');

View File

@@ -1,9 +1,10 @@
import { state } from './state.js';
import { badgeColorForYear } from '../utils/colors.js';
import { $answerForm, $answerResult, $dashboardList, $guesser, $lobby, $nameDisplay, $nextArea, $np, $placeArea, $readyChk, $revealBanner, $room, $roomId, $slotSelect, $startGame, $status, $timeline, $tokens } from './dom.js';
import { $answerForm, $answerResult, $dashboardList, $guesser, $lobby, $mediaControls, $nameDisplay, $nextArea, $np, $placeArea, $readyChk, $revealBanner, $room, $roomId, $slotSelect, $startGame, $status, $timeline, $tokens } from './dom.js';
export function renderRoom(room) {
state.room = room; if (!room) { $lobby.classList.remove('hidden'); $room.classList.add('hidden'); return; }
try { localStorage.setItem('lastRoomId', room.id); } catch {}
$lobby.classList.add('hidden'); $room.classList.remove('hidden');
$roomId.textContent = room.id;
$status.textContent = room.state.status;
@@ -59,6 +60,8 @@ export function renderRoom(room) {
if ($startGame) $startGame.classList.toggle('hidden', !(isHost && room.state.status==='lobby' && allReady));
const isMyTurn = room.state.status==='playing' && room.state.phase==='guess' && room.state.currentGuesser===state.playerId && room.state.currentTrack;
const canGuess = isMyTurn;
// Media controls (play/pause) only for current guesser while guessing and a track is active
if ($mediaControls) $mediaControls.classList.toggle('hidden', !isMyTurn);
// Build slot options for insertion positions when it's my turn
if ($placeArea && $slotSelect) {
if (canGuess) {

View File

@@ -4,6 +4,8 @@ let ws;
let reconnectAttempts = 0;
let reconnectTimer = null;
const outbox = [];
let sessionId = localStorage.getItem('sessionId') || null;
let lastRoomId = localStorage.getItem('lastRoomId') || null;
export function wsIsOpen() { return ws && ws.readyState === WebSocket.OPEN; }
export function sendMsg(obj) { if (wsIsOpen()) ws.send(JSON.stringify(obj)); else outbox.push(obj); }
@@ -20,6 +22,10 @@ export function connectWS(onMessage) {
ws = new WebSocket(url);
ws.addEventListener('open', () => {
reconnectAttempts = 0;
// Try to resume session immediately on (re)connect
if (sessionId) {
try { ws.send(JSON.stringify({ type: 'resume', sessionId })); } catch {}
}
setTimeout(() => { while (outbox.length && wsIsOpen()) { try { ws.send(JSON.stringify(outbox.shift())); } catch { break; } } }, 100);
});
ws.addEventListener('message', (ev) => onMessage(ev));
@@ -33,3 +39,15 @@ window.addEventListener('online', () => {
// Kick off a reconnect by calling connectWS from app main again
}
});
// Helpers to update cached ids from other modules
export function cacheSessionId(id) {
if (!id) return;
sessionId = id;
try { localStorage.setItem('sessionId', id); } catch {}
}
export function cacheLastRoomId(id) {
if (!id) return;
lastRoomId = id;
try { localStorage.setItem('lastRoomId', id); } catch {}
}