Fix double-click to switch blocks and TV elapsed reset
Double-click fix: - After awaiting the pause, optimistically set current_block_id and isPaused on the store so the UI switches instantly. The subsequent WS start event confirms the state without requiring a second click. TV elapsed reset fix: - The TV's local cache was empty for blocks paused before it connected, so returning to those blocks showed 0 elapsed. - Backend now computes the block's accumulated elapsed from previous start/pause cycles and includes it as block_elapsed_seconds in the 'start' WS event payload. - All clients (Dashboard and TV) now use this authoritative server value instead of relying solely on the local cache. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -189,6 +189,29 @@ async def timer_action(
|
|||||||
await db.commit()
|
await db.commit()
|
||||||
await db.refresh(session)
|
await db.refresh(session)
|
||||||
|
|
||||||
|
# For 'start' events, compute elapsed from previous intervals so every
|
||||||
|
# client (including TV) can restore the correct offset without a cache.
|
||||||
|
block_elapsed_seconds = 0
|
||||||
|
if body.event_type == "start" and event.block_id:
|
||||||
|
tick_result = await db.execute(
|
||||||
|
select(TimerEvent)
|
||||||
|
.where(
|
||||||
|
TimerEvent.session_id == session.id,
|
||||||
|
TimerEvent.block_id == event.block_id,
|
||||||
|
TimerEvent.event_type.in_(["start", "resume", "pause"]),
|
||||||
|
TimerEvent.id != event.id,
|
||||||
|
)
|
||||||
|
.order_by(TimerEvent.occurred_at)
|
||||||
|
)
|
||||||
|
tick_events = tick_result.scalars().all()
|
||||||
|
last_start_time = None
|
||||||
|
for e in tick_events:
|
||||||
|
if e.event_type in ("start", "resume"):
|
||||||
|
last_start_time = e.occurred_at
|
||||||
|
elif e.event_type == "pause" and last_start_time:
|
||||||
|
block_elapsed_seconds += int((e.occurred_at - last_start_time).total_seconds())
|
||||||
|
last_start_time = None
|
||||||
|
|
||||||
# Broadcast the timer event to all TV clients
|
# Broadcast the timer event to all TV clients
|
||||||
ws_payload = {
|
ws_payload = {
|
||||||
"event": body.event_type,
|
"event": body.event_type,
|
||||||
@@ -196,6 +219,7 @@ async def timer_action(
|
|||||||
"block_id": event.block_id,
|
"block_id": event.block_id,
|
||||||
"current_block_id": session.current_block_id,
|
"current_block_id": session.current_block_id,
|
||||||
"is_active": session.is_active,
|
"is_active": session.is_active,
|
||||||
|
"block_elapsed_seconds": block_elapsed_seconds,
|
||||||
}
|
}
|
||||||
await manager.broadcast(session.child_id, ws_payload)
|
await manager.broadcast(session.child_id, ws_payload)
|
||||||
|
|
||||||
|
|||||||
@@ -86,9 +86,12 @@ export const useScheduleStore = defineStore('schedule', () => {
|
|||||||
blockStartedAt.value = null
|
blockStartedAt.value = null
|
||||||
isPaused.value = true
|
isPaused.value = true
|
||||||
}
|
}
|
||||||
// Start — restore cached elapsed if returning to a previously worked block
|
// Start — use server-provided elapsed (authoritative for all clients incl. TV),
|
||||||
|
// fall back to local cache, then 0 for a fresh block
|
||||||
if (event.event === 'start') {
|
if (event.event === 'start') {
|
||||||
blockElapsedOffset.value = blockElapsedCache.value[event.block_id] || 0
|
const elapsed = event.block_elapsed_seconds ?? blockElapsedCache.value[event.block_id] ?? 0
|
||||||
|
blockElapsedOffset.value = elapsed
|
||||||
|
if (event.block_id) blockElapsedCache.value[event.block_id] = elapsed
|
||||||
blockStartedAt.value = Date.now()
|
blockStartedAt.value = Date.now()
|
||||||
isPaused.value = false
|
isPaused.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,6 +236,10 @@ async function selectBlock(block) {
|
|||||||
await scheduleStore.sendTimerAction(scheduleStore.session.id, 'pause', currentId)
|
await scheduleStore.sendTimerAction(scheduleStore.session.id, 'pause', currentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optimistically update so the UI switches immediately without waiting for the WS start event
|
||||||
|
scheduleStore.session.current_block_id = block.id
|
||||||
|
scheduleStore.isPaused = false
|
||||||
|
|
||||||
scheduleStore.sendTimerAction(scheduleStore.session.id, 'start', block.id)
|
scheduleStore.sendTimerAction(scheduleStore.session.id, 'start', block.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user