Skip to content

Implement background timer notifications via service worker#28

Merged
willchan merged 1 commit intomainfrom
claude/fix-background-timer-8YVwF
Mar 16, 2026
Merged

Implement background timer notifications via service worker#28
willchan merged 1 commit intomainfrom
claude/fix-background-timer-8YVwF

Conversation

@willchan
Copy link
Owner

Summary

This PR implements reliable background timer notifications by delegating timer scheduling to the service worker. This ensures notifications fire even when the main thread is throttled or suspended by the browser (common on mobile after backgrounding).

Key Changes

  • Service Worker Timer Scheduling: Modified public/sw.js to maintain its own background timeout that fires notifications independently of the main thread. The SW now handles TIMER_START and TIMER_CANCEL messages to manage the background timer lifecycle.

  • Main Thread Communication: Added scheduleBackgroundTimerNotification() and cancelBackgroundTimerNotification() functions in src/ui/notifications.ts to post timer events to the service worker.

  • Timer Integration: Updated src/ui/workout.ts to:

    • Call scheduleBackgroundTimerNotification() when a rest timer starts
    • Call cancelBackgroundTimerNotification() when the timer completes, is skipped, or the workout ends
    • Ensures the SW timer is always cancelled to prevent stale notifications
  • AudioContext Resume: Enhanced fireTimerNotification() in src/ui/notifications.ts to call AudioContext.resume() before playing the beep sound. This prevents audio from being stuck in a suspended state after the browser has been backgrounded (common on mobile).

  • Comprehensive E2E Tests: Added e2e/background-timer.spec.ts with four test cases covering:

    • TIMER_START message posting when timer begins
    • TIMER_CANCEL message posting when timer is skipped
    • Service worker correctly cancels background timers
    • AudioContext.resume() is called before playing audio

Implementation Details

The background timer works by having the service worker calculate the delay from the current time to the expected end time, then scheduling its own setTimeout(). This approach is more reliable than relying on the main thread's timer loop, which can be suspended by the browser. The main thread still maintains its own timer for UI updates, but the notification is guaranteed by the SW's independent scheduling.

https://claude.ai/code/session_01WqokDmx5PoSQULQgS7iJj5

- Service worker now owns its own setTimeout for the rest timer so
  notifications fire even when the main thread is throttled/suspended
  by the browser while the app is backgrounded. The app sends
  TIMER_START (with expectedEndTime) to the SW when a rest timer
  begins and TIMER_CANCEL when it is skipped, completed, or abandoned.
- When the main thread fires the notification (app foregrounded), it
  cancels the SW timer first to prevent duplicate notifications.
- AudioContext.resume() is called before starting the oscillator so
  the beep plays correctly after the browser has suspended the audio
  context (common on mobile after backgrounding).

https://claude.ai/code/session_01WqokDmx5PoSQULQgS7iJj5
@willchan willchan merged commit 6b8cc9f into main Mar 16, 2026
1 check passed
@willchan willchan deleted the claude/fix-background-timer-8YVwF branch March 19, 2026 00:04
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.

2 participants