Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3821c04

Browse filesBrowse files
authored
feat(): add ability to continue processing hardware back button events (ionic-team#20613)
fixes ionic-team#17824
1 parent 429edb0 commit 3821c04
Copy full SHA for 3821c04

8 files changed

+113-35Lines changed: 113 additions & 35 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎angular/src/providers/nav-controller.ts‎

Copy file name to clipboardExpand all lines: angular/src/providers/nav-controller.ts
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ export class NavController {
4545
}
4646

4747
// Subscribe to backButton events
48-
platform.backButton.subscribeWithPriority(0, () => this.pop());
48+
platform.backButton.subscribeWithPriority(0, processNextHandler => {
49+
this.pop();
50+
processNextHandler();
51+
});
4952
}
5053

5154
/**
Collapse file

‎angular/src/providers/platform.ts‎

Copy file name to clipboardExpand all lines: angular/src/providers/platform.ts
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { BackButtonEventDetail, Platforms, getPlatforms, isPlatform } from '@ion
44
import { Subject, Subscription } from 'rxjs';
55

66
export interface BackButtonEmitter extends Subject<BackButtonEventDetail> {
7-
subscribeWithPriority(priority: number, callback: () => Promise<any> | void): Subscription;
7+
subscribeWithPriority(priority: number, callback: (processNextHandler: () => void) => Promise<any> | void): Subscription;
88
}
99

1010
@Injectable({
@@ -46,9 +46,9 @@ export class Platform {
4646
zone.run(() => {
4747
this.win = doc.defaultView;
4848
this.backButton.subscribeWithPriority = function(priority, callback) {
49-
return this.subscribe(ev => (
50-
ev.register(priority, () => zone.run(callback))
51-
));
49+
return this.subscribe(ev => {
50+
return ev.register(priority, processNextHandler => zone.run(() => callback(processNextHandler)));
51+
});
5252
};
5353

5454
proxyEvent(this.pause, doc, 'pause');
Collapse file

‎core/src/components/router/router.tsx‎

Copy file name to clipboardExpand all lines: core/src/components/router/router.tsx
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ export class Router implements ComponentInterface {
7979

8080
@Listen('ionBackButton', { target: 'document' })
8181
protected onBackButton(ev: BackButtonEvent) {
82-
ev.detail.register(0, () => this.back());
82+
ev.detail.register(0, processNextHandler => {
83+
this.back();
84+
processNextHandler();
85+
});
8386
}
8487

8588
/**
Collapse file

‎core/src/interface.d.ts‎

Copy file name to clipboardExpand all lines: core/src/interface.d.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export interface FrameworkDelegate {
5353
}
5454

5555
export interface BackButtonEventDetail {
56-
register(priority: number, handler: () => Promise<any> | void): void;
56+
register(priority: number, handler: (processNextHandler: () => void) => Promise<any> | void): void;
5757
}
5858

5959
export interface StyleEventDetail {
Collapse file

‎core/src/utils/hardware-back-button.ts‎

Copy file name to clipboardExpand all lines: core/src/utils/hardware-back-button.ts
+35-26Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { BackButtonEvent } from '../interface';
22

3-
type Handler = () => Promise<any> | void | null;
3+
type Handler = (processNextHandler: () => void) => Promise<any> | void | null;
44

55
interface HandlerRegister {
66
priority: number;
77
handler: Handler;
8+
id: number;
89
}
910

1011
export const startHardwareBackButton = () => {
@@ -16,44 +17,52 @@ export const startHardwareBackButton = () => {
1617
return;
1718
}
1819

19-
const handlers: HandlerRegister[] = [];
20+
let index = 0;
21+
let handlers: HandlerRegister[] = [];
2022
const ev: BackButtonEvent = new CustomEvent('ionBackButton', {
2123
bubbles: false,
2224
detail: {
2325
register(priority: number, handler: Handler) {
24-
handlers.push({ priority, handler });
26+
handlers.push({ priority, handler, id: index++ });
2527
}
2628
}
2729
});
2830
doc.dispatchEvent(ev);
2931

30-
if (handlers.length > 0) {
31-
let selectedPriority = Number.MIN_SAFE_INTEGER;
32-
let selectedHandler: Handler | undefined;
33-
handlers.forEach(({ priority, handler }) => {
34-
if (priority >= selectedPriority) {
35-
selectedPriority = priority;
36-
selectedHandler = handler;
32+
const executeAction = async (handlerRegister: HandlerRegister | undefined) => {
33+
try {
34+
if (handlerRegister && handlerRegister.handler) {
35+
const result = handlerRegister.handler(processHandlers);
36+
if (result != null) {
37+
await result;
38+
}
3739
}
38-
});
40+
} catch (e) {
41+
console.error(e);
42+
}
43+
};
3944

40-
busy = true;
41-
executeAction(selectedHandler).then(() => busy = false);
42-
}
43-
});
44-
};
45+
const processHandlers = () => {
46+
if (handlers.length > 0) {
47+
let selectedHandler: HandlerRegister = {
48+
priority: Number.MIN_SAFE_INTEGER,
49+
handler: () => undefined,
50+
id: -1
51+
};
52+
handlers.forEach(handler => {
53+
if (handler.priority >= selectedHandler.priority) {
54+
selectedHandler = handler;
55+
}
56+
});
4557

46-
const executeAction = async (handler: Handler | undefined) => {
47-
try {
48-
if (handler) {
49-
const result = handler();
50-
if (result != null) {
51-
await result;
58+
busy = true;
59+
handlers = handlers.filter(handler => handler.id !== selectedHandler.id);
60+
executeAction(selectedHandler).then(() => busy = false);
5261
}
53-
}
54-
} catch (e) {
55-
console.error(e);
56-
}
62+
};
63+
64+
processHandlers();
65+
});
5766
};
5867

5968
export const OVERLAY_BACK_BUTTON_PRIORITY = 100;
Collapse file
+59Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { startHardwareBackButton } from '../hardware-back-button';
2+
3+
describe('Hardware Back Button', () => {
4+
beforeEach(() => startHardwareBackButton());
5+
it('should call handler', () => {
6+
const cbSpy = jest.fn();
7+
document.addEventListener('ionBackButton', (ev) => {
8+
ev.detail.register(0, cbSpy);
9+
});
10+
11+
dispatchBackButtonEvent();
12+
expect(cbSpy).toHaveBeenCalled();
13+
});
14+
15+
it('should call handlers in order of priority', () => {
16+
const cbSpy = jest.fn();
17+
const cbSpyTwo = jest.fn();
18+
document.addEventListener('ionBackButton', (ev) => {
19+
ev.detail.register(100, cbSpy);
20+
ev.detail.register(99, cbSpyTwo);
21+
});
22+
23+
dispatchBackButtonEvent();
24+
expect(cbSpy).toHaveBeenCalled();
25+
expect(cbSpyTwo).not.toHaveBeenCalled();
26+
});
27+
28+
it('should only call last handler to be added for handlers with same priority', () => {
29+
const cbSpy = jest.fn();
30+
const cbSpyTwo = jest.fn();
31+
document.addEventListener('ionBackButton', (ev) => {
32+
ev.detail.register(100, cbSpy);
33+
ev.detail.register(100, cbSpyTwo);
34+
});
35+
36+
dispatchBackButtonEvent();
37+
expect(cbSpy).not.toHaveBeenCalled();
38+
expect(cbSpyTwo).toHaveBeenCalled();
39+
});
40+
41+
it('should call multiple callbacks', () => {
42+
const cbSpy = (processNextHandler) => {
43+
processNextHandler();
44+
}
45+
const cbSpyTwo = jest.fn();
46+
document.addEventListener('ionBackButton', (ev) => {
47+
ev.detail.register(100, cbSpy);
48+
ev.detail.register(99, cbSpyTwo);
49+
});
50+
51+
dispatchBackButtonEvent();
52+
expect(cbSpyTwo).toHaveBeenCalled();
53+
});
54+
});
55+
56+
const dispatchBackButtonEvent = () => {
57+
const ev = new Event('backbutton');
58+
document.dispatchEvent(ev);
59+
}
Collapse file

‎packages/react-router/src/ReactRouter/NavManager.tsx‎

Copy file name to clipboardExpand all lines: packages/react-router/src/ReactRouter/NavManager.tsx
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ export class NavManager extends React.Component<NavManagerProps, NavContextState
3636

3737
if (document) {
3838
document.addEventListener('ionBackButton', (e: any) => {
39-
e.detail.register(0, () => {
39+
e.detail.register(0, (processNextHandler: () => void) => {
4040
this.props.history.goBack();
41+
processNextHandler();
4142
});
4243
});
4344
}
Collapse file

‎vue/src/router.ts‎

Copy file name to clipboardExpand all lines: vue/src/router.ts
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ export default class Router extends VueRouter {
3737

3838
// Listen to Ionic's back button event
3939
document.addEventListener('ionBackButton', (e: Event) => {
40-
(e as BackButtonEvent).detail.register(0, () => this.back());
40+
(e as BackButtonEvent).detail.register(0, processNextHandler => {
41+
this.back();
42+
processNextHandler();
43+
});
4144
});
4245
}
4346

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.