Skip to content

fix(workout): snap timers to wall clock on mobile screen wake - #116

Merged
rghamilton3 merged 4 commits into
mainfrom
fix/timer-timeout
Apr 23, 2026
Merged

rghamilton3 merged 4 commits into
mainfrom
fix/timer-timeout

Conversation

@rghamilton3

Copy link
Copy Markdown
Contributor

Summary

  • Root cause: Both the rest timer and elapsed timer used tick-accumulation (decrement/increment per interval). When the mobile screen times out, the JS runtime and tokio async tasks are suspended, so ticks stop accumulating. Displays freeze and rest expiry fires late.
  • Fix: Convert all timer math to wall-clock timestamps so the next tick after wake self-corrects to the right value regardless of how long the screen was off.
  • Foreground snap: Wire the existing createForegroundDetector into the log page so both timers update instantly when the screen turns back on, not just on the next scheduled tick.

Test plan

  • Start a workout, let the screen time out mid-rest, unlock -- rest timer should show the correct remaining time immediately (not the stale pre-sleep value)
  • If the rest period ends while the screen is off, unlocking should fire onExpired immediately and dismiss the rest banner
  • Elapsed workout timer should show the correct total time after wake (no gap/freeze artifacts)
  • Adjust rest (+/- buttons) still works correctly before and after a screen wake
  • bun run test passes (2496 tests)
  • cargo test --lib passes (85 tests)

@vercel

vercel Bot commented Apr 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ardent-forge Error Error Apr 23, 2026 2:53am

Rest timers and the elapsed timer used tick-accumulation (decrement/increment
per interval) which pauses when the mobile screen times out, leaving displays
stale and rest expiry firing late.

- Rust rest timer: replace remaining-- with started_at.elapsed() computation;
  adjust() shifts started_at instead of mutating a counter
- JS browser rest timer: add startedAt to RestTimer, add recalcRestTimer()
  which snaps to wall-clock reality and fires onExpired if rest ended while
  screen was off
- Elapsed interval: compute from startedAt timestamps instead of prev+1 so
  it self-corrects on the next tick after wake
- Foreground detector: wire createForegroundDetector in log page to
  immediately snap both timers the moment the screen turns back on
Dead fixture function and its type import left over in
active-workout-store.test.ts caused tsc to error and the
Vercel deploy to fail.
Fix-Now:
- P23-001: recalcRestTimer expiry now stops Rust timer + cleans listeners (Tauri)
- P23-002/003: _onRestExpired wrapped in try/catch/finally in recalcRestTimer and
  tickRest -- cleanup always runs even if callback throws
- P23-004: bare catch {} in log.$workoutId.tsx replaced with logged catch (err)
- P23-005: store.recalcRestTimer() wrapped in its own try/catch in foreground callback
- P23-006: tickRest now uses wall-clock computation (total - elapsed) not remaining - 1
- P23-007: Rust adjust tests use tolerance assertions to avoid 1s CI timing race
- P23-010: onBackground no-op callback has explanatory comment
- P23-011: Rust adjust() unwrap_or_else fallback has saturation semantics comment
- P23-012: recalcRestTimer interface comment documents both running and expiry paths
- P23-013: adjustRest comment clarifies Rust vs JS saturation mechanism difference

Tests:
- recalcRestTimer: expired path, running path, no-op, exact boundary, throwing callback
- tickRest: updated existing tests for wall-clock; throwing-callback resilience
- adjustRest: startedAt shift verified via follow-up recalcRestTimer call

Review: Context/Reviews/0023-pr116-timer-timeout-2026-04-22.md
P23-012 comment was missing the running-path description (corrects
remaining to wall-clock value). Updated interface comment to document
both paths. Marked P23 and P22 reviews as verified.
@rghamilton3
rghamilton3 merged commit bc198ab into main Apr 23, 2026
1 check was pending
@rghamilton3
rghamilton3 deleted the fix/timer-timeout branch April 23, 2026 02:52

This branch had an error being deployed

1 failed deployment
Preview — 1a614a92 Deployed Apr 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant