mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +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
|
.env
|
||||||
/nbproject/
|
/nbproject/
|
||||||
|
|
||||||
|
junit.xml
|
||||||
|
@@ -142,7 +142,7 @@
|
|||||||
"@types/node": "^14.14.9",
|
"@types/node": "^14.14.9",
|
||||||
"axe-core": "^4.3.3",
|
"axe-core": "^4.3.3",
|
||||||
"codelyzer": "^6.0.0",
|
"codelyzer": "^6.0.0",
|
||||||
"compression-webpack-plugin": "^3.0.1",
|
"compression-webpack-plugin": "^6.1.1",
|
||||||
"copy-webpack-plugin": "^6.4.1",
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "3.4.0",
|
"css-loader": "3.4.0",
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { NgModule } from '@angular/core';
|
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 { AuthBlockingGuard } from './core/auth/auth-blocking.guard';
|
||||||
|
|
||||||
import { AuthenticatedGuard } from './core/auth/authenticated.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 { 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 { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component';
|
||||||
import { ServerCheckGuard } from './core/server-check/server-check.guard';
|
import { ServerCheckGuard } from './core/server-check/server-check.guard';
|
||||||
|
import { MenuResolver } from './menu.resolver';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -39,6 +40,7 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard';
|
|||||||
path: '',
|
path: '',
|
||||||
canActivate: [AuthBlockingGuard],
|
canActivate: [AuthBlockingGuard],
|
||||||
canActivateChild: [ServerCheckGuard],
|
canActivateChild: [ServerCheckGuard],
|
||||||
|
resolve: [MenuResolver],
|
||||||
children: [
|
children: [
|
||||||
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
{ 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',
|
onSameUrlNavigation: 'reload',
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
@@ -72,7 +72,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
/**
|
/**
|
||||||
* Whether or not the app is in the process of rerouting
|
* 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
|
* 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) => {
|
this.themeService.getThemeName$().subscribe((themeName: string) => {
|
||||||
if (isPlatformBrowser(this.platformId)) {
|
if (isPlatformBrowser(this.platformId)) {
|
||||||
// the theme css will never download server side, so this should only happen on the browser
|
// 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)) {
|
if (hasValue(themeName)) {
|
||||||
this.loadGlobalThemeConfig(themeName);
|
this.loadGlobalThemeConfig(themeName);
|
||||||
@@ -200,8 +200,8 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
this.router.events.subscribe((event) => {
|
this.router.events.subscribe((event) => {
|
||||||
if (event instanceof NavigationStart) {
|
if (event instanceof NavigationStart) {
|
||||||
resolveEndFound = false;
|
resolveEndFound = false;
|
||||||
this.isRouteLoading$.next(true);
|
this.distinctNext(this.isRouteLoading$, true);
|
||||||
this.isThemeLoading$.next(true);
|
this.distinctNext(this.isThemeLoading$, true);
|
||||||
} else if (event instanceof ResolveEnd) {
|
} else if (event instanceof ResolveEnd) {
|
||||||
resolveEndFound = true;
|
resolveEndFound = true;
|
||||||
const activatedRouteSnapShot: ActivatedRouteSnapshot = event.state.root;
|
const activatedRouteSnapShot: ActivatedRouteSnapshot = event.state.root;
|
||||||
@@ -214,16 +214,16 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
).subscribe((changed) => {
|
).subscribe((changed) => {
|
||||||
this.isThemeLoading$.next(changed);
|
this.distinctNext(this.isThemeLoading$, changed);
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
event instanceof NavigationEnd ||
|
event instanceof NavigationEnd ||
|
||||||
event instanceof NavigationCancel
|
event instanceof NavigationCancel
|
||||||
) {
|
) {
|
||||||
if (!resolveEndFound) {
|
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.
|
// 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);
|
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';
|
} from '@ng-dynamic-forms/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
|
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 { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { appEffects } from './app.effects';
|
import { appEffects } from './app.effects';
|
||||||
@@ -28,36 +24,18 @@ import { appReducers, AppState, storeModuleConfig } from './app.reducer';
|
|||||||
import { CheckAuthenticationTokenAction } from './core/auth/auth.actions';
|
import { CheckAuthenticationTokenAction } from './core/auth/auth.actions';
|
||||||
import { CoreModule } from './core/core.module';
|
import { CoreModule } from './core/core.module';
|
||||||
import { ClientCookieService } from './core/services/client-cookie.service';
|
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 { NavbarModule } from './navbar/navbar.module';
|
||||||
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
|
||||||
import { DSpaceRouterStateSerializer } from './shared/ngrx/dspace-router-state-serializer';
|
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 { SharedModule } from './shared/shared.module';
|
||||||
import { BreadcrumbsComponent } from './breadcrumbs/breadcrumbs.component';
|
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { ForbiddenComponent } from './forbidden/forbidden.component';
|
|
||||||
import { AuthInterceptor } from './core/auth/auth.interceptor';
|
import { AuthInterceptor } from './core/auth/auth.interceptor';
|
||||||
import { LocaleInterceptor } from './core/locale/locale.interceptor';
|
import { LocaleInterceptor } from './core/locale/locale.interceptor';
|
||||||
import { XsrfInterceptor } from './core/xsrf/xsrf.interceptor';
|
import { XsrfInterceptor } from './core/xsrf/xsrf.interceptor';
|
||||||
import { LogInterceptor } from './core/log/log.interceptor';
|
import { LogInterceptor } from './core/log/log.interceptor';
|
||||||
import { RootComponent } from './root/root.component';
|
import { EagerThemesModule } from '../themes/eager-themes.module';
|
||||||
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 { APP_CONFIG, AppConfig } from '../config/app-config.interface';
|
import { APP_CONFIG, AppConfig } from '../config/app-config.interface';
|
||||||
|
import { RootModule } from './root.module';
|
||||||
|
|
||||||
export function getConfig() {
|
export function getConfig() {
|
||||||
return environment;
|
return environment;
|
||||||
@@ -92,7 +70,8 @@ const IMPORTS = [
|
|||||||
EffectsModule.forRoot(appEffects),
|
EffectsModule.forRoot(appEffects),
|
||||||
StoreModule.forRoot(appReducers, storeModuleConfig),
|
StoreModule.forRoot(appReducers, storeModuleConfig),
|
||||||
StoreRouterConnectingModule.forRoot(),
|
StoreRouterConnectingModule.forRoot(),
|
||||||
ThemedEntryComponentModule.withEntryComponents(),
|
EagerThemesModule,
|
||||||
|
RootModule,
|
||||||
];
|
];
|
||||||
|
|
||||||
IMPORTS.push(
|
IMPORTS.push(
|
||||||
@@ -164,28 +143,6 @@ const PROVIDERS = [
|
|||||||
|
|
||||||
const DECLARATIONS = [
|
const DECLARATIONS = [
|
||||||
AppComponent,
|
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 = [
|
const EXPORTS = [
|
||||||
|
@@ -92,11 +92,15 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
|
|||||||
});
|
});
|
||||||
|
|
||||||
case AuthActionTypes.AUTHENTICATED:
|
case AuthActionTypes.AUTHENTICATED:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
loading: true,
|
||||||
|
blocking: true
|
||||||
|
});
|
||||||
|
|
||||||
case AuthActionTypes.CHECK_AUTHENTICATION_TOKEN:
|
case AuthActionTypes.CHECK_AUTHENTICATION_TOKEN:
|
||||||
case AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE:
|
case AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE:
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
loading: true,
|
loading: true,
|
||||||
blocking: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
case AuthActionTypes.AUTHENTICATED_ERROR:
|
case AuthActionTypes.AUTHENTICATED_ERROR:
|
||||||
@@ -210,7 +214,6 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
|
|||||||
case AuthActionTypes.RETRIEVE_AUTH_METHODS:
|
case AuthActionTypes.RETRIEVE_AUTH_METHODS:
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
loading: true,
|
loading: true,
|
||||||
blocking: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
case AuthActionTypes.RETRIEVE_AUTH_METHODS_SUCCESS:
|
case AuthActionTypes.RETRIEVE_AUTH_METHODS_SUCCESS:
|
||||||
|
@@ -75,7 +75,6 @@ import { RegistryService } from './registry/registry.service';
|
|||||||
import { RoleService } from './roles/role.service';
|
import { RoleService } from './roles/role.service';
|
||||||
import { FeedbackDataService } from './feedback/feedback-data.service';
|
import { FeedbackDataService } from './feedback/feedback-data.service';
|
||||||
|
|
||||||
import { ApiService } from './services/api.service';
|
|
||||||
import { ServerResponseService } from './services/server-response.service';
|
import { ServerResponseService } from './services/server-response.service';
|
||||||
import { NativeWindowFactory, NativeWindowService } from './services/window.service';
|
import { NativeWindowFactory, NativeWindowService } from './services/window.service';
|
||||||
import { BitstreamFormat } from './shared/bitstream-format.model';
|
import { BitstreamFormat } from './shared/bitstream-format.model';
|
||||||
@@ -186,7 +185,6 @@ const DECLARATIONS = [];
|
|||||||
const EXPORTS = [];
|
const EXPORTS = [];
|
||||||
|
|
||||||
const PROVIDERS = [
|
const PROVIDERS = [
|
||||||
ApiService,
|
|
||||||
AuthenticatedGuard,
|
AuthenticatedGuard,
|
||||||
CommunityDataService,
|
CommunityDataService,
|
||||||
CollectionDataService,
|
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 { slideMobileNav } from '../shared/animations/slide';
|
||||||
import { MenuComponent } from '../shared/menu/menu.component';
|
import { MenuComponent } from '../shared/menu/menu.component';
|
||||||
import { MenuService } from '../shared/menu/menu.service';
|
import { MenuService } from '../shared/menu/menu.service';
|
||||||
import { MenuID, MenuItemType } from '../shared/menu/initial-menus-state';
|
import { MenuID } 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 { HostWindowService } from '../shared/host-window.service';
|
import { HostWindowService } from '../shared/host-window.service';
|
||||||
import { BrowseService } from '../core/browse/browse.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 { ActivatedRoute } from '@angular/router';
|
||||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||||
|
|
||||||
@@ -41,64 +35,6 @@ export class NavbarComponent extends MenuComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.createMenu();
|
|
||||||
super.ngOnInit();
|
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);
|
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> => {
|
const menuSectionByIDSelector = (id: string): MemoizedSelector<MenuState, MenuSection> => {
|
||||||
return menuKeySelector<MenuSection>(id, menuSectionStateSelector);
|
return menuKeySelector<MenuSection>(id, menuSectionStateSelector);
|
||||||
@@ -164,7 +164,7 @@ export class MenuService {
|
|||||||
*/
|
*/
|
||||||
isMenuCollapsed(menuID: MenuID): Observable<boolean> {
|
isMenuCollapsed(menuID: MenuID): Observable<boolean> {
|
||||||
return this.getMenu(menuID).pipe(
|
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> {
|
isMenuPreviewCollapsed(menuID: MenuID): Observable<boolean> {
|
||||||
return this.getMenu(menuID).pipe(
|
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> {
|
isMenuVisible(menuID: MenuID): Observable<boolean> {
|
||||||
return this.getMenu(menuID).pipe(
|
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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
import { BrowserModule, makeStateKey, TransferState } from '@angular/platform-browser';
|
import { BrowserModule, makeStateKey, TransferState } from '@angular/platform-browser';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { RouterModule, NoPreloading } from '@angular/router';
|
|
||||||
import { REQUEST } from '@nguniversal/express-engine/tokens';
|
import { REQUEST } from '@nguniversal/express-engine/tokens';
|
||||||
|
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
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';
|
import { IdlePreloadModule } from 'angular-idle-preload';
|
||||||
|
|
||||||
@@ -42,8 +41,8 @@ import { environment } from '../../environments/environment';
|
|||||||
|
|
||||||
export const REQ_KEY = makeStateKey<string>('req');
|
export const REQ_KEY = makeStateKey<string>('req');
|
||||||
|
|
||||||
export function createTranslateLoader(http: HttpClient) {
|
export function createTranslateLoader(transferState: TransferState, http: HttpClient) {
|
||||||
return new TranslateJson5HttpLoader(http, 'assets/i18n/', '.json5');
|
return new TranslateBrowserLoader(transferState, http, 'assets/i18n/', '.json5');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRequest(transferState: TransferState): any {
|
export function getRequest(transferState: TransferState): any {
|
||||||
@@ -59,13 +58,6 @@ export function getRequest(transferState: TransferState): any {
|
|||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
// forRoot ensures the providers are only created once
|
// forRoot ensures the providers are only created once
|
||||||
IdlePreloadModule.forRoot(),
|
IdlePreloadModule.forRoot(),
|
||||||
RouterModule.forRoot([], {
|
|
||||||
// enableTracing: true,
|
|
||||||
useHash: false,
|
|
||||||
scrollPositionRestoration: 'enabled',
|
|
||||||
anchorScrolling: 'enabled',
|
|
||||||
preloadingStrategy: NoPreloading
|
|
||||||
}),
|
|
||||||
StatisticsModule.forRoot(),
|
StatisticsModule.forRoot(),
|
||||||
Angulartics2RouterlessModule.forRoot(),
|
Angulartics2RouterlessModule.forRoot(),
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
@@ -74,7 +66,7 @@ export function getRequest(transferState: TransferState): any {
|
|||||||
loader: {
|
loader: {
|
||||||
provide: TranslateLoader,
|
provide: TranslateLoader,
|
||||||
useFactory: (createTranslateLoader),
|
useFactory: (createTranslateLoader),
|
||||||
deps: [HttpClient]
|
deps: [TransferState, HttpClient]
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
AppModule
|
AppModule
|
||||||
@@ -92,9 +84,11 @@ export function getRequest(transferState: TransferState): any {
|
|||||||
// extend environment with app config for browser
|
// extend environment with app config for browser
|
||||||
extendEnvironmentWithAppConfig(environment, appConfig);
|
extendEnvironmentWithAppConfig(environment, appConfig);
|
||||||
}
|
}
|
||||||
dspaceTransferState.transfer();
|
return () =>
|
||||||
correlationIdService.initCorrelationId();
|
dspaceTransferState.transfer().then((b: boolean) => {
|
||||||
return () => true;
|
correlationIdService.initCorrelationId();
|
||||||
|
return b;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
deps: [TransferState, DSpaceTransferState, CorrelationIdService],
|
deps: [TransferState, DSpaceTransferState, CorrelationIdService],
|
||||||
multi: true
|
multi: true
|
||||||
|
@@ -15,7 +15,7 @@ import { AppComponent } from '../../app/app.component';
|
|||||||
import { AppModule } from '../../app/app.module';
|
import { AppModule } from '../../app/app.module';
|
||||||
import { DSpaceServerTransferStateModule } from '../transfer-state/dspace-server-transfer-state.module';
|
import { DSpaceServerTransferStateModule } from '../transfer-state/dspace-server-transfer-state.module';
|
||||||
import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service';
|
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 { CookieService } from '../../app/core/services/cookie.service';
|
||||||
import { ServerCookieService } from '../../app/core/services/server-cookie.service';
|
import { ServerCookieService } from '../../app/core/services/server-cookie.service';
|
||||||
import { AuthService } from '../../app/core/auth/auth.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';
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
export function createTranslateLoader() {
|
export function createTranslateLoader(transferState: TransferState) {
|
||||||
return new TranslateJson5UniversalLoader('dist/server/assets/i18n/', '.json5');
|
return new TranslateServerLoader(transferState, 'dist/server/assets/i18n/', '.json5');
|
||||||
}
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -47,16 +47,13 @@ export function createTranslateLoader() {
|
|||||||
BrowserModule.withServerTransition({
|
BrowserModule.withServerTransition({
|
||||||
appId: 'dspace-angular'
|
appId: 'dspace-angular'
|
||||||
}),
|
}),
|
||||||
RouterModule.forRoot([], {
|
|
||||||
useHash: false
|
|
||||||
}),
|
|
||||||
NoopAnimationsModule,
|
NoopAnimationsModule,
|
||||||
DSpaceServerTransferStateModule,
|
DSpaceServerTransferStateModule,
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
provide: TranslateLoader,
|
provide: TranslateLoader,
|
||||||
useFactory: (createTranslateLoader),
|
useFactory: (createTranslateLoader),
|
||||||
deps: []
|
deps: [TransferState]
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
ServerModule,
|
ServerModule,
|
||||||
|
@@ -1,12 +1,19 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { coreSelector } from 'src/app/core/core.selectors';
|
||||||
import { StoreAction, StoreActionTypes } from '../../app/store.actions';
|
import { StoreAction, StoreActionTypes } from '../../app/store.actions';
|
||||||
import { DSpaceTransferState } from './dspace-transfer-state.service';
|
import { DSpaceTransferState } from './dspace-transfer-state.service';
|
||||||
|
import { find, map } from 'rxjs/operators';
|
||||||
|
import { isNotEmpty } from '../../app/shared/empty.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DSpaceBrowserTransferState extends DSpaceTransferState {
|
export class DSpaceBrowserTransferState extends DSpaceTransferState {
|
||||||
transfer() {
|
async transfer(): Promise<boolean> {
|
||||||
const state = this.transferState.get<any>(DSpaceTransferState.NGRX_STATE, null);
|
const state = this.transferState.get<any>(DSpaceTransferState.NGRX_STATE, null);
|
||||||
this.transferState.remove(DSpaceTransferState.NGRX_STATE);
|
this.transferState.remove(DSpaceTransferState.NGRX_STATE);
|
||||||
this.store.dispatch(new StoreAction(StoreActionTypes.REHYDRATE, 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()
|
@Injectable()
|
||||||
export class DSpaceServerTransferState extends DSpaceTransferState {
|
export class DSpaceServerTransferState extends DSpaceTransferState {
|
||||||
transfer() {
|
transfer(): Promise<boolean> {
|
||||||
this.transferState.onSerialize(DSpaceTransferState.NGRX_STATE, () => {
|
this.transferState.onSerialize(DSpaceTransferState.NGRX_STATE, () => {
|
||||||
let state;
|
let state;
|
||||||
this.store.pipe(take(1)).subscribe((saveState: any) => {
|
this.store.pipe(take(1)).subscribe((saveState: any) => {
|
||||||
@@ -14,5 +14,7 @@ export class DSpaceServerTransferState extends DSpaceTransferState {
|
|||||||
|
|
||||||
return state;
|
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 { ResourcePoliciesModule } from '../../app/shared/resource-policies/resource-policies.module';
|
||||||
import { ComcolModule } from '../../app/shared/comcol/comcol.module';
|
import { ComcolModule } from '../../app/shared/comcol/comcol.module';
|
||||||
import { FeedbackComponent } from './app/info/feedback/feedback.component';
|
import { FeedbackComponent } from './app/info/feedback/feedback.component';
|
||||||
|
import { RootModule } from '../../app/root.module';
|
||||||
|
|
||||||
const DECLARATIONS = [
|
const DECLARATIONS = [
|
||||||
FileSectionComponent,
|
FileSectionComponent,
|
||||||
@@ -136,6 +137,7 @@ const DECLARATIONS = [
|
|||||||
AdminSearchModule,
|
AdminSearchModule,
|
||||||
AdminWorkflowModuleModule,
|
AdminWorkflowModuleModule,
|
||||||
AppModule,
|
AppModule,
|
||||||
|
RootModule,
|
||||||
BitstreamFormatsModule,
|
BitstreamFormatsModule,
|
||||||
BrowseByModule,
|
BrowseByModule,
|
||||||
CollectionFormModule,
|
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 { CommonModule } from '@angular/common';
|
||||||
import { AdminRegistriesModule } from '../../app/admin/admin-registries/admin-registries.module';
|
import { AdminRegistriesModule } from '../../app/admin/admin-registries/admin-registries.module';
|
||||||
import { AdminSearchModule } from '../../app/admin/admin-search-page/admin-search.module';
|
import { AdminSearchModule } from '../../app/admin/admin-search-page/admin-search.module';
|
||||||
import { AdminWorkflowModuleModule } from '../../app/admin/admin-workflow-page/admin-workflow.module';
|
import {
|
||||||
import { BitstreamFormatsModule } from '../../app/admin/admin-registries/bitstream-formats/bitstream-formats.module';
|
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 { 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 { CommunityFormModule } from '../../app/community-page/community-form/community-form.module';
|
||||||
import { CoreModule } from '../../app/core/core.module';
|
import { CoreModule } from '../../app/core/core.module';
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
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 { FormsModule } from '@angular/forms';
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { IdlePreloadModule } from 'angular-idle-preload';
|
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 { MyDspaceSearchModule } from '../../app/my-dspace-page/my-dspace-search.module';
|
||||||
import { MenuModule } from '../../app/shared/menu/menu.module';
|
import { MenuModule } from '../../app/shared/menu/menu.module';
|
||||||
import { NavbarModule } from '../../app/navbar/navbar.module';
|
import { NavbarModule } from '../../app/navbar/navbar.module';
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { ProfilePageModule } from '../../app/profile-page/profile-page.module';
|
import { ProfilePageModule } from '../../app/profile-page/profile-page.module';
|
||||||
import { RegisterEmailFormModule } from '../../app/register-email-form/register-email-form.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 { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
|
||||||
import { SearchPageModule } from '../../app/search-page/search-page.module';
|
import { SearchPageModule } from '../../app/search-page/search-page.module';
|
||||||
import { SharedModule } from '../../app/shared/shared.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 { StoreModule } from '@ngrx/store';
|
||||||
import { StoreRouterConnectingModule } from '@ngrx/router-store';
|
import { StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
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 { HomePageModule } from '../../app/home-page/home-page.module';
|
||||||
import { AppModule } from '../../app/app.module';
|
import { AppModule } from '../../app/app.module';
|
||||||
import { ItemPageModule } from '../../app/item-page/item-page.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 { CollectionPageModule } from '../../app/collection-page/collection-page.module';
|
||||||
import { SubmissionModule } from '../../app/submission/submission.module';
|
import { SubmissionModule } from '../../app/submission/submission.module';
|
||||||
import { MyDSpacePageModule } from '../../app/my-dspace-page/my-dspace-page.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 { 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 { ComcolModule } from '../../app/shared/comcol/comcol.module';
|
||||||
|
import { RootModule } from '../../app/root.module';
|
||||||
|
|
||||||
const DECLARATIONS = [
|
const DECLARATIONS = [
|
||||||
HomeNewsComponent,
|
|
||||||
HeaderComponent,
|
|
||||||
HeaderNavbarWrapperComponent,
|
|
||||||
NavbarComponent
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -60,6 +65,7 @@ const DECLARATIONS = [
|
|||||||
AdminSearchModule,
|
AdminSearchModule,
|
||||||
AdminWorkflowModuleModule,
|
AdminWorkflowModuleModule,
|
||||||
AppModule,
|
AppModule,
|
||||||
|
RootModule,
|
||||||
BitstreamFormatsModule,
|
BitstreamFormatsModule,
|
||||||
BrowseByModule,
|
BrowseByModule,
|
||||||
CollectionFormModule,
|
CollectionFormModule,
|
||||||
@@ -105,12 +111,12 @@ const DECLARATIONS = [
|
|||||||
declarations: DECLARATIONS
|
declarations: DECLARATIONS
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This module serves as an index for all the components in this theme.
|
* This module serves as an index for all the components in this theme.
|
||||||
* It should import all other modules, so the compiler knows where to find any components referenced
|
* It should import all other modules, so the compiler knows where to find any components referenced
|
||||||
* from a component in this theme
|
* from a component in this theme
|
||||||
* It is purposefully not exported, it should never be imported anywhere else, its only purpose is
|
* 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
|
* 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,14 +3,39 @@ import { join } from 'path';
|
|||||||
import { buildAppConfig } from '../src/config/config.server';
|
import { buildAppConfig } from '../src/config/config.server';
|
||||||
import { commonExports } from './webpack.common';
|
import { commonExports } from './webpack.common';
|
||||||
|
|
||||||
|
const CompressionPlugin = require('compression-webpack-plugin');
|
||||||
|
const zlib = require('zlib');
|
||||||
|
|
||||||
module.exports = Object.assign({}, commonExports, {
|
module.exports = Object.assign({}, commonExports, {
|
||||||
target: 'web',
|
target: 'web',
|
||||||
node: {
|
node: {
|
||||||
module: 'empty'
|
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: {
|
devServer: {
|
||||||
before(app, server) {
|
before(app, server) {
|
||||||
buildAppConfig(join(process.cwd(), 'src/assets/config.json'));
|
buildAppConfig(join(process.cwd(), 'src/assets/config.json'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user