Autosave: 20260325-174136
This commit is contained in:
parent
342ba02e74
commit
1a199bfeb2
@ -11,6 +11,15 @@ run_migrations();
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$method = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET'));
|
$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') {
|
if ($method === 'GET') {
|
||||||
listScores((int) ($_GET['limit'] ?? 10));
|
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));
|
$limit = max(1, min(25, $limit));
|
||||||
|
|
||||||
@ -136,6 +145,11 @@ function listScores(int $limit = 10): void
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$includeBody) {
|
||||||
|
http_response_code(200);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
jsonResponse([
|
jsonResponse([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'scores' => $scores,
|
'scores' => $scores,
|
||||||
|
|||||||
@ -634,6 +634,59 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
: 'Just now';
|
: '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 = {}) {
|
async function loadScoreboard(options = {}) {
|
||||||
if (!ui.scoreboardStatus) return;
|
if (!ui.scoreboardStatus) return;
|
||||||
const preserveOnError = Boolean(options.preserveOnError);
|
const preserveOnError = Boolean(options.preserveOnError);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user