diff --git a/src/app/features/tasks/store/short-syntax.effects.ts b/src/app/features/tasks/store/short-syntax.effects.ts new file mode 100644 index 0000000000..e510a97c66 --- /dev/null +++ b/src/app/features/tasks/store/short-syntax.effects.ts @@ -0,0 +1,186 @@ +import { Injectable } from '@angular/core'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { + addTask, + moveToOtherProject, + scheduleTask, + updateTask, + updateTaskTags, +} from './task.actions'; +import { + concatMap, + filter, + map, + mapTo, + mergeMap, + tap, + withLatestFrom, +} from 'rxjs/operators'; +import { TaskReminderOptionId } from '../task.model'; +import { GlobalConfigService } from '../../config/global-config.service'; +import { unique } from '../../../util/unique'; +import { TaskService } from '../task.service'; +import { EMPTY, Observable, of } from 'rxjs'; +import { ProjectService } from '../../project/project.service'; +import { TagService } from '../../tag/tag.service'; +import { shortSyntax } from '../short-syntax.util'; +import { remindOptionToMilliseconds } from '../util/remind-option-to-milliseconds'; +import { environment } from '../../../../environments/environment'; +import { SnackService } from '../../../core/snack/snack.service'; +import { T } from '../../../t.const'; + +@Injectable() +export class ShortSyntaxEffects { + shortSyntax$: any = createEffect(() => + this._actions$.pipe( + ofType(addTask, updateTask), + filter((action): boolean => { + if (action.isIgnoreShortSyntax) { + return false; + } + if (action.type !== updateTask.type) { + return true; + } + const changeProps = Object.keys(action.task.changes); + // we only want to execute this for task title updates + return changeProps.length === 1 && changeProps[0] === 'title'; + }), + // dirty fix to execute this after setDefaultProjectId$ effect + concatMap((originalAction): Observable => { + return this._taskService.getByIdOnce$(originalAction.task.id as string).pipe( + map((task) => ({ + task, + originalAction, + })), + ); + }), + withLatestFrom( + this._tagService.tags$, + this._projectService.list$, + this._globalConfigService.misc$.pipe( + map((misc) => misc.defaultProjectId), + concatMap((defaultProjectId) => + defaultProjectId + ? this._projectService.getByIdOnce$(defaultProjectId).pipe( + tap((project) => { + if (!project) { + // to avoid further data inconsistencies + throw new Error('Default Project not found'); + } + }), + mapTo(defaultProjectId), + ) + : of(defaultProjectId), + ), + ), + ), + mergeMap(([{ task, originalAction }, tags, projects, defaultProjectId]) => { + const r = shortSyntax(task, tags, projects); + if (environment.production) { + console.log('shortSyntax', r); + } + const isAddDefaultProjectIfNecessary: boolean = + !!defaultProjectId && + !task.projectId && + !task.parentId && + task.projectId !== defaultProjectId && + originalAction.type === addTask.type; + + if (!r) { + if (isAddDefaultProjectIfNecessary) { + return [ + moveToOtherProject({ + task, + targetProjectId: defaultProjectId as string, + }), + ]; + } + return EMPTY; + } + + const actions: any[] = []; + const tagIds: string[] = [...(r.taskChanges.tagIds || task.tagIds)]; + const { taskChanges } = r; + + actions.push( + updateTask({ + task: { + id: task.id, + changes: r.taskChanges, + }, + isIgnoreShortSyntax: true, + }), + ); + if (taskChanges.plannedAt && !taskChanges.reminderId) { + const { plannedAt } = taskChanges; + const schedule = scheduleTask({ + task, + plannedAt, + remindAt: remindOptionToMilliseconds(plannedAt, TaskReminderOptionId.AtStart), + isMoveToBacklog: false, + }); + actions.push(schedule); + } + if (r.projectId && r.projectId !== task.projectId && !task.parentId) { + if (task.repeatCfgId) { + this._snackService.open({ + ico: 'warning', + msg: T.F.TASK.S.CANNOT_ASSIGN_PROJECT_FOR_REPEATABLE_TASK, + }); + } else { + actions.push( + moveToOtherProject({ + task, + targetProjectId: r.projectId, + }), + ); + } + } else if (isAddDefaultProjectIfNecessary) { + actions.push( + moveToOtherProject({ + task, + targetProjectId: defaultProjectId as string, + }), + ); + } + + if (r.newTagTitles.length) { + r.newTagTitles.forEach((newTagTitle) => { + const { action, id } = this._tagService.getAddTagActionAndId({ + title: newTagTitle, + }); + tagIds.push(id); + actions.push(action); + }); + } + + if (tagIds && tagIds.length) { + const isEqualTags = JSON.stringify(tagIds) === JSON.stringify(task.tagIds); + if (!task.tagIds) { + throw new Error('Task Old TagIds need to be passed'); + } + if (!isEqualTags) { + actions.push( + updateTaskTags({ + task, + newTagIds: unique(tagIds), + oldTagIds: task.tagIds, + }), + ); + } + } + + return actions; + }), + ), + ); + + constructor( + private _actions$: Actions, + private _taskService: TaskService, + private _tagService: TagService, + private _projectService: ProjectService, + private _globalConfigService: GlobalConfigService, + private _snackService: SnackService, + ) {} +} diff --git a/src/app/features/tasks/store/task-related-model.effects.ts b/src/app/features/tasks/store/task-related-model.effects.ts index c9bfeb663d..ade85ae1a8 100644 --- a/src/app/features/tasks/store/task-related-model.effects.ts +++ b/src/app/features/tasks/store/task-related-model.effects.ts @@ -1,34 +1,15 @@ import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { - addTask, addTimeSpent, moveToArchive, - moveToOtherProject, restoreTask, - scheduleTask, updateTask, updateTaskTags, } from './task.actions'; -import { - concatMap, - filter, - first, - map, - mapTo, - mergeMap, - switchMap, - tap, - withLatestFrom, -} from 'rxjs/operators'; +import { concatMap, filter, first, map, switchMap, tap } from 'rxjs/operators'; import { PersistenceService } from '../../../core/persistence/persistence.service'; -import { - Task, - TaskArchive, - TaskCopy, - TaskReminderOptionId, - TaskWithSubTasks, -} from '../task.model'; +import { Task, TaskArchive, TaskCopy, TaskWithSubTasks } from '../task.model'; import { ReminderService } from '../../reminder/reminder.service'; import { moveTaskInTodayList } from '../../work-context/store/work-context-meta.actions'; import { taskAdapter } from './task.adapter'; @@ -39,11 +20,6 @@ import { unique } from '../../../util/unique'; import { TaskService } from '../task.service'; import { EMPTY, Observable, of } from 'rxjs'; import { createEmptyEntity } from '../../../util/create-empty-entity'; -import { ProjectService } from '../../project/project.service'; -import { TagService } from '../../tag/tag.service'; -import { shortSyntax } from '../short-syntax.util'; -import { remindOptionToMilliseconds } from '../util/remind-option-to-milliseconds'; -import { environment } from '../../../../environments/environment'; import { moveProjectTaskToTodayList } from '../../project/store/project.actions'; import { SnackService } from '../../../core/snack/snack.service'; import { T } from '../../../t.const'; @@ -238,156 +214,10 @@ export class TaskRelatedModelEffects { ), ); - shortSyntax$: any = createEffect(() => - this._actions$.pipe( - ofType(addTask, updateTask), - filter((action): boolean => { - if (action.isIgnoreShortSyntax) { - return false; - } - if (action.type !== updateTask.type) { - return true; - } - const changeProps = Object.keys(action.task.changes); - // we only want to execute this for task title updates - return changeProps.length === 1 && changeProps[0] === 'title'; - }), - // dirty fix to execute this after setDefaultProjectId$ effect - concatMap((originalAction): Observable => { - return this._taskService.getByIdOnce$(originalAction.task.id as string).pipe( - map((task) => ({ - task, - originalAction, - })), - ); - }), - withLatestFrom( - this._tagService.tags$, - this._projectService.list$, - this._globalConfigService.misc$.pipe( - map((misc) => misc.defaultProjectId), - concatMap((defaultProjectId) => - defaultProjectId - ? this._projectService.getByIdOnce$(defaultProjectId).pipe( - tap((project) => { - if (!project) { - // to avoid further data inconsistencies - throw new Error('Default Project not found'); - } - }), - mapTo(defaultProjectId), - ) - : of(defaultProjectId), - ), - ), - ), - mergeMap(([{ task, originalAction }, tags, projects, defaultProjectId]) => { - const r = shortSyntax(task, tags, projects); - if (environment.production) { - console.log('shortSyntax', r); - } - const isAddDefaultProjectIfNecessary: boolean = - !!defaultProjectId && - !task.projectId && - !task.parentId && - task.projectId !== defaultProjectId && - originalAction.type === addTask.type; - - if (!r) { - if (isAddDefaultProjectIfNecessary) { - return [ - moveToOtherProject({ - task, - targetProjectId: defaultProjectId as string, - }), - ]; - } - return EMPTY; - } - - const actions: any[] = []; - const tagIds: string[] = [...(r.taskChanges.tagIds || task.tagIds)]; - const { taskChanges } = r; - - actions.push( - updateTask({ - task: { - id: task.id, - changes: r.taskChanges, - }, - isIgnoreShortSyntax: true, - }), - ); - if (taskChanges.plannedAt && !taskChanges.reminderId) { - const { plannedAt } = taskChanges; - const schedule = scheduleTask({ - task, - plannedAt, - remindAt: remindOptionToMilliseconds(plannedAt, TaskReminderOptionId.AtStart), - isMoveToBacklog: false, - }); - actions.push(schedule); - } - if (r.projectId && r.projectId !== task.projectId && !task.parentId) { - if (task.repeatCfgId) { - this._snackService.open({ - ico: 'warning', - msg: T.F.TASK.S.CANNOT_ASSIGN_PROJECT_FOR_REPEATABLE_TASK, - }); - } else { - actions.push( - moveToOtherProject({ - task, - targetProjectId: r.projectId, - }), - ); - } - } else if (isAddDefaultProjectIfNecessary) { - actions.push( - moveToOtherProject({ - task, - targetProjectId: defaultProjectId as string, - }), - ); - } - - if (r.newTagTitles.length) { - r.newTagTitles.forEach((newTagTitle) => { - const { action, id } = this._tagService.getAddTagActionAndId({ - title: newTagTitle, - }); - tagIds.push(id); - actions.push(action); - }); - } - - if (tagIds && tagIds.length) { - const isEqualTags = JSON.stringify(tagIds) === JSON.stringify(task.tagIds); - if (!task.tagIds) { - throw new Error('Task Old TagIds need to be passed'); - } - if (!isEqualTags) { - actions.push( - updateTaskTags({ - task, - newTagIds: unique(tagIds), - oldTagIds: task.tagIds, - }), - ); - } - } - - return actions; - }), - ), - ); - constructor( private _actions$: Actions, private _reminderService: ReminderService, private _taskService: TaskService, - private _tagService: TagService, - private _projectService: ProjectService, private _globalConfigService: GlobalConfigService, private _persistenceService: PersistenceService, private _snackService: SnackService, diff --git a/src/app/features/tasks/tasks.module.ts b/src/app/features/tasks/tasks.module.ts index 21e2de290e..a00c1602a1 100644 --- a/src/app/features/tasks/tasks.module.ts +++ b/src/app/features/tasks/tasks.module.ts @@ -34,6 +34,7 @@ import { DialogViewTaskRemindersComponent } from './dialog-view-task-reminders/d import { TaskSummaryTablesComponent } from './task-summary-tables/task-summary-tables.component'; import { IS_ELECTRON } from '../../app.constants'; import { TasksByTagComponent } from './tasks-by-tag/tasks-by-tag.component'; +import { ShortSyntaxEffects } from './store/short-syntax.effects'; @NgModule({ imports: [ @@ -52,6 +53,7 @@ import { TasksByTagComponent } from './tasks-by-tag/tasks-by-tag.component'; TaskRelatedModelEffects, TaskReminderEffects, TaskUiEffects, + ShortSyntaxEffects, ...(IS_ELECTRON ? [TaskElectronEffects] : []), ]), BetterDrawerModule,