feat: move task stuff to save to db effects

This commit is contained in:
Johannes Millan 2025-05-08 17:49:30 +02:00
parent d0f3bbbd56
commit 085d7eeff6
4 changed files with 105 additions and 197 deletions

View file

@ -1,155 +0,0 @@
import { inject, Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import {
__updateMultipleTaskSimple,
addReminderIdToTask,
addSubTask,
addTask,
convertToMainTask,
deleteTask,
deleteTasks,
moveSubTask,
moveSubTaskDown,
moveSubTaskToBottom,
moveSubTaskToTop,
moveSubTaskUp,
moveToArchive_,
moveToOtherProject,
removeReminderFromTask,
removeTagsForAllTasks,
removeTimeSpent,
reScheduleTaskWithTime,
restoreTask,
roundTimeSpentForDay,
scheduleTaskWithTime,
toggleTaskHideSubTasks,
undoDeleteTask,
unScheduleTask,
updateTask,
updateTaskTags,
updateTaskUi,
} from './task.actions';
import { select, Store } from '@ngrx/store';
import { auditTime, first, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { selectTaskFeatureState } from './task.selectors';
import { TaskState } from '../task.model';
import { environment } from '../../../../environments/environment';
import { addTaskRepeatCfgToTask } from '../../task-repeat-cfg/store/task-repeat-cfg.actions';
import {
addTaskAttachment,
deleteTaskAttachment,
updateTaskAttachment,
} from '../task-attachment/task-attachment.actions';
import { PlannerActions } from '../../planner/store/planner.actions';
import { deleteProject } from '../../project/store/project.actions';
import { PfapiService } from '../../../pfapi/pfapi.service';
import { TimeTrackingActions } from '../../time-tracking/store/time-tracking.actions';
import { TIME_TRACKING_TO_DB_INTERVAL } from '../../../app.constants';
import { planTasksForToday } from '../../tag/store/tag.actions';
@Injectable()
export class TaskDbEffects {
private _actions$ = inject(Actions);
private _store$ = inject<Store<any>>(Store);
private _pfapiService = inject(PfapiService);
updateTaskAuditTime$: any = createEffect(
() =>
this._actions$.pipe(
ofType(
// TIME TRACKING
TimeTrackingActions.addTimeSpent,
),
auditTime(TIME_TRACKING_TO_DB_INTERVAL),
switchMap(() => this._store$.pipe(select(selectTaskFeatureState), first())),
tap((taskState) => this._saveToLs(taskState, true)),
),
{ dispatch: false },
);
updateTask$: any = createEffect(
() =>
this._actions$.pipe(
ofType(
addTask,
restoreTask,
deleteTask,
deleteTasks,
undoDeleteTask,
addSubTask,
convertToMainTask,
removeTimeSpent,
// setCurrentTask,
// unsetCurrentTask,
// toggleStart,
updateTask,
__updateMultipleTaskSimple,
updateTaskTags,
removeTagsForAllTasks,
moveSubTask,
moveSubTaskUp,
moveSubTaskDown,
moveSubTaskToTop,
moveSubTaskToBottom,
moveToArchive_,
moveToOtherProject,
roundTimeSpentForDay,
// REMINDER
addReminderIdToTask,
scheduleTaskWithTime,
reScheduleTaskWithTime,
unScheduleTask,
removeReminderFromTask,
// ATTACHMENT ACTIONS
addTaskAttachment,
deleteTaskAttachment,
updateTaskAttachment,
// RELATED ACTIONS
addTaskRepeatCfgToTask,
// PLANNER
PlannerActions.transferTask,
PlannerActions.moveBeforeTask,
PlannerActions.planTaskForDay,
planTasksForToday,
// PROJECT
deleteProject,
),
withLatestFrom(this._store$.pipe(select(selectTaskFeatureState))),
tap(([, taskState]) => this._saveToLs(taskState, true)),
),
{ dispatch: false },
);
updateTaskUi$: any = createEffect(
() =>
this._actions$.pipe(
ofType(updateTaskUi, toggleTaskHideSubTasks),
withLatestFrom(this._store$.pipe(select(selectTaskFeatureState))),
tap(([, taskState]) => this._saveToLs(taskState)),
),
{ dispatch: false },
);
// @debounce(50)
private _saveToLs(
taskState: TaskState,
isUpdateRevAndLastUpdate: boolean = false,
): void {
this._pfapiService.m.task.save(
{
...taskState,
// make sure those are never set to something
selectedTaskId: environment.production ? null : taskState.selectedTaskId,
currentTaskId: null,
},
{ isUpdateRevAndLastUpdate },
);
}
}

View file

@ -27,20 +27,6 @@ export const timeTrackingReducer = createReducer(
on(TimeTrackingActions.addTimeSpent, (state, { task, date }) => {
const isUpdateProject = !!task.projectId;
console.log({
...state.tag,
...([TODAY_TAG.id, ...task.tagIds] as string[]).reduce((acc, tagId) => {
acc[tagId] = {
...state.tag[tagId],
[date]: {
...state.tag[tagId]?.[date],
e: roundTsToMinutes(Date.now()),
s: roundTsToMinutes(state.tag[tagId]?.[date]?.s || Date.now()),
},
};
return acc;
}, {}),
});
return {
...state,

View file

@ -62,7 +62,6 @@ import {
import { TaskRepeatCfgEffects } from '../features/task-repeat-cfg/store/task-repeat-cfg.effects';
import { TaskDueEffects } from '../features/tasks/store/task-due.effects';
import { TASK_FEATURE_NAME, taskReducer } from '../features/tasks/store/task.reducer';
import { TaskDbEffects } from '../features/tasks/store/task-db.effects';
import { TaskInternalEffects } from '../features/tasks/store/task-internal.effects';
import { TaskRelatedModelEffects } from '../features/tasks/store/task-related-model.effects';
import { TaskReminderEffects } from '../features/tasks/store/task-reminder.effects';
@ -150,7 +149,6 @@ import { SaveToDbEffects } from './shared/save-to-db.effects';
StoreModule.forFeature(TASK_FEATURE_NAME, taskReducer),
EffectsModule.forFeature([
TaskDbEffects,
TaskInternalEffects,
TaskRelatedModelEffects,
TaskReminderEffects,

View file

@ -1,7 +1,7 @@
import { inject, Injectable } from '@angular/core';
import { Actions, createEffect } from '@ngrx/effects';
import { skip, switchMap } from 'rxjs/operators';
import { MemoizedSelector, Store } from '@ngrx/store';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { auditTime, filter, first, map, skip, switchMap, tap } from 'rxjs/operators';
import { MemoizedSelector, select, Store } from '@ngrx/store';
import { PfapiService } from '../../pfapi/pfapi.service';
import { selectBoardsState } from '../../features/boards/store/boards.selectors';
import { selectConfigFeatureState } from '../../features/config/store/global-config.reducer';
@ -17,10 +17,17 @@ import { selectTaskRepeatCfgFeatureState } from '../../features/task-repeat-cfg/
import { selectMetricFeatureState } from '../../features/metric/store/metric.selectors';
import { DataInitStateService } from '../../core/data-init/data-init-state.service';
import { RootState } from '../root-state';
// NOTE task state is a bit special, since we have ui only actions and we throttle addTimeSpent
// NOTE TimeTrackingState is also a bit special, since we throttle addTimeSpent
// NOTE: reminders are saved in reminderService
import { selectTaskFeatureState } from '../../features/tasks/store/task.selectors';
import { TimeTrackingActions } from '../../features/time-tracking/store/time-tracking.actions';
import {
setCurrentTask,
toggleStart,
toggleTaskHideSubTasks,
unsetCurrentTask,
updateTaskUi,
} from '../../features/tasks/store/task.actions';
import { TIME_TRACKING_TO_DB_INTERVAL } from '../../app.constants';
import { environment } from '../../../environments/environment';
@Injectable()
export class SaveToDbEffects {
@ -29,7 +36,64 @@ export class SaveToDbEffects {
private _pfapiService = inject(PfapiService);
private _dataInitStateService = inject(DataInitStateService);
// Infer the correct type for each modelKey from PfapiService['m']
// --------------------------------------------------
// NOTE TimeTrackingState is also a bit special, since we throttle addTimeSpent => TimeTrackingEffects
// NOTE: reminders are saved in reminderService => ReminderService
// --------------------------------------------------
tag$ = this.createSaveEffect(selectTagFeatureState, 'tag');
project$ = this.createSaveEffect(selectProjectFeatureState, 'project');
globalCfg$ = this.createSaveEffect(selectConfigFeatureState, 'globalConfig');
planner$ = this.createSaveEffect(selectPlannerState, 'planner');
boards$ = this.createSaveEffect(selectBoardsState, 'boards');
issueProvider$ = this.createSaveEffect(selectIssueProviderState, 'issueProvider');
note$ = this.createSaveEffect(selectNoteFeatureState, 'note');
metric$ = this.createSaveEffect(selectMetricFeatureState, 'metric');
improvement$ = this.createSaveEffect(selectImprovementFeatureState, 'improvement');
obstruction$ = this.createSaveEffect(selectObstructionFeatureState, 'obstruction');
simpleCounter$ = this.createSaveEffect(
selectSimpleCounterFeatureState,
'simpleCounter',
);
taskRepeatCfg$ = this.createSaveEffect(
selectTaskRepeatCfgFeatureState,
'taskRepeatCfg',
);
task$ = this.createSaveEffectWithFilter(selectTaskFeatureState, 'task', [
TimeTrackingActions.addTimeSpent.type,
setCurrentTask.type,
updateTaskUi.type,
toggleTaskHideSubTasks.type,
unsetCurrentTask.type,
toggleStart.type,
]);
updateTaskAuditTime$ = createEffect(
() =>
this._actions.pipe(
ofType(
// TIME TRACKING
TimeTrackingActions.addTimeSpent,
),
auditTime(TIME_TRACKING_TO_DB_INTERVAL),
switchMap(() => this._store.pipe(select(selectTaskFeatureState), first())),
tap((taskState) => {
this._pfapiService.m.task.save(
{
...taskState,
// make sure those are never set to something
selectedTaskId: environment.production ? null : taskState.selectedTaskId,
currentTaskId: null,
},
{ isUpdateRevAndLastUpdate: true },
);
}),
),
{ dispatch: false },
);
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
private createSaveEffect<K extends keyof typeof this._pfapiService.m>(
selector: MemoizedSelector<
@ -53,22 +117,37 @@ export class SaveToDbEffects {
);
}
tag$ = this.createSaveEffect(selectTagFeatureState, 'tag');
project$ = this.createSaveEffect(selectProjectFeatureState, 'project');
globalCfg$ = this.createSaveEffect(selectConfigFeatureState, 'globalConfig');
planner$ = this.createSaveEffect(selectPlannerState, 'planner');
boards$ = this.createSaveEffect(selectBoardsState, 'boards');
issueProvider$ = this.createSaveEffect(selectIssueProviderState, 'issueProvider');
note$ = this.createSaveEffect(selectNoteFeatureState, 'note');
metric$ = this.createSaveEffect(selectMetricFeatureState, 'metric');
improvement$ = this.createSaveEffect(selectImprovementFeatureState, 'improvement');
obstruction$ = this.createSaveEffect(selectObstructionFeatureState, 'obstruction');
simpleCounter$ = this.createSaveEffect(
selectSimpleCounterFeatureState,
'simpleCounter',
);
taskRepeatCfg$ = this.createSaveEffect(
selectTaskRepeatCfgFeatureState,
'taskRepeatCfg',
);
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
private createSaveEffectWithFilter<K extends keyof typeof this._pfapiService.m>(
selector: MemoizedSelector<
RootState,
Parameters<(typeof this._pfapiService.m)[K]['save']>[0]
>,
modelKey: K,
ignoredActionsTypes: string[],
) {
return createEffect(
() =>
this._dataInitStateService.isAllDataLoadedInitially$.pipe(
switchMap(() => this._store.select(selector)),
skip(1),
switchMap((state) =>
// thankfully the next action after is the action that triggered the change
this._actions.pipe(
first(),
map((a) => [state, a]),
),
),
tap(([state, action]) => console.log('1', action.type)),
filter(([state, action]) => !ignoredActionsTypes.includes(action.type)),
tap(([state, action]) => console.log('22', action.type)),
switchMap(([state]) =>
this._pfapiService.m[modelKey].save(state, {
isUpdateRevAndLastUpdate: true,
}),
),
),
{ dispatch: false },
);
}
}