diff --git a/src/app/app.component.ts b/src/app/app.component.ts index fc46daea6..6897feeac 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -148,7 +148,7 @@ export class AppComponent implements OnInit, AfterViewInit { CorePlatform.resume.subscribe(() => { // Wait a second before setting it to false since in iOS there could be some frozen WS calls. setTimeout(() => { - if (CoreLoginHelper.isWaitingForBrowser()) { + if (CoreLoginHelper.isWaitingForBrowser() && !CoreUtils.isInAppBrowserOpen()) { CoreLoginHelper.stopWaitingForBrowser(); CoreLoginHelper.checkLogout(); } diff --git a/src/core/features/login/pages/change-password/change-password.ts b/src/core/features/login/pages/change-password/change-password.ts index 697e5d939..26db139cf 100644 --- a/src/core/features/login/pages/change-password/change-password.ts +++ b/src/core/features/login/pages/change-password/change-password.ts @@ -35,6 +35,7 @@ export class CoreLoginChangePasswordPage implements OnDestroy { logoutLabel: string; protected urlLoadedObserver?: CoreEventObserver; + protected messageObserver?: CoreEventObserver; protected browserClosedObserver?: CoreEventObserver; constructor() { @@ -90,9 +91,34 @@ export class CoreLoginChangePasswordPage implements OnDestroy { return; } - this.urlLoadedObserver = CoreEvents.on(CoreEvents.IAB_LOAD_START, (event) => { + this.urlLoadedObserver = CoreEvents.on(CoreEvents.IAB_LOAD_STOP, (event) => { if (event.url.match(/\/login\/change_password\.php.*return=1/)) { - // Password should have changed. + // Password has changed, close the IAB now. + CoreUtils.closeInAppBrowser(); + this.login(); + + return; + } + + if (!event.url.match(/\/login\/change_password\.php/)) { + return; + } + + // Use a script to check if the user changed the password, in some platforms we cannot tell using the URL. + CoreUtils.getInAppBrowserInstance()?.executeScript({ + code: ` + if ( + document.querySelector('input[type="password"]') === null && + document.querySelector('button[type="submit"]') !== null + ) { + webkit.messageHandlers.cordova_iab.postMessage(JSON.stringify({ passwordChanged: true })); + } + `, + }); + }); + + this.messageObserver = CoreEvents.on(CoreEvents.IAB_MESSAGE, (data) => { + if (data.passwordChanged) { CoreUtils.closeInAppBrowser(); this.login(); } @@ -100,8 +126,11 @@ export class CoreLoginChangePasswordPage implements OnDestroy { this.browserClosedObserver = CoreEvents.on(CoreEvents.IAB_EXIT, () => { this.urlLoadedObserver?.off(); + this.messageObserver?.off(); this.browserClosedObserver?.off(); + delete this.urlLoadedObserver; + delete this.messageObserver; delete this.browserClosedObserver; }); } @@ -111,6 +140,7 @@ export class CoreLoginChangePasswordPage implements OnDestroy { */ ngOnDestroy(): void { this.urlLoadedObserver?.off(); + this.messageObserver?.off(); this.browserClosedObserver?.off(); } diff --git a/src/core/services/utils/utils.ts b/src/core/services/utils/utils.ts index 23f51f9e1..73e253df3 100644 --- a/src/core/services/utils/utils.ts +++ b/src/core/services/utils/utils.ts @@ -266,6 +266,24 @@ export class CoreUtilsProvider { } } + /** + * Get inapp browser instance (if any). + * + * @return IAB instance, undefined if not open. + */ + getInAppBrowserInstance(): InAppBrowserObject | undefined { + return this.iabInstance; + } + + /** + * Check if inapp browser is open. + * + * @return Whether it's open. + */ + isInAppBrowserOpen(): boolean { + return !!this.iabInstance; + } + /** * Clone a variable. It should be an object, array or primitive type. * @@ -1024,15 +1042,14 @@ export class CoreUtilsProvider { this.setInAppBrowserToolbarColors(options); + this.iabInstance?.close(); // Close window if there is one already open, only allow one. + this.iabInstance = InAppBrowser.create(url, '_blank', options); if (CorePlatform.isMobile()) { - let loadStopSubscription: Subscription | undefined; const loadStartUrls: string[] = []; - // Trigger global events when a url is loaded or the window is closed. This is to make it work like in Ionic 1. const loadStartSubscription = this.iabInstance.on('loadstart').subscribe((event) => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. NgZone.run(() => { // Store the last loaded URLs (max 10). loadStartUrls.push(event.url); @@ -1044,25 +1061,26 @@ export class CoreUtilsProvider { }); }); - if (CoreApp.isAndroid()) { - // Load stop is needed with InAppBrowser v3. Custom URL schemes no longer trigger load start, simulate it. - loadStopSubscription = this.iabInstance.on('loadstop').subscribe((event) => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. - NgZone.run(() => { - if (loadStartUrls.indexOf(event.url) == -1) { - // The URL was stopped but not started, probably a custom URL scheme. - CoreEvents.trigger(CoreEvents.IAB_LOAD_START, event); - } - }); + const loadStopSubscription = this.iabInstance.on('loadstop').subscribe((event) => { + NgZone.run(() => { + CoreEvents.trigger(CoreEvents.IAB_LOAD_STOP, event); }); - } + }); + + const messageSubscription = this.iabInstance.on('message').subscribe((event) => { + NgZone.run(() => { + CoreEvents.trigger(CoreEvents.IAB_MESSAGE, event.data); + }); + }); const exitSubscription = this.iabInstance.on('exit').subscribe((event) => { - // Execute the callback in the Angular zone, so change detection doesn't stop working. NgZone.run(() => { loadStartSubscription.unsubscribe(); - loadStopSubscription && loadStopSubscription.unsubscribe(); + loadStopSubscription.unsubscribe(); + messageSubscription.unsubscribe(); exitSubscription.unsubscribe(); + + this.iabInstance = undefined; CoreEvents.trigger(CoreEvents.IAB_EXIT, event); }); }); diff --git a/src/core/singletons/events.ts b/src/core/singletons/events.ts index f37fdb0de..6d4d7213d 100644 --- a/src/core/singletons/events.ts +++ b/src/core/singletons/events.ts @@ -53,6 +53,8 @@ export interface CoreEventsData { [CoreEvents.SECTION_STATUS_CHANGED]: CoreEventSectionStatusChangedData; [CoreEvents.ACTIVITY_DATA_SENT]: CoreEventActivityDataSentData; [CoreEvents.IAB_LOAD_START]: InAppBrowserEvent; + [CoreEvents.IAB_LOAD_STOP]: InAppBrowserEvent; + [CoreEvents.IAB_MESSAGE]: Record; [CoreEvents.LOGIN_SITE_CHECKED]: CoreEventLoginSiteCheckedData; [CoreEvents.LOGIN_SITE_UNCHECKED]: CoreEventLoginSiteUncheckedData; [CoreEvents.SEND_ON_ENTER_CHANGED]: CoreEventSendOnEnterChangedData; @@ -99,7 +101,9 @@ export class CoreEvents { static readonly LOGIN_SITE_CHECKED = 'login_site_checked'; static readonly LOGIN_SITE_UNCHECKED = 'login_site_unchecked'; static readonly IAB_LOAD_START = 'inappbrowser_load_start'; + static readonly IAB_LOAD_STOP = 'inappbrowser_load_stop'; static readonly IAB_EXIT = 'inappbrowser_exit'; + static readonly IAB_MESSAGE = 'inappbrowser_message'; static readonly APP_LAUNCHED_URL = 'app_launched_url'; // App opened with a certain URL (custom URL scheme). static readonly FILE_SHARED = 'file_shared'; static readonly KEYBOARD_CHANGE = 'keyboard_change';