From 9a935a39462a6bc1b7b30b48595999ac69e8c03c Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 21 Jan 2021 15:24:45 +0100 Subject: [PATCH] MOBILE-3659 core: Handle objects in nav params Also, always use CoreNavigator instead of Router or NavController --- .../pages/edit-event/edit-event.page.ts | 6 +- .../services/handlers/mediaplugin.ts | 1 - .../components/actions/actions.ts | 1 - .../notifications/pages/settings/settings.ts | 7 +- src/addons/privatefiles/pages/index/index.ts | 9 +-- src/app/app.component.test.ts | 9 ++- src/app/app.component.ts | 5 +- .../dynamic-component/dynamic-component.ts | 6 +- src/core/components/iframe/iframe.ts | 5 +- src/core/components/tabs/tabs.ts | 13 ++-- .../components/user-avatar/user-avatar.ts | 11 ++-- src/core/directives/format-text.ts | 17 ++--- src/core/directives/tests/format-text.test.ts | 3 +- src/core/directives/user-link.ts | 2 - .../pages/choose-site/choose-site.ts | 4 +- .../services/contentlinks-helper.ts | 10 +-- .../course/pages/contents/contents.ts | 29 ++++----- src/core/features/course/pages/index/index.ts | 27 ++++---- .../features/course/services/course-helper.ts | 5 -- src/core/features/course/services/course.ts | 3 +- .../services/handlers/default-format.ts | 4 +- .../course/services/module-delegate.ts | 1 - .../course-list-item/course-list-item.ts | 9 +-- .../courses/pages/categories/categories.ts | 3 +- .../pages/course-preview/course-preview.ts | 13 ++-- .../courses/pages/my-courses/my-courses.ts | 9 ++- .../login/pages/credentials/credentials.ts | 4 +- .../login/pages/email-signup/email-signup.ts | 8 +-- .../forgotten-password/forgotten-password.ts | 5 +- src/core/features/login/pages/init/init.ts | 4 +- .../login/pages/reconnect/reconnect.ts | 2 - .../login/pages/site-policy/site-policy.ts | 4 +- src/core/features/login/pages/site/site.ts | 8 +-- .../features/login/services/login-helper.ts | 30 +++------ .../features/login/tests/pages/init.test.ts | 21 ++---- src/core/features/mainmenu/pages/menu/menu.ts | 11 ++-- .../features/settings/pages/about/about.ts | 6 +- .../features/sitehome/pages/index/index.ts | 6 +- .../features/tag/pages/index/index.page.ts | 7 +- .../features/tag/pages/search/search.page.ts | 3 +- .../user/pages/profile/profile.page.ts | 9 ++- src/core/features/user/services/user.ts | 8 ++- src/core/services/navigator.ts | 64 ++++++++++++++++++- src/core/services/utils/iframe.ts | 19 ++---- src/core/singletons/window.ts | 7 +- 45 files changed, 204 insertions(+), 234 deletions(-) diff --git a/src/addons/calendar/pages/edit-event/edit-event.page.ts b/src/addons/calendar/pages/edit-event/edit-event.page.ts index 2b97b36fc..42f0cc90e 100644 --- a/src/addons/calendar/pages/edit-event/edit-event.page.ts +++ b/src/addons/calendar/pages/edit-event/edit-event.page.ts @@ -14,7 +14,7 @@ import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { IonRefresher, NavController } from '@ionic/angular'; +import { IonRefresher } from '@ionic/angular'; import { CoreEvents } from '@singletons/events'; import { CoreGroup, CoreGroups } from '@services/groups'; import { CoreSites } from '@services/sites'; @@ -43,6 +43,7 @@ import { CoreFilterHelper } from '@features/filter/services/filter-helper'; import { ActivatedRoute } from '@angular/router'; import { AddonCalendarOfflineEventDBRecord } from '../../services/database/calendar-offline'; import { CoreError } from '@classes/errors/error'; +import { CoreNavigator } from '@services/navigator'; /** * Page that displays a form to create/edit an event. @@ -90,7 +91,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { protected gotEventData = false; constructor( - protected navCtrl: NavController, protected route: ActivatedRoute, protected fb: FormBuilder, ) { @@ -578,7 +578,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { this.originalData = CoreUtils.instance.clone(this.form.value); } else {*/ this.originalData = undefined; // Avoid asking for confirmation. - this.navCtrl.pop(); + CoreNavigator.instance.back(); } /** diff --git a/src/addons/filter/mediaplugin/services/handlers/mediaplugin.ts b/src/addons/filter/mediaplugin/services/handlers/mediaplugin.ts index 552df81e3..9d3712100 100644 --- a/src/addons/filter/mediaplugin/services/handlers/mediaplugin.ts +++ b/src/addons/filter/mediaplugin/services/handlers/mediaplugin.ts @@ -61,7 +61,6 @@ export class AddonFilterMediaPluginHandlerService extends CoreFilterDefaultHandl * Treat video filters. Currently only treating youtube video using video JS. * * @param el Video element. - * @param navCtrl NavController to use. */ protected treatVideoFilters(video: HTMLElement): void { // Treat Video JS Youtube video links and translate them to iframes. diff --git a/src/addons/notifications/components/actions/actions.ts b/src/addons/notifications/components/actions/actions.ts index cd0ceb35b..8a4a4e58a 100644 --- a/src/addons/notifications/components/actions/actions.ts +++ b/src/addons/notifications/components/actions/actions.ts @@ -74,7 +74,6 @@ export class AddonNotificationsActionsComponent implements OnInit { * Default action. Open in browser. * * @param siteId Site ID to use. - * @param navCtrl NavController. */ protected async openInBrowser(siteId?: string): Promise { const url = this.data?.appurl || this.contextUrl; diff --git a/src/addons/notifications/pages/settings/settings.ts b/src/addons/notifications/pages/settings/settings.ts index fd24867d5..f93df756a 100644 --- a/src/addons/notifications/pages/settings/settings.ts +++ b/src/addons/notifications/pages/settings/settings.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, OnInit, OnDestroy } from '@angular/core'; -import { IonRefresher, NavController } from '@ionic/angular'; +import { IonRefresher } from '@ionic/angular'; import { CoreConfig } from '@services/config'; import { CoreLocalNotifications } from '@services/local-notifications'; @@ -61,10 +61,7 @@ export class AddonNotificationsSettingsPage implements OnInit, OnDestroy { protected updateTimeout?: number; - constructor( - protected navCtrl: NavController, - // @Optional() protected svComponent: CoreSplitViewComponent, - ) { + constructor() { // @todo @Optional() protected svComponent: CoreSplitViewComponent, this.notifPrefsEnabled = AddonNotifications.instance.isNotificationPreferencesEnabled(); this.canChangeSound = CoreLocalNotifications.instance.canDisableSound(); } diff --git a/src/addons/privatefiles/pages/index/index.ts b/src/addons/privatefiles/pages/index/index.ts index c377e7cfe..3d2312722 100644 --- a/src/addons/privatefiles/pages/index/index.ts +++ b/src/addons/privatefiles/pages/index/index.ts @@ -14,7 +14,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { IonRefresher, NavController } from '@ionic/angular'; +import { IonRefresher } from '@ionic/angular'; import { Md5 } from 'ts-md5/dist/md5'; import { CoreApp } from '@services/app'; @@ -32,6 +32,7 @@ import { } from '@/addons/privatefiles/services/privatefiles'; import { AddonPrivateFilesHelper } from '@/addons/privatefiles/services/privatefiles-helper'; import { CoreUtils } from '@services/utils/utils'; +import { CoreNavigator } from '@services/navigator'; /** * Page that displays the list of files. @@ -60,7 +61,6 @@ export class AddonPrivateFilesIndexPage implements OnInit, OnDestroy { constructor( protected route: ActivatedRoute, - protected navCtrl: NavController, ) { // Update visibility if current site info is updated. this.updateSiteObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => { @@ -254,10 +254,7 @@ export class AddonPrivateFilesIndexPage implements OnInit, OnDestroy { const hash = Md5.hashAsciiStr(JSON.stringify(params)); - this.navCtrl.navigateForward([`../${hash}`], { - relativeTo: this.route, - queryParams: params, - }); + CoreNavigator.instance.navigate(`../${hash}`, { params }); } /** diff --git a/src/app/app.component.test.ts b/src/app/app.component.test.ts index 216b99dfe..88a874411 100644 --- a/src/app/app.component.test.ts +++ b/src/app/app.component.test.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Observable } from 'rxjs'; -import { NavController } from '@ionic/angular'; import { AppComponent } from '@/app/app.component'; import { CoreApp } from '@services/app'; @@ -22,11 +21,12 @@ import { CoreLangProvider } from '@services/lang'; import { Network, Platform, NgZone } from '@singletons'; import { mock, mockSingleton, renderComponent, RenderConfig } from '@/testing/utils'; +import { CoreNavigator, CoreNavigatorService } from '@services/navigator'; describe('AppComponent', () => { let langProvider: CoreLangProvider; - let navController: NavController; + let navigator: CoreNavigatorService; let config: Partial; beforeEach(() => { @@ -35,12 +35,11 @@ describe('AppComponent', () => { mockSingleton(Platform, { ready: () => Promise.resolve() }); mockSingleton(NgZone, { run: jest.fn() }); + navigator = mockSingleton(CoreNavigator, ['navigate']); langProvider = mock(['clearCustomStrings']); - navController = mock(['navigateRoot']); config = { providers: [ { provide: CoreLangProvider, useValue: langProvider }, - { provide: NavController, useValue: navController }, ], }; }); @@ -59,7 +58,7 @@ describe('AppComponent', () => { CoreEvents.trigger(CoreEvents.LOGOUT); expect(langProvider.clearCustomStrings).toHaveBeenCalled(); - expect(navController.navigateRoot).toHaveBeenCalledWith('/login/sites'); + expect(navigator.navigate).toHaveBeenCalledWith('/login/sites', { reset: true }); }); it.todo('shows loading while app isn\'t ready'); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5b8f9f36b..68ce542f6 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Component, OnInit } from '@angular/core'; -import { NavController } from '@ionic/angular'; import { CoreLangProvider } from '@services/lang'; import { CoreLoginHelperProvider } from '@features/login/services/login-helper'; @@ -27,6 +26,7 @@ import { import { Network, NgZone, Platform } from '@singletons'; import { CoreApp } from '@services/app'; import { CoreSites } from '@services/sites'; +import { CoreNavigator } from '@services/navigator'; @Component({ selector: 'app-root', @@ -37,7 +37,6 @@ export class AppComponent implements OnInit { constructor( protected langProvider: CoreLangProvider, - protected navCtrl: NavController, protected loginHelper: CoreLoginHelperProvider, ) { } @@ -56,7 +55,7 @@ export class AppComponent implements OnInit { ngOnInit(): void { CoreEvents.on(CoreEvents.LOGOUT, () => { // Go to sites page when user is logged out. - this.navCtrl.navigateRoot('/login/sites'); + CoreNavigator.instance.navigate('/login/sites', { reset: true }); // Unload lang custom strings. this.langProvider.clearCustomStrings(); diff --git a/src/core/components/dynamic-component/dynamic-component.ts b/src/core/components/dynamic-component/dynamic-component.ts index 943ec0a4c..b4ffa9f6d 100644 --- a/src/core/components/dynamic-component/dynamic-component.ts +++ b/src/core/components/dynamic-component/dynamic-component.ts @@ -24,12 +24,10 @@ import { KeyValueDiffers, SimpleChange, ChangeDetectorRef, - Optional, ElementRef, KeyValueDiffer, Type, } from '@angular/core'; -import { NavController } from '@ionic/angular'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreLogger } from '@singletons/logger'; @@ -57,7 +55,7 @@ import { CoreLogger } from '@singletons/logger'; * * Alternatively, you can also supply a ComponentRef instead of the class of the component. In this case, the component won't * be instantiated because it already is, it will be attached to the view and the right data will be passed to it. - * Passing ComponentRef is meant for site plugins, so we'll inject a NavController instance to the component. + * Passing ComponentRef is meant for site plugins. * * The contents of this component will be displayed if no component is supplied or it cannot be created. In the example above, * if no component is supplied then the template will show the message "Cannot render the data.". @@ -90,7 +88,6 @@ export class CoreDynamicComponent implements OnChanges, DoCheck { constructor( protected factoryResolver: ComponentFactoryResolver, differs: KeyValueDiffers, - @Optional() protected navCtrl: NavController, protected cdr: ChangeDetectorRef, protected element: ElementRef, ) { @@ -167,7 +164,6 @@ export class CoreDynamicComponent implements OnChanges, DoCheck { // This feature is usually meant for site plugins. Inject some properties. this.instance['ChangeDetectorRef'] = this.cdr; - this.instance['NavController'] = this.navCtrl; this.instance['componentContainer'] = this.element.nativeElement; } else { try { diff --git a/src/core/components/iframe/iframe.ts b/src/core/components/iframe/iframe.ts index c3fa0e8b2..d934b4174 100644 --- a/src/core/components/iframe/iframe.ts +++ b/src/core/components/iframe/iframe.ts @@ -16,7 +16,6 @@ import { Component, Input, Output, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange, } from '@angular/core'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; -import { NavController } from '@ionic/angular'; import { CoreFile } from '@services/file'; import { CoreDomUtils } from '@services/utils/dom'; @@ -47,7 +46,6 @@ export class CoreIframeComponent implements OnChanges { constructor( protected sanitizer: DomSanitizer, - protected navCtrl: NavController, ) { this.logger = CoreLogger.getInstance('CoreIframe'); @@ -77,7 +75,8 @@ export class CoreIframeComponent implements OnChanges { this.loading = !this.src || !CoreUrlUtils.instance.isLocalFileUrl(this.src); // @todo const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; - CoreIframeUtils.instance.treatFrame(iframe, false, this.navCtrl); + // CoreIframeUtils.instance.treatFrame(iframe, false, this.navCtrl); + CoreIframeUtils.instance.treatFrame(iframe, false); iframe.addEventListener('load', () => { this.loading = false; diff --git a/src/core/components/tabs/tabs.ts b/src/core/components/tabs/tabs.ts index e05fc8939..a8eb31188 100644 --- a/src/core/components/tabs/tabs.ts +++ b/src/core/components/tabs/tabs.ts @@ -24,18 +24,18 @@ import { ViewChild, ElementRef, } from '@angular/core'; -import { Platform, IonSlides, IonTabs, NavController } from '@ionic/angular'; +import { Platform, IonSlides, IonTabs } from '@ionic/angular'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; import { CoreApp } from '@services/app'; import { CoreConfig } from '@services/config'; import { CoreConstants } from '@/core/constants'; import { CoreUtils } from '@services/utils/utils'; -import { NavigationOptions } from '@ionic/angular/providers/nav-controller'; import { Params } from '@angular/router'; import { CoreNavBarButtonsComponent } from '../navbar-buttons/navbar-buttons'; import { CoreDomUtils } from '@services/utils/dom'; import { StackEvent } from '@ionic/angular/directives/navigation/stack-utils'; +import { CoreNavigator } from '@services/navigator'; /** * This component displays some top scrollable tabs that will autohide on vertical scroll. @@ -114,7 +114,6 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges, OnDe protected element: ElementRef, platform: Platform, translate: TranslateService, - protected navCtrl: NavController, ) { this.direction = platform.isRTL ? 'rtl' : 'ltr'; @@ -594,11 +593,9 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges, OnDe await this.slides!.slideTo(index); } - const pageParams: NavigationOptions = {}; - if (tabToSelect.pageParams) { - pageParams.queryParams = tabToSelect.pageParams; - } - const ok = await this.navCtrl.navigateForward(tabToSelect.page, pageParams); + const ok = await CoreNavigator.instance.navigate(tabToSelect.page, { + params: tabToSelect.pageParams, + }); if (ok !== false) { this.selectHistory.push(tabToSelect.id!); diff --git a/src/core/components/user-avatar/user-avatar.ts b/src/core/components/user-avatar/user-avatar.ts index 2e8348a76..533af9cc5 100644 --- a/src/core/components/user-avatar/user-avatar.ts +++ b/src/core/components/user-avatar/user-avatar.ts @@ -14,14 +14,13 @@ import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChange } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { NavController } from '@ionic/angular'; import { CoreApp } from '@services/app'; import { CoreSites } from '@services/sites'; import { CoreUtils } from '@services/utils/utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; -import { CoreObject } from '@singletons/object'; import { CoreUserProvider, CoreUserBasicData, CoreUserProfilePictureUpdatedData } from '@features/user/services/user'; +import { CoreNavigator } from '@services/navigator'; /** * Component to display a "user avatar". @@ -54,7 +53,6 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { protected pictureObserver: CoreEventObserver; constructor( - protected navCtrl: NavController, protected route: ActivatedRoute, ) { this.currentUserId = CoreSites.instance.getCurrentSiteUserId(); @@ -143,12 +141,11 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy { event.stopPropagation(); // @todo Decide which navCtrl to use. If this component is inside a split view, use the split view's master nav. - this.navCtrl.navigateForward(['user'], { - relativeTo: this.route, - queryParams: CoreObject.withoutEmpty({ + CoreNavigator.instance.navigateToSitePath('user', { + params: { userId: this.userId, courseId: this.courseId, - }), + }, }); } diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts index 53574a3e5..67b19b95c 100644 --- a/src/core/directives/format-text.ts +++ b/src/core/directives/format-text.ts @@ -23,7 +23,7 @@ import { Optional, ViewContainerRef, } from '@angular/core'; -import { NavController, IonContent } from '@ionic/angular'; +import { IonContent } from '@ionic/angular'; import { CoreEventLoadingChangedData, CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; @@ -84,7 +84,6 @@ export class CoreFormatTextDirective implements OnChanges { constructor( element: ElementRef, - @Optional() protected navCtrl: NavController, @Optional() protected content: IonContent, protected viewContainerRef: ViewContainerRef, ) { @@ -471,7 +470,8 @@ export class CoreFormatTextDirective implements OnChanges { */ protected async treatHTMLElements(div: HTMLElement, site?: CoreSite): Promise { const canTreatVimeo = site?.isVersionGreaterEqualThan(['3.3.4', '3.4']) || false; - const navCtrl = this.navCtrl; // @todo this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; + // @todo this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; + // @todo: Pass navCtrl to all treateFrame calls? const images = Array.from(div.querySelectorAll('img')); const anchors = Array.from(div.querySelectorAll('a')); @@ -521,7 +521,7 @@ export class CoreFormatTextDirective implements OnChanges { }); iframes.forEach((iframe) => { - this.treatIframe(iframe, site, canTreatVimeo, navCtrl); + this.treatIframe(iframe, site, canTreatVimeo); }); svgImages.forEach((image) => { @@ -554,7 +554,7 @@ export class CoreFormatTextDirective implements OnChanges { // Handle all kind of frames. frames.forEach((frame: HTMLFrameElement | HTMLObjectElement | HTMLEmbedElement) => { - CoreIframeUtils.instance.treatFrame(frame, false, navCtrl); + CoreIframeUtils.instance.treatFrame(frame, false); }); CoreDomUtils.instance.handleBootstrapTooltips(div); @@ -671,13 +671,11 @@ export class CoreFormatTextDirective implements OnChanges { * @param iframe Iframe to treat. * @param site Site instance. * @param canTreatVimeo Whether Vimeo videos can be treated in the site. - * @param navCtrl NavController to use. */ protected async treatIframe( iframe: HTMLIFrameElement, site: CoreSite | undefined, canTreatVimeo: boolean, - navCtrl: NavController, ): Promise { const src = iframe.src; const currentSite = CoreSites.instance.getCurrentSite(); @@ -689,8 +687,7 @@ export class CoreFormatTextDirective implements OnChanges { const finalUrl = await currentSite.getAutoLoginUrl(src, false); iframe.src = finalUrl; - - CoreIframeUtils.instance.treatFrame(iframe, false, navCtrl); + CoreIframeUtils.instance.treatFrame(iframe, false); return; } @@ -751,7 +748,7 @@ export class CoreFormatTextDirective implements OnChanges { } } - CoreIframeUtils.instance.treatFrame(iframe, false, navCtrl); + CoreIframeUtils.instance.treatFrame(iframe, false); } /** diff --git a/src/core/directives/tests/format-text.test.ts b/src/core/directives/tests/format-text.test.ts index 7d2e8d4fd..498d3fb79 100644 --- a/src/core/directives/tests/format-text.test.ts +++ b/src/core/directives/tests/format-text.test.ts @@ -13,7 +13,7 @@ // limitations under the License. import { DomSanitizer } from '@angular/platform-browser'; -import { IonContent, NavController } from '@ionic/angular'; +import { IonContent } from '@ionic/angular'; import { NgZone } from '@angular/core'; import Faker from 'faker'; @@ -44,7 +44,6 @@ describe('CoreFormatTextDirective', () => { config = { providers: [ - { provide: NavController, useValue: null }, { provide: IonContent, useValue: null }, ], }; diff --git a/src/core/directives/user-link.ts b/src/core/directives/user-link.ts index b8b2cf2c1..cd256b9bb 100644 --- a/src/core/directives/user-link.ts +++ b/src/core/directives/user-link.ts @@ -14,7 +14,6 @@ import { Directive, Input, OnInit, ElementRef } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { NavController } from '@ionic/angular'; import { CoreNavigator } from '@services/navigator'; import { CoreObject } from '@singletons/object'; @@ -34,7 +33,6 @@ export class CoreUserLinkDirective implements OnInit { constructor( element: ElementRef, - protected navCtrl: NavController, protected route: ActivatedRoute, ) { this.element = element.nativeElement; diff --git a/src/core/features/contentlinks/pages/choose-site/choose-site.ts b/src/core/features/contentlinks/pages/choose-site/choose-site.ts index 2a95d6e56..a753f98ee 100644 --- a/src/core/features/contentlinks/pages/choose-site/choose-site.ts +++ b/src/core/features/contentlinks/pages/choose-site/choose-site.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Component, OnInit } from '@angular/core'; -import { NavController } from '@ionic/angular'; import { CoreSiteBasicInfo, CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { Translate } from '@singletons'; @@ -42,7 +41,6 @@ export class CoreContentLinksChooseSitePage implements OnInit { constructor( route: ActivatedRoute, - protected navCtrl: NavController, ) { this.url = route.snapshot.queryParamMap.get('url')!; } @@ -115,7 +113,7 @@ export class CoreContentLinksChooseSitePage implements OnInit { try { await CoreSites.instance.logout(); } finally { - await this.navCtrl.navigateRoot('/login/sites'); + await CoreNavigator.instance.navigate('/login/sites', { reset: true }); } } diff --git a/src/core/features/contentlinks/services/contentlinks-helper.ts b/src/core/features/contentlinks/services/contentlinks-helper.ts index f789767d7..a198a1fa9 100644 --- a/src/core/features/contentlinks/services/contentlinks-helper.ts +++ b/src/core/features/contentlinks/services/contentlinks-helper.ts @@ -20,6 +20,7 @@ import { CoreContentLinksDelegate, CoreContentLinksAction } from './contentlinks import { CoreSite } from '@classes/site'; import { makeSingleton, Translate } from '@singletons'; import { CoreNavigator } from '@services/navigator'; +import { Params } from '@angular/router'; /** * Service that provides some features regarding content links. @@ -27,10 +28,6 @@ import { CoreNavigator } from '@services/navigator'; @Injectable({ providedIn: 'root' }) export class CoreContentLinksHelperProvider { - constructor( - protected navCtrl: NavController, - ) { } - /** * Check whether a link can be handled by the app. * @@ -93,8 +90,7 @@ export class CoreContentLinksHelperProvider { * @return Promise resolved when done. * @deprecated since 3.9.5. Use CoreNavigator.navigateToSitePath instead. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - async goInSite(navCtrl: NavController, pageName: string, pageParams: any, siteId?: string): Promise { + async goInSite(navCtrl: NavController, pageName: string, pageParams: Params, siteId?: string): Promise { await CoreNavigator.instance.navigateToSitePath(pageName, { params: pageParams, siteId }); } @@ -105,7 +101,7 @@ export class CoreContentLinksHelperProvider { * @todo set correct root. */ async goToChooseSite(url: string): Promise { - await this.navCtrl.navigateRoot('CoreContentLinksChooseSitePage @todo', { queryParams: { url } }); + await CoreNavigator.instance.navigate('CoreContentLinksChooseSitePage @todo', { params: { url }, reset: true }); } /** diff --git a/src/core/features/course/pages/contents/contents.ts b/src/core/features/course/pages/contents/contents.ts index 1c5d2f9dc..a3781a086 100644 --- a/src/core/features/course/pages/contents/contents.ts +++ b/src/core/features/course/pages/contents/contents.ts @@ -13,8 +13,7 @@ // limitations under the License. import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { IonContent, IonRefresher, NavController } from '@ionic/angular'; +import { IonContent, IonRefresher } from '@ionic/angular'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; @@ -45,7 +44,7 @@ import { CoreEventCourseStatusChanged, CoreEventCompletionModuleViewedData, } from '@singletons/events'; -import { CoreNavHelper } from '@services/nav-helper'; +import { CoreNavigator } from '@services/navigator'; /** * Page that displays the contents of a course. @@ -84,28 +83,24 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { protected syncObserver?: CoreEventObserver; protected isDestroyed = false; - constructor( - protected route: ActivatedRoute, - protected navCtrl: NavController, - ) { } - /** * Component being initialized. */ async ngOnInit(): Promise { - // Get params. - this.course = this.route.snapshot.queryParams['course']; - this.sectionId = this.route.snapshot.queryParams['sectionId']; - this.sectionNumber = this.route.snapshot.queryParams['sectionNumber']; - this.moduleId = this.route.snapshot.queryParams['moduleId']; + const course = CoreNavigator.instance.getRouteParam('course'); - if (!this.course) { + if (!course) { CoreDomUtils.instance.showErrorModal('Missing required course parameter.'); - this.navCtrl.pop(); + CoreNavigator.instance.back(); return; } + this.course = course; + this.sectionId = CoreNavigator.instance.getRouteParam('sectionId'); + this.sectionNumber = CoreNavigator.instance.getRouteParam('sectionNumber'); + this.moduleId = CoreNavigator.instance.getRouteParam('moduleId'); + this.displayEnableDownload = !CoreSites.instance.getCurrentSite()?.isOfflineDisabled() && CoreCourseFormatDelegate.instance.displayEnableDownload(this.course); this.downloadCourseEnabled = !CoreCourses.instance.isDownloadCourseDisabledInSite(); @@ -466,7 +461,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { * Open the course summary */ openCourseSummary(): void { - CoreNavHelper.instance.goInCurrentMainMenuTab('/courses/preview', { course: this.course, avoidOpenCourse: true }); + CoreNavigator.instance.navigateToSitePath('/courses/preview', { params: { course: this.course, avoidOpenCourse: true } }); } /** @@ -476,7 +471,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy { */ openMenuItem(item: CoreCourseOptionsMenuHandlerToDisplay): void { const params = Object.assign({ course: this.course }, item.data.pageParams); - CoreNavHelper.instance.goInCurrentMainMenuTab(item.data.page, params); + CoreNavigator.instance.navigateToSitePath(item.data.page, { params }); } /** diff --git a/src/core/features/course/pages/index/index.ts b/src/core/features/course/pages/index/index.ts index 8c2a4e525..3d1c8b93f 100644 --- a/src/core/features/course/pages/index/index.ts +++ b/src/core/features/course/pages/index/index.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, ViewChild, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute, Params } from '@angular/router'; +import { Params } from '@angular/router'; import { CoreTab, CoreTabsComponent } from '@components/tabs/tabs'; import { CoreCourseFormatDelegate } from '../../services/format-delegate'; @@ -24,8 +24,7 @@ import { CoreCourse, CoreCourseWSModule } from '@features/course/services/course import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CoreUtils } from '@services/utils/utils'; import { CoreTextUtils } from '@services/utils/text'; -import { CoreNavHelper } from '@services/nav-helper'; -import { CoreObject } from '@singletons/object'; +import { CoreNavigator } from '@services/navigator'; /** * Page that displays the list of courses the user is enrolled in. @@ -52,9 +51,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy { pageParams: {}, }; - constructor( - protected route: ActivatedRoute, - ) { + constructor() { this.selectTabObserver = CoreEvents.on(CoreEvents.SELECT_COURSE_TAB, (data) => { if (!data.name) { // If needed, set sectionId and sectionNumber. They'll only be used if the content tabs hasn't been loaded yet. @@ -82,18 +79,18 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy { */ async ngOnInit(): Promise { // Get params. - this.course = this.route.snapshot.queryParams['course']; - this.firstTabName = this.route.snapshot.queryParams['selectedTab']; - const module: CoreCourseWSModule | undefined = this.route.snapshot.queryParams['module']; - const modParams: Params | undefined = this.route.snapshot.queryParams['modParams']; + this.course = CoreNavigator.instance.getRouteParam('course'); + this.firstTabName = CoreNavigator.instance.getRouteParam('selectedTab'); + const module = CoreNavigator.instance.getRouteParam('module'); + const modParams = CoreNavigator.instance.getRouteParam('modParams'); - this.currentPagePath = CoreNavHelper.instance.getCurrentPage(); + this.currentPagePath = CoreNavigator.instance.getCurrentPath(); this.contentsTab.page = CoreTextUtils.instance.concatenatePaths(this.currentPagePath, this.contentsTab.page); - this.contentsTab.pageParams = CoreObject.removeUndefined({ + this.contentsTab.pageParams = { course: this.course, - sectionId: this.route.snapshot.queryParams['sectionId'], - sectionNumber: this.route.snapshot.queryParams['sectionNumber'], - }); + sectionId: CoreNavigator.instance.getRouteParam('sectionId'), + sectionNumber: CoreNavigator.instance.getRouteParam('sectionNumber'), + }; if (module) { this.contentsTab.pageParams!.moduleId = module.id; diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts index 59b891e57..fdf595792 100644 --- a/src/core/features/course/services/course-helper.ts +++ b/src/core/features/course/services/course-helper.ts @@ -1389,8 +1389,6 @@ export class CoreCourseHelperProvider { * @param useModNameToGetModule If true, the app will retrieve all modules of this type with a single WS call. This reduces the * number of WS calls, but it isn't recommended for modules that can return a lot of contents. * @param modParams Params to pass to the module - * @param navCtrl NavController for adding new pages to the current history. Optional for legacy support, but - * generates a warning if omitted. * @return Promise resolved when done. */ navigateToModuleByInstance(): void { @@ -1407,8 +1405,6 @@ export class CoreCourseHelperProvider { * @param modName If set, the app will retrieve all modules of this type with a single WS call. This reduces the * number of WS calls, but it isn't recommended for modules that can return a lot of contents. * @param modParams Params to pass to the module - * @param navCtrl NavController for adding new pages to the current history. Optional for legacy support, but - * generates a warning if omitted. * @return Promise resolved when done. */ navigateToModule(): void { @@ -1418,7 +1414,6 @@ export class CoreCourseHelperProvider { /** * Open a module. * - * @param navCtrl The NavController to use. * @param module The module to open. * @param courseId The course ID of the module. * @param sectionId The section ID of the module. diff --git a/src/core/features/course/services/course.ts b/src/core/features/course/services/course.ts index 0d8abe2eb..989682fe6 100644 --- a/src/core/features/course/services/course.ts +++ b/src/core/features/course/services/course.ts @@ -133,9 +133,8 @@ export class CoreCourseProvider { } /** - * Check if the current view in a NavController is a certain course initial page. + * Check if the current view is a certain course initial page. * - * @param navCtrl NavController. * @param courseId Course ID. * @return Whether the current view is a certain course. */ diff --git a/src/core/features/course/services/handlers/default-format.ts b/src/core/features/course/services/handlers/default-format.ts index bd1c287b8..f77d8f7df 100644 --- a/src/core/features/course/services/handlers/default-format.ts +++ b/src/core/features/course/services/handlers/default-format.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { Params } from '@angular/router'; import { CoreCourseAnyCourseData, CoreCourses } from '@features/courses/services/courses'; -import { CoreNavHelper } from '@services/nav-helper'; +import { CoreNavigator } from '@services/navigator'; import { CoreUtils } from '@services/utils/utils'; import { CoreCourseWSSection } from '../course'; import { CoreCourseSection } from '../course-helper'; @@ -177,7 +177,7 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler { Object.assign(params, { course: course }); // Don't return the .push promise, we don't want to display a loading modal during the page transition. - CoreNavHelper.instance.goInCurrentMainMenuTab('course', params); + CoreNavigator.instance.navigateToSitePath('course', { params }); } /** diff --git a/src/core/features/course/services/module-delegate.ts b/src/core/features/course/services/module-delegate.ts index 0a333258b..e8559eb9f 100644 --- a/src/core/features/course/services/module-delegate.ts +++ b/src/core/features/course/services/module-delegate.ts @@ -226,7 +226,6 @@ export interface CoreCourseModuleHandlerButton { * Action to perform when the button is clicked. * * @param event The click event. - * @param navCtrl NavController instance. * @param module The module object. * @param courseId The course ID. */ diff --git a/src/core/features/courses/components/course-list-item/course-list-item.ts b/src/core/features/courses/components/course-list-item/course-list-item.ts index 6598fd2e2..0ec9214c0 100644 --- a/src/core/features/courses/components/course-list-item/course-list-item.ts +++ b/src/core/features/courses/components/course-list-item/course-list-item.ts @@ -14,7 +14,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { CoreCourseHelper } from '@features/course/services/course-helper'; -import { NavController } from '@ionic/angular'; +import { CoreNavigator } from '@services/navigator'; import { CoreCourses, CoreCourseSearchedData } from '../../services/courses'; import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '../../services/courses-helper'; @@ -40,11 +40,6 @@ export class CoreCoursesCourseListItemComponent implements OnInit { icons: CoreCoursesEnrolmentIcons[] = []; isEnrolled = false; - constructor( - protected navCtrl: NavController, - ) { - } - /** * Component being initialized. */ @@ -99,7 +94,7 @@ export class CoreCoursesCourseListItemComponent implements OnInit { if (this.isEnrolled) { CoreCourseHelper.instance.openCourse(this.course); } else { - this.navCtrl.navigateForward('/main/home/courses/preview', { queryParams: { course: this.course } }); + CoreNavigator.instance.navigate('courses/preview', { params: { course: this.course } }); } } diff --git a/src/core/features/courses/pages/categories/categories.ts b/src/core/features/courses/pages/categories/categories.ts index 2926c94f6..38790ea75 100644 --- a/src/core/features/courses/pages/categories/categories.ts +++ b/src/core/features/courses/pages/categories/categories.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, OnInit } from '@angular/core'; -import { IonRefresher, NavController } from '@ionic/angular'; +import { IonRefresher } from '@ionic/angular'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; @@ -39,7 +39,6 @@ export class CoreCoursesCategoriesPage implements OnInit { protected categoryId = 0; constructor( - protected navCtrl: NavController, protected route: ActivatedRoute, ) { this.title = Translate.instance.instant('core.courses.categories'); diff --git a/src/core/features/courses/pages/course-preview/course-preview.ts b/src/core/features/courses/pages/course-preview/course-preview.ts index 2bde270cd..855d05326 100644 --- a/src/core/features/courses/pages/course-preview/course-preview.ts +++ b/src/core/features/courses/pages/course-preview/course-preview.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, OnDestroy, NgZone, OnInit } from '@angular/core'; -import { ModalController, IonRefresher, NavController } from '@ionic/angular'; +import { ModalController, IonRefresher } from '@ionic/angular'; import { CoreApp } from '@services/app'; import { CoreEventCourseStatusChanged, CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; @@ -31,9 +31,9 @@ import { CoreCourseOptionsDelegate } from '@features/course/services/course-opti import { CoreCourse, CoreCourseProvider } from '@features/course/services/course'; import { CoreCourseHelper, CorePrefetchStatusInfo } from '@features/course/services/course-helper'; import { Translate } from '@singletons'; -import { ActivatedRoute } from '@angular/router'; import { CoreConstants } from '@/core/constants'; import { CoreCoursesSelfEnrolPasswordComponent } from '../../components/self-enrol-password/self-enrol-password'; +import { CoreNavigator } from '@services/navigator'; /** * Page that allows "previewing" a course and enrolling in it if enabled and not enrolled. @@ -78,8 +78,6 @@ export class CoreCoursesCoursePreviewPage implements OnInit, OnDestroy { constructor( protected modalCtrl: ModalController, protected zone: NgZone, - protected route: ActivatedRoute, - protected navCtrl: NavController, ) { this.isMobile = CoreApp.instance.isMobile(); this.downloadCourseEnabled = !CoreCourses.instance.isDownloadCourseDisabledInSite(); @@ -98,12 +96,11 @@ export class CoreCoursesCoursePreviewPage implements OnInit, OnDestroy { * View loaded. */ async ngOnInit(): Promise { - const navParams = this.route.snapshot.queryParams; - this.course = navParams['course']; - this.avoidOpenCourse = !!navParams['avoidOpenCourse']; + this.course = CoreNavigator.instance.getRouteParam('course'); + this.avoidOpenCourse = !!CoreNavigator.instance.getRouteParam('avoidOpenCourse'); if (!this.course) { - this.navCtrl.back(); + CoreNavigator.instance.back(); return; } diff --git a/src/core/features/courses/pages/my-courses/my-courses.ts b/src/core/features/courses/pages/my-courses/my-courses.ts index 015dc4212..0e95d0a58 100644 --- a/src/core/features/courses/pages/my-courses/my-courses.ts +++ b/src/core/features/courses/pages/my-courses/my-courses.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; -import { NavController, IonSearchbar, IonRefresher } from '@ionic/angular'; +import { IonSearchbar, IonRefresher } from '@ionic/angular'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; @@ -26,6 +26,7 @@ import { CoreCoursesHelper, CoreEnrolledCourseDataWithExtraInfoAndOptions } from import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CoreConstants } from '@/core/constants'; import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate'; +import { CoreNavigator } from '@services/navigator'; /** * Page that displays the list of courses the user is enrolled in. @@ -54,9 +55,7 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy { protected isDestroyed = false; protected courseIds = ''; - constructor( - protected navCtrl: NavController, - ) { + constructor() { // Update list if user enrols in a course. this.myCoursesObserver = CoreEvents.on( CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, @@ -200,7 +199,7 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy { * Go to search courses. */ openSearch(): void { - this.navCtrl.navigateForward(['/main/home/courses/search']); + CoreNavigator.instance.navigate('courses/search'); } /** diff --git a/src/core/features/login/pages/credentials/credentials.ts b/src/core/features/login/pages/credentials/credentials.ts index 34cb74c81..98565da9e 100644 --- a/src/core/features/login/pages/credentials/credentials.ts +++ b/src/core/features/login/pages/credentials/credentials.ts @@ -15,7 +15,6 @@ import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { NavController } from '@ionic/angular'; import { CoreApp } from '@services/app'; import { CoreSites } from '@services/sites'; @@ -63,7 +62,6 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy { constructor( protected fb: FormBuilder, protected route: ActivatedRoute, - protected navCtrl: NavController, ) { const canScanQR = CoreUtils.instance.canScanQR(); @@ -251,7 +249,7 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy { CoreLoginHelper.instance.treatUserTokenError(siteUrl, error, username, password); if (error.loggedout) { - this.navCtrl.navigateRoot('/login/sites'); + CoreNavigator.instance.navigate('/login/sites', { reset: true }); } else if (error.errorcode == 'forcepasswordchangenotice') { // Reset password field. this.credForm.controls.password.reset(); diff --git a/src/core/features/login/pages/email-signup/email-signup.ts b/src/core/features/login/pages/email-signup/email-signup.ts index ba3d36dae..494e10bc3 100644 --- a/src/core/features/login/pages/email-signup/email-signup.ts +++ b/src/core/features/login/pages/email-signup/email-signup.ts @@ -15,7 +15,7 @@ import { Component, ViewChild, ElementRef, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; -import { NavController, IonContent, IonRefresher } from '@ionic/angular'; +import { IonContent, IonRefresher } from '@ionic/angular'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; @@ -32,6 +32,7 @@ import { AuthEmailSignupSettings, CoreLoginHelper, } from '@features/login/services/login-helper'; +import { CoreNavigator } from '@services/navigator'; /** * Page to signup using email. @@ -80,7 +81,6 @@ export class CoreLoginEmailSignupPage implements OnInit { namefieldsErrors?: Record>; constructor( - protected navCtrl: NavController, protected fb: FormBuilder, protected route: ActivatedRoute, ) { @@ -238,7 +238,7 @@ export class CoreLoginEmailSignupPage implements OnInit { { $a: Translate.instance.instant('core.login.auth_email') }, ), ); - this.navCtrl.pop(); + CoreNavigator.instance.back(); return false; } @@ -321,7 +321,7 @@ export class CoreLoginEmailSignupPage implements OnInit { // Show alert and ho back. const message = Translate.instance.instant('core.login.emailconfirmsent', { $a: params.email }); CoreDomUtils.instance.showAlert(Translate.instance.instant('core.success'), message); - this.navCtrl.pop(); + CoreNavigator.instance.back(); } else { if (result.warnings && result.warnings.length) { let error = result.warnings[0].message; diff --git a/src/core/features/login/pages/forgotten-password/forgotten-password.ts b/src/core/features/login/pages/forgotten-password/forgotten-password.ts index d42e63054..61896d2bf 100644 --- a/src/core/features/login/pages/forgotten-password/forgotten-password.ts +++ b/src/core/features/login/pages/forgotten-password/forgotten-password.ts @@ -15,12 +15,12 @@ import { Component, ViewChild, ElementRef, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { NavController } from '@ionic/angular'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreLoginHelper } from '@features/login/services/login-helper'; import { Translate, Platform } from '@singletons'; import { CoreWSExternalWarning } from '@services/ws'; +import { CoreNavigator } from '@services/navigator'; /** * Page to recover a forgotten password. @@ -38,7 +38,6 @@ export class CoreLoginForgottenPasswordPage implements OnInit { autoFocus!: boolean; constructor( - protected navCtrl: NavController, protected formBuilder: FormBuilder, protected route: ActivatedRoute, ) { @@ -97,7 +96,7 @@ export class CoreLoginForgottenPasswordPage implements OnInit { CoreDomUtils.instance.triggerFormSubmittedEvent(this.formElement, true); CoreDomUtils.instance.showAlert(Translate.instance.instant('core.success'), response.notice); - this.navCtrl.pop(); + CoreNavigator.instance.back(); } } catch (error) { CoreDomUtils.instance.showErrorModal(error); diff --git a/src/core/features/login/pages/init/init.ts b/src/core/features/login/pages/init/init.ts index 096dda7a0..98bd7d68a 100644 --- a/src/core/features/login/pages/init/init.ts +++ b/src/core/features/login/pages/init/init.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Component, OnInit } from '@angular/core'; -import { NavController } from '@ionic/angular'; import { CoreApp, CoreRedirectData } from '@services/app'; import { ApplicationInit, SplashScreen } from '@singletons'; @@ -34,7 +33,6 @@ export class CoreLoginInitPage implements OnInit { // @todo this page should be removed in favor of native splash // or a splash component rendered in the root app component - constructor(protected navCtrl: NavController) {} /** * Initialize the component. @@ -121,7 +119,7 @@ export class CoreLoginInitPage implements OnInit { return; } - await this.navCtrl.navigateRoot('/login/sites'); + await CoreNavigator.instance.navigate('/login/sites', { reset: true }); } } diff --git a/src/core/features/login/pages/reconnect/reconnect.ts b/src/core/features/login/pages/reconnect/reconnect.ts index 24204a371..73c079e87 100644 --- a/src/core/features/login/pages/reconnect/reconnect.ts +++ b/src/core/features/login/pages/reconnect/reconnect.ts @@ -15,7 +15,6 @@ import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { ActivatedRoute, Params } from '@angular/router'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { NavController } from '@ionic/angular'; import { CoreApp } from '@services/app'; import { CoreSites } from '@services/sites'; @@ -60,7 +59,6 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { protected eventThrown = false; constructor( - protected navCtrl: NavController, protected fb: FormBuilder, protected route: ActivatedRoute, ) { diff --git a/src/core/features/login/pages/site-policy/site-policy.ts b/src/core/features/login/pages/site-policy/site-policy.ts index 15af5cce8..9612378e2 100644 --- a/src/core/features/login/pages/site-policy/site-policy.ts +++ b/src/core/features/login/pages/site-policy/site-policy.ts @@ -14,7 +14,6 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { NavController } from '@ionic/angular'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; @@ -40,7 +39,6 @@ export class CoreLoginSitePolicyPage implements OnInit { protected currentSite?: CoreSite; constructor( - protected navCtrl: NavController, protected route: ActivatedRoute, ) { } @@ -111,7 +109,7 @@ export class CoreLoginSitePolicyPage implements OnInit { async cancel(): Promise { await CoreUtils.instance.ignoreErrors(CoreSites.instance.logout()); - await this.navCtrl.navigateRoot('/login/sites'); + await CoreNavigator.instance.navigate('/login/sites', { reset: true }); } /** diff --git a/src/core/features/login/pages/site/site.ts b/src/core/features/login/pages/site/site.ts index 326b6f648..6d4086d29 100644 --- a/src/core/features/login/pages/site/site.ts +++ b/src/core/features/login/pages/site/site.ts @@ -15,7 +15,6 @@ import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { FormBuilder, FormGroup, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms'; -import { NavController } from '@ionic/angular'; import { CoreApp } from '@services/app'; import { CoreConfig } from '@services/config'; @@ -62,7 +61,6 @@ export class CoreLoginSitePage implements OnInit { constructor( protected route: ActivatedRoute, protected formBuilder: FormBuilder, - protected navCtrl: NavController, ) { let url = ''; @@ -336,7 +334,7 @@ export class CoreLoginSitePage implements OnInit { CoreLoginHelper.instance.treatUserTokenError(siteData.url, error, siteData.username, siteData.password); if (error.loggedout) { - this.navCtrl.navigateRoot('/login/sites'); + CoreNavigator.instance.navigate('/login/sites', { reset: true }); } } finally { modal.dismiss(); @@ -375,8 +373,8 @@ export class CoreLoginSitePage implements OnInit { pageParams['logoUrl'] = foundSite.imageurl; } - this.navCtrl.navigateForward('/login/credentials', { - queryParams: pageParams, + CoreNavigator.instance.navigate('/login/credentials', { + params: pageParams, }); } } diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index dade42d2e..5bcf17d22 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -33,7 +33,6 @@ import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CoreUrl } from '@singletons/url'; -import { CoreObject } from '@singletons/object'; import { CoreNavigator } from '@services/navigator'; /** @@ -57,9 +56,7 @@ export class CoreLoginHelperProvider { protected isOpeningReconnect = false; waitingForBrowser = false; - constructor( - protected navCtrl: NavController, - ) { + constructor() { this.logger = CoreLogger.getInstance('CoreLoginHelper'); } @@ -178,8 +175,8 @@ export class CoreLoginHelperProvider { const canReset = await this.canRequestPasswordReset(siteUrl); if (canReset) { - await this.navCtrl.navigateForward(['/login/forgottenpassword'], { - queryParams: { + await CoreNavigator.instance.navigate('/login/forgottenpassword', { + params: { siteUrl, username, }, @@ -426,15 +423,7 @@ export class CoreLoginHelperProvider { params = { showKeyboard: showKeyboard }; } - if (setRoot) { - await this.navCtrl.navigateRoot(pageRoute, { - queryParams: params, - }); - } else { - await this.navCtrl.navigateForward(pageRoute, { - queryParams: params, - }); - } + await CoreNavigator.instance.navigate(pageRoute, { params, reset: setRoot }); } /** @@ -791,7 +780,7 @@ export class CoreLoginHelperProvider { return; } - await this.navCtrl.navigateRoot('/login/changepassword', { queryParams: { siteId } }); + await CoreNavigator.instance.navigate('/login/changepassword', { params: { siteId }, reset: true }); } /** @@ -982,12 +971,13 @@ export class CoreLoginHelperProvider { this.isOpeningReconnect = true; - await CoreUtils.instance.ignoreErrors(this.navCtrl.navigateRoot('/login/reconnect', { - queryParams: CoreObject.withoutEmpty({ + await CoreUtils.instance.ignoreErrors(CoreNavigator.instance.navigate('/login/reconnect', { + params: { siteId, pageName: data.pageName, pageParams: data.params, - }), + }, + reset: true, })); this.isOpeningReconnect = false; @@ -1148,7 +1138,7 @@ export class CoreLoginHelperProvider { return; } - this.navCtrl.navigateRoot('/login/sitepolicy', { queryParams: { siteId: siteId } }); + CoreNavigator.instance.navigate('/login/sitepolicy', { params: { siteId }, reset: true }); } /** diff --git a/src/core/features/login/tests/pages/init.test.ts b/src/core/features/login/tests/pages/init.test.ts index fd135d94c..8124299a8 100644 --- a/src/core/features/login/tests/pages/init.test.ts +++ b/src/core/features/login/tests/pages/init.test.ts @@ -12,19 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { NavController } from '@ionic/angular'; - import { CoreApp } from '@services/app'; import { CoreLoginInitPage } from '@features/login/pages/init/init'; import { CoreSites } from '@services/sites'; import { ApplicationInit, SplashScreen } from '@singletons'; -import { mock, mockSingleton, renderComponent, RenderConfig } from '@/testing/utils'; +import { mockSingleton, renderComponent } from '@/testing/utils'; +import { CoreNavigator, CoreNavigatorService } from '@services/navigator'; describe('CoreLoginInitPage', () => { - let navController: NavController; - let config: Partial; + let navigator: CoreNavigatorService; beforeEach(() => { mockSingleton(CoreApp, { getRedirect: () => ({}) }); @@ -32,28 +30,23 @@ describe('CoreLoginInitPage', () => { mockSingleton(CoreSites, { isLoggedIn: () => false }); mockSingleton(SplashScreen, ['hide']); - navController = mock(['navigateRoot']); - config = { - providers: [ - { provide: NavController, useValue: navController }, - ], - }; + navigator = mockSingleton(CoreNavigator, ['navigate']); }); it('should render', async () => { - const fixture = await renderComponent(CoreLoginInitPage, config); + const fixture = await renderComponent(CoreLoginInitPage, {}); expect(fixture.debugElement.componentInstance).toBeTruthy(); expect(fixture.nativeElement.querySelector('ion-spinner')).toBeTruthy(); }); it('navigates to sites page after loading', async () => { - const fixture = await renderComponent(CoreLoginInitPage, config); + const fixture = await renderComponent(CoreLoginInitPage, {}); fixture.componentInstance.ngOnInit(); await ApplicationInit.instance.donePromise; - expect(navController.navigateRoot).toHaveBeenCalledWith('/login/sites'); + expect(navigator.navigate).toHaveBeenCalledWith('/login/sites', { reset: true }); }); }); diff --git a/src/core/features/mainmenu/pages/menu/menu.ts b/src/core/features/mainmenu/pages/menu/menu.ts index d0298183f..66a48045f 100644 --- a/src/core/features/mainmenu/pages/menu/menu.ts +++ b/src/core/features/mainmenu/pages/menu/menu.ts @@ -14,7 +14,7 @@ import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { NavController, IonTabs } from '@ionic/angular'; +import { IonTabs } from '@ionic/angular'; import { Subscription } from 'rxjs'; import { CoreApp } from '@services/app'; @@ -25,7 +25,7 @@ import { CoreMainMenu, CoreMainMenuProvider } from '../../services/mainmenu'; import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../services/mainmenu-delegate'; import { CoreDomUtils } from '@services/utils/dom'; import { Translate } from '@singletons'; -import { CoreRedirectPayload } from '@services/navigator'; +import { CoreNavigator, CoreRedirectPayload } from '@services/navigator'; /** * Page that displays the main menu of the app. @@ -55,7 +55,6 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { constructor( protected route: ActivatedRoute, - protected navCtrl: NavController, protected changeDetector: ChangeDetectorRef, protected router: Router, ) {} @@ -66,7 +65,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { ngOnInit(): void { // @TODO this should be handled by route guards and can be removed if (!CoreSites.instance.isLoggedIn()) { - this.navCtrl.navigateRoot('/login/init'); + CoreNavigator.instance.navigate('/login/init', { reset: true }); return; } @@ -191,8 +190,8 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { if (i >= 0) { // Tab found. Open it with the params. - this.navCtrl.navigateForward(data.redirectPath, { - queryParams: data.redirectParams, + CoreNavigator.instance.navigate(data.redirectPath, { + params: data.redirectParams, animated: false, }); } else { diff --git a/src/core/features/settings/pages/about/about.ts b/src/core/features/settings/pages/about/about.ts index ca4546ebf..aaa5937ee 100644 --- a/src/core/features/settings/pages/about/about.ts +++ b/src/core/features/settings/pages/about/about.ts @@ -13,10 +13,11 @@ // limitations under the License. import { Component } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { CoreConstants } from '@/core/constants'; import { CoreSites } from '@services/sites'; +import { CoreNavigator } from '@services/navigator'; /** * App settings about menu page. @@ -32,7 +33,6 @@ export class CoreSettingsAboutPage { privacyPolicy: string; constructor( - protected router: Router, protected route: ActivatedRoute, ) { const currentSite = CoreSites.instance.getCurrentSite(); @@ -53,7 +53,7 @@ export class CoreSettingsAboutPage { openPage(page: string): void { // const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; // navCtrl.push(page); - this.router.navigate([page], { relativeTo: this.route }); + CoreNavigator.instance.navigate(page); } } diff --git a/src/core/features/sitehome/pages/index/index.ts b/src/core/features/sitehome/pages/index/index.ts index 0f3a7b29d..0378e19cc 100644 --- a/src/core/features/sitehome/pages/index/index.ts +++ b/src/core/features/sitehome/pages/index/index.ts @@ -14,7 +14,7 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { IonRefresher, NavController } from '@ionic/angular'; +import { IonRefresher } from '@ionic/angular'; import { CoreSite, CoreSiteConfig } from '@classes/site'; import { CoreCourse, CoreCourseModuleBasicInfo, CoreCourseWSSection } from '@features/course/services/course'; @@ -27,6 +27,7 @@ import { CoreCourseHelper } from '@features/course/services/course-helper'; import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks'; import { CoreCourseModuleDelegate, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; +import { CoreNavigator } from '@services/navigator'; /** * Page that displays site home index. @@ -59,7 +60,6 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { constructor( protected route: ActivatedRoute, - protected navCtrl: NavController, ) {} /** @@ -224,7 +224,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy { * Go to search courses. */ openSearch(): void { - this.navCtrl.navigateForward(['/main/home/courses/search']); + CoreNavigator.instance.navigateToSitePath('courses/search'); } /** diff --git a/src/core/features/tag/pages/index/index.page.ts b/src/core/features/tag/pages/index/index.page.ts index 3dc89b254..fc2c91fce 100644 --- a/src/core/features/tag/pages/index/index.page.ts +++ b/src/core/features/tag/pages/index/index.page.ts @@ -20,6 +20,7 @@ import { CoreTag } from '@features/tag/services/tag'; import { CoreTagAreaDelegate } from '@features/tag/services/tag-area-delegate'; import { ActivatedRoute, Router } from '@angular/router'; import { CoreTagFeedElement } from '../../services/tag-helper'; +import { CoreNavigator } from '@services/navigator'; /** * Page that displays the tag index. @@ -169,11 +170,7 @@ export class CoreTagIndexPage implements OnInit { nextPage: 1, }; // this.splitviewCtrl.push('index-area', params); - this.router.navigate(['../index-area'], { - queryParams: params, - relativeTo: this.route, - }); - + CoreNavigator.instance.navigate('../index-area', { params }); } } diff --git a/src/core/features/tag/pages/search/search.page.ts b/src/core/features/tag/pages/search/search.page.ts index ee1b80aac..fdd202825 100644 --- a/src/core/features/tag/pages/search/search.page.ts +++ b/src/core/features/tag/pages/search/search.page.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, OnInit } from '@angular/core'; -import { IonRefresher, NavController } from '@ionic/angular'; +import { IonRefresher } from '@ionic/angular'; import { ActivatedRoute } from '@angular/router'; import { CoreApp } from '@services/app'; @@ -42,7 +42,6 @@ export class CoreTagSearchPage implements OnInit { searching = false; constructor( - protected navCtrl: NavController, protected route: ActivatedRoute, ) { diff --git a/src/core/features/user/pages/profile/profile.page.ts b/src/core/features/user/pages/profile/profile.page.ts index a932a4645..cf2cbe056 100644 --- a/src/core/features/user/pages/profile/profile.page.ts +++ b/src/core/features/user/pages/profile/profile.page.ts @@ -14,7 +14,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { IonRefresher, NavController } from '@ionic/angular'; +import { IonRefresher } from '@ionic/angular'; import { Subscription } from 'rxjs'; import { CoreSite } from '@classes/site'; @@ -35,6 +35,7 @@ import { CoreUserDelegate, CoreUserDelegateService, CoreUserProfileHandlerData } import { CoreFileUploaderHelper } from '@features/fileuploader/services/fileuploader-helper'; import { CoreIonLoadingElement } from '@classes/ion-loading'; import { CoreUtils } from '@services/utils/utils'; +import { CoreNavigator } from '@services/navigator'; @Component({ selector: 'page-core-user-profile', @@ -63,7 +64,6 @@ export class CoreUserProfilePage implements OnInit, OnDestroy { constructor( protected route: ActivatedRoute, - protected navCtrl: NavController, ) { this.obsProfileRefreshed = CoreEvents.on(CoreUserProvider.PROFILE_REFRESHED, (data) => { @@ -257,9 +257,8 @@ export class CoreUserProfilePage implements OnInit, OnDestroy { */ openUserDetails(): void { // @todo: Navigate out of split view if this page is in the right pane. - this.navCtrl.navigateForward(['../about'], { - relativeTo: this.route, - queryParams: { + CoreNavigator.instance.navigate('../about', { + params: { courseId: this.courseId, userId: this.userId, }, diff --git a/src/core/features/user/services/user.ts b/src/core/features/user/services/user.ts index 260956e93..68e13eeae 100644 --- a/src/core/features/user/services/user.ts +++ b/src/core/features/user/services/user.ts @@ -257,8 +257,12 @@ export class CoreUserProvider { try { return await this.getUserFromWS(userId, courseId, siteId); - } catch { - return this.getUserFromLocalDb(userId, siteId); + } catch (error) { + try { + return await this.getUserFromLocalDb(userId, siteId); + } catch { + throw error; + } } } diff --git a/src/core/services/navigator.ts b/src/core/services/navigator.ts index af34f481d..aced30e4e 100644 --- a/src/core/services/navigator.ts +++ b/src/core/services/navigator.ts @@ -53,6 +53,9 @@ export type CoreNavigationOptions = { @Injectable({ providedIn: 'root' }) export class CoreNavigatorService { + protected storedParams: Record = {}; + protected lastParamId = 0; + /** * Check whether the active route is using the given path. * @@ -89,6 +92,10 @@ export class CoreNavigatorService { queryParams: CoreObject.isEmpty(options.params ?? {}) ? null : options.params, relativeTo: path.startsWith('/') ? null : this.getCurrentRoute(), }); + + // Remove objects from queryParams and replace them with an ID. + this.replaceObjectParams(navigationOptions.queryParams); + const navigationResult = (options.reset ?? false) ? await NavController.instance.navigateRoot(url, navigationOptions) : await NavController.instance.navigateForward(url, navigationOptions); @@ -183,10 +190,40 @@ export class CoreNavigatorService { * * @return Current path. */ - protected getCurrentPath(): string { + getCurrentPath(): string { return CoreUrlUtils.instance.removeUrlParams(Router.instance.url); } + /** + * Get a parameter for the current route. + * Please notice that objects can only be retrieved once. You must call this function only once per page and parameter, + * unless there's a new navigation to the page. + * + * @param name Name of the parameter. + * @return Value of the parameter, undefined if not found. + */ + getRouteParam(name: string): T | undefined { + const route = this.getCurrentRoute(); + const value = route.snapshot.queryParams[name] ?? route.snapshot.params[name]; + + const storedParam = this.storedParams[value]; + // Remove the parameter from our map if it's in there. + delete this.storedParams[value]; + + // @todo: Convert strings to number/boolean if needed? All number & boolean params are converted to string. + + return storedParam ?? value; + } + + /** + * Navigate back. + * + * @return Promise resolved when done. + */ + back(): Promise { + return NavController.instance.pop(); + } + /** * Get current activated route. * @@ -243,6 +280,31 @@ export class CoreNavigatorService { }); } + /** + * Replace all objects in query params with an ID that can be used to retrieve the object later. + * + * @param queryParams Params. + */ + protected replaceObjectParams(queryParams?: Params | null): void { + for (const name in queryParams) { + const value = queryParams[name]; + if (typeof value != 'object' || value === null) { + continue; + } + + const id = this.getNewParamId(); + this.storedParams[id] = value; + queryParams[name] = id; + } + } + + /** + * Get an ID for a new parameter. + */ + protected getNewParamId(): string { + return 'param-' + (++this.lastParamId); + } + } export class CoreNavigator extends makeSingleton(CoreNavigatorService) {} diff --git a/src/core/services/utils/iframe.ts b/src/core/services/utils/iframe.ts index 09a5f5de1..b2ee65307 100644 --- a/src/core/services/utils/iframe.ts +++ b/src/core/services/utils/iframe.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { NavController } from '@ionic/angular'; import { WKUserScriptWindow } from 'cordova-plugin-wkuserscript'; import { WKWebViewCookiesWindow } from 'cordova-plugin-wkwebview-cookies'; @@ -197,18 +196,16 @@ export class CoreIframeUtilsProvider { * @param element Element to treat (iframe, embed, ...). * @param contentWindow The window of the element contents. * @param contentDocument The document of the element contents. - * @param navCtrl NavController to use if a link can be opened in the app. */ redefineWindowOpen( element: CoreFrameElement, contentWindow: Window, contentDocument: Document, - navCtrl?: NavController, ): void { if (contentWindow) { // Intercept window.open. contentWindow.open = (url: string, name: string) => { - this.windowOpen(url, name, element, navCtrl); + this.windowOpen(url, name, element); // eslint-disable-next-line @typescript-eslint/no-explicit-any return null as any; @@ -220,7 +217,7 @@ export class CoreIframeUtilsProvider { CoreIframeUtilsProvider.FRAME_TAGS.forEach((tag) => { const elements = Array.from(contentDocument.querySelectorAll(tag)); elements.forEach((subElement: CoreFrameElement) => { - this.treatFrame(subElement, true, navCtrl); + this.treatFrame(subElement, true); }); }); } @@ -232,9 +229,8 @@ export class CoreIframeUtilsProvider { * * @param element Element to treat (iframe, embed, ...). * @param isSubframe Whether it's a frame inside another frame. - * @param navCtrl NavController to use if a link can be opened in the app. */ - treatFrame(element: CoreFrameElement, isSubframe?: boolean, navCtrl?: NavController): void { + treatFrame(element: CoreFrameElement, isSubframe?: boolean): void { if (!element) { return; } @@ -246,7 +242,7 @@ export class CoreIframeUtilsProvider { // Redefine window.open in this element and sub frames, it might have been loaded already. if (window && document) { - this.redefineWindowOpen(element, window, document, navCtrl); + this.redefineWindowOpen(element, window, document); } // Treat links. @@ -309,10 +305,9 @@ export class CoreIframeUtilsProvider { * @param url URL passed to window.open. * @param name Name passed to window.open. * @param element HTML element of the frame. - * @param navCtrl NavController to use if a link can be opened in the app. * @return Promise resolved when done. */ - protected async windowOpen(url: string, name: string, element?: CoreFrameElement, navCtrl?: NavController): Promise { + protected async windowOpen(url: string, name: string, element?: CoreFrameElement): Promise { const scheme = CoreUrlUtils.instance.getUrlScheme(url); if (!scheme) { // It's a relative URL, use the frame src to create the full URL. @@ -367,9 +362,7 @@ export class CoreIframeUtilsProvider { } } else { // It's an external link, check if it can be opened in the app. - await CoreWindow.open(url, name, { - navCtrl, - }); + await CoreWindow.open(url, name); } } diff --git a/src/core/singletons/window.ts b/src/core/singletons/window.ts index c3a5762e2..593cbcea6 100644 --- a/src/core/singletons/window.ts +++ b/src/core/singletons/window.ts @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { NavController } from '@ionic/angular'; + import { CoreFileHelper } from '@services/file-helper'; import { CoreSites } from '@services/sites'; import { CoreUrlUtils } from '@services/utils/url'; @@ -23,9 +25,10 @@ import { CoreUtils } from '@services/utils/utils'; export type CoreWindowOpenOptions = { /** * NavController to use when opening the link in the app. + * + * @deprecated since 3.9.5 */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - navCtrl?: any; // @todo NavController; + navCtrl?: NavController; }; /**