diff --git a/src/app/features/tasks/store/task.selectors.ts b/src/app/features/tasks/store/task.selectors.ts index d6ac2dd3e4..b7c09ff8c0 100644 --- a/src/app/features/tasks/store/task.selectors.ts +++ b/src/app/features/tasks/store/task.selectors.ts @@ -13,12 +13,12 @@ import { TODAY_TAG } from '../../tag/tag.const'; import { IssueProvider } from '../../issue/issue.model'; import { Project } from '../../project/project.model'; import { selectAllProjects } from '../../project/store/project.selectors'; -import { getWorklogStr } from '../../../util/get-work-log-str'; import { selectTagFeatureState, selectTodayTagTaskIds, } from '../../tag/store/tag.reducer'; import { isToday } from '../../../util/is-today.util'; +import { selectTodayStr } from '../../../root-store/app-state/app-state.selectors'; const mapSubTasksToTasks = (tasksIN: any[]): TaskWithSubTasks[] => { return tasksIN @@ -117,18 +117,22 @@ export const selectStartableTasks = createSelector( }, ); -export const selectOverdueTasks = createSelector(selectTaskFeatureState, (s): Task[] => { - const today = new Date(getWorklogStr()); - const todayStart = new Date(today); - todayStart.setHours(0, 0, 0, 0); - return s.ids - .map((id) => s.entities[id] as Task) - .filter( - (task) => - (task.dueDay && new Date(task.dueDay) < today) || - (task.dueWithTime && task.dueWithTime < todayStart.getTime()), - ); -}); +export const selectOverdueTasks = createSelector( + selectTaskFeatureState, + selectTodayStr, + (s, todayStr): Task[] => { + const today = new Date(todayStr); + const todayStart = new Date(today); + todayStart.setHours(0, 0, 0, 0); + return s.ids + .map((id) => s.entities[id] as Task) + .filter( + (task) => + (task.dueDay && new Date(task.dueDay) < today) || + (task.dueWithTime && task.dueWithTime < todayStart.getTime()), + ); + }, +); export const selectOverdueTasksOnToday = createSelector( selectOverdueTasks, @@ -180,8 +184,9 @@ export const selectOverdueTasksWithSubTasks = createSelector( export const selectAllTasksDueAndOverdue = createSelector( selectTaskFeatureState, selectTagFeatureState, - (s, tagState): Task[] => { - const today = new Date(getWorklogStr()); + selectTodayStr, + (s, tagState, todayStr): Task[] => { + const today = new Date(todayStr); return s.ids .map((id) => s.entities[id] as Task) .filter( @@ -462,9 +467,8 @@ export const selectAllTasksWithDueDay = createSelector( export const selectAllTasksDueToday = createSelector( selectTaskFeatureState, - (taskState): (TaskWithDueTime | TaskWithDueDay)[] => { - const todayStr = getWorklogStr(); - + selectTodayStr, + (taskState, todayStr): (TaskWithDueTime | TaskWithDueDay)[] => { const allDueDayTasks = Object.values(taskState.entities).filter( (task) => !!task && !!(task as TaskWithDueTime).dueDay && todayStr === task.dueDay, ) as TaskWithDueDay[]; diff --git a/src/app/root-store/app-state/app-state.actions.ts b/src/app/root-store/app-state/app-state.actions.ts new file mode 100644 index 0000000000..2f23e2c8a6 --- /dev/null +++ b/src/app/root-store/app-state/app-state.actions.ts @@ -0,0 +1,9 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { createActionGroup, props } from '@ngrx/store'; + +export const AppStateActions = createActionGroup({ + source: 'AppState', + events: { + 'Set Today String': props<{ todayStr: string }>(), + }, +}); diff --git a/src/app/root-store/app-state/app-state.effects.spec.ts b/src/app/root-store/app-state/app-state.effects.spec.ts new file mode 100644 index 0000000000..7a859aa978 --- /dev/null +++ b/src/app/root-store/app-state/app-state.effects.spec.ts @@ -0,0 +1,25 @@ +// import { TestBed } from '@angular/core/testing'; +// import { provideMockActions } from '@ngrx/effects/testing'; +// import { Observable } from 'rxjs'; +// +// import { AppStateEffects } from './app-state.effects'; +// +// describe('AppStateEffects', () => { +// let actions$: Observable; +// let effects: AppStateEffects; +// +// beforeEach(() => { +// TestBed.configureTestingModule({ +// providers: [ +// AppStateEffects, +// provideMockActions(() => actions$) +// ] +// }); +// +// effects = TestBed.inject(AppStateEffects); +// }); +// +// it('should be created', () => { +// expect(effects).toBeTruthy(); +// }); +// }); diff --git a/src/app/root-store/app-state/app-state.effects.ts b/src/app/root-store/app-state/app-state.effects.ts new file mode 100644 index 0000000000..d2ddec2856 --- /dev/null +++ b/src/app/root-store/app-state/app-state.effects.ts @@ -0,0 +1,18 @@ +import { inject, Injectable } from '@angular/core'; +import { createEffect } from '@ngrx/effects'; + +import { map, skip } from 'rxjs/operators'; +import { AppStateActions } from './app-state.actions'; +import { GlobalTrackingIntervalService } from '../../core/global-tracking-interval/global-tracking-interval.service'; + +@Injectable() +export class AppStateEffects { + private _globalTimeTrackingIntervalService = inject(GlobalTrackingIntervalService); + + setTodayStr$ = createEffect(() => { + return this._globalTimeTrackingIntervalService.todayDateStr$.pipe( + skip(1), // skip first since it should be already the default value + map((todayStr) => AppStateActions.setTodayString({ todayStr })), + ); + }); +} diff --git a/src/app/root-store/app-state/app-state.reducer.spec.ts b/src/app/root-store/app-state/app-state.reducer.spec.ts new file mode 100644 index 0000000000..4638e2e0e4 --- /dev/null +++ b/src/app/root-store/app-state/app-state.reducer.spec.ts @@ -0,0 +1,13 @@ +// import { reducer, initialState } from './app-state.reducer'; +// +// describe('AppState Reducer', () => { +// describe('an unknown action', () => { +// it('should return the previous state', () => { +// const action = {} as any; +// +// const result = reducer(initialState, action); +// +// expect(result).toBe(initialState); +// }); +// }); +// }); diff --git a/src/app/root-store/app-state/app-state.reducer.ts b/src/app/root-store/app-state/app-state.reducer.ts new file mode 100644 index 0000000000..b661a34885 --- /dev/null +++ b/src/app/root-store/app-state/app-state.reducer.ts @@ -0,0 +1,26 @@ +import { createFeature, createReducer, on } from '@ngrx/store'; +import { AppStateActions } from './app-state.actions'; +import { getWorklogStr } from '../../util/get-work-log-str'; + +export const appStateFeatureKey = 'appState'; + +export interface AppState { + todayStr: string; +} + +export const appStateInitialState: AppState = { + todayStr: getWorklogStr(), +}; + +export const appStateReducer = createReducer( + appStateInitialState, + on(AppStateActions.setTodayString, (state, { todayStr }) => ({ + ...state, + todayStr, + })), +); + +export const appStateFeature = createFeature({ + name: appStateFeatureKey, + reducer: appStateReducer, +}); diff --git a/src/app/root-store/app-state/app-state.selectors.spec.ts b/src/app/root-store/app-state/app-state.selectors.spec.ts new file mode 100644 index 0000000000..30fb2f9f9e --- /dev/null +++ b/src/app/root-store/app-state/app-state.selectors.spec.ts @@ -0,0 +1,12 @@ +// import * as fromAppState from './app-state.reducer'; +// import { selectAppStateState } from './app-state.selectors'; +// +// describe('AppState Selectors', () => { +// it('should select the feature state', () => { +// const result = selectAppStateState({ +// [fromAppState.appStateFeatureKey]: {} +// }); +// +// expect(result).toEqual({}); +// }); +// }); diff --git a/src/app/root-store/app-state/app-state.selectors.ts b/src/app/root-store/app-state/app-state.selectors.ts new file mode 100644 index 0000000000..a89fbe469b --- /dev/null +++ b/src/app/root-store/app-state/app-state.selectors.ts @@ -0,0 +1,11 @@ +import { createFeatureSelector, createSelector } from '@ngrx/store'; +import * as fromAppState from './app-state.reducer'; + +export const selectAppStateState = createFeatureSelector( + fromAppState.appStateFeatureKey, +); + +export const selectTodayStr = createSelector( + selectAppStateState, + (state: fromAppState.AppState) => state.todayStr, +); diff --git a/src/app/root-store/feature-stores.module.ts b/src/app/root-store/feature-stores.module.ts index 59fd2596e8..b1c87a731d 100644 --- a/src/app/root-store/feature-stores.module.ts +++ b/src/app/root-store/feature-stores.module.ts @@ -94,10 +94,15 @@ import { timeTrackingFeature } from '../features/time-tracking/store/time-tracki import { TimeTrackingEffects } from '../features/time-tracking/store/time-tracking.effects'; import { plannerFeature } from '../features/planner/store/planner.reducer'; import { PlannerEffects } from '../features/planner/store/planner.effects'; +import { AppStateEffects } from './app-state/app-state.effects'; +import { appStateFeature } from './app-state/app-state.reducer'; @NgModule({ declarations: [], imports: [ + StoreModule.forFeature(appStateFeature), + EffectsModule.forFeature([AppStateEffects]), + StoreModule.forFeature(LAYOUT_FEATURE_NAME, layoutReducer), EffectsModule.forFeature([LayoutEffects]), diff --git a/src/app/root-store/root-state.ts b/src/app/root-store/root-state.ts index 4f9db8680d..98c6fdac2c 100644 --- a/src/app/root-store/root-state.ts +++ b/src/app/root-store/root-state.ts @@ -17,6 +17,7 @@ import { } from '../features/boards/store/boards.reducer'; import * as fromPlanner from '../features/planner/store/planner.reducer'; import { PlannerState } from '../features/planner/store/planner.reducer'; +import { AppState, appStateFeatureKey } from './app-state/app-state.reducer'; export interface RootState { [TASK_FEATURE_NAME]: TaskState; @@ -28,4 +29,5 @@ export interface RootState { [CONFIG_FEATURE_NAME]: GlobalConfigState; [BOARDS_FEATURE_NAME]: BoardsState; [fromPlanner.plannerFeatureKey]: PlannerState; + [appStateFeatureKey]: AppState; }