From b00d4ee99e07cbaa409a9c1c0b2cdfd678d42c11 Mon Sep 17 00:00:00 2001 From: derekc Date: Sun, 1 Mar 2026 21:22:25 -0800 Subject: [PATCH] Preserve elapsed time when switching between schedule blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, clicking a different block always fired 'start' which reset elapsed to zero — returning to a block lost all accumulated time. Store changes: - Add blockElapsedCache (blockId → elapsed seconds) that persists across block switches within a session - On 'pause' WS event: write the block's total elapsed into the cache - On 'start' WS event: restore elapsed from cache (0 if never worked) - On applySnapshot: seed cache with server-computed elapsed for the current block (so reloading preserves state correctly) - Clear cache when the session ends Dashboard selectBlock changes: - Auto-pause the currently running block before switching to another - Clicking the active block while paused now sends 'resume' instead of doing nothing - Clicking the already-running active block is a no-op Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/stores/schedule.js | 14 ++++++++++---- frontend/src/views/DashboardView.vue | 21 ++++++++++++++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/frontend/src/stores/schedule.js b/frontend/src/stores/schedule.js index 689f951..e5dc49b 100644 --- a/frontend/src/stores/schedule.js +++ b/frontend/src/stores/schedule.js @@ -10,6 +10,7 @@ export const useScheduleStore = defineStore('schedule', () => { const isPaused = ref(false) const blockStartedAt = ref(null) // Date.now() ms when current counting period started const blockElapsedOffset = ref(0) // seconds already elapsed before blockStartedAt + const blockElapsedCache = ref({}) // blockId → total elapsed seconds (survives block switches) const dayStartTime = ref(null) // "HH:MM:SS" string or null const dayEndTime = ref(null) // "HH:MM:SS" string or null @@ -39,9 +40,10 @@ export const useScheduleStore = defineStore('schedule', () => { if (snapshot.child) child.value = snapshot.child dayStartTime.value = snapshot.day_start_time || null dayEndTime.value = snapshot.day_end_time || null - // Restore elapsed time from server-computed value + // Restore elapsed time from server-computed value and seed the per-block cache const serverElapsed = snapshot.block_elapsed_seconds || 0 if (snapshot.session?.current_block_id && serverElapsed > 0) { + blockElapsedCache.value[snapshot.session.current_block_id] = serverElapsed blockElapsedOffset.value = serverElapsed // Only start the live counter if the block is actually running (not paused) blockStartedAt.value = isPaused.value ? null : Date.now() @@ -68,21 +70,25 @@ export const useScheduleStore = defineStore('schedule', () => { isPaused.value = false blockStartedAt.value = null blockElapsedOffset.value = 0 + blockElapsedCache.value = {} dayStartTime.value = null dayEndTime.value = null return } - // Pause — accumulate elapsed, stop counting + // Pause — accumulate elapsed, save to cache, stop counting if (event.event === 'pause') { if (blockStartedAt.value) { blockElapsedOffset.value += Math.floor((Date.now() - blockStartedAt.value) / 1000) } + if (event.block_id) { + blockElapsedCache.value[event.block_id] = blockElapsedOffset.value + } blockStartedAt.value = null isPaused.value = true } - // Start (new block) — reset elapsed, begin counting + // Start — restore cached elapsed if returning to a previously worked block if (event.event === 'start') { - blockElapsedOffset.value = 0 + blockElapsedOffset.value = blockElapsedCache.value[event.block_id] || 0 blockStartedAt.value = Date.now() isPaused.value = false } diff --git a/frontend/src/views/DashboardView.vue b/frontend/src/views/DashboardView.vue index 62c31de..80cec59 100644 --- a/frontend/src/views/DashboardView.vue +++ b/frontend/src/views/DashboardView.vue @@ -217,10 +217,25 @@ async function sendAction(type) { await scheduleStore.sendTimerAction(scheduleStore.session.id, type) } -function selectBlock(block) { +async function selectBlock(block) { if (!scheduleStore.session) return - scheduleStore.session.current_block_id = block.id - scheduleStore.isPaused = false + + const currentId = scheduleStore.session.current_block_id + + // Clicking the current block while paused → resume it + if (block.id === currentId && scheduleStore.isPaused) { + scheduleStore.sendTimerAction(scheduleStore.session.id, 'resume') + return + } + + // Clicking the current block while running → do nothing + if (block.id === currentId) return + + // Switching to a different block — pause the current one first if it's running + if (currentId && !scheduleStore.isPaused) { + await scheduleStore.sendTimerAction(scheduleStore.session.id, 'pause', currentId) + } + scheduleStore.sendTimerAction(scheduleStore.session.id, 'start', block.id) }