Files
hitstar/public/js/main.js

262 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { $audio, $copyRoomCode, $leaveRoom, $nameDisplay, $nameLobby, $npArtist, $npTitle, $npYear, $pauseBtn, $placeBtn, $readyChk, $roomId, $roomCode, $slotSelect, $startGame, $volumeSlider, $playBtn, $nextBtn, $createRoom, $joinRoom, $lobby, $room, $setNameLobby } from './dom.js';
import { state } from './state.js';
import { connectWS, sendMsg } from './ws.js';
import { renderRoom } from './render.js';
import { initAudioUI, applySync } from './audio.js';
function showToast(msg) {
const el = document.getElementById('toast');
if (!el) {
return;
}
el.textContent = msg;
el.style.opacity = '1';
setTimeout(() => {
el.style.opacity = '0';
}, 1200);
}
function handleConnected(msg) {
state.playerId = msg.playerId;
const stored = localStorage.getItem('playerName');
if (stored) {
if ($nameLobby && $nameLobby.value !== stored) {
$nameLobby.value = stored;
}
if ($nameDisplay) {
$nameDisplay.textContent = stored;
}
sendMsg({ type: 'set_name', name: stored });
}
if (state.room?.id) {
sendMsg({ type: 'join_room', code: state.room.id });
}
}
function handleRoomUpdate(msg) {
renderRoom(msg.room);
}
function handlePlayTrack(msg) {
const t = msg.track;
state.lastTrack = t;
state.revealed = false;
$npTitle.textContent = '???';
$npArtist.textContent = '';
$npYear.textContent = '';
try {
$audio.preload = 'auto';
} catch {}
$audio.src = t.url;
const pf = document.getElementById('progressFill');
if (pf) {
pf.style.width = '0%';
}
const rd = document.getElementById('recordDisc');
if (rd) {
rd.classList.remove('spin-record');
}
const { startAt, serverNow } = msg;
const now = Date.now();
const offsetMs = startAt - serverNow;
const localStart = now + offsetMs;
const delay = Math.max(0, localStart - now);
setTimeout(() => {
$audio.currentTime = 0;
$audio.play().catch(() => {});
const disc = document.getElementById('recordDisc');
if (disc) {
disc.classList.add('spin-record');
}
}, delay);
if (state.room) {
renderRoom(state.room);
}
}
function handleSync(msg) {
applySync(msg.startAt, msg.serverNow);
}
function handleControl(msg) {
const { action, startAt, serverNow } = msg;
if (action === 'pause') {
$audio.pause();
const disc = document.getElementById('recordDisc');
if (disc) {
disc.classList.remove('spin-record');
}
$audio.playbackRate = 1.0;
} else if (action === 'play') {
if (startAt && serverNow) {
const now = Date.now();
const elapsed = (now - startAt) / 1000;
$audio.currentTime = Math.max(0, elapsed);
}
$audio.play().catch(() => {});
const disc = document.getElementById('recordDisc');
if (disc) {
disc.classList.add('spin-record');
}
}
}
function handleReveal(msg) {
const { result, track } = msg;
$npTitle.textContent = track.title || track.id || 'Track';
$npArtist.textContent = track.artist ? ` ${track.artist}` : '';
$npYear.textContent = track.year ? ` (${track.year})` : '';
state.revealed = true;
const $rb = document.getElementById('revealBanner');
if ($rb) {
if (result.correct) {
$rb.textContent = 'Richtig!';
$rb.className = 'inline-block rounded-md bg-emerald-600 text-white px-3 py-1 text-sm font-medium';
} else {
$rb.textContent = 'Falsch!';
$rb.className = 'inline-block rounded-md bg-rose-600 text-white px-3 py-1 text-sm font-medium';
}
}
const $placeArea = document.getElementById('placeArea');
if ($placeArea) {
$placeArea.classList.add('hidden');
}
}
function handleGameEnded(msg) {
alert(`Gewinner: ${shortName(msg.winner)}`);
}
function onMessage(ev) {
const msg = JSON.parse(ev.data);
switch (msg.type) {
case 'connected':
handleConnected(msg);
break;
case 'room_update':
handleRoomUpdate(msg);
break;
case 'play_track':
handlePlayTrack(msg);
break;
case 'sync':
handleSync(msg);
break;
case 'control':
handleControl(msg);
break;
case 'reveal':
handleReveal(msg);
break;
case 'game_ended':
handleGameEnded(msg);
break;
default:
break;
}
}
function shortName(id) {
if (!id) return '-';
const p = state.room?.players.find((x) => x.id === id);
return p ? p.name : id.slice(0, 4);
}
function wire(el, type, handler) {
if (el) {
el.addEventListener(type, handler);
}
}
function wireUi() {
initAudioUI();
wire($setNameLobby, 'click', () => {
const name = $nameLobby.value.trim();
if (!name) return;
localStorage.setItem('playerName', name);
if ($nameDisplay) {
$nameDisplay.textContent = name;
}
sendMsg({ type: 'set_name', name });
});
wire($createRoom, 'click', () => sendMsg({ type: 'create_room' }));
wire($joinRoom, 'click', () => {
const code = $roomCode.value.trim();
if (code) {
sendMsg({ type: 'join_room', code });
}
});
wire($leaveRoom, 'click', () => {
sendMsg({ type: 'leave_room' });
state.room = null;
$lobby.classList.remove('hidden');
$room.classList.add('hidden');
});
wire($startGame, 'click', () => sendMsg({ type: 'start_game' }));
wire($readyChk, 'change', (e) => {
const val = !!e.target.checked;
state.pendingReady = val;
sendMsg({ type: 'set_ready', ready: val });
});
wire($placeBtn, 'click', () => {
const slot = parseInt($slotSelect.value, 10);
sendMsg({ type: 'place_guess', slot });
});
wire($playBtn, 'click', () => sendMsg({ type: 'player_control', action: 'play' }));
wire($pauseBtn, 'click', () => sendMsg({ type: 'player_control', action: 'pause' }));
wire($nextBtn, 'click', () => sendMsg({ type: 'next_track' }));
if ($volumeSlider && $audio) {
try {
$volumeSlider.value = String($audio.volume ?? 1);
} catch {}
$volumeSlider.addEventListener('input', () => {
$audio.volume = parseFloat($volumeSlider.value);
});
}
if ($copyRoomCode) {
$copyRoomCode.style.display = 'inline-block';
wire($copyRoomCode, 'click', () => {
if (state.room?.id) {
navigator.clipboard.writeText(state.room.id).then(() => {
$copyRoomCode.textContent = '✔️';
showToast('Code kopiert!');
setTimeout(() => {
$copyRoomCode.textContent = '📋';
}, 1200);
});
}
});
}
if ($roomId) {
wire($roomId, 'click', () => {
if (state.room?.id) {
navigator.clipboard.writeText(state.room.id).then(() => {
$roomId.title = 'Kopiert!';
showToast('Code kopiert!');
setTimeout(() => {
$roomId.title = 'Klicken zum Kopieren';
}, 1200);
});
}
});
$roomId.style.cursor = 'pointer';
}
}
// boot
wireUi();
connectWS(onMessage);
// restore name immediately if present
(() => {
const saved = localStorage.getItem('playerName');
if (saved) {
if ($nameLobby && $nameLobby.value !== saved) {
$nameLobby.value = saved;
}
if ($nameDisplay) {
$nameDisplay.textContent = saved;
}
}
})();