From 5bfb6bcf64cc40461b40c195b9ae4f0e95501acb Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Fri, 28 Feb 2025 15:37:18 +0100 Subject: [PATCH] feat(focusMode): better integrate idle case --- .../focus-mode-main.component.html | 6 +- .../focus-mode-main.component.ts | 10 +- .../focus-mode-overlay.component.ts | 11 +- .../features/focus-mode/focus-mode.service.ts | 103 ++++++++++++++++++ .../focus-mode/store/focus-mode.actions.ts | 14 ++- .../focus-mode/store/focus-mode.effects.ts | 87 +++++---------- .../focus-mode/store/focus-mode.reducer.ts | 52 ++++----- .../focus-mode/store/focus-mode.selectors.ts | 16 --- .../idle/dialog-idle/dialog-idle.component.ts | 4 + .../idle/dialog-idle/dialog-idle.model.ts | 2 + src/app/features/idle/store/idle.actions.ts | 2 + src/app/features/idle/store/idle.effects.ts | 83 ++++++++++---- 12 files changed, 247 insertions(+), 143 deletions(-) create mode 100644 src/app/features/focus-mode/focus-mode.service.ts diff --git a/src/app/features/focus-mode/focus-mode-main/focus-mode-main.component.html b/src/app/features/focus-mode/focus-mode-main/focus-mode-main.component.html index 4fa8de91b8..fbb603c195 100644 --- a/src/app/features/focus-mode/focus-mode-main/focus-mode-main.component.html +++ b/src/app/features/focus-mode/focus-mode-main/focus-mode-main.component.html @@ -8,7 +8,9 @@ >
@if (isCountTimeDown) { - + } @else { } @@ -19,7 +21,7 @@ > @if (isCountTimeDown) { - {{ timeToGo$ | async | msToMinuteClockString }} + {{ focusModeService.timeToGo$ | async | msToMinuteClockString }} } @else { {{ timeElapsed$ | async | msToMinuteClockString }} } diff --git a/src/app/features/focus-mode/focus-mode-main/focus-mode-main.component.ts b/src/app/features/focus-mode/focus-mode-main/focus-mode-main.component.ts index 2170d3fd1a..ae2a95bbed 100644 --- a/src/app/features/focus-mode/focus-mode-main/focus-mode-main.component.ts +++ b/src/app/features/focus-mode/focus-mode-main/focus-mode-main.component.ts @@ -18,9 +18,7 @@ import { IssueService } from '../../issue/issue.service'; import { Store } from '@ngrx/store'; import { selectFocusModeMode, - selectFocusSessionProgress, selectFocusSessionTimeElapsed, - selectFocusSessionTimeToGo, } from '../store/focus-mode.selectors'; import { focusSessionDone, setFocusSessionActivePage } from '../store/focus-mode.actions'; import { updateTask } from '../../tasks/store/task.actions'; @@ -42,6 +40,7 @@ import { IssueIconPipe } from '../../issue/issue-icon/issue-icon.pipe'; import { SimpleCounterButtonComponent } from '../../simple-counter/simple-counter-button/simple-counter-button.component'; import { TaskAttachmentListComponent } from '../../tasks/task-attachment/task-attachment-list/task-attachment-list.component'; import { slideInOutFromBottomAni } from '../../../ui/animations/slide-in-out-from-bottom.ani'; +import { FocusModeService } from '../focus-mode.service'; @Component({ selector: 'focus-mode-main', @@ -74,13 +73,12 @@ export class FocusModeMainComponent implements OnDestroy { private readonly _issueService = inject(IssueService); private readonly _store = inject(Store); - timeToGo$ = this._store.select(selectFocusSessionTimeToGo); + focusModeService = inject(FocusModeService); + timeElapsed$ = this._store.select(selectFocusSessionTimeElapsed); mode$ = this._store.select(selectFocusModeMode); isCountTimeDown$ = this.mode$.pipe(map((mode) => mode !== FocusModeMode.Flowtime)); - sessionProgress$ = this._store.select(selectFocusSessionProgress); - @HostBinding('class.isShowNotes') isShowNotes: boolean = false; task: TaskCopy | null = null; @@ -165,7 +163,7 @@ export class FocusModeMainComponent implements OnDestroy { } finishCurrentTask(): void { - this._store.dispatch(focusSessionDone()); + this._store.dispatch(focusSessionDone({})); this._store.dispatch( updateTask({ task: { diff --git a/src/app/features/focus-mode/focus-mode-overlay/focus-mode-overlay.component.ts b/src/app/features/focus-mode/focus-mode-overlay/focus-mode-overlay.component.ts index fb0f243061..6168dfb2d3 100644 --- a/src/app/features/focus-mode/focus-mode-overlay/focus-mode-overlay.component.ts +++ b/src/app/features/focus-mode/focus-mode-overlay/focus-mode-overlay.component.ts @@ -9,9 +9,7 @@ import { Store } from '@ngrx/store'; import { selectFocusModeMode, selectFocusSessionActivePage, - selectFocusSessionProgress, selectFocusSessionTimeElapsed, - selectFocusSessionTimeToGo, selectIsFocusSessionRunning, } from '../store/focus-mode.selectors'; import { @@ -40,6 +38,7 @@ import { BannerService } from '../../../core/banner/banner.service'; import { BannerId } from '../../../core/banner/banner.model'; import { toSignal } from '@angular/core/rxjs-interop'; import { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button-toggle'; +import { FocusModeService } from '../focus-mode.service'; @Component({ selector: 'focus-mode-overlay', @@ -68,6 +67,8 @@ import { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button- export class FocusModeOverlayComponent implements OnDestroy { readonly taskService = inject(TaskService); readonly bannerService = inject(BannerService); + readonly focusModeService = inject(FocusModeService); + private readonly _globalConfigService = inject(GlobalConfigService); private readonly _store = inject(Store); @@ -151,10 +152,8 @@ export class FocusModeOverlayComponent implements OnDestroy { msg: 'Focus Session is running', timer$: isCountTimeUp ? this._store.select(selectFocusSessionTimeElapsed) - : this._store.select(selectFocusSessionTimeToGo), - progress$: isCountTimeUp - ? undefined - : this._store.select(selectFocusSessionProgress), + : this.focusModeService.timeToGo$, + progress$: isCountTimeUp ? undefined : this.focusModeService.sessionProgress$, action2: { label: 'To Focus Overlay', fn: () => { diff --git a/src/app/features/focus-mode/focus-mode.service.ts b/src/app/features/focus-mode/focus-mode.service.ts new file mode 100644 index 0000000000..96d0ef4489 --- /dev/null +++ b/src/app/features/focus-mode/focus-mode.service.ts @@ -0,0 +1,103 @@ +import { inject, Injectable } from '@angular/core'; +import { combineLatest, EMPTY, interval, merge, Observable, of } from 'rxjs'; +import { + selectFocusSessionDuration, + selectIsFocusSessionRunning, +} from './store/focus-mode.selectors'; +import { + map, + mapTo, + pairwise, + scan, + shareReplay, + startWith, + switchMap, + tap, +} from 'rxjs/operators'; +import { Actions, ofType } from '@ngrx/effects'; +import { cancelFocusSession, unPauseFocusSession } from './store/focus-mode.actions'; +import { Store } from '@ngrx/store'; + +const TICK_DURATION = 500; + +@Injectable({ + providedIn: 'root', +}) +export class FocusModeService { + private _store = inject(Store); + private _actions$ = inject(Actions); + private _isRunning$ = this._store.select(selectIsFocusSessionRunning); + + private _plannedSessionDuration$ = this._store.select(selectFocusSessionDuration); + private _timer$: Observable = interval(TICK_DURATION).pipe( + switchMap(() => of(Date.now())), + pairwise(), + map(([a, b]) => b - a), + ); + + private _tick$: Observable = this._isRunning$.pipe( + switchMap((isRunning) => (isRunning ? this._timer$ : EMPTY)), + map((tick) => tick * -1), + ); + + currentSessionTime$: Observable = merge( + // first val is negative otherwise + // TODO maybe comment in again + // startWith(567), + + this._tick$, + this._actions$.pipe(ofType(cancelFocusSession), mapTo(0)), + this._actions$.pipe( + ofType(unPauseFocusSession), + map(({ idleTimeToAdd = 0 }) => idleTimeToAdd * -1), + ), + // TODO remove if not needed + // this._store.select(selectFocusModeMode).pipe( + // switchMap((mode) => + // mode === FocusModeMode.Countdown + // ? // needed in case plannedSessionDuration did not change + // this._actions$.pipe( + // ofType(startFocusSession, cancelFocusSession), + // switchMap(() => this._plannedSessionDuration$.pipe(first())), + // ) + // : EMPTY, + // ), + // ), + ).pipe( + scan((acc, value) => { + const accValMinZero = acc < 0 ? 0 : acc; + console.log('VALUPD currentSessionTime', value, value < 0, accValMinZero); + return value < 0 ? accValMinZero - value : value; + }), + shareReplay(1), + // tap((v) => console.log('___currentSessionTimeShared', v)), + ); + + timeToGo$: Observable = combineLatest([ + this.currentSessionTime$.pipe(startWith(0)), + this._plannedSessionDuration$, + ]).pipe( + tap((v) => console.log('timeToGo$ params', v)), + map( + ([currentSessionTime, plannedSessionDuration]) => + plannedSessionDuration - currentSessionTime, + ), + ); + // timeToGo$: Observable = this.currentSessionTime$.pipe( + // withLatestFrom(this._plannedSessionDuration$), + // tap((v) => console.log('timeToGo$ params', v)), + // + // map( + // ([currentSessionTime, plannedSessionDuration]) => + // plannedSessionDuration - currentSessionTime, + // ), + // ); + + sessionProgress$ = combineLatest([this.timeToGo$, this._plannedSessionDuration$]).pipe( + // tap((v) => console.log('sessionProgress$ params', v)), + map( + ([timeToGo, plannedSessionDuration]) => + ((plannedSessionDuration - timeToGo) * 100) / plannedSessionDuration, + ), + ); +} diff --git a/src/app/features/focus-mode/store/focus-mode.actions.ts b/src/app/features/focus-mode/store/focus-mode.actions.ts index a0e44df293..264a63e46f 100644 --- a/src/app/features/focus-mode/store/focus-mode.actions.ts +++ b/src/app/features/focus-mode/store/focus-mode.actions.ts @@ -15,18 +15,22 @@ export const setFocusSessionDuration = createAction( props<{ focusSessionDuration: number }>(), ); -export const setFocusSessionTimeToGo = createAction( - '[FocusMode] Set focus session time to go', - props<{ focusSessionTimeToGo: number }>(), -); export const setFocusSessionTimeElapsed = createAction( '[FocusMode] Set focus session elapsed time', props<{ focusSessionTimeElapsed: number }>(), ); export const startFocusSession = createAction('[FocusMode] Start focus session'); export const cancelFocusSession = createAction('[FocusMode] Cancel Focus Session'); +export const pauseFocusSession = createAction('[FocusMode] Pause Focus Session'); +export const unPauseFocusSession = createAction( + '[FocusMode] UnPause Focus Session', + props<{ idleTimeToAdd?: number }>(), +); -export const focusSessionDone = createAction('[FocusMode] Focus session done'); +export const focusSessionDone = createAction( + '[FocusMode] Focus session done', + props<{ isResetPlannedSessionDuration?: boolean }>(), +); export const showFocusOverlay = createAction('[FocusMode] Show Focus Overlay'); export const hideFocusOverlay = createAction('[FocusMode] Hide Focus Overlay'); diff --git a/src/app/features/focus-mode/store/focus-mode.effects.ts b/src/app/features/focus-mode/store/focus-mode.effects.ts index b8da7c74e9..2d61274c65 100644 --- a/src/app/features/focus-mode/store/focus-mode.effects.ts +++ b/src/app/features/focus-mode/store/focus-mode.effects.ts @@ -3,12 +3,11 @@ import { Actions, createEffect, ofType } from '@ngrx/effects'; import { cancelFocusSession, focusSessionDone, + pauseFocusSession, setFocusModeMode, setFocusSessionActivePage, setFocusSessionTimeElapsed, - setFocusSessionTimeToGo, showFocusOverlay, - startFocusSession, } from './focus-mode.actions'; import { GlobalConfigService } from '../../config/global-config.service'; import { @@ -18,20 +17,16 @@ import { first, map, mapTo, - pairwise, - scan, - startWith, switchMap, switchMapTo, tap, withLatestFrom, } from 'rxjs/operators'; -import { EMPTY, interval, merge, Observable, of } from 'rxjs'; +import { EMPTY, Observable, of } from 'rxjs'; import { TaskService } from '../../tasks/task.service'; import { selectFocusModeMode, selectFocusSessionDuration, - selectFocusSessionProgress, selectIsFocusSessionRunning, } from './focus-mode.selectors'; import { Store } from '@ngrx/store'; @@ -42,14 +37,16 @@ import { IdleService } from '../../idle/idle.service'; import { FocusModeMode, FocusModePage } from '../focus-mode.const'; import { selectFocusModeConfig } from '../../config/store/global-config.reducer'; import { LS } from '../../../core/persistence/storage-keys.const'; +import { openIdleDialog } from '../../idle/store/idle.actions'; +import { FocusModeService } from '../focus-mode.service'; -const TICK_DURATION = 500; const SESSION_DONE_SOUND = 'positive.ogg'; // const DEFAULT_TICK_SOUND = 'tick.mp3'; @Injectable() export class FocusModeEffects { + private _focusModeService = inject(FocusModeService); private _store = inject(Store); private _actions$ = inject(Actions); private _idleService = inject(IdleService); @@ -57,49 +54,8 @@ export class FocusModeEffects { private _taskService = inject(TaskService); private _isRunning$ = this._store.select(selectIsFocusSessionRunning); - private _sessionDuration$ = this._store.select(selectFocusSessionDuration); - private _sessionProgress$ = this._store.select(selectFocusSessionProgress); - - private _timer$: Observable = interval(TICK_DURATION).pipe( - switchMap(() => of(Date.now())), - pairwise(), - map(([a, b]) => b - a), - ); - - private _tick$: Observable = this._isRunning$.pipe( - switchMap((isRunning) => (isRunning ? this._timer$ : EMPTY)), - map((tick) => tick * -1), - ); - - private _currentSessionTime$: Observable = this._store - .select(selectFocusModeMode) - .pipe( - switchMap((mode) => - mode === FocusModeMode.Countdown - ? merge( - this._sessionDuration$, - this._tick$, - this._actions$.pipe( - ofType(startFocusSession, cancelFocusSession), - switchMap(() => this._sessionDuration$.pipe(first())), - ), - ).pipe( - scan((acc, value) => { - return value < 0 ? acc + value : value; - }), - ) - : merge( - this._tick$, - this._actions$.pipe(ofType(cancelFocusSession), mapTo(0)), - ).pipe( - // first val is negative otherwise - startWith(500), - scan((acc, value) => { - return value < 0 ? acc - value : value; - }), - ), - ), - ); + // TODO also rename store value maybe + private _plannedSessionDuration$ = this._store.select(selectFocusSessionDuration); autoStartFocusMode$ = createEffect(() => { return this._store.select(selectFocusModeConfig).pipe( @@ -115,24 +71,36 @@ export class FocusModeEffects { ), ); }); + setElapsedTime$ = createEffect(() => { - return this._currentSessionTime$.pipe( - withLatestFrom(this._store.select(selectFocusModeMode)), - map(([focusSessionTimeToGo, mode]) => { + return this._focusModeService.currentSessionTime$.pipe( + tap((v) => console.log('currentSessionTime', v)), + + withLatestFrom( + this._store.select(selectFocusModeMode), + this._focusModeService.timeToGo$, + ), + map(([currentSessionTime, mode, timeToGo]) => { if (mode === FocusModeMode.Flowtime) { return setFocusSessionTimeElapsed({ - focusSessionTimeElapsed: focusSessionTimeToGo, + focusSessionTimeElapsed: currentSessionTime, }); } - return focusSessionTimeToGo >= 0 - ? setFocusSessionTimeToGo({ focusSessionTimeToGo }) - : focusSessionDone(); + return timeToGo >= 0 + ? // ? setFocusSessionTimeToGo({ currentSessionTime }) + setFocusSessionTimeElapsed({ + focusSessionTimeElapsed: currentSessionTime, + }) + : focusSessionDone({ isResetPlannedSessionDuration: true }); }), ); }); stopTrackingOnOnCancel$ = createEffect(() => { return this._actions$.pipe(ofType(cancelFocusSession), mapTo(unsetCurrentTask())); }); + pauseOnIdle$ = createEffect(() => { + return this._actions$.pipe(ofType(openIdleDialog), mapTo(pauseFocusSession())); + }); playSessionDoneSoundIfEnabled$: Observable = createEffect( () => @@ -149,6 +117,7 @@ export class FocusModeEffects { { dispatch: false }, ); + // TODO check if needed handleIdleCurrentTaskDeSelection$: Observable = createEffect(() => this._isRunning$.pipe( switchMap((isRunning) => (isRunning ? this._idleService.isIdle$ : EMPTY)), @@ -169,7 +138,7 @@ export class FocusModeEffects { IS_ELECTRON && createEffect( () => - this._sessionProgress$.pipe( + this._focusModeService.sessionProgress$.pipe( withLatestFrom(this._isRunning$), tap(([progress, isRunning]: [number, boolean]) => { const progressBarMode: 'normal' | 'pause' = isRunning ? 'normal' : 'pause'; diff --git a/src/app/features/focus-mode/store/focus-mode.reducer.ts b/src/app/features/focus-mode/store/focus-mode.reducer.ts index cd53eae04a..ee3cfb5a7b 100644 --- a/src/app/features/focus-mode/store/focus-mode.reducer.ts +++ b/src/app/features/focus-mode/store/focus-mode.reducer.ts @@ -3,30 +3,30 @@ import { cancelFocusSession, focusSessionDone, hideFocusOverlay, + pauseFocusSession, setFocusModeMode, setFocusSessionActivePage, setFocusSessionDuration, setFocusSessionTimeElapsed, - setFocusSessionTimeToGo, showFocusOverlay, startFocusSession, + unPauseFocusSession, } from './focus-mode.actions'; import { FocusModeMode, FocusModePage } from '../focus-mode.const'; import { LS } from '../../../core/persistence/storage-keys.const'; const DEFAULT_FOCUS_SESSION_DURATION = 25 * 60 * 1000; -const USE_REMAINING_SESSION_TIME_THRESHOLD = 60 * 1000; +// const USE_REMAINING_SESSION_TIME_THRESHOLD = 60 * 1000; + export const FOCUS_MODE_FEATURE_KEY = 'focusMode'; export interface State { isFocusOverlayShown: boolean; isFocusSessionRunning: boolean; focusSessionDuration: number; - focusSessionTimeToGo: number; focusSessionTimeElapsed: number; focusSessionActivePage: FocusModePage; mode: FocusModeMode; - lastFocusSessionDuration: number; } const focusModeModeFromLS = localStorage.getItem(LS.FOCUS_MODE_MODE); @@ -35,8 +35,6 @@ export const initialState: State = { isFocusOverlayShown: false, isFocusSessionRunning: false, focusSessionDuration: DEFAULT_FOCUS_SESSION_DURATION, - lastFocusSessionDuration: 0, - focusSessionTimeToGo: 0, focusSessionTimeElapsed: 0, focusSessionActivePage: FocusModePage.TaskSelection, mode: Object.values(FocusModeMode).includes(focusModeModeFromLS as any) @@ -60,16 +58,6 @@ export const focusModeReducer = createReducer( focusSessionDuration, })), - on(setFocusSessionTimeToGo, (state, { focusSessionTimeToGo }) => ({ - ...state, - focusSessionTimeToGo, - })), - - on(setFocusSessionTimeToGo, (state, { focusSessionTimeToGo }) => ({ - ...state, - focusSessionTimeToGo, - })), - on(setFocusSessionTimeElapsed, (state, { focusSessionTimeElapsed }) => ({ ...state, focusSessionTimeElapsed, @@ -78,7 +66,6 @@ export const focusModeReducer = createReducer( on(startFocusSession, (state) => ({ ...state, isFocusSessionRunning: true, - focusSessionTimeToGo: 0, focusSessionActivePage: FocusModePage.Main, // NOTE: not resetting since, we might want to continue // focusSessionTimeElapsed: 0, @@ -87,16 +74,31 @@ export const focusModeReducer = createReducer( ? state.focusSessionDuration : DEFAULT_FOCUS_SESSION_DURATION, })), - on(focusSessionDone, (state) => ({ + + on(focusSessionDone, (state, { isResetPlannedSessionDuration }) => { + return { + ...state, + isFocusSessionRunning: false, + isFocusOverlayShown: true, + ...(isResetPlannedSessionDuration + ? { + focusSessionDuration: DEFAULT_FOCUS_SESSION_DURATION, + } + : {}), + focusSessionActivePage: FocusModePage.SessionDone, + }; + }), + + on(pauseFocusSession, (state) => ({ ...state, isFocusSessionRunning: false, - isFocusOverlayShown: true, - lastFocusSessionDuration: state.focusSessionDuration, - focusSessionDuration: - state.focusSessionTimeToGo >= USE_REMAINING_SESSION_TIME_THRESHOLD - ? state.focusSessionTimeToGo - : DEFAULT_FOCUS_SESSION_DURATION, - focusSessionActivePage: FocusModePage.SessionDone, + })), + + on(unPauseFocusSession, (state, { idleTimeToAdd = 0 }) => ({ + ...state, + isFocusSessionRunning: true, + // NOTE: this is adjusted in the effect via session time + // focusSessionDuration: state.focusSessionDuration, })), on(showFocusOverlay, (state) => ({ diff --git a/src/app/features/focus-mode/store/focus-mode.selectors.ts b/src/app/features/focus-mode/store/focus-mode.selectors.ts index d361e54e36..7d4663b697 100644 --- a/src/app/features/focus-mode/store/focus-mode.selectors.ts +++ b/src/app/features/focus-mode/store/focus-mode.selectors.ts @@ -15,20 +15,11 @@ export const selectFocusSessionDuration = createSelector( selectFocusModeState, (state) => state.focusSessionDuration, ); -export const selectLastFocusSessionDuration = createSelector( - selectFocusModeState, - (state) => state.lastFocusSessionDuration, -); export const selectIsFocusOverlayShown = createSelector( selectFocusModeState, (state) => state.isFocusOverlayShown, ); -export const selectFocusSessionTimeToGo = createSelector( - selectFocusModeState, - (state) => state.focusSessionTimeToGo, -); - export const selectFocusSessionTimeElapsed = createSelector( selectFocusModeState, (state) => state.focusSessionTimeElapsed, @@ -38,10 +29,3 @@ export const selectFocusSessionActivePage = createSelector( selectFocusModeState, (state) => state.focusSessionActivePage, ); - -export const selectFocusSessionProgress = createSelector( - selectFocusModeState, - (state) => - ((state.focusSessionDuration - state.focusSessionTimeToGo) * 100) / - state.focusSessionDuration, -); diff --git a/src/app/features/idle/dialog-idle/dialog-idle.component.ts b/src/app/features/idle/dialog-idle/dialog-idle.component.ts index 0fa5afe267..6063bade25 100644 --- a/src/app/features/idle/dialog-idle/dialog-idle.component.ts +++ b/src/app/features/idle/dialog-idle/dialog-idle.component.ts @@ -144,6 +144,7 @@ export class DialogIdleComponent implements OnInit, OnDestroy { this._matDialogRef.close({ isResetBreakTimer: this.isResetBreakTimer, trackItems: res.trackItems, + wasFocusSessionRunning: this.data.wasFocusSessionRunning, }); } }); @@ -165,12 +166,14 @@ export class DialogIdleComponent implements OnInit, OnDestroy { isResetBreakTimer: this.isResetBreakTimer, trackItems: [], simpleCounterToggleBtnsWhenNoTrackItems: this.simpleCounterToggleBtns, + wasFocusSessionRunning: this.data.wasFocusSessionRunning, }); } trackAsBreak(): void { this._matDialogRef.close({ isResetBreakTimer: this.isResetBreakTimer, + wasFocusSessionRunning: this.data.wasFocusSessionRunning, trackItems: [ { type: 'BREAK', @@ -184,6 +187,7 @@ export class DialogIdleComponent implements OnInit, OnDestroy { track(): void { this._matDialogRef.close({ isResetBreakTimer: this.isResetBreakTimer, + wasFocusSessionRunning: this.data.wasFocusSessionRunning, trackItems: [ { type: 'TASK', diff --git a/src/app/features/idle/dialog-idle/dialog-idle.model.ts b/src/app/features/idle/dialog-idle/dialog-idle.model.ts index ad9438ebfa..13cda326d5 100644 --- a/src/app/features/idle/dialog-idle/dialog-idle.model.ts +++ b/src/app/features/idle/dialog-idle/dialog-idle.model.ts @@ -13,11 +13,13 @@ export interface DialogIdleReturnData { trackItems: IdleTrackItem[]; simpleCounterToggleBtnsWhenNoTrackItems?: SimpleCounterIdleBtn[]; isResetBreakTimer: boolean; + wasFocusSessionRunning: boolean; } export interface DialogIdlePassedData { enabledSimpleStopWatchCounters: SimpleCounter[]; lastCurrentTaskId: string | null; + wasFocusSessionRunning: boolean; } export interface DialogIdleSplitPassedData { diff --git a/src/app/features/idle/store/idle.actions.ts b/src/app/features/idle/store/idle.actions.ts index 2be3176f0b..a77ca98946 100644 --- a/src/app/features/idle/store/idle.actions.ts +++ b/src/app/features/idle/store/idle.actions.ts @@ -13,6 +13,7 @@ export const openIdleDialog = createAction( props<{ lastCurrentTaskId: string | null; enabledSimpleStopWatchCounters: SimpleCounter[]; + wasFocusSessionRunning: boolean; }>(), ); @@ -26,6 +27,7 @@ export const idleDialogResult = createAction( props<{ idleTime: number; isResetBreakTimer: boolean; + wasFocusSessionRunning: boolean; trackItems: IdleTrackItem[]; simpleCounterToggleBtnsWhenNoTrackItems?: SimpleCounterIdleBtn[]; }>(), diff --git a/src/app/features/idle/store/idle.effects.ts b/src/app/features/idle/store/idle.effects.ts index 9e0323d7b0..d8c2d8a7fa 100644 --- a/src/app/features/idle/store/idle.effects.ts +++ b/src/app/features/idle/store/idle.effects.ts @@ -45,6 +45,12 @@ import { DialogConfirmComponent } from '../../../ui/dialog-confirm/dialog-confir import { T } from '../../../t.const'; import { DateService } from 'src/app/core/date/date.service'; import { ipcIdleTime$ } from '../../../core/ipc-events'; +import { selectIsFocusSessionRunning } from '../../focus-mode/store/focus-mode.selectors'; +import { + focusSessionDone, + showFocusOverlay, + unPauseFocusSession, +} from '../../focus-mode/store/focus-mode.actions'; const DEFAULT_MIN_IDLE_TIME = 60000; const IDLE_POLL_INTERVAL = 1000; @@ -68,6 +74,7 @@ export class IdleEffects { // NOTE: needs to live forever since we can't unsubscribe from ipcEvent$ // TODO check if this works as expected private _electronIdleTime$: Observable = IS_ELECTRON ? ipcIdleTime$ : EMPTY; + private _isFocusSessionRunning$ = this._store.select(selectIsFocusSessionRunning); private _triggerIdleApis$ = IS_ELECTRON ? this._electronIdleTime$ @@ -118,8 +125,9 @@ export class IdleEffects { withLatestFrom( this._store.select(selectIdleTime), this._simpleCounterService.enabledSimpleStopWatchCounters$, + this._isFocusSessionRunning$, ), - map(([, idleTime, enabledSimpleStopWatchCounters]) => { + map(([, idleTime, enabledSimpleStopWatchCounters, isFocusSessionRunning]) => { // ALL IDLE SIDE EFFECTS // --------------------- if (IS_ELECTRON) { @@ -152,7 +160,11 @@ export class IdleEffects { // this._openDialog(enabledSimpleStopWatchCounters, lastCurrentTaskId); // finally open dialog - return openIdleDialog({ enabledSimpleStopWatchCounters, lastCurrentTaskId }); + return openIdleDialog({ + enabledSimpleStopWatchCounters, + lastCurrentTaskId, + wasFocusSessionRunning: isFocusSessionRunning, + }); }), ), ); @@ -163,22 +175,24 @@ export class IdleEffects { filter(() => !this._isDialogOpen), tap(() => (this._isDialogOpen = true)), // use exhaustMap to prevent opening up multiple dialogs - exhaustMap(({ enabledSimpleStopWatchCounters, lastCurrentTaskId }) => - this._matDialog - .open< - DialogIdleComponent, - DialogIdlePassedData, - DialogIdleReturnData | undefined - >(DialogIdleComponent, { - restoreFocus: true, - disableClose: true, - closeOnNavigation: false, - data: { - lastCurrentTaskId, - enabledSimpleStopWatchCounters, - }, - }) - .afterClosed(), + exhaustMap( + ({ enabledSimpleStopWatchCounters, lastCurrentTaskId, wasFocusSessionRunning }) => + this._matDialog + .open< + DialogIdleComponent, + DialogIdlePassedData, + DialogIdleReturnData | undefined + >(DialogIdleComponent, { + restoreFocus: true, + disableClose: true, + closeOnNavigation: false, + data: { + lastCurrentTaskId, + enabledSimpleStopWatchCounters, + wasFocusSessionRunning, + }, + }) + .afterClosed(), ), tap((dialogRes) => { if (!dialogRes) { @@ -191,6 +205,7 @@ export class IdleEffects { idleDialogResult({ ...dialogRes, idleTime, + // TODO }), ), tap(() => (this._isDialogOpen = false)), @@ -205,6 +220,7 @@ export class IdleEffects { trackItems, simpleCounterToggleBtnsWhenNoTrackItems, idleTime, + wasFocusSessionRunning, isResetBreakTimer, }) => { this._cancelIdlePoll(); @@ -215,6 +231,15 @@ export class IdleEffects { } if (trackItems.length === 0 && simpleCounterToggleBtnsWhenNoTrackItems) { + if (wasFocusSessionRunning) { + this._store.dispatch( + focusSessionDone({ + isResetPlannedSessionDuration: true, + }), + ); + this._store.dispatch(showFocusOverlay()); + } + const activatedItemNr = simpleCounterToggleBtnsWhenNoTrackItems.filter( (btn) => btn.isTrackTo, ).length; @@ -247,7 +272,6 @@ export class IdleEffects { return; } - // TODO remove TASK_AND_BREAK case completely const itemsWithMappedIdleTime = trackItems.map((trackItem) => ({ ...trackItem, time: trackItem.time === 'IDLE_TIME' ? idleTime : trackItem.time, @@ -268,6 +292,17 @@ export class IdleEffects { item.time, ); }); + if (wasFocusSessionRunning) { + this._store.dispatch( + focusSessionDone({ + isResetPlannedSessionDuration: true, + }), + ); + this._store.dispatch(showFocusOverlay()); + } + } else if (wasFocusSessionRunning) { + this._store.dispatch(unPauseFocusSession({ idleTimeToAdd: idleTime })); + this._store.dispatch(showFocusOverlay()); } const taskItems = itemsWithMappedIdleTime.filter( @@ -298,11 +333,11 @@ export class IdleEffects { ), ); - // constructor() { - // window.setTimeout(() => { - // this._store.dispatch(triggerIdle({ idleTime: 60 * 1000 })); - // }, 2700); - // } + constructor() { + window.setTimeout(() => { + this._store.dispatch(triggerIdle({ idleTime: 60 * 1000 })); + }, 8700); + } private _initIdlePoll(initialIdleTime: number): void { const idleStart = Date.now();