diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 316bc13a31fa109990d06d4b9582a4e61db41319..eb358f377da0a24ec823ba018072f5a84ee7b2fd 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -21,15 +21,20 @@ import { IE11WarningDialogComponent } from './degree-planner/dialogs/ie11-warnin import { darsReducer } from './dars/store/reducer'; import { DARSModule } from './dars/dars.module'; import { DegreePlannerModule } from './degree-planner/degree-planner.module'; +import { preferencesReducer } from './core/reducer'; +import { UserPreferencesEffects } from './core/effects'; +import { EffectsModule } from '@ngrx/effects'; @NgModule({ imports: [ DARSModule, DegreePlannerModule, StoreModule.forRoot({ + preferences: preferencesReducer, degreePlanner: degreePlannerReducer, dars: darsReducer, }), + EffectsModule.forFeature([UserPreferencesEffects]), BrowserModule, BrowserAnimationsModule, HttpClientModule, diff --git a/src/app/degree-planner/store/actions/userPreferences.actions.ts b/src/app/core/actions.ts similarity index 99% rename from src/app/degree-planner/store/actions/userPreferences.actions.ts rename to src/app/core/actions.ts index 2e8f2af5137da7a9f68d27adae9c360a5a1948d7..907f1ab058c6e3d13484d7924c5ba5f3f0945622 100644 --- a/src/app/degree-planner/store/actions/userPreferences.actions.ts +++ b/src/app/core/actions.ts @@ -4,6 +4,7 @@ import { UserPreferences } from '@app/core/models/user-preferences'; export enum UserPreferencesActionTypes { UpdateUserPreferences = '[User Preferences] Update', } + export class UpdateUserPreferences implements Action { public readonly type = UserPreferencesActionTypes.UpdateUserPreferences; constructor(public payload: Partial<UserPreferences>) {} diff --git a/src/app/degree-planner/store/effects/userPreferences.effects.ts b/src/app/core/effects.ts similarity index 85% rename from src/app/degree-planner/store/effects/userPreferences.effects.ts rename to src/app/core/effects.ts index 728b8523d4b8e218361373c284d7d7f708e963f4..5a29880abac8389662dff12377c67308793bc500 100644 --- a/src/app/degree-planner/store/effects/userPreferences.effects.ts +++ b/src/app/core/effects.ts @@ -4,10 +4,8 @@ import { tap, withLatestFrom } from 'rxjs/operators'; import { GlobalState } from '@app/core/state'; import { Store } from '@ngrx/store'; import { DegreePlannerApiService } from '@app/degree-planner/services/api.service'; -import { - UpdateUserPreferences, - UserPreferencesActionTypes, -} from '@app/degree-planner/store/actions/userPreferences.actions'; +import { UpdateUserPreferences, UserPreferencesActionTypes } from './actions'; +import { UserPreferences } from './models/user-preferences'; @Injectable() export class UserPreferencesEffects { @@ -23,7 +21,7 @@ export class UserPreferencesEffects { ), withLatestFrom(this.store$), tap(([update, state]) => { - const prefs = { ...state.degreePlanner.userPreferences }; + const prefs: UserPreferences = { ...state.preferences }; // Remove properties who's value is undefined Object.entries(update.payload).forEach(([key, value]) => { diff --git a/src/app/core/reducer.ts b/src/app/core/reducer.ts new file mode 100644 index 0000000000000000000000000000000000000000..0069409b350d6c509d3033cf93d9c93fce35fd00 --- /dev/null +++ b/src/app/core/reducer.ts @@ -0,0 +1,27 @@ +import { UserPreferencesActionTypes, UpdateUserPreferences } from './actions'; +import { INITIAL_PREFERENCES_STATE } from './state'; +import { UserPreferences } from './models/user-preferences'; + +export function preferencesReducer( + state = INITIAL_PREFERENCES_STATE, + action: UpdateUserPreferences, +): UserPreferences { + switch (action.type) { + case UserPreferencesActionTypes.UpdateUserPreferences: { + const update: UserPreferences = { ...state }; + + // Remove properties who's value is undefined + Object.entries(action.payload).forEach(([key, value]) => { + if (value === undefined && update[key] !== undefined) { + delete update[key]; + } else { + update[key] = value; + } + }); + + return update; + } + default: + return state; + } +} diff --git a/src/app/core/selectors.ts b/src/app/core/selectors.ts new file mode 100644 index 0000000000000000000000000000000000000000..26b368d482173b2534a8b4c7d1aafba03b33f033 --- /dev/null +++ b/src/app/core/selectors.ts @@ -0,0 +1,10 @@ +import { createSelector } from '@ngrx/store'; +import { GlobalState } from './state'; +import { UserPreferences } from './models/user-preferences'; + +export const getUserPreference = createSelector( + (state: GlobalState) => state.preferences, + (preferences: UserPreferences, pref: keyof UserPreferences): unknown => { + return preferences[pref]; + }, +); diff --git a/src/app/core/state.ts b/src/app/core/state.ts index ce1b679edfc4fd561c564b4ac5a766e2479faee3..9c72764530f7b48cff637091beb3a732a7820c38 100644 --- a/src/app/core/state.ts +++ b/src/app/core/state.ts @@ -1,7 +1,11 @@ import { DARSState } from '@app/dars/store/state'; import { DegreePlannerState } from '@app/degree-planner/store/state'; +import { UserPreferences } from './models/user-preferences'; export interface GlobalState { + preferences: UserPreferences; dars: DARSState; degreePlanner: DegreePlannerState; } + +export const INITIAL_PREFERENCES_STATE: UserPreferences = {}; diff --git a/src/app/degree-planner/degree-planner-view/degree-planner-view.component.ts b/src/app/degree-planner/degree-planner-view/degree-planner-view.component.ts index 1a26b17210c4fec655c3f2cb376127f1e309e424..e25a8e1d2aa18a08986c3e095472726df139b26f 100644 --- a/src/app/degree-planner/degree-planner-view/degree-planner-view.component.ts +++ b/src/app/degree-planner/degree-planner-view/degree-planner-view.component.ts @@ -41,7 +41,8 @@ import { YearCode } from '@app/degree-planner/shared/term-codes/yearcode'; import { ConstantsService } from '../services/constants.service'; import { TermCodeFactory } from '../services/termcode.factory'; import { IE11WarningDialogComponent } from '../dialogs/ie11-warning-dialog/ie11-warning-dialog.component'; -import { UpdateUserPreferences } from '../store/actions/userPreferences.actions'; +import { getUserPreference } from '@app/core/selectors'; +import { UpdateUserPreferences } from '@app/core/actions'; // From: https://stackoverflow.com/a/21825207 const isIE11 = @@ -91,7 +92,7 @@ export class DegreePlannerViewComponent implements OnInit { this.showGrades$ = this.store.pipe(select(selectors.selectGradeVisibility)); this.hasDismissedIEWarning$ = this.store.pipe( - select(selectors.getUserPreference, 'degreePlannerHasDismissedIEWarning'), + select(getUserPreference, 'degreePlannerHasDismissedIEWarning'), map(hasDismissedIEWarning => hasDismissedIEWarning === true), ); diff --git a/src/app/degree-planner/degree-planner.module.ts b/src/app/degree-planner/degree-planner.module.ts index 894b97f2b74b8b2861617c774239d98365dd204a..a3d71e679ca8d940c0a5b5d1640c10703cdcfd1d 100644 --- a/src/app/degree-planner/degree-planner.module.ts +++ b/src/app/degree-planner/degree-planner.module.ts @@ -16,7 +16,6 @@ import { DegreePlanEffects } from './store/effects/plan.effects'; import { NoteEffects } from './store/effects/note.effects'; import { CourseEffects } from './store/effects/course.effects'; import { ErrorEffects } from './store/effects/error.effects'; -import { UserPreferencesEffects } from './store/effects/userPreferences.effects'; @NgModule({ imports: [ @@ -27,7 +26,6 @@ import { UserPreferencesEffects } from './store/effects/userPreferences.effects' NoteEffects, CourseEffects, ErrorEffects, - UserPreferencesEffects, ]), ], exports: [DragDropModule], diff --git a/src/app/degree-planner/store/effects/plan.effects.ts b/src/app/degree-planner/store/effects/plan.effects.ts index 087b5fcafa1f438a0d67102a22370b0e84ac3306..743117bc6f886222089d576e4b717688d0cccf10 100644 --- a/src/app/degree-planner/store/effects/plan.effects.ts +++ b/src/app/degree-planner/store/effects/plan.effects.ts @@ -9,6 +9,7 @@ import { withLatestFrom, catchError, filter, + switchMap, } from 'rxjs/operators'; import { GlobalState } from '@app/core/state'; import { Store } from '@ngrx/store'; @@ -41,7 +42,7 @@ import { Note } from '@app/core/models/note'; import { CourseBase, Course } from '@app/core/models/course'; import { TermCode } from '@app/degree-planner/shared/term-codes/termcode'; import { Alert, DisclaimerAlert } from '@app/core/models/alert'; -import { UpdateUserPreferences } from '../actions/userPreferences.actions'; +import { UpdateUserPreferences } from '@app/core/actions'; import { TermCodeFactory } from '@app/degree-planner/services/termcode.factory'; @Injectable() @@ -107,15 +108,18 @@ export class DegreePlanEffects { savedForLaterCourses, allDegreePlans: of(allDegreePlans), alerts: of(alerts), - userPreferences: of(userPreferences), - }); - }), - map(payload => { - return new InitialLoadSuccess({ - ...INITIAL_DEGREE_PLANNER_STATE, - ...payload, - isLoadingPlan: false, - }); + }).pipe( + switchMap(payload => { + return [ + new InitialLoadSuccess({ + ...INITIAL_DEGREE_PLANNER_STATE, + ...payload, + isLoadingPlan: false, + }), + new UpdateUserPreferences(userPreferences), + ]; + }), + ); }), catchError(error => { return of( diff --git a/src/app/degree-planner/store/reducer.ts b/src/app/degree-planner/store/reducer.ts index 2e9f7481ae204366c01ec117f8f2b8faf174a83e..3b8f0f73d33fea40de0fcb1108834aebbf2f6960 100644 --- a/src/app/degree-planner/store/reducer.ts +++ b/src/app/degree-planner/store/reducer.ts @@ -6,7 +6,6 @@ import * as planActions from '@app/degree-planner/store/actions/plan.actions'; import * as courseActions from '@app/degree-planner/store/actions/course.actions'; import * as noteActions from '@app/degree-planner/store/actions/note.actions'; import * as uiActions from '@app/degree-planner/store/actions/ui.actions'; -import * as userPrefsActions from './actions/userPreferences.actions'; import { SavedForLaterCourse } from '@app/core/models/saved-for-later-course'; import { DegreePlan } from '@app/core/models/degree-plan'; import { Year, YearMapping } from '@app/core/models/year'; @@ -49,8 +48,7 @@ type SupportedActions = | uiActions.OpenSidenav | uiActions.CloseSidenav | uiActions.UpdateSearchTermCode - | planActions.ChangeGradeVisibility - | userPrefsActions.UpdateUserPreferences; + | planActions.ChangeGradeVisibility; export function degreePlannerReducer( state = INITIAL_DEGREE_PLANNER_STATE, @@ -222,23 +220,6 @@ export function degreePlannerReducer( }; } - case userPrefsActions.UserPreferencesActionTypes.UpdateUserPreferences: { - const update = { - ...state, - }; - - // Remove properties who's value is undefined - Object.entries(action.payload).forEach(([key, value]) => { - if (value === undefined && update.userPreferences[key] !== undefined) { - delete update.userPreferences[key]; - } else { - update.userPreferences[key] = value; - } - }); - - return update; - } - case noteActions.NoteActionTypes.WriteNote: { const { termCode, noteText } = action.payload; const { yearCode, termName } = termCode; diff --git a/src/app/degree-planner/store/selectors.ts b/src/app/degree-planner/store/selectors.ts index 9e2f688dc5cad96aaba75ef999e1a9fc3ad0afbe..a7677dc7222acc74b17ceb3ef0d5ee4825ea07a9 100644 --- a/src/app/degree-planner/store/selectors.ts +++ b/src/app/degree-planner/store/selectors.ts @@ -66,13 +66,6 @@ export const selectGradeVisibility = createSelector( (state: DegreePlannerState) => state.showGrades, ); -export const getUserPreference = createSelector( - getDegreePlannerState, - (state: DegreePlannerState, pref: keyof UserPreferences) => { - return state.userPreferences[pref] as unknown; - }, -); - export const isCourseSearchOpen = createSelector( getDegreePlannerState, (state: DegreePlannerState) => { diff --git a/src/app/degree-planner/store/state.ts b/src/app/degree-planner/store/state.ts index 0976a2a4fd72643f5c44229a7416a4c355fb332f..4c06642d16c4a713fa98d69f4fcd31c37fbad1d0 100644 --- a/src/app/degree-planner/store/state.ts +++ b/src/app/degree-planner/store/state.ts @@ -4,7 +4,6 @@ import { SavedForLaterCourse } from '@app/core/models/saved-for-later-course'; import { SubjectCodesTo, SubjectDescription } from '@app/core/models/course'; import { TermCode } from '@app/degree-planner/shared/term-codes/termcode'; import { Alert } from '@app/core/models/alert'; -import { UserPreferences } from '@app/core/models/user-preferences'; export interface DegreePlannerState { visibleDegreePlan: DegreePlan | undefined; @@ -17,7 +16,6 @@ export interface DegreePlannerState { isSidenavOpen: 'defer' | boolean; alerts: Alert[]; showGrades: boolean; - userPreferences: UserPreferences; } export const INITIAL_DEGREE_PLANNER_STATE: DegreePlannerState = { @@ -31,7 +29,4 @@ export const INITIAL_DEGREE_PLANNER_STATE: DegreePlannerState = { isSidenavOpen: 'defer', alerts: [], showGrades: true, - userPreferences: { - degreePlannerGradesVisibility: true, - }, };