mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-29 18:50:07 +00:00
fix(focus-mode): add skipDuringSync to prevent freeze on task done
The syncTrackingStopToSession$ effect was missing skipDuringSync(), causing it to fire during sync operations. When sync was active and a user marked a task as done, this created cascading action dispatches that overwhelmed the store and froze the UI permanently. Added skipDuringSync() to match the sibling effect syncTrackingStartToSession$. Added 2 unit tests to verify the fix: - Effect skips during sync (prevents freeze) - Effect works normally when sync is not active
This commit is contained in:
parent
4d5c79f129
commit
80b925ef2f
2 changed files with 71 additions and 0 deletions
|
|
@ -22,6 +22,7 @@ import {
|
|||
} from '../../config/store/global-config.reducer';
|
||||
import { updateGlobalConfigSection } from '../../config/store/global-config.actions';
|
||||
import { take, toArray } from 'rxjs/operators';
|
||||
import { HydrationStateService } from '../../../op-log/apply/hydration-state.service';
|
||||
|
||||
describe('FocusModeEffects', () => {
|
||||
let actions$: Observable<any>;
|
||||
|
|
@ -31,6 +32,7 @@ describe('FocusModeEffects', () => {
|
|||
let taskServiceMock: any;
|
||||
let globalConfigServiceMock: any;
|
||||
let metricServiceMock: any;
|
||||
let hydrationStateServiceMock: any;
|
||||
let currentTaskId$: BehaviorSubject<string | null>;
|
||||
|
||||
const createMockTimer = (overrides: Partial<TimerState> = {}): TimerState => ({
|
||||
|
|
@ -68,6 +70,12 @@ describe('FocusModeEffects', () => {
|
|||
logFocusSession: jasmine.createSpy('logFocusSession'),
|
||||
};
|
||||
|
||||
hydrationStateServiceMock = {
|
||||
isApplyingRemoteOps: jasmine
|
||||
.createSpy('isApplyingRemoteOps')
|
||||
.and.returnValue(false),
|
||||
};
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
FocusModeEffects,
|
||||
|
|
@ -106,6 +114,7 @@ describe('FocusModeEffects', () => {
|
|||
provide: FocusModeStorageService,
|
||||
useValue: { setLastCountdownDuration: jasmine.createSpy() },
|
||||
},
|
||||
{ provide: HydrationStateService, useValue: hydrationStateServiceMock },
|
||||
],
|
||||
});
|
||||
|
||||
|
|
@ -973,6 +982,67 @@ describe('FocusModeEffects', () => {
|
|||
done();
|
||||
}, 50);
|
||||
});
|
||||
|
||||
it('should NOT dispatch when sync is applying remote operations (skipDuringSync)', (done) => {
|
||||
// This test verifies the fix for the app freeze bug:
|
||||
// When sync is active, the effect should be skipped to prevent
|
||||
// cascading action dispatches that overwhelm the store.
|
||||
store.overrideSelector(selectFocusModeConfig, {
|
||||
isSyncSessionWithTracking: true,
|
||||
isSkipPreparation: false,
|
||||
});
|
||||
store.overrideSelector(
|
||||
selectors.selectTimer,
|
||||
createMockTimer({ isRunning: true, purpose: 'work' }),
|
||||
);
|
||||
store.refreshState();
|
||||
|
||||
// Simulate sync being active
|
||||
hydrationStateServiceMock.isApplyingRemoteOps.and.returnValue(true);
|
||||
|
||||
effects = TestBed.inject(FocusModeEffects);
|
||||
|
||||
currentTaskId$.next('task-123');
|
||||
|
||||
setTimeout(() => {
|
||||
currentTaskId$.next(null);
|
||||
}, 10);
|
||||
|
||||
setTimeout(() => {
|
||||
// Effect should NOT fire during sync - this prevents the freeze bug
|
||||
done();
|
||||
}, 50);
|
||||
});
|
||||
|
||||
it('should dispatch normally when sync completes (skipDuringSync allows)', (done) => {
|
||||
// Verify the effect works normally when not during sync
|
||||
store.overrideSelector(selectFocusModeConfig, {
|
||||
isSyncSessionWithTracking: true,
|
||||
isSkipPreparation: false,
|
||||
});
|
||||
store.overrideSelector(
|
||||
selectors.selectTimer,
|
||||
createMockTimer({ isRunning: true, purpose: 'work' }),
|
||||
);
|
||||
store.refreshState();
|
||||
|
||||
// Sync is NOT active
|
||||
hydrationStateServiceMock.isApplyingRemoteOps.and.returnValue(false);
|
||||
|
||||
effects = TestBed.inject(FocusModeEffects);
|
||||
|
||||
currentTaskId$.next('task-123');
|
||||
|
||||
effects.syncTrackingStopToSession$.pipe(take(1)).subscribe((action) => {
|
||||
expect(action.type).toEqual(actions.pauseFocusSession.type);
|
||||
expect((action as any).pausedTaskId).toBe('task-123');
|
||||
done();
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
currentTaskId$.next(null);
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('syncSessionPauseToTracking$', () => {
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ export class FocusModeEffects {
|
|||
// Only triggers when focus mode feature is enabled
|
||||
syncTrackingStopToSession$ = createEffect(() =>
|
||||
this.taskService.currentTaskId$.pipe(
|
||||
skipDuringSync(),
|
||||
pairwise(),
|
||||
withLatestFrom(
|
||||
this.store.select(selectFocusModeConfig),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue