Merge pull request #3044 from dpalou/MOBILE-3922

Mobile 3922
main
Noel De Martin 2022-01-13 14:01:41 +01:00 committed by GitHub
commit 7cc309275c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 77 additions and 47 deletions

View File

@ -118,14 +118,14 @@ export class CoreSortedDelegate<
const handler = this.enabledHandlers[name]; const handler = this.enabledHandlers[name];
const data = <DisplayType> handler.getDisplayData(); const data = <DisplayType> handler.getDisplayData();
data.priority = handler.priority || 0; data.priority = data.priority ?? handler.priority ?? 0;
data.name = handler.name; data.name = handler.name;
displayData.push(data); displayData.push(data);
} }
// Sort them by priority. // Sort them by priority.
displayData.sort((a, b) => b.priority! - a.priority!); displayData.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
this.loaded = true; this.loaded = true;
this.sortedHandlersRxJs.next(displayData); this.sortedHandlersRxJs.next(displayData);

View File

@ -27,7 +27,7 @@ export class CoreDashboardHomeHandlerService implements CoreMainMenuHomeHandler
static readonly PAGE_NAME = 'dashboard'; static readonly PAGE_NAME = 'dashboard';
name = 'CoreCoursesDashboard'; name = 'CoreCoursesDashboard';
priority = 1100; priority = 1200;
/** /**
* Check if the handler is enabled on a site level. * Check if the handler is enabled on a site level.
@ -92,7 +92,6 @@ export class CoreDashboardHomeHandlerService implements CoreMainMenuHomeHandler
page: CoreDashboardHomeHandlerService.PAGE_NAME, page: CoreDashboardHomeHandlerService.PAGE_NAME,
class: 'core-courses-dashboard-handler', class: 'core-courses-dashboard-handler',
icon: 'fas-tachometer-alt', icon: 'fas-tachometer-alt',
selectPriority: 1000,
}; };
} }

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CoreSiteInfoUserHomepage } from '@classes/site';
import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate'; import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate';
import { CoreSiteHomeHomeHandler } from '@features/sitehome/services/handlers/sitehome-home'; import { CoreSiteHomeHomeHandler } from '@features/sitehome/services/handlers/sitehome-home';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
@ -59,11 +60,16 @@ export class CoreCoursesMyCoursesMainMenuHandlerService implements CoreMainMenuH
* @inheritdoc * @inheritdoc
*/ */
getDisplayData(): CoreMainMenuHandlerData { getDisplayData(): CoreMainMenuHandlerData {
const site = CoreSites.getCurrentSite();
const displayMyCourses = site?.getInfo() && site?.getInfo()?.userhomepage === CoreSiteInfoUserHomepage.HOMEPAGE_MYCOURSES;
return { return {
title: 'core.courses.mycourses', title: 'core.courses.mycourses',
page: CoreCoursesMyCoursesMainMenuHandlerService.PAGE_NAME, page: CoreCoursesMyCoursesMainMenuHandlerService.PAGE_NAME,
class: 'core-courses-my-courses-handler', class: 'core-courses-my-courses-handler',
icon: 'fas-graduation-cap', icon: 'fas-graduation-cap',
priority: displayMyCourses ? this.priority + 200 : this.priority,
}; };
} }

View File

