diff --git a/src/app/init.service.spec.ts b/src/app/init.service.spec.ts index c046ad5715..181fe58700 100644 --- a/src/app/init.service.spec.ts +++ b/src/app/init.service.spec.ts @@ -25,7 +25,6 @@ import { RouteService } from './core/services/route.service'; import { getMockLocaleService } from './app.component.spec'; import { MenuServiceStub } from './shared/testing/menu-service.stub'; import { CorrelationIdService } from './correlation-id/correlation-id.service'; -import { DSpaceTransferState } from '../modules/transfer-state/dspace-transfer-state.service'; import { KlaroService } from './shared/cookies/klaro.service'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from './shared/mocks/translate-loader.mock'; @@ -161,7 +160,6 @@ describe('InitService', () => { providers: [ { provide: InitService, useClass: ConcreteInitServiceMock }, { provide: CorrelationIdService, useValue: correlationIdServiceSpy }, - { provide: DSpaceTransferState, useValue: dspaceTransferStateSpy }, { provide: APP_CONFIG, useValue: environment }, { provide: LocaleService, useValue: getMockLocaleService() }, { provide: Angulartics2DSpace, useValue: new AngularticsProviderMock() }, diff --git a/src/app/init.service.ts b/src/app/init.service.ts index 62461212d2..ae04b6a82e 100644 --- a/src/app/init.service.ts +++ b/src/app/init.service.ts @@ -8,9 +8,8 @@ import { select, Store } from '@ngrx/store'; import { CheckAuthenticationTokenAction } from './core/auth/auth.actions'; import { CorrelationIdService } from './correlation-id/correlation-id.service'; -import { DSpaceTransferState } from '../modules/transfer-state/dspace-transfer-state.service'; import { APP_INITIALIZER, Inject, Optional, Provider, Type } from '@angular/core'; -import { TransferState } from '@angular/platform-browser'; +import { makeStateKey, TransferState } from '@angular/platform-browser'; import { APP_CONFIG, AppConfig } from '../config/app-config.interface'; import { environment } from '../environments/environment'; import { AppState } from './app.reducer'; @@ -38,10 +37,15 @@ import { ThemeService } from './shared/theme-support/theme.service'; * For example, NgbModal depends on ApplicationRef and can therefore not be used during initialization. */ export abstract class InitService { + /** + * The state transfer key to use for the NgRx store state + * @protected + */ + protected static NGRX_STATE = makeStateKey('NGRX_STATE'); + protected constructor( protected store: Store, protected correlationIdService: CorrelationIdService, - protected dspaceTransferState: DSpaceTransferState, @Inject(APP_CONFIG) protected appConfig: AppConfig, protected translate: TranslateService, protected localeService: LocaleService, @@ -130,14 +134,6 @@ export abstract class InitService { this.correlationIdService.initCorrelationId(); } - /** - * Transfer the application's NgRx state between server-side and client-side - * @protected - */ - protected async transferAppState(): Promise { - return this.dspaceTransferState.transfer(); - } - /** * Make sure the {@link environment} matches {@link APP_CONFIG} and print * some information about it to the console diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index cd3feedad8..d857b14dbd 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpClientModule } from '@angular/common/http'; import { NgModule } from '@angular/core'; -import { BrowserModule, makeStateKey, TransferState } from '@angular/platform-browser'; +import { BrowserModule, BrowserTransferStateModule, makeStateKey, TransferState } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { REQUEST } from '@nguniversal/express-engine/tokens'; @@ -12,7 +12,6 @@ import { IdlePreloadModule } from 'angular-idle-preload'; import { AppComponent } from '../../app/app.component'; import { AppModule } from '../../app/app.module'; -import { DSpaceBrowserTransferStateModule } from '../transfer-state/dspace-browser-transfer-state.module'; import { ClientCookieService } from '../../app/core/services/client-cookie.service'; import { CookieService } from '../../app/core/services/cookie.service'; import { AuthService } from '../../app/core/auth/auth.service'; @@ -28,7 +27,6 @@ import { GoogleAnalyticsService } from '../../app/statistics/google-analytics.se import { AuthRequestService } from '../../app/core/auth/auth-request.service'; import { BrowserAuthRequestService } from '../../app/core/auth/browser-auth-request.service'; import { BrowserInitService } from './browser-init.service'; -import { InitService } from '../../app/init.service'; export const REQ_KEY = makeStateKey('req'); @@ -52,7 +50,7 @@ export function getRequest(transferState: TransferState): any { StatisticsModule.forRoot(), Angulartics2RouterlessModule.forRoot(), BrowserAnimationsModule, - DSpaceBrowserTransferStateModule, + BrowserTransferStateModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, diff --git a/src/modules/app/browser-init.service.ts b/src/modules/app/browser-init.service.ts index e5718045c6..28d2e0aad5 100644 --- a/src/modules/app/browser-init.service.ts +++ b/src/modules/app/browser-init.service.ts @@ -8,7 +8,6 @@ import { InitService } from '../../app/init.service'; import { Store } from '@ngrx/store'; import { AppState } from '../../app/app.reducer'; -import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service'; import { TransferState } from '@angular/platform-browser'; import { APP_CONFIG, APP_CONFIG_STATE, AppConfig } from '../../config/app-config.interface'; import { DefaultAppConfig } from '../../config/default-app-config'; @@ -26,6 +25,10 @@ import { CSSVariableService } from '../../app/shared/sass-helper/sass-helper.ser import { KlaroService } from '../../app/shared/cookies/klaro.service'; import { AuthService } from '../../app/core/auth/auth.service'; import { ThemeService } from '../../app/shared/theme-support/theme.service'; +import { StoreAction, StoreActionTypes } from '../../app/store.actions'; +import { coreSelector } from '../../app/core/core.selectors'; +import { find, map } from 'rxjs/operators'; +import { isNotEmpty } from '../../app/shared/empty.util'; /** * Performs client-side initialization. @@ -36,7 +39,6 @@ export class BrowserInitService extends InitService { protected store: Store, protected correlationIdService: CorrelationIdService, protected transferState: TransferState, - protected dspaceTransferState: DSpaceTransferState, @Inject(APP_CONFIG) protected appConfig: AppConfig, protected translate: TranslateService, protected localeService: LocaleService, @@ -52,7 +54,6 @@ export class BrowserInitService extends InitService { super( store, correlationIdService, - dspaceTransferState, appConfig, translate, localeService, @@ -77,7 +78,7 @@ export class BrowserInitService extends InitService { protected init(): () => Promise { return async () => { - await this.transferAppState(); + await this.loadAppState(); this.checkAuthenticationToken(); this.initCorrelationId(); @@ -97,6 +98,21 @@ export class BrowserInitService extends InitService { // Browser-only initialization steps + /** + * Retrieve server-side application state from the {@link NGRX_STATE} key and rehydrate the store. + * Resolves once the store is no longer empty. + * @private + */ + private async loadAppState(): Promise { + const state = this.transferState.get(InitService.NGRX_STATE, null); + this.transferState.remove(InitService.NGRX_STATE); + this.store.dispatch(new StoreAction(StoreActionTypes.REHYDRATE, state)); + return this.store.select(coreSelector).pipe( + find((core: any) => isNotEmpty(core)), + map(() => true) + ).toPromise(); + } + private trackAuthTokenExpiration(): void { this.authService.trackTokenExpiration(); } diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts index 2b0462e9a0..c87963b2f8 100644 --- a/src/modules/app/server-app.module.ts +++ b/src/modules/app/server-app.module.ts @@ -2,7 +2,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { NgModule } from '@angular/core'; import { BrowserModule, TransferState } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { ServerModule } from '@angular/platform-server'; +import { ServerModule, ServerTransferStateModule } from '@angular/platform-server'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; @@ -12,7 +12,6 @@ import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; import { AppComponent } from '../../app/app.component'; import { AppModule } from '../../app/app.module'; -import { DSpaceServerTransferStateModule } from '../transfer-state/dspace-server-transfer-state.module'; import { TranslateServerLoader } from '../../ngx-translate-loaders/translate-server.loader'; import { CookieService } from '../../app/core/services/cookie.service'; import { ServerCookieService } from '../../app/core/services/server-cookie.service'; @@ -43,7 +42,7 @@ export function createTranslateLoader(transferState: TransferState) { appId: 'dspace-angular' }), NoopAnimationsModule, - DSpaceServerTransferStateModule, + ServerTransferStateModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, diff --git a/src/modules/app/server-init.service.ts b/src/modules/app/server-init.service.ts index 9e23bbeef3..a0e0b84769 100644 --- a/src/modules/app/server-init.service.ts +++ b/src/modules/app/server-init.service.ts @@ -9,7 +9,6 @@ import { InitService } from '../../app/init.service'; import { Store } from '@ngrx/store'; import { AppState } from '../../app/app.reducer'; import { TransferState } from '@angular/platform-browser'; -import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service'; import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service'; import { APP_CONFIG, APP_CONFIG_STATE, AppConfig } from '../../config/app-config.interface'; import { environment } from '../../environments/environment'; @@ -23,6 +22,7 @@ import { BreadcrumbsService } from '../../app/breadcrumbs/breadcrumbs.service'; import { CSSVariableService } from '../../app/shared/sass-helper/sass-helper.service'; import { KlaroService } from '../../app/shared/cookies/klaro.service'; import { ThemeService } from '../../app/shared/theme-support/theme.service'; +import { take } from 'rxjs/operators'; /** * Performs server-side initialization. @@ -33,7 +33,6 @@ export class ServerInitService extends InitService { protected store: Store, protected correlationIdService: CorrelationIdService, protected transferState: TransferState, - protected dspaceTransferState: DSpaceTransferState, @Inject(APP_CONFIG) protected appConfig: AppConfig, protected translate: TranslateService, protected localeService: LocaleService, @@ -48,7 +47,6 @@ export class ServerInitService extends InitService { super( store, correlationIdService, - dspaceTransferState, appConfig, translate, localeService, @@ -65,7 +63,7 @@ export class ServerInitService extends InitService { return async () => { this.checkAuthenticationToken(); this.saveAppConfigForCSR(); - this.transferAppState(); // todo: SSR breaks if we await this (why?) + this.saveAppState(); this.initCorrelationId(); this.checkEnvironment(); @@ -82,6 +80,21 @@ export class ServerInitService extends InitService { // Server-only initialization steps + /** + * Set the {@link NGRX_STATE} key when state is serialized to be transfered + * @private + */ + private saveAppState() { + this.transferState.onSerialize(InitService.NGRX_STATE, () => { + let state; + this.store.pipe(take(1)).subscribe((saveState: any) => { + state = saveState; + }); + + return state; + }); + } + private saveAppConfigForCSR(): void { this.transferState.set(APP_CONFIG_STATE, environment as AppConfig); } diff --git a/src/modules/transfer-state/dspace-browser-transfer-state.module.ts b/src/modules/transfer-state/dspace-browser-transfer-state.module.ts deleted file mode 100644 index e251d0b3b2..0000000000 --- a/src/modules/transfer-state/dspace-browser-transfer-state.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BrowserTransferStateModule } from '@angular/platform-browser'; -import { DSpaceBrowserTransferState } from './dspace-browser-transfer-state.service'; -import { DSpaceTransferState } from './dspace-transfer-state.service'; - -@NgModule({ - imports: [ - BrowserTransferStateModule - ], - providers: [ - { provide: DSpaceTransferState, useClass: DSpaceBrowserTransferState } - ] -}) -export class DSpaceBrowserTransferStateModule { - -} diff --git a/src/modules/transfer-state/dspace-browser-transfer-state.service.ts b/src/modules/transfer-state/dspace-browser-transfer-state.service.ts deleted file mode 100644 index 512d6aeb71..0000000000 --- a/src/modules/transfer-state/dspace-browser-transfer-state.service.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable } from '@angular/core'; -import { coreSelector } from 'src/app/core/core.selectors'; -import { StoreAction, StoreActionTypes } from '../../app/store.actions'; -import { DSpaceTransferState } from './dspace-transfer-state.service'; -import { find, map } from 'rxjs/operators'; -import { isNotEmpty } from '../../app/shared/empty.util'; - -@Injectable() -export class DSpaceBrowserTransferState extends DSpaceTransferState { - transfer(): Promise { - const state = this.transferState.get(DSpaceTransferState.NGRX_STATE, null); - this.transferState.remove(DSpaceTransferState.NGRX_STATE); - this.store.dispatch(new StoreAction(StoreActionTypes.REHYDRATE, state)); - return this.store.select(coreSelector).pipe( - find((core: any) => isNotEmpty(core)), - map(() => true) - ).toPromise(); - } -} diff --git a/src/modules/transfer-state/dspace-server-transfer-state.module.ts b/src/modules/transfer-state/dspace-server-transfer-state.module.ts deleted file mode 100644 index f8f2631cd0..0000000000 --- a/src/modules/transfer-state/dspace-server-transfer-state.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ServerTransferStateModule } from '@angular/platform-server'; -import { DSpaceServerTransferState } from './dspace-server-transfer-state.service'; -import { DSpaceTransferState } from './dspace-transfer-state.service'; - -@NgModule({ - imports: [ - ServerTransferStateModule - ], - providers: [ - { provide: DSpaceTransferState, useClass: DSpaceServerTransferState } - ] -}) -export class DSpaceServerTransferStateModule { - -} diff --git a/src/modules/transfer-state/dspace-server-transfer-state.service.ts b/src/modules/transfer-state/dspace-server-transfer-state.service.ts deleted file mode 100644 index 96b1e4be38..0000000000 --- a/src/modules/transfer-state/dspace-server-transfer-state.service.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import {take} from 'rxjs/operators'; -import { Injectable } from '@angular/core'; -import { DSpaceTransferState } from './dspace-transfer-state.service'; - -@Injectable() -export class DSpaceServerTransferState extends DSpaceTransferState { - transfer(): Promise { - this.transferState.onSerialize(DSpaceTransferState.NGRX_STATE, () => { - let state; - this.store.pipe(take(1)).subscribe((saveState: any) => { - state = saveState; - }); - - return state; - }); - - return new Promise(() => true); - } -} diff --git a/src/modules/transfer-state/dspace-transfer-state.service.ts b/src/modules/transfer-state/dspace-transfer-state.service.ts deleted file mode 100644 index 32761866fb..0000000000 --- a/src/modules/transfer-state/dspace-transfer-state.service.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Injectable } from '@angular/core'; -import { makeStateKey, TransferState } from '@angular/platform-browser'; -import { Store } from '@ngrx/store'; -import { AppState } from '../../app/app.reducer'; - -@Injectable() -export abstract class DSpaceTransferState { - - protected static NGRX_STATE = makeStateKey('NGRX_STATE'); - - constructor( - protected transferState: TransferState, - protected store: Store - ) { - } - - abstract transfer(): Promise; -}