From c41cb6bb19f8b6a71762d2b5377c8712a7b1d9b6 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 4 Jan 2019 11:18:29 +0100 Subject: [PATCH] MOBILE-2795 core: Fix redirect to other sites --- src/addon/messages/messages.module.ts | 18 ++-- src/addon/messages/providers/messages.ts | 19 ++++- .../notifications/notifications.module.ts | 9 +- src/components/ion-tabs/ion-tabs.ts | 83 ++++++++++++++----- src/core/login/providers/helper.ts | 14 +++- src/core/mainmenu/pages/menu/menu.ts | 9 ++ 6 files changed, 117 insertions(+), 35 deletions(-) diff --git a/src/addon/messages/messages.module.ts b/src/addon/messages/messages.module.ts index 0d3cc13fc..43b3609c6 100644 --- a/src/addon/messages/messages.module.ts +++ b/src/addon/messages/messages.module.ts @@ -107,11 +107,14 @@ export class AddonMessagesModule { } messagesProvider.invalidateDiscussionsCache(notification.site).finally(() => { - let pageName = 'AddonMessagesIndexPage'; - if (messagesProvider.isGroupMessagingEnabled()) { - pageName = 'AddonMessagesGroupConversationsPage'; - } - linkHelper.goInSite(undefined, pageName, undefined, notification.site); + // Check if group messaging is enabled, to determine which page should be loaded. + messagesProvider.isGroupMessagingEnabledInSite(notification.site).then((enabled) => { + let pageName = 'AddonMessagesIndexPage'; + if (enabled) { + pageName = 'AddonMessagesGroupConversationsPage'; + } + linkHelper.goInSite(undefined, pageName, undefined, notification.site); + }); }); }); }); @@ -125,7 +128,10 @@ export class AddonMessagesModule { // Register push notification clicks. pushNotificationsDelegate.on('click').subscribe((notification) => { if (utils.isFalseOrZero(notification.notif)) { - notificationClicked(notification); + // Execute the callback in the Angular zone, so change detection doesn't stop working. + zone.run(() => { + notificationClicked(notification); + }); return true; } diff --git a/src/addon/messages/providers/messages.ts b/src/addon/messages/providers/messages.ts index 5b49d149c..c81d27e65 100644 --- a/src/addon/messages/providers/messages.ts +++ b/src/addon/messages/providers/messages.ts @@ -1770,17 +1770,32 @@ export class AddonMessagesProvider { /** * Returns whether or not group messaging is supported. * - * @return {boolean} If related WS is avalaible on current site. + * @return {boolean} If related WS is available on current site. * @since 3.6 */ isGroupMessagingEnabled(): boolean { return this.sitesProvider.wsAvailableInCurrentSite('core_message_get_conversations'); } + /** + * Returns whether or not group messaging is supported in a certain site. + * + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with boolean: whether related WS is available on a certain site. + * @since 3.6 + */ + isGroupMessagingEnabledInSite(siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.wsAvailable('core_message_get_conversations'); + }).catch(() => { + return false; + }); + } + /** * Returns whether or not we can mark all messages as read. * - * @return {boolean} If related WS is avalaible on current site. + * @return {boolean} If related WS is available on current site. * @since 3.2 */ isMarkAllMessagesReadEnabled(): boolean { diff --git a/src/addon/notifications/notifications.module.ts b/src/addon/notifications/notifications.module.ts index 5359e1e35..384901f44 100644 --- a/src/addon/notifications/notifications.module.ts +++ b/src/addon/notifications/notifications.module.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { NgModule } from '@angular/core'; +import { NgModule, NgZone } from '@angular/core'; import { AddonNotificationsProvider } from './providers/notifications'; import { AddonNotificationsMainMenuHandler } from './providers/mainmenu-handler'; import { AddonNotificationsSettingsHandler } from './providers/settings-handler'; @@ -47,7 +47,7 @@ export const ADDON_NOTIFICATIONS_PROVIDERS: any[] = [ export class AddonNotificationsModule { constructor(mainMenuDelegate: CoreMainMenuDelegate, mainMenuHandler: AddonNotificationsMainMenuHandler, settingsDelegate: CoreSettingsDelegate, settingsHandler: AddonNotificationsSettingsHandler, - cronDelegate: CoreCronDelegate, cronHandler: AddonNotificationsCronHandler, + cronDelegate: CoreCronDelegate, cronHandler: AddonNotificationsCronHandler, zone: NgZone, appProvider: CoreAppProvider, utils: CoreUtilsProvider, sitesProvider: CoreSitesProvider, notificationsProvider: AddonNotificationsProvider, localNotifications: CoreLocalNotificationsProvider, linkHelper: CoreContentLinksHelperProvider, pushNotificationsDelegate: AddonPushNotificationsDelegate) { @@ -76,7 +76,10 @@ export class AddonNotificationsModule { // Register push notification clicks. pushNotificationsDelegate.on('click').subscribe((notification) => { if (utils.isTrueOrOne(notification.notif)) { - notificationClicked(notification); + // Execute the callback in the Angular zone, so change detection doesn't stop working. + zone.run(() => { + notificationClicked(notification); + }); return true; } diff --git a/src/components/ion-tabs/ion-tabs.ts b/src/components/ion-tabs/ion-tabs.ts index 9de17f576..4a6eaccf8 100644 --- a/src/components/ion-tabs/ion-tabs.ts +++ b/src/components/ion-tabs/ion-tabs.ts @@ -14,9 +14,11 @@ import { Component, Optional, ElementRef, Renderer, ViewEncapsulation, forwardRef, ViewChild, Input, OnDestroy } from '@angular/core'; -import { Tabs, NavController, ViewController, App, Config, Platform, DeepLinker, Keyboard, RootNode } from 'ionic-angular'; +import { + Tabs, Tab, NavController, ViewController, App, Config, Platform, DeepLinker, Keyboard, RootNode, NavOptions +} from 'ionic-angular'; import { CoreIonTabComponent } from './ion-tab'; -import { CoreUtilsProvider } from '@providers/utils/utils'; +import { CoreUtilsProvider, PromiseDefer } from '@providers/utils/utils'; import { CoreAppProvider } from '@providers/app'; /** @@ -66,6 +68,7 @@ export class CoreIonTabsComponent extends Tabs implements OnDestroy { protected firstSelectedTab: string; protected unregisterBackButtonAction: any; + protected selectTabPromiseDefer: PromiseDefer; constructor(protected utils: CoreUtilsProvider, protected appProvider: CoreAppProvider, @Optional() parent: NavController, @Optional() viewCtrl: ViewController, _app: App, config: Config, elementRef: ElementRef, _plt: Platform, @@ -148,20 +151,18 @@ export class CoreIonTabsComponent extends Tabs implements OnDestroy { // Tabs initialized. Force select the tab if it's not enabled. if (this.selectedDisabled && typeof this.selectedIndex != 'undefined') { const tab = this.getByIndex(this.selectedIndex); - if (tab && (!tab.enabled)) { - this.select(tab); - } - } else { - // Select first tab on init. - const tab = this._tabs.find((tab) => { - return tab.enabled; - }); - if (tab) { + if (tab && !tab.enabled) { this.select(tab); } } this.firstSelectedTab = this._selectHistory[0] || null; + }).finally(() => { + // If there was a select promise pending to be resolved, do it now. + if (this.selectTabPromiseDefer) { + this.selectTabPromiseDefer.resolve(); + delete this.selectTabPromiseDefer; + } }); } else { // Tabs not loaded yet. Set the tab bar position so the tab bar is shown, it'll have a spinner. @@ -265,20 +266,62 @@ export class CoreIonTabsComponent extends Tabs implements OnDestroy { } } + /** + * Select a tab. + * + * @param {number|Tab} tabOrIndex Index, or the Tab instance, of the tab to select. + * @param {NavOptions} Nav options. + * @param {boolean} [fromUrl=true] Whether to load from a URL. + * @return {Promise} Promise resolved when selected. + */ + select(tabOrIndex: number | Tab, opts: NavOptions = {}, fromUrl: boolean = false): Promise { + + if (this.initialized) { + // Tabs have been initialized, select the tab. + return super.select(tabOrIndex, opts, fromUrl); + } else { + // Tabs not initialized yet. Mark it as "selectedIndex" input so it's treated when the tabs are initialized. + if (typeof tabOrIndex == 'number') { + this.selectedIndex = tabOrIndex; + } else { + this.selectedIndex = this.getIndex(tabOrIndex); + } + + // Don't resolve the Promise until the tab is really selected (tabs are initialized). + this.selectTabPromiseDefer = this.selectTabPromiseDefer || this.utils.promiseDefer(); + + return this.selectTabPromiseDefer.promise; + } + } + /** * Select a tab by Index. First it will reset the status of the tab. * * @param {number} index Index of the tab. + * @return {Promise} Promise resolved when selected. */ - selectTabRootByIndex(index: number): void { - const tab = this.getByIndex(index); - if (tab) { - tab.goToRoot({animate: false, updateUrl: true, isNavRoot: true}).then(() => { - // Tab not previously selected. Select it after going to root. - if (!tab.isSelected) { - this.select(tab, {animate: false, updateUrl: true, isNavRoot: true}); - } - }); + selectTabRootByIndex(index: number): Promise { + if (this.initialized) { + const tab = this.getByIndex(index); + if (tab) { + return tab.goToRoot({animate: false, updateUrl: true, isNavRoot: true}).then(() => { + // Tab not previously selected. Select it after going to root. + if (!tab.isSelected) { + return this.select(tab, {animate: false, updateUrl: true, isNavRoot: true}); + } + }); + } + + // Not found. + return Promise.reject(null); + } else { + // Tabs not initialized yet. Mark it as "selectedIndex" input so it's treated when the tabs are initialized. + this.selectedIndex = index; + + // Don't resolve the Promise until the tab is really selected (tabs are initialized). + this.selectTabPromiseDefer = this.selectTabPromiseDefer || this.utils.promiseDefer(); + + return this.selectTabPromiseDefer.promise; } } diff --git a/src/core/login/providers/helper.ts b/src/core/login/providers/helper.ts index af4bb667f..2f0dc2d78 100644 --- a/src/core/login/providers/helper.ts +++ b/src/core/login/providers/helper.ts @@ -587,18 +587,24 @@ export class CoreLoginHelperProvider { * @param {string} siteId Site to load. */ protected loadSiteAndPage(page: string, params: any, siteId: string): void { + const navCtrl = this.appProvider.getRootNavController(); + if (siteId == CoreConstants.NO_SITE_ID) { // Page doesn't belong to a site, just load the page. - this.appProvider.getRootNavController().setRoot(page, params); + navCtrl.setRoot(page, params); } else { const modal = this.domUtils.showModalLoading(); this.sitesProvider.loadSite(siteId, page, params).then((loggedIn) => { if (loggedIn) { - this.loadPageInMainMenu(page, params); + // Due to DeepLinker, we need to remove the path from the URL before going to main menu. + // IonTabs checks the URL to determine which path to load for deep linking, so we clear the URL. + this.location.replaceState(''); + + navCtrl.setRoot('CoreMainMenuPage', { redirectPage: page, redirectParams: params }); } - }).catch(() => { + }).catch((error) => { // Site doesn't exist. - this.appProvider.getRootNavController().setRoot('CoreLoginSitesPage'); + navCtrl.setRoot('CoreLoginSitesPage'); }).finally(() => { modal.dismiss(); }); diff --git a/src/core/mainmenu/pages/menu/menu.ts b/src/core/mainmenu/pages/menu/menu.ts index d41e91a2e..4bd7b97d5 100644 --- a/src/core/mainmenu/pages/menu/menu.ts +++ b/src/core/mainmenu/pages/menu/menu.ts @@ -43,6 +43,15 @@ export class CoreMainMenuPage implements OnDestroy { constructor(private menuDelegate: CoreMainMenuDelegate, private sitesProvider: CoreSitesProvider, navParams: NavParams, private navCtrl: NavController, private eventsProvider: CoreEventsProvider) { + + // Check if the menu was loaded with a redirect. + const redirectPage = navParams.get('redirectPage'); + if (redirectPage) { + this.pendingRedirect = { + redirectPage: redirectPage, + redirectParams: navParams.get('redirectParams') + }; + } } /**