feat(calendar): split up reminder and startAt

This commit is contained in:
Johannes Millan 2021-01-10 21:25:01 +01:00
parent 51314d3b6e
commit 606f2e93e4
12 changed files with 178 additions and 76 deletions

View File

@ -33,7 +33,12 @@ export class CalendarComponent {
const start = calEvent.event._instance.range.start;
// const start = calEvent.event._instance.range.start;
const task: TaskWithReminderData = calEvent.event.extendedProps;
this._taskService.updateReminder(task.id, task.reminderId as string, start.getTime() - WEIRD_MAGIC_HOUR, task.title);
this._taskService.reScheduleTask({
taskId: task.id,
reminderId: task.reminderId as string,
plannedAt: start.getTime() - WEIRD_MAGIC_HOUR,
title: task.title
});
// console.log(calEvent.endDelta.milliseconds + (task.timeSpent));
@ -49,16 +54,26 @@ export class CalendarComponent {
// TODO understand and fix this
if (calEvent.event.allDay) {
if (isToday(start)) {
this._taskService.removeReminder(task.id, task.reminderId as string);
this._taskService.unScheduleTask(task.id, task.reminderId as string);
} else {
const dayStartsSplit = DAY_STARTS_AT.split(':');
start.setHours(dayStartsSplit[0], dayStartsSplit[1], 0, 0);
const startTime = start.getTime();
this._taskService.updateReminder(task.id, task.reminderId as string, startTime, task.title);
this._taskService.reScheduleTask({
taskId: task.id,
reminderId: task.reminderId as string,
plannedAt: startTime,
title: task.title
});
}
} else {
const startTime = start.getTime() - WEIRD_MAGIC_HOUR;
this._taskService.updateReminder(task.id, task.reminderId as string, startTime, task.title);
this._taskService.reScheduleTask({
taskId: task.id,
reminderId: task.reminderId as string,
plannedAt: startTime,
title: task.title
});
}
},
// should be EventClickArg but not exported :(

View File

@ -11,7 +11,7 @@ import {
import { selectTaskRepeatCfgFeatureState } from './task-repeat-cfg.reducer';
import { PersistenceService } from '../../../core/persistence/persistence.service';
import { Task, TaskArchive, TaskWithSubTasks } from '../../tasks/task.model';
import { AddTask, MoveToArchive, RemoveTaskReminder, UpdateTask } from '../../tasks/store/task.actions';
import { AddTask, MoveToArchive, UnScheduleTask, UpdateTask } from '../../tasks/store/task.actions';
import { TaskService } from '../../tasks/task.service';
import { TaskRepeatCfgService } from '../task-repeat-cfg.service';
import { TASK_REPEAT_WEEKDAY_MAP, TaskRepeatCfg, TaskRepeatCfgState } from '../task-repeat-cfg.model';
@ -150,7 +150,7 @@ export class TaskRepeatCfgEffects {
),
concatMap((a: AddTaskRepeatCfgToTask) => this._taskService.getByIdOnce$(a.payload.taskId).pipe(take(1))),
filter((task: TaskWithSubTasks) => typeof task.reminderId === 'string'),
map((task: TaskWithSubTasks) => new RemoveTaskReminder({
map((task: TaskWithSubTasks) => new UnScheduleTask({
id: task.id,
reminderId: task.reminderId as string
})),

View File

@ -5,7 +5,7 @@
<div class="additional-controls">
<mat-form-field>
<mat-select [(ngModel)]="reminderCfg"
<mat-select [(ngModel)]="reminderCfgId"
[placeholder]="(T.F.BOOKMARK.DIALOG_EDIT.SELECT_TYPE|translate)"
name="type"
required="true">

View File

@ -22,10 +22,9 @@ export class DialogAddTaskReminderComponent {
: undefined;
isEdit: boolean = !!(this.reminder && this.reminder.id);
dateTime?: number = this.reminder && this.reminder.remindAt;
dateTime?: number = this.task.plannedAt || undefined;
isShowMoveToBacklog: boolean = (!this.isEdit && !!this.task.projectId && this.task.parentId === null);
isMoveToBacklog: boolean = (this.isShowMoveToBacklog);
reminderCfg?: TaskReminderOption;
// TODO make translatable
remindAvailableOptions: TaskReminderOption[] = [{
id: TaskReminderOptionId.DoNotRemind,
@ -46,6 +45,7 @@ export class DialogAddTaskReminderComponent {
id: TaskReminderOptionId.h1,
title: '1 hour before it starts',
}];
reminderCfgId: TaskReminderOptionId;
constructor(
private _taskService: TaskService,
@ -53,6 +53,28 @@ export class DialogAddTaskReminderComponent {
private _matDialogRef: MatDialogRef<DialogAddTaskReminderComponent>,
@Inject(MAT_DIALOG_DATA) public data: AddTaskReminderInterface,
) {
if (this.isEdit) {
this.reminderCfgId = TaskReminderOptionId.DoNotRemind;
// todo add more sophisticated logic
console.log(this.task.plannedAt, this.reminder?.remindAt);
const diff: number = this.task.plannedAt as number - +(this.reminder?.remindAt as any);
console.log(diff);
if (diff >= 60 * 60 * 1000) {
this.reminderCfgId = TaskReminderOptionId.h1;
} else if (diff >= 30 * 60 * 1000) {
this.reminderCfgId = TaskReminderOptionId.m30;
} else if (diff >= 15 * 60 * 1000) {
this.reminderCfgId = TaskReminderOptionId.m15;
} else if (diff >= 10 * 60 * 1000) {
this.reminderCfgId = TaskReminderOptionId.m10;
} else if (diff === 0) {
this.reminderCfgId = TaskReminderOptionId.AtStart;
} else {
this.reminderCfgId = TaskReminderOptionId.DoNotRemind;
}
} else {
this.reminderCfgId = TaskReminderOptionId.AtStart;
}
}
// NOTE: throttle is used as quick way to prevent multiple submits
@ -65,17 +87,19 @@ export class DialogAddTaskReminderComponent {
}
if (this.isEdit && this.reminder) {
this._taskService.updateReminder(
this.task.id,
this.reminder.id,
timestamp,
this.task.title,
);
this._taskService.reScheduleTask({
taskId: this.task.id,
reminderId: this.task.reminderId as string,
plannedAt: timestamp,
remindCfg: this.reminderCfgId,
title: this.task.title,
});
this.close();
} else {
this._taskService.addReminder(
this._taskService.scheduleTask(
this.task,
timestamp,
this.reminderCfgId,
this.isMoveToBacklog,
);
this.close();
@ -89,7 +113,7 @@ export class DialogAddTaskReminderComponent {
console.log(this.reminder, this.task);
throw new Error('No reminder or id');
}
this._taskService.removeReminder(this.task.id, this.reminder.id);
this._taskService.unScheduleTask(this.task.id, this.reminder.id);
this.close();
}

View File

@ -18,7 +18,7 @@ export class TaskDbEffects {
TaskActionTypes.AddTask,
TaskActionTypes.RestoreTask,
TaskActionTypes.AddTimeSpent,
TaskActionTypes.RemoveTaskReminder,
TaskActionTypes.UnScheduleTask,
TaskActionTypes.DeleteTask,
TaskActionTypes.DeleteMainTasks,
TaskActionTypes.UndoDeleteTask,
@ -38,9 +38,9 @@ export class TaskDbEffects {
TaskActionTypes.RoundTimeSpentForDay,
// REMINDER
TaskActionTypes.AddTaskReminder,
TaskActionTypes.UpdateTaskReminder,
TaskActionTypes.RemoveTaskReminder,
TaskActionTypes.ScheduleTask,
TaskActionTypes.ReScheduleTask,
TaskActionTypes.UnScheduleTask,
// SUB ACTIONS
TaskAttachmentActionTypes.AddTaskAttachment,

View File

@ -1,21 +1,22 @@
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects';
import {
AddTaskReminder,
DeleteTask,
RemoveTaskReminder,
ReScheduleTask,
ScheduleTask,
TaskActionTypes,
UnScheduleTask,
UpdateTask,
UpdateTaskReminder,
UpdateTaskTags
} from './task.actions';
import { map, mergeMap, tap } from 'rxjs/operators';
import { filter, map, mergeMap, tap } from 'rxjs/operators';
import { ReminderService } from '../../reminder/reminder.service';
import { truncate } from '../../../util/truncate';
import { T } from '../../../t.const';
import { SnackService } from '../../../core/snack/snack.service';
import { moveTaskToBacklogListAuto } from '../../work-context/store/work-context-meta.actions';
import { TODAY_TAG } from '../../tag/tag.const';
import { EMPTY } from 'rxjs';
@Injectable()
export class TaskReminderEffects {
@ -23,9 +24,9 @@ export class TaskReminderEffects {
@Effect()
addTaskReminder$: any = this._actions$.pipe(
ofType(
TaskActionTypes.AddTaskReminder,
TaskActionTypes.ScheduleTask,
),
tap((a: AddTaskReminder) => this._snackService.open({
tap((a: ScheduleTask) => this._snackService.open({
type: 'SUCCESS',
translateParams: {
title: truncate(a.payload.task.title)
@ -33,11 +34,16 @@ export class TaskReminderEffects {
msg: T.F.TASK.S.REMINDER_ADDED,
ico: 'schedule',
})),
mergeMap((a: AddTaskReminder) => {
mergeMap((a: ScheduleTask) => {
console.log(a);
const {task, remindAt, isMoveToBacklog} = a.payload;
if (isMoveToBacklog && !task.projectId) {
throw new Error('Move to backlog not possible for non project tasks');
}
if (!remindAt) {
return EMPTY;
}
const reminderId = this._reminderService.addReminder('TASK', task.id, truncate(task.title), remindAt);
const isRemoveFromToday = isMoveToBacklog && task.tagIds.includes(TODAY_TAG.id);
@ -68,16 +74,18 @@ export class TaskReminderEffects {
@Effect({dispatch: false})
updateTaskReminder$: any = this._actions$.pipe(
ofType(
TaskActionTypes.UpdateTaskReminder,
TaskActionTypes.ReScheduleTask,
),
tap((a: UpdateTaskReminder) => {
filter(({payload}: ReScheduleTask) => !!payload.remindAt && !!payload.reminderId),
tap((a: ReScheduleTask) => {
console.log(a);
const {title, remindAt, reminderId} = a.payload;
this._reminderService.updateReminder(reminderId, {
this._reminderService.updateReminder(reminderId as string, {
remindAt,
title,
});
}),
tap((a: UpdateTaskReminder) => this._snackService.open({
tap((a: ReScheduleTask) => this._snackService.open({
type: 'SUCCESS',
translateParams: {
title: truncate(a.payload.title)
@ -90,11 +98,12 @@ export class TaskReminderEffects {
@Effect()
removeTaskReminder$: any = this._actions$.pipe(
ofType(
TaskActionTypes.RemoveTaskReminder,
TaskActionTypes.UnScheduleTask,
),
map((a: RemoveTaskReminder) => {
filter(({payload}: UnScheduleTask) => !!payload.reminderId),
map((a: UnScheduleTask) => {
const {id, reminderId} = a.payload;
this._reminderService.removeReminder(reminderId);
this._reminderService.removeReminder(reminderId as string);
return new UpdateTask({
task: {

View File

@ -27,10 +27,10 @@ export enum TaskActionTypes {
'AddTimeSpent' = '[Task] Add time spent',
'RemoveTimeSpent' = '[Task] Remove time spent',
// Reminders
'AddTaskReminder' = '[Task] Add reminder',
'UpdateTaskReminder' = '[Task] Update reminder',
'RemoveTaskReminder' = '[Task] Remove reminder',
// Reminders & StartAt
'ScheduleTask' = '[Task] Schedule',
'UnScheduleTask' = '[Task] UnSchedule',
'ReScheduleTask' = '[Task] ReSchedule',
// Sub Task Actions
'AddSubTask' = '[Task] Add SubTask',
@ -178,24 +178,24 @@ export class RemoveTimeSpent implements Action {
}
// Reminder Actions
export class AddTaskReminder implements Action {
readonly type: string = TaskActionTypes.AddTaskReminder;
export class ScheduleTask implements Action {
readonly type: string = TaskActionTypes.ScheduleTask;
constructor(public payload: { task: Task; remindAt: number; isMoveToBacklog: boolean }) {
constructor(public payload: { task: Task; plannedAt: number; remindAt?: number; isMoveToBacklog: boolean }) {
}
}
export class UpdateTaskReminder implements Action {
readonly type: string = TaskActionTypes.UpdateTaskReminder;
export class ReScheduleTask implements Action {
readonly type: string = TaskActionTypes.ReScheduleTask;
constructor(public payload: { id: string; title: string; reminderId: string; remindAt: number }) {
constructor(public payload: { id: string; title: string; plannedAt: number; reminderId?: string; remindAt?: number }) {
}
}
export class RemoveTaskReminder implements Action {
readonly type: string = TaskActionTypes.RemoveTaskReminder;
export class UnScheduleTask implements Action {
readonly type: string = TaskActionTypes.UnScheduleTask;
constructor(public payload: { id: string; reminderId: string }) {
constructor(public payload: { id: string; reminderId?: string }) {
}
}
@ -264,9 +264,9 @@ export type TaskActions
| MoveSubTaskDown
| AddTimeSpent
| RemoveTimeSpent
| AddTaskReminder
| UpdateTaskReminder
| RemoveTaskReminder
| ScheduleTask
| ReScheduleTask
| UnScheduleTask
| RestoreTask
| AddSubTask
| ConvertToMainTask

View File

@ -1,7 +1,7 @@
import {
AddSubTask,
AddTask,
AddTaskReminder,
ScheduleTask,
AddTimeSpent,
ConvertToMainTask,
DeleteMainTasks,
@ -12,17 +12,17 @@ import {
MoveToArchive,
MoveToOtherProject,
RemoveTagsForAllTasks,
RemoveTaskReminder,
UnScheduleTask,
RemoveTimeSpent,
RestoreTask,
RoundTimeSpentForDay,
ReScheduleTask,
SetCurrentTask,
SetSelectedTask,
TaskActions,
TaskActionTypes,
ToggleTaskShowSubTasks,
UpdateTask,
UpdateTaskReminder,
UpdateTaskTags,
UpdateTaskUi
} from './task.actions';
@ -549,8 +549,8 @@ export function taskReducer(
// REMINDER STUFF
// --------------
case TaskActionTypes.AddTaskReminder: {
const {task, remindAt} = (action as AddTaskReminder).payload;
case TaskActionTypes.ScheduleTask: {
const {task, remindAt} = (action as ScheduleTask).payload;
return taskAdapter.updateOne({
id: task.id,
changes: {
@ -559,18 +559,18 @@ export function taskReducer(
}, state);
}
case TaskActionTypes.UpdateTaskReminder: {
const {id, remindAt} = (action as UpdateTaskReminder).payload;
case TaskActionTypes.ReScheduleTask: {
const {id, plannedAt} = (action as ReScheduleTask).payload;
return taskAdapter.updateOne({
id,
changes: {
plannedAt: remindAt,
plannedAt,
}
}, state);
}
case TaskActionTypes.RemoveTaskReminder: {
const {id} = (action as RemoveTaskReminder).payload;
case TaskActionTypes.UnScheduleTask: {
const {id} = (action as UnScheduleTask).payload;
return taskAdapter.updateOne({
id,
changes: {

View File

@ -10,6 +10,7 @@ import {
Task,
TaskAdditionalInfoTargetPanel,
TaskArchive,
TaskReminderOptionId,
TaskState,
TaskWithSubTasks
} from './task.model';
@ -17,7 +18,6 @@ import { select, Store } from '@ngrx/store';
import {
AddSubTask,
AddTask,
AddTaskReminder,
AddTimeSpent,
ConvertToMainTask,
DeleteMainTasks,
@ -28,17 +28,18 @@ import {
MoveToArchive,
MoveToOtherProject,
RemoveTagsForAllTasks,
RemoveTaskReminder,
RemoveTimeSpent,
ReScheduleTask,
RestoreTask,
RoundTimeSpentForDay,
ScheduleTask,
SetCurrentTask,
SetSelectedTask,
ToggleStart,
ToggleTaskShowSubTasks,
UnScheduleTask,
UnsetCurrentTask,
UpdateTask,
UpdateTaskReminder,
UpdateTaskTags,
UpdateTaskUi
} from './store/task.actions';
@ -52,7 +53,8 @@ import {
selectCurrentTaskOrParentWithData,
selectCurrentTaskParentOrCurrent,
selectIsTaskDataLoaded,
selectMainTasksWithoutTag, selectPlannedTasks,
selectMainTasksWithoutTag,
selectPlannedTasks,
selectSelectedTask,
selectSelectedTaskId, selectStartableTasks,
selectTaskAdditionalInfoTargetPanel,
@ -87,6 +89,7 @@ import { unique } from '../../util/unique';
import { SnackService } from '../../core/snack/snack.service';
import { T } from '../../t.const';
import { ImexMetaService } from '../../imex/imex-meta/imex-meta.service';
import { remindOptionToMilliseconds } from './util/remind-option-to-milliseconds';
@Injectable({
providedIn: 'root',
@ -492,19 +495,51 @@ export class TaskService {
// REMINDER
// --------
addReminder(task: Task | TaskWithSubTasks, remindAt: number, isMoveToBacklog: boolean = false) {
this._store.dispatch(new AddTaskReminder({task, remindAt, isMoveToBacklog}));
scheduleTask(task: Task | TaskWithSubTasks, plannedAt: number, remindCfg: TaskReminderOptionId, isMoveToBacklog: boolean = false) {
console.log(remindOptionToMilliseconds(plannedAt, remindCfg), plannedAt);
console.log(remindOptionToMilliseconds(plannedAt, remindCfg) as number - plannedAt);
console.log({
plannedAt,
remindCfg,
});
this._store.dispatch(new ScheduleTask({
task,
plannedAt,
remindAt: remindOptionToMilliseconds(plannedAt, remindCfg),
isMoveToBacklog
}));
}
updateReminder(taskId: string, reminderId: string, remindAt: number, title: string) {
this._store.dispatch(new UpdateTaskReminder({id: taskId, reminderId, remindAt, title}));
reScheduleTask({
taskId,
plannedAt,
reminderId,
remindCfg,
title
}: { taskId: string, plannedAt: number, title: string, reminderId?: string, remindCfg: TaskReminderOptionId }) {
console.log(remindOptionToMilliseconds(plannedAt, remindCfg), plannedAt);
console.log(remindOptionToMilliseconds(plannedAt, remindCfg) as number - plannedAt);
console.log({
plannedAt,
remindCfg,
});
this._store.dispatch(new ReScheduleTask({
id: taskId,
plannedAt,
reminderId,
remindAt: remindOptionToMilliseconds(plannedAt, remindCfg),
title
}));
}
removeReminder(taskId: string, reminderId: string) {
if (!reminderId || !taskId) {
throw new Error('No reminder or task id');
unScheduleTask(taskId: string, reminderId?: string) {
console.log('unschedzle', {reminderId});
if (!taskId) {
throw new Error('No task id');
}
this._store.dispatch(new RemoveTaskReminder({id: taskId, reminderId}));
this._store.dispatch(new UnScheduleTask({id: taskId, reminderId}));
}
// HELPER

View File

@ -0,0 +1,19 @@
import { TaskReminderOptionId } from '../task.model';
export const remindOptionToMilliseconds = (plannedAt: number, remindOptId: TaskReminderOptionId): number | undefined => {
switch (remindOptId) {
case TaskReminderOptionId.m10 : {
return plannedAt - 10 * 60 * 1000;
}
case TaskReminderOptionId.m15 : {
return plannedAt - 15 * 60 * 1000;
}
case TaskReminderOptionId.m30 : {
return plannedAt - 30 * 60 * 1000;
}
case TaskReminderOptionId.h1 : {
return plannedAt - 60 * 60 * 1000;
}
}
return undefined;
};

View File

@ -39,7 +39,7 @@ export class BacklogComponent {
if (!task.reminderId) {
throw new Error('Task without reminder');
}
this.taskService.removeReminder(task.id, task.reminderId);
this.taskService.unScheduleTask(task.id, task.reminderId);
}
}

View File

@ -54,7 +54,7 @@ export class SchedulePageComponent {
removeReminder(task: TaskWithReminderData) {
if (task.reminderId) {
this._taskService.removeReminder(task.id, task.reminderId);
this._taskService.unScheduleTask(task.id, task.reminderId);
}
}
@ -84,7 +84,7 @@ export class SchedulePageComponent {
this._taskService.moveToToday(task.id, true);
}
if (!!task.reminderId) {
this._taskService.removeReminder(task.id, task.reminderId);
this._taskService.unScheduleTask(task.id, task.reminderId);
}
this._taskService.setCurrentId(task.id);
this._router.navigate(['/active/tasks']);