mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
pages loading twice poc
Cherry-picked from original branch started from Angular 13 PR
This commit is contained in:

committed by
Yura Bondarenko

parent
e4f483c308
commit
3bc5ee0253
2
.gitignore
vendored
2
.gitignore
vendored
@@ -36,3 +36,5 @@ package-lock.json
|
||||
|
||||
.env
|
||||
/nbproject/
|
||||
|
||||
junit.xml
|
||||
|
@@ -142,7 +142,7 @@
|
||||
"@types/node": "^14.14.9",
|
||||
"axe-core": "^4.3.3",
|
||||
"codelyzer": "^6.0.0",
|
||||
"compression-webpack-plugin": "^3.0.1",
|
||||
"compression-webpack-plugin": "^6.1.1",
|
||||
"copy-webpack-plugin": "^6.4.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "3.4.0",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { RouterModule, NoPreloading } from '@angular/router';
|
||||
import { AuthBlockingGuard } from './core/auth/auth-blocking.guard';
|
||||
|
||||
import { AuthenticatedGuard } from './core/auth/authenticated.guard';
|
||||
@@ -30,6 +30,7 @@ import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component
|
||||
import { GroupAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard';
|
||||
import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component';
|
||||
import { ServerCheckGuard } from './core/server-check/server-check.guard';
|
||||
import { MenuResolver } from './menu.resolver';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -39,6 +40,7 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard';
|
||||
path: '',
|
||||
canActivate: [AuthBlockingGuard],
|
||||
canActivateChild: [ServerCheckGuard],
|
||||
resolve: [MenuResolver],
|
||||
children: [
|
||||
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
||||
{
|
||||
@@ -217,6 +219,12 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard';
|
||||
]
|
||||
}
|
||||
], {
|
||||
// enableTracing: true,
|
||||
useHash: false,
|
||||
scrollPositionRestoration: 'enabled',
|
||||
anchorScrolling: 'enabled',
|
||||
initialNavigation: 'enabledBlocking',
|
||||
preloadingStrategy: NoPreloading,
|
||||
onSameUrlNavigation: 'reload',
|
||||
})
|
||||
],
|
||||
|
@@ -72,7 +72,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
/**
|
||||
* Whether or not the app is in the process of rerouting
|
||||
*/
|
||||
isRouteLoading$: BehaviorSubject<boolean> = new BehaviorSubject(true);
|
||||
isRouteLoading$: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
|
||||
/**
|
||||
* Whether or not the theme is in the process of being swapped
|
||||
@@ -121,7 +121,7 @@ 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.isThemeCSSLoading$.next(true);
|
||||
this.distinctNext(this.isThemeCSSLoading$, true);
|
||||
}
|
||||
if (hasValue(themeName)) {
|
||||
this.loadGlobalThemeConfig(themeName);
|
||||
@@ -200,8 +200,8 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
this.router.events.subscribe((event) => {
|
||||
if (event instanceof NavigationStart) {
|
||||
resolveEndFound = false;
|
||||
this.isRouteLoading$.next(true);
|
||||
this.isThemeLoading$.next(true);
|
||||
this.distinctNext(this.isRouteLoading$, true);
|
||||
this.distinctNext(this.isThemeLoading$, true);
|
||||
} else if (event instanceof ResolveEnd) {
|
||||
resolveEndFound = true;
|
||||
const activatedRouteSnapShot: ActivatedRouteSnapshot = event.state.root;
|
||||
@@ -214,16 +214,16 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
})
|
||||
).subscribe((changed) => {
|
||||
this.isThemeLoading$.next(changed);
|
||||
this.distinctNext(this.isThemeLoading$, changed);
|
||||
});
|
||||
} else if (
|
||||
event instanceof NavigationEnd ||
|
||||
event instanceof NavigationCancel
|
||||
) {
|
||||
if (!resolveEndFound) {
|
||||
this.isThemeLoading$.next(false);
|
||||
this.distinctNext(this.isThemeLoading$, false);
|
||||
}
|
||||
this.isRouteLoading$.next(false);
|
||||
this.distinctNext(this.isRouteLoading$, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -281,7 +281,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
});
|
||||
}
|
||||
// the fact that this callback is used, proves we're on the browser.
|
||||
this.isThemeCSSLoading$.next(false);
|
||||
this.distinctNext(this.isThemeCSSLoading$, false);
|
||||
};
|
||||
head.appendChild(link);
|
||||
}
|
||||
@@ -376,4 +376,17 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Use nextValue to update a given BehaviorSubject, only if it differs from its current value
|
||||
*
|
||||
* @param bs a BehaviorSubject
|
||||
* @param nextValue the next value for that BehaviorSubject
|
||||
* @protected
|
||||
*/
|
||||
protected distinctNext<T>(bs: BehaviorSubject<T>, nextValue: T): void {
|
||||
if (bs.getValue() !== nextValue) {
|
||||
bs.next(nextValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,10 +16,6 @@ import {
|
||||
} from '@ng-dynamic-forms/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
|
||||
|
||||
import { AdminSidebarSectionComponent } from './admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component';
|
||||
import { AdminSidebarComponent } from './admin/admin-sidebar/admin-sidebar.component';
|
||||
import { ExpandableAdminSidebarSectionComponent } from './admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { appEffects } from './app.effects';
|
||||
@@ -28,36 +24,18 @@ import { appReducers, AppState, storeModuleConfig } from './app.reducer';
|
||||
import { CheckAuthenticationTokenAction } from './core/auth/auth.actions';
|
||||
import { CoreModule } from './core/core.module';
|
||||
import { ClientCookieService } from './core/services/client-cookie.service';
|
||||
import { FooterComponent } from './footer/footer.component';
|
||||
import { HeaderNavbarWrapperComponent } from './header-nav-wrapper/header-navbar-wrapper.component';
|
||||
import { HeaderComponent } from './header/header.component';
|
||||
import { NavbarModule } from './navbar/navbar.module';
|
||||
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
||||
import { DSpaceRouterStateSerializer } from './shared/ngrx/dspace-router-state-serializer';
|
||||
import { NotificationComponent } from './shared/notifications/notification/notification.component';
|
||||
import { NotificationsBoardComponent } from './shared/notifications/notifications-board/notifications-board.component';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { BreadcrumbsComponent } from './breadcrumbs/breadcrumbs.component';
|
||||
import { environment } from '../environments/environment';
|
||||
import { ForbiddenComponent } from './forbidden/forbidden.component';
|
||||
import { AuthInterceptor } from './core/auth/auth.interceptor';
|
||||
import { LocaleInterceptor } from './core/locale/locale.interceptor';
|
||||
import { XsrfInterceptor } from './core/xsrf/xsrf.interceptor';
|
||||
import { LogInterceptor } from './core/log/log.interceptor';
|
||||
import { RootComponent } from './root/root.component';
|
||||
import { ThemedRootComponent } from './root/themed-root.component';
|
||||
import { ThemedEntryComponentModule } from '../themes/themed-entry-component.module';
|
||||
import { ThemedPageNotFoundComponent } from './pagenotfound/themed-pagenotfound.component';
|
||||
import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component';
|
||||
import { ThemedHeaderComponent } from './header/themed-header.component';
|
||||
import { ThemedFooterComponent } from './footer/themed-footer.component';
|
||||
import { ThemedBreadcrumbsComponent } from './breadcrumbs/themed-breadcrumbs.component';
|
||||
import { ThemedHeaderNavbarWrapperComponent } from './header-nav-wrapper/themed-header-navbar-wrapper.component';
|
||||
import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
|
||||
import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component';
|
||||
import { PageInternalServerErrorComponent } from './page-internal-server-error/page-internal-server-error.component';
|
||||
import { EagerThemesModule } from '../themes/eager-themes.module';
|
||||
|
||||
import { APP_CONFIG, AppConfig } from '../config/app-config.interface';
|
||||
import { RootModule } from './root.module';
|
||||
|
||||
export function getConfig() {
|
||||
return environment;
|
||||
@@ -92,7 +70,8 @@ const IMPORTS = [
|
||||
EffectsModule.forRoot(appEffects),
|
||||
StoreModule.forRoot(appReducers, storeModuleConfig),
|
||||
StoreRouterConnectingModule.forRoot(),
|
||||
ThemedEntryComponentModule.withEntryComponents(),
|
||||
EagerThemesModule,
|
||||
RootModule,
|
||||
];
|
||||
|
||||
IMPORTS.push(
|
||||
@@ -164,28 +143,6 @@ const PROVIDERS = [
|
||||
|
||||
const DECLARATIONS = [
|
||||
AppComponent,
|
||||
RootComponent,
|
||||
ThemedRootComponent,
|
||||
HeaderComponent,
|
||||
ThemedHeaderComponent,
|
||||
HeaderNavbarWrapperComponent,
|
||||
ThemedHeaderNavbarWrapperComponent,
|
||||
AdminSidebarComponent,
|
||||
AdminSidebarSectionComponent,
|
||||
ExpandableAdminSidebarSectionComponent,
|
||||
FooterComponent,
|
||||
ThemedFooterComponent,
|
||||
PageNotFoundComponent,
|
||||
ThemedPageNotFoundComponent,
|
||||
NotificationComponent,
|
||||
NotificationsBoardComponent,
|
||||
BreadcrumbsComponent,
|
||||
ThemedBreadcrumbsComponent,
|
||||
ForbiddenComponent,
|
||||
ThemedForbiddenComponent,
|
||||
IdleModalComponent,
|
||||
ThemedPageInternalServerErrorComponent,
|
||||
PageInternalServerErrorComponent
|
||||
];
|
||||
|
||||
const EXPORTS = [
|
||||
|
@@ -92,11 +92,15 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
|
||||
});
|
||||
|
||||
case AuthActionTypes.AUTHENTICATED:
|
||||
return Object.assign({}, state, {
|
||||
loading: true,
|
||||
blocking: true
|
||||
});
|
||||
|
||||
case AuthActionTypes.CHECK_AUTHENTICATION_TOKEN:
|
||||
case AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE:
|
||||
return Object.assign({}, state, {
|
||||
loading: true,
|
||||
blocking: true
|
||||
});
|
||||
|
||||
case AuthActionTypes.AUTHENTICATED_ERROR:
|
||||
@@ -210,7 +214,6 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
|
||||
case AuthActionTypes.RETRIEVE_AUTH_METHODS:
|
||||
return Object.assign({}, state, {
|
||||
loading: true,
|
||||
blocking: true
|
||||
});
|
||||
|
||||
case AuthActionTypes.RETRIEVE_AUTH_METHODS_SUCCESS:
|
||||
|
@@ -75,7 +75,6 @@ import { RegistryService } from './registry/registry.service';
|
||||
import { RoleService } from './roles/role.service';
|
||||
import { FeedbackDataService } from './feedback/feedback-data.service';
|
||||
|
||||
import { ApiService } from './services/api.service';
|
||||
import { ServerResponseService } from './services/server-response.service';
|
||||
import { NativeWindowFactory, NativeWindowService } from './services/window.service';
|
||||
import { BitstreamFormat } from './shared/bitstream-format.model';
|
||||
@@ -186,7 +185,6 @@ const DECLARATIONS = [];
|
||||
const EXPORTS = [];
|
||||
|
||||
const PROVIDERS = [
|
||||
ApiService,
|
||||
AuthenticatedGuard,
|
||||
CommunityDataService,
|
||||
CollectionDataService,
|
||||
|
@@ -1,24 +0,0 @@
|
||||
import { throwError as observableThrowError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
@Injectable()
|
||||
export class ApiService {
|
||||
constructor(public _http: HttpClient) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* whatever domain/feature method name
|
||||
*/
|
||||
get(url: string, options?: any) {
|
||||
return this._http.get(url, options).pipe(
|
||||
catchError((err) => {
|
||||
console.log('Error: ', err);
|
||||
return observableThrowError(err);
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
16
src/app/menu.resolver.spec.ts
Normal file
16
src/app/menu.resolver.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MenuResolver } from './menu.resolver';
|
||||
|
||||
describe('MenuResolver', () => {
|
||||
let resolver: MenuResolver;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
resolver = TestBed.inject(MenuResolver);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(resolver).toBeTruthy();
|
||||
});
|
||||
});
|
89
src/app/menu.resolver.ts
Normal file
89
src/app/menu.resolver.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { MenuItemType, MenuID } from './shared/menu/initial-menus-state';
|
||||
import { LinkMenuItemModel } from './shared/menu/menu-item/models/link.model';
|
||||
import { getFirstCompletedRemoteData } from './core/shared/operators';
|
||||
import { PaginatedList } from './core/data/paginated-list.model';
|
||||
import { BrowseDefinition } from './core/shared/browse-definition.model';
|
||||
import { RemoteData } from './core/data/remote-data';
|
||||
import { TextMenuItemModel } from './shared/menu/menu-item/models/text.model';
|
||||
import { BrowseService } from './core/browse/browse.service';
|
||||
import { MenuService } from './shared/menu/menu.service';
|
||||
import { MenuState } from './shared/menu/menu.reducer';
|
||||
import { find, map } from 'rxjs/operators';
|
||||
import { hasValue } from './shared/empty.util';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MenuResolver implements Resolve<boolean> {
|
||||
constructor(
|
||||
protected menuService: MenuService,
|
||||
public browseService: BrowseService,
|
||||
) {
|
||||
}
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
this.createPublicMenu();
|
||||
return this.menuService.getMenu(MenuID.PUBLIC).pipe(
|
||||
find((menu: MenuState) => hasValue(menu)),
|
||||
map(() => true)
|
||||
);
|
||||
}
|
||||
|
||||
createPublicMenu() {
|
||||
const menuList: any[] = [
|
||||
/* Communities & Collections tree */
|
||||
{
|
||||
id: `browse_global_communities_and_collections`,
|
||||
active: false,
|
||||
visible: true,
|
||||
index: 0,
|
||||
model: {
|
||||
type: MenuItemType.LINK,
|
||||
text: `menu.section.browse_global_communities_and_collections`,
|
||||
link: `/community-list`
|
||||
} as LinkMenuItemModel
|
||||
}
|
||||
];
|
||||
// Read the different Browse-By types from config and add them to the browse menu
|
||||
this.browseService.getBrowseDefinitions()
|
||||
.pipe(getFirstCompletedRemoteData<PaginatedList<BrowseDefinition>>())
|
||||
.subscribe((browseDefListRD: RemoteData<PaginatedList<BrowseDefinition>>) => {
|
||||
if (browseDefListRD.hasSucceeded) {
|
||||
browseDefListRD.payload.page.forEach((browseDef: BrowseDefinition) => {
|
||||
menuList.push({
|
||||
id: `browse_global_by_${browseDef.id}`,
|
||||
parentID: 'browse_global',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: {
|
||||
type: MenuItemType.LINK,
|
||||
text: `menu.section.browse_global_by_${browseDef.id}`,
|
||||
link: `/browse/${browseDef.id}`
|
||||
} as LinkMenuItemModel
|
||||
});
|
||||
});
|
||||
menuList.push(
|
||||
/* Browse */
|
||||
{
|
||||
id: 'browse_global',
|
||||
active: false,
|
||||
visible: true,
|
||||
index: 1,
|
||||
model: {
|
||||
type: MenuItemType.TEXT,
|
||||
text: 'menu.section.browse_global'
|
||||
} as TextMenuItemModel,
|
||||
}
|
||||
);
|
||||
}
|
||||
menuList.forEach((menuSection) => this.menuService.addSection(MenuID.PUBLIC, Object.assign(menuSection, {
|
||||
shouldPersistOnRouteChange: true
|
||||
})));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -2,15 +2,9 @@ import { Component, Injector } from '@angular/core';
|
||||
import { slideMobileNav } from '../shared/animations/slide';
|
||||
import { MenuComponent } from '../shared/menu/menu.component';
|
||||
import { MenuService } from '../shared/menu/menu.service';
|
||||
import { MenuID, MenuItemType } from '../shared/menu/initial-menus-state';
|
||||
import { TextMenuItemModel } from '../shared/menu/menu-item/models/text.model';
|
||||
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
||||
import { MenuID } from '../shared/menu/initial-menus-state';
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { BrowseService } from '../core/browse/browse.service';
|
||||
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
||||
import { PaginatedList } from '../core/data/paginated-list.model';
|
||||
import { BrowseDefinition } from '../core/shared/browse-definition.model';
|
||||
import { RemoteData } from '../core/data/remote-data';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||
|
||||
@@ -41,64 +35,6 @@ export class NavbarComponent extends MenuComponent {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.createMenu();
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all menu sections and items for this menu
|
||||
*/
|
||||
createMenu() {
|
||||
const menuList: any[] = [
|
||||
/* Communities & Collections tree */
|
||||
{
|
||||
id: `browse_global_communities_and_collections`,
|
||||
active: false,
|
||||
visible: true,
|
||||
index: 0,
|
||||
model: {
|
||||
type: MenuItemType.LINK,
|
||||
text: `menu.section.browse_global_communities_and_collections`,
|
||||
link: `/community-list`
|
||||
} as LinkMenuItemModel
|
||||
}
|
||||
];
|
||||
// Read the different Browse-By types from config and add them to the browse menu
|
||||
this.browseService.getBrowseDefinitions()
|
||||
.pipe(getFirstCompletedRemoteData<PaginatedList<BrowseDefinition>>())
|
||||
.subscribe((browseDefListRD: RemoteData<PaginatedList<BrowseDefinition>>) => {
|
||||
if (browseDefListRD.hasSucceeded) {
|
||||
browseDefListRD.payload.page.forEach((browseDef: BrowseDefinition) => {
|
||||
menuList.push({
|
||||
id: `browse_global_by_${browseDef.id}`,
|
||||
parentID: 'browse_global',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: {
|
||||
type: MenuItemType.LINK,
|
||||
text: `menu.section.browse_global_by_${browseDef.id}`,
|
||||
link: `/browse/${browseDef.id}`
|
||||
} as LinkMenuItemModel
|
||||
});
|
||||
});
|
||||
menuList.push(
|
||||
/* Browse */
|
||||
{
|
||||
id: 'browse_global',
|
||||
active: false,
|
||||
visible: true,
|
||||
index: 1,
|
||||
model: {
|
||||
type: MenuItemType.TEXT,
|
||||
text: 'menu.section.browse_global'
|
||||
} as TextMenuItemModel,
|
||||
}
|
||||
);
|
||||
}
|
||||
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, {
|
||||
shouldPersistOnRouteChange: true
|
||||
})));
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
98
src/app/root.module.ts
Normal file
98
src/app/root.module.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
import {
|
||||
AdminSidebarSectionComponent
|
||||
} from './admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component';
|
||||
import { AdminSidebarComponent } from './admin/admin-sidebar/admin-sidebar.component';
|
||||
import {
|
||||
ExpandableAdminSidebarSectionComponent
|
||||
} from './admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component';
|
||||
import { FooterComponent } from './footer/footer.component';
|
||||
import { HeaderNavbarWrapperComponent } from './header-nav-wrapper/header-navbar-wrapper.component';
|
||||
import { HeaderComponent } from './header/header.component';
|
||||
import { NavbarModule } from './navbar/navbar.module';
|
||||
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
||||
import { NotificationComponent } from './shared/notifications/notification/notification.component';
|
||||
import {
|
||||
NotificationsBoardComponent
|
||||
} from './shared/notifications/notifications-board/notifications-board.component';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { BreadcrumbsComponent } from './breadcrumbs/breadcrumbs.component';
|
||||
import { ForbiddenComponent } from './forbidden/forbidden.component';
|
||||
import { RootComponent } from './root/root.component';
|
||||
import { ThemedRootComponent } from './root/themed-root.component';
|
||||
import { ThemedPageNotFoundComponent } from './pagenotfound/themed-pagenotfound.component';
|
||||
import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component';
|
||||
import { ThemedHeaderComponent } from './header/themed-header.component';
|
||||
import { ThemedFooterComponent } from './footer/themed-footer.component';
|
||||
import { ThemedBreadcrumbsComponent } from './breadcrumbs/themed-breadcrumbs.component';
|
||||
import {
|
||||
ThemedHeaderNavbarWrapperComponent
|
||||
} from './header-nav-wrapper/themed-header-navbar-wrapper.component';
|
||||
import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
|
||||
import {
|
||||
ThemedPageInternalServerErrorComponent
|
||||
} from './page-internal-server-error/themed-page-internal-server-error.component';
|
||||
import {
|
||||
PageInternalServerErrorComponent
|
||||
} from './page-internal-server-error/page-internal-server-error.component';
|
||||
|
||||
const IMPORTS = [
|
||||
CommonModule,
|
||||
SharedModule.withEntryComponents(),
|
||||
NavbarModule,
|
||||
NgbModule,
|
||||
];
|
||||
|
||||
const PROVIDERS = [
|
||||
];
|
||||
|
||||
const DECLARATIONS = [
|
||||
RootComponent,
|
||||
ThemedRootComponent,
|
||||
HeaderComponent,
|
||||
ThemedHeaderComponent,
|
||||
HeaderNavbarWrapperComponent,
|
||||
ThemedHeaderNavbarWrapperComponent,
|
||||
AdminSidebarComponent,
|
||||
AdminSidebarSectionComponent,
|
||||
ExpandableAdminSidebarSectionComponent,
|
||||
FooterComponent,
|
||||
ThemedFooterComponent,
|
||||
PageNotFoundComponent,
|
||||
ThemedPageNotFoundComponent,
|
||||
NotificationComponent,
|
||||
NotificationsBoardComponent,
|
||||
BreadcrumbsComponent,
|
||||
ThemedBreadcrumbsComponent,
|
||||
ForbiddenComponent,
|
||||
ThemedForbiddenComponent,
|
||||
IdleModalComponent,
|
||||
ThemedPageInternalServerErrorComponent,
|
||||
PageInternalServerErrorComponent
|
||||
];
|
||||
|
||||
const EXPORTS = [
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
...IMPORTS
|
||||
],
|
||||
providers: [
|
||||
...PROVIDERS
|
||||
],
|
||||
declarations: [
|
||||
...DECLARATIONS,
|
||||
],
|
||||
exports: [
|
||||
...EXPORTS,
|
||||
...DECLARATIONS,
|
||||
]
|
||||
})
|
||||
export class RootModule {
|
||||
|
||||
}
|
@@ -37,7 +37,7 @@ const menuByIDSelector = (menuID: MenuID): MemoizedSelector<AppState, MenuState>
|
||||
return keySelector<MenuState>(menuID, menusStateSelector);
|
||||
};
|
||||
|
||||
const menuSectionStateSelector = (state: MenuState) => state.sections;
|
||||
const menuSectionStateSelector = (state: MenuState) => hasValue(state) ? state.sections : {};
|
||||
|
||||
const menuSectionByIDSelector = (id: string): MemoizedSelector<MenuState, MenuSection> => {
|
||||
return menuKeySelector<MenuSection>(id, menuSectionStateSelector);
|
||||
@@ -164,7 +164,7 @@ export class MenuService {
|
||||
*/
|
||||
isMenuCollapsed(menuID: MenuID): Observable<boolean> {
|
||||
return this.getMenu(menuID).pipe(
|
||||
map((state: MenuState) => state.collapsed)
|
||||
map((state: MenuState) => hasValue(state) ? state.collapsed : undefined)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ export class MenuService {
|
||||
*/
|
||||
isMenuPreviewCollapsed(menuID: MenuID): Observable<boolean> {
|
||||
return this.getMenu(menuID).pipe(
|
||||
map((state: MenuState) => state.previewCollapsed)
|
||||
map((state: MenuState) => hasValue(state) ? state.previewCollapsed : undefined)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ export class MenuService {
|
||||
*/
|
||||
isMenuVisible(menuID: MenuID): Observable<boolean> {
|
||||
return this.getMenu(menuID).pipe(
|
||||
map((state: MenuState) => state.visible)
|
||||
map((state: MenuState) => hasValue(state) ? state.visible : undefined)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -2,11 +2,10 @@ import { HttpClient, HttpClientModule } from '@angular/common/http';
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { BrowserModule, makeStateKey, TransferState } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RouterModule, NoPreloading } from '@angular/router';
|
||||
import { REQUEST } from '@nguniversal/express-engine/tokens';
|
||||
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { TranslateJson5HttpLoader } from '../../ngx-translate-loaders/translate-json5-http.loader';
|
||||
import { TranslateBrowserLoader } from '../../ngx-translate-loaders/translate-browser.loader';
|
||||
|
||||
import { IdlePreloadModule } from 'angular-idle-preload';
|
||||
|
||||
@@ -42,8 +41,8 @@ import { environment } from '../../environments/environment';
|
||||
|
||||
export const REQ_KEY = makeStateKey<string>('req');
|
||||
|
||||
export function createTranslateLoader(http: HttpClient) {
|
||||
return new TranslateJson5HttpLoader(http, 'assets/i18n/', '.json5');
|
||||
export function createTranslateLoader(transferState: TransferState, http: HttpClient) {
|
||||
return new TranslateBrowserLoader(transferState, http, 'assets/i18n/', '.json5');
|
||||
}
|
||||
|
||||
export function getRequest(transferState: TransferState): any {
|
||||
@@ -59,13 +58,6 @@ export function getRequest(transferState: TransferState): any {
|
||||
HttpClientModule,
|
||||
// forRoot ensures the providers are only created once
|
||||
IdlePreloadModule.forRoot(),
|
||||
RouterModule.forRoot([], {
|
||||
// enableTracing: true,
|
||||
useHash: false,
|
||||
scrollPositionRestoration: 'enabled',
|
||||
anchorScrolling: 'enabled',
|
||||
preloadingStrategy: NoPreloading
|
||||
}),
|
||||
StatisticsModule.forRoot(),
|
||||
Angulartics2RouterlessModule.forRoot(),
|
||||
BrowserAnimationsModule,
|
||||
@@ -74,7 +66,7 @@ export function getRequest(transferState: TransferState): any {
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: (createTranslateLoader),
|
||||
deps: [HttpClient]
|
||||
deps: [TransferState, HttpClient]
|
||||
}
|
||||
}),
|
||||
AppModule
|
||||
@@ -92,9 +84,11 @@ export function getRequest(transferState: TransferState): any {
|
||||
// extend environment with app config for browser
|
||||
extendEnvironmentWithAppConfig(environment, appConfig);
|
||||
}
|
||||
dspaceTransferState.transfer();
|
||||
return () =>
|
||||
dspaceTransferState.transfer().then((b: boolean) => {
|
||||
correlationIdService.initCorrelationId();
|
||||
return () => true;
|
||||
return b;
|
||||
});
|
||||
},
|
||||
deps: [TransferState, DSpaceTransferState, CorrelationIdService],
|
||||
multi: true
|
||||
|
@@ -15,7 +15,7 @@ import { AppComponent } from '../../app/app.component';
|
||||
import { AppModule } from '../../app/app.module';
|
||||
import { DSpaceServerTransferStateModule } from '../transfer-state/dspace-server-transfer-state.module';
|
||||
import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service';
|
||||
import { TranslateJson5UniversalLoader } from '../../ngx-translate-loaders/translate-json5-universal.loader';
|
||||
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';
|
||||
import { AuthService } from '../../app/core/auth/auth.service';
|
||||
@@ -37,8 +37,8 @@ import { AppConfig, APP_CONFIG_STATE } from '../../config/app-config.interface';
|
||||
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
export function createTranslateLoader() {
|
||||
return new TranslateJson5UniversalLoader('dist/server/assets/i18n/', '.json5');
|
||||
export function createTranslateLoader(transferState: TransferState) {
|
||||
return new TranslateServerLoader(transferState, 'dist/server/assets/i18n/', '.json5');
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@@ -47,16 +47,13 @@ export function createTranslateLoader() {
|
||||
BrowserModule.withServerTransition({
|
||||
appId: 'dspace-angular'
|
||||
}),
|
||||
RouterModule.forRoot([], {
|
||||
useHash: false
|
||||
}),
|
||||
NoopAnimationsModule,
|
||||
DSpaceServerTransferStateModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: (createTranslateLoader),
|
||||
deps: []
|
||||
deps: [TransferState]
|
||||
}
|
||||
}),
|
||||
ServerModule,
|
||||
|
@@ -1,12 +1,19 @@
|
||||
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() {
|
||||
async transfer(): Promise<boolean> {
|
||||
const state = this.transferState.get<any>(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();
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import { DSpaceTransferState } from './dspace-transfer-state.service';
|
||||
|
||||
@Injectable()
|
||||
export class DSpaceServerTransferState extends DSpaceTransferState {
|
||||
transfer() {
|
||||
transfer(): Promise<boolean> {
|
||||
this.transferState.onSerialize(DSpaceTransferState.NGRX_STATE, () => {
|
||||
let state;
|
||||
this.store.pipe(take(1)).subscribe((saveState: any) => {
|
||||
@@ -14,5 +14,7 @@ export class DSpaceServerTransferState extends DSpaceTransferState {
|
||||
|
||||
return state;
|
||||
});
|
||||
|
||||
return new Promise<boolean>(() => true);
|
||||
}
|
||||
}
|
||||
|
@@ -14,5 +14,5 @@ export abstract class DSpaceTransferState {
|
||||
) {
|
||||
}
|
||||
|
||||
abstract transfer(): void;
|
||||
abstract transfer(): Promise<boolean>;
|
||||
}
|
||||
|
15
src/ngx-translate-loaders/ngx-translate-state.ts
Normal file
15
src/ngx-translate-loaders/ngx-translate-state.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { makeStateKey } from '@angular/platform-browser';
|
||||
|
||||
/**
|
||||
* Represents ngx-translate messages in different languages in the TransferState
|
||||
*/
|
||||
export class NgxTranslateState {
|
||||
[lang: string]: {
|
||||
[key: string]: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The key to store the NgxTranslateState as part of the TransferState
|
||||
*/
|
||||
export const NGX_TRANSLATE_STATE = makeStateKey<NgxTranslateState>('NGX_TRANSLATE_STATE');
|
44
src/ngx-translate-loaders/translate-browser.loader.ts
Normal file
44
src/ngx-translate-loaders/translate-browser.loader.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { TranslateLoader } from '@ngx-translate/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { TransferState } from '@angular/platform-browser';
|
||||
import { NGX_TRANSLATE_STATE, NgxTranslateState } from './ngx-translate-state';
|
||||
import { hasValue } from '../app/shared/empty.util';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { of as observableOf, Observable } from 'rxjs';
|
||||
import * as JSON5 from 'json5';
|
||||
|
||||
/**
|
||||
* A TranslateLoader for ngx-translate to retrieve i18n messages from the TransferState, or download
|
||||
* them if they're not available there
|
||||
*/
|
||||
export class TranslateBrowserLoader implements TranslateLoader {
|
||||
constructor(
|
||||
protected transferState: TransferState,
|
||||
protected http: HttpClient,
|
||||
protected prefix?: string,
|
||||
protected suffix?: string
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the i18n messages for a given language, first try to find them in the TransferState
|
||||
* retrieve them using HttpClient if they're not available there
|
||||
*
|
||||
* @param lang the language code
|
||||
*/
|
||||
getTranslation(lang: string): Observable<any> {
|
||||
// Get the ngx-translate messages from the transfer state, to speed up the initial page load
|
||||
// client side
|
||||
const state = this.transferState.get<NgxTranslateState>(NGX_TRANSLATE_STATE, {});
|
||||
const messages = state[lang];
|
||||
if (hasValue(messages)) {
|
||||
return observableOf(messages);
|
||||
} else {
|
||||
// If they're not available on the transfer state (e.g. when running in dev mode), retrieve
|
||||
// them using HttpClient
|
||||
return this.http.get('' + this.prefix + lang + this.suffix, { responseType: 'text' }).pipe(
|
||||
map((json: any) => JSON5.parse(json))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { TranslateLoader } from '@ngx-translate/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import * as JSON5 from 'json5';
|
||||
|
||||
export class TranslateJson5HttpLoader implements TranslateLoader {
|
||||
constructor(private http: HttpClient, public prefix?: string, public suffix?: string) {
|
||||
}
|
||||
|
||||
getTranslation(lang: string): any {
|
||||
return this.http.get('' + this.prefix + lang + this.suffix, {responseType: 'text'}).pipe(
|
||||
map((json: any) => JSON5.parse(json))
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
import { TranslateLoader } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import * as JSON5 from 'json5'
|
||||
import * as fs from 'fs';
|
||||
|
||||
export class TranslateJson5UniversalLoader implements TranslateLoader {
|
||||
|
||||
constructor(private prefix: string = 'dist/assets/i18n/', private suffix: string = '.json') { }
|
||||
|
||||
public getTranslation(lang: string): Observable<any> {
|
||||
return Observable.create((observer: any) => {
|
||||
observer.next(JSON5.parse(fs.readFileSync(`${this.prefix}${lang}${this.suffix}`, 'utf8')));
|
||||
observer.complete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
52
src/ngx-translate-loaders/translate-server.loader.ts
Normal file
52
src/ngx-translate-loaders/translate-server.loader.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { TranslateLoader } from '@ngx-translate/core';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import * as fs from 'fs';
|
||||
import { TransferState } from '@angular/platform-browser';
|
||||
import { NGX_TRANSLATE_STATE, NgxTranslateState } from './ngx-translate-state';
|
||||
|
||||
import * as JSON5 from 'json5';
|
||||
|
||||
/**
|
||||
* A TranslateLoader for ngx-translate to parse json5 files server-side, and store them in the
|
||||
* TransferState
|
||||
*/
|
||||
export class TranslateServerLoader implements TranslateLoader {
|
||||
|
||||
constructor(
|
||||
protected transferState: TransferState,
|
||||
protected prefix: string = 'dist/assets/i18n/',
|
||||
protected suffix: string = '.json'
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the i18n messages for a given language, and store them in the TransferState
|
||||
*
|
||||
* @param lang the language code
|
||||
*/
|
||||
public getTranslation(lang: string): Observable<any> {
|
||||
// Retrieve the file for the given language, and parse it
|
||||
const messages = JSON5.parse(fs.readFileSync(`${this.prefix}${lang}${this.suffix}`, 'utf8'));
|
||||
// Store the parsed messages in the transfer state so they'll be available immediately when the
|
||||
// app loads on the client
|
||||
this.storeInTransferState(lang, messages);
|
||||
// Return the parsed messages to translate things server side
|
||||
return observableOf(messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the i18n messages for the given language code in the transfer state, so they can be
|
||||
* retrieved client side
|
||||
*
|
||||
* @param lang the language code
|
||||
* @param messages the i18n messages
|
||||
* @protected
|
||||
*/
|
||||
protected storeInTransferState(lang: string, messages) {
|
||||
const prevState = this.transferState.get<NgxTranslateState>(NGX_TRANSLATE_STATE, {});
|
||||
const nextState = Object.assign({}, prevState, {
|
||||
[lang]: messages
|
||||
});
|
||||
this.transferState.set(NGX_TRANSLATE_STATE, nextState);
|
||||
}
|
||||
}
|
@@ -84,6 +84,7 @@ import { SearchModule } from '../../app/shared/search/search.module';
|
||||
import { ResourcePoliciesModule } from '../../app/shared/resource-policies/resource-policies.module';
|
||||
import { ComcolModule } from '../../app/shared/comcol/comcol.module';
|
||||
import { FeedbackComponent } from './app/info/feedback/feedback.component';
|
||||
import { RootModule } from '../../app/root.module';
|
||||
|
||||
const DECLARATIONS = [
|
||||
FileSectionComponent,
|
||||
@@ -136,6 +137,7 @@ const DECLARATIONS = [
|
||||
AdminSearchModule,
|
||||
AdminWorkflowModuleModule,
|
||||
AppModule,
|
||||
RootModule,
|
||||
BitstreamFormatsModule,
|
||||
BrowseByModule,
|
||||
CollectionFormModule,
|
||||
|
53
src/themes/dspace/eager-theme.module.ts
Normal file
53
src/themes/dspace/eager-theme.module.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { SharedModule } from '../../app/shared/shared.module';
|
||||
import { HomeNewsComponent } from './app/home-page/home-news/home-news.component';
|
||||
import { NavbarComponent } from './app/navbar/navbar.component';
|
||||
import { HeaderComponent } from './app/header/header.component';
|
||||
import {
|
||||
HeaderNavbarWrapperComponent
|
||||
} from './app/header-nav-wrapper/header-navbar-wrapper.component';
|
||||
import { SearchModule } from '../../app/shared/search/search.module';
|
||||
import { RootModule } from '../../app/root.module';
|
||||
import { NavbarModule } from '../../app/navbar/navbar.module';
|
||||
|
||||
/**
|
||||
* Add components that use a custom decorator to ENTRY_COM&PONENTS as well as DECLARATIONS. This will
|
||||
* ensure that decorator gets picked up when the app loads
|
||||
*/
|
||||
const ENTRY_COMPONENTS = [
|
||||
];
|
||||
|
||||
const DECLARATIONS = [
|
||||
HomeNewsComponent,
|
||||
HeaderComponent,
|
||||
HeaderNavbarWrapperComponent,
|
||||
NavbarComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
SearchModule,
|
||||
FormsModule,
|
||||
RootModule,
|
||||
NavbarModule,
|
||||
],
|
||||
declarations: DECLARATIONS,
|
||||
providers: [
|
||||
...ENTRY_COMPONENTS.map((component) => ({ provide: component }))
|
||||
]
|
||||
})
|
||||
/**
|
||||
* This module is included in the main bundle that gets downloaded at first page load. So it should
|
||||
* contain only the themed components that have to be available immediately for the first page load,
|
||||
* and the minimal set of imports required to make them work. Anything you can cut from it will make
|
||||
* the initial page load faster, but may cause the page to flicker as components that were already
|
||||
* rendered server side need to be lazy-loaded again client side
|
||||
*
|
||||
* Themed EntryComponents should also be added here
|
||||
*/
|
||||
export class EagerThemeModule {
|
||||
}
|
@@ -1,2 +0,0 @@
|
||||
export const ENTRY_COMPONENTS = [
|
||||
];
|
@@ -2,10 +2,16 @@ import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AdminRegistriesModule } from '../../app/admin/admin-registries/admin-registries.module';
|
||||
import { AdminSearchModule } from '../../app/admin/admin-search-page/admin-search.module';
|
||||
import { AdminWorkflowModuleModule } from '../../app/admin/admin-workflow-page/admin-workflow.module';
|
||||
import { BitstreamFormatsModule } from '../../app/admin/admin-registries/bitstream-formats/bitstream-formats.module';
|
||||
import {
|
||||
AdminWorkflowModuleModule
|
||||
} from '../../app/admin/admin-workflow-page/admin-workflow.module';
|
||||
import {
|
||||
BitstreamFormatsModule
|
||||
} from '../../app/admin/admin-registries/bitstream-formats/bitstream-formats.module';
|
||||
import { BrowseByModule } from '../../app/browse-by/browse-by.module';
|
||||
import { CollectionFormModule } from '../../app/collection-page/collection-form/collection-form.module';
|
||||
import {
|
||||
CollectionFormModule
|
||||
} from '../../app/collection-page/collection-form/collection-form.module';
|
||||
import { CommunityFormModule } from '../../app/community-page/community-form/community-form.module';
|
||||
import { CoreModule } from '../../app/core/core.module';
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
@@ -13,14 +19,18 @@ import { EditItemPageModule } from '../../app/item-page/edit-item-page/edit-item
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { IdlePreloadModule } from 'angular-idle-preload';
|
||||
import { JournalEntitiesModule } from '../../app/entity-groups/journal-entities/journal-entities.module';
|
||||
import {
|
||||
JournalEntitiesModule
|
||||
} from '../../app/entity-groups/journal-entities/journal-entities.module';
|
||||
import { MyDspaceSearchModule } from '../../app/my-dspace-page/my-dspace-search.module';
|
||||
import { MenuModule } from '../../app/shared/menu/menu.module';
|
||||
import { NavbarModule } from '../../app/navbar/navbar.module';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ProfilePageModule } from '../../app/profile-page/profile-page.module';
|
||||
import { RegisterEmailFormModule } from '../../app/register-email-form/register-email-form.module';
|
||||
import { ResearchEntitiesModule } from '../../app/entity-groups/research-entities/research-entities.module';
|
||||
import {
|
||||
ResearchEntitiesModule
|
||||
} from '../../app/entity-groups/research-entities/research-entities.module';
|
||||
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
|
||||
import { SearchPageModule } from '../../app/search-page/search-page.module';
|
||||
import { SharedModule } from '../../app/shared/shared.module';
|
||||
@@ -28,7 +38,6 @@ import { StatisticsModule } from '../../app/statistics/statistics.module';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HomeNewsComponent } from './app/home-page/home-news/home-news.component';
|
||||
import { HomePageModule } from '../../app/home-page/home-page.module';
|
||||
import { AppModule } from '../../app/app.module';
|
||||
import { ItemPageModule } from '../../app/item-page/item-page.module';
|
||||
@@ -40,18 +49,14 @@ import { CommunityPageModule } from '../../app/community-page/community-page.mod
|
||||
import { CollectionPageModule } from '../../app/collection-page/collection-page.module';
|
||||
import { SubmissionModule } from '../../app/submission/submission.module';
|
||||
import { MyDSpacePageModule } from '../../app/my-dspace-page/my-dspace-page.module';
|
||||
import { NavbarComponent } from './app/navbar/navbar.component';
|
||||
import { HeaderComponent } from './app/header/header.component';
|
||||
import { HeaderNavbarWrapperComponent } from './app/header-nav-wrapper/header-navbar-wrapper.component';
|
||||
import { SearchModule } from '../../app/shared/search/search.module';
|
||||
import { ResourcePoliciesModule } from '../../app/shared/resource-policies/resource-policies.module';
|
||||
import {
|
||||
ResourcePoliciesModule
|
||||
} from '../../app/shared/resource-policies/resource-policies.module';
|
||||
import { ComcolModule } from '../../app/shared/comcol/comcol.module';
|
||||
import { RootModule } from '../../app/root.module';
|
||||
|
||||
const DECLARATIONS = [
|
||||
HomeNewsComponent,
|
||||
HeaderComponent,
|
||||
HeaderNavbarWrapperComponent,
|
||||
NavbarComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
@@ -60,6 +65,7 @@ const DECLARATIONS = [
|
||||
AdminSearchModule,
|
||||
AdminWorkflowModuleModule,
|
||||
AppModule,
|
||||
RootModule,
|
||||
BitstreamFormatsModule,
|
||||
BrowseByModule,
|
||||
CollectionFormModule,
|
||||
@@ -112,5 +118,5 @@ const DECLARATIONS = [
|
||||
* It is purposefully not exported, it should never be imported anywhere else, its only purpose is
|
||||
* to give lazily loaded components a context in which they can be compiled successfully
|
||||
*/
|
||||
class ThemeModule {
|
||||
class LazyThemeModule {
|
||||
}
|
15
src/themes/eager-themes.module.ts
Normal file
15
src/themes/eager-themes.module.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { EagerThemeModule as DSpaceEagerThemeModule } from './dspace/eager-theme.module';
|
||||
|
||||
/**
|
||||
* This module only serves to ensure themed entry components are discoverable. It's kept separate
|
||||
* from the theme modules to ensure only the minimal number of theme components is loaded ahead of
|
||||
* time
|
||||
*/
|
||||
@NgModule({
|
||||
imports: [
|
||||
DSpaceEagerThemeModule
|
||||
],
|
||||
})
|
||||
export class EagerThemesModule {
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ENTRY_COMPONENTS as CUSTOM } from './custom/entry-components';
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
...CUSTOM,
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* This module only serves to ensure themed entry components are discoverable. It's kept separate
|
||||
* from the theme modules to ensure only the minimal number of theme components is loaded ahead of
|
||||
* time
|
||||
*/
|
||||
@NgModule()
|
||||
export class ThemedEntryComponentModule {
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
ngModule: ThemedEntryComponentModule,
|
||||
providers: ENTRY_COMPONENTS.map((component) => ({provide: component}))
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@@ -3,11 +3,36 @@ import { join } from 'path';
|
||||
import { buildAppConfig } from '../src/config/config.server';
|
||||
import { commonExports } from './webpack.common';
|
||||
|
||||
const CompressionPlugin = require('compression-webpack-plugin');
|
||||
const zlib = require('zlib');
|
||||
|
||||
module.exports = Object.assign({}, commonExports, {
|
||||
target: 'web',
|
||||
node: {
|
||||
module: 'empty'
|
||||
},
|
||||
plugins: [
|
||||
...commonExports.plugins,
|
||||
new CompressionPlugin({
|
||||
filename: '[path][base].gz',
|
||||
algorithm: 'gzip',
|
||||
test: /\.(js|css|html|svg)$/,
|
||||
threshold: 10240,
|
||||
minRatio: 0.8,
|
||||
}),
|
||||
new CompressionPlugin({
|
||||
filename: '[path][base].br',
|
||||
algorithm: 'brotliCompress',
|
||||
test: /\.(js|css|html|svg)$/,
|
||||
compressionOptions: {
|
||||
params: {
|
||||
[zlib.constants.BROTLI_PARAM_QUALITY]: 11,
|
||||
},
|
||||
},
|
||||
threshold: 10240,
|
||||
minRatio: 0.8,
|
||||
}),
|
||||
],
|
||||
devServer: {
|
||||
before(app, server) {
|
||||
buildAppConfig(join(process.cwd(), 'src/assets/config.json'));
|
||||
|
30
yarn.lock
30
yarn.lock
@@ -4112,17 +4112,16 @@ compressible@~2.0.16:
|
||||
dependencies:
|
||||
mime-db ">= 1.43.0 < 2"
|
||||
|
||||
compression-webpack-plugin@^3.0.1:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-3.1.0.tgz#9f510172a7b5fae5aad3b670652e8bd7997aeeca"
|
||||
integrity sha512-iqTHj3rADN4yHwXMBrQa/xrncex/uEQy8QHlaTKxGchT/hC0SdlJlmL/5eRqffmWq2ep0/Romw6Ld39JjTR/ug==
|
||||
compression-webpack-plugin@^6.1.1:
|
||||
version "6.1.1"
|
||||
resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-6.1.1.tgz#ae8e4b2ffdb7396bb776e66918d751a20d8ccf0e"
|
||||
integrity sha512-BEHft9M6lwOqVIQFMS/YJGmeCYXVOakC5KzQk05TFpMBlODByh1qNsZCWjUBxCQhUP9x0WfGidxTbGkjbWO/TQ==
|
||||
dependencies:
|
||||
cacache "^13.0.1"
|
||||
find-cache-dir "^3.0.0"
|
||||
neo-async "^2.5.0"
|
||||
schema-utils "^2.6.1"
|
||||
serialize-javascript "^2.1.2"
|
||||
webpack-sources "^1.0.1"
|
||||
cacache "^15.0.5"
|
||||
find-cache-dir "^3.3.1"
|
||||
schema-utils "^3.0.0"
|
||||
serialize-javascript "^5.0.1"
|
||||
webpack-sources "^1.4.3"
|
||||
|
||||
compression@^1.7.4:
|
||||
version "1.7.4"
|
||||
@@ -6237,7 +6236,7 @@ find-cache-dir@^2.1.0:
|
||||
make-dir "^2.0.0"
|
||||
pkg-dir "^3.0.0"
|
||||
|
||||
find-cache-dir@^3.0.0, find-cache-dir@^3.3.1:
|
||||
find-cache-dir@^3.3.1:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b"
|
||||
integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==
|
||||
@@ -12521,7 +12520,7 @@ schema-utils@^1.0.0:
|
||||
ajv-errors "^1.0.0"
|
||||
ajv-keywords "^3.1.0"
|
||||
|
||||
schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0:
|
||||
schema-utils@^2.6.0, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
|
||||
integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==
|
||||
@@ -12671,11 +12670,6 @@ send@0.17.1:
|
||||
range-parser "~1.2.1"
|
||||
statuses "~1.5.0"
|
||||
|
||||
serialize-javascript@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
|
||||
integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==
|
||||
|
||||
serialize-javascript@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
|
||||
@@ -14712,7 +14706,7 @@ webpack-merge@^5.7.3:
|
||||
clone-deep "^4.0.1"
|
||||
wildcard "^2.0.0"
|
||||
|
||||
webpack-sources@1.4.3, webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
|
||||
webpack-sources@1.4.3, webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
|
||||
integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
|
||||
|
Reference in New Issue
Block a user