diff --git a/src/app/app.component.ts b/src/app/app.component.ts index d3668fad5a..6f06a84144 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { delay, distinctUntilChanged, filter, switchMap, take, withLatestFrom } from 'rxjs/operators'; +import { distinctUntilChanged, filter, switchMap, take, withLatestFrom } from 'rxjs/operators'; import { AfterViewInit, ChangeDetectionStrategy, @@ -13,9 +13,8 @@ import { ActivatedRouteSnapshot, NavigationCancel, NavigationEnd, - NavigationStart, + NavigationStart, ResolveEnd, Router, - RoutesRecognized } from '@angular/router'; import { BehaviorSubject, Observable, of } from 'rxjs'; @@ -113,7 +112,6 @@ export class AppComponent implements OnInit, AfterViewInit { this.themeService.getThemeName$().subscribe((themeName: string) => { if (isPlatformBrowser(this.platformId)) { // the theme css will never download server side, so this should only happen on the browser - this.isThemeLoading$.next(true); this.isThemeCSSLoading$.next(true); } if (hasValue(themeName)) { @@ -186,14 +184,14 @@ export class AppComponent implements OnInit, AfterViewInit { } ngAfterViewInit() { - this.router.events.pipe( - // This fixes an ExpressionChangedAfterItHasBeenCheckedError from being thrown while loading the component - // More information on this bug-fix: https://blog.angular-university.io/angular-debugging/ - delay(0) - ).subscribe((event) => { + let resolveEndFound = false; + this.router.events.subscribe((event) => { if (event instanceof NavigationStart) { + resolveEndFound = false; this.isRouteLoading$.next(true); - } else if (event instanceof RoutesRecognized) { + this.isThemeLoading$.next(true); + } else if (event instanceof ResolveEnd) { + resolveEndFound = true; const activatedRouteSnapShot: ActivatedRouteSnapshot = event.state.root; this.themeService.updateThemeOnRouteChange$(event.urlAfterRedirects, activatedRouteSnapShot).pipe( switchMap((changed) => { @@ -210,6 +208,9 @@ export class AppComponent implements OnInit, AfterViewInit { event instanceof NavigationEnd || event instanceof NavigationCancel ) { + if (!resolveEndFound) { + this.isThemeLoading$.next(false); + } this.isRouteLoading$.next(false); } }); diff --git a/src/app/shared/theme-support/theme.service.ts b/src/app/shared/theme-support/theme.service.ts index f351f320d7..77be1691bf 100644 --- a/src/app/shared/theme-support/theme.service.ts +++ b/src/app/shared/theme-support/theme.service.ts @@ -3,9 +3,9 @@ import { Store, createFeatureSelector, createSelector, select, Action } from '@n import { Observable } from 'rxjs/internal/Observable'; import { ThemeState } from './theme.reducer'; import { SetThemeAction, ThemeActionTypes } from './theme.actions'; -import { expand, filter, map, startWith, switchMap, take, toArray } from 'rxjs/operators'; +import { expand, filter, map, startWith, switchMap, take, tap, toArray } from 'rxjs/operators'; import { hasValue, isNotEmpty } from '../empty.util'; -import { Actions, ofType } from '@ngrx/effects'; +import { act, Actions, ofType } from '@ngrx/effects'; import { ResolvedAction, ResolverActionTypes } from '../../core/resolving/resolver.actions'; import { RemoteData } from '../../core/data/remote-data'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; @@ -86,9 +86,10 @@ export class ThemeService { const action$ = currentTheme$.pipe( switchMap((currentTheme: string) => { + const snapshotWithData = this.findRouteData(activatedRouteSnapshot); if (this.hasDynamicTheme === true && isNotEmpty(this.themes)) { - if (hasValue(activatedRouteSnapshot) && hasValue(activatedRouteSnapshot.data) && hasValue(activatedRouteSnapshot.data.dso)) { - const dsoRD: RemoteData = activatedRouteSnapshot.data.dso; + if (hasValue(snapshotWithData) && hasValue(snapshotWithData.data) && hasValue(snapshotWithData.data.dso)) { + const dsoRD: RemoteData = snapshotWithData.data.dso; if (dsoRD.hasSucceeded) { // Start with the resolved dso and go recursively through its parents until you reach the top-level community return observableOf(dsoRD.payload).pipe( @@ -123,10 +124,10 @@ export class ThemeService { // If there are no themes configured, do nothing return [new NoOpAction()]; }), + take(1), ); action$.pipe( - take(1), filter((action) => action.type !== NO_OP_ACTION_TYPE), ).subscribe((action) => { this.store.dispatch(action); @@ -137,6 +138,22 @@ export class ThemeService { ); } + findRouteData(...routes: ActivatedRouteSnapshot[]) { + const result = routes.find((route) => hasValue(route.data.dso)); + if (hasValue(result)) { + return result; + } else { + const nextLevelRoutes = routes + .map((route: ActivatedRouteSnapshot) => route.children) + .reduce((combined: ActivatedRouteSnapshot[], current: ActivatedRouteSnapshot[]) => [...combined, ...current]); + if (isNotEmpty(nextLevelRoutes)) { + return this.findRouteData(...nextLevelRoutes); + } else { + return undefined; + } + } + } + /** * An rxjs operator that will return an array of all the ancestors of the DSpaceObject used as * input. The initial DSpaceObject will be the first element of the output array, followed by