feat(backButton): register back button actions

This commit is contained in:
Adam Bradley
2016-06-08 10:40:26 -05:00
parent d98f3c9bd7
commit 84f37cf4d5
3 changed files with 205 additions and 69 deletions

View File

@ -41,18 +41,28 @@ export class Platform {
private _readyPromise: Promise<any>; private _readyPromise: Promise<any>;
private _readyResolve: any; private _readyResolve: any;
private _resizeTm: any; private _resizeTm: any;
private _zone: NgZone; private _bbActions: BackButtonAction[] = [];
zone: NgZone;
constructor(platforms: string[] = []) { constructor(platforms: string[] = []) {
this._platforms = platforms; this._platforms = platforms;
this._readyPromise = new Promise(res => { this._readyResolve = res; } ); this._readyPromise = new Promise(res => { this._readyResolve = res; } );
this.backButton.subscribe(() => {
// the hardware back button event has been fired
console.debug('hardware back button');
// decide which backbutton action should run
this.runBackButtonAction();
});
} }
/** /**
* @private * @private
*/ */
setZone(zone: NgZone) { setZone(zone: NgZone) {
this._zone = zone; this.zone = zone;
} }
@ -212,7 +222,7 @@ export class Platform {
* such as Cordova or Electron, then it uses the default DOM ready. * such as Cordova or Electron, then it uses the default DOM ready.
*/ */
triggerReady(readySource: string) { triggerReady(readySource: string) {
this._zone.run(() => { this.zone.run(() => {
this._readyResolve(readySource); this._readyResolve(readySource);
}); });
} }
@ -310,14 +320,7 @@ export class Platform {
// ********************************************** // **********************************************
/** /**
* The back button event is emitted when the user presses the native * @private
* platform's back button, also referred to as the "hardware" back button.
* This event is only emitted 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. It's important to note that this event does not emit when the Ionic
* app's back button within the navbar is clicked, but this event is only
* referencing the platform's hardware back button.
*/ */
backButton: EventEmitter<Event> = new EventEmitter(); backButton: EventEmitter<Event> = new EventEmitter();
@ -336,6 +339,55 @@ export class Platform {
*/ */
resume: EventEmitter<Event> = new EventEmitter(); resume: EventEmitter<Event> = new EventEmitter();
/**
* 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.
*
* 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 {Function} callback Called when the back button is pressed,
* if this registered action has the highest priority.
* @param {number} priority Set the priority for this action. Only the highest priority will execute. Defaults to `0`.
* @returns {Function} A function that, when called, will unregister
* the its back button action.
*/
registerBackButtonAction(fn: Function, priority: number = 0): Function {
let action: BackButtonAction = {fn, priority};
this._bbActions.push(action);
// return a function to unregister this back button action
return () => {
let index = this._bbActions.indexOf(action);
if (index > -1) {
this._bbActions.splice(index, 1);
}
};
}
/**
* @private
*/
runBackButtonAction() {
// decide which one back button action should run
let winner: BackButtonAction = null;
this._bbActions.forEach((action: BackButtonAction) => {
if (!winner || action.priority >= winner.priority) {
winner = action;
}
});
// run the winning action if there is one
winner && winner.fn && winner.fn();
}
// Getter/Setter Methods // Getter/Setter Methods
// ********************************************** // **********************************************
@ -391,28 +443,32 @@ export class Platform {
} }
/** /**
* @private * Gets the width of the platform's viewport using `window.innerWidth`.
* Using this method is preferred since the dimension is a cached value,
* which reduces the chance of multiple and expensive DOM reads.
*/ */
width(): number { width(): number {
return windowDimensions().width; return windowDimensions().width;
} }
/** /**
* @private * Gets the height of the platform's viewport using `window.innerHeight`.
* Using this method is preferred since the dimension is a cached value,
* which reduces the chance of multiple and expensive DOM reads.
*/ */
height(): number { height(): number {
return windowDimensions().height; return windowDimensions().height;
} }
/** /**
* @private * Returns `true` if the app is in portait mode.
*/ */
isPortrait(): boolean { isPortrait(): boolean {
return this.width() < this.height(); return this.width() < this.height();
} }
/** /**
* @private * Returns `true` if the app is in landscape mode.
*/ */
isLandscape(): boolean { isLandscape(): boolean {
return !this.isPortrait(); return !this.isPortrait();
@ -440,7 +496,6 @@ export class Platform {
/** /**
* @private * @private
* @returns Unregister function
*/ */
onResize(cb: Function): Function { onResize(cb: Function): Function {
let self = this; let self = this;
@ -782,3 +837,8 @@ export interface PlatformVersion {
major?: number; major?: number;
minor?: number; minor?: number;
} }
interface BackButtonAction {
fn: Function;
priority: number;
}

View File

@ -177,14 +177,20 @@ Platform.register({
// add cordova listeners to emit platform events // add cordova listeners to emit platform events
doc.addEventListener('backbutton', function(ev: Event) { doc.addEventListener('backbutton', function(ev: Event) {
p.zone.run(() => {
p.backButton.emit(ev); p.backButton.emit(ev);
}); });
});
doc.addEventListener('pause', function(ev: Event) { doc.addEventListener('pause', function(ev: Event) {
p.zone.run(() => {
p.pause.emit(ev); p.pause.emit(ev);
}); });
});
doc.addEventListener('resume', function(ev: Event) { doc.addEventListener('resume', function(ev: Event) {
p.zone.run(() => {
p.resume.emit(ev); p.resume.emit(ev);
}); });
});
// cordova has its own exitApp method // cordova has its own exitApp method
p.exitApp = function() { p.exitApp = function() {

View File

@ -2,6 +2,74 @@ import {Platform} from '../../../src';
export function run() { export function run() {
describe('Platform', () => {
describe('registerBackButtonAction', () => {
it('should register two actions with different priorities, call the highest one', () => {
let platform = new Platform();
let ranAction1 = false;
let action1 = () => {
ranAction1 = true;
};
let ranAction2 = false;
let action2 = () => {
ranAction2= true;
};
platform.registerBackButtonAction(action1, 200);
platform.registerBackButtonAction(action2, 100);
platform.runBackButtonAction();
expect(ranAction1).toEqual(true);
expect(ranAction2).toEqual(false);
});
it('should register two actions with the same priority, call the second one', () => {
let platform = new Platform();
let ranAction1 = false;
let action1 = () => {
ranAction1 = true;
};
let ranAction2 = false;
let action2 = () => {
ranAction2= true;
};
platform.registerBackButtonAction(action1, 100);
platform.registerBackButtonAction(action2, 100);
platform.runBackButtonAction();
expect(ranAction1).toEqual(false);
expect(ranAction2).toEqual(true);
});
it('should register a default action', () => {
let platform = new Platform();
let ranAction1 = false;
let action1 = () => {
ranAction1 = true;
};
platform.registerBackButtonAction(action1);
platform.runBackButtonAction();
expect(ranAction1).toEqual(true);
});
it('should not run any actions when none registered', () => {
let platform = new Platform();
platform.runBackButtonAction();
});
});
it('should set core as the fallback', () => { it('should set core as the fallback', () => {
let platform = new Platform(); let platform = new Platform();
platform.setUserAgent('idk'); platform.setUserAgent('idk');
@ -257,6 +325,8 @@ export function run() {
expect(platform.is('tablet')).toEqual(false); expect(platform.is('tablet')).toEqual(false);
}); });
});
} }
const OSX_10_FIREFOX_43_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0'; const OSX_10_FIREFOX_43_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0';