From 3efecfda498d9ff0dc1752ce6d87e48e6ff7c2db Mon Sep 17 00:00:00 2001 From: derekc Date: Sun, 1 Mar 2026 14:27:05 -0800 Subject: [PATCH] Fix paused timer auto-resuming on page navigation applySnapshot was always setting isPaused = false and blockStartedAt = Date.now() regardless of the actual timer state, causing a paused block to appear running whenever the dashboard was reloaded. - Add is_paused field to DashboardSnapshot schema - Dashboard endpoint derives is_paused by checking whether the last start/resume/pause event for the current block is a pause - applySnapshot now reads is_paused from the snapshot instead of resetting to false, and only sets blockStartedAt when the block is actually running (not paused) Co-Authored-By: Claude Sonnet 4.6 --- backend/app/routers/dashboard.py | 6 +++++- backend/app/schemas/session.py | 1 + frontend/src/stores/schedule.js | 5 +++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/app/routers/dashboard.py b/backend/app/routers/dashboard.py index 7fc3b76..121ed8d 100644 --- a/backend/app/routers/dashboard.py +++ b/backend/app/routers/dashboard.py @@ -70,7 +70,8 @@ async def get_dashboard(child_id: int, db: AsyncSession = Depends(get_db)): ) completed_ids = [e.block_id for e in events_result.scalars().all() if e.block_id] - # Compute elapsed seconds for the current block from timer_events + # Compute elapsed seconds and paused state for the current block from timer_events + is_paused = False if session and session.current_block_id: tick_result = await db.execute( select(TimerEvent) @@ -93,6 +94,8 @@ async def get_dashboard(child_id: int, db: AsyncSession = Depends(get_db)): if last_start: elapsed += (datetime.utcnow() - last_start).total_seconds() block_elapsed_seconds = int(elapsed) + # Paused if the last tick event was a pause (last_start is None but events exist) + is_paused = bool(tick_events) and tick_events[-1].event_type == "pause" return DashboardSnapshot( session=session, @@ -100,6 +103,7 @@ async def get_dashboard(child_id: int, db: AsyncSession = Depends(get_db)): blocks=blocks, completed_block_ids=completed_ids, block_elapsed_seconds=block_elapsed_seconds, + is_paused=is_paused, day_start_time=day_start_time, day_end_time=day_end_time, ) diff --git a/backend/app/schemas/session.py b/backend/app/schemas/session.py index ce592da..87028c2 100644 --- a/backend/app/schemas/session.py +++ b/backend/app/schemas/session.py @@ -43,5 +43,6 @@ class DashboardSnapshot(BaseModel): blocks: list[ScheduleBlockOut] = [] completed_block_ids: list[int] = [] block_elapsed_seconds: int = 0 # seconds already elapsed for the current block + is_paused: bool = False # whether the current block's timer is paused day_start_time: time | None = None day_end_time: time | None = None diff --git a/frontend/src/stores/schedule.js b/frontend/src/stores/schedule.js index 802e661..689f951 100644 --- a/frontend/src/stores/schedule.js +++ b/frontend/src/stores/schedule.js @@ -35,7 +35,7 @@ export const useScheduleStore = defineStore('schedule', () => { session.value = snapshot.session blocks.value = sortBlocksByTime(snapshot.blocks) completedBlockIds.value = snapshot.completed_block_ids || [] - isPaused.value = false + isPaused.value = snapshot.is_paused || false if (snapshot.child) child.value = snapshot.child dayStartTime.value = snapshot.day_start_time || null dayEndTime.value = snapshot.day_end_time || null @@ -43,7 +43,8 @@ export const useScheduleStore = defineStore('schedule', () => { const serverElapsed = snapshot.block_elapsed_seconds || 0 if (snapshot.session?.current_block_id && serverElapsed > 0) { blockElapsedOffset.value = serverElapsed - blockStartedAt.value = Date.now() + // Only start the live counter if the block is actually running (not paused) + blockStartedAt.value = isPaused.value ? null : Date.now() } else { blockElapsedOffset.value = 0 blockStartedAt.value = null