mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-29 18:50:07 +00:00
feat(focusMode): better integrate idle case
This commit is contained in:
parent
ddd822b385
commit
5bfb6bcf64
12 changed files with 247 additions and 143 deletions
|
|
@ -8,7 +8,9 @@
|
|||
></task-title>
|
||||
<div class="progress-wrapper">
|
||||
@if (isCountTimeDown) {
|
||||
<progress-circle [progress]="sessionProgress$ | async"></progress-circle>
|
||||
<progress-circle
|
||||
[progress]="focusModeService.sessionProgress$ | async"
|
||||
></progress-circle>
|
||||
} @else {
|
||||
<progress-circle [autoRotationDuration]="60000"></progress-circle>
|
||||
}
|
||||
|
|
@ -19,7 +21,7 @@
|
|||
>
|
||||
<!-- {{focusModeTimeToGo / 60000}}min-->
|
||||
@if (isCountTimeDown) {
|
||||
{{ timeToGo$ | async | msToMinuteClockString }}
|
||||
{{ focusModeService.timeToGo$ | async | msToMinuteClockString }}
|
||||
} @else {
|
||||
{{ timeElapsed$ | async | msToMinuteClockString }}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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: () => {
|
||||
|
|
|
|||
103
src/app/features/focus-mode/focus-mode.service.ts
Normal file
103
src/app/features/focus-mode/focus-mode.service.ts
Normal file
|
|
@ -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<number> = interval(TICK_DURATION).pipe(
|
||||
switchMap(() => of(Date.now())),
|
||||
pairwise(),
|
||||
map(([a, b]) => b - a),
|
||||
);
|
||||
|
||||
private _tick$: Observable<number> = this._isRunning$.pipe(
|
||||
switchMap((isRunning) => (isRunning ? this._timer$ : EMPTY)),
|
||||
map((tick) => tick * -1),
|
||||
);
|
||||
|
||||
currentSessionTime$: Observable<number> = 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<number> = combineLatest([
|
||||
this.currentSessionTime$.pipe(startWith(0)),
|
||||
this._plannedSessionDuration$,
|
||||
]).pipe(
|
||||
tap((v) => console.log('timeToGo$ params', v)),
|
||||
map(
|
||||
([currentSessionTime, plannedSessionDuration]) =>
|
||||
plannedSessionDuration - currentSessionTime,
|
||||
),
|
||||
);
|
||||
// timeToGo$: Observable<number> = 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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<number> = interval(TICK_DURATION).pipe(
|
||||
switchMap(() => of(Date.now())),
|
||||
pairwise(),
|
||||
map(([a, b]) => b - a),
|
||||
);
|
||||
|
||||
private _tick$: Observable<number> = this._isRunning$.pipe(
|
||||
switchMap((isRunning) => (isRunning ? this._timer$ : EMPTY)),
|
||||
map((tick) => tick * -1),
|
||||
);
|
||||
|
||||
private _currentSessionTime$: Observable<number> = 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<unknown> = createEffect(
|
||||
() =>
|
||||
|
|
@ -149,6 +117,7 @@ export class FocusModeEffects {
|
|||
{ dispatch: false },
|
||||
);
|
||||
|
||||
// TODO check if needed
|
||||
handleIdleCurrentTaskDeSelection$: Observable<unknown> = 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';
|
||||
|
|
|
|||
|
|
@ -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<State>(
|
|||
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<State>(
|
|||
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>(
|
|||
? 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) => ({
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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[];
|
||||
}>(),
|
||||
|
|
|
|||
|
|
@ -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<number> = 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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue