From 96529c4705a24cd0891e7fbf647784818e93ab47 Mon Sep 17 00:00:00 2001 From: josh Date: Sun, 29 Mar 2026 14:51:05 -0400 Subject: [PATCH] feat: smooth clock countdown for intermission and live play 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 --- app/static/script.js | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/app/static/script.js b/app/static/script.js index 57ea4ad..75d6094 100644 --- a/app/static/script.js +++ b/app/static/script.js @@ -40,6 +40,13 @@ function renderLiveGame(game) { const dot = running ? `` : ''; + // 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 ? `
Hype Meter @@ -53,7 +60,7 @@ function renderLiveGame(game) {
${periodLabel} - ${time} + ${time}
${dot}
@@ -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 ────────────────────────────────────────── function ordinalPeriod(period) { @@ -149,4 +181,7 @@ function autoRefresh() { setTimeout(autoRefresh, 5000); } -window.addEventListener('load', autoRefresh); +window.addEventListener('load', () => { + autoRefresh(); + setInterval(tickClocks, 1000); +});