mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix drawer swipe (#7007)

* Fix drawer swipe

* Remove existing setDrawerSwipeDisabled management

This is already pretty error-prone. And with tracking whether we're idle it's going to get more complicated. Let's pause and think.

* Move setDrawerSwipeDisabled logic into Pager

* Remove win/2 threshold

It feels super arbitrary and breaks muscle memory. If the gesture is reliable, we shouldn't need it.

* Maybe work around iOS freeze

* Tweak gestures, add comments

* Tune gestures

authored by danabra.mov and committed by

GitHub 46e1e5ce fec3352b

+941 -38
+876
patches/react-native-drawer-layout+4.0.4.patch
··· 1 + diff --git a/node_modules/react-native-drawer-layout/lib/commonjs/index.js b/node_modules/react-native-drawer-layout/lib/commonjs/index.js 2 + index 3dce76e..6c4b3e5 100644 3 + --- a/node_modules/react-native-drawer-layout/lib/commonjs/index.js 4 + +++ b/node_modules/react-native-drawer-layout/lib/commonjs/index.js 5 + @@ -9,6 +9,12 @@ Object.defineProperty(exports, "Drawer", { 6 + return _Drawer.Drawer; 7 + } 8 + }); 9 + +Object.defineProperty(exports, "DrawerGestureContext", { 10 + + enumerable: true, 11 + + get: function () { 12 + + return _DrawerGestureContext.DrawerGestureContext; 13 + + } 14 + +}); 15 + Object.defineProperty(exports, "DrawerProgressContext", { 16 + enumerable: true, 17 + get: function () { 18 + @@ -21,6 +27,7 @@ Object.defineProperty(exports, "useDrawerProgress", { 19 + return _useDrawerProgress.useDrawerProgress; 20 + } 21 + }); 22 + +var _DrawerGestureContext = require("./utils/DrawerGestureContext.js"); 23 + var _DrawerProgressContext = require("./utils/DrawerProgressContext.js"); 24 + var _useDrawerProgress = require("./utils/useDrawerProgress.js"); 25 + var _Drawer = require("./views/Drawer"); 26 + diff --git a/node_modules/react-native-drawer-layout/lib/commonjs/utils/DrawerGestureContext.js b/node_modules/react-native-drawer-layout/lib/commonjs/utils/DrawerGestureContext.js 27 + new file mode 100644 28 + index 0000000..de6d793 29 + --- /dev/null 30 + +++ b/node_modules/react-native-drawer-layout/lib/commonjs/utils/DrawerGestureContext.js 31 + @@ -0,0 +1,11 @@ 32 + +"use strict"; 33 + + 34 + +Object.defineProperty(exports, "__esModule", { 35 + + value: true 36 + +}); 37 + +exports.DrawerGestureContext = void 0; 38 + +var React = _interopRequireWildcard(require("react")); 39 + +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } 40 + +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 41 + +const DrawerGestureContext = exports.DrawerGestureContext = /*#__PURE__*/React.createContext(undefined); 42 + +//# sourceMappingURL=DrawerGestureContext.js.map 43 + \ No newline at end of file 44 + diff --git a/node_modules/react-native-drawer-layout/lib/commonjs/views/Drawer.native.js b/node_modules/react-native-drawer-layout/lib/commonjs/views/Drawer.native.js 45 + index fd65ab8..86a9a6a 100644 46 + --- a/node_modules/react-native-drawer-layout/lib/commonjs/views/Drawer.native.js 47 + +++ b/node_modules/react-native-drawer-layout/lib/commonjs/views/Drawer.native.js 48 + @@ -8,6 +8,7 @@ var React = _interopRequireWildcard(require("react")); 49 + var _reactNative = require("react-native"); 50 + var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated")); 51 + var _useLatestCallback = _interopRequireDefault(require("use-latest-callback")); 52 + +var _DrawerGestureContext = require("../utils/DrawerGestureContext.js"); 53 + var _DrawerProgressContext = require("../utils/DrawerProgressContext.js"); 54 + var _getDrawerWidth = require("../utils/getDrawerWidth.js"); 55 + var _GestureHandler = require("./GestureHandler"); 56 + @@ -79,38 +80,38 @@ function Drawer({ 57 + return () => hideStatusBar(false); 58 + }, [isOpen, hideStatusBarOnOpen, statusBarAnimation, hideStatusBar]); 59 + const interactionHandleRef = React.useRef(null); 60 + - const startInteraction = () => { 61 + + const startInteraction = React.useCallback(() => { 62 + interactionHandleRef.current = _reactNative.InteractionManager.createInteractionHandle(); 63 + - }; 64 + - const endInteraction = () => { 65 + + }, []); 66 + + const endInteraction = React.useCallback(() => { 67 + if (interactionHandleRef.current != null) { 68 + _reactNative.InteractionManager.clearInteractionHandle(interactionHandleRef.current); 69 + interactionHandleRef.current = null; 70 + } 71 + - }; 72 + - const hideKeyboard = () => { 73 + + }, []); 74 + + const hideKeyboard = React.useCallback(() => { 75 + if (keyboardDismissMode === 'on-drag') { 76 + _reactNative.Keyboard.dismiss(); 77 + } 78 + - }; 79 + - const onGestureBegin = () => { 80 + + }, [keyboardDismissMode]); 81 + + const onGestureBegin = React.useCallback(() => { 82 + onGestureStart?.(); 83 + startInteraction(); 84 + hideKeyboard(); 85 + hideStatusBar(true); 86 + - }; 87 + - const onGestureFinish = () => { 88 + + }, [onGestureStart, startInteraction, hideKeyboard, hideStatusBar]); 89 + + const onGestureFinish = React.useCallback(() => { 90 + onGestureEnd?.(); 91 + endInteraction(); 92 + - }; 93 + - const onGestureAbort = () => { 94 + + }, [onGestureEnd, endInteraction]); 95 + + const onGestureAbort = React.useCallback(() => { 96 + onGestureCancel?.(); 97 + endInteraction(); 98 + - }; 99 + + }, [onGestureCancel, endInteraction]); 100 + 101 + // FIXME: Currently hitSlop is broken when on Android when drawer is on right 102 + // https://github.com/software-mansion/react-native-gesture-handler/issues/569 103 + - const hitSlop = isRight ? 104 + + const hitSlop = React.useMemo(() => isRight ? 105 + // Extend hitSlop to the side of the screen when drawer is closed 106 + // This lets the user drag the drawer from the side of the screen 107 + { 108 + @@ -119,7 +120,7 @@ function Drawer({ 109 + } : { 110 + left: 0, 111 + width: isOpen ? undefined : swipeEdgeWidth 112 + - }; 113 + + }, [isRight, isOpen, swipeEdgeWidth]); 114 + const touchStartX = (0, _reactNativeReanimated.useSharedValue)(0); 115 + const touchX = (0, _reactNativeReanimated.useSharedValue)(0); 116 + const translationX = (0, _reactNativeReanimated.useSharedValue)(getDrawerTranslationX(open)); 117 + @@ -158,40 +159,43 @@ function Drawer({ 118 + }, [getDrawerTranslationX, handleAnimationEnd, handleAnimationStart, onClose, onOpen, touchStartX, touchX, translationX]); 119 + React.useEffect(() => toggleDrawer(open), [open, toggleDrawer]); 120 + const startX = (0, _reactNativeReanimated.useSharedValue)(0); 121 + - let pan = _GestureHandler.Gesture?.Pan().onBegin(event => { 122 + - 'worklet'; 123 + + const pan = React.useMemo(() => { 124 + + let panGesture = _GestureHandler.Gesture?.Pan().onBegin(event => { 125 + + 'worklet'; 126 + 127 + - startX.value = translationX.value; 128 + - gestureState.value = event.state; 129 + - touchStartX.value = event.x; 130 + - }).onStart(() => { 131 + - 'worklet'; 132 + + startX.value = translationX.value; 133 + + gestureState.value = event.state; 134 + + touchStartX.value = event.x; 135 + + }).onStart(() => { 136 + + 'worklet'; 137 + 138 + - (0, _reactNativeReanimated.runOnJS)(onGestureBegin)(); 139 + - }).onChange(event => { 140 + - 'worklet'; 141 + + (0, _reactNativeReanimated.runOnJS)(onGestureBegin)(); 142 + + }).onChange(event => { 143 + + 'worklet'; 144 + 145 + - touchX.value = event.x; 146 + - translationX.value = startX.value + event.translationX; 147 + - gestureState.value = event.state; 148 + - }).onEnd((event, success) => { 149 + - 'worklet'; 150 + + touchX.value = event.x; 151 + + translationX.value = startX.value + event.translationX; 152 + + gestureState.value = event.state; 153 + + }).onEnd((event, success) => { 154 + + 'worklet'; 155 + 156 + - gestureState.value = event.state; 157 + - if (!success) { 158 + - (0, _reactNativeReanimated.runOnJS)(onGestureAbort)(); 159 + + gestureState.value = event.state; 160 + + if (!success) { 161 + + (0, _reactNativeReanimated.runOnJS)(onGestureAbort)(); 162 + + } 163 + + const nextOpen = Math.abs(event.translationX) > SWIPE_MIN_OFFSET && Math.abs(event.translationX) > swipeMinVelocity || Math.abs(event.translationX) > swipeMinDistance ? drawerPosition === 'left' ? 164 + + // If swiped to right, open the drawer, otherwise close it 165 + + (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 : 166 + + // If swiped to left, open the drawer, otherwise close it 167 + + (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 : open; 168 + + toggleDrawer(nextOpen, event.velocityX); 169 + + (0, _reactNativeReanimated.runOnJS)(onGestureFinish)(); 170 + + }).activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).hitSlop(hitSlop).enabled(drawerType !== 'permanent' && swipeEnabled); 171 + + if (panGesture && configureGestureHandler) { 172 + + panGesture = configureGestureHandler(panGesture); 173 + } 174 + - const nextOpen = Math.abs(event.translationX) > SWIPE_MIN_OFFSET && Math.abs(event.translationX) > swipeMinVelocity || Math.abs(event.translationX) > swipeMinDistance ? drawerPosition === 'left' ? 175 + - // If swiped to right, open the drawer, otherwise close it 176 + - (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 : 177 + - // If swiped to left, open the drawer, otherwise close it 178 + - (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 : open; 179 + - toggleDrawer(nextOpen, event.velocityX); 180 + - (0, _reactNativeReanimated.runOnJS)(onGestureFinish)(); 181 + - }).activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).hitSlop(hitSlop).enabled(drawerType !== 'permanent' && swipeEnabled); 182 + - if (pan && configureGestureHandler) { 183 + - pan = configureGestureHandler(pan); 184 + - } 185 + + return panGesture; 186 + + }, [configureGestureHandler, drawerPosition, drawerType, gestureState, hitSlop, onGestureBegin, onGestureAbort, onGestureFinish, open, startX, swipeEnabled, swipeMinDistance, swipeMinVelocity, toggleDrawer, touchStartX, touchX, translationX]); 187 + const translateX = (0, _reactNativeReanimated.useDerivedValue)(() => { 188 + // Comment stolen from react-native-gesture-handler/DrawerLayout 189 + // 190 + @@ -254,35 +258,38 @@ function Drawer({ 191 + style: [styles.container, style], 192 + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_DrawerProgressContext.DrawerProgressContext.Provider, { 193 + value: progress, 194 + - children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_GestureHandler.GestureDetector, { 195 + - gesture: pan, 196 + - children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, { 197 + - style: [styles.main, { 198 + - flexDirection: drawerType === 'permanent' ? isRight && direction === 'ltr' || !isRight && direction === 'rtl' ? 'row' : 'row-reverse' : 'row' 199 + - }], 200 + - children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, { 201 + - style: [styles.content, contentAnimatedStyle], 202 + - children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { 203 + - accessibilityElementsHidden: isOpen && drawerType !== 'permanent', 204 + - importantForAccessibility: isOpen && drawerType !== 'permanent' ? 'no-hide-descendants' : 'auto', 205 + - style: styles.content, 206 + - children: children 207 + - }), drawerType !== 'permanent' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Overlay.Overlay, { 208 + - open: open, 209 + - progress: progress, 210 + - onPress: () => toggleDrawer(false), 211 + - style: overlayStyle, 212 + - accessibilityLabel: overlayAccessibilityLabel 213 + - }) : null] 214 + - }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, { 215 + - removeClippedSubviews: _reactNative.Platform.OS !== 'ios', 216 + - style: [styles.drawer, { 217 + - width: drawerWidth, 218 + - position: drawerType === 'permanent' ? 'relative' : 'absolute', 219 + - zIndex: drawerType === 'back' ? -1 : 0 220 + - }, drawerAnimatedStyle, drawerStyle], 221 + - children: renderDrawerContent() 222 + - })] 223 + + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_DrawerGestureContext.DrawerGestureContext.Provider, { 224 + + value: pan, 225 + + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_GestureHandler.GestureDetector, { 226 + + gesture: pan, 227 + + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, { 228 + + style: [styles.main, { 229 + + flexDirection: drawerType === 'permanent' ? isRight && direction === 'ltr' || !isRight && direction === 'rtl' ? 'row' : 'row-reverse' : 'row' 230 + + }], 231 + + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, { 232 + + style: [styles.content, contentAnimatedStyle], 233 + + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { 234 + + accessibilityElementsHidden: isOpen && drawerType !== 'permanent', 235 + + importantForAccessibility: isOpen && drawerType !== 'permanent' ? 'no-hide-descendants' : 'auto', 236 + + style: styles.content, 237 + + children: children 238 + + }), drawerType !== 'permanent' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Overlay.Overlay, { 239 + + open: open, 240 + + progress: progress, 241 + + onPress: () => toggleDrawer(false), 242 + + style: overlayStyle, 243 + + accessibilityLabel: overlayAccessibilityLabel 244 + + }) : null] 245 + + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, { 246 + + removeClippedSubviews: _reactNative.Platform.OS !== 'ios', 247 + + style: [styles.drawer, { 248 + + width: drawerWidth, 249 + + position: drawerType === 'permanent' ? 'relative' : 'absolute', 250 + + zIndex: drawerType === 'back' ? -1 : 0 251 + + }, drawerAnimatedStyle, drawerStyle], 252 + + children: renderDrawerContent() 253 + + })] 254 + + }) 255 + }) 256 + }) 257 + }) 258 + diff --git a/node_modules/react-native-drawer-layout/lib/module/index.js b/node_modules/react-native-drawer-layout/lib/module/index.js 259 + index a600e51..f08275c 100644 260 + --- a/node_modules/react-native-drawer-layout/lib/module/index.js 261 + +++ b/node_modules/react-native-drawer-layout/lib/module/index.js 262 + @@ -1,5 +1,6 @@ 263 + "use strict"; 264 + 265 + +export { DrawerGestureContext } from "./utils/DrawerGestureContext.js"; 266 + export { DrawerProgressContext } from "./utils/DrawerProgressContext.js"; 267 + export { useDrawerProgress } from "./utils/useDrawerProgress.js"; 268 + export { Drawer } from './views/Drawer'; 269 + diff --git a/node_modules/react-native-drawer-layout/lib/module/utils/DrawerGestureContext.js b/node_modules/react-native-drawer-layout/lib/module/utils/DrawerGestureContext.js 270 + new file mode 100644 271 + index 0000000..1adaa9c 272 + --- /dev/null 273 + +++ b/node_modules/react-native-drawer-layout/lib/module/utils/DrawerGestureContext.js 274 + @@ -0,0 +1,5 @@ 275 + +"use strict"; 276 + + 277 + +import * as React from 'react'; 278 + +export const DrawerGestureContext = /*#__PURE__*/React.createContext(undefined); 279 + +//# sourceMappingURL=DrawerGestureContext.js.map 280 + \ No newline at end of file 281 + diff --git a/node_modules/react-native-drawer-layout/lib/module/views/Drawer.native.js b/node_modules/react-native-drawer-layout/lib/module/views/Drawer.native.js 282 + index 6d07126..981d9f8 100644 283 + --- a/node_modules/react-native-drawer-layout/lib/module/views/Drawer.native.js 284 + +++ b/node_modules/react-native-drawer-layout/lib/module/views/Drawer.native.js 285 + @@ -4,6 +4,7 @@ import * as React from 'react'; 286 + import { I18nManager, InteractionManager, Keyboard, Platform, StatusBar, StyleSheet, useWindowDimensions, View } from 'react-native'; 287 + import Animated, { interpolate, ReduceMotion, runOnJS, useAnimatedStyle, useDerivedValue, useSharedValue, withSpring } from 'react-native-reanimated'; 288 + import useLatestCallback from 'use-latest-callback'; 289 + +import { DrawerGestureContext } from "../utils/DrawerGestureContext.js"; 290 + import { DrawerProgressContext } from "../utils/DrawerProgressContext.js"; 291 + import { getDrawerWidth } from "../utils/getDrawerWidth.js"; 292 + import { Gesture, GestureDetector, GestureHandlerRootView, GestureState } from './GestureHandler'; 293 + @@ -72,38 +73,38 @@ export function Drawer({ 294 + return () => hideStatusBar(false); 295 + }, [isOpen, hideStatusBarOnOpen, statusBarAnimation, hideStatusBar]); 296 + const interactionHandleRef = React.useRef(null); 297 + - const startInteraction = () => { 298 + + const startInteraction = React.useCallback(() => { 299 + interactionHandleRef.current = InteractionManager.createInteractionHandle(); 300 + - }; 301 + - const endInteraction = () => { 302 + + }, []); 303 + + const endInteraction = React.useCallback(() => { 304 + if (interactionHandleRef.current != null) { 305 + InteractionManager.clearInteractionHandle(interactionHandleRef.current); 306 + interactionHandleRef.current = null; 307 + } 308 + - }; 309 + - const hideKeyboard = () => { 310 + + }, []); 311 + + const hideKeyboard = React.useCallback(() => { 312 + if (keyboardDismissMode === 'on-drag') { 313 + Keyboard.dismiss(); 314 + } 315 + - }; 316 + - const onGestureBegin = () => { 317 + + }, [keyboardDismissMode]); 318 + + const onGestureBegin = React.useCallback(() => { 319 + onGestureStart?.(); 320 + startInteraction(); 321 + hideKeyboard(); 322 + hideStatusBar(true); 323 + - }; 324 + - const onGestureFinish = () => { 325 + + }, [onGestureStart, startInteraction, hideKeyboard, hideStatusBar]); 326 + + const onGestureFinish = React.useCallback(() => { 327 + onGestureEnd?.(); 328 + endInteraction(); 329 + - }; 330 + - const onGestureAbort = () => { 331 + + }, [onGestureEnd, endInteraction]); 332 + + const onGestureAbort = React.useCallback(() => { 333 + onGestureCancel?.(); 334 + endInteraction(); 335 + - }; 336 + + }, [onGestureCancel, endInteraction]); 337 + 338 + // FIXME: Currently hitSlop is broken when on Android when drawer is on right 339 + // https://github.com/software-mansion/react-native-gesture-handler/issues/569 340 + - const hitSlop = isRight ? 341 + + const hitSlop = React.useMemo(() => isRight ? 342 + // Extend hitSlop to the side of the screen when drawer is closed 343 + // This lets the user drag the drawer from the side of the screen 344 + { 345 + @@ -112,7 +113,7 @@ export function Drawer({ 346 + } : { 347 + left: 0, 348 + width: isOpen ? undefined : swipeEdgeWidth 349 + - }; 350 + + }, [isRight, isOpen, swipeEdgeWidth]); 351 + const touchStartX = useSharedValue(0); 352 + const touchX = useSharedValue(0); 353 + const translationX = useSharedValue(getDrawerTranslationX(open)); 354 + @@ -151,40 +152,43 @@ export function Drawer({ 355 + }, [getDrawerTranslationX, handleAnimationEnd, handleAnimationStart, onClose, onOpen, touchStartX, touchX, translationX]); 356 + React.useEffect(() => toggleDrawer(open), [open, toggleDrawer]); 357 + const startX = useSharedValue(0); 358 + - let pan = Gesture?.Pan().onBegin(event => { 359 + - 'worklet'; 360 + + const pan = React.useMemo(() => { 361 + + let panGesture = Gesture?.Pan().onBegin(event => { 362 + + 'worklet'; 363 + 364 + - startX.value = translationX.value; 365 + - gestureState.value = event.state; 366 + - touchStartX.value = event.x; 367 + - }).onStart(() => { 368 + - 'worklet'; 369 + + startX.value = translationX.value; 370 + + gestureState.value = event.state; 371 + + touchStartX.value = event.x; 372 + + }).onStart(() => { 373 + + 'worklet'; 374 + 375 + - runOnJS(onGestureBegin)(); 376 + - }).onChange(event => { 377 + - 'worklet'; 378 + + runOnJS(onGestureBegin)(); 379 + + }).onChange(event => { 380 + + 'worklet'; 381 + 382 + - touchX.value = event.x; 383 + - translationX.value = startX.value + event.translationX; 384 + - gestureState.value = event.state; 385 + - }).onEnd((event, success) => { 386 + - 'worklet'; 387 + + touchX.value = event.x; 388 + + translationX.value = startX.value + event.translationX; 389 + + gestureState.value = event.state; 390 + + }).onEnd((event, success) => { 391 + + 'worklet'; 392 + 393 + - gestureState.value = event.state; 394 + - if (!success) { 395 + - runOnJS(onGestureAbort)(); 396 + + gestureState.value = event.state; 397 + + if (!success) { 398 + + runOnJS(onGestureAbort)(); 399 + + } 400 + + const nextOpen = Math.abs(event.translationX) > SWIPE_MIN_OFFSET && Math.abs(event.translationX) > swipeMinVelocity || Math.abs(event.translationX) > swipeMinDistance ? drawerPosition === 'left' ? 401 + + // If swiped to right, open the drawer, otherwise close it 402 + + (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 : 403 + + // If swiped to left, open the drawer, otherwise close it 404 + + (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 : open; 405 + + toggleDrawer(nextOpen, event.velocityX); 406 + + runOnJS(onGestureFinish)(); 407 + + }).activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).hitSlop(hitSlop).enabled(drawerType !== 'permanent' && swipeEnabled); 408 + + if (panGesture && configureGestureHandler) { 409 + + panGesture = configureGestureHandler(panGesture); 410 + } 411 + - const nextOpen = Math.abs(event.translationX) > SWIPE_MIN_OFFSET && Math.abs(event.translationX) > swipeMinVelocity || Math.abs(event.translationX) > swipeMinDistance ? drawerPosition === 'left' ? 412 + - // If swiped to right, open the drawer, otherwise close it 413 + - (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 : 414 + - // If swiped to left, open the drawer, otherwise close it 415 + - (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 : open; 416 + - toggleDrawer(nextOpen, event.velocityX); 417 + - runOnJS(onGestureFinish)(); 418 + - }).activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).hitSlop(hitSlop).enabled(drawerType !== 'permanent' && swipeEnabled); 419 + - if (pan && configureGestureHandler) { 420 + - pan = configureGestureHandler(pan); 421 + - } 422 + + return panGesture; 423 + + }, [configureGestureHandler, drawerPosition, drawerType, gestureState, hitSlop, onGestureBegin, onGestureAbort, onGestureFinish, open, startX, swipeEnabled, swipeMinDistance, swipeMinVelocity, toggleDrawer, touchStartX, touchX, translationX]); 424 + const translateX = useDerivedValue(() => { 425 + // Comment stolen from react-native-gesture-handler/DrawerLayout 426 + // 427 + @@ -247,35 +251,38 @@ export function Drawer({ 428 + style: [styles.container, style], 429 + children: /*#__PURE__*/_jsx(DrawerProgressContext.Provider, { 430 + value: progress, 431 + - children: /*#__PURE__*/_jsx(GestureDetector, { 432 + - gesture: pan, 433 + - children: /*#__PURE__*/_jsxs(Animated.View, { 434 + - style: [styles.main, { 435 + - flexDirection: drawerType === 'permanent' ? isRight && direction === 'ltr' || !isRight && direction === 'rtl' ? 'row' : 'row-reverse' : 'row' 436 + - }], 437 + - children: [/*#__PURE__*/_jsxs(Animated.View, { 438 + - style: [styles.content, contentAnimatedStyle], 439 + - children: [/*#__PURE__*/_jsx(View, { 440 + - accessibilityElementsHidden: isOpen && drawerType !== 'permanent', 441 + - importantForAccessibility: isOpen && drawerType !== 'permanent' ? 'no-hide-descendants' : 'auto', 442 + - style: styles.content, 443 + - children: children 444 + - }), drawerType !== 'permanent' ? /*#__PURE__*/_jsx(Overlay, { 445 + - open: open, 446 + - progress: progress, 447 + - onPress: () => toggleDrawer(false), 448 + - style: overlayStyle, 449 + - accessibilityLabel: overlayAccessibilityLabel 450 + - }) : null] 451 + - }), /*#__PURE__*/_jsx(Animated.View, { 452 + - removeClippedSubviews: Platform.OS !== 'ios', 453 + - style: [styles.drawer, { 454 + - width: drawerWidth, 455 + - position: drawerType === 'permanent' ? 'relative' : 'absolute', 456 + - zIndex: drawerType === 'back' ? -1 : 0 457 + - }, drawerAnimatedStyle, drawerStyle], 458 + - children: renderDrawerContent() 459 + - })] 460 + + children: /*#__PURE__*/_jsx(DrawerGestureContext.Provider, { 461 + + value: pan, 462 + + children: /*#__PURE__*/_jsx(GestureDetector, { 463 + + gesture: pan, 464 + + children: /*#__PURE__*/_jsxs(Animated.View, { 465 + + style: [styles.main, { 466 + + flexDirection: drawerType === 'permanent' ? isRight && direction === 'ltr' || !isRight && direction === 'rtl' ? 'row' : 'row-reverse' : 'row' 467 + + }], 468 + + children: [/*#__PURE__*/_jsxs(Animated.View, { 469 + + style: [styles.content, contentAnimatedStyle], 470 + + children: [/*#__PURE__*/_jsx(View, { 471 + + accessibilityElementsHidden: isOpen && drawerType !== 'permanent', 472 + + importantForAccessibility: isOpen && drawerType !== 'permanent' ? 'no-hide-descendants' : 'auto', 473 + + style: styles.content, 474 + + children: children 475 + + }), drawerType !== 'permanent' ? /*#__PURE__*/_jsx(Overlay, { 476 + + open: open, 477 + + progress: progress, 478 + + onPress: () => toggleDrawer(false), 479 + + style: overlayStyle, 480 + + accessibilityLabel: overlayAccessibilityLabel 481 + + }) : null] 482 + + }), /*#__PURE__*/_jsx(Animated.View, { 483 + + removeClippedSubviews: Platform.OS !== 'ios', 484 + + style: [styles.drawer, { 485 + + width: drawerWidth, 486 + + position: drawerType === 'permanent' ? 'relative' : 'absolute', 487 + + zIndex: drawerType === 'back' ? -1 : 0 488 + + }, drawerAnimatedStyle, drawerStyle], 489 + + children: renderDrawerContent() 490 + + })] 491 + + }) 492 + }) 493 + }) 494 + }) 495 + diff --git a/node_modules/react-native-drawer-layout/lib/typescript/commonjs/src/index.d.ts b/node_modules/react-native-drawer-layout/lib/typescript/commonjs/src/index.d.ts 496 + index 7e978f0..a8bce18 100644 497 + --- a/node_modules/react-native-drawer-layout/lib/typescript/commonjs/src/index.d.ts 498 + +++ b/node_modules/react-native-drawer-layout/lib/typescript/commonjs/src/index.d.ts 499 + @@ -1,3 +1,4 @@ 500 + +export { DrawerGestureContext } from './utils/DrawerGestureContext'; 501 + export { DrawerProgressContext } from './utils/DrawerProgressContext'; 502 + export { useDrawerProgress } from './utils/useDrawerProgress'; 503 + export { Drawer } from './views/Drawer'; 504 + diff --git a/node_modules/react-native-drawer-layout/lib/typescript/commonjs/src/utils/DrawerGestureContext.d.ts b/node_modules/react-native-drawer-layout/lib/typescript/commonjs/src/utils/DrawerGestureContext.d.ts 505 + new file mode 100644 506 + index 0000000..33ffbeb 507 + --- /dev/null 508 + +++ b/node_modules/react-native-drawer-layout/lib/typescript/commonjs/src/utils/DrawerGestureContext.d.ts 509 + @@ -0,0 +1,3 @@ 510 + +import * as React from 'react'; 511 + +export declare const DrawerGestureContext: React.Context<import("react-native-gesture-handler/lib/typescript/handlers/gestures/panGesture").PanGesture | undefined>; 512 + +//# sourceMappingURL=DrawerGestureContext.d.ts.map 513 + \ No newline at end of file 514 + diff --git a/node_modules/react-native-drawer-layout/lib/typescript/module/src/index.d.ts b/node_modules/react-native-drawer-layout/lib/typescript/module/src/index.d.ts 515 + index 7e978f0..a8bce18 100644 516 + --- a/node_modules/react-native-drawer-layout/lib/typescript/module/src/index.d.ts 517 + +++ b/node_modules/react-native-drawer-layout/lib/typescript/module/src/index.d.ts 518 + @@ -1,3 +1,4 @@ 519 + +export { DrawerGestureContext } from './utils/DrawerGestureContext'; 520 + export { DrawerProgressContext } from './utils/DrawerProgressContext'; 521 + export { useDrawerProgress } from './utils/useDrawerProgress'; 522 + export { Drawer } from './views/Drawer'; 523 + diff --git a/node_modules/react-native-drawer-layout/lib/typescript/module/src/utils/DrawerGestureContext.d.ts b/node_modules/react-native-drawer-layout/lib/typescript/module/src/utils/DrawerGestureContext.d.ts 524 + new file mode 100644 525 + index 0000000..33ffbeb 526 + --- /dev/null 527 + +++ b/node_modules/react-native-drawer-layout/lib/typescript/module/src/utils/DrawerGestureContext.d.ts 528 + @@ -0,0 +1,3 @@ 529 + +import * as React from 'react'; 530 + +export declare const DrawerGestureContext: React.Context<import("react-native-gesture-handler/lib/typescript/handlers/gestures/panGesture").PanGesture | undefined>; 531 + +//# sourceMappingURL=DrawerGestureContext.d.ts.map 532 + \ No newline at end of file 533 + diff --git a/node_modules/react-native-drawer-layout/src/index.tsx b/node_modules/react-native-drawer-layout/src/index.tsx 534 + index 0aa6f53..61a37cf 100644 535 + --- a/node_modules/react-native-drawer-layout/src/index.tsx 536 + +++ b/node_modules/react-native-drawer-layout/src/index.tsx 537 + @@ -1,3 +1,4 @@ 538 + +export { DrawerGestureContext } from './utils/DrawerGestureContext'; 539 + export { DrawerProgressContext } from './utils/DrawerProgressContext'; 540 + export { useDrawerProgress } from './utils/useDrawerProgress'; 541 + export { Drawer } from './views/Drawer'; 542 + diff --git a/node_modules/react-native-drawer-layout/src/utils/DrawerGestureContext.tsx b/node_modules/react-native-drawer-layout/src/utils/DrawerGestureContext.tsx 543 + new file mode 100644 544 + index 0000000..3aac957 545 + --- /dev/null 546 + +++ b/node_modules/react-native-drawer-layout/src/utils/DrawerGestureContext.tsx 547 + @@ -0,0 +1,6 @@ 548 + +import * as React from 'react'; 549 + +import type { PanGesture } from 'react-native-gesture-handler'; 550 + + 551 + +export const DrawerGestureContext = React.createContext<PanGesture | undefined>( 552 + + undefined 553 + +); 554 + diff --git a/node_modules/react-native-drawer-layout/src/views/Drawer.native.tsx b/node_modules/react-native-drawer-layout/src/views/Drawer.native.tsx 555 + index 9c40f41..ee5d075 100644 556 + --- a/node_modules/react-native-drawer-layout/src/views/Drawer.native.tsx 557 + +++ b/node_modules/react-native-drawer-layout/src/views/Drawer.native.tsx 558 + @@ -21,6 +21,7 @@ import Animated, { 559 + import useLatestCallback from 'use-latest-callback'; 560 + 561 + import type { DrawerProps } from '../types'; 562 + +import { DrawerGestureContext } from '../utils/DrawerGestureContext'; 563 + import { DrawerProgressContext } from '../utils/DrawerProgressContext'; 564 + import { getDrawerWidth } from '../utils/getDrawerWidth'; 565 + import { 566 + @@ -110,47 +111,51 @@ export function Drawer({ 567 + 568 + const interactionHandleRef = React.useRef<number | null>(null); 569 + 570 + - const startInteraction = () => { 571 + + const startInteraction = React.useCallback(() => { 572 + interactionHandleRef.current = InteractionManager.createInteractionHandle(); 573 + - }; 574 + + }, []); 575 + 576 + - const endInteraction = () => { 577 + + const endInteraction = React.useCallback(() => { 578 + if (interactionHandleRef.current != null) { 579 + InteractionManager.clearInteractionHandle(interactionHandleRef.current); 580 + interactionHandleRef.current = null; 581 + } 582 + - }; 583 + + }, []); 584 + 585 + - const hideKeyboard = () => { 586 + + const hideKeyboard = React.useCallback(() => { 587 + if (keyboardDismissMode === 'on-drag') { 588 + Keyboard.dismiss(); 589 + } 590 + - }; 591 + + }, [keyboardDismissMode]); 592 + 593 + - const onGestureBegin = () => { 594 + + const onGestureBegin = React.useCallback(() => { 595 + onGestureStart?.(); 596 + startInteraction(); 597 + hideKeyboard(); 598 + hideStatusBar(true); 599 + - }; 600 + + }, [onGestureStart, startInteraction, hideKeyboard, hideStatusBar]); 601 + 602 + - const onGestureFinish = () => { 603 + + const onGestureFinish = React.useCallback(() => { 604 + onGestureEnd?.(); 605 + endInteraction(); 606 + - }; 607 + + }, [onGestureEnd, endInteraction]); 608 + 609 + - const onGestureAbort = () => { 610 + + const onGestureAbort = React.useCallback(() => { 611 + onGestureCancel?.(); 612 + endInteraction(); 613 + - }; 614 + + }, [onGestureCancel, endInteraction]); 615 + 616 + // FIXME: Currently hitSlop is broken when on Android when drawer is on right 617 + // https://github.com/software-mansion/react-native-gesture-handler/issues/569 618 + - const hitSlop = isRight 619 + - ? // Extend hitSlop to the side of the screen when drawer is closed 620 + - // This lets the user drag the drawer from the side of the screen 621 + - { right: 0, width: isOpen ? undefined : swipeEdgeWidth } 622 + - : { left: 0, width: isOpen ? undefined : swipeEdgeWidth }; 623 + + const hitSlop = React.useMemo( 624 + + () => 625 + + isRight 626 + + ? // Extend hitSlop to the side of the screen when drawer is closed 627 + + // This lets the user drag the drawer from the side of the screen 628 + + { right: 0, width: isOpen ? undefined : swipeEdgeWidth } 629 + + : { left: 0, width: isOpen ? undefined : swipeEdgeWidth }, 630 + + [isRight, isOpen, swipeEdgeWidth] 631 + + ); 632 + 633 + const touchStartX = useSharedValue(0); 634 + const touchX = useSharedValue(0); 635 + @@ -217,53 +222,76 @@ export function Drawer({ 636 + 637 + const startX = useSharedValue(0); 638 + 639 + - let pan = Gesture?.Pan() 640 + - .onBegin((event) => { 641 + - 'worklet'; 642 + - startX.value = translationX.value; 643 + - gestureState.value = event.state; 644 + - touchStartX.value = event.x; 645 + - }) 646 + - .onStart(() => { 647 + - 'worklet'; 648 + - runOnJS(onGestureBegin)(); 649 + - }) 650 + - .onChange((event) => { 651 + - 'worklet'; 652 + - touchX.value = event.x; 653 + - translationX.value = startX.value + event.translationX; 654 + - gestureState.value = event.state; 655 + - }) 656 + - .onEnd((event, success) => { 657 + - 'worklet'; 658 + - gestureState.value = event.state; 659 + - 660 + - if (!success) { 661 + - runOnJS(onGestureAbort)(); 662 + - } 663 + - 664 + - const nextOpen = 665 + - (Math.abs(event.translationX) > SWIPE_MIN_OFFSET && 666 + - Math.abs(event.translationX) > swipeMinVelocity) || 667 + - Math.abs(event.translationX) > swipeMinDistance 668 + - ? drawerPosition === 'left' 669 + - ? // If swiped to right, open the drawer, otherwise close it 670 + - (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 671 + - : // If swiped to left, open the drawer, otherwise close it 672 + - (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 673 + - : open; 674 + - 675 + - toggleDrawer(nextOpen, event.velocityX); 676 + - runOnJS(onGestureFinish)(); 677 + - }) 678 + - .activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]) 679 + - .failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]) 680 + - .hitSlop(hitSlop) 681 + - .enabled(drawerType !== 'permanent' && swipeEnabled); 682 + - 683 + - if (pan && configureGestureHandler) { 684 + - pan = configureGestureHandler(pan); 685 + - } 686 + + const pan = React.useMemo(() => { 687 + + let panGesture = Gesture?.Pan() 688 + + .onBegin((event) => { 689 + + 'worklet'; 690 + + startX.value = translationX.value; 691 + + gestureState.value = event.state; 692 + + touchStartX.value = event.x; 693 + + }) 694 + + .onStart(() => { 695 + + 'worklet'; 696 + + runOnJS(onGestureBegin)(); 697 + + }) 698 + + .onChange((event) => { 699 + + 'worklet'; 700 + + touchX.value = event.x; 701 + + translationX.value = startX.value + event.translationX; 702 + + gestureState.value = event.state; 703 + + }) 704 + + .onEnd((event, success) => { 705 + + 'worklet'; 706 + + gestureState.value = event.state; 707 + + 708 + + if (!success) { 709 + + runOnJS(onGestureAbort)(); 710 + + } 711 + + 712 + + const nextOpen = 713 + + (Math.abs(event.translationX) > SWIPE_MIN_OFFSET && 714 + + Math.abs(event.translationX) > swipeMinVelocity) || 715 + + Math.abs(event.translationX) > swipeMinDistance 716 + + ? drawerPosition === 'left' 717 + + ? // If swiped to right, open the drawer, otherwise close it 718 + + (event.velocityX === 0 ? event.translationX : event.velocityX) > 719 + + 0 720 + + : // If swiped to left, open the drawer, otherwise close it 721 + + (event.velocityX === 0 ? event.translationX : event.velocityX) < 722 + + 0 723 + + : open; 724 + + 725 + + toggleDrawer(nextOpen, event.velocityX); 726 + + runOnJS(onGestureFinish)(); 727 + + }) 728 + + .activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]) 729 + + .failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]) 730 + + .hitSlop(hitSlop) 731 + + .enabled(drawerType !== 'permanent' && swipeEnabled); 732 + + 733 + + if (panGesture && configureGestureHandler) { 734 + + panGesture = configureGestureHandler(panGesture); 735 + + } 736 + + return panGesture; 737 + + }, [ 738 + + configureGestureHandler, 739 + + drawerPosition, 740 + + drawerType, 741 + + gestureState, 742 + + hitSlop, 743 + + onGestureBegin, 744 + + onGestureAbort, 745 + + onGestureFinish, 746 + + open, 747 + + startX, 748 + + swipeEnabled, 749 + + swipeMinDistance, 750 + + swipeMinVelocity, 751 + + toggleDrawer, 752 + + touchStartX, 753 + + touchX, 754 + + translationX, 755 + + ]); 756 + 757 + const translateX = useDerivedValue(() => { 758 + // Comment stolen from react-native-gesture-handler/DrawerLayout 759 + @@ -376,64 +404,66 @@ export function Drawer({ 760 + return ( 761 + <GestureHandlerRootView style={[styles.container, style]}> 762 + <DrawerProgressContext.Provider value={progress}> 763 + - <GestureDetector gesture={pan}> 764 + - {/* Immediate child of gesture handler needs to be an Animated.View */} 765 + - <Animated.View 766 + - style={[ 767 + - styles.main, 768 + - { 769 + - flexDirection: 770 + - drawerType === 'permanent' 771 + - ? (isRight && direction === 'ltr') || 772 + - (!isRight && direction === 'rtl') 773 + - ? 'row' 774 + - : 'row-reverse' 775 + - : 'row', 776 + - }, 777 + - ]} 778 + - > 779 + - <Animated.View style={[styles.content, contentAnimatedStyle]}> 780 + - <View 781 + - accessibilityElementsHidden={ 782 + - isOpen && drawerType !== 'permanent' 783 + - } 784 + - importantForAccessibility={ 785 + - isOpen && drawerType !== 'permanent' 786 + - ? 'no-hide-descendants' 787 + - : 'auto' 788 + - } 789 + - style={styles.content} 790 + - > 791 + - {children} 792 + - </View> 793 + - {drawerType !== 'permanent' ? ( 794 + - <Overlay 795 + - open={open} 796 + - progress={progress} 797 + - onPress={() => toggleDrawer(false)} 798 + - style={overlayStyle} 799 + - accessibilityLabel={overlayAccessibilityLabel} 800 + - /> 801 + - ) : null} 802 + - </Animated.View> 803 + + <DrawerGestureContext.Provider value={pan}> 804 + + <GestureDetector gesture={pan}> 805 + + {/* Immediate child of gesture handler needs to be an Animated.View */} 806 + <Animated.View 807 + - removeClippedSubviews={Platform.OS !== 'ios'} 808 + style={[ 809 + - styles.drawer, 810 + + styles.main, 811 + { 812 + - width: drawerWidth, 813 + - position: 814 + - drawerType === 'permanent' ? 'relative' : 'absolute', 815 + - zIndex: drawerType === 'back' ? -1 : 0, 816 + + flexDirection: 817 + + drawerType === 'permanent' 818 + + ? (isRight && direction === 'ltr') || 819 + + (!isRight && direction === 'rtl') 820 + + ? 'row' 821 + + : 'row-reverse' 822 + + : 'row', 823 + }, 824 + - drawerAnimatedStyle, 825 + - drawerStyle, 826 + ]} 827 + > 828 + - {renderDrawerContent()} 829 + + <Animated.View style={[styles.content, contentAnimatedStyle]}> 830 + + <View 831 + + accessibilityElementsHidden={ 832 + + isOpen && drawerType !== 'permanent' 833 + + } 834 + + importantForAccessibility={ 835 + + isOpen && drawerType !== 'permanent' 836 + + ? 'no-hide-descendants' 837 + + : 'auto' 838 + + } 839 + + style={styles.content} 840 + + > 841 + + {children} 842 + + </View> 843 + + {drawerType !== 'permanent' ? ( 844 + + <Overlay 845 + + open={open} 846 + + progress={progress} 847 + + onPress={() => toggleDrawer(false)} 848 + + style={overlayStyle} 849 + + accessibilityLabel={overlayAccessibilityLabel} 850 + + /> 851 + + ) : null} 852 + + </Animated.View> 853 + + <Animated.View 854 + + removeClippedSubviews={Platform.OS !== 'ios'} 855 + + style={[ 856 + + styles.drawer, 857 + + { 858 + + width: drawerWidth, 859 + + position: 860 + + drawerType === 'permanent' ? 'relative' : 'absolute', 861 + + zIndex: drawerType === 'back' ? -1 : 0, 862 + + }, 863 + + drawerAnimatedStyle, 864 + + drawerStyle, 865 + + ]} 866 + + > 867 + + {renderDrawerContent()} 868 + + </Animated.View> 869 + </Animated.View> 870 + - </Animated.View> 871 + - </GestureDetector> 872 + + </GestureDetector> 873 + + </DrawerGestureContext.Provider> 874 + </DrawerProgressContext.Provider> 875 + </GestureHandlerRootView> 876 + );
+2 -4
src/screens/Hashtag.tsx
··· 14 14 import {sanitizeHandle} from '#/lib/strings/handles' 15 15 import {enforceLen} from '#/lib/strings/helpers' 16 16 import {useSearchPostsQuery} from '#/state/queries/search-posts' 17 - import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' 17 + import {useSetMinimalShellMode} from '#/state/shell' 18 18 import {Pager} from '#/view/com/pager/Pager' 19 19 import {TabBar} from '#/view/com/pager/TabBar' 20 20 import {Post} from '#/view/com/post/Post' ··· 63 63 64 64 const [activeTab, setActiveTab] = React.useState(0) 65 65 const setMinimalShellMode = useSetMinimalShellMode() 66 - const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() 67 66 68 67 useFocusEffect( 69 68 React.useCallback(() => { ··· 74 73 const onPageSelected = React.useCallback( 75 74 (index: number) => { 76 75 setMinimalShellMode(false) 77 - setDrawerSwipeDisabled(index > 0) 78 76 setActiveTab(index) 79 77 }, 80 - [setDrawerSwipeDisabled, setMinimalShellMode], 78 + [setMinimalShellMode], 81 79 ) 82 80 83 81 const sections = React.useMemo(() => {
+31 -8
src/view/com/pager/Pager.tsx
··· 1 - import React, {forwardRef} from 'react' 1 + import React, {forwardRef, useCallback, useContext} from 'react' 2 2 import {View} from 'react-native' 3 + import {DrawerGestureContext} from 'react-native-drawer-layout' 4 + import {Gesture, GestureDetector} from 'react-native-gesture-handler' 3 5 import PagerView, { 4 6 PagerViewOnPageScrollEventData, 5 7 PagerViewOnPageSelectedEvent, ··· 13 15 useHandler, 14 16 useSharedValue, 15 17 } from 'react-native-reanimated' 18 + import {useFocusEffect} from '@react-navigation/native' 16 19 20 + import {useSetDrawerSwipeDisabled} from '#/state/shell' 17 21 import {atoms as a, native} from '#/alf' 18 22 19 23 export type PageSelectedEvent = PagerViewOnPageSelectedEvent ··· 58 62 const [selectedPage, setSelectedPage] = React.useState(initialPage) 59 63 const pagerView = React.useRef<PagerView>(null) 60 64 65 + const [isIdle, setIsIdle] = React.useState(true) 66 + const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() 67 + useFocusEffect( 68 + useCallback(() => { 69 + const canSwipeDrawer = selectedPage === 0 && isIdle 70 + setDrawerSwipeDisabled(!canSwipeDrawer) 71 + return () => { 72 + setDrawerSwipeDisabled(false) 73 + } 74 + }, [setDrawerSwipeDisabled, selectedPage, isIdle]), 75 + ) 76 + 61 77 React.useImperativeHandle(ref, () => ({ 62 78 setPage: (index: number) => { 63 79 pagerView.current?.setPage(index) ··· 96 112 }, 97 113 onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) { 98 114 'worklet' 115 + runOnJS(setIsIdle)(e.pageScrollState === 'idle') 99 116 if (dragState.get() === 'idle' && e.pageScrollState === 'settling') { 100 117 // This is a programmatic scroll on Android. 101 118 // Stay "idle" to match iOS and avoid confusing downstream code. ··· 113 130 [parentOnPageScrollStateChanged], 114 131 ) 115 132 133 + const drawerGesture = useContext(DrawerGestureContext)! 134 + const nativeGesture = 135 + Gesture.Native().requireExternalGestureToFail(drawerGesture) 136 + 116 137 return ( 117 138 <View testID={testID} style={[a.flex_1, native(a.overflow_hidden)]}> 118 139 {renderTabBar({ ··· 121 142 dragProgress, 122 143 dragState, 123 144 })} 124 - <AnimatedPagerView 125 - ref={pagerView} 126 - style={[a.flex_1]} 127 - initialPage={initialPage} 128 - onPageScroll={handlePageScroll}> 129 - {children} 130 - </AnimatedPagerView> 145 + <GestureDetector gesture={nativeGesture}> 146 + <AnimatedPagerView 147 + ref={pagerView} 148 + style={[a.flex_1]} 149 + initialPage={initialPage} 150 + onPageScroll={handlePageScroll}> 151 + {children} 152 + </AnimatedPagerView> 153 + </GestureDetector> 131 154 </View> 132 155 ) 133 156 },
+3 -9
src/view/screens/Home.tsx
··· 19 19 import {usePreferencesQuery} from '#/state/queries/preferences' 20 20 import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types' 21 21 import {useSession} from '#/state/session' 22 - import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' 22 + import {useSetMinimalShellMode} from '#/state/shell' 23 23 import {useLoggedOutViewControls} from '#/state/shell/logged-out' 24 24 import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed' 25 25 import {FeedPage} from '#/view/com/feeds/FeedPage' ··· 127 127 128 128 const {hasSession} = useSession() 129 129 const setMinimalShellMode = useSetMinimalShellMode() 130 - const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() 131 130 useFocusEffect( 132 131 React.useCallback(() => { 133 132 setMinimalShellMode(false) 134 - setDrawerSwipeDisabled(selectedIndex > 0) 135 - return () => { 136 - setDrawerSwipeDisabled(false) 137 - } 138 - }, [setDrawerSwipeDisabled, selectedIndex, setMinimalShellMode]), 133 + }, [setMinimalShellMode]), 139 134 ) 140 135 141 136 useFocusEffect( ··· 154 149 const onPageSelected = React.useCallback( 155 150 (index: number) => { 156 151 setMinimalShellMode(false) 157 - setDrawerSwipeDisabled(index > 0) 158 152 const feed = allFeeds[index] 159 153 // Mutate the ref before setting state to avoid the imperative syncing effect 160 154 // above from starting a loop on Android when swiping back and forth. ··· 166 160 feedUrl: feed, 167 161 }) 168 162 }, 169 - [setDrawerSwipeDisabled, setSelectedFeed, setMinimalShellMode, allFeeds], 163 + [setSelectedFeed, setMinimalShellMode, allFeeds], 170 164 ) 171 165 172 166 const onPressSelected = React.useCallback(() => {
+1 -11
src/view/screens/Profile.tsx
··· 32 32 import {useProfileQuery} from '#/state/queries/profile' 33 33 import {useResolveDidQuery} from '#/state/queries/resolve-uri' 34 34 import {useAgent, useSession} from '#/state/session' 35 - import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' 35 + import {useSetMinimalShellMode} from '#/state/shell' 36 36 import {useComposerControls} from '#/state/shell/composer' 37 37 import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens' 38 38 import {ProfileLists} from '#/view/com/lists/ProfileLists' ··· 183 183 }) 184 184 const [currentPage, setCurrentPage] = React.useState(0) 185 185 const {_} = useLingui() 186 - const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() 187 186 188 187 const [scrollViewTag, setScrollViewTag] = React.useState<number | null>(null) 189 188 ··· 305 304 scrollSectionToTop(currentPage) 306 305 }) 307 306 }, [setMinimalShellMode, currentPage, scrollSectionToTop]), 308 - ) 309 - 310 - useFocusEffect( 311 - React.useCallback(() => { 312 - setDrawerSwipeDisabled(currentPage > 0) 313 - return () => { 314 - setDrawerSwipeDisabled(false) 315 - } 316 - }, [setDrawerSwipeDisabled, currentPage]), 317 307 ) 318 308 319 309 // events
+2 -4
src/view/screens/Search/Search.tsx
··· 47 47 import {useSearchPostsQuery} from '#/state/queries/search-posts' 48 48 import {useSession} from '#/state/session' 49 49 import {useSetDrawerOpen} from '#/state/shell' 50 - import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' 50 + import {useSetMinimalShellMode} from '#/state/shell' 51 51 import {Pager} from '#/view/com/pager/Pager' 52 52 import {TabBar} from '#/view/com/pager/TabBar' 53 53 import {Post} from '#/view/com/post/Post' ··· 471 471 }): React.ReactNode => { 472 472 const pal = usePalette('default') 473 473 const setMinimalShellMode = useSetMinimalShellMode() 474 - const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() 475 474 const {hasSession} = useSession() 476 475 const {isDesktop} = useWebMediaQueries() 477 476 const [activeTab, setActiveTab] = React.useState(0) ··· 480 479 const onPageSelected = React.useCallback( 481 480 (index: number) => { 482 481 setMinimalShellMode(false) 483 - setDrawerSwipeDisabled(index > 0) 484 482 setActiveTab(index) 485 483 }, 486 - [setDrawerSwipeDisabled, setMinimalShellMode], 484 + [setMinimalShellMode], 487 485 ) 488 486 489 487 const sections = React.useMemo(() => {
+26 -2
src/view/shell/index.tsx
··· 90 90 } 91 91 }, [dedupe, navigation]) 92 92 93 + const swipeEnabled = !canGoBack && hasSession && !isDrawerSwipeDisabled 93 94 return ( 94 95 <> 95 96 <View style={[a.h_full]}> ··· 98 99 <Drawer 99 100 renderDrawerContent={renderDrawerContent} 100 101 drawerStyle={{width: Math.min(400, winDim.width * 0.8)}} 102 + configureGestureHandler={handler => { 103 + if (swipeEnabled) { 104 + if (isDrawerOpen) { 105 + return handler.activeOffsetX([-1, 1]) 106 + } else { 107 + return ( 108 + handler 109 + // Any movement to the left is a pager swipe 110 + // so fail the drawer gesture immediately. 111 + .failOffsetX(-1) 112 + // Don't rush declaring that a movement to the right 113 + // is a drawer swipe. It could be a vertical scroll. 114 + .activeOffsetX(5) 115 + ) 116 + } 117 + } else { 118 + // Fail the gesture immediately. 119 + // This seems more reliable than the `swipeEnabled` prop. 120 + // With `swipeEnabled` alone, the gesture may freeze after toggling off/on. 121 + return handler.failOffsetX([0, 0]).failOffsetY([0, 0]) 122 + } 123 + }} 101 124 open={isDrawerOpen} 102 125 onOpen={onOpenDrawer} 103 126 onClose={onCloseDrawer} 104 - swipeEdgeWidth={winDim.width / 2} 127 + swipeEdgeWidth={winDim.width} 128 + swipeMinVelocity={100} 129 + swipeMinDistance={10} 105 130 drawerType={isIOS ? 'slide' : 'front'} 106 - swipeEnabled={!canGoBack && hasSession && !isDrawerSwipeDisabled} 107 131 overlayStyle={{ 108 132 backgroundColor: select(t.name, { 109 133 light: 'rgba(0, 57, 117, 0.1)',