diff --git a/.travis.yml b/.travis.yml index 119151e71d..cd80bf8dc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,6 @@ node_js: cache: yarn: true - directories: - - node_modules bundler_args: --retry 5 diff --git a/package.json b/package.json index dd032c04cf..8f83e93651 100644 --- a/package.json +++ b/package.json @@ -80,10 +80,9 @@ "@angularclass/bootloader": "1.0.1", "@angularclass/idle-preload": "1.0.4", "@ng-bootstrap/ng-bootstrap": "^1.0.0-beta.1", - "@ngrx/core": "1.2.0", - "@ngrx/effects": "2.0.4", - "@ngrx/router-store": "1.2.6", - "@ngrx/store": "2.2.3", + "@ngrx/effects": "^4.0.5", + "@ngrx/router-store": "^4.0.4", + "@ngrx/store": "^4.0.3", "@nguniversal/express-engine": "1.0.0-beta.2", "@ngx-translate/core": "7.1.0", "@ngx-translate/http-loader": "0.1.0", @@ -113,7 +112,7 @@ "devDependencies": { "@angular/compiler": "4.3.1", "@angular/compiler-cli": "4.3.1", - "@ngrx/store-devtools": "3.2.4", + "@ngrx/store-devtools": "^4.0.0", "@ngtools/webpack": "1.5.1", "@types/cookie-parser": "1.3.30", "@types/deep-freeze": "0.1.1", @@ -143,6 +142,7 @@ "imports-loader": "0.7.1", "istanbul-instrumenter-loader": "2.0.0", "jasmine-core": "2.6.4", + "jasmine-marbles": "^0.1.0", "jasmine-spec-reporter": "4.1.1", "json-loader": "0.5.4", "karma": "1.7.0", @@ -158,7 +158,6 @@ "karma-sourcemap-loader": "0.3.7", "karma-webdriver-launcher": "1.0.5", "karma-webpack": "2.0.4", - "ngrx-store-freeze": "0.1.9", "node-sass": "4.5.3", "nodemon": "1.11.0", "npm-run-all": "4.0.2", diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 96037effc1..cc8e28535a 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -45,7 +45,7 @@ describe('App component', () => { return TestBed.configureTestingModule({ imports: [ CommonModule, - StoreModule.provideStore({}), + StoreModule.forRoot({}), TranslateModule.forRoot({ loader: { provide: TranslateLoader, diff --git a/src/app/app.effects.ts b/src/app/app.effects.ts index 1bc2129ce1..7fc42da80d 100644 --- a/src/app/app.effects.ts +++ b/src/app/app.effects.ts @@ -1,11 +1,8 @@ -import { EffectsModule } from '@ngrx/effects'; import { HeaderEffects } from './header/header.effects'; import { StoreEffects } from './store.effects'; -import { coreEffects } from './core/core.effects'; -export const effects = [ - ...coreEffects, // TODO: should probably be imported in coreModule - EffectsModule.run(StoreEffects), - EffectsModule.run(HeaderEffects) +export const appEffects = [ + StoreEffects, + HeaderEffects ]; diff --git a/src/app/app.metareducers.ts b/src/app/app.metareducers.ts new file mode 100644 index 0000000000..fadaa6a991 --- /dev/null +++ b/src/app/app.metareducers.ts @@ -0,0 +1,38 @@ +import { isNotEmpty } from './shared/empty.util'; +import { StoreActionTypes } from './store.actions'; + +// fallback ngrx debugger +let actionCounter = 0; + +export function debugMetaReducer(reducer) { + return (state, action) => { + if (isNotEmpty(console.debug)) { + actionCounter++; + console.debug('@ngrx action', actionCounter, action.type); + console.debug('state', state); + console.debug('action', action); + console.debug('------------------------------------'); + } + + return reducer(state, action); + } +} + +export function universalMetaReducer(reducer) { + return (state, action) => { + switch (action.type) { + case StoreActionTypes.REHYDRATE: + state = Object.assign({}, state, action.payload); + break; + case StoreActionTypes.REPLAY: + default: + break; + } + return reducer(state, action); + } +} + +export const appMetaReducers = [ + // debugMetaReducer, + universalMetaReducer, +]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index f359c7be71..10ca89926c 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -2,12 +2,11 @@ import { NgModule } from '@angular/core'; import { HttpModule } from '@angular/http'; import { FormsModule } from '@angular/forms'; -import { StoreModule, Store } from '@ngrx/store'; -import { RouterStoreModule } from '@ngrx/router-store'; +import { StoreModule } from '@ngrx/store'; import { StoreDevtoolsModule } from '@ngrx/store-devtools'; -import { rootReducer, AppState } from './app.reducer'; -import { effects } from './app.effects'; +import { appReducers } from './app.reducer'; +import { appEffects } from './app.effects'; import { CoreModule } from './core/core.module'; import { SharedModule } from './shared/shared.module'; @@ -28,6 +27,8 @@ import { HeaderComponent } from './header/header.component'; import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component'; import { GLOBAL_CONFIG, ENV_CONFIG } from '../config'; +import { EffectsModule } from '@ngrx/effects'; +import { appMetaReducers } from './app.metareducers'; export function getConfig() { return ENV_CONFIG; @@ -46,10 +47,9 @@ export function getConfig() { CommunityPageModule, SearchPageModule, AppRoutingModule, - StoreModule.provideStore(rootReducer), - RouterStoreModule.connectRouter(), - StoreDevtoolsModule.instrumentOnlyWithExtension(), - effects + StoreModule.forRoot(appReducers, { metaReducers: appMetaReducers }), + StoreDevtoolsModule.instrument({ maxAge: 50 }), + EffectsModule.forRoot(appEffects) ], providers: [ { provide: GLOBAL_CONFIG, useFactory: (getConfig) }, diff --git a/src/app/app.reducer.ts b/src/app/app.reducer.ts index 463fa0c05d..646d7a26b5 100644 --- a/src/app/app.reducer.ts +++ b/src/app/app.reducer.ts @@ -1,45 +1,17 @@ -import { combineReducers, ActionReducer } from '@ngrx/store'; -import { routerReducer, RouterState } from '@ngrx/router-store'; -import { storeFreeze } from 'ngrx-store-freeze'; -import { compose } from '@ngrx/core'; +import { ActionReducerMap } from '@ngrx/store'; +import * as fromRouter from '@ngrx/router-store'; import { headerReducer, HeaderState } from './header/header.reducer'; import { hostWindowReducer, HostWindowState } from './shared/host-window.reducer'; -import { CoreState, coreReducer } from './core/core.reducers'; - -import { StoreActionTypes } from './store.actions'; - -import { ENV_CONFIG } from '../config'; export interface AppState { - core: CoreState; - router: RouterState; + router: fromRouter.RouterReducerState; hostWindow: HostWindowState; header: HeaderState; } -export const reducers = { - core: coreReducer, - router: routerReducer, +export const appReducers: ActionReducerMap = { + router: fromRouter.routerReducer, hostWindow: hostWindowReducer, header: headerReducer }; - -export function rootReducer(state: any, action: any) { - switch (action.type) { - case StoreActionTypes.REHYDRATE: - state = Object.assign({}, state, action.payload); - break; - case StoreActionTypes.REPLAY: - break; - default: - } - let root: ActionReducer; - // TODO: attempt to not use InjectionToken GLOBAL_CONFIG over GlobalConfig ENV_CONFIG - if (ENV_CONFIG.production) { - root = combineReducers(reducers)(state, action); - } else { - root = compose(storeFreeze, combineReducers)(reducers)(state, action); - } - return root; -} diff --git a/src/app/browser-app.module.ts b/src/app/browser-app.module.ts index 99921a4fbd..7ba050668c 100644 --- a/src/app/browser-app.module.ts +++ b/src/app/browser-app.module.ts @@ -25,6 +25,8 @@ import { CoreModule } from './core/core.module'; import { AppModule } from './app.module'; import { AppComponent } from './app.component'; +import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store'; +import { DSpaceRouterStateSerializer } from './shared/ngrx/dspace-router-state-serializer'; export function init(cache: TransferState) { return () => { @@ -56,7 +58,8 @@ export function HttpLoaderFactory(http: Http) { BrowserDataLoaderModule, BrowserTransferStateModule, BrowserTransferStoreModule, - EffectsModule.run(BrowserTransferStoreEffects), + EffectsModule.forRoot([BrowserTransferStoreEffects]), + StoreRouterConnectingModule, BrowserAnimationsModule, AppModule ], @@ -68,6 +71,10 @@ export function HttpLoaderFactory(http: Http) { deps: [ TransferState ] + }, + { + provide: RouterStateSerializer, + useClass: DSpaceRouterStateSerializer } ] }) diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index eec7f6fefc..f170e342c0 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -1,12 +1,10 @@ import { Injectable } from '@angular/core'; -import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; import { CacheableObject } from '../object-cache.reducer'; import { ObjectCacheService } from '../object-cache.service'; import { RequestService } from '../../data/request.service'; import { ResponseCacheService } from '../response-cache.service'; -import { CoreState } from '../../core.reducers'; import { RequestEntry } from '../../data/request.reducer'; import { hasValue, isNotEmpty } from '../../../shared/empty.util'; import { ResponseCacheEntry } from '../response-cache.reducer'; @@ -20,10 +18,12 @@ import { PageInfo } from '../../shared/page-info.model'; @Injectable() export class RemoteDataBuildService { - constructor(protected objectCache: ObjectCacheService, - protected responseCache: ResponseCacheService, - protected requestService: RequestService, - protected store: Store,) { + constructor( + protected objectCache: ObjectCacheService, + protected responseCache: ResponseCacheService, + protected requestService: RequestService + + ) { } buildSingle(href: string, @@ -31,9 +31,9 @@ export class RemoteDataBuildService { const requestHrefObs = this.objectCache.getRequestHrefBySelfLink(href); const requestObs = Observable.race( - this.store.select('core', 'data', 'request', href).filter((entry) => hasValue(entry)), + this.requestService.get(href).filter((entry) => hasValue(entry)), requestHrefObs.flatMap((requestHref) => - this.store.select('core', 'data', 'request', requestHref)).filter((entry) => hasValue(entry)) + this.requestService.get(requestHref)).filter((entry) => hasValue(entry)) ); const responseCacheObs = Observable.race( @@ -97,7 +97,7 @@ export class RemoteDataBuildService { ).filter((normalized) => hasValue(normalized)) .map((normalized: TNormalized) => { return this.build(normalized); - }); + }).distinctUntilChanged(); return new RemoteData( href, @@ -113,7 +113,7 @@ export class RemoteDataBuildService { buildList(href: string, normalizedType: GenericConstructor): RemoteData { - const requestObs = this.store.select('core', 'data', 'request', href) + const requestObs = this.requestService.get(href) .filter((entry) => hasValue(entry)); const responseCacheObs = this.responseCache.get(href).filter((entry) => hasValue(entry)); diff --git a/src/app/core/cache/cache.reducers.ts b/src/app/core/cache/cache.reducers.ts deleted file mode 100644 index a2bce403d3..0000000000 --- a/src/app/core/cache/cache.reducers.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { combineReducers } from '@ngrx/store'; - -import { ResponseCacheState, responseCacheReducer } from './response-cache.reducer'; -import { ObjectCacheState, objectCacheReducer } from './object-cache.reducer'; - -export interface CacheState { - response: ResponseCacheState, - object: ObjectCacheState -} - -export const reducers = { - response: responseCacheReducer, - object: objectCacheReducer -}; - -export function cacheReducer(state: any, action: any) { - return combineReducers(reducers)(state, action); -} diff --git a/src/app/core/cache/object-cache.reducer.ts b/src/app/core/cache/object-cache.reducer.ts index e690efaccc..494db4c34d 100644 --- a/src/app/core/cache/object-cache.reducer.ts +++ b/src/app/core/cache/object-cache.reducer.ts @@ -48,7 +48,7 @@ const initialState: ObjectCacheState = Object.create(null); * @return ObjectCacheState * the new state */ -export const objectCacheReducer = (state = initialState, action: ObjectCacheAction): ObjectCacheState => { +export function objectCacheReducer(state = initialState, action: ObjectCacheAction): ObjectCacheState { switch (action.type) { case ObjectCacheActionTypes.ADD: { @@ -67,7 +67,7 @@ export const objectCacheReducer = (state = initialState, action: ObjectCacheActi return state; } } -}; +} /** * Add an object to the cache diff --git a/src/app/core/cache/object-cache.service.spec.ts b/src/app/core/cache/object-cache.service.spec.ts index 09d28605e2..54b49d3cf6 100644 --- a/src/app/core/cache/object-cache.service.spec.ts +++ b/src/app/core/cache/object-cache.service.spec.ts @@ -2,8 +2,9 @@ import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; import { ObjectCacheService } from './object-cache.service'; -import { ObjectCacheState, CacheableObject } from './object-cache.reducer'; +import { CacheableObject } from './object-cache.reducer'; import { AddToObjectCacheAction, RemoveFromObjectCacheAction } from './object-cache.actions'; +import { CoreState } from '../core.reducers'; class TestClass implements CacheableObject { constructor( @@ -18,7 +19,7 @@ class TestClass implements CacheableObject { describe('ObjectCacheService', () => { let service: ObjectCacheService; - let store: Store; + let store: Store; const uuid = '1698f1d3-be98-4c51-9fd8-6bfedcbd59b7'; const requestHref = 'https://rest.api/endpoint/1698f1d3-be98-4c51-9fd8-6bfedcbd59b7'; @@ -36,7 +37,7 @@ describe('ObjectCacheService', () => { const invalidCacheEntry = Object.assign({}, cacheEntry, { msToLive: -1 }); beforeEach(() => { - store = new Store(undefined, undefined, undefined); + store = new Store(undefined, undefined, undefined); spyOn(store, 'dispatch'); service = new ObjectCacheService(store); diff --git a/src/app/core/cache/object-cache.service.ts b/src/app/core/cache/object-cache.service.ts index 7f4dbf9893..0755b268cf 100644 --- a/src/app/core/cache/object-cache.service.ts +++ b/src/app/core/cache/object-cache.service.ts @@ -1,12 +1,22 @@ import { Injectable } from '@angular/core'; -import { Store } from '@ngrx/store'; +import { MemoizedSelector, Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; -import { ObjectCacheState, ObjectCacheEntry, CacheableObject } from './object-cache.reducer'; +import { ObjectCacheEntry, CacheableObject } from './object-cache.reducer'; import { AddToObjectCacheAction, RemoveFromObjectCacheAction } from './object-cache.actions'; import { hasNoValue } from '../../shared/empty.util'; import { GenericConstructor } from '../shared/generic-constructor'; +import { CoreState } from '../core.reducers'; +import { keySelector } from '../shared/selectors'; + +function objectFromUuidSelector(uuid: string): MemoizedSelector { + return keySelector('data/object', uuid); +} + +function uuidFromHrefSelector(href: string): MemoizedSelector { + return keySelector('index/href', href); +} /** * A service to interact with the object cache @@ -14,7 +24,7 @@ import { GenericConstructor } from '../shared/generic-constructor'; @Injectable() export class ObjectCacheService { constructor( - private store: Store + private store: Store ) { } /** @@ -65,12 +75,12 @@ export class ObjectCacheService { } getBySelfLink(href: string, type: GenericConstructor): Observable { - return this.store.select('core', 'index', 'href', href) + return this.store.select(uuidFromHrefSelector(href)) .flatMap((uuid: string) => this.get(uuid, type)) } private getEntry(uuid: string): Observable { - return this.store.select('core', 'cache', 'object', uuid) + return this.store.select(objectFromUuidSelector(uuid)) .filter((entry) => this.isValid(entry)) .distinctUntilChanged(); } @@ -82,7 +92,7 @@ export class ObjectCacheService { } getRequestHrefBySelfLink(self: string): Observable { - return this.store.select('core', 'index', 'href', self) + return this.store.select(uuidFromHrefSelector(self)) .flatMap((uuid: string) => this.getRequestHref(uuid)); } @@ -123,7 +133,7 @@ export class ObjectCacheService { has(uuid: string): boolean { let result: boolean; - this.store.select('core', 'cache', 'object', uuid) + this.store.select(objectFromUuidSelector(uuid)) .take(1) .subscribe((entry) => result = this.isValid(entry)); @@ -142,7 +152,7 @@ export class ObjectCacheService { hasBySelfLink(href: string): boolean { let result = false; - this.store.select('core', 'index', 'href', href) + this.store.select(uuidFromHrefSelector(href)) .take(1) .subscribe((uuid: string) => result = this.has(uuid)); diff --git a/src/app/core/cache/response-cache.reducer.ts b/src/app/core/cache/response-cache.reducer.ts index ab2c49dc14..b6a9d903b4 100644 --- a/src/app/core/cache/response-cache.reducer.ts +++ b/src/app/core/cache/response-cache.reducer.ts @@ -37,7 +37,7 @@ const initialState = Object.create(null); * @return ResponseCacheState * the new state */ -export const responseCacheReducer = (state = initialState, action: ResponseCacheAction): ResponseCacheState => { +export function responseCacheReducer(state = initialState, action: ResponseCacheAction): ResponseCacheState { switch (action.type) { case ResponseCacheActionTypes.ADD: { @@ -56,7 +56,7 @@ export const responseCacheReducer = (state = initialState, action: ResponseCache return state; } } -}; +} function addToCache(state: ResponseCacheState, action: ResponseCacheAddAction): ResponseCacheState { return Object.assign({}, state, { diff --git a/src/app/core/cache/response-cache.service.ts b/src/app/core/cache/response-cache.service.ts index 3bb2ecaeeb..ed1291e67a 100644 --- a/src/app/core/cache/response-cache.service.ts +++ b/src/app/core/cache/response-cache.service.ts @@ -1,12 +1,18 @@ import { Injectable } from '@angular/core'; -import { Store } from '@ngrx/store'; +import { MemoizedSelector, Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; -import { ResponseCacheState, ResponseCacheEntry } from './response-cache.reducer'; +import { ResponseCacheEntry } from './response-cache.reducer'; import { hasNoValue } from '../../shared/empty.util'; import { ResponseCacheRemoveAction, ResponseCacheAddAction } from './response-cache.actions'; import { Response } from './response-cache.models'; +import { CoreState } from '../core.reducers'; +import { keySelector } from '../shared/selectors'; + +function entryFromKeySelector(key: string): MemoizedSelector { + return keySelector('data/response', key); +} /** * A service to interact with the response cache @@ -14,7 +20,7 @@ import { Response } from './response-cache.models'; @Injectable() export class ResponseCacheService { constructor( - private store: Store + private store: Store ) { } add(key: string, response: Response, msToLive: number): Observable { @@ -34,8 +40,9 @@ export class ResponseCacheService { * an observable of the ResponseCacheEntry with the specified key */ get(key: string): Observable { - return this.store.select('core', 'cache', 'response', key) - .filter((entry) => this.isValid(entry)) + return this.store.select(entryFromKeySelector(key)) + .filter((entry: ResponseCacheEntry) => this.isValid(entry)) + .distinctUntilChanged() } /** @@ -50,9 +57,9 @@ export class ResponseCacheService { has(key: string): boolean { let result: boolean; - this.store.select('core', 'cache', 'response', key) + this.store.select(entryFromKeySelector(key)) .take(1) - .subscribe((entry) => { + .subscribe((entry: ResponseCacheEntry) => { result = this.isValid(entry); }); diff --git a/src/app/core/core.effects.ts b/src/app/core/core.effects.ts index d3a4aed7d7..51a2cb7e06 100644 --- a/src/app/core/core.effects.ts +++ b/src/app/core/core.effects.ts @@ -1,4 +1,3 @@ -import { EffectsModule } from '@ngrx/effects'; import { ObjectCacheEffects } from './data/object-cache.effects'; import { RequestCacheEffects } from './data/request-cache.effects'; @@ -6,7 +5,8 @@ import { HrefIndexEffects } from './index/href-index.effects'; import { RequestEffects } from './data/request.effects'; export const coreEffects = [ - EffectsModule.run(RequestEffects), - EffectsModule.run(ObjectCacheEffects), - EffectsModule.run(HrefIndexEffects), + RequestCacheEffects, + RequestEffects, + ObjectCacheEffects, + HrefIndexEffects, ]; diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 9414293d72..2f98eecb0c 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -14,10 +14,16 @@ import { RequestService } from './data/request.service'; import { RemoteDataBuildService } from './cache/builders/remote-data-build.service'; import { CommunityDataService } from './data/community-data.service'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; +import { coreEffects } from './core.effects'; +import { EffectsModule } from '@ngrx/effects'; +import { StoreModule } from '@ngrx/store'; +import { coreReducers } from './core.reducers'; const IMPORTS = [ CommonModule, - SharedModule + SharedModule, + StoreModule.forFeature('core', coreReducers, { }), + EffectsModule.forFeature(coreEffects) ]; const DECLARATIONS = [ diff --git a/src/app/core/core.reducers.ts b/src/app/core/core.reducers.ts index cc16c6b49d..29959f7b43 100644 --- a/src/app/core/core.reducers.ts +++ b/src/app/core/core.reducers.ts @@ -1,21 +1,22 @@ -import { combineReducers } from '@ngrx/store'; +import { ActionReducerMap, createFeatureSelector } from '@ngrx/store'; -import { CacheState, cacheReducer } from './cache/cache.reducers'; -import { IndexState, indexReducer } from './index/index.reducers'; -import { DataState, dataReducer } from './data/data.reducers'; +import { responseCacheReducer, ResponseCacheState } from './cache/response-cache.reducer'; +import { objectCacheReducer, ObjectCacheState } from './cache/object-cache.reducer'; +import { hrefIndexReducer, HrefIndexState } from './index/href-index.reducer'; +import { requestReducer, RequestState } from './data/request.reducer'; export interface CoreState { - cache: CacheState, - index: IndexState, - data: DataState + 'data/object': ObjectCacheState, + 'data/response': ResponseCacheState, + 'data/request': RequestState, + 'index/href': HrefIndexState } -export const reducers = { - cache: cacheReducer, - index: indexReducer, - data: dataReducer +export const coreReducers: ActionReducerMap = { + 'data/object': objectCacheReducer, + 'data/response': responseCacheReducer, + 'data/request': requestReducer, + 'index/href': hrefIndexReducer }; -export function coreReducer(state: any, action: any) { - return combineReducers(reducers)(state, action); -} +export const coreSelector = createFeatureSelector('core'); diff --git a/src/app/core/data/data.reducers.ts b/src/app/core/data/data.reducers.ts deleted file mode 100644 index df151154cd..0000000000 --- a/src/app/core/data/data.reducers.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { combineReducers } from '@ngrx/store'; - -import { RequestState, requestReducer } from './request.reducer'; - -export interface DataState { - request: RequestState -} - -export const reducers = { - request: requestReducer -}; - -export function dataReducer(state: any, action: any) { - return combineReducers(reducers)(state, action); -} diff --git a/src/app/core/data/request-cache.effects.ts b/src/app/core/data/request-cache.effects.ts index 8ff41fb9f9..a9c1c24957 100644 --- a/src/app/core/data/request-cache.effects.ts +++ b/src/app/core/data/request-cache.effects.ts @@ -1,8 +1,8 @@ -import { Injectable, Inject } from '@angular/core'; +import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; -import { ObjectCacheActionTypes } from '../cache/object-cache.actions'; import { ResetResponseCacheTimestampsAction } from '../cache/response-cache.actions'; +import { StoreActionTypes } from '../../store.actions'; @Injectable() export class RequestCacheEffects { @@ -14,17 +14,9 @@ export class RequestCacheEffects { * * This assumes that the server cached everything a negligible * time ago, and will likely need to be revisited later - * - * This effect should listen for StoreActionTypes.REHYDRATE, - * but can't because you can only have one effect listen to - * an action atm. Github issue: - * https://github.com/ngrx/effects/issues/87 - * - * It's listening for ObjectCacheActionTypes.RESET_TIMESTAMPS - * instead, until there's a solution. */ @Effect() fixTimestampsOnRehydrate = this.actions$ - .ofType(ObjectCacheActionTypes.RESET_TIMESTAMPS) + .ofType(StoreActionTypes.REHYDRATE) .map(() => new ResetResponseCacheTimestampsAction(new Date().getTime())); constructor(private actions$: Actions, ) { } diff --git a/src/app/core/data/request.reducer.ts b/src/app/core/data/request.reducer.ts index 77278d9a67..6b84fbb77c 100644 --- a/src/app/core/data/request.reducer.ts +++ b/src/app/core/data/request.reducer.ts @@ -19,7 +19,7 @@ export interface RequestState { // Object.create(null) ensures the object has no default js properties (e.g. `__proto__`) const initialState = Object.create(null); -export const requestReducer = (state = initialState, action: RequestAction): RequestState => { +export function requestReducer(state = initialState, action: RequestAction): RequestState { switch (action.type) { case RequestActionTypes.CONFIGURE: { @@ -38,7 +38,7 @@ export const requestReducer = (state = initialState, action: RequestAction): Req return state; } } -}; +} function configureRequest(state: RequestState, action: RequestConfigureAction): RequestState { return Object.assign({}, state, { diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts index 08e0e66b96..f9f296a666 100644 --- a/src/app/core/data/request.service.ts +++ b/src/app/core/data/request.service.ts @@ -1,12 +1,10 @@ import { Injectable } from '@angular/core'; -import { Store } from '@ngrx/store'; - -import { request } from 'http'; +import { MemoizedSelector, Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; -import { RequestEntry, RequestState } from './request.reducer'; +import { RequestEntry } from './request.reducer'; import { Request } from './request.models'; import { hasValue } from '../../shared/empty.util'; import { RequestConfigureAction, RequestExecuteAction } from './request.actions'; @@ -15,6 +13,12 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { CacheableObject } from '../cache/object-cache.reducer'; import { ResponseCacheEntry } from '../cache/response-cache.reducer'; import { SuccessResponse } from '../cache/response-cache.models'; +import { CoreState } from '../core.reducers'; +import { keySelector } from '../shared/selectors'; + +function entryFromHrefSelector(href: string): MemoizedSelector { + return keySelector('data/request', href); +} @Injectable() export class RequestService { @@ -22,13 +26,13 @@ export class RequestService { constructor( private objectCache: ObjectCacheService, private responseCache: ResponseCacheService, - private store: Store + private store: Store ) { } isPending(href: string): boolean { let isPending = false; - this.store.select('core', 'data', 'request', href) + this.store.select(entryFromHrefSelector(href)) .take(1) .subscribe((re: RequestEntry) => { isPending = (hasValue(re) && !re.completed) @@ -38,7 +42,7 @@ export class RequestService { } get(href: string): Observable { - return this.store.select('core', 'data', 'request', href); + return this.store.select(entryFromHrefSelector(href)); } configure(request: Request): void { diff --git a/src/app/core/footer/footer.component.spec.ts b/src/app/core/footer/footer.component.spec.ts index 13debf504b..693b5ec537 100644 --- a/src/app/core/footer/footer.component.spec.ts +++ b/src/app/core/footer/footer.component.spec.ts @@ -33,7 +33,7 @@ describe('Footer component', () => { // async beforeEach beforeEach(async(() => { return TestBed.configureTestingModule({ - imports: [CommonModule, StoreModule.provideStore({}), TranslateModule.forRoot({ + imports: [CommonModule, StoreModule.forRoot({}), TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: MockTranslateLoader diff --git a/src/app/core/index/href-index.reducer.ts b/src/app/core/index/href-index.reducer.ts index 639163fab0..160b04f5b3 100644 --- a/src/app/core/index/href-index.reducer.ts +++ b/src/app/core/index/href-index.reducer.ts @@ -12,7 +12,7 @@ export interface HrefIndexState { // Object.create(null) ensures the object has no default js properties (e.g. `__proto__`) const initialState: HrefIndexState = Object.create(null); -export const hrefIndexReducer = (state = initialState, action: HrefIndexAction): HrefIndexState => { +export function hrefIndexReducer(state = initialState, action: HrefIndexAction): HrefIndexState { switch (action.type) { case HrefIndexActionTypes.ADD: { @@ -27,7 +27,7 @@ export const hrefIndexReducer = (state = initialState, action: HrefIndexAction): return state; } } -}; +} function addToHrefIndex(state: HrefIndexState, action: AddToHrefIndexAction): HrefIndexState { return Object.assign({}, state, { diff --git a/src/app/core/index/index.reducers.ts b/src/app/core/index/index.reducers.ts deleted file mode 100644 index d8e9d9e17f..0000000000 --- a/src/app/core/index/index.reducers.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { combineReducers } from '@ngrx/store'; - -import { HrefIndexState, hrefIndexReducer } from './href-index.reducer'; - -export interface IndexState { - href: HrefIndexState -} - -export const reducers = { - href: hrefIndexReducer -}; - -export function indexReducer(state: any, action: any) { - return combineReducers(reducers)(state, action); -} diff --git a/src/app/core/shared/selectors.ts b/src/app/core/shared/selectors.ts new file mode 100644 index 0000000000..06f444b2e6 --- /dev/null +++ b/src/app/core/shared/selectors.ts @@ -0,0 +1,13 @@ +import { createSelector, MemoizedSelector } from '@ngrx/store'; +import { coreSelector, CoreState } from '../core.reducers'; +import { hasValue } from '../../shared/empty.util'; + +export function keySelector(subState: string, key: string): MemoizedSelector { + return createSelector(coreSelector, (state: CoreState) => { + if (hasValue(state[subState])) { + return state[subState][key]; + } else { + return undefined; + } + }); +} diff --git a/src/app/header/header.component.spec.ts b/src/app/header/header.component.spec.ts index cd77385363..4a66e1d9f1 100644 --- a/src/app/header/header.component.spec.ts +++ b/src/app/header/header.component.spec.ts @@ -21,7 +21,7 @@ describe('HeaderComponent', () => { // async beforeEach beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [StoreModule.provideStore({}), TranslateModule.forRoot(), NgbCollapseModule.forRoot()], + imports: [StoreModule.forRoot({}), TranslateModule.forRoot(), NgbCollapseModule.forRoot()], declarations: [HeaderComponent] }) .compileComponents(); // compile template and css @@ -70,7 +70,7 @@ describe('HeaderComponent', () => { beforeEach(() => { menu = fixture.debugElement.query(By.css('#collapsingNav')).nativeElement; - spyOn(store, 'select').and.returnValue(Observable.of({ navCollapsed: false })); + spyOn(store, 'select').and.returnValue(Observable.of(false)); fixture.detectChanges(); }); diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index cf5b99d6f8..624ae209dd 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -1,9 +1,13 @@ import { Component, OnInit } from '@angular/core'; -import { Store } from '@ngrx/store'; +import { createSelector, Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; import { HeaderState } from './header.reducer'; import { HeaderToggleAction } from './header.actions'; +import { AppState } from '../app.reducer'; + +const headerStateSelector = (state: AppState) => state.header; +const navCollapsedSelector = createSelector(headerStateSelector, (header: HeaderState) => header.navCollapsed); @Component({ selector: 'ds-header', @@ -14,14 +18,12 @@ export class HeaderComponent implements OnInit { public isNavBarCollapsed: Observable; constructor( - private store: Store + private store: Store ) { } ngOnInit(): void { - this.isNavBarCollapsed = this.store.select('header') - // unwrap navCollapsed - .map(({ navCollapsed }: HeaderState) => navCollapsed); + this.isNavBarCollapsed = this.store.select(navCollapsedSelector); } public toggle(): void { diff --git a/src/app/header/header.effects.spec.ts b/src/app/header/header.effects.spec.ts index d8bd3af7c4..e67043dcba 100644 --- a/src/app/header/header.effects.spec.ts +++ b/src/app/header/header.effects.spec.ts @@ -1,41 +1,36 @@ -import { TestBed, inject } from '@angular/core/testing'; -import { EffectsTestingModule, EffectsRunner } from '@ngrx/effects/testing'; -import { routerActions } from '@ngrx/router-store'; - +import { TestBed } from '@angular/core/testing'; import { HeaderEffects } from './header.effects'; import { HeaderCollapseAction } from './header.actions'; import { HostWindowResizeAction } from '../shared/host-window.actions'; +import { Observable } from 'rxjs/Observable'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { cold, hot } from 'jasmine-marbles'; +import * as fromRouter from '@ngrx/router-store'; describe('HeaderEffects', () => { - beforeEach(() => TestBed.configureTestingModule({ - imports: [ - EffectsTestingModule - ], - providers: [ - HeaderEffects - ] - })); - - let runner: EffectsRunner; let headerEffects: HeaderEffects; + let actions: Observable; - beforeEach(inject([ - EffectsRunner, HeaderEffects - ], - (_runner, _headerEffects) => { - runner = _runner; - headerEffects = _headerEffects; - } - )); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + HeaderEffects, + provideMockActions(() => actions), + // other providers + ], + }); + + headerEffects = TestBed.get(HeaderEffects); + }); describe('resize$', () => { it('should return a COLLAPSE action in response to a RESIZE action', () => { - runner.queue(new HostWindowResizeAction(800, 600)); + actions = hot('--a-', { a: new HostWindowResizeAction(800, 600) }); - headerEffects.resize$.subscribe((result) => { - expect(result).toEqual(new HeaderCollapseAction()); - }); + const expected = cold('--b-', { b: new HeaderCollapseAction() }); + + expect(headerEffects.resize$).toBeObservable(expected); }); }); @@ -43,11 +38,11 @@ describe('HeaderEffects', () => { describe('routeChange$', () => { it('should return a COLLAPSE action in response to an UPDATE_LOCATION action', () => { - runner.queue({ type: routerActions.UPDATE_LOCATION }); + actions = hot('--a-', { a: { type: fromRouter.ROUTER_NAVIGATION } }); - headerEffects.resize$.subscribe((result) => { - expect(result).toEqual(new HeaderCollapseAction()); - }); + const expected = cold('--b-', { b: new HeaderCollapseAction() }); + + expect(headerEffects.routeChange$).toBeObservable(expected); }); }); diff --git a/src/app/header/header.effects.ts b/src/app/header/header.effects.ts index adc5ac8df4..e1d281958b 100644 --- a/src/app/header/header.effects.ts +++ b/src/app/header/header.effects.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { Effect, Actions } from '@ngrx/effects' -import { routerActions } from '@ngrx/router-store'; +import * as fromRouter from '@ngrx/router-store'; import { HostWindowActionTypes } from '../shared/host-window.actions'; import { HeaderCollapseAction } from './header.actions'; @@ -13,7 +13,7 @@ export class HeaderEffects { .map(() => new HeaderCollapseAction()); @Effect() routeChange$ = this.actions$ - .ofType(routerActions.UPDATE_LOCATION) + .ofType(fromRouter.ROUTER_NAVIGATION) .map(() => new HeaderCollapseAction()); constructor(private actions$: Actions) { diff --git a/src/app/header/header.reducer.ts b/src/app/header/header.reducer.ts index 0c41e8bf24..2c8a89249b 100644 --- a/src/app/header/header.reducer.ts +++ b/src/app/header/header.reducer.ts @@ -8,7 +8,7 @@ const initialState: HeaderState = { navCollapsed: true }; -export const headerReducer = (state = initialState, action: HeaderAction): HeaderState => { +export function headerReducer(state = initialState, action: HeaderAction): HeaderState { switch (action.type) { case HeaderActionTypes.COLLAPSE: { @@ -35,4 +35,4 @@ export const headerReducer = (state = initialState, action: HeaderAction): Heade return state; } } -}; +} diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.html b/src/app/item-page/simple/field-components/file-section/file-section.component.html index 1d32c8b309..0a56242075 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.html +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.html @@ -1,4 +1,4 @@ - +
{{file?.name}} diff --git a/src/app/pagenotfound/pagenotfound.component.ts b/src/app/pagenotfound/pagenotfound.component.ts index 97b70b8414..762a4000a1 100644 --- a/src/app/pagenotfound/pagenotfound.component.ts +++ b/src/app/pagenotfound/pagenotfound.component.ts @@ -1,12 +1,14 @@ -import { Component } from '@angular/core'; +import { ServerResponseService } from '../shared/server-response.service'; +import { Component, ChangeDetectionStrategy } from '@angular/core'; @Component({ selector: 'ds-pagenotfound', styleUrls: ['./pagenotfound.component.scss'], - templateUrl: './pagenotfound.component.html' + templateUrl: './pagenotfound.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class PageNotFoundComponent { - - data: any = {}; - + constructor(responseService: ServerResponseService) { + responseService.setNotFound(); + } } diff --git a/src/app/server-app.module.ts b/src/app/server-app.module.ts index e713d9fe13..cd5c24034d 100644 --- a/src/app/server-app.module.ts +++ b/src/app/server-app.module.ts @@ -1,7 +1,7 @@ import 'rxjs/add/operator/filter'; import 'rxjs/add/operator/first'; -import { ApplicationRef, Inject, NgModule, APP_BOOTSTRAP_LISTENER } from '@angular/core'; +import { ApplicationRef, NgModule, APP_BOOTSTRAP_LISTENER } from '@angular/core'; import { RouterModule } from '@angular/router'; import { ServerModule } from '@angular/platform-server'; import { BrowserModule } from '@angular/platform-browser'; @@ -16,14 +16,13 @@ import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { Store } from '@ngrx/store'; -import { Actions, EffectsModule } from '@ngrx/effects'; +import { EffectsModule } from '@ngrx/effects'; import { TranslateUniversalLoader } from '../modules/translate-universal-loader'; import { ServerTransferStateModule } from '../modules/transfer-state/server-transfer-state.module'; import { TransferState } from '../modules/transfer-state/transfer-state'; -import { TransferStoreEffects } from '../modules/transfer-store/transfer-store.effects'; import { ServerTransferStoreEffects } from '../modules/transfer-store/server-transfer-store.effects'; import { ServerTransferStoreModule } from '../modules/transfer-store/server-transfer-store.module'; @@ -32,15 +31,14 @@ import { ServerCookiesModule } from '../modules/cookies/server-cookies.module'; import { ServerDataLoaderModule } from '../modules/data-loader/server-data-loader.module'; import { AppState } from './app.reducer'; -import { effects } from './app.effects'; -import { SharedModule } from './shared/shared.module'; -import { CoreModule } from './core/core.module'; import { AppModule } from './app.module'; import { AppComponent } from './app.component'; import { GLOBAL_CONFIG, GlobalConfig } from '../config'; +import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store'; +import { DSpaceRouterStateSerializer } from './shared/ngrx/dspace-router-state-serializer'; export function boot(cache: TransferState, appRef: ApplicationRef, store: Store, request: Request, config: GlobalConfig) { // authentication mechanism goes here @@ -61,6 +59,7 @@ export function UniversalLoaderFactory() { appId: 'ds-app-id' }), RouterModule.forRoot([], { useHash: false }), + StoreRouterConnectingModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, @@ -74,7 +73,7 @@ export function UniversalLoaderFactory() { ServerDataLoaderModule, ServerTransferStateModule, ServerTransferStoreModule, - EffectsModule.run(ServerTransferStoreEffects), + EffectsModule.forRoot([ServerTransferStoreEffects]), NoopAnimationsModule, AppModule ], @@ -90,6 +89,10 @@ export function UniversalLoaderFactory() { REQUEST, GLOBAL_CONFIG ] + }, + { + provide: RouterStateSerializer, + useClass: DSpaceRouterStateSerializer } ] }) diff --git a/src/app/shared/host-window.reducer.ts b/src/app/shared/host-window.reducer.ts index ae9204c6c5..f8287efdc1 100644 --- a/src/app/shared/host-window.reducer.ts +++ b/src/app/shared/host-window.reducer.ts @@ -10,7 +10,7 @@ const initialState: HostWindowState = { height: null }; -export const hostWindowReducer = (state = initialState, action: HostWindowAction): HostWindowState => { +export function hostWindowReducer(state = initialState, action: HostWindowAction): HostWindowState { switch (action.type) { case HostWindowActionTypes.RESIZE: { @@ -21,4 +21,4 @@ export const hostWindowReducer = (state = initialState, action: HostWindowAction return state; } } -}; +} diff --git a/src/app/shared/host-window.service.spec.ts b/src/app/shared/host-window.service.spec.ts index b48d094a1e..674d0e1332 100644 --- a/src/app/shared/host-window.service.spec.ts +++ b/src/app/shared/host-window.service.spec.ts @@ -1,17 +1,18 @@ import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; +import { AppState } from '../app.reducer'; +import { HostWindowState } from './host-window.reducer'; import { HostWindowService } from './host-window.service'; -import { HostWindowState } from './host-window.reducer'; describe('HostWindowService', () => { let service: HostWindowService; - let store: Store; + let store: Store; describe('', () => { beforeEach(() => { - const _initialState = { width: 1600, height: 770 }; - store = new Store(undefined, undefined, Observable.of(_initialState)); + const _initialState = { hostWindow: { width: 1600, height: 770 } }; + store = new Store(Observable.of(_initialState), undefined, undefined); service = new HostWindowService(store); }); @@ -46,8 +47,8 @@ describe('HostWindowService', () => { describe('', () => { beforeEach(() => { - const _initialState = { width: 1100, height: 770 }; - store = new Store(undefined, undefined, Observable.of(_initialState)); + const _initialState = { hostWindow: { width: 1100, height: 770 } }; + store = new Store(Observable.of(_initialState), undefined, undefined); service = new HostWindowService(store); }); @@ -82,8 +83,8 @@ describe('HostWindowService', () => { describe('', () => { beforeEach(() => { - const _initialState = { width: 800, height: 770 }; - store = new Store(undefined, undefined, Observable.of(_initialState)); + const _initialState = { hostWindow: { width: 800, height: 770 } }; + store = new Store(Observable.of(_initialState), undefined, undefined); service = new HostWindowService(store); }); @@ -118,8 +119,8 @@ describe('HostWindowService', () => { describe('', () => { beforeEach(() => { - const _initialState = { width: 600, height: 770 }; - store = new Store(undefined, undefined, Observable.of(_initialState)); + const _initialState = { hostWindow: { width: 600, height: 770 } }; + store = new Store(Observable.of(_initialState), undefined, undefined); service = new HostWindowService(store); }); @@ -154,8 +155,8 @@ describe('HostWindowService', () => { describe('', () => { beforeEach(() => { - const _initialState = { width: 400, height: 770 }; - store = new Store(undefined, undefined, Observable.of(_initialState)); + const _initialState = { hostWindow: { width: 400, height: 770 } }; + store = new Store(Observable.of(_initialState), undefined, undefined); service = new HostWindowService(store); }); diff --git a/src/app/shared/host-window.service.ts b/src/app/shared/host-window.service.ts index d04ce3a7dd..6fa5a6b32b 100644 --- a/src/app/shared/host-window.service.ts +++ b/src/app/shared/host-window.service.ts @@ -1,9 +1,10 @@ import { HostWindowState } from './host-window.reducer'; import { Injectable } from '@angular/core'; -import { Store } from '@ngrx/store'; +import { createSelector, Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; import { hasValue } from './empty.util'; +import { AppState } from '../app.reducer'; // TODO: ideally we should get these from sass somehow export enum GridBreakpoint { @@ -14,16 +15,19 @@ export enum GridBreakpoint { XL = 1200 } +const hostWindowStateSelector = (state: AppState) => state.hostWindow; +const widthSelector = createSelector(hostWindowStateSelector, (hostWindow: HostWindowState) => hostWindow.width); + @Injectable() export class HostWindowService { constructor( - private store: Store + private store: Store ) { } private getWidthObs(): Observable { - return this.store.select('hostWindow', 'width') + return this.store.select(widthSelector) .filter((width) => hasValue(width)); } diff --git a/src/app/shared/ngrx/dspace-router-state-serializer.ts b/src/app/shared/ngrx/dspace-router-state-serializer.ts new file mode 100644 index 0000000000..2ddd37ca43 --- /dev/null +++ b/src/app/shared/ngrx/dspace-router-state-serializer.ts @@ -0,0 +1,18 @@ +import { RouterStateSerializer } from '@ngrx/router-store'; +import { Params, RouterStateSnapshot } from '@angular/router'; + +export interface RouterStateUrl { + url: string; + queryParams: Params; +} + +export class DSpaceRouterStateSerializer implements RouterStateSerializer { + serialize(routerState: RouterStateSnapshot): RouterStateUrl { + const { url } = routerState; + const queryParams = routerState.root.queryParams; + + // Only return an object including the URL and query params + // instead of the entire snapshot + return { url, queryParams }; + } +} diff --git a/src/app/shared/pagination/pagination.component.spec.ts b/src/app/shared/pagination/pagination.component.spec.ts index 96a3695d01..051683c914 100644 --- a/src/app/shared/pagination/pagination.component.spec.ts +++ b/src/app/shared/pagination/pagination.component.spec.ts @@ -140,7 +140,7 @@ describe('Pagination component', () => { TestBed.configureTestingModule({ imports: [ CommonModule, - StoreModule.provideStore({}), + StoreModule.forRoot({}), TranslateModule.forRoot({ loader: { provide: TranslateLoader, diff --git a/src/app/shared/server-response.service.ts b/src/app/shared/server-response.service.ts new file mode 100644 index 0000000000..f143c56ffb --- /dev/null +++ b/src/app/shared/server-response.service.ts @@ -0,0 +1,26 @@ +import { RESPONSE } from '@nguniversal/express-engine/tokens'; +import { Inject, Injectable, Optional } from '@angular/core'; +import { Response } from 'express'; + +@Injectable() +export class ServerResponseService { + private response: Response; + + constructor(@Optional() @Inject(RESPONSE) response: any) { + this.response = response; + } + + setStatus(code: number, message?: string): this { + if (this.response) { + this.response.statusCode = code; + if (message) { + this.response.statusMessage = message; + } + } + return this; + } + + setNotFound(message = 'Not found'): this { + return this.setStatus(404, message) + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 9a899bc095..98eaccdbab 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -27,6 +27,7 @@ import { TruncatePipe } from './utils/truncate.pipe'; import { WrapperListElementComponent } from '../object-list/wrapper-list-element/wrapper-list-element.component'; import { SearchResultListElementComponent } from '../object-list/search-result-list-element/search-result-list-element.component'; import { SearchFormComponent } from './search-form/search-form.component'; +import { ServerResponseService } from './server-response.service'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -71,7 +72,8 @@ const ENTRY_COMPONENTS = [ const PROVIDERS = [ ApiService, HostWindowService, - { provide: NativeWindowService, useFactory: NativeWindowFactory } + { provide: NativeWindowService, useFactory: NativeWindowFactory }, + ServerResponseService ]; @NgModule({ diff --git a/src/modules/transfer-state/browser-transfer-state.ts b/src/modules/transfer-state/browser-transfer-state.ts index d1e0a1ecb0..79d7ee3df0 100644 --- a/src/modules/transfer-state/browser-transfer-state.ts +++ b/src/modules/transfer-state/browser-transfer-state.ts @@ -8,6 +8,7 @@ import { StoreAction, StoreActionTypes } from '../../app/store.actions'; import { AppState } from '../../app/app.reducer'; import { GLOBAL_CONFIG, GlobalConfig } from '../../config'; +import { RouterNavigationAction } from '@ngrx/router-store'; @Injectable() export class BrowserTransferState extends TransferState { diff --git a/yarn.lock b/yarn.lock index adaaf92f4a..243cf65ae3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -90,25 +90,21 @@ version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-1.0.0-beta.1.tgz#a7d5935293df22a2275bf572f2197b45136e3c52" -"@ngrx/core@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@ngrx/core/-/core-1.2.0.tgz#882b46abafa2e0e6d887cb71a1b2c2fa3e6d0dc6" +"@ngrx/effects@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@ngrx/effects/-/effects-4.0.5.tgz#1224763800621b7305f9b18bc17ee09b25c861d1" -"@ngrx/effects@2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@ngrx/effects/-/effects-2.0.4.tgz#418eee5e1032fa66de5bbf1855653bb1951f12a4" +"@ngrx/router-store@^4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@ngrx/router-store/-/router-store-4.0.4.tgz#ab59f35aae93465088384faf009e21b22edd456a" -"@ngrx/router-store@1.2.6": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@ngrx/router-store/-/router-store-1.2.6.tgz#a2eb0ca515e9b367781f1030250dd64bb73c086b" +"@ngrx/store-devtools@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@ngrx/store-devtools/-/store-devtools-4.0.0.tgz#b79c24773217df7fd9735ad21f9cbf2533c96e04" -"@ngrx/store-devtools@3.2.4": - version "3.2.4" - resolved "https://registry.yarnpkg.com/@ngrx/store-devtools/-/store-devtools-3.2.4.tgz#2ce4d13bf34848a9e51ec87e3b125ed67b51e550" - -"@ngrx/store@2.2.3": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@ngrx/store/-/store-2.2.3.tgz#e7bd1149f1c44208f1cc4744353f0f98a0f1f57b" +"@ngrx/store@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@ngrx/store/-/store-4.0.3.tgz#36abacdfa19bfb8506e40de80bae06050a1e15e9" "@ngtools/webpack@1.5.1": version "1.5.1" @@ -1731,10 +1727,6 @@ deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" -deep-freeze-strict@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/deep-freeze-strict/-/deep-freeze-strict-1.1.1.tgz#77d0583ca24a69be4bbd9ac2fae415d55523e5b0" - deep-freeze@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" @@ -3487,6 +3479,12 @@ jasmine-core@2.6.4, jasmine-core@~2.6.0: version "2.6.4" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.6.4.tgz#dec926cd0a9fa287fb6db5c755fa487e74cecac5" +jasmine-marbles@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/jasmine-marbles/-/jasmine-marbles-0.1.0.tgz#c9ecdc64e20b6cf55b49a10201a5be33907dadcc" + dependencies: + lodash.isequal "^4.5.0" + jasmine-spec-reporter@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-4.1.1.tgz#5a6d58ab5d61bea7309fbc279239511756b1b588" @@ -3936,6 +3934,10 @@ lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + lodash.keys@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" @@ -4359,12 +4361,6 @@ nested-error-stacks@^1.0.0: dependencies: inherits "~2.0.1" -ngrx-store-freeze@0.1.9: - version "0.1.9" - resolved "https://registry.yarnpkg.com/ngrx-store-freeze/-/ngrx-store-freeze-0.1.9.tgz#b20f18f21fd5efc4e1b1e05f6f279674d0f70c81" - dependencies: - deep-freeze-strict "^1.1.1" - ngx-pagination@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/ngx-pagination/-/ngx-pagination-3.0.1.tgz#5a8000e40c0424d9c41c9d6d592562e1547abf24"