From f93477487cd309c9426b20a0308516621692f6ae Mon Sep 17 00:00:00 2001 From: NejcZdovc Date: Wed, 28 Sep 2016 14:52:34 +0200 Subject: [PATCH] Added effects --- src/app.module.ts | 13 +-- src/components/popover/popover.component.ts | 3 +- .../side-menu/side-menu.component.ts | 5 +- src/store/effects/index.ts | 2 + src/store/effects/keymap.ts | 38 ++++---- src/store/effects/macro.ts | 23 +++-- src/store/index.ts | 13 ++- src/store/reducers/index.ts | 6 +- src/store/reducers/keymap.ts | 90 ++++++++++++------- src/store/reducers/macro.ts | 67 ++++++++++---- src/store/storage/local.ts | 28 ++++-- 11 files changed, 199 insertions(+), 89 deletions(-) create mode 100644 src/store/effects/index.ts diff --git a/src/app.module.ts b/src/app.module.ts index afe0e168..68a9f30d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,7 +2,10 @@ import { NgModule, ReflectiveInjector } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; +import { EffectsModule } from '@ngrx/effects'; import { StoreModule } from '@ngrx/store'; +import { StoreDevtoolsModule } from '@ngrx/store-devtools'; +import { StoreLogMonitorModule, useLogMonitor } from '@ngrx/store-log-monitor'; import { DragulaModule } from 'ng2-dragula/ng2-dragula'; import { Select2Module } from 'ng2-select2/ng2-select2'; @@ -54,11 +57,9 @@ import { DataProviderService } from './services/data-provider.service'; import { MapperService } from './services/mapper.service'; import { UhkConfigurationService } from './services/uhk-configuration.service'; -import { DataStorage } from './store/storage'; -import { StoreDevtoolsModule } from '@ngrx/store-devtools'; -import { StoreLogMonitorModule, useLogMonitor } from '@ngrx/store-log-monitor'; - +import { KeymapEffects, MacroEffects } from './store/effects'; import { keymapReducer, macroReducer, presetReducer } from './store/reducers'; +import { DataStorage } from './store/storage'; // Create DataStorage dependency injection const storageProvider = ReflectiveInjector.resolve([DataStorage]); @@ -127,7 +128,9 @@ const storeConfig = { }) }), StoreLogMonitorModule, - Select2Module + Select2Module, + EffectsModule.runAfterBootstrap(KeymapEffects), + EffectsModule.runAfterBootstrap(MacroEffects) ], providers: [ DataProviderService, diff --git a/src/components/popover/popover.component.ts b/src/components/popover/popover.component.ts index 949dd6f9..2923aa76 100644 --- a/src/components/popover/popover.component.ts +++ b/src/components/popover/popover.component.ts @@ -15,6 +15,7 @@ import { Keymap } from '../../config-serializer/config-items/Keymap'; import { Tab } from './tab/tab'; import { AppState } from '../../store'; +import { getKeymapEntities } from '../../store/reducers'; import { Observable } from 'rxjs/Observable'; enum TabName { @@ -48,7 +49,7 @@ export class PopoverComponent implements OnInit { private keymaps$: Observable; constructor(private store: Store) { - this.keymaps$ = store.select((appState: AppState) => appState.keymaps); + this.keymaps$ = store.let(getKeymapEntities()); } ngOnInit() { diff --git a/src/components/side-menu/side-menu.component.ts b/src/components/side-menu/side-menu.component.ts index cafb0771..1518a784 100644 --- a/src/components/side-menu/side-menu.component.ts +++ b/src/components/side-menu/side-menu.component.ts @@ -4,6 +4,7 @@ import { Keymap } from '../../config-serializer/config-items/Keymap'; import { Macro } from '../../config-serializer/config-items/Macro'; import { AppState } from '../../store'; +import { getKeymapEntities, getMacroEntities } from '../../store/reducers'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; @@ -35,8 +36,8 @@ export class SideMenuComponent { addon: 'active' }; - this.keymaps$ = store.select(appState => appState.keymaps); - this.macros$ = store.select(appState => appState.macros); + this.keymaps$ = store.let(getKeymapEntities()); + this.macros$ = store.let(getMacroEntities()); } toggleHide(event: Event, type: string) { diff --git a/src/store/effects/index.ts b/src/store/effects/index.ts new file mode 100644 index 00000000..0da8f3ab --- /dev/null +++ b/src/store/effects/index.ts @@ -0,0 +1,2 @@ +export * from './keymap'; +export * from './macro'; diff --git a/src/store/effects/keymap.ts b/src/store/effects/keymap.ts index 8037a920..0ae3ad41 100644 --- a/src/store/effects/keymap.ts +++ b/src/store/effects/keymap.ts @@ -1,32 +1,38 @@ import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; import { Actions, Effect } from '@ngrx/effects'; +import { Store } from '@ngrx/store'; -import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/do'; +import 'rxjs/add/operator/withLatestFrom'; -import { KeymapActions } from '../actions/keymap'; +import { KeymapActions } from '../actions'; +import { AppState } from '../index'; @Injectable() export class KeymapEffects { - @Effect()remove$: any = this.actions$ + @Effect({ dispatch: false })remove$: any = this.actions$ .ofType(KeymapActions.REMOVE) - .map(() => { - // TODO: Waiting for the fix: https://github.com/angular/angular/issues/10770 - // If state is empty router.navigate(['/keymap']); - // Else router.navigate(['/keymap']); + .withLatestFrom(this.store) + .do((latest) => { + let state: AppState = latest[1]; + + if (state.keymaps.entities.length === 0) { + this.router.navigate(['/keymap/add']); + } else { + this.router.navigate(['/keymap']); + } }); - @Effect() editAbbr$: any = this.actions$ + @Effect({ dispatch: false }) editAbbr$: any = this.actions$ .ofType(KeymapActions.EDIT_ABBR) - .map(action => action.payload.abbr) - .map((abbr: string) => { - console.log(abbr); - // TODO: Waiting for the fix: https://github.com/angular/angular/issues/10770 - // // router.navigate(['/keymap', abbr]); + .withLatestFrom(this.store) + .do((latest) => { + let state: AppState = latest[1]; + this.router.navigate(['/keymap', state.keymaps.newAbbr]); }); - constructor( - private actions$: Actions - ) {} + constructor(private actions$: Actions, private router: Router, private store: Store) {} } diff --git a/src/store/effects/macro.ts b/src/store/effects/macro.ts index 7717e4b6..d8c71939 100644 --- a/src/store/effects/macro.ts +++ b/src/store/effects/macro.ts @@ -1,21 +1,30 @@ import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; import { Actions, Effect } from '@ngrx/effects'; +import { Store } from '@ngrx/store'; -import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/do'; +import 'rxjs/add/operator/withLatestFrom'; import { MacroActions } from '../actions'; +import { AppState } from '../index'; @Injectable() export class MacroEffects { - @Effect()remove$: any = this.actions$ + @Effect({dispatch: false}) remove$: any = this.actions$ .ofType(MacroActions.REMOVE) - .map(() => { - // TODO: Waiting for the fix: https://github.com/angular/angular/issues/10770 - // If state is empty router.navigate(['/macro']); - // Else router.navigate(['/macro']); + .withLatestFrom(this.store) + .do((latest) => { + let state: AppState = latest[1]; + + if (state.macros.entities.length === 0) { + this.router.navigate(['/macro/add']); + } else { + this.router.navigate(['/macro']); + } }); - constructor(private actions$: Actions) {} + constructor(private actions$: Actions, private router: Router, private store: Store) {} } diff --git a/src/store/index.ts b/src/store/index.ts index 08ef3e5a..37cf8f41 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,9 +1,18 @@ import { Keymap } from '../config-serializer/config-items/Keymap'; import { Macro } from '../config-serializer/config-items/Macro'; +export interface KeymapState { + entities: Keymap[]; + newAbbr?: string; +} + +export interface MacroState { + entities: Macro[]; +} + // State interface for the application export interface AppState { - keymaps: Keymap[]; - macros: Macro[]; + keymaps: KeymapState; + macros: MacroState; presetKeymaps: Keymap[]; } diff --git a/src/store/reducers/index.ts b/src/store/reducers/index.ts index 76ad3e4c..7a297701 100644 --- a/src/store/reducers/index.ts +++ b/src/store/reducers/index.ts @@ -1,5 +1,5 @@ -import keymapReducer from './keymap'; -import macroReducer from './macro'; +import keymapReducer, { getKeymapEntities } from './keymap'; +import macroReducer, { getMacroEntities } from './macro'; import presetReducer from './preset'; -export { keymapReducer, macroReducer, presetReducer }; +export { keymapReducer, macroReducer, presetReducer, getKeymapEntities, getMacroEntities }; diff --git a/src/store/reducers/keymap.ts b/src/store/reducers/keymap.ts index 1fe44d92..250b9379 100644 --- a/src/store/reducers/keymap.ts +++ b/src/store/reducers/keymap.ts @@ -6,56 +6,75 @@ import { Observable } from 'rxjs/Observable'; import { Keymap } from '../../config-serializer/config-items/Keymap'; import { KeymapActions } from '../actions'; -import { AppState } from '../index'; +import { AppState, KeymapState } from '../index'; -const initialState: Keymap[] = []; +const initialState: KeymapState = { + entities: [] +}; + +export default function(state = initialState, action: Action): KeymapState { + let newState: Keymap[]; -export default function(state = initialState, action: Action): Keymap[] { switch (action.type) { case KeymapActions.ADD: case KeymapActions.DUPLICATE: let newKeymap: Keymap = new Keymap(action.payload); - newKeymap.abbreviation = generateAbbr(state, newKeymap.abbreviation); - newKeymap.name = generateName(state, newKeymap.name); - newKeymap.isDefault = false; + newKeymap.abbreviation = generateAbbr(state.entities, newKeymap.abbreviation); + newKeymap.name = generateName(state.entities, newKeymap.name); + newKeymap.isDefault = (state.entities.length === 0); - return [...state, newKeymap]; + return { + entities: [...state.entities, newKeymap] + }; case KeymapActions.EDIT_NAME: - let name: string = generateName(state, action.payload.name); + let name: string = generateName(state.entities, action.payload.name); - return state.map((keymap: Keymap) => { - if (keymap.abbreviation === action.payload.abbr) { - keymap.name = name; - } + newState = state.entities.map((keymap: Keymap) => { + if (keymap.abbreviation === action.payload.abbr) { + keymap.name = name; + } - return keymap; - }); + return keymap; + }); + + return { + entities: newState + }; case KeymapActions.EDIT_ABBR: - let abbr: string = generateAbbr(state, action.payload.newAbbr); + let abbr: string = generateAbbr(state.entities, action.payload.newAbbr); - return state.map((keymap: Keymap) => { - if (keymap.abbreviation === action.payload.abbr) { - keymap.abbreviation = abbr; - } + newState = state.entities.map((keymap: Keymap) => { + if (keymap.abbreviation === action.payload.abbr) { + keymap.abbreviation = abbr; + } - return keymap; - }); + return keymap; + }); + + return { + entities: newState, + newAbbr: abbr + }; case KeymapActions.SET_DEFAULT: - return state.map((keymap: Keymap) => { - keymap.isDefault = (keymap.abbreviation === action.payload); + newState = state.entities.map((keymap: Keymap) => { + keymap.isDefault = (keymap.abbreviation === action.payload); - return keymap; - }); + return keymap; + }); + + return { + entities: newState + }; case KeymapActions.REMOVE: let isDefault: boolean; - let filtered: Keymap[] = state.filter((keymap: Keymap) => { + let filtered: Keymap[] = state.entities.filter((keymap: Keymap) => { if (keymap.abbreviation === action.payload) { isDefault = keymap.isDefault; return false; @@ -70,12 +89,14 @@ export default function(state = initialState, action: Action): Keymap[] { filtered[0].isDefault = true; } - return filtered; + return { + entities: filtered + }; case KeymapActions.SAVE_KEY: let changedKeymap: Keymap = new Keymap; - return state.map((keymap: Keymap) => { + newState = state.entities.map((keymap: Keymap) => { if (keymap.abbreviation === action.payload.abbreviation) { keymap = Object.assign(changedKeymap, action.payload); } @@ -83,19 +104,28 @@ export default function(state = initialState, action: Action): Keymap[] { return keymap; }); + return { + entities: newState + }; + default: { return state; } } } +export function getKeymapEntities(): (state$: Observable) => Observable { + return (state$: Observable) => state$ + .select(state => state.keymaps.entities); +} + export function getKeymap(abbr: string) { if (abbr === undefined) { return getDefault(); } return (state$: Observable) => state$ - .select(appState => appState.keymaps) + .select(appState => appState.keymaps.entities) .map((keymaps: Keymap[]) => keymaps.find((keymap: Keymap) => keymap.abbreviation === abbr) ); @@ -103,7 +133,7 @@ export function getKeymap(abbr: string) { export function getDefault() { return (state$: Observable) => state$ - .select(appState => appState.keymaps) + .select(appState => appState.keymaps.entities) .map((keymaps: Keymap[]) => keymaps.find((keymap: Keymap) => keymap.isDefault) ); diff --git a/src/store/reducers/macro.ts b/src/store/reducers/macro.ts index 0f188070..f7abfccc 100644 --- a/src/store/reducers/macro.ts +++ b/src/store/reducers/macro.ts @@ -7,26 +7,31 @@ import { Observable } from 'rxjs/Observable'; import { Macro } from '../../config-serializer/config-items/Macro'; import { MacroActions } from '../actions'; -import { AppState } from '../index'; +import { AppState, MacroState } from '../index'; -const initialState: Macro[] = []; +const initialState: MacroState = { + entities: [] +}; -export default function(state = initialState, action: Action): Macro[] { +export default function(state = initialState, action: Action): MacroState { let newMacro: Macro; + let newState: Macro[]; switch (action.type) { case MacroActions.DUPLICATE: newMacro = new Macro(action.payload); - newMacro.name = generateName(state, newMacro.name); - newMacro.id = generateId(state); + newMacro.name = generateName(state.entities, newMacro.name); + newMacro.id = generateId(state.entities); - return [...state, newMacro]; + return { + entities: [...state.entities, newMacro] + }; case MacroActions.EDIT_NAME: - let name: string = generateName(state, action.payload.name); + let name: string = generateName(state.entities, action.payload.name); - return state.map((macro: Macro) => { + newState = state.entities.map((macro: Macro) => { if (macro.id === action.payload.id) { macro.name = name; } @@ -34,11 +39,19 @@ export default function(state = initialState, action: Action): Macro[] { return macro; }); + return { + entities: newState + }; + case MacroActions.REMOVE: - return state.filter((macro: Macro) => macro.id !== action.payload); + newState = state.entities.filter((macro: Macro) => macro.id !== action.payload); + + return { + entities: newState + }; case MacroActions.ADD_ACTION: - return state.map((macro: Macro) => { + newState = state.entities.map((macro: Macro) => { if (macro.id === action.payload.id) { newMacro = new Macro(macro); newMacro.macroActions.push(action.payload.action); @@ -49,8 +62,12 @@ export default function(state = initialState, action: Action): Macro[] { return macro; }); + return { + entities: newState + }; + case MacroActions.SAVE_ACTION: - return state.map((macro: Macro) => { + newState = state.entities.map((macro: Macro) => { if (macro.id === action.payload.id) { newMacro = new Macro(macro); newMacro.macroActions[action.payload.index] = action.payload.action; @@ -61,8 +78,12 @@ export default function(state = initialState, action: Action): Macro[] { return macro; }); + return { + entities: newState + }; + case MacroActions.DELETE_ACTION: - return state.map((macro: Macro) => { + newState = state.entities.map((macro: Macro) => { if (macro.id === action.payload.id) { newMacro = new Macro(macro); newMacro.macroActions.splice(action.payload.index, 1); @@ -73,8 +94,12 @@ export default function(state = initialState, action: Action): Macro[] { return macro; }); + return { + entities: newState + }; + case MacroActions.REORDER_ACTION: - return state.map((macro: Macro) => { + newState = state.entities.map((macro: Macro) => { if (macro.id === action.payload.id) { let newIndex: number = action.payload.newIndex; @@ -96,16 +121,24 @@ export default function(state = initialState, action: Action): Macro[] { return macro; }); - default: { + return { + entities: newState + }; + + default: return state; - } } } +export function getMacroEntities(): (state$: Observable) => Observable { + return (state$: Observable) => state$ + .select(state => state.macros.entities); +} + export function getMacro(id: number) { if (isNaN(id)) { return (state$: Observable) => state$ - .select(appState => appState.macros) + .select(appState => appState.macros.entities) .map((macros: Macro[]) => { if (macros.length > 0) { return macros[0]; @@ -115,7 +148,7 @@ export function getMacro(id: number) { }); } else { return (state$: Observable) => state$ - .select(appState => appState.macros) + .select(appState => appState.macros.entities) .map((macros: Macro[]) => macros.find((macro: Macro) => macro.id === id)); } } diff --git a/src/store/storage/local.ts b/src/store/storage/local.ts index 9b6f9efa..3a4b119a 100644 --- a/src/store/storage/local.ts +++ b/src/store/storage/local.ts @@ -32,8 +32,12 @@ export class Local { } return { - keymaps: config.keymaps, - macros: config.macros, + keymaps: { + entities: config.keymaps + }, + macros: { + entities: config.macros + }, presetKeymaps: presetAll }; } @@ -44,17 +48,29 @@ export class Local { let config: UhkConfiguration; // Save elements to the UhkConfiguration - if (action.type.startsWith(KeymapActions.PREFIX) && state.length && state[0] instanceof Keymap) { + if ( + action.type.startsWith(KeymapActions.PREFIX) && + ( + (nextState.entities && nextState.entities.length && nextState.entities[0] instanceof Keymap) || + (state.entities && state.entities.length && state.entities[0] instanceof Keymap) + ) + ) { config = new UhkConfiguration().fromJsObject( JSON.parse(localStorage.getItem('config')) ); - config.keymaps = Object.values(nextState); + config.keymaps = Object.values(nextState.entities); localStorage.setItem('config', JSON.stringify(config.toJsObject())); - } else if (action.type.startsWith(MacroActions.PREFIX) && state.length && state[0] instanceof Macro) { + } else if ( + action.type.startsWith(MacroActions.PREFIX) && + ( + (nextState.entities && nextState.entities.length && nextState.entities[0] instanceof Macro) || + (state.entities && state.entities.length && state.entities[0] instanceof Macro) + ) + ) { config = new UhkConfiguration().fromJsObject( JSON.parse(localStorage.getItem('config')) ); - config.macros = Object.values(nextState); + config.macros = Object.values(nextState.entities); localStorage.setItem('config', JSON.stringify(config.toJsObject())); }