refactor(idle): change effect to allow handling multiple track items #1660

This commit is contained in:
Johannes Millan 2021-12-11 19:46:33 +01:00
parent e66834246e
commit af43ed3aef
9 changed files with 128 additions and 104 deletions

View file

@ -83,7 +83,7 @@
<mat-icon *ngIf="!isCreate">track_changes</mat-icon>
<mat-icon *ngIf="isCreate">add</mat-icon>
<!-- <mat-icon style="margin-left: -4px;">free_breakfast</mat-icon>-->
{{T.F.TIME_TRACKING.D_IDLE.TASK_BREAK|translate}}
{{T.F.TIME_TRACKING.D_IDLE.TASK_AND_BREAK|translate}}
<mat-icon>free_breakfast</mat-icon>
</button>
</div>

View file

@ -14,8 +14,6 @@ import { T } from '../../../t.const';
import { ipcRenderer } from 'electron';
import { IPC } from '../../../../../electron/ipc-events.const';
import { SimpleCounterService } from '../../simple-counter/simple-counter.service';
import { first } from 'rxjs/operators';
import { DialogConfirmComponent } from '../../../ui/dialog-confirm/dialog-confirm.component';
import { ElectronService } from '../../../core/electron/electron.service';
import { IS_ELECTRON } from '../../../app.constants';
import { SimpleCounter } from '../../simple-counter/simple-counter.model';
@ -73,8 +71,6 @@ export class DialogIdleComponent implements OnInit, OnDestroy {
isWasEnabledBefore: isOn,
} as SimpleCounterIdleBtn),
);
console.log(this.simpleCounterToggleBtns);
_matDialogRef.disableClose = true;
}
@ -109,67 +105,36 @@ export class DialogIdleComponent implements OnInit, OnDestroy {
}
skipTrack(): void {
const activatedItemNr = this.simpleCounterToggleBtns.filter(
(btn) => btn.isTrackTo,
).length;
if (activatedItemNr > 0) {
this._matDialog
.open(DialogConfirmComponent, {
restoreFocus: true,
data: {
cancelTxt: T.F.TIME_TRACKING.D_IDLE.SIMPLE_CONFIRM_COUNTER_CANCEL,
okTxt: T.F.TIME_TRACKING.D_IDLE.SIMPLE_CONFIRM_COUNTER_OK,
message: T.F.TIME_TRACKING.D_IDLE.SIMPLE_COUNTER_CONFIRM_TXT,
translateParams: {
nr: activatedItemNr,
},
},
})
.afterClosed()
.subscribe((isConfirm: boolean) => {
if (isConfirm) {
this._updateSimpleCounterValues();
}
});
}
this._matDialogRef.close({
task: null,
isResetBreakTimer: true,
isTrackAsBreak: false,
trackItems: [],
simpleCounterToggleBtnsWhenNoTrackItems: this.simpleCounterToggleBtns,
});
}
trackAsBreak(): void {
this._updateSimpleCounterValues();
this._matDialogRef.close({
task: null,
isResetBreakTimer: true,
isTrackAsBreak: true,
trackItems: [
{
type: 'BREAK',
time: 'IDLE_TIME',
simpleCounterToggleBtns: this.simpleCounterToggleBtns,
},
],
});
}
track(isTrackAsBreak: boolean = false): void {
this._updateSimpleCounterValues();
this._matDialogRef.close({
task: this.selectedTask || this.newTaskTitle,
isTrackAsBreak,
isResetBreakTimer: isTrackAsBreak,
});
}
private async _updateSimpleCounterValues(): Promise<void> {
const idleTime = await this.idleTime$.pipe(first()).toPromise();
this.simpleCounterToggleBtns.forEach((tglBtn) => {
if (tglBtn.isTrackTo) {
this._simpleCounterService.increaseCounterToday(tglBtn.id, idleTime);
if (tglBtn.isWasEnabledBefore) {
this._simpleCounterService.toggleCounter(tglBtn.id);
}
}
trackItems: [
{
type: isTrackAsBreak ? 'TASK_AND_BREAK' : 'TASK',
time: 'IDLE_TIME',
simpleCounterToggleBtns: this.simpleCounterToggleBtns,
...(this.isCreate
? { title: this.newTaskTitle as string }
: { task: this.selectedTask as Task }),
},
],
});
}
}

