diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5d6154614..71bd22964 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -115,6 +115,8 @@ export class AppComponent implements OnInit, AfterViewInit { }); this.onPlatformReady(); + + // @todo: Quit app with back button. How to tell if we're at root level? } /** diff --git a/src/core/classes/tabs.ts b/src/core/classes/tabs.ts index 10e252956..8cc91af13 100644 --- a/src/core/classes/tabs.ts +++ b/src/core/classes/tabs.ts @@ -25,9 +25,9 @@ import { ElementRef, } from '@angular/core'; import { IonSlides } from '@ionic/angular'; +import { BackButtonEvent } from '@ionic/core'; import { Subscription } from 'rxjs'; -import { CoreApp } from '@services/app'; import { Platform, Translate } from '@singletons'; import { CoreSettingsHelper } from '@features/settings/services/settings-helper'; @@ -81,7 +81,7 @@ export class CoreTabsBaseComponent implements OnInit, Aft protected selectHistory: string[] = []; protected firstSelectedTab?: string; // ID of the first selected tab to control history. - protected unregisterBackButtonAction: any; + protected backButtonFunction: (event: BackButtonEvent) => void; protected languageChangedSubscription?: Subscription; protected isInTransition = false; // Weather Slides is in transition. protected slidesSwiper: any; // eslint-disable-line @typescript-eslint/no-explicit-any @@ -91,6 +91,7 @@ export class CoreTabsBaseComponent implements OnInit, Aft constructor( protected element: ElementRef, ) { + this.backButtonFunction = this.backButtonClicked.bind(this); } /** @@ -171,43 +172,47 @@ export class CoreTabsBaseComponent implements OnInit, Aft this.calculateSlides(); - this.registerBackButtonAction(); + document.addEventListener('ionBackButton', this.backButtonFunction); } /** - * Register back button action. + * Back button clicked. + * + * @param event Event. */ - protected registerBackButtonAction(): void { - this.unregisterBackButtonAction = CoreApp.registerBackButtonAction(() => { - // The previous page in history is not the last one, we need the previous one. + protected backButtonClicked(event: BackButtonEvent): void { + event.detail.register(40, (processNextHandler: () => void) => { if (this.selectHistory.length > 1) { - const tabIndex = this.selectHistory[this.selectHistory.length - 2]; + // The previous page in history is not the last one, we need the previous one. + const previousTabId = this.selectHistory[this.selectHistory.length - 2]; // Remove curent and previous tabs from history. - this.selectHistory = this.selectHistory.filter((tabId) => this.selected != tabId && tabIndex != tabId); + this.selectHistory = this.selectHistory.filter((tabId) => this.selected != tabId && previousTabId != tabId); - this.selectTab(tabIndex); + this.selectTab(previousTabId); - return true; - } else if (this.selected != this.firstSelectedTab) { + return; + } + + if (this.firstSelectedTab && this.selected != this.firstSelectedTab) { // All history is gone but we are not in the first selected tab. this.selectHistory = []; - this.selectTab(this.firstSelectedTab!); + this.selectTab(this.firstSelectedTab); - return true; + return; } - return false; - }, 750); + processNextHandler(); + }); } /** * User left the page that contains the component. */ ionViewDidLeave(): void { - // Unregister the custom back button action for this page - this.unregisterBackButtonAction && this.unregisterBackButtonAction(); + // Unregister the custom back button action for this component. + document.removeEventListener('ionBackButton', this.backButtonFunction); this.isCurrentView = false; } diff --git a/src/core/features/mainmenu/pages/menu/menu.html b/src/core/features/mainmenu/pages/menu/menu.html index 916230a83..8042716f7 100644 --- a/src/core/features/mainmenu/pages/menu/menu.html +++ b/src/core/features/mainmenu/pages/menu/menu.html @@ -1,4 +1,5 @@ - + diff --git a/src/core/features/mainmenu/pages/menu/menu.ts b/src/core/features/mainmenu/pages/menu/menu.ts index 7dfca5ad7..9dd61a115 100644 --- a/src/core/features/mainmenu/pages/menu/menu.ts +++ b/src/core/features/mainmenu/pages/menu/menu.ts @@ -15,6 +15,7 @@ import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { IonTabs } from '@ionic/angular'; +import { BackButtonEvent } from '@ionic/core'; import { Subscription } from 'rxjs'; import { CoreApp } from '@services/app'; @@ -50,6 +51,11 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { protected pendingRedirect?: CoreRedirectPayload; protected urlToOpen?: string; protected keyboardObserver?: CoreEventObserver; + protected resizeFunction: () => void; + protected backButtonFunction: (event: BackButtonEvent) => void; + protected selectHistory: string[] = []; + protected selectedTab?: string; + protected firstSelectedTab?: string; @ViewChild('mainTabs') mainTabs?: IonTabs; @@ -57,7 +63,10 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { protected route: ActivatedRoute, protected changeDetector: ChangeDetectorRef, protected router: Router, - ) {} + ) { + this.resizeFunction = this.initHandlers.bind(this); + this.backButtonFunction = this.backButtonClicked.bind(this); + } /** * Initialize the component. @@ -100,7 +109,8 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { } }); - window.addEventListener('resize', this.initHandlers.bind(this)); + window.addEventListener('resize', this.resizeFunction); + document.addEventListener('ionBackButton', this.backButtonFunction); if (CoreApp.isIOS()) { // In iOS, the resize event is triggered before the keyboard is opened/closed and not triggered again once done. @@ -209,7 +219,8 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { ngOnDestroy(): void { this.subscription?.unsubscribe(); this.redirectObs?.off(); - window.removeEventListener('resize', this.initHandlers.bind(this)); + window.removeEventListener('resize', this.resizeFunction); + document.removeEventListener('ionBackButton', this.backButtonFunction); this.keyboardObserver?.off(); } @@ -262,4 +273,46 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { } } + /** + * Selected tab has changed. + * + * @param event Event. + */ + tabChanged(event: {tab: string}): void { + this.selectedTab = event.tab; + this.firstSelectedTab = this.firstSelectedTab ?? event.tab; + this.selectHistory.push(event.tab); + } + + /** + * Back button clicked. + * + * @param event Event. + */ + protected backButtonClicked(event: BackButtonEvent): void { + event.detail.register(20, (processNextHandler: () => void) => { + if (this.selectHistory.length > 1) { + // The previous page in history is not the last one, we need the previous one. + const previousTab = this.selectHistory[this.selectHistory.length - 2]; + + // Remove curent and previous tabs from history. + this.selectHistory = this.selectHistory.filter((tab) => this.selectedTab != tab && previousTab != tab); + + this.mainTabs?.select(previousTab); + + return; + } + + if (this.firstSelectedTab && this.selectedTab != this.firstSelectedTab) { + // All history is gone but we are not in the first selected tab. + this.selectHistory = []; + this.mainTabs?.select(this.firstSelectedTab); + + return; + } + + processNextHandler(); + }); + } + } diff --git a/src/core/services/app.ts b/src/core/services/app.ts index 808976e5a..abc92e0b4 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -56,7 +56,6 @@ export class CoreAppProvider { protected isKeyboardShown = false; protected keyboardOpening = false; protected keyboardClosing = false; - protected backActions: {callback: () => boolean; priority: number}[] = []; protected forceOffline = false; protected redirect?: CoreRedirectData; @@ -68,11 +67,6 @@ export class CoreAppProvider { this.schemaVersionsManager = new Promise(resolve => this.resolveSchemaVersionsManager = resolve); this.db = CoreDB.getDB(DBNAME); this.logger = CoreLogger.getInstance('CoreAppProvider'); - - // @todo - // this.platform.registerBackButtonAction(() => { - // this.backButtonAction(); - // }, 100); } /** @@ -592,37 +586,18 @@ export class CoreAppProvider { } /** - * The back button event is triggered when the user presses the native - * platform's back button, also referred to as the "hardware" back button. - * This event is only used within Cordova apps running on Android and - * Windows platforms. This event is not fired on iOS since iOS doesn't come - * with a hardware back button in the same sense an Android or Windows device - * does. + * Register a back button action. + * This function is deprecated and no longer works. You should now use Ionic events directly, please see: + * https://ionicframework.com/docs/developing/hardware-back-button * - * Registering a hardware back button action and setting a priority allows - * apps to control which action should be called when the hardware back - * button is pressed. This method decides which of the registered back button - * actions has the highest priority and should be called. - * - * @param callback Called when the back button is pressed, if this registered action has the highest priority. - * @param priority Set the priority for this action. All actions sorted by priority will be executed since one of - * them returns true. - * - Priorities higher or equal than 1000 will go before closing modals - * - Priorities lower than 500 will only be executed if you are in the first state of the app (before exit). + * @param callback Called when the back button is pressed. + * @param priority Priority. * @return A function that, when called, will unregister the back button action. + * @deprecated since 3.9.5 */ - registerBackButtonAction(callback: () => boolean, priority: number = 0): () => boolean { - const action = { callback, priority }; - - this.backActions.push(action); - - this.backActions.sort((a, b) => b.priority - a.priority); - - return (): boolean => { - const index = this.backActions.indexOf(action); - - return index >= 0 && !!this.backActions.splice(index, 1); - }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + registerBackButtonAction(callback: () => boolean, priority = 0): () => boolean { + return () => false; } /**