about summary refs log tree commit diff
path: root/patches
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2024-12-10 04:40:40 +0000
committerGitHub <noreply@github.com>2024-12-10 04:40:40 +0000
commit46e1e5cee6f0670444da4e1c64a26d8247cf49ec (patch)
tree6b74644ea81733c11794796b712b5fe7ab077db5 /patches
parentfec3352b68473f1e1d9b2c038a783b7e2c8650e6 (diff)
downloadvoidsky-46e1e5cee6f0670444da4e1c64a26d8247cf49ec.tar.zst
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
Diffstat (limited to 'patches')
-rw-r--r--patches/react-native-drawer-layout+4.0.4.patch876
1 files changed, 876 insertions, 0 deletions
diff --git a/patches/react-native-drawer-layout+4.0.4.patch b/patches/react-native-drawer-layout+4.0.4.patch
new file mode 100644
index 000000000..004fb4a00
--- /dev/null
+++ b/patches/react-native-drawer-layout+4.0.4.patch
@@ -0,0 +1,876 @@
+diff --git a/node_modules/react-native-drawer-layout/lib/commonjs/index.js b/node_modules/react-native-drawer-layout/lib/commonjs/index.js
+index 3dce76e..6c4b3e5 100644
+--- a/node_modules/react-native-drawer-layout/lib/commonjs/index.js
++++ b/node_modules/react-native-drawer-layout/lib/commonjs/index.js
+@@ -9,6 +9,12 @@ Object.defineProperty(exports, "Drawer", {
+     return _Drawer.Drawer;
+   }
+ });
++Object.defineProperty(exports, "DrawerGestureContext", {
++  enumerable: true,
++  get: function () {
++    return _DrawerGestureContext.DrawerGestureContext;
++  }
++});
+ Object.defineProperty(exports, "DrawerProgressContext", {
+   enumerable: true,
+   get: function () {
+@@ -21,6 +27,7 @@ Object.defineProperty(exports, "useDrawerProgress", {
+     return _useDrawerProgress.useDrawerProgress;
+   }
+ });
++var _DrawerGestureContext = require("./utils/DrawerGestureContext.js");
+ var _DrawerProgressContext = require("./utils/DrawerProgressContext.js");
+ var _useDrawerProgress = require("./utils/useDrawerProgress.js");
+ var _Drawer = require("./views/Drawer");
+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
+new file mode 100644
+index 0000000..de6d793
+--- /dev/null
++++ b/node_modules/react-native-drawer-layout/lib/commonjs/utils/DrawerGestureContext.js
+@@ -0,0 +1,11 @@
++"use strict";
++
++Object.defineProperty(exports, "__esModule", {
++  value: true
++});
++exports.DrawerGestureContext = void 0;
++var React = _interopRequireWildcard(require("react"));
++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); }
++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; }
++const DrawerGestureContext = exports.DrawerGestureContext = /*#__PURE__*/React.createContext(undefined);
++//# sourceMappingURL=DrawerGestureContext.js.map
+\ No newline at end of file
+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
+index fd65ab8..86a9a6a 100644
+--- 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
+@@ -8,6 +8,7 @@ var React = _interopRequireWildcard(require("react"));
+ var _reactNative = require("react-native");
+ var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
+ var _useLatestCallback = _interopRequireDefault(require("use-latest-callback"));
++var _DrawerGestureContext = require("../utils/DrawerGestureContext.js");
+ var _DrawerProgressContext = require("../utils/DrawerProgressContext.js");
+ var _getDrawerWidth = require("../utils/getDrawerWidth.js");
+ var _GestureHandler = require("./GestureHandler");
+@@ -79,38 +80,38 @@ function Drawer({
+     return () => hideStatusBar(false);
+   }, [isOpen, hideStatusBarOnOpen, statusBarAnimation, hideStatusBar]);
+   const interactionHandleRef = React.useRef(null);
+-  const startInteraction = () => {
++  const startInteraction = React.useCallback(() => {
+     interactionHandleRef.current = _reactNative.InteractionManager.createInteractionHandle();
+-  };
+-  const endInteraction = () => {
++  }, []);
++  const endInteraction = React.useCallback(() => {
+     if (interactionHandleRef.current != null) {
+       _reactNative.InteractionManager.clearInteractionHandle(interactionHandleRef.current);
+       interactionHandleRef.current = null;
+     }
+-  };
+-  const hideKeyboard = () => {
++  }, []);
++  const hideKeyboard = React.useCallback(() => {
+     if (keyboardDismissMode === 'on-drag') {
+       _reactNative.Keyboard.dismiss();
+     }
+-  };
+-  const onGestureBegin = () => {
++  }, [keyboardDismissMode]);
++  const onGestureBegin = React.useCallback(() => {
+     onGestureStart?.();
+     startInteraction();
+     hideKeyboard();
+     hideStatusBar(true);
+-  };
+-  const onGestureFinish = () => {
++  }, [onGestureStart, startInteraction, hideKeyboard, hideStatusBar]);
++  const onGestureFinish = React.useCallback(() => {
+     onGestureEnd?.();
+     endInteraction();
+-  };
+-  const onGestureAbort = () => {
++  }, [onGestureEnd, endInteraction]);
++  const onGestureAbort = React.useCallback(() => {
+     onGestureCancel?.();
+     endInteraction();
+-  };
++  }, [onGestureCancel, endInteraction]);
+ 
+   // FIXME: Currently hitSlop is broken when on Android when drawer is on right
+   // https://github.com/software-mansion/react-native-gesture-handler/issues/569
+-  const hitSlop = isRight ?
++  const hitSlop = React.useMemo(() => isRight ?
+   // Extend hitSlop to the side of the screen when drawer is closed
+   // This lets the user drag the drawer from the side of the screen
+   {
+@@ -119,7 +120,7 @@ function Drawer({
+   } : {
+     left: 0,
+     width: isOpen ? undefined : swipeEdgeWidth
+-  };
++  }, [isRight, isOpen, swipeEdgeWidth]);
+   const touchStartX = (0, _reactNativeReanimated.useSharedValue)(0);
+   const touchX = (0, _reactNativeReanimated.useSharedValue)(0);
+   const translationX = (0, _reactNativeReanimated.useSharedValue)(getDrawerTranslationX(open));
+@@ -158,40 +159,43 @@ function Drawer({
+   }, [getDrawerTranslationX, handleAnimationEnd, handleAnimationStart, onClose, onOpen, touchStartX, touchX, translationX]);
+   React.useEffect(() => toggleDrawer(open), [open, toggleDrawer]);
+   const startX = (0, _reactNativeReanimated.useSharedValue)(0);
+-  let pan = _GestureHandler.Gesture?.Pan().onBegin(event => {
+-    'worklet';
++  const pan = React.useMemo(() => {
++    let panGesture = _GestureHandler.Gesture?.Pan().onBegin(event => {
++      'worklet';
+ 
+-    startX.value = translationX.value;
+-    gestureState.value = event.state;
+-    touchStartX.value = event.x;
+-  }).onStart(() => {
+-    'worklet';
++      startX.value = translationX.value;
++      gestureState.value = event.state;
++      touchStartX.value = event.x;
++    }).onStart(() => {
++      'worklet';
+ 
+-    (0, _reactNativeReanimated.runOnJS)(onGestureBegin)();
+-  }).onChange(event => {
+-    'worklet';
++      (0, _reactNativeReanimated.runOnJS)(onGestureBegin)();
++    }).onChange(event => {
++      'worklet';
+ 
+-    touchX.value = event.x;
+-    translationX.value = startX.value + event.translationX;
+-    gestureState.value = event.state;
+-  }).onEnd((event, success) => {
+-    'worklet';
++      touchX.value = event.x;
++      translationX.value = startX.value + event.translationX;
++      gestureState.value = event.state;
++    }).onEnd((event, success) => {
++      'worklet';
+ 
+-    gestureState.value = event.state;
+-    if (!success) {
+-      (0, _reactNativeReanimated.runOnJS)(onGestureAbort)();
++      gestureState.value = event.state;
++      if (!success) {
++        (0, _reactNativeReanimated.runOnJS)(onGestureAbort)();
++      }
++      const nextOpen = Math.abs(event.translationX) > SWIPE_MIN_OFFSET && Math.abs(event.translationX) > swipeMinVelocity || Math.abs(event.translationX) > swipeMinDistance ? drawerPosition === 'left' ?
++      // If swiped to right, open the drawer, otherwise close it
++      (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 :
++      // If swiped to left, open the drawer, otherwise close it
++      (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 : open;
++      toggleDrawer(nextOpen, event.velocityX);
++      (0, _reactNativeReanimated.runOnJS)(onGestureFinish)();
++    }).activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).hitSlop(hitSlop).enabled(drawerType !== 'permanent' && swipeEnabled);
++    if (panGesture && configureGestureHandler) {
++      panGesture = configureGestureHandler(panGesture);
+     }
+-    const nextOpen = Math.abs(event.translationX) > SWIPE_MIN_OFFSET && Math.abs(event.translationX) > swipeMinVelocity || Math.abs(event.translationX) > swipeMinDistance ? drawerPosition === 'left' ?
+-    // If swiped to right, open the drawer, otherwise close it
+-    (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 :
+-    // If swiped to left, open the drawer, otherwise close it
+-    (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 : open;
+-    toggleDrawer(nextOpen, event.velocityX);
+-    (0, _reactNativeReanimated.runOnJS)(onGestureFinish)();
+-  }).activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).hitSlop(hitSlop).enabled(drawerType !== 'permanent' && swipeEnabled);
+-  if (pan && configureGestureHandler) {
+-    pan = configureGestureHandler(pan);
+-  }
++    return panGesture;
++  }, [configureGestureHandler, drawerPosition, drawerType, gestureState, hitSlop, onGestureBegin, onGestureAbort, onGestureFinish, open, startX, swipeEnabled, swipeMinDistance, swipeMinVelocity, toggleDrawer, touchStartX, touchX, translationX]);
+   const translateX = (0, _reactNativeReanimated.useDerivedValue)(() => {
+     // Comment stolen from react-native-gesture-handler/DrawerLayout
+     //
+@@ -254,35 +258,38 @@ function Drawer({
+     style: [styles.container, style],
+     children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_DrawerProgressContext.DrawerProgressContext.Provider, {
+       value: progress,
+-      children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_GestureHandler.GestureDetector, {
+-        gesture: pan,
+-        children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, {
+-          style: [styles.main, {
+-            flexDirection: drawerType === 'permanent' ? isRight && direction === 'ltr' || !isRight && direction === 'rtl' ? 'row' : 'row-reverse' : 'row'
+-          }],
+-          children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, {
+-            style: [styles.content, contentAnimatedStyle],
+-            children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
+-              accessibilityElementsHidden: isOpen && drawerType !== 'permanent',
+-              importantForAccessibility: isOpen && drawerType !== 'permanent' ? 'no-hide-descendants' : 'auto',
+-              style: styles.content,
+-              children: children
+-            }), drawerType !== 'permanent' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Overlay.Overlay, {
+-              open: open,
+-              progress: progress,
+-              onPress: () => toggleDrawer(false),
+-              style: overlayStyle,
+-              accessibilityLabel: overlayAccessibilityLabel
+-            }) : null]
+-          }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
+-            removeClippedSubviews: _reactNative.Platform.OS !== 'ios',
+-            style: [styles.drawer, {
+-              width: drawerWidth,
+-              position: drawerType === 'permanent' ? 'relative' : 'absolute',
+-              zIndex: drawerType === 'back' ? -1 : 0
+-            }, drawerAnimatedStyle, drawerStyle],
+-            children: renderDrawerContent()
+-          })]
++      children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_DrawerGestureContext.DrawerGestureContext.Provider, {
++        value: pan,
++        children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_GestureHandler.GestureDetector, {
++          gesture: pan,
++          children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, {
++            style: [styles.main, {
++              flexDirection: drawerType === 'permanent' ? isRight && direction === 'ltr' || !isRight && direction === 'rtl' ? 'row' : 'row-reverse' : 'row'
++            }],
++            children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, {
++              style: [styles.content, contentAnimatedStyle],
++              children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
++                accessibilityElementsHidden: isOpen && drawerType !== 'permanent',
++                importantForAccessibility: isOpen && drawerType !== 'permanent' ? 'no-hide-descendants' : 'auto',
++                style: styles.content,
++                children: children
++              }), drawerType !== 'permanent' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Overlay.Overlay, {
++                open: open,
++                progress: progress,
++                onPress: () => toggleDrawer(false),
++                style: overlayStyle,
++                accessibilityLabel: overlayAccessibilityLabel
++              }) : null]
++            }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
++              removeClippedSubviews: _reactNative.Platform.OS !== 'ios',
++              style: [styles.drawer, {
++                width: drawerWidth,
++                position: drawerType === 'permanent' ? 'relative' : 'absolute',
++                zIndex: drawerType === 'back' ? -1 : 0
++              }, drawerAnimatedStyle, drawerStyle],
++              children: renderDrawerContent()
++            })]
++          })
+         })
+       })
+     })
+diff --git a/node_modules/react-native-drawer-layout/lib/module/index.js b/node_modules/react-native-drawer-layout/lib/module/index.js
+index a600e51..f08275c 100644
+--- a/node_modules/react-native-drawer-layout/lib/module/index.js
++++ b/node_modules/react-native-drawer-layout/lib/module/index.js
+@@ -1,5 +1,6 @@
+ "use strict";
+ 
++export { DrawerGestureContext } from "./utils/DrawerGestureContext.js";
+ export { DrawerProgressContext } from "./utils/DrawerProgressContext.js";
+ export { useDrawerProgress } from "./utils/useDrawerProgress.js";
+ export { Drawer } from './views/Drawer';
+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
+new file mode 100644
+index 0000000..1adaa9c
+--- /dev/null
++++ b/node_modules/react-native-drawer-layout/lib/module/utils/DrawerGestureContext.js
+@@ -0,0 +1,5 @@
++"use strict";
++
++import * as React from 'react';
++export const DrawerGestureContext = /*#__PURE__*/React.createContext(undefined);
++//# sourceMappingURL=DrawerGestureContext.js.map
+\ No newline at end of file
+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
+index 6d07126..981d9f8 100644
+--- 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
+@@ -4,6 +4,7 @@ import * as React from 'react';
+ import { I18nManager, InteractionManager, Keyboard, Platform, StatusBar, StyleSheet, useWindowDimensions, View } from 'react-native';
+ import Animated, { interpolate, ReduceMotion, runOnJS, useAnimatedStyle, useDerivedValue, useSharedValue, withSpring } from 'react-native-reanimated';
+ import useLatestCallback from 'use-latest-callback';
++import { DrawerGestureContext } from "../utils/DrawerGestureContext.js";
+ import { DrawerProgressContext } from "../utils/DrawerProgressContext.js";
+ import { getDrawerWidth } from "../utils/getDrawerWidth.js";
+ import { Gesture, GestureDetector, GestureHandlerRootView, GestureState } from './GestureHandler';
+@@ -72,38 +73,38 @@ export function Drawer({
+     return () => hideStatusBar(false);
+   }, [isOpen, hideStatusBarOnOpen, statusBarAnimation, hideStatusBar]);
+   const interactionHandleRef = React.useRef(null);
+-  const startInteraction = () => {
++  const startInteraction = React.useCallback(() => {
+     interactionHandleRef.current = InteractionManager.createInteractionHandle();
+-  };
+-  const endInteraction = () => {
++  }, []);
++  const endInteraction = React.useCallback(() => {
+     if (interactionHandleRef.current != null) {
+       InteractionManager.clearInteractionHandle(interactionHandleRef.current);
+       interactionHandleRef.current = null;
+     }
+-  };
+-  const hideKeyboard = () => {
++  }, []);
++  const hideKeyboard = React.useCallback(() => {
+     if (keyboardDismissMode === 'on-drag') {
+       Keyboard.dismiss();
+     }
+-  };
+-  const onGestureBegin = () => {
++  }, [keyboardDismissMode]);
++  const onGestureBegin = React.useCallback(() => {
+     onGestureStart?.();
+     startInteraction();
+     hideKeyboard();
+     hideStatusBar(true);
+-  };
+-  const onGestureFinish = () => {
++  }, [onGestureStart, startInteraction, hideKeyboard, hideStatusBar]);
++  const onGestureFinish = React.useCallback(() => {
+     onGestureEnd?.();
+     endInteraction();
+-  };
+-  const onGestureAbort = () => {
++  }, [onGestureEnd, endInteraction]);
++  const onGestureAbort = React.useCallback(() => {
+     onGestureCancel?.();
+     endInteraction();
+-  };
++  }, [onGestureCancel, endInteraction]);
+ 
+   // FIXME: Currently hitSlop is broken when on Android when drawer is on right
+   // https://github.com/software-mansion/react-native-gesture-handler/issues/569
+-  const hitSlop = isRight ?
++  const hitSlop = React.useMemo(() => isRight ?
+   // Extend hitSlop to the side of the screen when drawer is closed
+   // This lets the user drag the drawer from the side of the screen
+   {
+@@ -112,7 +113,7 @@ export function Drawer({
+   } : {
+     left: 0,
+     width: isOpen ? undefined : swipeEdgeWidth
+-  };
++  }, [isRight, isOpen, swipeEdgeWidth]);
+   const touchStartX = useSharedValue(0);
+   const touchX = useSharedValue(0);
+   const translationX = useSharedValue(getDrawerTranslationX(open));
+@@ -151,40 +152,43 @@ export function Drawer({
+   }, [getDrawerTranslationX, handleAnimationEnd, handleAnimationStart, onClose, onOpen, touchStartX, touchX, translationX]);
+   React.useEffect(() => toggleDrawer(open), [open, toggleDrawer]);
+   const startX = useSharedValue(0);
+-  let pan = Gesture?.Pan().onBegin(event => {
+-    'worklet';
++  const pan = React.useMemo(() => {
++    let panGesture = Gesture?.Pan().onBegin(event => {
++      'worklet';
+ 
+-    startX.value = translationX.value;
+-    gestureState.value = event.state;
+-    touchStartX.value = event.x;
+-  }).onStart(() => {
+-    'worklet';
++      startX.value = translationX.value;
++      gestureState.value = event.state;
++      touchStartX.value = event.x;
++    }).onStart(() => {
++      'worklet';
+ 
+-    runOnJS(onGestureBegin)();
+-  }).onChange(event => {
+-    'worklet';
++      runOnJS(onGestureBegin)();
++    }).onChange(event => {
++      'worklet';
+ 
+-    touchX.value = event.x;
+-    translationX.value = startX.value + event.translationX;
+-    gestureState.value = event.state;
+-  }).onEnd((event, success) => {
+-    'worklet';
++      touchX.value = event.x;
++      translationX.value = startX.value + event.translationX;
++      gestureState.value = event.state;
++    }).onEnd((event, success) => {
++      'worklet';
+ 
+-    gestureState.value = event.state;
+-    if (!success) {
+-      runOnJS(onGestureAbort)();
++      gestureState.value = event.state;
++      if (!success) {
++        runOnJS(onGestureAbort)();
++      }
++      const nextOpen = Math.abs(event.translationX) > SWIPE_MIN_OFFSET && Math.abs(event.translationX) > swipeMinVelocity || Math.abs(event.translationX) > swipeMinDistance ? drawerPosition === 'left' ?
++      // If swiped to right, open the drawer, otherwise close it
++      (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 :
++      // If swiped to left, open the drawer, otherwise close it
++      (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 : open;
++      toggleDrawer(nextOpen, event.velocityX);
++      runOnJS(onGestureFinish)();
++    }).activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).hitSlop(hitSlop).enabled(drawerType !== 'permanent' && swipeEnabled);
++    if (panGesture && configureGestureHandler) {
++      panGesture = configureGestureHandler(panGesture);
+     }
+-    const nextOpen = Math.abs(event.translationX) > SWIPE_MIN_OFFSET && Math.abs(event.translationX) > swipeMinVelocity || Math.abs(event.translationX) > swipeMinDistance ? drawerPosition === 'left' ?
+-    // If swiped to right, open the drawer, otherwise close it
+-    (event.velocityX === 0 ? event.translationX : event.velocityX) > 0 :
+-    // If swiped to left, open the drawer, otherwise close it
+-    (event.velocityX === 0 ? event.translationX : event.velocityX) < 0 : open;
+-    toggleDrawer(nextOpen, event.velocityX);
+-    runOnJS(onGestureFinish)();
+-  }).activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]).hitSlop(hitSlop).enabled(drawerType !== 'permanent' && swipeEnabled);
+-  if (pan && configureGestureHandler) {
+-    pan = configureGestureHandler(pan);
+-  }
++    return panGesture;
++  }, [configureGestureHandler, drawerPosition, drawerType, gestureState, hitSlop, onGestureBegin, onGestureAbort, onGestureFinish, open, startX, swipeEnabled, swipeMinDistance, swipeMinVelocity, toggleDrawer, touchStartX, touchX, translationX]);
+   const translateX = useDerivedValue(() => {
+     // Comment stolen from react-native-gesture-handler/DrawerLayout
+     //
+@@ -247,35 +251,38 @@ export function Drawer({
+     style: [styles.container, style],
+     children: /*#__PURE__*/_jsx(DrawerProgressContext.Provider, {
+       value: progress,
+-      children: /*#__PURE__*/_jsx(GestureDetector, {
+-        gesture: pan,
+-        children: /*#__PURE__*/_jsxs(Animated.View, {
+-          style: [styles.main, {
+-            flexDirection: drawerType === 'permanent' ? isRight && direction === 'ltr' || !isRight && direction === 'rtl' ? 'row' : 'row-reverse' : 'row'
+-          }],
+-          children: [/*#__PURE__*/_jsxs(Animated.View, {
+-            style: [styles.content, contentAnimatedStyle],
+-            children: [/*#__PURE__*/_jsx(View, {
+-              accessibilityElementsHidden: isOpen && drawerType !== 'permanent',
+-              importantForAccessibility: isOpen && drawerType !== 'permanent' ? 'no-hide-descendants' : 'auto',
+-              style: styles.content,
+-              children: children
+-            }), drawerType !== 'permanent' ? /*#__PURE__*/_jsx(Overlay, {
+-              open: open,
+-              progress: progress,
+-              onPress: () => toggleDrawer(false),
+-              style: overlayStyle,
+-              accessibilityLabel: overlayAccessibilityLabel
+-            }) : null]
+-          }), /*#__PURE__*/_jsx(Animated.View, {
+-            removeClippedSubviews: Platform.OS !== 'ios',
+-            style: [styles.drawer, {
+-              width: drawerWidth,
+-              position: drawerType === 'permanent' ? 'relative' : 'absolute',
+-              zIndex: drawerType === 'back' ? -1 : 0
+-            }, drawerAnimatedStyle, drawerStyle],
+-            children: renderDrawerContent()
+-          })]
++      children: /*#__PURE__*/_jsx(DrawerGestureContext.Provider, {
++        value: pan,
++        children: /*#__PURE__*/_jsx(GestureDetector, {
++          gesture: pan,
++          children: /*#__PURE__*/_jsxs(Animated.View, {
++            style: [styles.main, {
++              flexDirection: drawerType === 'permanent' ? isRight && direction === 'ltr' || !isRight && direction === 'rtl' ? 'row' : 'row-reverse' : 'row'
++            }],
++            children: [/*#__PURE__*/_jsxs(Animated.View, {
++              style: [styles.content, contentAnimatedStyle],
++              children: [/*#__PURE__*/_jsx(View, {
++                accessibilityElementsHidden: isOpen && drawerType !== 'permanent',
++                importantForAccessibility: isOpen && drawerType !== 'permanent' ? 'no-hide-descendants' : 'auto',
++                style: styles.content,
++                children: children
++              }), drawerType !== 'permanent' ? /*#__PURE__*/_jsx(Overlay, {
++                open: open,
++                progress: progress,
++                onPress: () => toggleDrawer(false),
++                style: overlayStyle,
++                accessibilityLabel: overlayAccessibilityLabel
++              }) : null]
++            }), /*#__PURE__*/_jsx(Animated.View, {
++              removeClippedSubviews: Platform.OS !== 'ios',
++              style: [styles.drawer, {
++                width: drawerWidth,
++                position: drawerType === 'permanent' ? 'relative' : 'absolute',
++                zIndex: drawerType === 'back' ? -1 : 0
++              }, drawerAnimatedStyle, drawerStyle],
++              children: renderDrawerContent()
++            })]
++          })
+         })
+       })
+     })
+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
+index 7e978f0..a8bce18 100644
+--- 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
+@@ -1,3 +1,4 @@
++export { DrawerGestureContext } from './utils/DrawerGestureContext';
+ export { DrawerProgressContext } from './utils/DrawerProgressContext';
+ export { useDrawerProgress } from './utils/useDrawerProgress';
+ export { Drawer } from './views/Drawer';
+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
+new file mode 100644
+index 0000000..33ffbeb
+--- /dev/null
++++ b/node_modules/react-native-drawer-layout/lib/typescript/commonjs/src/utils/DrawerGestureContext.d.ts
+@@ -0,0 +1,3 @@
++import * as React from 'react';
++export declare const DrawerGestureContext: React.Context<import("react-native-gesture-handler/lib/typescript/handlers/gestures/panGesture").PanGesture | undefined>;
++//# sourceMappingURL=DrawerGestureContext.d.ts.map
+\ No newline at end of file
+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
+index 7e978f0..a8bce18 100644
+--- 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
+@@ -1,3 +1,4 @@
++export { DrawerGestureContext } from './utils/DrawerGestureContext';
+ export { DrawerProgressContext } from './utils/DrawerProgressContext';
+ export { useDrawerProgress } from './utils/useDrawerProgress';
+ export { Drawer } from './views/Drawer';
+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
+new file mode 100644
+index 0000000..33ffbeb
+--- /dev/null
++++ b/node_modules/react-native-drawer-layout/lib/typescript/module/src/utils/DrawerGestureContext.d.ts
+@@ -0,0 +1,3 @@
++import * as React from 'react';
++export declare const DrawerGestureContext: React.Context<import("react-native-gesture-handler/lib/typescript/handlers/gestures/panGesture").PanGesture | undefined>;
++//# sourceMappingURL=DrawerGestureContext.d.ts.map
+\ No newline at end of file
+diff --git a/node_modules/react-native-drawer-layout/src/index.tsx b/node_modules/react-native-drawer-layout/src/index.tsx
+index 0aa6f53..61a37cf 100644
+--- a/node_modules/react-native-drawer-layout/src/index.tsx
++++ b/node_modules/react-native-drawer-layout/src/index.tsx
+@@ -1,3 +1,4 @@
++export { DrawerGestureContext } from './utils/DrawerGestureContext';
+ export { DrawerProgressContext } from './utils/DrawerProgressContext';
+ export { useDrawerProgress } from './utils/useDrawerProgress';
+ export { Drawer } from './views/Drawer';
+diff --git a/node_modules/react-native-drawer-layout/src/utils/DrawerGestureContext.tsx b/node_modules/react-native-drawer-layout/src/utils/DrawerGestureContext.tsx
+new file mode 100644
+index 0000000..3aac957
+--- /dev/null
++++ b/node_modules/react-native-drawer-layout/src/utils/DrawerGestureContext.tsx
+@@ -0,0 +1,6 @@
++import * as React from 'react';
++import type { PanGesture } from 'react-native-gesture-handler';
++
++export const DrawerGestureContext = React.createContext<PanGesture | undefined>(
++  undefined
++);
+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
+index 9c40f41..ee5d075 100644
+--- a/node_modules/react-native-drawer-layout/src/views/Drawer.native.tsx
++++ b/node_modules/react-native-drawer-layout/src/views/Drawer.native.tsx
+@@ -21,6 +21,7 @@ import Animated, {
+ import useLatestCallback from 'use-latest-callback';
+ 
+ import type { DrawerProps } from '../types';
++import { DrawerGestureContext } from '../utils/DrawerGestureContext';
+ import { DrawerProgressContext } from '../utils/DrawerProgressContext';
+ import { getDrawerWidth } from '../utils/getDrawerWidth';
+ import {
+@@ -110,47 +111,51 @@ export function Drawer({
+ 
+   const interactionHandleRef = React.useRef<number | null>(null);
+ 
+-  const startInteraction = () => {
++  const startInteraction = React.useCallback(() => {
+     interactionHandleRef.current = InteractionManager.createInteractionHandle();
+-  };
++  }, []);
+ 
+-  const endInteraction = () => {
++  const endInteraction = React.useCallback(() => {
+     if (interactionHandleRef.current != null) {
+       InteractionManager.clearInteractionHandle(interactionHandleRef.current);
+       interactionHandleRef.current = null;
+     }
+-  };
++  }, []);
+ 
+-  const hideKeyboard = () => {
++  const hideKeyboard = React.useCallback(() => {
+     if (keyboardDismissMode === 'on-drag') {
+       Keyboard.dismiss();
+     }
+-  };
++  }, [keyboardDismissMode]);
+ 
+-  const onGestureBegin = () => {
++  const onGestureBegin = React.useCallback(() => {
+     onGestureStart?.();
+     startInteraction();
+     hideKeyboard();
+     hideStatusBar(true);
+-  };
++  }, [onGestureStart, startInteraction, hideKeyboard, hideStatusBar]);
+ 
+-  const onGestureFinish = () => {
++  const onGestureFinish = React.useCallback(() => {
+     onGestureEnd?.();
+     endInteraction();
+-  };
++  }, [onGestureEnd, endInteraction]);
+ 
+-  const onGestureAbort = () => {
++  const onGestureAbort = React.useCallback(() => {
+     onGestureCancel?.();
+     endInteraction();
+-  };
++  }, [onGestureCancel, endInteraction]);
+ 
+   // FIXME: Currently hitSlop is broken when on Android when drawer is on right
+   // https://github.com/software-mansion/react-native-gesture-handler/issues/569
+-  const hitSlop = isRight
+-    ? // Extend hitSlop to the side of the screen when drawer is closed
+-      // This lets the user drag the drawer from the side of the screen
+-      { right: 0, width: isOpen ? undefined : swipeEdgeWidth }
+-    : { left: 0, width: isOpen ? undefined : swipeEdgeWidth };
++  const hitSlop = React.useMemo(
++    () =>
++      isRight
++        ? // Extend hitSlop to the side of the screen when drawer is closed
++          // This lets the user drag the drawer from the side of the screen
++          { right: 0, width: isOpen ? undefined : swipeEdgeWidth }
++        : { left: 0, width: isOpen ? undefined : swipeEdgeWidth },
++    [isRight, isOpen, swipeEdgeWidth]
++  );
+ 
+   const touchStartX = useSharedValue(0);
+   const touchX = useSharedValue(0);
+@@ -217,53 +222,76 @@ export function Drawer({
+ 
+   const startX = useSharedValue(0);
+ 
+-  let pan = Gesture?.Pan()
+-    .onBegin((event) => {
+-      'worklet';
+-      startX.value = translationX.value;
+-      gestureState.value = event.state;
+-      touchStartX.value = event.x;
+-    })
+-    .onStart(() => {
+-      'worklet';
+-      runOnJS(onGestureBegin)();
+-    })
+-    .onChange((event) => {
+-      'worklet';
+-      touchX.value = event.x;
+-      translationX.value = startX.value + event.translationX;
+-      gestureState.value = event.state;
+-    })
+-    .onEnd((event, success) => {
+-      'worklet';
+-      gestureState.value = event.state;
+-
+-      if (!success) {
+-        runOnJS(onGestureAbort)();
+-      }
+-
+-      const nextOpen =
+-        (Math.abs(event.translationX) > SWIPE_MIN_OFFSET &&
+-          Math.abs(event.translationX) > swipeMinVelocity) ||
+-        Math.abs(event.translationX) > swipeMinDistance
+-          ? drawerPosition === 'left'
+-            ? // If swiped to right, open the drawer, otherwise close it
+-              (event.velocityX === 0 ? event.translationX : event.velocityX) > 0
+-            : // If swiped to left, open the drawer, otherwise close it
+-              (event.velocityX === 0 ? event.translationX : event.velocityX) < 0
+-          : open;
+-
+-      toggleDrawer(nextOpen, event.velocityX);
+-      runOnJS(onGestureFinish)();
+-    })
+-    .activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET])
+-    .failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET])
+-    .hitSlop(hitSlop)
+-    .enabled(drawerType !== 'permanent' && swipeEnabled);
+-
+-  if (pan && configureGestureHandler) {
+-    pan = configureGestureHandler(pan);
+-  }
++  const pan = React.useMemo(() => {
++    let panGesture = Gesture?.Pan()
++      .onBegin((event) => {
++        'worklet';
++        startX.value = translationX.value;
++        gestureState.value = event.state;
++        touchStartX.value = event.x;
++      })
++      .onStart(() => {
++        'worklet';
++        runOnJS(onGestureBegin)();
++      })
++      .onChange((event) => {
++        'worklet';
++        touchX.value = event.x;
++        translationX.value = startX.value + event.translationX;
++        gestureState.value = event.state;
++      })
++      .onEnd((event, success) => {
++        'worklet';
++        gestureState.value = event.state;
++
++        if (!success) {
++          runOnJS(onGestureAbort)();
++        }
++
++        const nextOpen =
++          (Math.abs(event.translationX) > SWIPE_MIN_OFFSET &&
++            Math.abs(event.translationX) > swipeMinVelocity) ||
++          Math.abs(event.translationX) > swipeMinDistance
++            ? drawerPosition === 'left'
++              ? // If swiped to right, open the drawer, otherwise close it
++                (event.velocityX === 0 ? event.translationX : event.velocityX) >
++                0
++              : // If swiped to left, open the drawer, otherwise close it
++                (event.velocityX === 0 ? event.translationX : event.velocityX) <
++                0
++            : open;
++
++        toggleDrawer(nextOpen, event.velocityX);
++        runOnJS(onGestureFinish)();
++      })
++      .activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET])
++      .failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET])
++      .hitSlop(hitSlop)
++      .enabled(drawerType !== 'permanent' && swipeEnabled);
++
++    if (panGesture && configureGestureHandler) {
++      panGesture = configureGestureHandler(panGesture);
++    }
++    return panGesture;
++  }, [
++    configureGestureHandler,
++    drawerPosition,
++    drawerType,
++    gestureState,
++    hitSlop,
++    onGestureBegin,
++    onGestureAbort,
++    onGestureFinish,
++    open,
++    startX,
++    swipeEnabled,
++    swipeMinDistance,
++    swipeMinVelocity,
++    toggleDrawer,
++    touchStartX,
++    touchX,
++    translationX,
++  ]);
+ 
+   const translateX = useDerivedValue(() => {
+     // Comment stolen from react-native-gesture-handler/DrawerLayout
+@@ -376,64 +404,66 @@ export function Drawer({
+   return (
+     <GestureHandlerRootView style={[styles.container, style]}>
+       <DrawerProgressContext.Provider value={progress}>
+-        <GestureDetector gesture={pan}>
+-          {/* Immediate child of gesture handler needs to be an Animated.View */}
+-          <Animated.View
+-            style={[
+-              styles.main,
+-              {
+-                flexDirection:
+-                  drawerType === 'permanent'
+-                    ? (isRight && direction === 'ltr') ||
+-                      (!isRight && direction === 'rtl')
+-                      ? 'row'
+-                      : 'row-reverse'
+-                    : 'row',
+-              },
+-            ]}
+-          >
+-            <Animated.View style={[styles.content, contentAnimatedStyle]}>
+-              <View
+-                accessibilityElementsHidden={
+-                  isOpen && drawerType !== 'permanent'
+-                }
+-                importantForAccessibility={
+-                  isOpen && drawerType !== 'permanent'
+-                    ? 'no-hide-descendants'
+-                    : 'auto'
+-                }
+-                style={styles.content}
+-              >
+-                {children}
+-              </View>
+-              {drawerType !== 'permanent' ? (
+-                <Overlay
+-                  open={open}
+-                  progress={progress}
+-                  onPress={() => toggleDrawer(false)}
+-                  style={overlayStyle}
+-                  accessibilityLabel={overlayAccessibilityLabel}
+-                />
+-              ) : null}
+-            </Animated.View>
++        <DrawerGestureContext.Provider value={pan}>
++          <GestureDetector gesture={pan}>
++            {/* Immediate child of gesture handler needs to be an Animated.View */}
+             <Animated.View
+-              removeClippedSubviews={Platform.OS !== 'ios'}
+               style={[
+-                styles.drawer,
++                styles.main,
+                 {
+-                  width: drawerWidth,
+-                  position:
+-                    drawerType === 'permanent' ? 'relative' : 'absolute',
+-                  zIndex: drawerType === 'back' ? -1 : 0,
++                  flexDirection:
++                    drawerType === 'permanent'
++                      ? (isRight && direction === 'ltr') ||
++                        (!isRight && direction === 'rtl')
++                        ? 'row'
++                        : 'row-reverse'
++                      : 'row',
+                 },
+-                drawerAnimatedStyle,
+-                drawerStyle,
+               ]}
+             >
+-              {renderDrawerContent()}
++              <Animated.View style={[styles.content, contentAnimatedStyle]}>
++                <View
++                  accessibilityElementsHidden={
++                    isOpen && drawerType !== 'permanent'
++                  }
++                  importantForAccessibility={
++                    isOpen && drawerType !== 'permanent'
++                      ? 'no-hide-descendants'
++                      : 'auto'
++                  }
++                  style={styles.content}
++                >
++                  {children}
++                </View>
++                {drawerType !== 'permanent' ? (
++                  <Overlay
++                    open={open}
++                    progress={progress}
++                    onPress={() => toggleDrawer(false)}
++                    style={overlayStyle}
++                    accessibilityLabel={overlayAccessibilityLabel}
++                  />
++                ) : null}
++              </Animated.View>
++              <Animated.View
++                removeClippedSubviews={Platform.OS !== 'ios'}
++                style={[
++                  styles.drawer,
++                  {
++                    width: drawerWidth,
++                    position:
++                      drawerType === 'permanent' ? 'relative' : 'absolute',
++                    zIndex: drawerType === 'back' ? -1 : 0,
++                  },
++                  drawerAnimatedStyle,
++                  drawerStyle,
++                ]}
++              >
++                {renderDrawerContent()}
++              </Animated.View>
+             </Animated.View>
+-          </Animated.View>
+-        </GestureDetector>
++          </GestureDetector>
++        </DrawerGestureContext.Provider>
+       </DrawerProgressContext.Provider>
+     </GestureHandlerRootView>
+   );