feat(worker): server-authoritative Stopwatch screen with active-session recovery

This commit is contained in:
Bas van Rossem
2026-06-17 16:24:56 +02:00
parent 1ecad6bbb4
commit 5af5a9c2bb
5 changed files with 625 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
import { describe, expect, it } from 'vitest';
import { elapsedSeconds, formatTime } from './stopwatch';
describe('stopwatch logic', () => {
it('formats seconds as HH:MM:SS', () => {
expect(formatTime(0)).toBe('00:00:00');
expect(formatTime(65)).toBe('00:01:05');
expect(formatTime(3661)).toBe('01:01:01');
});
it('computes elapsed seconds since start excluding paused time', () => {
expect(elapsedSeconds(1000, 6000, 0)).toBe(5);
expect(elapsedSeconds(1000, 6000, 2000)).toBe(3);
});
it('never returns a negative elapsed value', () => {
expect(elapsedSeconds(5000, 1000, 0)).toBe(0);
});
});

View File

@@ -0,0 +1,16 @@
// Pure timing helpers for the Stopwatch screen — server-authoritative elapsed.
// Elapsed is computed from the server start_time (wall-clock), not a tick counter,
// so it survives backgrounding. Pause accumulates paused time client-side.
export function formatTime(totalSeconds: number): string {
const s = Math.max(0, Math.floor(totalSeconds));
const h = Math.floor(s / 3600);
const m = Math.floor((s % 3600) / 60);
const sec = s % 60;
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
}
// elapsed seconds since startMs, excluding accumulated paused ms, evaluated at nowMs.
export function elapsedSeconds(startMs: number, nowMs: number, pausedMs: number): number {
return Math.max(0, Math.floor((nowMs - startMs - pausedMs) / 1000));
}