From f705bfc8d43c50010e374421d6a62e2d06382693 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 19 Mar 2026 22:46:24 +0000 Subject: [PATCH 1/2] Hide rest timer immediately on expiry instead of showing expired state Tests expect #rest-timer to be hidden after notification fires, but showTimerExpired() was keeping it visible. Replace all three call sites (normal expiry, recovery interval, already-expired on load) with a direct classList.add('hidden') and remove the now-unused showTimerExpired function. https://claude.ai/code/session_015kygLeCbusNCZUgXsraTFw --- workout-tracker/src/ui/workout.ts | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/workout-tracker/src/ui/workout.ts b/workout-tracker/src/ui/workout.ts index 8a82430..fddc8fe 100644 --- a/workout-tracker/src/ui/workout.ts +++ b/workout-tracker/src/ui/workout.ts @@ -360,7 +360,7 @@ export async function renderWorkout(container: HTMLElement): Promise { // Cancel the SW background timer — the main thread is handling this one cancelBackgroundTimerNotification(); fireTimerNotification(); - showTimerExpired(timerEl); + timerEl.classList.add('hidden'); } }; @@ -368,28 +368,6 @@ export async function renderWorkout(container: HTMLElement): Promise { updateTimer(); } - function showTimerExpired(el: HTMLElement) { - el.classList.remove('hidden'); - el.classList.add('timer-expired'); - el.dataset.testid = 'timer-expired'; - const timerValue = document.getElementById('timer-value'); - if (timerValue) timerValue.textContent = "Time's Up!"; - // Hide skip button during expired state - const skipBtn = document.getElementById('skip-timer-btn'); - if (skipBtn) skipBtn.classList.add('hidden'); - - const dismiss = () => { - el.classList.add('hidden'); - el.classList.remove('timer-expired'); - delete el.dataset.testid; - el.removeEventListener('click', dismiss); - }; - - el.addEventListener('click', dismiss); - // Auto-dismiss after 10 seconds - setTimeout(dismiss, 10000); - } - function detectFailures() { const mainFailed: Array<{ exerciseId: string; got: number; prescribed: number }> = []; const bbbFailed: Array<{ exerciseId: string; got: number; prescribed: number }> = []; @@ -579,14 +557,14 @@ export async function renderWorkout(container: HTMLElement): Promise { setDoneButtonDisabled(false); cancelBackgroundTimerNotification(); fireTimerNotification(); - showTimerExpired(timerEl); + timerEl.classList.add('hidden'); } }, 250); } else { await putTimerState(null); cancelBackgroundTimerNotification(); fireTimerNotification(); - showTimerExpired(timerEl); + timerEl.classList.add('hidden'); } } From c22311f007252efe6312b5257218861353410eae Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 19 Mar 2026 23:02:17 +0000 Subject: [PATCH 2/2] Reconcile timer expiry behavior between notification and attention tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two test suites had conflicting expectations: - timer-completion-attention: expects showTimerExpired UI after foreground expiry - timer-notification: was checking toBeHidden() after expiry (stale expectation) Fix: restore showTimerExpired() for interval-based expiry (foreground and recovery paths). Keep the simple hide behavior only for the already-expired- on-reload path, where the user didn't watch the timer count down and showing "Time's Up!" would be confusing. Update notification test 2's final assertion from toBeHidden() to checking the expired state is visible — the key assertion (vibrateCount == 1) already proves no duplicate notifications. https://claude.ai/code/session_015kygLeCbusNCZUgXsraTFw --- .../e2e/timer-notification.spec.ts | 4 ++-- workout-tracker/src/ui/workout.ts | 24 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/workout-tracker/e2e/timer-notification.spec.ts b/workout-tracker/e2e/timer-notification.spec.ts index d3a3c63..ee784c5 100644 --- a/workout-tracker/e2e/timer-notification.spec.ts +++ b/workout-tracker/e2e/timer-notification.spec.ts @@ -101,7 +101,7 @@ test.describe('Rest Timer Notification Reliability', () => { const vibrateCount = await page.evaluate(() => (window as any).__vibrateCount); expect(vibrateCount).toBe(1); - // Timer should be hidden - await expect(page.locator('#rest-timer')).toBeHidden(); + // Timer should show expired state (active countdown stopped) + await expect(page.locator('[data-testid="timer-expired"]')).toBeVisible(); }); }); diff --git a/workout-tracker/src/ui/workout.ts b/workout-tracker/src/ui/workout.ts index fddc8fe..53ec5ba 100644 --- a/workout-tracker/src/ui/workout.ts +++ b/workout-tracker/src/ui/workout.ts @@ -360,7 +360,7 @@ export async function renderWorkout(container: HTMLElement): Promise { // Cancel the SW background timer — the main thread is handling this one cancelBackgroundTimerNotification(); fireTimerNotification(); - timerEl.classList.add('hidden'); + showTimerExpired(timerEl); } }; @@ -368,6 +368,26 @@ export async function renderWorkout(container: HTMLElement): Promise { updateTimer(); } + function showTimerExpired(el: HTMLElement) { + el.classList.remove('hidden'); + el.classList.add('timer-expired'); + el.dataset.testid = 'timer-expired'; + const timerValue = document.getElementById('timer-value'); + if (timerValue) timerValue.textContent = "Time's Up!"; + const skipBtn = document.getElementById('skip-timer-btn'); + if (skipBtn) skipBtn.classList.add('hidden'); + + const dismiss = () => { + el.classList.add('hidden'); + el.classList.remove('timer-expired'); + delete el.dataset.testid; + el.removeEventListener('click', dismiss); + }; + + el.addEventListener('click', dismiss); + setTimeout(dismiss, 10000); + } + function detectFailures() { const mainFailed: Array<{ exerciseId: string; got: number; prescribed: number }> = []; const bbbFailed: Array<{ exerciseId: string; got: number; prescribed: number }> = []; @@ -557,7 +577,7 @@ export async function renderWorkout(container: HTMLElement): Promise { setDoneButtonDisabled(false); cancelBackgroundTimerNotification(); fireTimerNotification(); - timerEl.classList.add('hidden'); + showTimerExpired(timerEl); } }, 250); } else {