diff --git a/src/addons/badges/pages/issued-badge/issued-badge.page.ts b/src/addons/badges/pages/issued-badge/issued-badge.page.ts index ea1412a1a..038dcc365 100644 --- a/src/addons/badges/pages/issued-badge/issued-badge.page.ts +++ b/src/addons/badges/pages/issued-badge/issued-badge.page.ts @@ -51,16 +51,12 @@ export class AddonBadgesIssuedBadgePage implements OnInit { * View loaded. */ ngOnInit(): void { - this.route.queryParams.subscribe(() => { - this.badgeLoaded = false; + this.courseId = CoreNavigator.getRouteNumberParam('courseId') || this.courseId; // Use 0 for site badges. + this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getCurrentSite()!.getUserId(); + this.badgeHash = CoreNavigator.getRouteParam('badgeHash') || ''; - this.courseId = CoreNavigator.getRouteNumberParam('courseId') || this.courseId; // Use 0 for site badges. - this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getCurrentSite()!.getUserId(); - this.badgeHash = CoreNavigator.getRouteParam('badgeHash') || ''; - - this.fetchIssuedBadge().finally(() => { - this.badgeLoaded = true; - }); + this.fetchIssuedBadge().finally(() => { + this.badgeLoaded = true; }); } diff --git a/src/addons/calendar/calendar-lazy.module.ts b/src/addons/calendar/calendar-lazy.module.ts index 8b7c40fc1..c8058bd01 100644 --- a/src/addons/calendar/calendar-lazy.module.ts +++ b/src/addons/calendar/calendar-lazy.module.ts @@ -24,7 +24,7 @@ export const AddonCalendarEditRoute: Route = { }; export const AddonCalendarEventRoute: Route ={ - path: 'event', + path: 'event/:id', loadChildren: () => import('@/addons/calendar/pages/event/event.module').then(m => m.AddonCalendarEventPageModule), }; diff --git a/src/addons/calendar/pages/day/day.page.ts b/src/addons/calendar/pages/day/day.page.ts index 8768f77e7..15f89e3ee 100644 --- a/src/addons/calendar/pages/day/day.page.ts +++ b/src/addons/calendar/pages/day/day.page.ts @@ -525,7 +525,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { // It's an offline event, go to the edit page. this.openEdit(eventId); } else { - CoreNavigator.navigateToSitePath('/calendar/event', { params: { id: eventId } }); + CoreNavigator.navigateToSitePath(`/calendar/event/${eventId}`); } } diff --git a/src/addons/calendar/pages/event/event.page.ts b/src/addons/calendar/pages/event/event.page.ts index b14e27981..e18344707 100644 --- a/src/addons/calendar/pages/event/event.page.ts +++ b/src/addons/calendar/pages/event/event.page.ts @@ -155,22 +155,10 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { * View loaded. */ ngOnInit(): void { - this.route.queryParams.subscribe(() => { - this.eventLoaded = false; + this.eventId = CoreNavigator.getRouteNumberParam('id')!; + this.syncIcon = CoreConstants.ICON_LOADING; - const eventId = CoreNavigator.getRouteNumberParam('id'); - if (!eventId) { - CoreDomUtils.showErrorModal('Event ID not supplied.'); - CoreNavigator.back(); - - return; - } - - this.eventId = eventId; - this.syncIcon = CoreConstants.ICON_LOADING; - - this.fetchEvent(); - }); + this.fetchEvent(); } /** diff --git a/src/addons/calendar/pages/index/index.page.ts b/src/addons/calendar/pages/index/index.page.ts index edc213f18..f5ae23d48 100644 --- a/src/addons/calendar/pages/index/index.page.ts +++ b/src/addons/calendar/pages/index/index.page.ts @@ -46,7 +46,6 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { @ViewChild(AddonCalendarCalendarComponent) calendarComponent?: AddonCalendarCalendarComponent; @ViewChild(AddonCalendarUpcomingEventsComponent) upcomingEventsComponent?: AddonCalendarUpcomingEventsComponent; - protected eventId?: number; protected currentSiteId: string; // Observers. @@ -167,8 +166,7 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { ngOnInit(): void { this.notificationsEnabled = CoreLocalNotifications.isAvailable(); - this.route.queryParams.subscribe(() => { - this.eventId = CoreNavigator.getRouteNumberParam('eventId'); + this.route.queryParams.subscribe(async () => { this.filter.courseId = CoreNavigator.getRouteNumberParam('courseId'); this.year = CoreNavigator.getRouteNumberParam('year'); this.month = CoreNavigator.getRouteNumberParam('month'); @@ -176,11 +174,6 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { this.showCalendar = !this.loadUpcoming; this.filter.filtered = !!this.filter.courseId; - if (this.eventId) { - // There is an event to load, open the event in a new state. - this.gotoEvent(this.eventId); - } - this.fetchData(true, false); }); } @@ -311,7 +304,7 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { // It's an offline event, go to the edit page. this.openEdit(eventId); } else { - CoreNavigator.navigateToSitePath('/calendar/event', { params: { id: eventId } }); + CoreNavigator.navigateToSitePath(`/calendar/event/${eventId}`); } } diff --git a/src/addons/calendar/pages/list/list.page.ts b/src/addons/calendar/pages/list/list.page.ts index 857f79293..27911a5b2 100644 --- a/src/addons/calendar/pages/list/list.page.ts +++ b/src/addons/calendar/pages/list/list.page.ts @@ -244,14 +244,7 @@ export class AddonCalendarListPage implements OnInit, OnDestroy { * View loaded. */ async ngOnInit(): Promise { - this.eventId = CoreNavigator.getRouteNumberParam('eventId'); this.filter.courseId = CoreNavigator.getRouteNumberParam('courseId') || -1; - - if (this.eventId) { - // There is an event to load, open the event in a new state. - this.gotoEvent(this.eventId); - } - this.syncIcon = CoreConstants.ICON_LOADING; await this.fetchData(false, true, false); diff --git a/src/addons/calendar/services/calendar.ts b/src/addons/calendar/services/calendar.ts index a16a3e4d1..c1a01b8ec 100644 --- a/src/addons/calendar/services/calendar.ts +++ b/src/addons/calendar/services/calendar.ts @@ -373,7 +373,14 @@ export class AddonCalendarProvider { CoreNavigator.navigateToSitePath( pageName, - { params: { eventId: notification.eventId }, siteId: notification.siteId }, + { + siteId: notification.siteId, + preferCurrentTab: false, + nextNavigation: { + path: `calendar/event/${notification.eventId}`, + isSitePath: true, + }, + }, ); } }, diff --git a/src/addons/calendar/services/handlers/view-link.ts b/src/addons/calendar/services/handlers/view-link.ts index 2faf2b177..e7a59019f 100644 --- a/src/addons/calendar/services/handlers/view-link.ts +++ b/src/addons/calendar/services/handlers/view-link.ts @@ -58,8 +58,11 @@ export class AddonCalendarViewLinkHandlerService extends CoreContentLinksHandler stateParams.year = date.getFullYear(); stateParams.month = date.getMonth() + 1; - // @todo: Add checkMenu param. - CoreNavigator.navigateToSitePath('/calendar/index', { params: stateParams, siteId }); + CoreNavigator.navigateToSitePath('/calendar/index', { + params: stateParams, + siteId, + preferCurrentTab: false, + }); } else if (params.view == 'day') { // Daily view, open the page. @@ -82,8 +85,11 @@ export class AddonCalendarViewLinkHandlerService extends CoreContentLinksHandler upcoming: true, }; - // @todo: Add checkMenu param. - CoreNavigator.navigateToSitePath('/calendar/index', { params: stateParams, siteId }); + CoreNavigator.navigateToSitePath('/calendar/index', { + params: stateParams, + siteId, + preferCurrentTab: false, + }); } }, diff --git a/src/addons/messages/components/conversation-info/conversation-info.ts b/src/addons/messages/components/conversation-info/conversation-info.ts index 7b877e732..0022f8083 100644 --- a/src/addons/messages/components/conversation-info/conversation-info.ts +++ b/src/addons/messages/components/conversation-info/conversation-info.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { IonRefresher } from '@ionic/angular'; import { AddonMessagesConversationFormatted, @@ -22,7 +22,6 @@ import { import { CoreDomUtils } from '@services/utils/dom'; import { ActivatedRoute } from '@angular/router'; import { ModalController } from '@singletons'; -import { CoreNavigator } from '@services/navigator'; /** * Component that displays the list of conversations, including group conversations. @@ -33,14 +32,14 @@ import { CoreNavigator } from '@services/navigator'; }) export class AddonMessagesConversationInfoComponent implements OnInit { + @Input() conversationId = 0; + loaded = false; conversation?: AddonMessagesConversationFormatted; members: AddonMessagesConversationMember[] = []; canLoadMore = false; loadMoreError = false; - protected conversationId!: number; - constructor( protected route: ActivatedRoute, ) { @@ -50,13 +49,8 @@ export class AddonMessagesConversationInfoComponent implements OnInit { * Component loaded. */ ngOnInit(): void { - this.route.queryParams.subscribe(async () => { - this.conversationId = CoreNavigator.getRouteNumberParam('conversationId') || 0; - - this.loaded = false; - this.fetchData().finally(() => { - this.loaded = true; - }); + this.fetchData().finally(() => { + this.loaded = true; }); } diff --git a/src/addons/messages/pages/contacts-35/contacts.page.ts b/src/addons/messages/pages/contacts-35/contacts.page.ts index 5d66baa9c..6ef6d90fe 100644 --- a/src/addons/messages/pages/contacts-35/contacts.page.ts +++ b/src/addons/messages/pages/contacts-35/contacts.page.ts @@ -84,42 +84,40 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy { /** * Component loaded. */ - ngOnInit(): void { - this.route.queryParams.subscribe(async () => { - const discussionUserId = CoreNavigator.getRouteNumberParam('discussionUserId') || - CoreNavigator.getRouteNumberParam('userId') || undefined; + async ngOnInit(): Promise { + const discussionUserId = CoreNavigator.getRouteNumberParam('discussionUserId') || + CoreNavigator.getRouteNumberParam('userId') || undefined; - if (this.loaded && this.discussionUserId == discussionUserId) { - return; - } + if (this.loaded && this.discussionUserId == discussionUserId) { + return; + } - this.discussionUserId = discussionUserId; + this.discussionUserId = discussionUserId; - if (this.discussionUserId) { - // There is a discussion to load, open the discussion in a new state. - this.gotoDiscussion(this.discussionUserId); - } + if (this.discussionUserId) { + // There is a discussion to load, open the discussion in a new state. + this.gotoDiscussion(this.discussionUserId); + } - try { - await this.fetchData(); - if (!this.discussionUserId && this.hasContacts && CoreScreen.isTablet) { - let contact: AddonMessagesGetContactsContact | undefined; - for (const x in this.contacts) { - if (this.contacts[x].length > 0) { - contact = this.contacts[x][0]; - break; - } - } - - if (contact) { - // Take first and load it. - this.gotoDiscussion(contact.id); + try { + await this.fetchData(); + if (!this.discussionUserId && this.hasContacts && CoreScreen.isTablet) { + let contact: AddonMessagesGetContactsContact | undefined; + for (const x in this.contacts) { + if (this.contacts[x].length > 0) { + contact = this.contacts[x][0]; + break; } } - } finally { - this.loaded = true; + + if (contact) { + // Take first and load it. + this.gotoDiscussion(contact.id); + } } - }); + } finally { + this.loaded = true; + } } /** diff --git a/src/addons/messages/pages/discussions-35/discussions.page.ts b/src/addons/messages/pages/discussions-35/discussions.page.ts index da4b4a5b7..b7b9d97d1 100644 --- a/src/addons/messages/pages/discussions-35/discussions.page.ts +++ b/src/addons/messages/pages/discussions-35/discussions.page.ts @@ -136,29 +136,18 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy { /** * Component loaded. */ - ngOnInit(): void { + async ngOnInit(): Promise { this.route.queryParams.subscribe(async (params) => { - const discussionUserId = CoreNavigator.getRouteNumberParam('discussionUserId', { params }) || - CoreNavigator.getRouteNumberParam('userId', { params }) || undefined; - - if (this.loaded && this.discussionUserId == discussionUserId) { - return; - } - - this.discussionUserId = discussionUserId; - - if (this.discussionUserId) { - // There is a discussion to load, open the discussion in a new state. - this.gotoDiscussion(this.discussionUserId); - } - - await this.fetchData(); - - if (!this.discussionUserId && this.discussions.length > 0 && CoreScreen.isTablet) { - // Take first and load it. - this.gotoDiscussion(this.discussions[0].message!.user); - } + // When a child page loads this callback is triggered too. + this.discussionUserId = CoreNavigator.getRouteNumberParam('userId', { params }) ?? this.discussionUserId; }); + + await this.fetchData(); + + if (!this.discussionUserId && this.discussions.length > 0 && CoreScreen.isTablet) { + // Take first and load it. + this.gotoDiscussion(this.discussions[0].message!.user); + } } /** diff --git a/src/addons/messages/pages/group-conversations/group-conversations.page.ts b/src/addons/messages/pages/group-conversations/group-conversations.page.ts index 6dbc9d9ca..3335ebce9 100644 --- a/src/addons/messages/pages/group-conversations/group-conversations.page.ts +++ b/src/addons/messages/pages/group-conversations/group-conversations.page.ts @@ -87,8 +87,6 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { protected siteId: string; protected currentUserId: number; - protected conversationId?: number; - protected discussionUserId?: number; protected newMessagesObserver: CoreEventObserver; protected pushObserver: Subscription; protected appResumeSubscription: Subscription; @@ -270,34 +268,30 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { /** * Component loaded. */ - ngOnInit(): void { + async ngOnInit(): Promise { this.route.queryParams.subscribe(async (params) => { - // Conversation to load. - this.conversationId = CoreNavigator.getRouteNumberParam('conversationId', { params }) || undefined; - if (!this.conversationId) { - this.discussionUserId = CoreNavigator.getRouteNumberParam('discussionUserId', { params }) || undefined; - } + // When a child page loads this callback is triggered too. + this.selectedConversationId = + CoreNavigator.getRouteNumberParam('conversationId', { params }) ?? this.selectedConversationId; + this.selectedUserId = + CoreNavigator.getRouteNumberParam('userId', { params }) ?? this.selectedUserId; + }); - if (this.conversationId || this.discussionUserId) { - // There is a discussion to load, open the discussion in a new state. - this.gotoConversation(this.conversationId, this.discussionUserId); - } + await this.fetchData(); - await this.fetchData(); - if (!this.conversationId && !this.discussionUserId && CoreScreen.isTablet) { - // Load the first conversation. - let conversation: AddonMessagesConversationForList; - const expandedOption = this.getExpandedOption(); + if (!this.selectedConversationId && !this.selectedUserId && CoreScreen.isTablet) { + // Load the first conversation. + let conversation: AddonMessagesConversationForList; + const expandedOption = this.getExpandedOption(); - if (expandedOption && expandedOption.conversations.length) { - conversation = expandedOption.conversations[0]; + if (expandedOption && expandedOption.conversations.length) { + conversation = expandedOption.conversations[0]; - if (conversation) { - this.gotoConversation(conversation.id); - } + if (conversation) { + this.gotoConversation(conversation.id); } } - }); + } } /** @@ -322,7 +316,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { await Promise.all(promises); // The expanded status hasn't been initialized. Do it now. - if (typeof this.favourites.expanded == 'undefined' && this.conversationId || this.discussionUserId) { + if (typeof this.favourites.expanded == 'undefined' && (this.selectedConversationId || this.selectedUserId)) { // A certain conversation should be opened. // We don't know which option it belongs to, so we need to fetch the data for all of them. const promises: Promise[] = []; @@ -333,7 +327,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { await Promise.all(promises); // All conversations have been loaded, find the one we need to load and expand its option. - const conversation = this.findConversation(this.conversationId, this.discussionUserId); + const conversation = this.findConversation(this.selectedConversationId, this.selectedUserId); if (conversation) { const option = this.getConversationOption(conversation); diff --git a/src/addons/messages/services/handlers/index-link.ts b/src/addons/messages/services/handlers/index-link.ts index f1336d7e8..e528bce36 100644 --- a/src/addons/messages/services/handlers/index-link.ts +++ b/src/addons/messages/services/handlers/index-link.ts @@ -39,7 +39,10 @@ export class AddonMessagesIndexLinkHandlerService extends CoreContentLinksHandle action: async (siteId): Promise => { const pageName = await AddonMessages.getMainMessagesPagePathInSite(siteId); - CoreNavigator.navigateToSitePath(pageName, { siteId }); + CoreNavigator.navigateToSitePath(pageName, { + siteId, + preferCurrentTab: false, + }); }, }]; } diff --git a/src/addons/messages/services/handlers/push-click.ts b/src/addons/messages/services/handlers/push-click.ts index a9d0f7682..4363fa90c 100644 --- a/src/addons/messages/services/handlers/push-click.ts +++ b/src/addons/messages/services/handlers/push-click.ts @@ -63,16 +63,29 @@ export class AddonMessagesPushClickHandlerService implements CorePushNotificatio const enabled = await AddonMessages.isGroupMessagingEnabledInSite(notification.site); const pageName = await AddonMessages.getMainMessagesPagePathInSite(notification.site); - const pageParams: Params = {}; + let nextPageParams: Params | undefined; // Check if we have enough information to open the conversation. if (notification.convid && enabled) { - pageParams.conversationId = Number(notification.convid); + nextPageParams = { + conversationId: Number(notification.convid), + }; } else if (notification.userfromid) { - pageParams.discussionUserId = Number(notification.userfromid); + nextPageParams = { + userId: Number(notification.userfromid), + }; } - await CoreNavigator.navigateToSitePath(pageName, { params: pageParams, siteId: notification.site }); + await CoreNavigator.navigateToSitePath(pageName, { + siteId: notification.site, + preferCurrentTab: false, + nextNavigation: nextPageParams ? + { + path: 'discussion', + options: { params: nextPageParams }, + } : + undefined, + }); } } diff --git a/src/addons/notifications/services/handlers/push-click.ts b/src/addons/notifications/services/handlers/push-click.ts index c9a467c0b..1a4457874 100644 --- a/src/addons/notifications/services/handlers/push-click.ts +++ b/src/addons/notifications/services/handlers/push-click.ts @@ -125,7 +125,10 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi await CoreNavigator.navigateToSitePath( AddonNotificationsMainMenuHandlerService.PAGE_NAME, - { siteId: notification.site }, + { + siteId: notification.site, + preferCurrentTab: false, + }, ); } diff --git a/src/core/classes/delegate-sorted.ts b/src/core/classes/delegate-sorted.ts index 3c65e3b73..41f9fdc5c 100644 --- a/src/core/classes/delegate-sorted.ts +++ b/src/core/classes/delegate-sorted.ts @@ -15,6 +15,7 @@ import { BehaviorSubject, Subject } from 'rxjs'; import { CoreEvents } from '@singletons/events'; import { CoreDelegate, CoreDelegateDisplayHandler, CoreDelegateToDisplay } from './delegate'; +import { CoreUtils } from '@services/utils/utils'; /** * Superclass to help creating sorted delegates. @@ -76,6 +77,30 @@ export class CoreSortedDelegate< return this.sortedHandlersRxJs; } + /** + * Get the handlers for the current site once they're loaded. + * + * @return Promise resolved with the handlers. + */ + async getHandlersWhenLoaded(): Promise { + if (this.loaded) { + return this.sortedHandlers; + } + + const deferred = CoreUtils.promiseDefer(); + + const subscription = this.getHandlersObservable().subscribe((handlers) => { + if (this.loaded) { + subscription?.unsubscribe(); + + // Return main handlers. + deferred.resolve(handlers); + } + }); + + return deferred.promise; + } + /** * Update handlers Data. */ diff --git a/src/core/features/grades/services/handlers/overview-link.ts b/src/core/features/grades/services/handlers/overview-link.ts index ddfd90351..307124802 100644 --- a/src/core/features/grades/services/handlers/overview-link.ts +++ b/src/core/features/grades/services/handlers/overview-link.ts @@ -36,7 +36,10 @@ export class CoreGradesOverviewLinkHandlerService extends CoreContentLinksHandle getActions(): CoreContentLinksAction[] | Promise { return [{ action: siteId => { - CoreNavigator.navigateToSitePath('/grades', { siteId }); + CoreNavigator.navigateToSitePath('/grades', { + siteId, + preferCurrentTab: false, + }); }, }]; } diff --git a/src/core/features/login/pages/reconnect/reconnect.ts b/src/core/features/login/pages/reconnect/reconnect.ts index c6ce18c5e..275b52e71 100644 --- a/src/core/features/login/pages/reconnect/reconnect.ts +++ b/src/core/features/login/pages/reconnect/reconnect.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; -import { Params } from '@angular/router'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { CoreApp } from '@services/app'; @@ -24,7 +23,7 @@ import { CoreLoginHelper } from '@features/login/services/login-helper'; import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/site'; import { CoreEvents } from '@singletons/events'; import { CoreError } from '@classes/errors/error'; -import { CoreNavigator } from '@services/navigator'; +import { CoreNavigationOptions, CoreNavigator, CoreRedirectPayload } from '@services/navigator'; import { CoreForms } from '@singletons/form'; /** @@ -55,7 +54,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { showScanQR = false; protected page?: string; - protected pageParams?: Params; + protected pageOptions?: CoreNavigationOptions; protected siteConfig?: CoreSitePublicConfigResponse; protected viewLeft = false; protected eventThrown = false; @@ -83,7 +82,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { this.siteUrl = siteId; this.page = CoreNavigator.getRouteParam('pageName'); - this.pageParams = CoreNavigator.getRouteParam('pageParams'); + this.pageOptions = CoreNavigator.getRouteParam('pageOptions'); this.showScanQR = CoreLoginHelper.displayQRInSiteScreen() || CoreLoginHelper.displayQRInCredentialsScreen(); try { @@ -214,8 +213,8 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { await CoreNavigator.navigateToSiteHome({ params: { redirectPath: this.page, - redirectParams: this.pageParams, - }, + redirectOptions: this.pageOptions, + } as CoreRedirectPayload, }); } catch (error) { CoreLoginHelper.treatUserTokenError(this.siteUrl, error, this.username, password); @@ -244,7 +243,15 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { * @param provider The provider that was clicked. */ oauthClicked(provider: CoreSiteIdentityProvider): void { - if (!CoreLoginHelper.openBrowserForOAuthLogin(this.siteUrl, provider, this.siteConfig?.launchurl)) { + const result = CoreLoginHelper.openBrowserForOAuthLogin( + this.siteUrl, + provider, + this.siteConfig?.launchurl, + this.page, + this.pageOptions, + ); + + if (!result) { CoreDomUtils.showErrorModal('Invalid data.'); } } diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index e52012c5d..8d24d8c98 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -32,7 +32,7 @@ import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CoreUrl } from '@singletons/url'; -import { CoreNavigator } from '@services/navigator'; +import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreCanceledError } from '@classes/errors/cancelederror'; import { CoreCustomURLSchemes } from '@services/urlschemes'; @@ -461,7 +461,7 @@ export class CoreLoginHelperProvider { ...options, params: { redirectPath: page, - redirectParams: params, + redirectOptions: { params }, urlToOpen: url, }, }); @@ -549,10 +549,10 @@ export class CoreLoginHelperProvider { * Check if current site is logged out, triggering mmCoreEventSessionExpired if it is. * * @param pageName Name of the page to go once authenticated if logged out. If not defined, site initial page. - * @param params Params of the page to go once authenticated if logged out. + * @param options Options of the page to go once authenticated if logged out. * @return True if user is logged out, false otherwise. */ - isSiteLoggedOut(pageName?: string, params?: Params): boolean { + isSiteLoggedOut(pageName?: string, options?: CoreNavigationOptions): boolean { const site = CoreSites.getCurrentSite(); if (!site) { return false; @@ -561,7 +561,7 @@ export class CoreLoginHelperProvider { if (site.isLoggedOut()) { CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, { pageName, - params, + options, }, site.getId()); return true; @@ -633,7 +633,7 @@ export class CoreLoginHelperProvider { * @param provider The identity provider. * @param launchUrl The URL to open for SSO. If not defined, tool/mobile launch URL will be used. * @param pageName Name of the page to go once authenticated. If not defined, site initial page. - * @param pageParams Params of the state to go once authenticated. + * @param pageOptions Options of the page to go once authenticated. * @return True if success, false if error. */ openBrowserForOAuthLogin( @@ -641,7 +641,7 @@ export class CoreLoginHelperProvider { provider: CoreSiteIdentityProvider, launchUrl?: string, pageName?: string, - pageParams?: Params, + pageOptions?: CoreNavigationOptions, ): boolean { launchUrl = launchUrl || siteUrl + '/admin/tool/mobile/launch.php'; if (!provider || !provider.url) { @@ -655,7 +655,7 @@ export class CoreLoginHelperProvider { } const service = CoreSites.determineService(siteUrl); - const loginUrl = this.prepareForSSOLogin(siteUrl, service, launchUrl, pageName, pageParams, { + const loginUrl = this.prepareForSSOLogin(siteUrl, service, launchUrl, pageName, pageOptions, { oauthsso: params.id, }); @@ -676,7 +676,7 @@ export class CoreLoginHelperProvider { * @param service The service to use. If not defined, external service will be used. * @param launchUrl The URL to open for SSO. If not defined, local_mobile launch URL will be used. * @param pageName Name of the page to go once authenticated. If not defined, site initial page. - * @param pageParams Params of the state to go once authenticated. + * @param pageOptions Options of the state to go once authenticated. */ openBrowserForSSOLogin( siteUrl: string, @@ -684,9 +684,9 @@ export class CoreLoginHelperProvider { service?: string, launchUrl?: string, pageName?: string, - pageParams?: Params, + pageOptions?: CoreNavigationOptions, ): void { - const loginUrl = this.prepareForSSOLogin(siteUrl, service, launchUrl, pageName, pageParams); + const loginUrl = this.prepareForSSOLogin(siteUrl, service, launchUrl, pageName, pageOptions); if (this.isSSOEmbeddedBrowser(typeOfLogin)) { CoreUtils.openInApp(loginUrl, { @@ -797,7 +797,7 @@ export class CoreLoginHelperProvider { * @param service The service to use. If not defined, external service will be used. * @param launchUrl The URL to open for SSO. If not defined, local_mobile launch URL will be used. * @param pageName Name of the page to go once authenticated. If not defined, site initial page. - * @param pageParams Params of the state to go once authenticated. + * @param pageOptions Options of the page to go once authenticated. * @param urlParams Other params to add to the URL. * @return Login Url. */ @@ -806,7 +806,7 @@ export class CoreLoginHelperProvider { service?: string, launchUrl?: string, pageName?: string, - pageParams?: Params, + pageOptions?: CoreNavigationOptions, urlParams?: CoreUrlParams, ): string { @@ -829,7 +829,7 @@ export class CoreLoginHelperProvider { siteUrl: siteUrl, passport: passport, pageName: pageName || '', - pageParams: pageParams || {}, + pageOptions: pageOptions || {}, ssoUrlParams: urlParams || {}, })); @@ -917,7 +917,7 @@ export class CoreLoginHelperProvider { result.service, result.config?.launchurl, data.pageName, - data.params, + data.options, ); } catch (error) { // User cancelled, logout him. @@ -955,7 +955,7 @@ export class CoreLoginHelperProvider { providerToUse, result.config?.launchurl, data.pageName, - data.params, + data.options, ); } catch (error) { // User cancelled, logout him. @@ -982,7 +982,7 @@ export class CoreLoginHelperProvider { params: { siteId, pageName: data.pageName, - pageParams: data.params, + pageOptions: data.options, }, reset: true, })); @@ -1213,7 +1213,7 @@ export class CoreLoginHelperProvider { token: params[1], privateToken: params[2], pageName: data.pageName, - pageParams: data.pageParams, + pageOptions: data.pageOptions, ssoUrlParams: data.ssoUrlParams, }; } else { @@ -1357,9 +1357,9 @@ export interface CoreLoginSSOData { pageName?: string; /** - * Params to page to the page. + * Options of the navigation to the page. */ - pageParams?: Params; + pageOptions?: CoreNavigationOptions; /** * Other params added to the login url. @@ -1450,6 +1450,6 @@ type StoredLoginLaunchData = { siteUrl: string; passport: number; pageName: string; - pageParams: Params; + pageOptions: CoreNavigationOptions; ssoUrlParams: CoreUrlParams; }; diff --git a/src/core/features/mainmenu/pages/home/home.ts b/src/core/features/mainmenu/pages/home/home.ts index 50fe9f604..d8c38e377 100644 --- a/src/core/features/mainmenu/pages/home/home.ts +++ b/src/core/features/mainmenu/pages/home/home.ts @@ -64,7 +64,7 @@ export class CoreMainMenuHomePage implements OnInit { if (params.redirectPath) { this.pendingRedirect = { redirectPath: params.redirectPath, - redirectParams: params.redirectParams, + redirectOptions: params.redirectOptions, }; } }); @@ -141,7 +141,7 @@ export class CoreMainMenuHomePage implements OnInit { * @param data Data received. */ protected handleRedirect(data: CoreRedirectPayload): void { - const params = data.redirectParams; + const params = data.redirectOptions?.params; const coursePathMatches = data.redirectPath.match(/^course\/(\d+)\/?$/); if (coursePathMatches) { @@ -152,7 +152,7 @@ export class CoreMainMenuHomePage implements OnInit { } } else { CoreNavigator.navigateToSitePath(data.redirectPath, { - params: data.redirectParams, + ...data.redirectOptions, preferCurrentTab: false, }); } diff --git a/src/core/features/mainmenu/services/mainmenu.ts b/src/core/features/mainmenu/services/mainmenu.ts index 681824249..76814de46 100644 --- a/src/core/features/mainmenu/services/mainmenu.ts +++ b/src/core/features/mainmenu/services/mainmenu.ts @@ -42,10 +42,10 @@ export class CoreMainMenuProvider { * * @return Promise resolved with the current main menu handlers. */ - getCurrentMainMenuHandlers(): CoreMainMenuHandlerToDisplay[] { - return CoreMainMenuDelegate.getHandlers() - .filter(handler => !handler.onlyInMore) - .slice(0, this.getNumItems()); + async getCurrentMainMenuHandlers(): Promise { + const handlers = await CoreMainMenuDelegate.getHandlersWhenLoaded(); + + return handlers.filter(handler => !handler.onlyInMore).slice(0, this.getNumItems()); } /** @@ -215,7 +215,11 @@ export class CoreMainMenuProvider { async isCurrentMainMenuHandler(pageName: string): Promise { const handlers = await this.getCurrentMainMenuHandlers(); - const handler = handlers.find((handler) => handler.page == pageName); + const handler = handlers.find((handler) => { + const tabRoot = /^[^/]+/.exec(handler.page)?.[0] ?? handler.page; + + return tabRoot == pageName; + }); return !!handler; } diff --git a/src/core/guards/redirect.ts b/src/core/guards/redirect.ts index 0f3fb750a..248518aaa 100644 --- a/src/core/guards/redirect.ts +++ b/src/core/guards/redirect.ts @@ -15,6 +15,7 @@ import { Injectable } from '@angular/core'; import { CanActivate, CanLoad, UrlTree } from '@angular/router'; import { CoreApp } from '@services/app'; +import { CoreRedirectPayload } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { Router } from '@singletons'; import { CoreConstants } from '../constants'; @@ -40,7 +41,7 @@ export class CoreRedirectGuard implements CanLoad, CanActivate { * Check if there is a pending redirect and trigger it. */ private async guard(): Promise { - const redirect = CoreApp.consumeRedirect(); + const redirect = CoreApp.consumeMemoryRedirect(); if (!redirect) { return true; @@ -56,14 +57,14 @@ export class CoreRedirectGuard implements CanLoad, CanActivate { const loggedIn = await CoreSites.loadSite( redirect.siteId, redirect.page, - redirect.params, + redirect.options, ); const route = Router.parseUrl('/main/home'); route.queryParams = { redirectPath: redirect.page, - redirectParams: redirect.params, - }; + redirectOptions: redirect.options, + } as CoreRedirectPayload; return loggedIn ? route : true; } @@ -78,8 +79,8 @@ export class CoreRedirectGuard implements CanLoad, CanActivate { route.queryParams = { redirectPath: redirect.page, - redirectParams: redirect.params, - }; + redirectOptions: redirect.options, + } as CoreRedirectPayload; return route; } diff --git a/src/core/services/app.ts b/src/core/services/app.ts index 7a5365563..5045ad68f 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { Params } from '@angular/router'; import { CoreDB } from '@services/db'; import { CoreEvents } from '@singletons/events'; @@ -25,6 +24,7 @@ import { CoreLogger } from '@singletons/logger'; import { CoreColors } from '@singletons/colors'; import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/database/app'; import { CoreObject } from '@singletons/object'; +import { CoreNavigationOptions } from './navigator'; /** * Object responsible of managing schema versions. @@ -554,7 +554,7 @@ export class CoreAppProvider { * * @return Redirect data if any. */ - consumeRedirect(): CoreRedirectData | null { + consumeMemoryRedirect(): CoreRedirectData | null { const redirect = this.getRedirect(); this.forgetRedirect(); @@ -583,14 +583,14 @@ export class CoreAppProvider { * * @param siteId Site ID. * @param page Page to go. - * @param params Page params. + * @param options Navigation options. */ - storeRedirect(siteId: string, page: string, params: Params): void { + storeRedirect(siteId: string, page: string, options: CoreNavigationOptions): void { try { const redirect: CoreRedirectData = { siteId, page, - params, + options, timemodified: Date.now(), }; @@ -671,14 +671,14 @@ export type CoreRedirectData = { siteId?: string; /** - * Name of the page to redirect to. + * Path of the page to redirect to. */ page?: string; /** - * Params to pass to the page. + * Options of the navigation. */ - params?: Params; + options?: CoreNavigationOptions; /** * Timestamp when this redirect was last modified. diff --git a/src/core/services/navigator.ts b/src/core/services/navigator.ts index e1c23827f..dbd7cdee7 100644 --- a/src/core/services/navigator.ts +++ b/src/core/services/navigator.ts @@ -39,7 +39,7 @@ const DEFAULT_MAIN_MENU_TAB = CoreMainMenuHomeHandlerService.PAGE_NAME; */ export type CoreRedirectPayload = { redirectPath: string; - redirectParams?: Params; + redirectOptions?: CoreNavigationOptions; }; /** @@ -50,6 +50,11 @@ export type CoreNavigationOptions = { params?: Params; reset?: boolean; preferCurrentTab?: boolean; // Default true. + nextNavigation?: { + path: string; + isSitePath?: boolean; + options?: CoreNavigationOptions; + }; }; /** @@ -149,6 +154,14 @@ export class CoreNavigatorService { ? await NavController.navigateRoot(url, navigationOptions) : await NavController.navigateForward(url, navigationOptions); + if (options.nextNavigation?.path && navigationResult !== false) { + if (options.nextNavigation.isSitePath) { + return this.navigateToSitePath(options.nextNavigation.path, options.nextNavigation.options); + } + + return this.navigate(options.nextNavigation.path, options.nextNavigation.options); + } + return navigationResult !== false; } @@ -211,7 +224,7 @@ export class CoreNavigatorService { if (CoreSites.isLoggedIn() && CoreSites.getCurrentSiteId() !== siteId) { if (CoreSitePlugins.hasSitePluginsLoaded) { // The site has site plugins so the app will be restarted. Store the data and logout. - CoreApp.instance.storeRedirect(siteId, path, options.params || {}); + CoreApp.storeRedirect(siteId, path, options || {}); await CoreSites.logout(); @@ -450,7 +463,7 @@ export class CoreNavigatorService { ...options, params: { redirectPath: `/main/${DEFAULT_MAIN_MENU_TAB}/${path}`, - redirectParams: options.params, + redirectOptions: options.params || options.nextNavigation ? options : undefined, } as CoreRedirectPayload, }); } diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index f2be5fb5a..f2385dd70 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -50,6 +50,7 @@ import { } from '@services/database/sites'; import { CoreArray } from '../singletons/array'; import { CoreNetworkError } from '@classes/errors/network-error'; +import { CoreNavigationOptions } from './navigator'; export const CORE_SITE_SCHEMAS = new InjectionToken('CORE_SITE_SCHEMAS'); @@ -795,10 +796,10 @@ export class CoreSitesProvider { * * @param siteId ID of the site to load. * @param pageName Name of the page to go once authenticated if logged out. If not defined, site initial page. - * @param params Params of the page to go once authenticated if logged out. + * @param pageOptions Options of the navigation to pageName. * @return Promise resolved with true if site is loaded, resolved with false if cannot login. */ - async loadSite(siteId: string, pageName?: string, params?: Record): Promise { + async loadSite(siteId: string, pageName?: string, pageOptions?: CoreNavigationOptions): Promise { this.logger.debug(`Load site ${siteId}`); const site = await this.getSite(siteId); @@ -809,7 +810,7 @@ export class CoreSitesProvider { // Logged out, trigger session expired event and stop. CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, { pageName, - params, + options: pageOptions, }, site.getId()); return false; @@ -822,7 +823,7 @@ export class CoreSitesProvider { // Local mobile was added. Throw invalid session to force reconnect and create a new token. CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, { pageName, - params, + options: pageOptions, }, siteId); return false; diff --git a/src/core/services/urlschemes.ts b/src/core/services/urlschemes.ts index 67b3924d9..c5f40582d 100644 --- a/src/core/services/urlschemes.ts +++ b/src/core/services/urlschemes.ts @@ -153,9 +153,7 @@ export class CoreCustomURLSchemesProvider { // Site created and authenticated, open the page to go. if (data.pageName) { // Page defined, go to that page instead of site initial page. - CoreNavigator.navigateToSitePath(data.pageName, { - params: data.pageParams, - }); + CoreNavigator.navigateToSitePath(data.pageName, data.pageOptions); } else { CoreNavigator.navigateToSiteHome(); } @@ -408,7 +406,7 @@ export class CoreCustomURLSchemesProvider { hasSitePluginsLoaded = CoreSitePlugins.hasSitePluginsLoaded; if (hasSitePluginsLoaded) { // Store the redirect since logout will restart the app. - CoreApp.storeRedirect(CoreConstants.NO_SITE_ID, '/login/credentials', pageParams); + CoreApp.storeRedirect(CoreConstants.NO_SITE_ID, '/login/credentials', { params: pageParams }); } await CoreSites.logout(); diff --git a/src/core/singletons/events.ts b/src/core/singletons/events.ts index 21c12f149..ee72d67e9 100644 --- a/src/core/singletons/events.ts +++ b/src/core/singletons/events.ts @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Params } from '@angular/router'; import { Subject } from 'rxjs'; import { CoreLogger } from '@singletons/logger'; import { CoreSite, CoreSiteInfoResponse, CoreSitePublicConfigResponse } from '@classes/site'; import { CoreFilepoolComponentFileEventData } from '@services/filepool'; +import { CoreNavigationOptions } from '@services/navigator'; /** * Observer instance to stop listening to an event. @@ -260,7 +260,7 @@ export type CoreEventSiteAddedData = CoreSiteInfoResponse; */ export type CoreEventSessionExpiredData = { pageName?: string; - params?: Params; + options?: CoreNavigationOptions; }; /**