Skip to content
This repository was archived by the owner on Nov 3, 2025. It is now read-only.

Commit a883adc

Browse files
committed
🚧 (BackHandler) pipe events to canals
1 parent f4f5595 commit a883adc

6 files changed

Lines changed: 81 additions & 26 deletions

File tree

src/Navigation/BackContext.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { createContext } from 'react';
2+
import { Observable } from 'rxjs';
3+
import { IBackEvent } from './BackHandlerDelegate';
4+
import { Navigation } from './Navigation';
5+
6+
interface IBackContext {
7+
back$: Observable<IBackEvent>;
8+
}
9+
10+
export const BackContext = createContext<IBackContext>(
11+
Navigation.instance.backHandlerDelegate.defaultBackContext
12+
);
Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,24 @@
11
import { BackHandler } from 'react-native';
22
import { fromEventPattern } from 'rxjs';
3-
import { map, elementAt } from 'rxjs/operators';
3+
4+
export interface IBackEvent {
5+
target: string | null;
6+
}
47

58
export class BackHandlerDelegate {
6-
private shouldExit: boolean = false;
7-
constructor() {
8-
const addHandler = (handler: () => void) => {
9-
BackHandler.addEventListener('hardwareBackPress', () => {
10-
handler();
11-
return !this.shouldExit;
9+
private static addEventListener = (handler: (event: IBackEvent) => void) => {
10+
BackHandler.addEventListener('hardwareBackPress', () => {
11+
handler({
12+
target: null
1213
});
13-
};
14-
const hardwareBacks$ = fromEventPattern<undefined>(addHandler);
15-
hardwareBacks$
16-
.pipe(map(this.resetShouldExit))
17-
.pipe(
18-
map(() => {
19-
// tslint:disable-next-line
20-
console.log("back");
21-
})
22-
)
23-
.pipe(elementAt(3))
24-
.subscribe(this.setShouldExit);
25-
}
26-
private resetShouldExit = () => {
27-
this.shouldExit = false;
28-
};
29-
private setShouldExit = () => {
30-
this.shouldExit = true;
14+
return true;
15+
});
3116
};
17+
18+
private back$ = fromEventPattern<IBackEvent>(
19+
BackHandlerDelegate.addEventListener
20+
);
21+
22+
// tslint:disable-next-line:member-ordering
23+
defaultBackContext = { back$: this.back$ };
3224
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import BackHandler from 'react-native/Libraries/Utilities/__mocks__/BackHandler';
2+
import { BackHandlerDelegate } from '../BackHandlerDelegate';
3+
4+
jest.mock('react-native', () => ({
5+
BackHandler: require.requireActual(
6+
'react-native/Libraries/Utilities/__mocks__/BackHandler'
7+
)
8+
}));
9+
10+
describe('BackHandlerDelegate', () => {
11+
it('exposes a root context which emits back events', () => {
12+
const backHandlerDelegate = new BackHandlerDelegate();
13+
backHandlerDelegate.defaultBackContext.back$.subscribe(event => {
14+
expect(event).toStrictEqual({ target: null });
15+
});
16+
BackHandler.mockPressBack();
17+
expect.assertions(1);
18+
});
19+
});

src/createCanal.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { ViewStyle, View, StyleSheet, StyleProp } from 'react-native';
33
import { Observer } from 'mobx-react/native';
44
import { fromStream } from 'mobx-utils';
55
import { Subject } from 'rxjs';
6-
import { map, distinctUntilChanged } from 'rxjs/operators';
6+
import { map, distinctUntilChanged, withLatestFrom } from 'rxjs/operators';
77
import { Navigation } from './Navigation';
8+
import { BackContext } from './Navigation/BackContext';
9+
import { last } from './utils/Array.last';
810

911
type CanalComponentProps<T> = {
1012
style?: StyleProp<ViewStyle>;
@@ -49,6 +51,9 @@ export const createCanal = <
4951
style: StyleSheet.absoluteFill
5052
};
5153

54+
static contextType = BackContext;
55+
context!: React.ContextType<typeof BackContext>;
56+
5257
canalId = Date.now().toString();
5358

5459
stopsList = stopsList;
@@ -126,6 +131,18 @@ export const createCanal = <
126131
}
127132

128133
render() {
134+
// @ts-ignore
135+
const back$ = this.context.back$
136+
.pipe(withLatestFrom(this.progress$))
137+
.pipe(
138+
map(([_, progress]) => {
139+
const currentStop = last(progress);
140+
if (currentStop) {
141+
return { target: currentStop.name };
142+
}
143+
return { target: null };
144+
})
145+
);
129146
return (
130147
<View style={this.props.style}>
131148
<Observer>

src/utils/Array.last.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export function last<T>(arr: T[]): T | null {
2+
if (arr.length < 1) { return null; }
3+
return arr[arr.length - 1];
4+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { last } from '../Array.last';
2+
3+
describe('Array.last', () => {
4+
it('returns null if arr is empty', () => {
5+
expect(last([])).toBeNull();
6+
});
7+
8+
it('returns the last element of the list', () => {
9+
expect(last([1, 2, 3])).toBe(3);
10+
});
11+
});

0 commit comments

Comments
 (0)