fix(focus-mode): get latest isResumingBreak value in effect (#5995)

Move selectIsResumingBreak from outer combineLatest to inner
withLatestFrom to ensure we check the current flag value at
execution time, not a stale value from the outer closure.
This commit is contained in:
Johannes Millan 2026-01-19 18:31:10 +01:00
parent b7139036f7
commit fb6041c0b6

View file

@ -83,13 +83,10 @@ export class FocusModeEffects {
combineLatest([
this.store.select(selectFocusModeConfig),
this.store.select(selectIsFocusModeEnabled),
// Track break resume intent via store flag to distinguish between
// user manually starting tracking vs focus mode resuming tracking
this.store.select(selectors.selectIsResumingBreak),
]).pipe(
// Outer guard: skip config changes during sync
skipWhileApplyingRemoteOps(),
switchMap(([cfg, isFocusModeEnabled, isResumingBreak]) =>
switchMap(([cfg, isFocusModeEnabled]) =>
isFocusModeEnabled && cfg?.isSyncSessionWithTracking
? this.taskService.currentTaskId$.pipe(
// currentTaskId$ is local UI state (not synced), so distinctUntilChanged is sufficient
@ -100,33 +97,45 @@ export class FocusModeEffects {
this.store.select(selectors.selectMode),
this.store.select(selectors.selectCurrentScreen),
this.store.select(selectors.selectPausedTaskId),
// Bug #5995 Fix: Get the LATEST value of isResumingBreak here
// to avoid using stale value from outer closure
this.store.select(selectors.selectIsResumingBreak),
),
switchMap(([_taskId, timer, mode, currentScreen, pausedTaskId]) => {
// If session is paused (purpose is 'work' but not running), resume it
if (timer.purpose === 'work' && !timer.isRunning) {
return of(actions.unPauseFocusSession());
}
// If break is active, handle based on state and cause
// Bug #5995 Fix: Don't skip breaks that were just resumed
if (timer.purpose === 'break') {
// Check store flag to distinguish between break resume and manual tracking start
if (isResumingBreak) {
// Clear flag after processing to prevent false positives
this.store.dispatch(actions.clearResumingBreakFlag());
return EMPTY; // Don't skip - break is resuming
switchMap(
([
_taskId,
timer,
mode,
currentScreen,
pausedTaskId,
isResumingBreak,
]) => {
// If session is paused (purpose is 'work' but not running), resume it
if (timer.purpose === 'work' && !timer.isRunning) {
return of(actions.unPauseFocusSession());
}
// User manually started tracking during break
// Skip the break to sync with tracking (bug #5875 fix)
return of(actions.skipBreak({ pausedTaskId }));
}
// If no session active, start a new one (only from Main screen)
if (timer.purpose === null && currentScreen === FocusScreen.Main) {
const strategy = this.strategyFactory.getStrategy(mode);
const duration = strategy.initialSessionDuration;
return of(actions.startFocusSession({ duration }));
}
return EMPTY;
}),
// If break is active, handle based on state and cause
// Bug #5995 Fix: Don't skip breaks that were just resumed
if (timer.purpose === 'break') {
// Check store flag to distinguish between break resume and manual tracking start
if (isResumingBreak) {
// Clear flag after processing to prevent false positives
this.store.dispatch(actions.clearResumingBreakFlag());
return EMPTY; // Don't skip - break is resuming
}
// User manually started tracking during break
// Skip the break to sync with tracking (bug #5875 fix)
return of(actions.skipBreak({ pausedTaskId }));
}
// If no session active, start a new one (only from Main screen)
if (timer.purpose === null && currentScreen === FocusScreen.Main) {
const strategy = this.strategyFactory.getStrategy(mode);
const duration = strategy.initialSessionDuration;
return of(actions.startFocusSession({ duration }));
}
return EMPTY;
},
),
)
: EMPTY,
),