Skip to content

Commit 104e65a

Browse files
authored
Merge pull request #22678 from adamgrzybowski/@swm/navigation-refactor-fix-animation-glitch-for-swipe-ios-safari
[navigation-refactor] Fix animation glitch when navigating in the browser history with swiping iOS Safari
2 parents b48e9a6 + 036e8c3 commit 104e65a

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
diff --git a/node_modules/@react-navigation/stack/lib/module/utils/edgeDragGestureMonitor.js b/node_modules/@react-navigation/stack/lib/module/utils/edgeDragGestureMonitor.js
2+
new file mode 100644
3+
index 0000000..d8284fc
4+
--- /dev/null
5+
+++ b/node_modules/@react-navigation/stack/lib/module/utils/edgeDragGestureMonitor.js
6+
@@ -0,0 +1,44 @@
7+
+let isInitialized = false;
8+
+let justFinishedEdgeGestureFromLeft = false;
9+
+let expectingTouchend = false;
10+
+
11+
+/// This returns information if the user just performed edge gesture on iOS safari to navigate in the browser history.
12+
+export const getIsEdgeDragGesture = () => {
13+
+ return expectingTouchend || justFinishedEdgeGestureFromLeft;
14+
+};
15+
+
16+
+// We need to manualy reset this flag after deciding if there should be animation for navigation.
17+
+export const resetExpectingTouchendWithDelay = () => {
18+
+ setTimeout(() => {
19+
+ expectingTouchend = false;
20+
+ }, 100);
21+
+};
22+
+
23+
+export const maybeInitializeEdgeDragGestureMonitor = () => {
24+
+ if (isInitialized) {
25+
+ return;
26+
+ }
27+
+ isInitialized = true;
28+
+ let timer;
29+
+
30+
+ // Gestures that would trigger navigation forward are broken on iOS safari.
31+
+ // They don't have touchend event fired so we can look at expectingTouchEnd flag to detect if we should run animation.
32+
+ const handleTouchStart = () => {
33+
+ expectingTouchend = true;
34+
+ };
35+
+ const handleTouchEnd = e => {
36+
+ var _e$changedTouches$;
37+
+ const pageX = (_e$changedTouches$ = e.changedTouches[0]) === null || _e$changedTouches$ === void 0 ? void 0 : _e$changedTouches$.pageX;
38+
+ // PageX for gesture that would trigger navigation back is negative.
39+
+ if (pageX < 0) {
40+
+ if (timer) {
41+
+ clearTimeout(timer);
42+
+ }
43+
+ justFinishedEdgeGestureFromLeft = true;
44+
+ timer = setTimeout(() => justFinishedEdgeGestureFromLeft = false, 100);
45+
+ }
46+
+ expectingTouchend = false;
47+
+ };
48+
+ document.addEventListener('touchstart', handleTouchStart);
49+
+ document.addEventListener('touchend', handleTouchEnd);
50+
+};
51+
diff --git a/node_modules/@react-navigation/stack/lib/module/utils/edgeDragGestureMonitor.native.js b/node_modules/@react-navigation/stack/lib/module/utils/edgeDragGestureMonitor.native.js
52+
new file mode 100644
53+
index 0000000..668d198
54+
--- /dev/null
55+
+++ b/node_modules/@react-navigation/stack/lib/module/utils/edgeDragGestureMonitor.native.js
56+
@@ -0,0 +1,5 @@
57+
+// We don't need edgeDragGestureMonitor for native platforms.
58+
+
59+
+export const getIsEdgeDragGesture = () => false;
60+
+export const resetExpectingTouchendWithDelay = () => {};
61+
+export const maybeInitializeEdgeDragGestureMonitor = () => {};
62+
diff --git a/node_modules/@react-navigation/stack/lib/module/views/Stack/Card.js b/node_modules/@react-navigation/stack/lib/module/views/Stack/Card.js
63+
index 548ba79..4bedb81 100644
64+
--- a/node_modules/@react-navigation/stack/lib/module/views/Stack/Card.js
65+
+++ b/node_modules/@react-navigation/stack/lib/module/views/Stack/Card.js
66+
@@ -4,6 +4,7 @@ import * as React from 'react';
67+
import { Animated, InteractionManager, Platform, StyleSheet, View } from 'react-native';
68+
import { forModalPresentationIOS } from '../../TransitionConfigs/CardStyleInterpolators';
69+
import CardAnimationContext from '../../utils/CardAnimationContext';
70+
+import { getIsEdgeDragGesture, resetExpectingTouchendWithDelay } from '../../utils/edgeDragGestureMonitor';
71+
import getDistanceForDirection from '../../utils/getDistanceForDirection';
72+
import getInvertedMultiplier from '../../utils/getInvertedMultiplier';
73+
import memoize from '../../utils/memoize';
74+
@@ -121,6 +122,8 @@ export default class Card extends React.Component {
75+
});
76+
animation(gesture, {
77+
...spec.config,
78+
+ // Detecting if the user used swipe gesture on iOS safari to trigger navigation in the browser history.
79+
+ duration: getIsEdgeDragGesture() ? 0 : undefined,
80+
velocity,
81+
toValue,
82+
useNativeDriver,
83+
@@ -131,6 +134,8 @@ export default class Card extends React.Component {
84+
} = _ref3;
85+
this.handleEndInteraction();
86+
clearTimeout(this.pendingGestureCallback);
87+
+ // We need to reset edgeDragGestureMonitor manualy because of broken events on iOS safari.
88+
+ resetExpectingTouchendWithDelay();
89+
if (finished) {
90+
if (closing) {
91+
onClose();
92+
diff --git a/node_modules/@react-navigation/stack/lib/module/views/Stack/CardStack.js b/node_modules/@react-navigation/stack/lib/module/views/Stack/CardStack.js
93+
index 95e6871..7558eb3 100644
94+
--- a/node_modules/@react-navigation/stack/lib/module/views/Stack/CardStack.js
95+
+++ b/node_modules/@react-navigation/stack/lib/module/views/Stack/CardStack.js
96+
@@ -4,6 +4,7 @@ import * as React from 'react';
97+
import { Animated, Platform, StyleSheet } from 'react-native';
98+
import { forModalPresentationIOS, forNoAnimation as forNoAnimationCard } from '../../TransitionConfigs/CardStyleInterpolators';
99+
import { DefaultTransition, ModalFadeTransition, ModalTransition } from '../../TransitionConfigs/TransitionPresets';
100+
+import { maybeInitializeEdgeDragGestureMonitor } from '../../utils/edgeDragGestureMonitor';
101+
import findLastIndex from '../../utils/findLastIndex';
102+
import getDistanceForDirection from '../../utils/getDistanceForDirection';
103+
import { MaybeScreen, MaybeScreenContainer } from '../Screens';
104+
@@ -166,6 +167,8 @@ export default class CardStack extends React.Component {
105+
}
106+
constructor(props) {
107+
super(props);
108+
+ // Gesture monitor is only needed on iOS safari to detect if the user performed edge swipe gesture to to navigate in the browser history.
109+
+ maybeInitializeEdgeDragGestureMonitor();
110+
this.state = {
111+
routes: [],
112+
scenes: [],

0 commit comments

Comments
 (0)