MOBILE-3659 core: Handle objects in nav params

Also, always use CoreNavigator instead of Router or NavController
main
Dani Palou 2021-01-21 15:24:45 +01:00
parent adf8a1f481
commit 9a935a3946
45 changed files with 204 additions and 234 deletions

View File

@ -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();
}
/**

View File

@ -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.

View File

@ -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<void> {
const url = <string> this.data?.appurl || this.contextUrl;

View File

@ -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();
}

View File

@ -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 = <string> Md5.hashAsciiStr(JSON.stringify(params));
this.navCtrl.navigateForward([`../${hash}`], {
relativeTo: this.route,
queryParams: params,
});
CoreNavigator.instance.navigate(`../${hash}`, { params });
}
/**

View File

@ -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<RenderConfig>;
beforeEach(() => {
@ -35,12 +35,11 @@ describe('AppComponent', () => {
mockSingleton(Platform, { ready: () => Promise.resolve() });
mockSingleton(NgZone, { run: jest.fn() });
navigator = mockSingleton(CoreNavigator, ['navigate']);
langProvider = mock<CoreLangProvider>(['clearCustomStrings']);
navController = mock<NavController>(['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');

View File

@ -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();

View File

@ -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 {

View File

@ -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;

View File

@ -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!);

View File

@ -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,
}),
},
});
}

View File

@ -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<void> {
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<void> {
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);
}
/**

View File

@ -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 },
],
};

View File

@ -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;

View File

@ -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 });
}
}

View File

@ -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<void> {
async goInSite(navCtrl: NavController, pageName: string, pageParams: Params, siteId?: string): Promise<void> {
await CoreNavigator.instance.navigateToSitePath(pageName, { params: pageParams, siteId });
}
@ -105,7 +101,7 @@ export class CoreContentLinksHelperProvider {
* @todo set correct root.
*/
async goToChooseSite(url: string): Promise<void> {
await this.navCtrl.navigateRoot('CoreContentLinksChooseSitePage @todo', { queryParams: { url } });
await CoreNavigator.instance.navigate('CoreContentLinksChooseSitePage @todo', { params: { url }, reset: true });
}
/**

View File

@ -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<void> {
// 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<CoreCourseAnyCourseData>('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 });
}
/**

View File

@ -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<CoreEventSelectCourseTabData>(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<void> {
// 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<CoreCourseWSModule>('module');
const modParams = CoreNavigator.instance.getRouteParam<Params>('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<number>('sectionId'),
sectionNumber: CoreNavigator.instance.getRouteParam<number>('sectionNumber'),
};
if (module) {
this.contentsTab.pageParams!.moduleId = module.id;

View File

@ -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.

View File

@ -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.
*/

View File

@ -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 });
}
/**

View File

@ -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.
*/

View File

@ -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 } });
}
}

View File

@ -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');

View File

@ -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<void> {
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;
}

View File

@ -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');
}
/**

View File

@ -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();

View File

@ -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<string, Record<string, string>>;
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;

View File

@ -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);

View File

@ -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 });
}
}

View File

@ -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,
) {

View File

@ -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<void> {
await CoreUtils.instance.ignoreErrors(CoreSites.instance.logout());
await this.navCtrl.navigateRoot('/login/sites');
await CoreNavigator.instance.navigate('/login/sites', { reset: true });
}
/**

View File

@ -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,
});
}
}

View File

@ -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 });
}
/**

View File

@ -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<RenderConfig>;
let navigator: CoreNavigatorService;
beforeEach(() => {
mockSingleton(CoreApp, { getRedirect: () => ({}) });
@ -32,28 +30,23 @@ describe('CoreLoginInitPage', () => {
mockSingleton(CoreSites, { isLoggedIn: () => false });
mockSingleton(SplashScreen, ['hide']);
navController = mock<NavController>(['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 });
});
});

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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');
}
/**

View File

@ -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 });
}
}

View File

@ -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,
) {

View File

@ -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<CoreUserProfileRefreshedData>(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,
},

View File

@ -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;
}
}
}

View File

@ -53,6 +53,9 @@ export type CoreNavigationOptions = {
@Injectable({ providedIn: 'root' })
export class CoreNavigatorService {
protected storedParams: Record<number, unknown> = {};
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<T = unknown>(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 <T> storedParam ?? value;
}
/**
* Navigate back.
*
* @return Promise resolved when done.
*/
back(): Promise<void> {
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) {}

View File

@ -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<void> {
protected async windowOpen(url: string, name: string, element?: CoreFrameElement): Promise<void> {
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);
}
}

View File

@ -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;
};
/**