mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
68067: Refactoring menu-component resolving route menu data to menu effects
This commit is contained in:
@@ -18,7 +18,6 @@ import { TextMenuItemModel } from '../../shared/menu/menu-item/models/text.model
|
|||||||
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 { CSSVariableService } from '../../shared/sass-helper/sass-helper.service';
|
import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component representing the admin sidebar
|
* Component representing the admin sidebar
|
||||||
@@ -60,19 +59,18 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(protected menuService: MenuService,
|
constructor(protected menuService: MenuService,
|
||||||
protected injector: Injector,
|
protected injector: Injector,
|
||||||
protected route: ActivatedRoute,
|
|
||||||
protected router: Router,
|
|
||||||
private variableService: CSSVariableService,
|
private variableService: CSSVariableService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private modalService: NgbModal
|
private modalService: NgbModal
|
||||||
) {
|
) {
|
||||||
super(menuService, injector, route, router);
|
super(menuService, injector);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set and calculate all initial values of the instance variables
|
* Set and calculate all initial values of the instance variables
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.createMenu();
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.sidebarWidth = this.variableService.getVariable('sidebarItemsWidth');
|
this.sidebarWidth = this.variableService.getVariable('sidebarItemsWidth');
|
||||||
this.authService.isAuthenticated()
|
this.authService.isAuthenticated()
|
||||||
@@ -455,7 +453,9 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
index: 10
|
index: 10
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, menuSection));
|
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, {
|
||||||
|
shouldPersistOnRouteChange: true
|
||||||
|
})));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@ import { ServerSyncBufferEffects } from './cache/server-sync-buffer.effects';
|
|||||||
import { ObjectUpdatesEffects } from './data/object-updates/object-updates.effects';
|
import { ObjectUpdatesEffects } from './data/object-updates/object-updates.effects';
|
||||||
import { RouteEffects } from './services/route.effects';
|
import { RouteEffects } from './services/route.effects';
|
||||||
import { RouterEffects } from './router/router.effects';
|
import { RouterEffects } from './router/router.effects';
|
||||||
|
import { MenuEffects } from '../shared/menu/menu.effects';
|
||||||
|
|
||||||
export const coreEffects = [
|
export const coreEffects = [
|
||||||
RequestEffects,
|
RequestEffects,
|
||||||
@@ -17,5 +18,6 @@ export const coreEffects = [
|
|||||||
ServerSyncBufferEffects,
|
ServerSyncBufferEffects,
|
||||||
ObjectUpdatesEffects,
|
ObjectUpdatesEffects,
|
||||||
RouteEffects,
|
RouteEffects,
|
||||||
RouterEffects
|
RouterEffects,
|
||||||
|
MenuEffects
|
||||||
];
|
];
|
||||||
|
@@ -7,7 +7,6 @@ import { TextMenuItemModel } from '../shared/menu/menu-item/models/text.model';
|
|||||||
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.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 { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component representing the public navbar
|
* Component representing the public navbar
|
||||||
@@ -27,11 +26,14 @@ export class NavbarComponent extends MenuComponent {
|
|||||||
|
|
||||||
constructor(protected menuService: MenuService,
|
constructor(protected menuService: MenuService,
|
||||||
protected injector: Injector,
|
protected injector: Injector,
|
||||||
protected route: ActivatedRoute,
|
|
||||||
protected router: Router,
|
|
||||||
public windowService: HostWindowService
|
public windowService: HostWindowService
|
||||||
) {
|
) {
|
||||||
super(menuService, injector, route, router);
|
super(menuService, injector);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.createMenu();
|
||||||
|
super.ngOnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,7 +93,9 @@ export class NavbarComponent extends MenuComponent {
|
|||||||
} as LinkMenuItemModel
|
} as LinkMenuItemModel
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, menuSection));
|
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, {
|
||||||
|
shouldPersistOnRouteChange: true
|
||||||
|
})));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,7 +21,6 @@ export const MenuActionTypes = {
|
|||||||
EXPAND_MENU_PREVIEW: type('dspace/menu/EXPAND_MENU_PREVIEW'),
|
EXPAND_MENU_PREVIEW: type('dspace/menu/EXPAND_MENU_PREVIEW'),
|
||||||
ADD_SECTION: type('dspace/menu-section/ADD_SECTION'),
|
ADD_SECTION: type('dspace/menu-section/ADD_SECTION'),
|
||||||
REMOVE_SECTION: type('dspace/menu-section/REMOVE_SECTION'),
|
REMOVE_SECTION: type('dspace/menu-section/REMOVE_SECTION'),
|
||||||
RESET_SECTIONS: type('dspace/menu-section/RESET_SECTIONS'),
|
|
||||||
SHOW_SECTION: type('dspace/menu-section/SHOW_SECTION'),
|
SHOW_SECTION: type('dspace/menu-section/SHOW_SECTION'),
|
||||||
HIDE_SECTION: type('dspace/menu-section/HIDE_SECTION'),
|
HIDE_SECTION: type('dspace/menu-section/HIDE_SECTION'),
|
||||||
ACTIVATE_SECTION: type('dspace/menu-section/ACTIVATE_SECTION'),
|
ACTIVATE_SECTION: type('dspace/menu-section/ACTIVATE_SECTION'),
|
||||||
@@ -116,18 +115,6 @@ export class ExpandMenuPreviewAction implements Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Action used to remove all sections from a certain menu
|
|
||||||
*/
|
|
||||||
export class ResetMenuSectionsAction implements Action {
|
|
||||||
type = MenuActionTypes.RESET_SECTIONS;
|
|
||||||
menuID: MenuID;
|
|
||||||
|
|
||||||
constructor(menuID: MenuID) {
|
|
||||||
this.menuID = menuID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MENU SECTION ACTIONS
|
// MENU SECTION ACTIONS
|
||||||
/**
|
/**
|
||||||
* Action used to perform state changes for a section of a certain menu
|
* Action used to perform state changes for a section of a certain menu
|
||||||
@@ -238,5 +225,4 @@ export type MenuAction =
|
|||||||
| ToggleActiveMenuSectionAction
|
| ToggleActiveMenuSectionAction
|
||||||
| CollapseMenuPreviewAction
|
| CollapseMenuPreviewAction
|
||||||
| ExpandMenuPreviewAction
|
| ExpandMenuPreviewAction
|
||||||
| ResetMenuSectionsAction
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
/* tslint:enable:max-classes-per-file */
|
||||||
|
@@ -8,7 +8,6 @@ import { GenericConstructor } from '../../core/shared/generic-constructor';
|
|||||||
import { hasNoValue, hasValue } from '../empty.util';
|
import { hasNoValue, hasValue } from '../empty.util';
|
||||||
import { MenuSectionComponent } from './menu-section/menu-section.component';
|
import { MenuSectionComponent } from './menu-section/menu-section.component';
|
||||||
import { getComponentForMenu } from './menu-section.decorator';
|
import { getComponentForMenu } from './menu-section.decorator';
|
||||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
|
||||||
import { Subscription } from 'rxjs/internal/Subscription';
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,17 +69,13 @@ export class MenuComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
subs: Subscription[] = [];
|
subs: Subscription[] = [];
|
||||||
|
|
||||||
constructor(protected menuService: MenuService,
|
constructor(protected menuService: MenuService, protected injector: Injector) {
|
||||||
protected injector: Injector,
|
|
||||||
protected route: ActivatedRoute,
|
|
||||||
protected router: Router) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets all instance variables to their initial values
|
* Sets all instance variables to their initial values
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.initSections();
|
|
||||||
this.menuCollapsed = this.menuService.isMenuCollapsed(this.menuID);
|
this.menuCollapsed = this.menuService.isMenuCollapsed(this.menuID);
|
||||||
this.menuPreviewCollapsed = this.menuService.isMenuPreviewCollapsed(this.menuID);
|
this.menuPreviewCollapsed = this.menuService.isMenuPreviewCollapsed(this.menuID);
|
||||||
this.menuVisible = this.menuService.isMenuVisible(this.menuID);
|
this.menuVisible = this.menuService.isMenuVisible(this.menuID);
|
||||||
@@ -93,49 +88,6 @@ export class MenuComponent implements OnInit, OnDestroy {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize all menu sections and add them to the menu's store
|
|
||||||
*/
|
|
||||||
initSections() {
|
|
||||||
this.subs.push(this.router.events.pipe(
|
|
||||||
filter((e): e is NavigationEnd => e instanceof NavigationEnd),
|
|
||||||
tap(() => this.menuService.resetSections(this.menuID)),
|
|
||||||
map(() => this.resolveMenuSections(this.route.root))
|
|
||||||
).subscribe((sections: MenuSection[]) => {
|
|
||||||
this.createMenu();
|
|
||||||
sections.forEach((section: MenuSection) => {
|
|
||||||
this.menuService.addSection(this.menuID, section);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize all static menu sections and items for this menu
|
|
||||||
* These sections will be available on any route. Route specific sections should be defined in the route's data instead.
|
|
||||||
*/
|
|
||||||
createMenu() {
|
|
||||||
// Override this method to create and add a standard set of menu sections that should be available for the current menu type on all routes
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve menu sections defined in the current route data (including parent routes)
|
|
||||||
* @param route The route to resolve data for
|
|
||||||
*/
|
|
||||||
resolveMenuSections(route: ActivatedRoute): MenuSection[] {
|
|
||||||
const data = route.snapshot.data;
|
|
||||||
const last: boolean = hasNoValue(route.firstChild);
|
|
||||||
|
|
||||||
if (hasValue(data) && hasValue(data.menu) && hasValue(data.menu[this.menuID])) {
|
|
||||||
if (!last) {
|
|
||||||
return [...data.menu[this.menuID], ...this.resolveMenuSections(route.firstChild)]
|
|
||||||
} else {
|
|
||||||
return [...data.menu[this.menuID]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !last ? this.resolveMenuSections(route.firstChild) : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collapse this menu when it's currently expanded, expand it when its currently collapsed
|
* Collapse this menu when it's currently expanded, expand it when its currently collapsed
|
||||||
* @param {Event} event The user event that triggered this method
|
* @param {Event} event The user event that triggered this method
|
||||||
|
72
src/app/shared/menu/menu.effects.ts
Normal file
72
src/app/shared/menu/menu.effects.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { MenuSection } from './menu.reducer';
|
||||||
|
import { hasNoValue, hasValue } from '../empty.util';
|
||||||
|
import { Actions, Effect, ofType } from '@ngrx/effects';
|
||||||
|
import { MenuService } from './menu.service';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { Action } from '@ngrx/store';
|
||||||
|
import { ROUTER_NAVIGATED } from '@ngrx/router-store';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { map, take, tap } from 'rxjs/operators';
|
||||||
|
import { MenuID } from './initial-menus-state';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MenuEffects {
|
||||||
|
|
||||||
|
constructor(private actions$: Actions,
|
||||||
|
private menuService: MenuService,
|
||||||
|
private route: ActivatedRoute) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Effect({ dispatch: false })
|
||||||
|
public buildRouteMenuSections$: Observable<Action> = this.actions$
|
||||||
|
.pipe(
|
||||||
|
ofType(ROUTER_NAVIGATED),
|
||||||
|
tap(() => {
|
||||||
|
Object.values(MenuID).forEach((menuID) => {
|
||||||
|
this.buildRouteMenuSections(menuID);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
buildRouteMenuSections(menuID: MenuID) {
|
||||||
|
this.menuService.getNonPersistentMenuSections(menuID).pipe(
|
||||||
|
map((sections) => sections.map((section) => section.id)),
|
||||||
|
take(1)
|
||||||
|
).subscribe((shouldNotPersistIDs: string[]) => {
|
||||||
|
const resolvedSections = this.resolveRouteMenuSections(this.route.root, menuID);
|
||||||
|
resolvedSections.forEach((section) => {
|
||||||
|
const index = shouldNotPersistIDs.indexOf(section.id);
|
||||||
|
if (index > -1) {
|
||||||
|
shouldNotPersistIDs.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
this.menuService.addSection(menuID, section);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
shouldNotPersistIDs.forEach((id) => {
|
||||||
|
this.menuService.removeSection(menuID, id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve menu sections defined in the current route data (including parent routes)
|
||||||
|
* @param route The route to resolve data for
|
||||||
|
* @param menuID The menu to resolve data for
|
||||||
|
*/
|
||||||
|
resolveRouteMenuSections(route: ActivatedRoute, menuID: MenuID): MenuSection[] {
|
||||||
|
const data = route.snapshot.data;
|
||||||
|
const last: boolean = hasNoValue(route.firstChild);
|
||||||
|
|
||||||
|
if (hasValue(data) && hasValue(data.menu) && hasValue(data.menu[menuID])) {
|
||||||
|
if (!last) {
|
||||||
|
return [...data.menu[menuID], ...this.resolveRouteMenuSections(route.firstChild, menuID)]
|
||||||
|
} else {
|
||||||
|
return [...data.menu[menuID]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !last ? this.resolveRouteMenuSections(route.firstChild, menuID) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -6,7 +6,7 @@ import {
|
|||||||
MenuAction,
|
MenuAction,
|
||||||
MenuActionTypes,
|
MenuActionTypes,
|
||||||
MenuSectionAction,
|
MenuSectionAction,
|
||||||
RemoveMenuSectionAction, ResetMenuSectionsAction,
|
RemoveMenuSectionAction,
|
||||||
ShowMenuSectionAction,
|
ShowMenuSectionAction,
|
||||||
ToggleActiveMenuSectionAction
|
ToggleActiveMenuSectionAction
|
||||||
} from './menu.actions';
|
} from './menu.actions';
|
||||||
@@ -58,6 +58,7 @@ export class MenuSection {
|
|||||||
model: MenuItemModel;
|
model: MenuItemModel;
|
||||||
index?: number;
|
index?: number;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
shouldPersistOnRouteChange? = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,10 +119,6 @@ export function menusReducer(state: MenusState = initialMenusState, action: Menu
|
|||||||
case MenuActionTypes.SHOW_SECTION: {
|
case MenuActionTypes.SHOW_SECTION: {
|
||||||
return showSection(state, action as ShowMenuSectionAction);
|
return showSection(state, action as ShowMenuSectionAction);
|
||||||
}
|
}
|
||||||
case MenuActionTypes.RESET_SECTIONS: {
|
|
||||||
const newMenuState = Object.assign({}, menuState, { sections: {} });
|
|
||||||
return Object.assign({}, state, { [action.menuID]: newMenuState });
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return state;
|
return state;
|
||||||
|
@@ -14,7 +14,7 @@ import {
|
|||||||
ExpandMenuAction,
|
ExpandMenuAction,
|
||||||
ExpandMenuPreviewAction,
|
ExpandMenuPreviewAction,
|
||||||
HideMenuAction,
|
HideMenuAction,
|
||||||
RemoveMenuSectionAction, ResetMenuSectionsAction,
|
RemoveMenuSectionAction,
|
||||||
ShowMenuAction,
|
ShowMenuAction,
|
||||||
ToggleActiveMenuSectionAction,
|
ToggleActiveMenuSectionAction,
|
||||||
ToggleMenuAction,
|
ToggleMenuAction,
|
||||||
@@ -129,6 +129,16 @@ export class MenuService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve menu sections that shouldn't persist on route change
|
||||||
|
* @param menuID The ID of the menu the sections reside in
|
||||||
|
*/
|
||||||
|
getNonPersistentMenuSections(menuID: MenuID): Observable<MenuSection[]> {
|
||||||
|
return this.getMenu(menuID).pipe(
|
||||||
|
map((state: MenuState) => Object.values(state.sections).filter((section: MenuSection) => !section.shouldPersistOnRouteChange))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new section to the store
|
* Add a new section to the store
|
||||||
* @param {MenuID} menuID The menu to which the new section is to be added
|
* @param {MenuID} menuID The menu to which the new section is to be added
|
||||||
@@ -147,14 +157,6 @@ export class MenuService {
|
|||||||
this.store.dispatch(new RemoveMenuSectionAction(menuID, sectionID));
|
this.store.dispatch(new RemoveMenuSectionAction(menuID, sectionID));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all sections within a menu from the store
|
|
||||||
* @param {MenuID} menuID The menu from which the sections are to be removed
|
|
||||||
*/
|
|
||||||
resetSections(menuID: MenuID) {
|
|
||||||
this.store.dispatch(new ResetMenuSectionsAction(menuID));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a given menu is collapsed
|
* Check if a given menu is collapsed
|
||||||
* @param {MenuID} menuID The ID of the menu that is to be checked
|
* @param {MenuID} menuID The ID of the menu that is to be checked
|
||||||
|
Reference in New Issue
Block a user