@ -35,7 +35,6 @@ function buildRoutes(injector: Injector): Routes {
{ {
path: '', path: '',
pathMatch: 'full', pathMatch: 'full',
redirectTo: CoreMainMenuHomeHandlerService.PAGE_NAME,
}, },
{ {
path: CoreMainMenuHomeHandlerService.PAGE_NAME, path: CoreMainMenuHomeHandlerService.PAGE_NAME,

View File

@ -17,5 +17,5 @@
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<core-empty-box *ngIf="tabs.length == 0" icon="fas-home" [message]="'core.courses.nocourses' | translate"></core-empty-box> <core-empty-box *ngIf="tabs.length == 0" icon="fas-home" [message]="'core.courses.nocourses' | translate"></core-empty-box>
</core-loading> </core-loading>
<core-tabs-outlet *ngIf="tabs.length > 0" [selectedIndex]="selectedTab" [hideUntil]="loaded" [tabs]="tabs" (ionChange)="tabSelected()"> <core-tabs-outlet *ngIf="tabs.length > 0" [hideUntil]="loaded" [tabs]="tabs" (ionChange)="tabSelected()">
</core-tabs-outlet> </core-tabs-outlet>

View File

@ -26,6 +26,7 @@ import { CoreCourseHelper } from '@features/course/services/course-helper';
import { CoreCourse } from '@features/course/services/course'; import { CoreCourse } from '@features/course/services/course';
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
import { CoreMainMenuHomeHandlerService } from '@features/mainmenu/services/handlers/mainmenu';
/** /**
* Page that displays the Home. * Page that displays the Home.
@ -39,10 +40,9 @@ export class CoreMainMenuHomePage implements OnInit {
@ViewChild(CoreTabsOutletComponent) tabsComponent?: CoreTabsOutletComponent; @ViewChild(CoreTabsOutletComponent) tabsComponent?: CoreTabsOutletComponent;
siteName!: string; siteName = '';
tabs: CoreTabsOutletTab[] = []; tabs: CoreTabsOutletTab[] = [];
loaded = false; loaded = false;
selectedTab?: number;
protected subscription?: Subscription; protected subscription?: Subscription;
protected updateSiteObserver?: CoreEventObserver; protected updateSiteObserver?: CoreEventObserver;
@ -97,7 +97,7 @@ export class CoreMainMenuHomePage implements OnInit {
} }
return { return {
page: `/main/home/${handler.page}`, page: `/main/${CoreMainMenuHomeHandlerService.PAGE_NAME}/${handler.page}`,
pageParams: handler.pageParams, pageParams: handler.pageParams,
title: handler.title, title: handler.title,
class: handler.class, class: handler.class,
@ -109,21 +109,6 @@ export class CoreMainMenuHomePage implements OnInit {
// Sort them by priority so new handlers are in the right position. // Sort them by priority so new handlers are in the right position.
newTabs.sort((a, b) => (handlersMap[b.title].priority || 0) - (handlersMap[a.title].priority || 0)); newTabs.sort((a, b) => (handlersMap[b.title].priority || 0) - (handlersMap[a.title].priority || 0));
if (this.selectedTab === undefined && newTabs.length > 0) {
let maxPriority = 0;
this.selectedTab = Object.entries(newTabs).reduce((maxIndex, [index, tab]) => {
const selectPriority = handlersMap[tab.title].selectPriority ?? 0;
if (selectPriority > maxPriority) {
maxPriority = selectPriority;
maxIndex = Number(index);
}
return maxIndex;
}, 0);
}
this.tabs = newTabs; this.tabs = newTabs;
// Try to prevent empty box displayed for an instant when it shouldn't. // Try to prevent empty box displayed for an instant when it shouldn't.
@ -136,7 +121,7 @@ export class CoreMainMenuHomePage implements OnInit {
* Load the site name. * Load the site name.
*/ */
protected loadSiteName(): void { protected loadSiteName(): void {
this.siteName = CoreSites.getCurrentSite()!.getSiteName(); this.siteName = CoreSites.getCurrentSite()?.getSiteName() || '';
} }
/** /**
@ -171,8 +156,8 @@ export class CoreMainMenuHomePage implements OnInit {
const actions = await CoreContentLinksDelegate.getActionsFor(url, undefined); const actions = await CoreContentLinksDelegate.getActionsFor(url, undefined);
const action = CoreContentLinksHelper.getFirstValidAction(actions); const action = CoreContentLinksHelper.getFirstValidAction(actions);
if (action) { if (action?.sites?.[0]) {
action.action(action.sites![0]); action.action(action.sites[0]);
} }
} }

View File

@ -173,6 +173,10 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
this.loaded = CoreMainMenuDelegate.areHandlersLoaded(); this.loaded = CoreMainMenuDelegate.areHandlersLoaded();
if (this.loaded && this.tabs[0] && !CoreNavigator.getCurrentMainMenuTab()) {
// No tab selected, select the first one.
this.mainTabs?.select(this.tabs[0].page);
}
} }
/** /**

View File

@ -72,12 +72,7 @@ export interface CoreMainMenuHomeHandlerData {
/** /**
* Data returned by the delegate for each handler. * Data returned by the delegate for each handler.
*/ */
export interface CoreMainMenuHomeHandlerToDisplay extends CoreDelegateToDisplay, CoreMainMenuHomeHandlerData { export interface CoreMainMenuHomeHandlerToDisplay extends CoreDelegateToDisplay, CoreMainMenuHomeHandlerData {}
/**
* Priority to select handler.
*/
selectPriority?: number;
}
/** /**
* Service to interact with plugins to be shown in the main menu. Provides functions to register a plugin * Service to interact with plugins to be shown in the main menu. Provides functions to register a plugin

View File

@ -77,6 +77,11 @@ export interface CoreMainMenuHandlerData {
* Whether the handler should only appear in More menu. * Whether the handler should only appear in More menu.
*/ */
onlyInMore?: boolean; onlyInMore?: boolean;
/**
* Priority of the handler. If set, overrides the priority defined in CoreMainMenuHandler.
*/
priority?: number;
} }
/** /**

View File

@ -28,7 +28,7 @@ export class CoreSiteHomeHomeHandlerService implements CoreMainMenuHomeHandler {
static readonly PAGE_NAME = 'site'; static readonly PAGE_NAME = 'site';
name = 'CoreSiteHomeDashboard'; name = 'CoreSiteHomeDashboard';
priority = 1200; priority = 1100;
/** /**
* Check if the handler is enabled on a site level. * Check if the handler is enabled on a site level.
@ -64,7 +64,7 @@ export class CoreSiteHomeHomeHandlerService implements CoreMainMenuHomeHandler {
page: CoreSiteHomeHomeHandlerService.PAGE_NAME, page: CoreSiteHomeHomeHandlerService.PAGE_NAME,
class: 'core-sitehome-dashboard-handler', class: 'core-sitehome-dashboard-handler',
icon: 'fas-home', icon: 'fas-home',
selectPriority: displaySiteHome ? 1100 : 900, priority: displaySiteHome ? this.priority + 200 : this.priority,
}; };
} }

View File

@ -14,6 +14,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CanActivate, CanLoad, UrlTree } from '@angular/router'; import { CanActivate, CanLoad, UrlTree } from '@angular/router';
import { CoreMainMenuHomeHandlerService } from '@features/mainmenu/services/handlers/mainmenu';
import { CoreApp } from '@services/app'; import { CoreApp } from '@services/app';
import { CoreRedirectPayload } from '@services/navigator'; import { CoreRedirectPayload } from '@services/navigator';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
@ -59,7 +60,7 @@ export class CoreRedirectGuard implements CanLoad, CanActivate {
redirect.page, redirect.page,
redirect.options, redirect.options,
); );
const route = Router.parseUrl('/main/home'); const route = Router.parseUrl(`/main/${CoreMainMenuHomeHandlerService.PAGE_NAME}`);
route.queryParams = { route.queryParams = {
redirectPath: redirect.page, redirectPath: redirect.page,

View File

@ -20,7 +20,6 @@ import { NavigationOptions } from '@ionic/angular/providers/nav-controller';
import { CoreConstants } from '@/core/constants'; import { CoreConstants } from '@/core/constants';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreMainMenu } from '@features/mainmenu/services/mainmenu'; import { CoreMainMenu } from '@features/mainmenu/services/mainmenu';
import { CoreMainMenuHomeHandlerService } from '@features/mainmenu/services/handlers/mainmenu';
import { CoreObject } from '@singletons/object'; import { CoreObject } from '@singletons/object';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
@ -31,8 +30,8 @@ import { CoreScreen } from './screen';
import { CoreApp } from './app'; import { CoreApp } from './app';
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate';
const DEFAULT_MAIN_MENU_TAB = CoreMainMenuHomeHandlerService.PAGE_NAME; import { CoreMainMenuHomeHandlerService } from '@features/mainmenu/services/handlers/mainmenu';
/** /**
* Redirect payload. * Redirect payload.
@ -184,9 +183,12 @@ export class CoreNavigatorService {
* @return Whether navigation suceeded. * @return Whether navigation suceeded.
*/ */
async navigateToSiteHome(options: Omit<CoreNavigationOptions, 'reset'> & { siteId?: string } = {}): Promise<boolean> { async navigateToSiteHome(options: Omit<CoreNavigationOptions, 'reset'> & { siteId?: string } = {}): Promise<boolean> {
return this.navigateToSitePath(DEFAULT_MAIN_MENU_TAB, { const landingPagePath = this.getLandingTabPage();
return this.navigateToSitePath(landingPagePath, {
...options, ...options,
reset: true, reset: true,
preferCurrentTab: false,
}); });
} }
@ -535,8 +537,13 @@ export class CoreNavigatorService {
path = path.replace(/^(\.|\/main)?\//, ''); path = path.replace(/^(\.|\/main)?\//, '');
const pathRoot = /^[^/]+/.exec(path)?.[0] ?? ''; const pathRoot = /^[^/]+/.exec(path)?.[0] ?? '';
if (!pathRoot) {
// No path root, going to the site home.
return this.navigate('/main', options);
}
const currentMainMenuTab = this.getCurrentMainMenuTab(); const currentMainMenuTab = this.getCurrentMainMenuTab();
const isMainMenuTab = pathRoot === currentMainMenuTab || (!currentMainMenuTab && path === DEFAULT_MAIN_MENU_TAB) || const isMainMenuTab = pathRoot === currentMainMenuTab || (!currentMainMenuTab && path === this.getLandingTabPage()) ||
await CoreUtils.ignoreErrors(CoreMainMenu.isMainMenuTab(pathRoot), false); await CoreUtils.ignoreErrors(CoreMainMenu.isMainMenuTab(pathRoot), false);
if (!options.preferCurrentTab && isMainMenuTab) { if (!options.preferCurrentTab && isMainMenuTab) {
@ -553,16 +560,32 @@ export class CoreNavigatorService {
return this.navigate(`/main/${path}`, options); return this.navigate(`/main/${path}`, options);
} }
// Open the path within the default main tab. // Open the path within the home tab.
return this.navigate(`/main/${DEFAULT_MAIN_MENU_TAB}`, { return this.navigate(`/main/${CoreMainMenuHomeHandlerService.PAGE_NAME}`, {
...options, ...options,
params: { params: {
redirectPath: `/main/${DEFAULT_MAIN_MENU_TAB}/${path}`, redirectPath: `/main/${CoreMainMenuHomeHandlerService.PAGE_NAME}/${path}`,
redirectOptions: options.params || options.nextNavigation ? options : undefined, redirectOptions: options.params || options.nextNavigation ? options : undefined,
} as CoreRedirectPayload, } as CoreRedirectPayload,
}); });
} }
/**
* Get the first page path using priority.
*
* @return Landing page path.
*/
protected getLandingTabPage(): string {
if (!CoreMainMenuDelegate.areHandlersLoaded()) {
// Handlers not loaded yet, landing page is the root page.
return '';
}
const handlers = CoreMainMenuDelegate.getHandlers();
return handlers[0]?.page || '';
}
/** /**
* Replace all objects in query params with an ID that can be used to retrieve the object later. * Replace all objects in query params with an ID that can be used to retrieve the object later.
* *

View File

@ -21,6 +21,7 @@ import { NavController, Router } from '@singletons';
import { ActivatedRoute, RouterState } from '@angular/router'; import { ActivatedRoute, RouterState } from '@angular/router';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreMainMenu } from '@features/mainmenu/services/mainmenu'; import { CoreMainMenu } from '@features/mainmenu/services/mainmenu';
import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate';
describe('CoreNavigator', () => { describe('CoreNavigator', () => {
@ -118,7 +119,7 @@ describe('CoreNavigator', () => {
}); });
}); });
it('navigates to site paths ussing different path formats', async () => { it('navigates to site paths using different path formats', async () => {
currentMainMenuHandlers.push('users'); currentMainMenuHandlers.push('users');
const assertNavigation = async (currentPath, sitePath, expectedPath) => { const assertNavigation = async (currentPath, sitePath, expectedPath) => {
@ -136,11 +137,23 @@ describe('CoreNavigator', () => {
await assertNavigation('/main/home', '/users/user/42', '/main/users/user/42'); await assertNavigation('/main/home', '/users/user/42', '/main/users/user/42');
}); });
it('navigates to site home', async () => { it('navigates to site home: no handlers loaded', async () => {
const success = await navigator.navigateToSiteHome(); const success = await navigator.navigateToSiteHome();
expect(success).toBe(true); expect(success).toBe(true);
expect(navControllerMock.navigateRoot).toHaveBeenCalledWith(['/main/home'], {}); expect(navControllerMock.navigateRoot).toHaveBeenCalledWith(['/main'], {});
});
it('navigates to site home: handlers loaded', async () => {
mockSingleton(CoreMainMenuDelegate, {
areHandlersLoaded: () => true,
getHandlers: () => [{ title: 'Test', page: 'initialpage', icon: '' }],
});
const success = await navigator.navigateToSiteHome();
expect(success).toBe(true);
expect(navControllerMock.navigateRoot).toHaveBeenCalledWith(['/main/initialpage'], {});
}); });
it.todo('navigates to a different site'); it.todo('navigates to a different site');