View file

@ -10,12 +10,19 @@ export interface SimpleCounterIdleBtn {
}
export interface DialogIdleReturnData {
task: null | Task | string | undefined;
isResetBreakTimer: boolean;
isTrackAsBreak: boolean;
trackItems: IdleTrackItem[];
simpleCounterToggleBtnsWhenNoTrackItems?: SimpleCounterIdleBtn[];
}
export interface DialogIdlePassedData {
enabledSimpleStopWatchCounters: SimpleCounter[];
lastCurrentTaskId: string | null;
}
export interface IdleTrackItem {
type: 'BREAK' | 'TASK' | 'TASK_AND_BREAK';
time: number | 'IDLE_TIME';
simpleCounterToggleBtns: SimpleCounterIdleBtn[];
task?: Task;
title?: string;
}

View file

@ -9,7 +9,7 @@
<mat-button-toggle value="BREAK">
<mat-icon>free_breakfast</mat-icon>
</mat-button-toggle>
<mat-button-toggle value="TASK_BREAK">
<mat-button-toggle value="TASK_AND_BREAK">
<mat-icon>free_breakfast</mat-icon>
+
<mat-icon>track_changes</mat-icon>

View file

@ -12,17 +12,9 @@ import { Store } from '@ngrx/store';
import { SimpleCounterService } from '../../../simple-counter/simple-counter.service';
import { first } from 'rxjs/operators';
import { T } from 'src/app/t.const';
import { SimpleCounterIdleBtn } from '../dialog-idle.model';
import { SimpleCounterIdleBtn, IdleTrackItem } from '../dialog-idle.model';
import { dirtyDeepCopy } from '../../../../util/dirtyDeepCopy';
interface TrackToItem {
type: 'BREAK' | 'TASK' | 'TASK_BREAK';
time: number;
simpleCounterToggleBtns: SimpleCounterIdleBtn[];
task?: Task;
title?: string;
}
@Component({
selector: 'idle-split-mode',
templateUrl: './idle-split-mode.component.html',
@ -36,7 +28,7 @@ export class IdleSplitModeComponent implements OnInit {
@Input() prevSelectedTask: Task | null = null;
idleTime$ = this._store.select(selectIdleTime);
trackToItems: TrackToItem[] = [];
trackToItems: IdleTrackItem[] = [];
@Output() cancel = new EventEmitter();
@Output() save = new EventEmitter();
@ -78,7 +70,7 @@ export class IdleSplitModeComponent implements OnInit {
];
}
onTaskChange(item: TrackToItem, taskOrTaskTitle: Task | string): void {
onTaskChange(item: IdleTrackItem, taskOrTaskTitle: Task | string): void {
const isCreate = typeof taskOrTaskTitle === 'string';
if (isCreate) {
item.title = taskOrTaskTitle as string;

View file

@ -1,6 +1,6 @@
import { createAction, props } from '@ngrx/store';
import { SimpleCounter } from '../../simple-counter/simple-counter.model';
import { Task } from '../../tasks/task.model';
import { IdleTrackItem, SimpleCounterIdleBtn } from '../dialog-idle/dialog-idle.model';
export const triggerIdle = createAction(
'[Idle] Trigger Idle',
@ -24,10 +24,8 @@ export const setIdleTime = createAction(
export const idleDialogResult = createAction(
'[Idle] Dialog result',
props<{
timeSpent: number;
selectedTaskOrTitle: Task | string;
isResetBreakTimer: boolean;
isTrackAsBreak: boolean;
trackItems: IdleTrackItem[];
simpleCounterToggleBtnsWhenNoTrackItems?: SimpleCounterIdleBtn[];
}>(),
);

View file

@ -43,8 +43,12 @@ import { devError } from '../../../util/dev-error';
import {
DialogIdlePassedData,
DialogIdleReturnData,
IdleTrackItem,
SimpleCounterIdleBtn,
} from '../dialog-idle/dialog-idle.model';
import { isNotNullOrUndefined } from '../../../util/is-not-null-or-undefined';
import { DialogConfirmComponent } from '../../../ui/dialog-confirm/dialog-confirm.component';
import { T } from '../../../t.const';
const DEFAULT_MIN_IDLE_TIME = 60000;
const IDLE_POLL_INTERVAL = 1000;
@ -57,7 +61,6 @@ export class IdleEffects {
private _triggerIdleApis$ = IS_ELECTRON
? fromEvent(this._electronService.ipcRenderer as IpcRenderer, IPC.IDLE_TIME).pipe(
tap((v) => console.log('IIIIIIIIII', v)),
map(([ev, idleTimeInMs]: any) => idleTimeInMs as number),
)
: this._chromeExtensionInterfaceService.onReady$.pipe(
@ -175,15 +178,7 @@ export class IdleEffects {
}
}),
isNotNullOrUndefined(),
withLatestFrom(this._store.select(selectIdleTime)),
map(([{ task, isResetBreakTimer, isTrackAsBreak }, idleTimeI]) =>
idleDialogResult({
timeSpent: idleTimeI,
selectedTaskOrTitle: task as any,
isResetBreakTimer,
isTrackAsBreak,
}),
),
map((dialogRes) => idleDialogResult(dialogRes)),
tap(() => (this._isDialogOpen = false)),
),
);
@ -191,31 +186,84 @@ export class IdleEffects {
onIdleDialogResult$ = createEffect(() =>
this.actions$.pipe(
ofType(idleDialogResult),
tap(({ timeSpent, selectedTaskOrTitle, isResetBreakTimer, isTrackAsBreak }) => {
if (isResetBreakTimer || isTrackAsBreak) {
withLatestFrom(this._store.select(selectIdleTime)),
tap(([{ trackItems, simpleCounterToggleBtnsWhenNoTrackItems }, idleTime]) => {
this._cancelIdlePoll();
if (trackItems.length === 0 && simpleCounterToggleBtnsWhenNoTrackItems) {
const activatedItemNr = simpleCounterToggleBtnsWhenNoTrackItems.filter(
(btn) => btn.isTrackTo,
).length;
// TODO maybe move to effect
if (activatedItemNr > 0) {
this._matDialog
.open(DialogConfirmComponent, {
restoreFocus: true,
data: {
cancelTxt: T.F.TIME_TRACKING.D_IDLE.SIMPLE_CONFIRM_COUNTER_CANCEL,
okTxt: T.F.TIME_TRACKING.D_IDLE.SIMPLE_CONFIRM_COUNTER_OK,
message: T.F.TIME_TRACKING.D_IDLE.SIMPLE_COUNTER_CONFIRM_TXT,
translateParams: {
nr: activatedItemNr,
},
},
})
.afterClosed()
.subscribe((isConfirm: boolean) => {
if (isConfirm) {
// TODO maybe move to effect
this._updateSimpleCounterValues(
simpleCounterToggleBtnsWhenNoTrackItems,
idleTime,
);
}
});
}
return;
}
const itemsWithMappedIdleTime = trackItems.map((trackItem) => ({
...trackItem,
time: trackItem.time === 'IDLE_TIME' ? idleTime : trackItem.time,
}));
itemsWithMappedIdleTime.forEach((item) => {
this._updateSimpleCounterValues(item.simpleCounterToggleBtns, item.time);
});
const breakItems = itemsWithMappedIdleTime.filter(
(item: IdleTrackItem) =>
item.type === 'BREAK' || item.type === 'TASK_AND_BREAK',
);
if (breakItems.length) {
this._store.dispatch(triggerResetBreakTimer());
breakItems.forEach((item) => {
this._workContextService.addToBreakTimeForActiveContext(undefined, item.time);
});
}
if (isTrackAsBreak) {
this._workContextService.addToBreakTimeForActiveContext(undefined, timeSpent);
}
if (selectedTaskOrTitle) {
if (typeof selectedTaskOrTitle === 'string') {
const currId = this._taskService.add(selectedTaskOrTitle, false, {
timeSpent,
const taskItems = itemsWithMappedIdleTime.filter(
(item: IdleTrackItem) => item.type === 'TASK' || item.type === 'TASK_AND_BREAK',
);
let taskItemId: string | undefined;
taskItems.forEach((taskItem) => {
if (typeof taskItem.title === 'string') {
taskItemId = this._taskService.add(taskItem.title, false, {
timeSpent: taskItem.time,
timeSpentOnDay: {
[getWorklogStr()]: timeSpent,
[getWorklogStr()]: taskItem.time,
},
});
this._taskService.setCurrentId(currId);
} else {
this._taskService.addTimeSpent(selectedTaskOrTitle, timeSpent);
this._taskService.setCurrentId(selectedTaskOrTitle.id);
} else if (taskItem.task) {
taskItemId = taskItem.task.id;
this._taskService.addTimeSpent(taskItem.task, taskItem.time);
}
}
});
this._cancelIdlePoll();
if (taskItems.length === 1 && taskItemId) {
this._taskService.setCurrentId(taskItemId);
}
}),
// unset idle at the end
mapTo(resetIdle()),
@ -258,4 +306,18 @@ export class IdleEffects {
}
this._isFrontEndIdlePollRunning = false;
}
private async _updateSimpleCounterValues(
simpleCounterToggleBtns: SimpleCounterIdleBtn[],
idleTime: number,
): Promise<void> {
simpleCounterToggleBtns.forEach((tglBtn) => {
if (tglBtn.isTrackTo) {
this._simpleCounterService.increaseCounterToday(tglBtn.id, idleTime);
if (tglBtn.isWasEnabledBefore) {
this._simpleCounterService.toggleCounter(tglBtn.id);
}
}
});
}
}

View file

@ -923,7 +923,7 @@ const T = {
'F.TIME_TRACKING.D_IDLE.SIMPLE_COUNTER_TOOLTIP_DISABLE',
SKIP: 'F.TIME_TRACKING.D_IDLE.SKIP',
TASK: 'F.TIME_TRACKING.D_IDLE.TASK',
TASK_BREAK: 'F.TIME_TRACKING.D_IDLE.TASK_BREAK',
TASK_AND_BREAK: 'F.TIME_TRACKING.D_IDLE.TASK_BREAK',
TRACK_TO: 'F.TIME_TRACKING.D_IDLE.TRACK_TO',
},
D_TRACKING_REMINDER: {

View file

@ -905,7 +905,7 @@
"IDLE_FOR": "You have been idle for:",
"SIMPLE_CONFIRM_COUNTER_CANCEL": "Skip",
"SIMPLE_CONFIRM_COUNTER_OK": "Track",
"SIMPLE_COUNTER_CONFIRM_TXT": "You selected skip, but activated {{nr}1} simple counter button(s). Do you want to track the idle time to them?",
"SIMPLE_COUNTER_CONFIRM_TXT": "You selected skip, but activated {{nr}} simple counter button(s). Do you want to track the idle time to them?",
"SIMPLE_COUNTER_TOOLTIP": "Click to track to {{title}}",
"SIMPLE_COUNTER_TOOLTIP_DISABLE": "Click to NOT track to {{title}}",
"SKIP": "Skip",