Files
react-navigation/packages/compat/src/createCompatNavigationProp.tsx
Satyajit Sahoo 3a77107968 fix: drop isFirstRouteInParent method (#145)
The `isFirstRouteInParent` method was added to determine whether the back button should be shown in the header for stack navigator or not.
This was mainly due to the API of the old version of stack whose public API of header didn't have all required info to determine whether it should be shown.
It was probably a mistake to add it, because this method doesn't look at history and so pretty much useless for other navigators which aren't stack.

In the new stack, the header receives a `previous` prop and the `headerLeft` option receives a `canGoBack` prop which can be used for this purpose.
It's also possible to check if a route is the first by doing `navigation.dangerouslyGetState().routes[0].key === route.key`.

So it's time to drop this method.
2019-10-30 08:51:59 +01:00

196 lines
5.2 KiB
TypeScript

import {
NavigationState,
PartialState,
ParamListBase,
NavigationProp,
RouteProp,
} from '@react-navigation/core';
import * as helpers from './helpers';
import { CompatNavigationProp } from './types';
type EventName = 'willFocus' | 'willBlur' | 'didFocus' | 'didBlur' | 'refocus';
const focusSubscriptions = new WeakMap<() => void, () => void>();
const blurSubscriptions = new WeakMap<() => void, () => void>();
const refocusSubscriptions = new WeakMap<() => void, () => void>();
export default function createCompatNavigationProp<
NavigationPropType extends NavigationProp<ParamListBase>,
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
infer P
>
? P
: ParamListBase
>(
navigation: NavigationPropType,
state:
| (RouteProp<ParamList, keyof ParamList> & {
state?: NavigationState | PartialState<NavigationState>;
})
| NavigationState
| PartialState<NavigationState>
): CompatNavigationProp<NavigationPropType> {
return {
...navigation,
...Object.entries(helpers).reduce<{
[key: string]: (...args: any[]) => void;
}>((acc, [name, method]: [string, Function]) => {
if (name in navigation) {
acc[name] = (...args: any[]) => navigation.dispatch(method(...args));
}
return acc;
}, {}),
original: navigation,
addListener(type: EventName, callback: () => void) {
let unsubscribe: () => void;
switch (type) {
case 'willFocus':
unsubscribe = navigation.addListener('focus', callback);
break;
case 'willBlur':
unsubscribe = navigation.addListener('blur', callback);
break;
case 'didFocus': {
const listener = () => {
if (navigation.isFocused()) {
callback();
}
};
// @ts-ignore
unsubscribe = navigation.addListener('transitionEnd', listener);
focusSubscriptions.set(callback, unsubscribe);
break;
}
case 'didBlur': {
const listener = () => {
if (!navigation.isFocused()) {
callback();
}
};
// @ts-ignore
unsubscribe = navigation.addListener('transitionEnd', listener);
blurSubscriptions.set(callback, unsubscribe);
break;
}
case 'refocus': {
const listener = () => {
if (navigation.isFocused()) {
callback();
}
};
// @ts-ignore
unsubscribe = navigation.addListener('tabPress', listener);
refocusSubscriptions.set(callback, unsubscribe);
break;
}
default:
// @ts-ignore
unsubscribe = navigation.addListener(type, callback);
}
const subscription = () => unsubscribe();
subscription.remove = unsubscribe;
return subscription;
},
removeListener(type: EventName, callback: () => void) {
switch (type) {
case 'willFocus':
navigation.removeListener('focus', callback);
break;
case 'willBlur':
navigation.removeListener('blur', callback);
break;
case 'didFocus': {
const unsubscribe = focusSubscriptions.get(callback);
unsubscribe && unsubscribe();
break;
}
case 'didBlur': {
const unsubscribe = blurSubscriptions.get(callback);
unsubscribe && unsubscribe();
break;
}
case 'refocus': {
const unsubscribe = refocusSubscriptions.get(callback);
unsubscribe && unsubscribe();
break;
}
default:
// @ts-ignore
navigation.removeListener(type, callback);
}
},
state: {
...state,
// @ts-ignore
routeName: state.name,
get index() {
// @ts-ignore
if (state.index !== undefined) {
// @ts-ignore
return state.index;
}
console.warn(
"Accessing child navigation state for a route is not safe and won't work correctly."
);
// @ts-ignore
return state.state ? state.state.index : undefined;
},
get routes() {
// @ts-ignore
if (state.routes !== undefined) {
// @ts-ignore
return state.routes;
}
console.warn(
"Accessing child navigation state for a route is not safe and won't work correctly."
);
// @ts-ignore
return state.state ? state.state.routes : undefined;
},
},
getParam<T extends keyof ParamList>(
paramName: T,
defaultValue: ParamList[T]
): ParamList[T] {
// @ts-ignore
const params = state.params;
if (params && paramName in params) {
return params[paramName];
}
return defaultValue;
},
isFirstRouteInParent(): boolean {
const { routes } = navigation.dangerouslyGetState();
// @ts-ignore
return routes[0].key === state.key;
},
dangerouslyGetParent() {
const parent = navigation.dangerouslyGetParent();
if (parent) {
return createCompatNavigationProp(
parent,
navigation.dangerouslyGetState()
);
}
return undefined;
},
} as any;
}