Autosave: 20260325-174136
This commit is contained in:
parent
342ba02e74
commit
1a199bfeb2
@ -11,6 +11,15 @@ run_migrations();
|
||||
|
||||
try {
|
||||
$method = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET'));
|
||||
header('Allow: GET, POST, HEAD, OPTIONS');
|
||||
|
||||
if ($method === 'OPTIONS') {
|
||||
jsonResponse(['success' => true], 204);
|
||||
}
|
||||
|
||||
if ($method === 'HEAD') {
|
||||
listScores((int) ($_GET['limit'] ?? 10), false);
|
||||
}
|
||||
|
||||
if ($method === 'GET') {
|
||||
listScores((int) ($_GET['limit'] ?? 10));
|
||||
@ -106,7 +115,7 @@ function saveScore(array $payload): void
|
||||
]);
|
||||
}
|
||||
|
||||
function listScores(int $limit = 10): void
|
||||
function listScores(int $limit = 10, bool $includeBody = true): void
|
||||
{
|
||||
$limit = max(1, min(25, $limit));
|
||||
|
||||
@ -136,6 +145,11 @@ function listScores(int $limit = 10): void
|
||||
];
|
||||
}
|
||||
|
||||
if (!$includeBody) {
|
||||
http_response_code(200);
|
||||
exit;
|
||||
}
|
||||
|
||||
jsonResponse([
|
||||
'success' => true,
|
||||
'scores' => $scores,
|
||||
|
||||
@ -634,6 +634,59 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
: 'Just now';
|
||||
}
|
||||
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function renderScoreboard() {
|
||||
if (!ui.scoreboardList || !ui.scoreboardEmpty) return;
|
||||
|
||||
if (!Array.isArray(scoreboardEntries) || scoreboardEntries.length === 0) {
|
||||
ui.scoreboardList.innerHTML = '';
|
||||
ui.scoreboardEmpty.classList.remove('d-none');
|
||||
return;
|
||||
}
|
||||
|
||||
ui.scoreboardEmpty.classList.add('d-none');
|
||||
ui.scoreboardList.innerHTML = scoreboardEntries.map((entry, index) => {
|
||||
const playerName = escapeHtml(entry.player_name || 'Player');
|
||||
const scoreLabel = Number(entry.score || 0).toLocaleString();
|
||||
const levelLabel = Number(entry.level || 0).toString();
|
||||
const linesLabel = Number(entry.lines || 0).toString();
|
||||
const modeLabel = entry.mode === 'multiplayer' ? 'Multiplayer' : 'Solo';
|
||||
const durationLabel = formatDuration(Number(entry.duration_seconds || 0) * 1000);
|
||||
const createdAt = entry.created_at
|
||||
? new Date(String(entry.created_at).replace(' ', 'T')).toLocaleString([], {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
: 'Just now';
|
||||
const roomCode = entry.room_code ? ` · Room ${escapeHtml(entry.room_code)}` : '';
|
||||
|
||||
return `
|
||||
<article class="scoreboard-item">
|
||||
<div class="scoreboard-rank">#${index + 1}</div>
|
||||
<div class="scoreboard-item-body">
|
||||
<div class="scoreboard-topline">
|
||||
<span class="scoreboard-name">${playerName}</span>
|
||||
<span class="scoreboard-score">${scoreLabel}</span>
|
||||
</div>
|
||||
<div class="scoreboard-meta">Level ${levelLabel} · Lines ${linesLabel} · ${durationLabel}</div>
|
||||
<div class="scoreboard-meta">${modeLabel}${roomCode} · ${createdAt}</div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
async function loadScoreboard(options = {}) {
|
||||
if (!ui.scoreboardStatus) return;
|
||||
const preserveOnError = Boolean(options.preserveOnError);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user