fix pull search gesture despawn bug

This commit is contained in:
Boof2015
2026-07-13 22:03:56 -04:00
parent 03904d5df3
commit 3ac75432a4
+30 -41
View File
@@ -28,7 +28,6 @@ import {
} from 'react-native-gesture-handler'; } from 'react-native-gesture-handler';
import { import {
runOnJS, runOnJS,
runOnUI,
useSharedValue useSharedValue
} from 'react-native-reanimated'; } from 'react-native-reanimated';
import { Text } from '@/components/Text'; import { Text } from '@/components/Text';
@@ -44,11 +43,11 @@ const RESET_THRESHOLD = 58;
const MAX_PULL = 112; const MAX_PULL = 112;
type PullSearchGestureRef = MutableRefObject<GestureType | undefined>; type PullSearchGestureRef = MutableRefObject<GestureType | undefined>;
type SimultaneousHandlers = NativeViewGestureHandlerProps['simultaneousHandlers']; type WaitForHandlers = NativeViewGestureHandlerProps['waitFor'];
type PullSearchScrollViewProps = ScrollViewProps & Pick<NativeViewGestureHandlerProps, 'simultaneousHandlers'>; type PullSearchScrollViewProps = ScrollViewProps &
Pick<NativeViewGestureHandlerProps, 'simultaneousHandlers' | 'waitFor'>;
type PullSearchContextValue = { type PullSearchContextValue = {
gestureRef: PullSearchGestureRef; gestureRef: PullSearchGestureRef;
cancelIfScrolledAway: (offsetY: number) => void;
}; };
const PullSearchGestureContext = createContext<PullSearchContextValue | null>(null); const PullSearchGestureContext = createContext<PullSearchContextValue | null>(null);
@@ -57,10 +56,10 @@ function clamp(value: number, min: number, max: number): number {
return Math.max(min, Math.min(max, value)); return Math.max(min, Math.min(max, value));
} }
function mergeSimultaneousHandlers( function mergeWaitForHandlers(
existing: SimultaneousHandlers, existing: WaitForHandlers,
contextValue: PullSearchContextValue | null contextValue: PullSearchContextValue | null
): SimultaneousHandlers { ): WaitForHandlers {
if (!contextValue) return existing; if (!contextValue) return existing;
if (!existing) return contextValue.gestureRef; if (!existing) return contextValue.gestureRef;
return Array.isArray(existing) return Array.isArray(existing)
@@ -69,26 +68,18 @@ function mergeSimultaneousHandlers(
} }
export const PullSearchScrollView = forwardRef<RNScrollView, PullSearchScrollViewProps>( export const PullSearchScrollView = forwardRef<RNScrollView, PullSearchScrollViewProps>(
function PullSearchScrollView({ simultaneousHandlers, onScroll, ...props }, ref) { function PullSearchScrollView({ waitFor, ...props }, ref) {
const pullSearchContext = useContext(PullSearchGestureContext); const pullSearchContext = useContext(PullSearchGestureContext);
const mergedHandlers = useMemo( const mergedWaitFor = useMemo(
() => mergeSimultaneousHandlers(simultaneousHandlers, pullSearchContext), () => mergeWaitForHandlers(waitFor, pullSearchContext),
[pullSearchContext, simultaneousHandlers] [pullSearchContext, waitFor]
);
const handleScroll = useCallback(
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
onScroll?.(event);
pullSearchContext?.cancelIfScrolledAway(event.nativeEvent.contentOffset.y);
},
[onScroll, pullSearchContext]
); );
return ( return (
<GestureScrollView <GestureScrollView
ref={ref} ref={ref}
{...props} {...props}
onScroll={handleScroll} waitFor={mergedWaitFor}
simultaneousHandlers={mergedHandlers}
/> />
); );
} }
@@ -142,6 +133,7 @@ export function PullSearchGesture({
const pullValue = useSharedValue(0); const pullValue = useSharedValue(0);
const armedValue = useSharedValue(false); const armedValue = useSharedValue(false);
const draggingValue = useSharedValue(false); const draggingValue = useSharedValue(false);
const pullOriginY = useSharedValue(0);
const resetUi = useCallback(() => { const resetUi = useCallback(() => {
setArmed(false); setArmed(false);
@@ -155,27 +147,9 @@ export function PullSearchGesture({
resetUi(); resetUi();
}, [onOpen, resetUi]); }, [onOpen, resetUi]);
const resetShared = useCallback(() => {
runOnUI(() => {
'worklet';
pullValue.value = 0;
armedValue.value = false;
draggingValue.value = false;
})();
}, [armedValue, draggingValue, pullValue]);
const cancelIfScrolledAway = useCallback(
(offsetY: number) => {
if (offsetY <= 2 || !dragging) return;
resetShared();
resetUi();
},
[dragging, resetShared, resetUi]
);
const contextValue = useMemo<PullSearchContextValue>( const contextValue = useMemo<PullSearchContextValue>(
() => ({ gestureRef: pullGestureRef, cancelIfScrolledAway }), () => ({ gestureRef: pullGestureRef }),
[cancelIfScrolledAway, pullGestureRef] [pullGestureRef]
); );
const pullGesture = useMemo( const pullGesture = useMemo(
@@ -186,6 +160,19 @@ export function PullSearchGesture({
.activeOffsetY(10) .activeOffsetY(10)
.failOffsetY(-8) .failOffsetY(-8)
.failOffsetX([-28, 28]) .failOffsetX([-28, 28])
.onTouchesDown((event) => {
'worklet';
if (event.numberOfTouches !== 1) return;
pullOriginY.value = event.allTouches[0]?.absoluteY ?? 0;
})
.onTouchesMove((event, stateManager) => {
'worklet';
if (!draggingValue.value || event.numberOfTouches !== 1) return;
const touchY = event.allTouches[0]?.absoluteY;
if (touchY !== undefined && touchY <= pullOriginY.value) {
stateManager.fail();
}
})
.onStart(() => { .onStart(() => {
'worklet'; 'worklet';
pullValue.value = 0; pullValue.value = 0;
@@ -211,8 +198,9 @@ export function PullSearchGesture({
runOnJS(playHaptic)('thresholdExit'); runOnJS(playHaptic)('thresholdExit');
} }
}) })
.onEnd((event) => { .onEnd((event, success) => {
'worklet'; 'worklet';
if (!success) return;
const finalPull = pullValue.value; const finalPull = pullValue.value;
pullValue.value = 0; pullValue.value = 0;
armedValue.value = false; armedValue.value = false;
@@ -239,6 +227,7 @@ export function PullSearchGesture({
enabled, enabled,
open, open,
pullGestureRef, pullGestureRef,
pullOriginY,
pullValue, pullValue,
resetUi, resetUi,
] ]