feat: smooth clock countdown for intermission and live play
All checks were successful
CI / Lint (push) Successful in 6s
CI / Test (push) Successful in 6s
CI / Build & Push (push) Successful in 17s

Store seconds + received-at timestamp on time badge. A 1s interval
decrements locally so the clock never stutters between API polls.
Drift-corrected: always computed from the anchored API value, not
accumulated ticks. Re-render on each API response reanchors to the
real value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 14:51:05 -04:00
parent e2d2c7dd97
commit 96529c4705

View File

@@ -40,6 +40,13 @@ function renderLiveGame(game) {
const dot = running ? `<span class="live-dot"></span>` : ''; const dot = running ? `<span class="live-dot"></span>` : '';
// Tick the clock locally when the clock is running or during intermission
const shouldTick = running || intermission;
const rawSeconds = timeToSeconds(time);
const clockAttrs = shouldTick
? `data-seconds="${rawSeconds}" data-received-at="${Date.now()}"`
: '';
const hype = !intermission ? ` const hype = !intermission ? `
<div class="hype-meter"> <div class="hype-meter">
<span class="hype-label">Hype Meter</span> <span class="hype-label">Hype Meter</span>
@@ -53,7 +60,7 @@ function renderLiveGame(game) {
<div class="card-header"> <div class="card-header">
<div class="badges"> <div class="badges">
${periodLabel} ${periodLabel}
<span class="badge">${time}</span> <span class="badge" ${clockAttrs}>${time}</span>
</div> </div>
${dot} ${dot}
</div> </div>
@@ -132,6 +139,31 @@ function updateGauges() {
}); });
} }
// ── Clock ─────────────────────────────────────────────
function timeToSeconds(str) {
if (!str || str === 'END') return 0;
const [m, s] = str.split(':').map(Number);
return m * 60 + s;
}
function secondsToTime(s) {
if (s <= 0) return 'END';
const m = Math.floor(s / 60);
const sec = s % 60;
return `${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
}
function tickClocks() {
const now = Date.now();
document.querySelectorAll('[data-seconds][data-received-at]').forEach(el => {
const seconds = parseInt(el.dataset.seconds, 10);
const receivedAt = parseInt(el.dataset.receivedAt, 10);
const elapsed = Math.floor((now - receivedAt) / 1000);
el.textContent = secondsToTime(Math.max(0, seconds - elapsed));
});
}
// ── Helpers ────────────────────────────────────────── // ── Helpers ──────────────────────────────────────────
function ordinalPeriod(period) { function ordinalPeriod(period) {
@@ -149,4 +181,7 @@ function autoRefresh() {
setTimeout(autoRefresh, 5000); setTimeout(autoRefresh, 5000);
} }
window.addEventListener('load', autoRefresh); window.addEventListener('load', () => {
autoRefresh();
setInterval(tickClocks, 1000);
});