refactor: update player handling and dashboard display in client and server
This commit is contained in:
@@ -46,7 +46,8 @@ let state = {
|
||||
const el = (id) => document.getElementById(id);
|
||||
const $lobby = el('lobby');
|
||||
const $room = el('room');
|
||||
const $players = el('players');
|
||||
// Removed old players chip list; using dashboard only
|
||||
const $dashboardList = el('dashboardList');
|
||||
const $roomId = el('roomId');
|
||||
const $status = el('status');
|
||||
const $guesser = el('guesser');
|
||||
@@ -129,22 +130,38 @@ function showToast(msg) {
|
||||
// Show my current name (from server if available) or fallback to stored value
|
||||
const me = room.players.find(p=>p.id===state.playerId);
|
||||
if ($nameDisplay) $nameDisplay.textContent = (me?.name || localStorage.getItem('playerName') || '-');
|
||||
$players.innerHTML = room.players.map(p => {
|
||||
const badges = [
|
||||
p.id===room.hostId ? '<span class="ml-1 text-amber-600">⭐</span>' : '',
|
||||
p.ready ? '<span class="ml-1 text-emerald-600">✓</span>' : '',
|
||||
!p.connected ? '<span class="ml-1 text-rose-600">(off)</span>' : '',
|
||||
].join('');
|
||||
return `<span class="inline-flex items-center gap-1 px-2 py-1 rounded-full border border-slate-300 dark:border-slate-700 text-sm">${escapeHtml(p.name)}${badges}</span>`;
|
||||
}).join(' ');
|
||||
// Old players chip list removed
|
||||
// Dashboard rows
|
||||
if ($dashboardList) {
|
||||
$dashboardList.innerHTML = room.players.map(p => {
|
||||
const connected = p.connected ? '<span class="text-emerald-600">online</span>' : '<span class="text-rose-600">offline</span>';
|
||||
const ready = p.ready ? '<span class="text-emerald-600">bereit</span>' : '<span class="text-slate-400">-</span>';
|
||||
const score = (room.state.timeline?.[p.id]?.length) ?? 0;
|
||||
const isMe = p.id === state.playerId;
|
||||
return `
|
||||
<tr class="align-top">
|
||||
<td class="py-2 pr-3">
|
||||
<div class="inline-flex items-center gap-1">
|
||||
<span>${escapeHtml(p.name)}</span>${p.spectator ? ' <span title="Zuschauer">👻</span>' : ''}
|
||||
${p.id===room.hostId ? '<span title="Host" class="text-amber-600">\u2B50</span>' : ''}
|
||||
${isMe ? '<span title="Du" class="text-indigo-600">(du)</span>' : ''}
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-2 pr-3">${connected}</td>
|
||||
<td class="py-2 pr-3">${ready}</td>
|
||||
<td class="py-2 pr-3 font-semibold tabular-nums">${score}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
const myTl = room.state.timeline?.[state.playerId] || [];
|
||||
$timeline.innerHTML = myTl.map(t => {
|
||||
const title = escapeHtml(t.title || t.trackId || 'Unbekannt');
|
||||
const artist = t.artist ? escapeHtml(t.artist) : '';
|
||||
const year = (t.year ?? '?');
|
||||
const badgeStyle = badgeColorForYear(year);
|
||||
return `
|
||||
<div class="flex items-center gap-2 border border-slate-200 dark:border-slate-800 rounded-lg px-3 py-2 bg-white text-slate-900 dark:bg-slate-800 dark:text-slate-100 shadow-sm" title="${title}${artist? ' — '+artist : ''} (${year})">
|
||||
<div class="font-bold tabular-nums bg-indigo-600 text-white rounded-md px-2 py-0.5 min-w-[3ch] text-center">${year}</div>
|
||||
<div class="font-bold tabular-nums text-white rounded-md px-2 py-0.5 min-w-[3ch] text-center" style="${badgeStyle}">${year}</div>
|
||||
<div class="leading-tight">
|
||||
<div class="font-semibold">${title}</div>
|
||||
<div class="text-sm text-slate-600 dark:text-slate-300">${artist}</div>
|
||||
@@ -216,6 +233,18 @@ function shortName(id) {
|
||||
|
||||
function escapeHtml(s) { return String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); }
|
||||
|
||||
// Stable distinct color per year for the year badge
|
||||
function badgeColorForYear(y) {
|
||||
const val = (y === undefined || y === null) ? '?' : y;
|
||||
if (val === '?' || Number.isNaN(Number(val))) {
|
||||
// Neutral slate for unknown years
|
||||
return 'background-color: hsl(215 16% 34%);';
|
||||
}
|
||||
const n = Number(val);
|
||||
const hue = ((n * 23) % 360 + 360) % 360; // spread hues deterministically
|
||||
return `background-color: hsl(${hue} 70% 42%);`;
|
||||
}
|
||||
|
||||
function handleMessage(ev) {
|
||||
const msg = JSON.parse(ev.data);
|
||||
if (msg.type === 'connected') {
|
||||
|
||||
@@ -46,9 +46,29 @@
|
||||
|
||||
<div class="text-slate-700 dark:text-slate-300">Dein Name: <strong id="nameDisplay" class="font-semibold"></strong></div>
|
||||
|
||||
|
||||
|
||||
<!-- Expandable Dashboard: player statuses and scores -->
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold text-slate-500 uppercase tracking-wide">Spieler</h3>
|
||||
<div id="players" class="mt-2 flex flex-wrap gap-2"></div>
|
||||
<details id="dashboard" class="rounded-lg border border-slate-200 dark:border-slate-800 bg-white/60 dark:bg-slate-900/40 p-3">
|
||||
<summary class="cursor-pointer select-none text-sm font-semibold text-slate-700 dark:text-slate-300 flex items-center gap-2">
|
||||
<span class="inline-flex h-5 w-5 items-center justify-center rounded-full bg-indigo-600 text-white text-[11px] font-bold">i</span>
|
||||
Dashboard: Spielerstatus & Punkte
|
||||
</summary>
|
||||
<div class="mt-3 overflow-x-auto">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead class="text-left text-slate-500 dark:text-slate-400">
|
||||
<tr>
|
||||
<th class="py-2 pr-3">Spieler</th>
|
||||
<th class="py-2 pr-3">Verbindung</th>
|
||||
<th class="py-2 pr-3">Ready</th>
|
||||
<th class="py-2 pr-3">Score</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dashboardList" class="divide-y divide-slate-200 dark:divide-slate-800"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 items-start">
|
||||
|
||||
Reference in New Issue
Block a user