fix: dispatch pop early when screen is closed with gesture (#336)

fixes #267
This commit is contained in:
Satyajit Sahoo
2020-02-02 23:56:05 +01:00
committed by GitHub
parent 343320783f
commit 3d937d1e65
7 changed files with 44 additions and 19 deletions

View File

@@ -12,7 +12,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 12.x
node-version: 10.x
- name: Setup Expo
uses: expo/expo-github-action@v5

View File

@@ -286,7 +286,7 @@ export default function App() {
<List.Item
key={name}
title={SCREENS[name].title}
onPress={() => navigation.push(name)}
onPress={() => navigation.navigate(name)}
/>
)
)}

View File

@@ -5,7 +5,6 @@ import {
Route,
ParamListBase,
} from '@react-navigation/native';
import { StackNavigationState } from '@react-navigation/routers';
import { EdgeInsets } from 'react-native-safe-area-context';
import Header from './Header';
@@ -28,10 +27,10 @@ export type Props = {
layout: Layout;
insets: EdgeInsets;
scenes: (Scene<Route<string>> | undefined)[];
state: StackNavigationState;
getPreviousRoute: (props: {
route: Route<string>;
}) => Route<string> | undefined;
getFocusedRoute: () => Route<string>;
onContentHeightChange?: (props: {
route: Route<string>;
height: number;
@@ -46,14 +45,14 @@ export default function HeaderContainer({
scenes,
layout,
insets,
state,
getFocusedRoute,
getPreviousRoute,
onContentHeightChange,
gestureDirection,
styleInterpolator,
style,
}: Props) {
const focusedRoute = state.routes[state.index];
const focusedRoute = getFocusedRoute();
return (
<View pointerEvents="box-none" style={style}>

View File

@@ -133,6 +133,8 @@ export default class Card extends React.Component<Props> {
private interactionHandle: number | undefined;
private pendingGestureCallback: number | undefined;
private animate = ({
closing,
velocity,
@@ -161,6 +163,8 @@ export default class Card extends React.Component<Props> {
this.setPointerEventsEnabled(!closing);
this.handleStartInteraction();
clearTimeout(this.pendingGestureCallback);
onTransitionStart?.({ closing });
animation(gesture, {
...spec.config,
@@ -171,6 +175,8 @@ export default class Card extends React.Component<Props> {
}).start(({ finished }) => {
this.handleEndInteraction();
clearTimeout(this.pendingGestureCallback);
if (finished) {
if (closing) {
onClose();
@@ -221,6 +227,7 @@ export default class Card extends React.Component<Props> {
}: PanGestureHandlerGestureEvent) => {
const {
layout,
onClose,
onGestureBegin,
onGestureCanceled,
onGestureEnd,
@@ -266,6 +273,16 @@ export default class Card extends React.Component<Props> {
: false;
this.animate({ closing, velocity });
if (closing) {
// We call onClose with a delay to make sure that the animation has already started
// This will make sure that the state update caused by this doesn't affect start of animation
this.pendingGestureCallback = (setTimeout(
onClose,
32
) as any) as number;
}
onGestureEnd?.();
break;
}

View File

@@ -7,7 +7,6 @@ import {
ViewStyle,
Platform,
} from 'react-native';
import { StackNavigationState } from '@react-navigation/routers';
import { Route, useTheme } from '@react-navigation/native';
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
import Card from './Card';
@@ -23,7 +22,6 @@ type Props = TransitionPreset & {
gesture: Animated.Value;
previousScene?: Scene<Route<string>>;
scene: Scene<Route<string>>;
state: StackNavigationState;
safeAreaInsetTop: number;
safeAreaInsetRight: number;
safeAreaInsetBottom: number;
@@ -34,6 +32,7 @@ type Props = TransitionPreset & {
getPreviousRoute: (props: {
route: Route<string>;
}) => Route<string> | undefined;
getFocusedRoute: () => Route<string>;
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
renderScene: (props: { route: Route<string> }) => React.ReactNode;
onOpenRoute: (props: { route: Route<string> }) => void;
@@ -78,6 +77,7 @@ function CardContainer({
gestureResponseDistance,
gestureVelocityImpact,
getPreviousRoute,
getFocusedRoute,
headerMode,
headerShown,
headerStyleInterpolator,
@@ -101,7 +101,6 @@ function CardContainer({
safeAreaInsetRight,
safeAreaInsetTop,
scene,
state,
transitionSpec,
}: Props) {
React.useEffect(() => {
@@ -203,8 +202,8 @@ function CardContainer({
layout,
insets,
scenes: [previousScene, scene],
state,
getPreviousRoute,
getFocusedRoute,
gestureDirection,
styleInterpolator: headerStyleInterpolator,
onContentHeightChange: onHeaderHeightChange,

View File

@@ -398,6 +398,12 @@ export default class CardStack extends React.Component<Props, State> {
});
};
private getFocusedRoute = () => {
const { state } = this.props;
return state.routes[state.index];
};
render() {
const {
mode,
@@ -581,7 +587,6 @@ export default class CardStack extends React.Component<Props, State> {
gesture={gesture}
scene={scene}
previousScene={previousScene}
state={state}
safeAreaInsetTop={safeAreaInsetTop}
safeAreaInsetRight={safeAreaInsetRight}
safeAreaInsetBottom={safeAreaInsetBottom}
@@ -596,6 +601,7 @@ export default class CardStack extends React.Component<Props, State> {
headerHeight={headerHeights[route.key]}
onHeaderHeightChange={this.handleHeaderLayout}
getPreviousRoute={getPreviousRoute}
getFocusedRoute={this.getFocusedRoute}
headerMode={headerMode}
headerShown={headerShown}
headerTransparent={headerTransparent}
@@ -619,8 +625,8 @@ export default class CardStack extends React.Component<Props, State> {
layout,
insets: { top, right, bottom, left },
scenes,
state,
getPreviousRoute,
getFocusedRoute: this.getFocusedRoute,
onContentHeightChange: this.handleHeaderLayout,
gestureDirection:
focusedOptions.gestureDirection !== undefined

View File

@@ -314,14 +314,18 @@ class StackView extends React.Component<Props, State> {
source: route.key,
target: state.key,
});
} else {
// We need to clean up any state tracking the route and pop it immediately
this.setState(state => ({
routes: state.routes.filter(r => r.key !== route.key),
openingRouteKeys: state.openingRouteKeys.filter(
key => key !== route.key
),
closingRouteKeys: state.closingRouteKeys.filter(
key => key !== route.key
),
}));
}
// We need to clean up any state tracking the route and pop it immediately
this.setState(state => ({
routes: state.routes.filter(r => r.key !== route.key),
openingRouteKeys: state.openingRouteKeys.filter(key => key !== route.key),
closingRouteKeys: state.closingRouteKeys.filter(key => key !== route.key),
}));
};
private handleTransitionStart = (