From 7d9de2e484a594900f713ef2fbf48833e72b057e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 25 Aug 2025 12:07:50 +0200 Subject: [PATCH 01/90] logic detector web --- .../react-native-gesture-handler/src/index.ts | 1 + .../src/v3/HostGestureDetector.web.tsx | 61 +++++++++++++++--- .../src/v3/LogicDetector.tsx | 39 +++++++++++ .../src/v3/NativeDetector.tsx | 64 ++++++++++++++----- .../src/web/interfaces.ts | 9 +++ 5 files changed, 149 insertions(+), 25 deletions(-) create mode 100644 packages/react-native-gesture-handler/src/v3/LogicDetector.tsx diff --git a/packages/react-native-gesture-handler/src/index.ts b/packages/react-native-gesture-handler/src/index.ts index 1f6ad3b622..df4a25a510 100644 --- a/packages/react-native-gesture-handler/src/index.ts +++ b/packages/react-native-gesture-handler/src/index.ts @@ -165,6 +165,7 @@ export { default as DrawerLayout } from './components/DrawerLayout'; export type { NativeDetectorProps } from './v3/NativeDetector'; export { NativeDetector } from './v3/NativeDetector'; +export { LogicDetector } from './v3/LogicDetector'; export * from './v3/hooks/useGesture'; initialize(); diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index 8f193f10b8..c85ead1f6b 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -1,7 +1,7 @@ -import { Ref, useEffect, useRef } from 'react'; +import { Ref, RefObject, useEffect, useRef } from 'react'; import RNGestureHandlerModule from '../RNGestureHandlerModule.web'; import { ActionType } from '../ActionType'; -import { PropsRef } from '../web/interfaces'; +import { LogicChild, PropsRef } from '../web/interfaces'; import { View } from 'react-native'; import { tagMessage } from '../utils'; @@ -9,6 +9,7 @@ export interface GestureHandlerDetectorProps extends PropsRef { handlerTags: number[]; moduleId: number; children?: React.ReactNode; + logicChildren?: RefObject>; } const HostGestureDetector = (props: GestureHandlerDetectorProps) => { @@ -19,7 +20,11 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { const attachedHandlerTags = useRef>(new Set()); const attachedNativeHandlerTags = useRef>(new Set()); - const detachHandlers = (oldHandlerTags: Set) => { + const detachHandlers = ( + oldHandlerTags: Set, + attachedHandlerTags: RefObject>, + attachedNativeHandlerTags: RefObject> + ) => { oldHandlerTags.forEach((tag) => { RNGestureHandlerModule.detachGestureHandler(tag); attachedNativeHandlerTags.current.delete(tag); @@ -27,14 +32,24 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }); }; - const attachHandlers = (currentHandlerTags: Set) => { + const attachHandlers = ( + viewRef: RefObject, + propsRef: RefObject, + currentHandlerTags: Set, + attachedHandlerTags: RefObject>, + attachedNativeHandlerTags: RefObject> + ) => { const oldHandlerTags = attachedHandlerTags.current.difference(currentHandlerTags); const newHandlerTags = currentHandlerTags.difference( attachedHandlerTags.current ); - detachHandlers(oldHandlerTags); + detachHandlers( + oldHandlerTags, + attachedHandlerTags, + attachedNativeHandlerTags + ); newHandlerTags.forEach((tag) => { if ( @@ -67,16 +82,46 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }; useEffect(() => { - detachHandlers(attachedNativeHandlerTags.current); + detachHandlers( + attachedNativeHandlerTags.current, + attachedHandlerTags, + attachedNativeHandlerTags + ); }, [children]); useEffect(() => { - attachHandlers(new Set(handlerTags)); + attachHandlers( + viewRef, + propsRef, + new Set(handlerTags), + attachedHandlerTags, + attachedNativeHandlerTags + ); + props.logicChildren?.current.forEach((child) => { + attachHandlers( + child.viewRef, + child.propsRef, + new Set(child.propsRef.current.handlerTags), + child.attachedHandlerTags, + child.attachedNativeHandlerTags + ); + }); }, [handlerTags, children]); useEffect(() => { return () => { - detachHandlers(attachedHandlerTags.current); + detachHandlers( + attachedHandlerTags.current, + attachedHandlerTags, + attachedNativeHandlerTags + ); + props.logicChildren?.current.forEach((child) => { + detachHandlers( + child.attachedHandlerTags.current, + child.attachedHandlerTags, + child.attachedNativeHandlerTags + ); + }); }; }, []); diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx new file mode 100644 index 0000000000..f02a2f9c6f --- /dev/null +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -0,0 +1,39 @@ +import { useEffect, useRef } from 'react'; +import { NativeDetectorProps, useDetectorContext } from './NativeDetector'; +import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; + +export function LogicDetector(props: NativeDetectorProps) { + const { register, unregister } = useDetectorContext(); + const viewRef = useRef(null); + const attachedHandlerTags = useRef>(new Set()); + const attachedNativeHandlerTags = useRef>(new Set()); + + const propsRef = useRef({ + onGestureHandlerStateChange: + props.gesture.gestureEvents.onGestureHandlerStateChange, + onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent!, + onGestureHandlerAnimatedEvent: + props.gesture.gestureEvents.onGestureHandlerAnimatedEvent, + onGestureHandlerTouchEvent: + props.gesture.gestureEvents.onGestureHandlerTouchEvent, + moduleId: globalThis._RNGH_MODULE_ID, + handlerTags: [props.gesture.tag], + }); + + const logicChild = useRef({ + viewRef: viewRef, + propsRef: propsRef, + attachedHandlerTags: attachedHandlerTags, + attachedNativeHandlerTags: attachedNativeHandlerTags, + }); + + useEffect(() => { + register(logicChild.current); + + return () => { + unregister(logicChild.current); + }; + }, [register, unregister]); + + return {props.children}; +} diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index 22d347d1f5..0476d49d54 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -1,10 +1,11 @@ -import React from 'react'; +import React, { createContext, RefObject, useContext, useRef } from 'react'; import { NativeGesture } from './hooks/useGesture'; import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../utils'; +import { LogicChild } from '../web/interfaces'; export interface NativeDetectorProps { children?: React.ReactNode; @@ -17,7 +18,17 @@ const AnimatedNativeDetector = const ReanimatedNativeDetector = Reanimated?.default.createAnimatedComponent(HostGestureDetector); +type DetectorContextType = { + register: (child: LogicChild) => void; + unregister: (child: LogicChild) => void; +}; + +const DetectorContext = createContext(null); + export function NativeDetector({ gesture, children }: NativeDetectorProps) { + const logicChildren: RefObject> = useRef( + new Set() + ); const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents ? AnimatedNativeDetector : // TODO: Remove this cast when we properly type config @@ -25,6 +36,14 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { ? ReanimatedNativeDetector : HostGestureDetector; + const register = (child: LogicChild) => { + logicChildren.current.add(child); + }; + + const unregister = (child: LogicChild) => { + logicChildren.current.delete(child); + }; + // It might happen only with ReanimatedNativeDetector if (!NativeDetectorComponent) { throw new Error( @@ -35,25 +54,36 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { } return ( - - {children} - + + + {children} + + ); } +export function useDetectorContext() { + const ctx = useContext(DetectorContext); + if (!ctx) { + throw new Error('Logic detector must be under a Native Detector'); + } + return ctx; +} + const styles = StyleSheet.create({ detector: { display: 'contents', diff --git a/packages/react-native-gesture-handler/src/web/interfaces.ts b/packages/react-native-gesture-handler/src/web/interfaces.ts index 4097507dbb..e8821a00a2 100644 --- a/packages/react-native-gesture-handler/src/web/interfaces.ts +++ b/packages/react-native-gesture-handler/src/web/interfaces.ts @@ -13,6 +13,8 @@ import { GestureUpdateEventWithData, } from '../v3/types'; import { State } from '../State'; +import { RefObject } from 'react'; +import { GestureHandlerDetectorProps } from '../v3/HostGestureDetector.web'; export interface HitSlop { left?: number; @@ -133,6 +135,13 @@ export interface PropsRef { onGestureHandlerTouchEvent?: (e: ResultEvent) => void; } +export interface LogicChild { + viewRef: RefObject; + propsRef: RefObject; + attachedHandlerTags: RefObject>; + attachedNativeHandlerTags: RefObject>; +} + export interface AdaptedEvent { x: number; y: number; From 4ccf49e8afbd6394b893c8b0d7b58740243a73ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 25 Aug 2025 13:12:06 +0200 Subject: [PATCH 02/90] avoiding propsRef --- .../src/v3/HostGestureDetector.web.tsx | 7 +++--- .../src/v3/LogicDetector.tsx | 4 ++-- .../src/v3/NativeDetector.tsx | 22 +++++++++++-------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index c85ead1f6b..9edc502251 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -9,7 +9,7 @@ export interface GestureHandlerDetectorProps extends PropsRef { handlerTags: number[]; moduleId: number; children?: React.ReactNode; - logicChildren?: RefObject>; + logicChildren?: Set; } const HostGestureDetector = (props: GestureHandlerDetectorProps) => { @@ -97,7 +97,8 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { attachedHandlerTags, attachedNativeHandlerTags ); - props.logicChildren?.current.forEach((child) => { + console.log(props.logicChildren); + props.logicChildren?.forEach((child) => { attachHandlers( child.viewRef, child.propsRef, @@ -115,7 +116,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { attachedHandlerTags, attachedNativeHandlerTags ); - props.logicChildren?.current.forEach((child) => { + props.logicChildren?.forEach((child) => { detachHandlers( child.attachedHandlerTags.current, child.attachedHandlerTags, diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index f02a2f9c6f..1cce821989 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react'; import { NativeDetectorProps, useDetectorContext } from './NativeDetector'; import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; -export function LogicDetector(props: NativeDetectorProps) { +export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); const viewRef = useRef(null); const attachedHandlerTags = useRef>(new Set()); @@ -36,4 +36,4 @@ export function LogicDetector(props: NativeDetectorProps) { }, [register, unregister]); return {props.children}; -} +}; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index 0476d49d54..fa2e51d935 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -1,4 +1,4 @@ -import React, { createContext, RefObject, useContext, useRef } from 'react'; +import React, { createContext, useCallback, useContext, useState } from 'react'; import { NativeGesture } from './hooks/useGesture'; import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; @@ -26,8 +26,8 @@ type DetectorContextType = { const DetectorContext = createContext(null); export function NativeDetector({ gesture, children }: NativeDetectorProps) { - const logicChildren: RefObject> = useRef( - new Set() + const [logicChildren, setLogicChildren] = useState>( + new Set() ); const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents ? AnimatedNativeDetector @@ -36,13 +36,17 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { ? ReanimatedNativeDetector : HostGestureDetector; - const register = (child: LogicChild) => { - logicChildren.current.add(child); - }; + const register = useCallback((child: LogicChild) => { + setLogicChildren((prev) => new Set(prev).add(child)); + }, []); - const unregister = (child: LogicChild) => { - logicChildren.current.delete(child); - }; + const unregister = useCallback((child: LogicChild) => { + setLogicChildren((prev) => { + const updated = new Set(prev); + updated.delete(child); + return updated; + }); + }, []); // It might happen only with ReanimatedNativeDetector if (!NativeDetectorComponent) { From 69e1c5df51737d8419d9af16b405375752ff5239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 27 Aug 2025 12:12:36 +0200 Subject: [PATCH 03/90] refactor --- .../src/v3/HostGestureDetector.web.tsx | 90 ++++++++++++------- .../src/v3/LogicDetector.tsx | 4 - .../src/v3/LogicDetector.web.tsx | 35 ++++++++ .../src/v3/NativeDetector.tsx | 18 ++-- .../src/web/interfaces.ts | 4 +- 5 files changed, 102 insertions(+), 49 deletions(-) create mode 100644 packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index 9edc502251..100d4fb7f8 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -1,7 +1,7 @@ import { Ref, RefObject, useEffect, useRef } from 'react'; import RNGestureHandlerModule from '../RNGestureHandlerModule.web'; import { ActionType } from '../ActionType'; -import { LogicChild, PropsRef } from '../web/interfaces'; +import { LogicDetectorProps, PropsRef } from '../web/interfaces'; import { View } from 'react-native'; import { tagMessage } from '../utils'; @@ -9,7 +9,12 @@ export interface GestureHandlerDetectorProps extends PropsRef { handlerTags: number[]; moduleId: number; children?: React.ReactNode; - logicChildren?: Set; + logicChildren?: Set; +} + +interface LogicChild { + attachedHandlerTags: Set; + attachedNativeHandlerTags: Set; } const HostGestureDetector = (props: GestureHandlerDetectorProps) => { @@ -20,15 +25,17 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { const attachedHandlerTags = useRef>(new Set()); const attachedNativeHandlerTags = useRef>(new Set()); + const logicChildren = useRef>(new Map()); + const detachHandlers = ( oldHandlerTags: Set, - attachedHandlerTags: RefObject>, - attachedNativeHandlerTags: RefObject> + attachedHandlerTags: Set, + attachedNativeHandlerTags: Set ) => { oldHandlerTags.forEach((tag) => { RNGestureHandlerModule.detachGestureHandler(tag); - attachedNativeHandlerTags.current.delete(tag); - attachedHandlerTags.current.delete(tag); + attachedNativeHandlerTags.delete(tag); + attachedHandlerTags.delete(tag); }); }; @@ -36,14 +43,11 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { viewRef: RefObject, propsRef: RefObject, currentHandlerTags: Set, - attachedHandlerTags: RefObject>, - attachedNativeHandlerTags: RefObject> + attachedHandlerTags: Set, + attachedNativeHandlerTags: Set ) => { - const oldHandlerTags = - attachedHandlerTags.current.difference(currentHandlerTags); - const newHandlerTags = currentHandlerTags.difference( - attachedHandlerTags.current - ); + const oldHandlerTags = attachedHandlerTags.difference(currentHandlerTags); + const newHandlerTags = currentHandlerTags.difference(attachedHandlerTags); detachHandlers( oldHandlerTags, @@ -68,7 +72,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { ActionType.NATIVE_DETECTOR, propsRef ); - attachedNativeHandlerTags.current.add(tag); + attachedNativeHandlerTags.add(tag); } else { RNGestureHandlerModule.attachGestureHandler( tag, @@ -77,15 +81,15 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { propsRef ); } + attachedHandlerTags.add(tag); }); - attachedHandlerTags.current = currentHandlerTags; }; useEffect(() => { detachHandlers( attachedNativeHandlerTags.current, - attachedHandlerTags, - attachedNativeHandlerTags + attachedHandlerTags.current, + attachedNativeHandlerTags.current ); }, [children]); @@ -94,18 +98,30 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { viewRef, propsRef, new Set(handlerTags), - attachedHandlerTags, - attachedNativeHandlerTags + attachedHandlerTags.current, + attachedNativeHandlerTags.current ); - console.log(props.logicChildren); + props.logicChildren?.forEach((child) => { - attachHandlers( - child.viewRef, - child.propsRef, - new Set(child.propsRef.current.handlerTags), - child.attachedHandlerTags, - child.attachedNativeHandlerTags - ); + if (!logicChildren.current.has(child)) { + logicChildren.current.set(child, { + attachedHandlerTags: new Set(), + attachedNativeHandlerTags: new Set(), + }); + } + const attachedHandlerTags = + logicChildren.current.get(child)?.attachedHandlerTags; + const attachedNativeHandlerTags = + logicChildren.current.get(child)?.attachedNativeHandlerTags; + if (attachedHandlerTags && attachedNativeHandlerTags) { + attachHandlers( + child.viewRef, + child.propsRef, + new Set(child.propsRef.current.handlerTags), + attachedHandlerTags, + attachedNativeHandlerTags + ); + } }); }, [handlerTags, children]); @@ -113,15 +129,21 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { return () => { detachHandlers( attachedHandlerTags.current, - attachedHandlerTags, - attachedNativeHandlerTags + attachedHandlerTags.current, + attachedNativeHandlerTags.current ); props.logicChildren?.forEach((child) => { - detachHandlers( - child.attachedHandlerTags.current, - child.attachedHandlerTags, - child.attachedNativeHandlerTags - ); + const attachedHandlerTags = + logicChildren.current.get(child)?.attachedHandlerTags; + const attachedNativeHandlerTags = + logicChildren.current.get(child)?.attachedNativeHandlerTags; + if (attachedHandlerTags && attachedNativeHandlerTags) { + detachHandlers( + attachedHandlerTags, + attachedHandlerTags, + attachedNativeHandlerTags + ); + } }); }; }, []); diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 1cce821989..57b7501e86 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -5,8 +5,6 @@ import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); const viewRef = useRef(null); - const attachedHandlerTags = useRef>(new Set()); - const attachedNativeHandlerTags = useRef>(new Set()); const propsRef = useRef({ onGestureHandlerStateChange: @@ -23,8 +21,6 @@ export const LogicDetector = (props: NativeDetectorProps) => { const logicChild = useRef({ viewRef: viewRef, propsRef: propsRef, - attachedHandlerTags: attachedHandlerTags, - attachedNativeHandlerTags: attachedNativeHandlerTags, }); useEffect(() => { diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx new file mode 100644 index 0000000000..57b7501e86 --- /dev/null +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx @@ -0,0 +1,35 @@ +import { useEffect, useRef } from 'react'; +import { NativeDetectorProps, useDetectorContext } from './NativeDetector'; +import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; + +export const LogicDetector = (props: NativeDetectorProps) => { + const { register, unregister } = useDetectorContext(); + const viewRef = useRef(null); + + const propsRef = useRef({ + onGestureHandlerStateChange: + props.gesture.gestureEvents.onGestureHandlerStateChange, + onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent!, + onGestureHandlerAnimatedEvent: + props.gesture.gestureEvents.onGestureHandlerAnimatedEvent, + onGestureHandlerTouchEvent: + props.gesture.gestureEvents.onGestureHandlerTouchEvent, + moduleId: globalThis._RNGH_MODULE_ID, + handlerTags: [props.gesture.tag], + }); + + const logicChild = useRef({ + viewRef: viewRef, + propsRef: propsRef, + }); + + useEffect(() => { + register(logicChild.current); + + return () => { + unregister(logicChild.current); + }; + }, [register, unregister]); + + return {props.children}; +}; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index fa2e51d935..13db76f411 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -5,7 +5,7 @@ import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../utils'; -import { LogicChild } from '../web/interfaces'; +import { LogicDetectorProps } from '../web/interfaces'; export interface NativeDetectorProps { children?: React.ReactNode; @@ -19,14 +19,14 @@ const ReanimatedNativeDetector = Reanimated?.default.createAnimatedComponent(HostGestureDetector); type DetectorContextType = { - register: (child: LogicChild) => void; - unregister: (child: LogicChild) => void; + register: (child: LogicDetectorProps) => void; + unregister: (child: LogicDetectorProps) => void; }; const DetectorContext = createContext(null); export function NativeDetector({ gesture, children }: NativeDetectorProps) { - const [logicChildren, setLogicChildren] = useState>( + const [logicChildren, setLogicChildren] = useState>( new Set() ); const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents @@ -36,12 +36,14 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { ? ReanimatedNativeDetector : HostGestureDetector; - const register = useCallback((child: LogicChild) => { - setLogicChildren((prev) => new Set(prev).add(child)); + const register = useCallback((child: LogicDetectorProps) => { + setLogicChildren((prev: Set) => + new Set(prev).add(child) + ); }, []); - const unregister = useCallback((child: LogicChild) => { - setLogicChildren((prev) => { + const unregister = useCallback((child: LogicDetectorProps) => { + setLogicChildren((prev: Set) => { const updated = new Set(prev); updated.delete(child); return updated; diff --git a/packages/react-native-gesture-handler/src/web/interfaces.ts b/packages/react-native-gesture-handler/src/web/interfaces.ts index e8821a00a2..cd50916ecb 100644 --- a/packages/react-native-gesture-handler/src/web/interfaces.ts +++ b/packages/react-native-gesture-handler/src/web/interfaces.ts @@ -135,11 +135,9 @@ export interface PropsRef { onGestureHandlerTouchEvent?: (e: ResultEvent) => void; } -export interface LogicChild { +export interface LogicDetectorProps { viewRef: RefObject; propsRef: RefObject; - attachedHandlerTags: RefObject>; - attachedNativeHandlerTags: RefObject>; } export interface AdaptedEvent { From 9959d8b3fd7e7904a13595207a819abee0677bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 27 Aug 2025 12:59:36 +0200 Subject: [PATCH 04/90] logicChildren in specs --- .../src/specs/RNGestureHandlerDetectorNativeComponent.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts index e48659d984..4a45a8257e 100644 --- a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts +++ b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts @@ -50,6 +50,7 @@ export interface NativeProps extends ViewProps { handlerTags: Int32[]; moduleId: Int32; + logicChildren: UnsafeMixed; } export default codegenNativeComponent('RNGestureHandlerDetector', { From 1f1f681d38fb4925b2015786d039eb7b189e8337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 1 Sep 2025 12:27:42 +0200 Subject: [PATCH 05/90] ios --- .../apple/RNGestureHandlerActionType.h | 1 + .../apple/RNGestureHandlerDetector.h | 6 + .../apple/RNGestureHandlerDetector.mm | 111 +++++++++++++++--- .../apple/RNGestureHandlerManager.h | 1 + .../apple/RNGestureHandlerManager.mm | 18 +++ .../apple/RNGestureHandlerNativeEventUtils.h | 6 +- .../apple/RNGestureHandlerNativeEventUtils.mm | 29 +++++ .../apple/RNGestureHandlerRegistry.h | 3 +- .../apple/RNGestureHandlerRegistry.m | 12 ++ .../src/ActionType.ts | 1 + .../src/RNGestureHandlerModule.web.ts | 10 +- ...RNGestureHandlerDetectorNativeComponent.ts | 50 +++++++- .../src/v3/HostGestureDetector.web.tsx | 25 ++-- .../src/v3/LogicDetector.tsx | 37 +++--- .../src/v3/LogicDetector.web.tsx | 39 +++--- .../src/v3/NativeDetector.tsx | 79 +++++++++---- .../src/web/handlers/GestureHandler.ts | 58 +++++++-- .../web/handlers/LongPressGestureHandler.ts | 5 +- .../web/handlers/NativeViewGestureHandler.ts | 5 +- .../src/web/handlers/PinchGestureHandler.ts | 5 +- .../web/handlers/RotationGestureHandler.ts | 5 +- .../src/web/interfaces.ts | 15 +-- 22 files changed, 413 insertions(+), 108 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerActionType.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerActionType.h index 76ab08b406..3a55299e95 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerActionType.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerActionType.h @@ -8,4 +8,5 @@ typedef NS_ENUM(NSInteger, RNGestureHandlerActionType) { RNGestureHandlerActionTypeJSFunctionNewAPI, // JS function or Animated.event with useNativeDriver: false using new // RNGH API RNGestureHandlerActionTypeNativeDetector, + RNGestureHandlerActionTypeLogicDetector, }; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h index 9ee88d0eb2..637904fe32 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h @@ -20,6 +20,12 @@ NS_ASSUME_NONNULL_BEGIN - (void)dispatchTouchEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerTouchEvent)event; +- (void)dispatchLogicStateChangeEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicStateChange)event; + +- (void)dispatchLogicGestureEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicEvent)event; + +- (void)dispatchLogicTouchEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicTouchEvent)event; + - (void)tryAttachNativeHandlersToChildView; - (void)detachNativeGestureHandlers; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index 071c4a0436..cab30528fa 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -24,8 +24,16 @@ typedef NS_ENUM(NSInteger, RNGestureHandlerMutation) { RNGestureHandlerMutationKeep, }; +struct LogicChild { + RNGHUIView *view; + std::vector handlerTags; + NSMutableSet *nativeHandlers; + NSMutableSet *attachedHandlers; +}; + @implementation RNGestureHandlerDetector { int _moduleId; + std::unordered_map logicChildren; } #if TARGET_OS_OSX @@ -87,6 +95,30 @@ - (void)dispatchTouchEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandl } } +- (void)dispatchLogicStateChangeEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicStateChange)event +{ + if (_eventEmitter != nullptr) { + std::dynamic_pointer_cast(_eventEmitter) + ->onGestureHandlerLogicStateChange(event); + } +} + +- (void)dispatchLogicGestureEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicEvent)event +{ + if (_eventEmitter != nullptr) { + std::dynamic_pointer_cast(_eventEmitter) + ->onGestureHandlerLogicEvent(event); + } +} + +- (void)dispatchLogicTouchEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicTouchEvent)event +{ + if (_eventEmitter != nullptr) { + std::dynamic_pointer_cast(_eventEmitter) + ->onGestureHandlerLogicTouchEvent(event); + } +} + - (BOOL)shouldAttachGestureToSubview:(NSNumber *)handlerTag { RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; @@ -125,25 +157,24 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics [super updateLayoutMetrics:newLayoutMetrics oldLayoutMetrics:oldLayoutMetrics]; } -- (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shared &)oldPropsBase -{ - const auto &newProps = *std::static_pointer_cast(propsBase); - const auto &oldProps = *std::static_pointer_cast(oldPropsBase); +- (void)updatePropsInternal:(const std::vector &)handlerTags + oldHandlerTags:(const std::vector &)oldHandlerTags + isLogic:(bool)isLogic + viewTag:(const int)viewTag + attachedHandlers:(NSMutableSet *)attachedHandlers + nativeHandlers:(NSMutableSet *)nativeHandlers - _moduleId = newProps.moduleId; +{ RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") std::unordered_map changes; - - if (oldPropsBase != nullptr) { - for (const auto oldHandler : oldProps.handlerTags) { - changes[oldHandler] = RNGestureHandlerMutationDetach; - } + for (const auto oldHandler : oldHandlerTags) { + changes[oldHandler] = RNGestureHandlerMutationDetach; } - for (const auto newHandler : newProps.handlerTags) { + for (const auto newHandler : handlerTags) { changes[newHandler] = changes.contains(newHandler) ? RNGestureHandlerMutationKeep : RNGestureHandlerMutationAttach; } @@ -154,18 +185,23 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar if ([self shouldAttachGestureToSubview:handlerTag]) { // It might happen that `attachHandlers` will be called before children are added into view hierarchy. In that // case we cannot attach `NativeViewGestureHandlers` here and we have to do it in `didAddSubview` method. - [_nativeHandlers addObject:handlerTag]; + [nativeHandlers addObject:handlerTag]; } else { - [handlerManager.registry attachHandlerWithTag:handlerTag - toView:self - withActionType:RNGestureHandlerActionTypeNativeDetector]; - - [_attachedHandlers addObject:handlerTag]; + if (isLogic) { + [handlerManager attachGestureHandler:handlerTag + toViewWithTag:@(viewTag) + withActionType:RNGestureHandlerActionTypeLogicDetector]; + } else { + [handlerManager.registry attachHandlerWithTag:handlerTag + toView:self + withActionType:RNGestureHandlerActionTypeNativeDetector]; + } + [attachedHandlers addObject:handlerTag]; } } else if (handlerChange.second == RNGestureHandlerMutationDetach) { [handlerManager.registry detachHandlerWithTag:handlerTag]; - [_attachedHandlers removeObject:handlerTag]; - [_nativeHandlers removeObject:handlerTag]; + [attachedHandlers removeObject:handlerTag]; + [nativeHandlers removeObject:handlerTag]; } } @@ -173,8 +209,45 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar if (!self.subviews[0]) { [self tryAttachNativeHandlersToChildView]; } +} + +- (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shared &)oldPropsBase +{ + const auto &newProps = *std::static_pointer_cast(propsBase); + const auto &oldProps = *std::static_pointer_cast(oldPropsBase); + _moduleId = newProps.moduleId; + static std::vector emptyVector; + const std::vector &oldHandlerTags = (oldPropsBase != nullptr) ? oldProps.handlerTags : emptyVector; + + [self updatePropsInternal:newProps.handlerTags + oldHandlerTags:oldHandlerTags + isLogic:false + viewTag:-1 + attachedHandlers:_attachedHandlers + nativeHandlers:_nativeHandlers]; [super updateProps:propsBase oldProps:oldPropsBase]; + + for (const RNGestureHandlerDetectorLogicChildrenStruct &child : newProps.logicChildren) { + if (logicChildren.find(child.viewTag) == logicChildren.end()) { + // Initialize the vector for a new logic child + RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; + react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") + logicChildren[child.viewTag] + .view = [handlerManager viewForReactTag:@(child.viewTag)]; + logicChildren[child.viewTag].handlerTags = {}; + logicChildren[child.viewTag].attachedHandlers = [NSMutableSet set]; + logicChildren[child.viewTag].nativeHandlers = [NSMutableSet set]; + [[handlerManager registry] registerLogicChild:@(child.viewTag) toParent:@(self.tag)]; + } + [self updatePropsInternal:child.handlerTags + oldHandlerTags:logicChildren[child.viewTag].handlerTags + isLogic:true + viewTag:child.viewTag + attachedHandlers:logicChildren[child.viewTag].attachedHandlers + nativeHandlers:logicChildren[child.viewTag].nativeHandlers]; + } + // Override to force hittesting to work outside bounds self.clipsToBounds = NO; } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h index a4ab245bae..7946033757 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h @@ -36,4 +36,5 @@ - (nullable RNGestureHandler *)handlerWithTag:(nonnull NSNumber *)handlerTag; +- (nullable RNGHUIView *)viewForReactTag:(nonnull NSNumber *)reactTag; @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index 7d86f7d072..87aa5357c1 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -311,6 +311,20 @@ - (void)sendEvent:(RNGestureHandlerStateChange *)event } break; } + case RNGestureHandlerActionTypeLogicDetector: { + NSNumber *parentTag = [_registry getLogicParent:@(detectorView.tag)]; + RNGHUIView *parentView = [self viewForReactTag:parentTag]; + if ([event isKindOfClass:[RNGestureHandlerEvent class]]) { + // TODO: handle forAnimated + RNGestureHandlerEvent *gestureEvent = (RNGestureHandlerEvent *)event; + auto nativeEvent = [gestureEvent getNativeLogicEvent:@(detectorView.tag)]; + [(RNGestureHandlerDetector *)parentView dispatchLogicGestureEvent:nativeEvent]; + } else { + auto nativeEvent = [event getNativeLogicEvent:@(detectorView.tag)]; + [(RNGestureHandlerDetector *)parentView dispatchLogicStateChangeEvent:nativeEvent]; + } + break; + } case RNGestureHandlerActionTypeReanimatedWorklet: [self sendEventForReanimated:event]; @@ -418,4 +432,8 @@ - (void)sendEventForDeviceEvent:(RNGestureHandlerStateChange *)event [_eventDispatcher sendDeviceEventWithName:@"onGestureHandlerStateChange" body:body]; } +- (RNGHUIView *)viewForReactTag:(NSNumber *)reactTag +{ + return [_viewRegistry viewForReactTag:reactTag]; +} @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h index 21543720a3..bb621bb1b9 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h @@ -5,11 +5,13 @@ @interface RNGestureHandlerEvent (NativeEvent) - (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerEvent)getNativeEvent; - +- (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicEvent)getNativeLogicEvent: + (NSNumber *)childTag; @end @interface RNGestureHandlerStateChange (NativeEvent) - (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerStateChange)getNativeEvent; - +- (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicStateChange)getNativeLogicEvent: + (NSNumber *)childTag; @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.mm index feea2e7785..691da918f3 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.mm @@ -67,10 +67,25 @@ @implementation RNGestureHandlerEvent (NativeEvent) return nativeEvent; } +- (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicEvent)getNativeLogicEvent: + (NSNumber *)childTag +{ + folly::dynamic handlerData = rngh_dynamicFromId(self.extraData.data); + + facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicEvent nativeEvent = { + .handlerTag = [self.handlerTag intValue], + .state = static_cast(self.state), + .handlerData = handlerData, + .childTag = [childTag intValue]}; + + return nativeEvent; +} @end @implementation RNGestureHandlerStateChange (NativeEvent) +// TODO: unify logic getter + - (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerStateChange)getNativeEvent { folly::dynamic handlerData = rngh_dynamicFromId(self.extraData.data); @@ -85,4 +100,18 @@ @implementation RNGestureHandlerStateChange (NativeEvent) return nativeEvent; } +- (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicStateChange)getNativeLogicEvent: + (NSNumber *)childTag +{ + folly::dynamic handlerData = rngh_dynamicFromId(self.extraData.data); + + facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicStateChange nativeEvent = { + .handlerTag = [self.handlerTag intValue], + .state = static_cast(self.state), + .oldState = static_cast(self.previousState), + .handlerData = handlerData, + .childTag = [childTag intValue]}; + + return nativeEvent; +} @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h index dd73a1ce89..4f066a9066 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h @@ -18,5 +18,6 @@ - (void)detachHandlerWithTag:(nonnull NSNumber *)handlerTag; - (void)dropHandlerWithTag:(nonnull NSNumber *)handlerTag; - (void)dropAllHandlers; - +- (nullable NSNumber *)getLogicParent:(nonnull NSNumber *)child; +- (void)registerLogicChild:(nonnull NSNumber *)child toParent:(nonnull NSNumber *)parent; @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m index 856c568e53..357c0393e8 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m @@ -12,12 +12,14 @@ @implementation RNGestureHandlerRegistry { NSMutableDictionary *_handlers; + NSMutableDictionary *_logicChildMap; } - (instancetype)init { if ((self = [super init])) { _handlers = [NSMutableDictionary new]; + _logicChildMap = [NSMutableDictionary new]; } return self; } @@ -32,6 +34,16 @@ - (void)registerGestureHandler:(RNGestureHandler *)gestureHandler _handlers[gestureHandler.tag] = gestureHandler; } +- (void)registerLogicChild:(NSNumber *)child toParent:(NSNumber *)parent +{ + _logicChildMap[child] = parent; +} + +- (NSNumber *)getLogicParent:(NSNumber *)child +{ + return _logicChildMap[child]; +} + - (void)attachHandlerWithTag:(NSNumber *)handlerTag toView:(RNGHUIView *)view withActionType:(RNGestureHandlerActionType)actionType diff --git a/packages/react-native-gesture-handler/src/ActionType.ts b/packages/react-native-gesture-handler/src/ActionType.ts index a902e8356f..06135378eb 100644 --- a/packages/react-native-gesture-handler/src/ActionType.ts +++ b/packages/react-native-gesture-handler/src/ActionType.ts @@ -4,6 +4,7 @@ export const ActionType = { JS_FUNCTION_OLD_API: 3, JS_FUNCTION_NEW_API: 4, NATIVE_DETECTOR: 5, + LogicDetector: 6, } as const; // eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; it can be used as a type and as a value diff --git a/packages/react-native-gesture-handler/src/RNGestureHandlerModule.web.ts b/packages/react-native-gesture-handler/src/RNGestureHandlerModule.web.ts index a87d652ba1..d79670176e 100644 --- a/packages/react-native-gesture-handler/src/RNGestureHandlerModule.web.ts +++ b/packages/react-native-gesture-handler/src/RNGestureHandlerModule.web.ts @@ -48,7 +48,8 @@ export default { // eslint-disable-next-line @typescript-eslint/no-explicit-any newView: any, actionType: ActionType, - propsRef: React.RefObject + propsRef: React.RefObject, + childTag?: number ) { if (!(newView instanceof Element || newView instanceof React.Component)) { shouldPreventDrop = true; @@ -63,7 +64,12 @@ export default { } // @ts-ignore Types should be HTMLElement or React.Component - NodeManager.getHandler(handlerTag).init(newView, propsRef, actionType); + NodeManager.getHandler(handlerTag).init( + newView, + propsRef, + actionType, + childTag + ); }, detachGestureHandler(handlerTag: number) { if (shouldPreventDrop) { diff --git a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts index 4a45a8257e..759dafff90 100644 --- a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts +++ b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts @@ -13,6 +13,13 @@ type GestureHandlerEvent = Readonly<{ handlerData: UnsafeMixed; }>; +type GestureHandlerLogicEvent = Readonly<{ + handlerTag: Int32; + state: Int32; + handlerData: UnsafeMixed; + childTag: Int32; +}>; + type GestureHandlerStateChangeEvent = Readonly<{ handlerTag: Int32; state: Int32; @@ -20,6 +27,14 @@ type GestureHandlerStateChangeEvent = Readonly<{ handlerData: UnsafeMixed; }>; +type GestureHandlerLogicStateChangeEvent = Readonly<{ + handlerTag: Int32; + state: Int32; + oldState: Int32; + handlerData: UnsafeMixed; + childTag: Int32; +}>; + type GestureHandlerTouchEvent = Readonly<{ handlerTag: Int32; numberOfTouches: Int32; @@ -42,15 +57,48 @@ type GestureHandlerTouchEvent = Readonly<{ pointerType: Int32; }>; +type GestureHandlerLogicTouchEvent = Readonly<{ + handlerTag: Int32; + numberOfTouches: Int32; + state: Int32; + eventType: Int32; + allTouches: { + id: Int32; + x: Double; + y: Double; + absoluteX: Double; + absoluteY: Double; + }[]; + changedTouches: { + id: Int32; + x: Double; + y: Double; + absoluteX: Double; + absoluteY: Double; + }[]; + pointerType: Int32; + childTag: Int32; +}>; + +export interface LogicProps { + handlerTags: Int32[]; + moduleId: Int32; + viewTag: Int32; +} + export interface NativeProps extends ViewProps { onGestureHandlerEvent?: DirectEventHandler; onGestureHandlerAnimatedEvent?: DirectEventHandler; onGestureHandlerStateChange?: DirectEventHandler; onGestureHandlerTouchEvent?: DirectEventHandler; + onGestureHandlerLogicEvent?: DirectEventHandler; + onGestureHandlerLogicStateChange?: DirectEventHandler; + onGestureHandlerLogicTouchEvent?: DirectEventHandler; + handlerTags: Int32[]; moduleId: Int32; - logicChildren: UnsafeMixed; + logicChildren: LogicProps[]; } export default codegenNativeComponent('RNGestureHandlerDetector', { diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index 100d4fb7f8..756bd19bca 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -1,10 +1,10 @@ import { Ref, RefObject, useEffect, useRef } from 'react'; import RNGestureHandlerModule from '../RNGestureHandlerModule.web'; import { ActionType } from '../ActionType'; -import { LogicDetectorProps, PropsRef } from '../web/interfaces'; +import { PropsRef } from '../web/interfaces'; import { View } from 'react-native'; import { tagMessage } from '../utils'; - +import { LogicDetectorProps } from './LogicDetector.web'; export interface GestureHandlerDetectorProps extends PropsRef { handlerTags: number[]; moduleId: number; @@ -44,11 +44,11 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { propsRef: RefObject, currentHandlerTags: Set, attachedHandlerTags: Set, - attachedNativeHandlerTags: Set + attachedNativeHandlerTags: Set, + childTag?: number ) => { const oldHandlerTags = attachedHandlerTags.difference(currentHandlerTags); const newHandlerTags = currentHandlerTags.difference(attachedHandlerTags); - detachHandlers( oldHandlerTags, attachedHandlerTags, @@ -69,16 +69,18 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { RNGestureHandlerModule.attachGestureHandler( tag, viewRef.current.firstChild, - ActionType.NATIVE_DETECTOR, - propsRef + childTag ? ActionType.LogicDetector : ActionType.NATIVE_DETECTOR, + propsRef, + childTag ); attachedNativeHandlerTags.add(tag); } else { RNGestureHandlerModule.attachGestureHandler( tag, viewRef.current, - ActionType.NATIVE_DETECTOR, - propsRef + childTag ? ActionType.LogicDetector : ActionType.NATIVE_DETECTOR, + propsRef, + childTag ); } attachedHandlerTags.add(tag); @@ -116,10 +118,11 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { if (attachedHandlerTags && attachedNativeHandlerTags) { attachHandlers( child.viewRef, - child.propsRef, - new Set(child.propsRef.current.handlerTags), + propsRef, + new Set(child.handlerTags), attachedHandlerTags, - attachedNativeHandlerTags + attachedNativeHandlerTags, + child.viewTag ); } }); diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 57b7501e86..0f9b172885 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -1,12 +1,19 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { NativeDetectorProps, useDetectorContext } from './NativeDetector'; import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; +import { findNodeHandle } from 'react-native'; + +export interface LogicDetectorProps { + viewTag: number; + moduleId: number; + handlerTags: number[]; +} export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); const viewRef = useRef(null); - - const propsRef = useRef({ + const [viewTag, setViewTag] = useState(-1); + const logicMethods = { onGestureHandlerStateChange: props.gesture.gestureEvents.onGestureHandlerStateChange, onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent!, @@ -14,22 +21,24 @@ export const LogicDetector = (props: NativeDetectorProps) => { props.gesture.gestureEvents.onGestureHandlerAnimatedEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, - moduleId: globalThis._RNGH_MODULE_ID, - handlerTags: [props.gesture.tag], - }); - - const logicChild = useRef({ - viewRef: viewRef, - propsRef: propsRef, - }); + }; + useEffect(() => { + setViewTag(findNodeHandle(viewRef.current)!); + }, []); useEffect(() => { - register(logicChild.current); + const logicProps = { + viewTag: viewTag, + moduleId: globalThis._RNGH_MODULE_ID, + handlerTags: [props.gesture.tag], + }; + + register(logicProps, logicMethods); return () => { - unregister(logicChild.current); + unregister(viewTag); }; - }, [register, unregister]); + }, [viewTag, props.gesture.tag, register, unregister]); return {props.children}; }; diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx index 57b7501e86..f941acca50 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx @@ -1,12 +1,19 @@ -import { useEffect, useRef } from 'react'; +import { RefObject, useEffect, useRef, useState } from 'react'; import { NativeDetectorProps, useDetectorContext } from './NativeDetector'; import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; +export interface LogicDetectorProps { + viewTag: number; + viewRef: RefObject; + moduleId: number; + handlerTags: number[]; +} + export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); const viewRef = useRef(null); - - const propsRef = useRef({ + const [viewTag, setViewTag] = useState(-1); + const logicMethods = { onGestureHandlerStateChange: props.gesture.gestureEvents.onGestureHandlerStateChange, onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent!, @@ -14,22 +21,26 @@ export const LogicDetector = (props: NativeDetectorProps) => { props.gesture.gestureEvents.onGestureHandlerAnimatedEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, - moduleId: globalThis._RNGH_MODULE_ID, - handlerTags: [props.gesture.tag], - }); - - const logicChild = useRef({ - viewRef: viewRef, - propsRef: propsRef, - }); + }; + useEffect(() => { + // TODO: set tags from parent + setViewTag(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)); + }, []); useEffect(() => { - register(logicChild.current); + const logicProps = { + viewRef: viewRef, + viewTag: viewTag, + moduleId: globalThis._RNGH_MODULE_ID, + handlerTags: [props.gesture.tag], + }; + + register(logicProps, logicMethods); return () => { - unregister(logicChild.current); + unregister(viewTag); }; - }, [register, unregister]); + }, [viewTag, props.gesture.tag, register, unregister]); return {props.children}; }; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index 13db76f411..6492748d2d 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -1,17 +1,29 @@ -import React, { createContext, useCallback, useContext, useState } from 'react'; +import React, { + createContext, + RefObject, + useCallback, + useContext, + useRef, + useState, +} from 'react'; import { NativeGesture } from './hooks/useGesture'; import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../utils'; -import { LogicDetectorProps } from '../web/interfaces'; +import { LogicDetectorProps } from './LogicDetector'; export interface NativeDetectorProps { children?: React.ReactNode; gesture: NativeGesture; } +interface LogicMethods { + onGestureHandlerEvent: (e: unknown) => void; + onGestureHandlerStateChange: (e: unknown) => void; + onGestureHandlerTouchEvent?: (e: unknown) => void; +} const AnimatedNativeDetector = Animated.createAnimatedComponent(HostGestureDetector); @@ -19,16 +31,18 @@ const ReanimatedNativeDetector = Reanimated?.default.createAnimatedComponent(HostGestureDetector); type DetectorContextType = { - register: (child: LogicDetectorProps) => void; - unregister: (child: LogicDetectorProps) => void; + register: (child: LogicDetectorProps, methods: LogicMethods) => void; + unregister: (child: number | RefObject) => void; }; const DetectorContext = createContext(null); export function NativeDetector({ gesture, children }: NativeDetectorProps) { - const [logicChildren, setLogicChildren] = useState>( - new Set() - ); + const [logicChildren, setLogicChildren] = useState([]); + const logicMethods = useRef< + Map, LogicMethods> + >(new Map()); + const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents ? AnimatedNativeDetector : // TODO: Remove this cast when we properly type config @@ -36,19 +50,27 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { ? ReanimatedNativeDetector : HostGestureDetector; - const register = useCallback((child: LogicDetectorProps) => { - setLogicChildren((prev: Set) => - new Set(prev).add(child) - ); - }, []); + const register = useCallback( + (child: LogicDetectorProps, methods: LogicMethods) => { + if (child.viewTag !== -1) { + setLogicChildren((prev) => { + if (prev.some((c) => c.viewTag === child.viewTag)) { + return prev; + } + return [...prev, child]; + }); + } + logicMethods.current.set(child.viewTag, methods); + }, + [] + ); - const unregister = useCallback((child: LogicDetectorProps) => { - setLogicChildren((prev: Set) => { - const updated = new Set(prev); - updated.delete(child); - return updated; - }); - }, []); + const unregister = useCallback( + (childTag: number | RefObject) => { + setLogicChildren((prev) => prev.filter((c) => c.viewTag !== childTag)); + }, + [] + ); // It might happen only with ReanimatedNativeDetector if (!NativeDetectorComponent) { @@ -58,7 +80,6 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { ) ); } - return ( { + logicMethods.current + .get(e.nativeEvent.childTag) + ?.onGestureHandlerEvent(e); + }} + onGestureHandlerLogicStateChange={(e) => { + logicMethods.current + .get(e.nativeEvent.childTag) + ?.onGestureHandlerStateChange(e); + }} + onGestureHandlerLogicTouchEvent={(e) => { + const touchEvent = logicMethods.current.get( + e.nativeEvent.childTag + )?.onGestureHandlerTouchEvent; + if (touchEvent) { + touchEvent(e); + } + }} moduleId={globalThis._RNGH_MODULE_ID} handlerTags={[gesture.tag]} style={styles.detector} diff --git a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts index 09adf7ec63..48239d9d10 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts @@ -37,6 +37,7 @@ export default abstract class GestureHandler implements IGestureHandler { protected hasCustomActivationCriteria = false; private _enabled = false; + private childTag?: number; private viewRef: number | null = null; private propsRef: React.RefObject | null = null; private actionType: ActionType | null = null; @@ -69,19 +70,21 @@ export default abstract class GestureHandler implements IGestureHandler { protected init( viewRef: number, propsRef: React.RefObject, - actionType: ActionType + actionType: ActionType, + childTag?: number ) { this.propsRef = propsRef; this.viewRef = viewRef; this.actionType = actionType; this.state = State.UNDETERMINED; + this.childTag = childTag; this.delegate.init(viewRef, this); } public detach() { if (this.state === State.ACTIVE) { - this.cancel(); + this.cancelTouches(); } else { this.fail(); } @@ -90,6 +93,7 @@ export default abstract class GestureHandler implements IGestureHandler { this.actionType = null; this.state = State.UNDETERMINED; this.forAnimated = false; + this.childTag = undefined; this.delegate.detach(); } @@ -373,8 +377,11 @@ export default abstract class GestureHandler implements IGestureHandler { return; } this.ensurePropsRef(); - const { onGestureHandlerEvent, onGestureHandlerTouchEvent }: PropsRef = - this.propsRef!.current; + const { + onGestureHandlerEvent, + onGestureHandlerTouchEvent, + onGestureHandlerLogicTouchEvent, + }: PropsRef = this.propsRef!.current; const touchEvent: ResultEvent | undefined = this.transformTouchEvent(event); @@ -385,6 +392,11 @@ export default abstract class GestureHandler implements IGestureHandler { this.actionType === ActionType.NATIVE_DETECTOR ) { invokeNullableMethod(onGestureHandlerTouchEvent, touchEvent); + } else if ( + onGestureHandlerLogicTouchEvent && + this.actionType === ActionType.LogicDetector + ) { + invokeNullableMethod(onGestureHandlerLogicTouchEvent, touchEvent); } else { invokeNullableMethod(onGestureHandlerEvent, touchEvent); } @@ -400,32 +412,60 @@ export default abstract class GestureHandler implements IGestureHandler { onGestureHandlerEvent, onGestureHandlerStateChange, onGestureHandlerAnimatedEvent, + onGestureHandlerLogicStateChange, + onGestureHandlerLogicEvent, }: PropsRef = this.propsRef!.current; + const resultEvent: ResultEvent = - this.actionType !== ActionType.NATIVE_DETECTOR + this.actionType !== ActionType.NATIVE_DETECTOR && + this.actionType !== ActionType.LogicDetector ? this.transformEventData(newState, oldState) : this.lastSentState !== newState ? this.transformStateChangeEvent(newState, oldState) : this.transformUpdateEvent(newState); + // TODO: cleanup the logic detector types + if (this.actionType === ActionType.LogicDetector) { + resultEvent.nativeEvent = { + ...resultEvent.nativeEvent, + childTag: this.childTag, + }; + } // In the v2 API oldState field has to be undefined, unless we send event state changed // Here the order is flipped to avoid workarounds such as making backup of the state and setting it to undefined first, then changing it back // Flipping order with setting oldState to undefined solves issue, when events were being sent twice instead of once // However, this may cause trouble in the future (but for now we don't know that) - if (this.lastSentState !== newState) { this.lastSentState = newState; - invokeNullableMethod(onGestureHandlerStateChange, resultEvent); + if ( + onGestureHandlerLogicStateChange && + this.actionType === ActionType.LogicDetector + ) { + invokeNullableMethod(onGestureHandlerLogicStateChange, resultEvent); + } else { + invokeNullableMethod(onGestureHandlerStateChange, resultEvent); + } } if (this.state === State.ACTIVE) { - if (this.actionType !== ActionType.NATIVE_DETECTOR) { + if ( + this.actionType !== ActionType.NATIVE_DETECTOR && + this.actionType !== ActionType.LogicDetector + ) { (resultEvent.nativeEvent as GestureHandlerNativeEvent).oldState = undefined; } if (onGestureHandlerAnimatedEvent && this.forAnimated) { invokeNullableMethod(onGestureHandlerAnimatedEvent, resultEvent); } - invokeNullableMethod(onGestureHandlerEvent, resultEvent); + + if ( + onGestureHandlerLogicEvent && + this.actionType === ActionType.LogicDetector + ) { + invokeNullableMethod(onGestureHandlerLogicEvent, resultEvent); + } else { + invokeNullableMethod(onGestureHandlerEvent, resultEvent); + } } }; diff --git a/packages/react-native-gesture-handler/src/web/handlers/LongPressGestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/LongPressGestureHandler.ts index 8ab17b76f5..dc022b94ad 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/LongPressGestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/LongPressGestureHandler.ts @@ -25,13 +25,14 @@ export default class LongPressGestureHandler extends GestureHandler { public override init( ref: number, propsRef: React.RefObject, - actionType: ActionType + actionType: ActionType, + childTag?: number ) { if (this.config.enableContextMenu === undefined) { this.config.enableContextMenu = false; } - super.init(ref, propsRef, actionType); + super.init(ref, propsRef, actionType, childTag); } protected override transformNativeEvent() { diff --git a/packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts index 135fd31dfb..fbc43ce50d 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts @@ -20,9 +20,10 @@ export default class NativeViewGestureHandler extends GestureHandler { public override init( ref: number, propsRef: React.RefObject, - actionType: ActionType + actionType: ActionType, + childTag?: number ): void { - super.init(ref, propsRef, actionType); + super.init(ref, propsRef, actionType, childTag); this.shouldCancelWhenOutside = true; diff --git a/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts index 3461ec2377..71a3401888 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts @@ -52,9 +52,10 @@ export default class PinchGestureHandler extends GestureHandler { public override init( ref: number, propsRef: React.RefObject, - actionType: ActionType + actionType: ActionType, + childTag?: number ) { - super.init(ref, propsRef, actionType); + super.init(ref, propsRef, actionType, childTag); this.shouldCancelWhenOutside = false; } diff --git a/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts index 795c31dd3a..193e7a4416 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts @@ -48,9 +48,10 @@ export default class RotationGestureHandler extends GestureHandler { public override init( ref: number, propsRef: React.RefObject, - actionType: ActionType + actionType: ActionType, + childTag?: number ): void { - super.init(ref, propsRef, actionType); + super.init(ref, propsRef, actionType, childTag); this.shouldCancelWhenOutside = false; } diff --git a/packages/react-native-gesture-handler/src/web/interfaces.ts b/packages/react-native-gesture-handler/src/web/interfaces.ts index cd50916ecb..21fb531414 100644 --- a/packages/react-native-gesture-handler/src/web/interfaces.ts +++ b/packages/react-native-gesture-handler/src/web/interfaces.ts @@ -13,8 +13,6 @@ import { GestureUpdateEventWithData, } from '../v3/types'; import { State } from '../State'; -import { RefObject } from 'react'; -import { GestureHandlerDetectorProps } from '../v3/HostGestureDetector.web'; export interface HitSlop { left?: number; @@ -122,7 +120,11 @@ type ResultEventType = | GestureTouchEvent | GestureHandlerNativeEvent; -export interface ResultEvent +type LogicResultEventType = ResultEventType & { + childTag?: number; +}; + +export interface ResultEvent extends Record { nativeEvent: T; timeStamp: number; @@ -133,11 +135,10 @@ export interface PropsRef { onGestureHandlerAnimatedEvent?: (e: ResultEvent) => void; onGestureHandlerStateChange: (e: ResultEvent) => void; onGestureHandlerTouchEvent?: (e: ResultEvent) => void; -} -export interface LogicDetectorProps { - viewRef: RefObject; - propsRef: RefObject; + onGestureHandlerLogicEvent?: (e: ResultEvent) => void; + onGestureHandlerLogicStateChange?: (e: ResultEvent) => void; + onGestureHandlerLogicTouchEvent?: (e: ResultEvent) => void; } export interface AdaptedEvent { From 81bf405afff1521ba06b4df0ea39d01b0d63b9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 1 Sep 2025 15:45:34 +0200 Subject: [PATCH 06/90] fix reanimated version --- apps/expo-example/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/expo-example/package.json b/apps/expo-example/package.json index d752fb063f..87718b45c5 100644 --- a/apps/expo-example/package.json +++ b/apps/expo-example/package.json @@ -27,11 +27,12 @@ "react-dom": "19.1.0", "react-native": "0.81.0", "react-native-gesture-handler": "workspace:*", - "react-native-reanimated": "^3.19.1", + "react-native-reanimated": "^4.1.0", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.14.0", "react-native-svg": "15.12.1", - "react-native-web": "^0.21.0" + "react-native-web": "^0.21.0", + "react-native-worklets": "^0.5.0" }, "devDependencies": { "@babel/core": "^7.25.2", From a90b83cecb8d802f8df9ee74d6d8a38299e33ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 1 Sep 2025 16:02:42 +0200 Subject: [PATCH 07/90] yarn lock --- yarn.lock | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 78f2cf7d6f..f52c8f3956 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8392,11 +8392,12 @@ __metadata: react-dom: "npm:19.1.0" react-native: "npm:0.81.0" react-native-gesture-handler: "workspace:*" - react-native-reanimated: "npm:^3.19.1" + react-native-reanimated: "npm:^4.1.0" react-native-safe-area-context: "npm:~5.6.0" react-native-screens: "npm:~4.14.0" react-native-svg: "npm:15.12.1" react-native-web: "npm:^0.21.0" + react-native-worklets: "npm:^0.5.0" typescript: "npm:~5.9.2" languageName: unknown linkType: soft @@ -14297,7 +14298,7 @@ __metadata: languageName: node linkType: hard -"react-native-reanimated@npm:^3.18.0, react-native-reanimated@npm:^3.19.1": +"react-native-reanimated@npm:^3.18.0": version: 3.19.1 resolution: "react-native-reanimated@npm:3.19.1" dependencies: @@ -14336,6 +14337,21 @@ __metadata: languageName: node linkType: hard +"react-native-reanimated@npm:^4.1.0": + version: 4.1.0 + resolution: "react-native-reanimated@npm:4.1.0" + dependencies: + react-native-is-edge-to-edge: "npm:^1.2.1" + semver: "npm:7.7.2" + peerDependencies: + "@babel/core": ^7.0.0-0 + react: "*" + react-native: "*" + react-native-worklets: ">=0.5.0" + checksum: 10c0/45a072bfc3a56fc84bf21cb9853aec61622cf5e21be6f3f900988f8c53702cc6b2567380febe63c78166dac5aa50834b29d73dc1a7fdb4b9b950abdad1ef0cc4 + languageName: node + linkType: hard + "react-native-safe-area-context@npm:5.4.0": version: 5.4.0 resolution: "react-native-safe-area-context@npm:5.4.0" @@ -14453,6 +14469,29 @@ __metadata: languageName: node linkType: hard +"react-native-worklets@npm:^0.5.0": + version: 0.5.0 + resolution: "react-native-worklets@npm:0.5.0" + dependencies: + "@babel/plugin-transform-arrow-functions": "npm:^7.0.0-0" + "@babel/plugin-transform-class-properties": "npm:^7.0.0-0" + "@babel/plugin-transform-classes": "npm:^7.0.0-0" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.0.0-0" + "@babel/plugin-transform-optional-chaining": "npm:^7.0.0-0" + "@babel/plugin-transform-shorthand-properties": "npm:^7.0.0-0" + "@babel/plugin-transform-template-literals": "npm:^7.0.0-0" + "@babel/plugin-transform-unicode-regex": "npm:^7.0.0-0" + "@babel/preset-typescript": "npm:^7.16.7" + convert-source-map: "npm:^2.0.0" + semver: "npm:7.7.2" + peerDependencies: + "@babel/core": ^7.0.0-0 + react: "*" + react-native: "*" + checksum: 10c0/2e9b2b3b3c1549c5bc32d758aff538384bb851d80ed2a13c0c011dbe5857f62c2fcbd02dd2a7b2f38f8c357a6b1f2939f756ae24ea34a277e62a2a6c087e5ff8 + languageName: node + linkType: hard + "react-native@npm:*, react-native@npm:0.81.0": version: 0.81.0 resolution: "react-native@npm:0.81.0" From 8c03f58cc583e4ed70dac50dd070e4c0fadb57d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 1 Sep 2025 17:27:07 +0200 Subject: [PATCH 08/90] worklets --- .../src/v3/NativeDetector.tsx | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index 6492748d2d..4a61e74a45 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -13,6 +13,7 @@ import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../utils'; import { LogicDetectorProps } from './LogicDetector'; +import { runOnJS } from 'react-native-reanimated'; export interface NativeDetectorProps { children?: React.ReactNode; @@ -94,21 +95,27 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { gesture.gestureEvents.onGestureHandlerTouchEvent } onGestureHandlerLogicEvent={(e) => { - logicMethods.current - .get(e.nativeEvent.childTag) - ?.onGestureHandlerEvent(e); + const logicMethod = logicMethods.current.get( + e.nativeEvent.childTag + )?.onGestureHandlerEvent; + if (logicMethod) { + runOnJS(logicMethod); + } }} onGestureHandlerLogicStateChange={(e) => { - logicMethods.current - .get(e.nativeEvent.childTag) - ?.onGestureHandlerStateChange(e); + const logicMethod = logicMethods.current.get( + e.nativeEvent.childTag + )?.onGestureHandlerStateChange; + if (logicMethod) { + runOnJS(logicMethod); + } }} onGestureHandlerLogicTouchEvent={(e) => { - const touchEvent = logicMethods.current.get( + const logicMethod = logicMethods.current.get( e.nativeEvent.childTag )?.onGestureHandlerTouchEvent; - if (touchEvent) { - touchEvent(e); + if (logicMethod) { + runOnJS(logicMethod); } }} moduleId={globalThis._RNGH_MODULE_ID} From a3256d4947032c5ee115b5dfa982872de968f484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 2 Sep 2025 13:59:48 +0200 Subject: [PATCH 09/90] detach handlers --- .../apple/RNGestureHandlerDetector.mm | 29 +++++++-- .../src/v3/HostGestureDetector.web.tsx | 59 +++++++++++++------ 2 files changed, 65 insertions(+), 23 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index cab30528fa..b25a24449c 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -69,6 +69,12 @@ - (void)willMoveToWindow:(RNGHWindow *)newWindow NSNumber *handlerTag = [NSNumber numberWithInt:handler]; [handlerManager.registry detachHandlerWithTag:handlerTag]; } + for (const auto &child : logicChildren) { + for (id handlerTag : child.second.attachedHandlers) { + [handlerManager.registry detachHandlerWithTag:handlerTag]; + } + } + logicChildren.clear(); } } @@ -227,19 +233,25 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar attachedHandlers:_attachedHandlers nativeHandlers:_nativeHandlers]; [super updateProps:propsBase oldProps:oldPropsBase]; + RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; + react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") + + std::unordered_map + shouldKeepLogicChild; + for (const std::pair &child : logicChildren) { + shouldKeepLogicChild[child.first] = false; + } for (const RNGestureHandlerDetectorLogicChildrenStruct &child : newProps.logicChildren) { if (logicChildren.find(child.viewTag) == logicChildren.end()) { // Initialize the vector for a new logic child - RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; - react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") - logicChildren[child.viewTag] - .view = [handlerManager viewForReactTag:@(child.viewTag)]; + logicChildren[child.viewTag].view = [handlerManager viewForReactTag:@(child.viewTag)]; logicChildren[child.viewTag].handlerTags = {}; logicChildren[child.viewTag].attachedHandlers = [NSMutableSet set]; logicChildren[child.viewTag].nativeHandlers = [NSMutableSet set]; [[handlerManager registry] registerLogicChild:@(child.viewTag) toParent:@(self.tag)]; } + shouldKeepLogicChild[child.viewTag] = true; [self updatePropsInternal:child.handlerTags oldHandlerTags:logicChildren[child.viewTag].handlerTags isLogic:true @@ -248,6 +260,15 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar nativeHandlers:logicChildren[child.viewTag].nativeHandlers]; } + for (const auto &child : shouldKeepLogicChild) { + if (!child.second) { + for (id handlerTag : logicChildren[child.first].attachedHandlers) { + [handlerManager.registry detachHandlerWithTag:handlerTag]; + } + logicChildren.erase(child.first); + } + } + // Override to force hittesting to work outside bounds self.clipsToBounds = NO; } diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index d73aa8c697..e90eada4b4 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -25,7 +25,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { const attachedHandlerTags = useRef>(new Set()); const attachedNativeHandlerTags = useRef>(new Set()); - const logicChildren = useRef>(new Map()); + const logicChildren = useRef>(new Map()); const detachHandlers = ( oldHandlerTags: Set, @@ -103,18 +103,29 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { attachedHandlerTags.current, attachedNativeHandlerTags.current ); + }, [handlerTags, children]); + + useEffect(() => { + const shouldKeepLogicChild: Map = new Map(); - props.logicChildren?.forEach((child) => { - if (!logicChildren.current.has(child)) { - logicChildren.current.set(child, { + for (const key of logicChildren.current.keys()) { + shouldKeepLogicChild.set(key, false); + } + + props.logicChildren?.forEach((child, key) => { + if (!logicChildren.current.has(child.viewTag)) { + logicChildren.current.set(child.viewTag, { attachedHandlerTags: new Set(), attachedNativeHandlerTags: new Set(), }); } - const attachedHandlerTags = - logicChildren.current.get(child)?.attachedHandlerTags; - const attachedNativeHandlerTags = - logicChildren.current.get(child)?.attachedNativeHandlerTags; + shouldKeepLogicChild.set(key.viewTag, true); + const attachedHandlerTags = logicChildren.current.get( + child.viewTag + )?.attachedHandlerTags; + const attachedNativeHandlerTags = logicChildren.current.get( + child.viewTag + )?.attachedNativeHandlerTags; if (attachedHandlerTags && attachedNativeHandlerTags) { attachHandlers( child.viewRef, @@ -126,20 +137,13 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { ); } }); - }, [handlerTags, children]); - useEffect(() => { - return () => { - detachHandlers( - attachedHandlerTags.current, - attachedHandlerTags.current, - attachedNativeHandlerTags.current - ); - props.logicChildren?.forEach((child) => { + shouldKeepLogicChild.forEach((value, key) => { + if (value) { const attachedHandlerTags = - logicChildren.current.get(child)?.attachedHandlerTags; + logicChildren.current.get(key)?.attachedHandlerTags; const attachedNativeHandlerTags = - logicChildren.current.get(child)?.attachedNativeHandlerTags; + logicChildren.current.get(key)?.attachedNativeHandlerTags; if (attachedHandlerTags && attachedNativeHandlerTags) { detachHandlers( attachedHandlerTags, @@ -147,6 +151,23 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { attachedNativeHandlerTags ); } + } + }); + }, [props.logicChildren]); + + useEffect(() => { + return () => { + detachHandlers( + attachedHandlerTags.current, + attachedHandlerTags.current, + attachedNativeHandlerTags.current + ); + logicChildren?.current.forEach((child) => { + detachHandlers( + child.attachedHandlerTags, + child.attachedHandlerTags, + child.attachedNativeHandlerTags + ); }); }; }, []); From 6f45a3397f657784ee3422cd8368d84ce8af2dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 3 Sep 2025 10:50:43 +0200 Subject: [PATCH 10/90] worklets fix --- .../react-native-gesture-handler/src/utils.ts | 64 +++++++++++++++++++ .../src/v3/LogicDetector.tsx | 4 +- .../src/v3/LogicDetector.web.tsx | 7 +- .../src/v3/NativeDetector.tsx | 43 ++++++------- .../src/web/handlers/GestureHandler.ts | 55 +--------------- 5 files changed, 89 insertions(+), 84 deletions(-) diff --git a/packages/react-native-gesture-handler/src/utils.ts b/packages/react-native-gesture-handler/src/utils.ts index 8faf8bdf64..ddd84913cb 100644 --- a/packages/react-native-gesture-handler/src/utils.ts +++ b/packages/react-native-gesture-handler/src/utils.ts @@ -105,4 +105,68 @@ export function deepEqual(obj1: any, obj2: any) { return true; } +export function invokeNullableMethod( + method: + | ((event: any) => void) + | { __getHandler: () => (event: any) => void } + | { __nodeConfig: { argMapping: unknown[] } } + | { workletEventHandler: { worklet: (event: any) => void } } + | null + | undefined, + event: any +): void { + if (!method) { + return; + } + + if (typeof method === 'function') { + method(event); + return; + } + + if ('__getHandler' in method && typeof method.__getHandler === 'function') { + const handler = method.__getHandler(); + invokeNullableMethod(handler, event); + return; + } + + if ('workletEventHandler' in method) { + const we = method.workletEventHandler; + if ('worklet' in we) { + invokeNullableMethod(we.worklet, event); + } + return; + } + + if (!('__nodeConfig' in method)) { + return; + } + + const { argMapping }: { argMapping: unknown } = method.__nodeConfig; + if (!Array.isArray(argMapping)) { + return; + } + + for (const [index, [key, value]] of argMapping.entries()) { + if (!(key in event.nativeEvent)) { + continue; + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const nativeValue = event.nativeEvent[key]; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (value?.setValue) { + // Reanimated API + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + value.setValue(nativeValue); + } else { + // RN Animated API + method.__nodeConfig.argMapping[index] = [key, nativeValue]; + } + } + + return; +} + export const INT32_MAX = 2 ** 31 - 1; diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 0f9b172885..63f392833f 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -16,9 +16,7 @@ export const LogicDetector = (props: NativeDetectorProps) => { const logicMethods = { onGestureHandlerStateChange: props.gesture.gestureEvents.onGestureHandlerStateChange, - onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent!, - onGestureHandlerAnimatedEvent: - props.gesture.gestureEvents.onGestureHandlerAnimatedEvent, + onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, }; diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx index f941acca50..c38a98faaf 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx @@ -1,7 +1,7 @@ import { RefObject, useEffect, useRef, useState } from 'react'; import { NativeDetectorProps, useDetectorContext } from './NativeDetector'; -import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; +import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; export interface LogicDetectorProps { viewTag: number; viewRef: RefObject; @@ -16,9 +16,7 @@ export const LogicDetector = (props: NativeDetectorProps) => { const logicMethods = { onGestureHandlerStateChange: props.gesture.gestureEvents.onGestureHandlerStateChange, - onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent!, - onGestureHandlerAnimatedEvent: - props.gesture.gestureEvents.onGestureHandlerAnimatedEvent, + onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, }; @@ -42,5 +40,6 @@ export const LogicDetector = (props: NativeDetectorProps) => { }; }, [viewTag, props.gesture.tag, register, unregister]); + // fallback: still wrap if it's not a valid element return {props.children}; }; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index 4a61e74a45..59b368c0ce 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -11,9 +11,8 @@ import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; -import { tagMessage } from '../utils'; +import { invokeNullableMethod, tagMessage } from '../utils'; import { LogicDetectorProps } from './LogicDetector'; -import { runOnJS } from 'react-native-reanimated'; export interface NativeDetectorProps { children?: React.ReactNode; @@ -21,9 +20,9 @@ export interface NativeDetectorProps { } interface LogicMethods { - onGestureHandlerEvent: (e: unknown) => void; - onGestureHandlerStateChange: (e: unknown) => void; - onGestureHandlerTouchEvent?: (e: unknown) => void; + onGestureHandlerEvent?: (e: any) => void; + onGestureHandlerStateChange?: (e: any) => void; + onGestureHandlerTouchEvent?: (e: any) => void; } const AnimatedNativeDetector = Animated.createAnimatedComponent(HostGestureDetector); @@ -81,6 +80,7 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { ) ); } + return ( { - const logicMethod = logicMethods.current.get( - e.nativeEvent.childTag - )?.onGestureHandlerEvent; - if (logicMethod) { - runOnJS(logicMethod); - } + invokeNullableMethod( + logicMethods.current.get(e.nativeEvent.childTag) + ?.onGestureHandlerEvent, + e + ); }} onGestureHandlerLogicStateChange={(e) => { - const logicMethod = logicMethods.current.get( - e.nativeEvent.childTag - )?.onGestureHandlerStateChange; - if (logicMethod) { - runOnJS(logicMethod); - } + invokeNullableMethod( + logicMethods.current.get(e.nativeEvent.childTag) + ?.onGestureHandlerStateChange, + e + ); }} onGestureHandlerLogicTouchEvent={(e) => { - const logicMethod = logicMethods.current.get( - e.nativeEvent.childTag - )?.onGestureHandlerTouchEvent; - if (logicMethod) { - runOnJS(logicMethod); - } + invokeNullableMethod( + logicMethods.current.get(e.nativeEvent.childTag) + ?.onGestureHandlerTouchEvent, + e + ); }} moduleId={globalThis._RNGH_MODULE_ID} handlerTags={[gesture.tag]} diff --git a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts index 7f44239a0d..c842b59d1c 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts @@ -25,7 +25,7 @@ import { import { PointerType } from '../../PointerType'; import { GestureHandlerDelegate } from '../tools/GestureHandlerDelegate'; import { ActionType } from '../../ActionType'; -import { tagMessage } from '../../utils'; +import { invokeNullableMethod, tagMessage } from '../../utils'; import { GestureStateChangeEventWithData, GestureUpdateEventWithData, @@ -1042,56 +1042,3 @@ export default abstract class GestureHandler implements IGestureHandler { ); } } - -function invokeNullableMethod( - method: - | ((event: ResultEvent) => void) - | { __getHandler: () => (event: ResultEvent) => void } - | { __nodeConfig: { argMapping: unknown[] } }, - event: ResultEvent -): void { - if (!method) { - return; - } - - if (typeof method === 'function') { - method(event); - return; - } - - if ('__getHandler' in method && typeof method.__getHandler === 'function') { - const handler = method.__getHandler(); - invokeNullableMethod(handler, event); - return; - } - - if (!('__nodeConfig' in method)) { - return; - } - - const { argMapping }: { argMapping: unknown } = method.__nodeConfig; - if (!Array.isArray(argMapping)) { - return; - } - - for (const [index, [key, value]] of argMapping.entries()) { - if (!(key in event.nativeEvent)) { - continue; - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - const nativeValue = (event.nativeEvent as any)[key]; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (value?.setValue) { - // Reanimated API - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call - value.setValue(nativeValue); - } else { - // RN Animated API - method.__nodeConfig.argMapping[index] = [key, nativeValue]; - } - } - - return; -} From 66390abac494d8b43428ee3f9c512003c691cb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 4 Sep 2025 14:11:56 +0200 Subject: [PATCH 11/90] android --- .../gesturehandler/core/GestureHandler.kt | 8 +- .../react/RNGestureHandlerDetectorView.kt | 78 ++++++++++++++- .../RNGestureHandlerDetectorViewManager.kt | 4 + .../react/RNGestureHandlerEventDispatcher.kt | 25 +++++ .../react/RNGestureHandlerLogicEvent.kt | 76 ++++++++++++++ .../RNGestureHandlerLogicStateChangeEvent.kt | 99 +++++++++++++++++++ .../react/RNGestureHandlerLogicTouchEvent.kt | 77 +++++++++++++++ .../apple/RNGestureHandlerDetector.mm | 24 ++--- .../src/v3/LogicDetector.web.tsx | 1 - .../src/v3/NativeDetector.tsx | 4 +- yarn.lock | 26 ++++- 11 files changed, 397 insertions(+), 25 deletions(-) create mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicEvent.kt create mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicStateChangeEvent.kt create mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicTouchEvent.kt diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt index 63b3129a0c..2fb44c71e3 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt @@ -31,9 +31,13 @@ open class GestureHandler { var tag = 0 var view: View? = null private set + + // parent view is used for logicDetector + var parentView: RNGestureHandlerDetectorView? = null + val viewForEvents: RNGestureHandlerDetectorView get() { - assert(actionType == ACTION_TYPE_NATIVE_DETECTOR) { + assert(actionType == ACTION_TYPE_NATIVE_DETECTOR || actionType == ACTION_TYPE_LOGIC_DETECTOR) { "[react-native-gesture-handler] `viewForEvents` can only be used with NativeDetector." } @@ -996,6 +1000,8 @@ open class GestureHandler { const val ACTION_TYPE_JS_FUNCTION_OLD_API = 3 const val ACTION_TYPE_JS_FUNCTION_NEW_API = 4 const val ACTION_TYPE_NATIVE_DETECTOR = 5 + + const val ACTION_TYPE_LOGIC_DETECTOR = 6 const val POINTER_TYPE_TOUCH = 0 const val POINTER_TYPE_STYLUS = 1 const val POINTER_TYPE_MOUSE = 2 diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index 11d641b02d..52cfe7eca9 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -16,6 +16,14 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { private var nativeHandlers: MutableSet = mutableSetOf() private var attachedHandlers: MutableSet = mutableSetOf() private var moduleId: Int = -1 + private var logicChildren: HashMap = hashMapOf() + + class LogicChild { + var handlerTags: List? = null + var attachedHandlers: MutableSet = mutableSetOf() + } + + data class LogicProps(val handlerTags: List, val moduleId: Int, val viewTag: Int) fun setHandlerTags(handlerTags: ReadableArray?) { val newHandlers = handlerTags?.toArrayList()?.map { (it as Double).toInt() } ?: emptyList() @@ -26,17 +34,58 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { return } - attachHandlers(newHandlers) + attachHandlers(newHandlers, this.id, false, attachedHandlers) } fun setModuleId(id: Int) { assert(this.moduleId == -1) { "Tried to change moduleId of a native detector" } this.moduleId = id - this.attachHandlers(handlersToAttach ?: return) + this.attachHandlers(handlersToAttach ?: return, this.id, false, attachedHandlers) handlersToAttach = null } + fun setLogicChildren(newLogicChildren: ReadableArray?) { + val shouldKeepLogicChild = HashMap() + for (child in logicChildren) { + shouldKeepLogicChild[child.key] = false + } + val mappedChildren = mutableListOf() + + for (i in 0 until newLogicChildren!!.size()) { + val child = newLogicChildren.getMap(i) // Each element should be a ReadableMap + + val handlerTagsArray = child!!.getArray("handlerTags") + val handlerTags = mutableListOf() + for (j in 0 until handlerTagsArray!!.size()) { + handlerTags.add(handlerTagsArray.getInt(j)) + } + + val moduleId = child.getInt("moduleId") + val viewTag = child.getInt("viewTag") + + mappedChildren.add( + LogicProps( + handlerTags = handlerTags, + moduleId = moduleId, + viewTag = viewTag, + ), + ) + } + + for (child in logicChildren) { + shouldKeepLogicChild[child.key] = false + } + + for (child in mappedChildren) { + if (!logicChildren.containsKey(child.viewTag)) { + logicChildren.put(child.viewTag, LogicChild()) + } + shouldKeepLogicChild[child.viewTag] = true + attachHandlers(child.handlerTags, child.viewTag, true, logicChildren[child.viewTag]!!.attachedHandlers) + } + } + private fun shouldAttachGestureToChildView(tag: Int): Boolean { val registry = RNGestureHandlerModule.registries[moduleId] ?: throw Exception("Tried to access a non-existent registry") @@ -57,7 +106,12 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { super.removeViewAt(index) } - private fun attachHandlers(newHandlers: List) { + private fun attachHandlers( + newHandlers: List, + viewTag: Int, + isLogic: Boolean, + attachedHandlers: MutableSet, + ) { val registry = RNGestureHandlerModule.registries[moduleId] ?: throw Exception("Tried to access a non-existent registry") @@ -71,6 +125,12 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { changes[tag] = if (changes.containsKey(tag)) GestureHandlerMutation.Keep else GestureHandlerMutation.Attach } + val actionType = if (isLogic) { + GestureHandler.ACTION_TYPE_LOGIC_DETECTOR + } else { + GestureHandler.ACTION_TYPE_NATIVE_DETECTOR + } + for (entry in changes) { val tag = entry.key @@ -80,7 +140,10 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { // attach `NativeViewGestureHandlers` here and we have to do it in `addView` method. nativeHandlers.add(tag) } else { - registry.attachHandlerToView(tag, this.id, GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) + registry.attachHandlerToView(tag, viewTag, actionType) + if (isLogic) { + registry.getHandler(tag)?.parentView = this + } attachedHandlers.add(tag) } } else if (entry.value == GestureHandlerMutation.Detach) { @@ -132,6 +195,13 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { registry.detachHandler(tag) attachedHandlers.remove(tag) } + + for (child in logicChildren) { + for (tag in child.value.attachedHandlers) { + registry.detachHandler(tag) + } + child.value.attachedHandlers.clear() + } } companion object { diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt index 22092feebe..6f7cab14f5 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt @@ -37,6 +37,10 @@ class RNGestureHandlerDetectorViewManager : view.setModuleId(value) } + override fun setLogicChildren(view: RNGestureHandlerDetectorView?, value: ReadableArray?) { + view?.setLogicChildren(value) + } + override fun onDropViewInstance(view: RNGestureHandlerDetectorView) { view.onViewDrop() super.onDropViewInstance(view) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEventDispatcher.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEventDispatcher.kt index 4725dcb5ad..9b1a833be1 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEventDispatcher.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEventDispatcher.kt @@ -86,6 +86,15 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React handler.viewForEvents!!.dispatchEvent(event) } + GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { + val event = RNGestureHandlerEvent.obtain( + handler, + handler.actionType, + handlerFactory.createEventBuilder(handler), + ) + + handler.parentView?.dispatchEvent(event) + } } } @@ -142,6 +151,17 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React handler.viewForEvents!!.dispatchEvent(event) } + + GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { + val event = RNGestureHandlerLogicStateChangeEvent.obtain( + handler, + newState, + oldState, + handler.actionType, + handlerFactory.createEventBuilder(handler), + ) + handler.parentView?.dispatchEvent(event) + } } } @@ -177,6 +197,11 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React handler.viewForEvents!!.dispatchEvent(event) } + GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { + val event = RNGestureHandlerLogicTouchEvent.obtain(handler, handler.actionType) + + handler.parentView?.dispatchEvent(event) + } } } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicEvent.kt new file mode 100644 index 0000000000..d22f213b9a --- /dev/null +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicEvent.kt @@ -0,0 +1,76 @@ +// 1. RCTEventEmitter was deprecated in favor of RCTModernEventEmitter interface +// 2. Event#init() with only viewTag was deprecated in favor of two arg c-tor +// 3. Event#receiveEvent() with 3 args was deprecated in favor of 4 args version +// ref: https://github.com/facebook/react-native/commit/2fbbdbb2ce897e8da3f471b08b93f167d566db1d +@file:Suppress("DEPRECATION") + +package com.swmansion.gesturehandler.react + +import androidx.core.util.Pools +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.WritableMap +import com.facebook.react.uimanager.UIManagerHelper +import com.facebook.react.uimanager.events.Event +import com.swmansion.gesturehandler.core.GestureHandler +import com.swmansion.gesturehandler.react.eventbuilders.GestureHandlerEventDataBuilder + +class RNGestureHandlerLogicEvent private constructor() : Event() { + private var dataBuilder: GestureHandlerEventDataBuilder<*>? = null + private var coalescingKey: Short = 0 + private var actionType: Int = GestureHandler.ACTION_TYPE_LOGIC_DETECTOR + private var childTag: Int? = null + + private fun init(handler: T, actionType: Int, dataBuilder: GestureHandlerEventDataBuilder) { + val view = handler.parentView!! + + super.init(UIManagerHelper.getSurfaceId(view), view.id) + + this.dataBuilder = dataBuilder + coalescingKey = handler.eventCoalescingKey + this.actionType = actionType + + this.childTag = handler.view!!.id + } + + override fun onDispose() { + dataBuilder = null + EVENTS_POOL.release(this) + } + + override fun getEventName() = EVENT_NAME + + override fun canCoalesce() = true + + override fun getCoalescingKey() = coalescingKey + + override fun getEventData(): WritableMap = createNativeEventData(dataBuilder!!, childTag) + + companion object { + const val EVENT_NAME = "onGestureHandlerLogicEvent" + private const val TOUCH_EVENTS_POOL_SIZE = 7 // magic + private val EVENTS_POOL = Pools.SynchronizedPool(TOUCH_EVENTS_POOL_SIZE) + + fun obtain( + handler: T, + actionType: Int, + dataBuilder: GestureHandlerEventDataBuilder, + ): RNGestureHandlerLogicEvent = (EVENTS_POOL.acquire() ?: RNGestureHandlerLogicEvent()).apply { + init(handler, actionType, dataBuilder) + } + + fun createNativeEventData(dataBuilder: GestureHandlerEventDataBuilder<*>, childTag: Int?): WritableMap = + Arguments.createMap().apply { + putMap( + "handlerData", + Arguments.createMap().apply { + dataBuilder.buildEventData(this) + }, + ) + putInt("handlerTag", dataBuilder.handlerTag) + putInt("state", dataBuilder.state) + if (childTag != null) { + putInt("childTag", childTag) + } + } + } +} diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicStateChangeEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicStateChangeEvent.kt new file mode 100644 index 0000000000..df6fa8f605 --- /dev/null +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicStateChangeEvent.kt @@ -0,0 +1,99 @@ +// 1. RCTEventEmitter was deprecated in favor of RCTModernEventEmitter interface +// 2. Event#init() with only viewTag was deprecated in favor of two arg c-tor +// 3. Event#receiveEvent() with 3 args was deprecated in favor of 4 args version +// ref: https://github.com/facebook/react-native/commit/2fbbdbb2ce897e8da3f471b08b93f167d566db1d +@file:Suppress("DEPRECATION") + +package com.swmansion.gesturehandler.react + +import androidx.core.util.Pools +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.WritableMap +import com.facebook.react.uimanager.UIManagerHelper +import com.facebook.react.uimanager.events.Event +import com.swmansion.gesturehandler.core.GestureHandler +import com.swmansion.gesturehandler.react.eventbuilders.GestureHandlerEventDataBuilder + +class RNGestureHandlerLogicStateChangeEvent private constructor() : Event() { + private var dataBuilder: GestureHandlerEventDataBuilder<*>? = null + private var newState: Int = GestureHandler.STATE_UNDETERMINED + private var oldState: Int = GestureHandler.STATE_UNDETERMINED + private var actionType: Int = GestureHandler.ACTION_TYPE_LOGIC_DETECTOR + private var childTag: Int? = null + private fun init( + handler: T, + newState: Int, + oldState: Int, + actionType: Int, + dataBuilder: GestureHandlerEventDataBuilder, + ) { + val view = handler.parentView!! + + super.init(UIManagerHelper.getSurfaceId(view), view.id) + + this.dataBuilder = dataBuilder + this.newState = newState + this.oldState = oldState + this.actionType = actionType + + this.childTag = handler.view!!.id + } + + override fun onDispose() { + dataBuilder = null + newState = GestureHandler.STATE_UNDETERMINED + oldState = GestureHandler.STATE_UNDETERMINED + EVENTS_POOL.release(this) + } + + override fun getEventName() = EVENT_NAME + + // TODO: coalescing + override fun canCoalesce() = false + + // TODO: coalescing + override fun getCoalescingKey(): Short = 0 + + override fun getEventData(): WritableMap = createNativeEventData(dataBuilder!!, newState, oldState, childTag) + + companion object { + const val EVENT_NAME = "onGestureHandlerLogicStateChange" + private const val TOUCH_EVENTS_POOL_SIZE = 7 // magic + private val EVENTS_POOL = Pools.SynchronizedPool( + TOUCH_EVENTS_POOL_SIZE, + ) + + fun obtain( + handler: T, + newState: Int, + oldState: Int, + actionType: Int, + dataBuilder: GestureHandlerEventDataBuilder, + ): RNGestureHandlerLogicStateChangeEvent = ( + EVENTS_POOL.acquire() + ?: RNGestureHandlerLogicStateChangeEvent() + ).apply { + init(handler, newState, oldState, actionType, dataBuilder) + } + + fun createNativeEventData( + dataBuilder: GestureHandlerEventDataBuilder<*>, + newState: Int, + oldState: Int, + childTag: Int?, + ): WritableMap = Arguments.createMap().apply { + putMap( + "handlerData", + Arguments.createMap().apply { + dataBuilder.buildEventData(this) + }, + ) + putInt("handlerTag", dataBuilder.handlerTag) + putInt("state", newState) + putInt("oldState", oldState) + if (childTag != null) { + putInt("childTag", childTag) + } + } + } +} diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicTouchEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicTouchEvent.kt new file mode 100644 index 0000000000..f6d74f9e61 --- /dev/null +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicTouchEvent.kt @@ -0,0 +1,77 @@ +package com.swmansion.gesturehandler.react + +import androidx.core.util.Pools +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.WritableMap +import com.facebook.react.uimanager.UIManagerHelper +import com.facebook.react.uimanager.events.Event +import com.swmansion.gesturehandler.core.GestureHandler + +class RNGestureHandlerLogicTouchEvent private constructor() : Event() { + private var extraData: WritableMap? = null + private var coalescingKey: Short = 0 + private var actionType = GestureHandler.ACTION_TYPE_LOGIC_DETECTOR + + private var childTag: Int? = null + + private fun init(handler: T, actionType: Int) { + val view = handler.parentView!! + + super.init(UIManagerHelper.getSurfaceId(view), view.id) + + extraData = createEventData(handler, childTag) + coalescingKey = handler.eventCoalescingKey + this.actionType = actionType + + this.childTag = handler.view!!.id + } + + override fun onDispose() { + extraData = null + EVENTS_POOL.release(this) + } + + override fun getEventName() = NATIVE_EVENT_NAME + + override fun canCoalesce() = true + + override fun getCoalescingKey() = coalescingKey + override fun getEventData(): WritableMap? = extraData + + companion object { + const val NATIVE_EVENT_NAME = "onGestureHandlerTouchEvent" + private const val TOUCH_EVENTS_POOL_SIZE = 7 // magic + private val EVENTS_POOL = Pools.SynchronizedPool( + TOUCH_EVENTS_POOL_SIZE, + ) + + fun obtain(handler: T, actionType: Int): RNGestureHandlerLogicTouchEvent = + (EVENTS_POOL.acquire() ?: RNGestureHandlerLogicTouchEvent()).apply { + init(handler, actionType) + } + + fun createEventData(handler: T, childTag: Int?): WritableMap = Arguments.createMap().apply { + putInt("handlerTag", handler.tag) + putInt("state", handler.state) + putInt("numberOfTouches", handler.trackedPointersCount) + putInt("eventType", handler.touchEventType) + putInt("pointerType", handler.pointerType) + + handler.consumeChangedTouchesPayload()?.let { + putArray("changedTouches", it) + } + + handler.consumeAllTouchesPayload()?.let { + putArray("allTouches", it) + } + + if (handler.isAwaiting && handler.state == GestureHandler.STATE_ACTIVE) { + putInt("state", GestureHandler.STATE_BEGAN) + } + + if (childTag != null) { + putInt("childTag", childTag) + } + } + } +} diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index b25a24449c..fcdf64f593 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -25,9 +25,7 @@ typedef NS_ENUM(NSInteger, RNGestureHandlerMutation) { }; struct LogicChild { - RNGHUIView *view; std::vector handlerTags; - NSMutableSet *nativeHandlers; NSMutableSet *attachedHandlers; }; @@ -168,8 +166,6 @@ - (void)updatePropsInternal:(const std::vector &)handlerTags isLogic:(bool)isLogic viewTag:(const int)viewTag attachedHandlers:(NSMutableSet *)attachedHandlers - nativeHandlers:(NSMutableSet *)nativeHandlers - { RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") @@ -191,23 +187,25 @@ - (void)updatePropsInternal:(const std::vector &)handlerTags if ([self shouldAttachGestureToSubview:handlerTag]) { // It might happen that `attachHandlers` will be called before children are added into view hierarchy. In that // case we cannot attach `NativeViewGestureHandlers` here and we have to do it in `didAddSubview` method. - [nativeHandlers addObject:handlerTag]; + [_nativeHandlers addObject:handlerTag]; } else { if (isLogic) { + NSLog(@"attach logic child %@", handlerTag); + [handlerManager attachGestureHandler:handlerTag toViewWithTag:@(viewTag) withActionType:RNGestureHandlerActionTypeLogicDetector]; } else { - [handlerManager.registry attachHandlerWithTag:handlerTag - toView:self - withActionType:RNGestureHandlerActionTypeNativeDetector]; + // [handlerManager.registry attachHandlerWithTag:handlerTag + // toView:self + // withActionType:RNGestureHandlerActionTypeNativeDetector]; } [attachedHandlers addObject:handlerTag]; } } else if (handlerChange.second == RNGestureHandlerMutationDetach) { [handlerManager.registry detachHandlerWithTag:handlerTag]; [attachedHandlers removeObject:handlerTag]; - [nativeHandlers removeObject:handlerTag]; + [_nativeHandlers removeObject:handlerTag]; } } @@ -230,8 +228,7 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar oldHandlerTags:oldHandlerTags isLogic:false viewTag:-1 - attachedHandlers:_attachedHandlers - nativeHandlers:_nativeHandlers]; + attachedHandlers:_attachedHandlers]; [super updateProps:propsBase oldProps:oldPropsBase]; RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") @@ -245,10 +242,8 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar for (const RNGestureHandlerDetectorLogicChildrenStruct &child : newProps.logicChildren) { if (logicChildren.find(child.viewTag) == logicChildren.end()) { // Initialize the vector for a new logic child - logicChildren[child.viewTag].view = [handlerManager viewForReactTag:@(child.viewTag)]; logicChildren[child.viewTag].handlerTags = {}; logicChildren[child.viewTag].attachedHandlers = [NSMutableSet set]; - logicChildren[child.viewTag].nativeHandlers = [NSMutableSet set]; [[handlerManager registry] registerLogicChild:@(child.viewTag) toParent:@(self.tag)]; } shouldKeepLogicChild[child.viewTag] = true; @@ -256,8 +251,7 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar oldHandlerTags:logicChildren[child.viewTag].handlerTags isLogic:true viewTag:child.viewTag - attachedHandlers:logicChildren[child.viewTag].attachedHandlers - nativeHandlers:logicChildren[child.viewTag].nativeHandlers]; + attachedHandlers:logicChildren[child.viewTag].attachedHandlers]; } for (const auto &child : shouldKeepLogicChild) { diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx index c38a98faaf..b3a55cd3d4 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx @@ -40,6 +40,5 @@ export const LogicDetector = (props: NativeDetectorProps) => { }; }, [viewTag, props.gesture.tag, register, unregister]); - // fallback: still wrap if it's not a valid element return {props.children}; }; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index 59b368c0ce..d020d1ea44 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -39,9 +39,7 @@ const DetectorContext = createContext(null); export function NativeDetector({ gesture, children }: NativeDetectorProps) { const [logicChildren, setLogicChildren] = useState([]); - const logicMethods = useRef< - Map, LogicMethods> - >(new Map()); + const logicMethods = useRef>(new Map()); const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents ? AnimatedNativeDetector diff --git a/yarn.lock b/yarn.lock index f52c8f3956..07942cc063 100644 --- a/yarn.lock +++ b/yarn.lock @@ -220,7 +220,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.27.1, @babel/helper-plugin-utils@npm:^7.8.0": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.27.1, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": version: 7.27.1 resolution: "@babel/helper-plugin-utils@npm:7.27.1" checksum: 10c0/94cf22c81a0c11a09b197b41ab488d416ff62254ce13c57e62912c85700dc2e99e555225787a4099ff6bae7a1812d622c80fbaeda824b79baa10a6c5ac4cf69b @@ -411,6 +411,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-export-namespace-from@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.18.9" + "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/b90346bd3628ebd44138d0628a5aba1e6b11748893fb48e87008cac30f3bc7cd3161362e49433156737350318174164436357a66fbbfdbe952606b460bd8a0e4 + languageName: node + linkType: hard + "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2": version: 7.21.0-placeholder-for-preset-env.2 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2" @@ -497,6 +509,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-export-namespace-from@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-export-namespace-from@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/5100d658ba563829700cd8d001ddc09f4c0187b1a13de300d729c5b3e87503f75a6d6c99c1794182f7f1a9f546ee009df4f15a0ce36376e206ed0012fa7cdc24 + languageName: node + linkType: hard + "@babel/plugin-syntax-flow@npm:^7.12.1, @babel/plugin-syntax-flow@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-syntax-flow@npm:7.27.1" @@ -8373,6 +8396,7 @@ __metadata: resolution: "expo-example@workspace:apps/expo-example" dependencies: "@babel/core": "npm:^7.25.2" + "@babel/plugin-proposal-export-namespace-from": "npm:^7.18.9" "@expo/metro-runtime": "npm:~6.0.2" "@react-native-async-storage/async-storage": "npm:2.2.0" "@react-native-community/slider": "patch:@react-native-community/slider@npm%3A4.5.7#~/.yarn/patches/@react-native-community-slider-npm-4.5.7-658e0e58c9.patch" From 33797143bdc410079448f58172d31b33e5403cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 4 Sep 2025 14:25:50 +0200 Subject: [PATCH 12/90] yarn lock fix --- yarn.lock | 64 +------------------------------------------------------ 1 file changed, 1 insertion(+), 63 deletions(-) diff --git a/yarn.lock b/yarn.lock index b6728db843..bad6bbfcce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -220,7 +220,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.27.1, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.27.1, @babel/helper-plugin-utils@npm:^7.8.0": version: 7.27.1 resolution: "@babel/helper-plugin-utils@npm:7.27.1" checksum: 10c0/94cf22c81a0c11a09b197b41ab488d416ff62254ce13c57e62912c85700dc2e99e555225787a4099ff6bae7a1812d622c80fbaeda824b79baa10a6c5ac4cf69b @@ -411,18 +411,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-export-namespace-from@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.18.9" - "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/b90346bd3628ebd44138d0628a5aba1e6b11748893fb48e87008cac30f3bc7cd3161362e49433156737350318174164436357a66fbbfdbe952606b460bd8a0e4 - languageName: node - linkType: hard - "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2": version: 7.21.0-placeholder-for-preset-env.2 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2" @@ -509,17 +497,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-export-namespace-from@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-export-namespace-from@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.3" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/5100d658ba563829700cd8d001ddc09f4c0187b1a13de300d729c5b3e87503f75a6d6c99c1794182f7f1a9f546ee009df4f15a0ce36376e206ed0012fa7cdc24 - languageName: node - linkType: hard - "@babel/plugin-syntax-flow@npm:^7.12.1, @babel/plugin-syntax-flow@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-syntax-flow@npm:7.27.1" @@ -8396,7 +8373,6 @@ __metadata: resolution: "expo-example@workspace:apps/expo-example" dependencies: "@babel/core": "npm:^7.25.2" - "@babel/plugin-proposal-export-namespace-from": "npm:^7.18.9" "@expo/metro-runtime": "npm:~6.0.2" "@react-native-async-storage/async-storage": "npm:2.2.0" "@react-native-community/slider": "patch:@react-native-community/slider@npm%3A4.5.7#~/.yarn/patches/@react-native-community-slider-npm-4.5.7-658e0e58c9.patch" @@ -14361,21 +14337,6 @@ __metadata: languageName: node linkType: hard -"react-native-reanimated@npm:^4.1.0": - version: 4.1.0 - resolution: "react-native-reanimated@npm:4.1.0" - dependencies: - react-native-is-edge-to-edge: "npm:^1.2.1" - semver: "npm:7.7.2" - peerDependencies: - "@babel/core": ^7.0.0-0 - react: "*" - react-native: "*" - react-native-worklets: ">=0.5.0" - checksum: 10c0/45a072bfc3a56fc84bf21cb9853aec61622cf5e21be6f3f900988f8c53702cc6b2567380febe63c78166dac5aa50834b29d73dc1a7fdb4b9b950abdad1ef0cc4 - languageName: node - linkType: hard - "react-native-safe-area-context@npm:5.4.0": version: 5.4.0 resolution: "react-native-safe-area-context@npm:5.4.0" @@ -14493,29 +14454,6 @@ __metadata: languageName: node linkType: hard -"react-native-worklets@npm:^0.5.0": - version: 0.5.0 - resolution: "react-native-worklets@npm:0.5.0" - dependencies: - "@babel/plugin-transform-arrow-functions": "npm:^7.0.0-0" - "@babel/plugin-transform-class-properties": "npm:^7.0.0-0" - "@babel/plugin-transform-classes": "npm:^7.0.0-0" - "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.0.0-0" - "@babel/plugin-transform-optional-chaining": "npm:^7.0.0-0" - "@babel/plugin-transform-shorthand-properties": "npm:^7.0.0-0" - "@babel/plugin-transform-template-literals": "npm:^7.0.0-0" - "@babel/plugin-transform-unicode-regex": "npm:^7.0.0-0" - "@babel/preset-typescript": "npm:^7.16.7" - convert-source-map: "npm:^2.0.0" - semver: "npm:7.7.2" - peerDependencies: - "@babel/core": ^7.0.0-0 - react: "*" - react-native: "*" - checksum: 10c0/2e9b2b3b3c1549c5bc32d758aff538384bb851d80ed2a13c0c011dbe5857f62c2fcbd02dd2a7b2f38f8c357a6b1f2939f756ae24ea34a277e62a2a6c087e5ff8 - languageName: node - linkType: hard - "react-native@npm:*, react-native@npm:0.81.0": version: 0.81.0 resolution: "react-native@npm:0.81.0" From e9795cc461dc5891db748a076c7c5206d91c11ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 4 Sep 2025 14:51:20 +0200 Subject: [PATCH 13/90] removed circular dependency --- .../react-native-gesture-handler/src/index.ts | 2 +- .../src/v3/HostGestureDetector.web.tsx | 9 +++- .../src/v3/LogicDetector.tsx | 9 +--- .../src/v3/LogicDetector.web.tsx | 12 ++---- .../src/v3/NativeDetector.tsx | 14 +------ .../src/v3/hooks/useGesture.ts | 27 +----------- .../src/v3/types.ts | 42 +++++++++++++++++++ 7 files changed, 58 insertions(+), 57 deletions(-) diff --git a/packages/react-native-gesture-handler/src/index.ts b/packages/react-native-gesture-handler/src/index.ts index df4a25a510..b826d5b66a 100644 --- a/packages/react-native-gesture-handler/src/index.ts +++ b/packages/react-native-gesture-handler/src/index.ts @@ -162,7 +162,7 @@ export type { } from './components/DrawerLayout'; export { default as DrawerLayout } from './components/DrawerLayout'; -export type { NativeDetectorProps } from './v3/NativeDetector'; +export type { NativeDetectorProps } from './v3/types'; export { NativeDetector } from './v3/NativeDetector'; export { LogicDetector } from './v3/LogicDetector'; diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index e90eada4b4..2e2df16cef 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -4,7 +4,7 @@ import { ActionType } from '../ActionType'; import { PropsRef } from '../web/interfaces'; import { View } from 'react-native'; import { tagMessage } from '../utils'; -import { LogicDetectorProps } from './LogicDetector.web'; + export interface GestureHandlerDetectorProps extends PropsRef { handlerTags: number[]; moduleId: number; @@ -12,6 +12,13 @@ export interface GestureHandlerDetectorProps extends PropsRef { logicChildren?: Set; } +export interface LogicDetectorProps { + viewTag: number; + moduleId: number; + handlerTags: number[]; + viewRef: RefObject; +} + interface LogicChild { attachedHandlerTags: Set; attachedNativeHandlerTags: Set; diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 63f392833f..416ebcfe15 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -1,13 +1,8 @@ import { useEffect, useRef, useState } from 'react'; -import { NativeDetectorProps, useDetectorContext } from './NativeDetector'; +import { useDetectorContext } from './NativeDetector'; import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; import { findNodeHandle } from 'react-native'; - -export interface LogicDetectorProps { - viewTag: number; - moduleId: number; - handlerTags: number[]; -} +import { NativeDetectorProps } from './types'; export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx index b3a55cd3d4..c40be6f666 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx @@ -1,13 +1,7 @@ -import { RefObject, useEffect, useRef, useState } from 'react'; -import { NativeDetectorProps, useDetectorContext } from './NativeDetector'; - +import { useEffect, useRef, useState } from 'react'; +import { useDetectorContext } from './NativeDetector'; import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; -export interface LogicDetectorProps { - viewTag: number; - viewRef: RefObject; - moduleId: number; - handlerTags: number[]; -} +import { NativeDetectorProps } from './types'; export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index d020d1ea44..de8701935f 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -6,24 +6,12 @@ import React, { useRef, useState, } from 'react'; -import { NativeGesture } from './hooks/useGesture'; import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; - import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { invokeNullableMethod, tagMessage } from '../utils'; -import { LogicDetectorProps } from './LogicDetector'; - -export interface NativeDetectorProps { - children?: React.ReactNode; - gesture: NativeGesture; -} +import { LogicDetectorProps, LogicMethods, NativeDetectorProps } from './types'; -interface LogicMethods { - onGestureHandlerEvent?: (e: any) => void; - onGestureHandlerStateChange?: (e: any) => void; - onGestureHandlerTouchEvent?: (e: any) => void; -} const AnimatedNativeDetector = Animated.createAnimatedComponent(HostGestureDetector); diff --git a/packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts b/packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts index 35ec1fbe42..657464d6a5 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts @@ -7,33 +7,8 @@ import { SharedValue, } from '../../handlers/gestures/reanimatedWrapper'; import { tagMessage } from '../../utils'; -import { AnimatedEvent } from '../types'; import { hash, prepareConfig } from './utils'; - -type GestureType = - | 'TapGestureHandler' - | 'LongPressGestureHandler' - | 'PanGestureHandler' - | 'PinchGestureHandler' - | 'RotationGestureHandler' - | 'FlingGestureHandler' - | 'ForceTouchGestureHandler' - | 'ManualGestureHandler' - | 'NativeViewGestureHandler'; - -type GestureEvents = { - onGestureHandlerStateChange: (event: any) => void; - onGestureHandlerEvent: undefined | ((event: any) => void); - onGestureHandlerTouchEvent: (event: any) => void; - onGestureHandlerAnimatedEvent: undefined | AnimatedEvent; -}; - -export interface NativeGesture { - tag: number; - name: GestureType; - config: Record; - gestureEvents: GestureEvents; -} +import { GestureType, NativeGesture } from '../types'; function hasWorkletEventHandlers(config: Record) { return Object.values(config).some( diff --git a/packages/react-native-gesture-handler/src/v3/types.ts b/packages/react-native-gesture-handler/src/v3/types.ts index 3f04f28a9f..0c9d5c0ceb 100644 --- a/packages/react-native-gesture-handler/src/v3/types.ts +++ b/packages/react-native-gesture-handler/src/v3/types.ts @@ -6,6 +6,31 @@ import { } from '../handlers/gestureHandlerCommon'; import { HandlerCallbacks } from '../handlers/gestures/gesture'; +export type GestureType = + | 'TapGestureHandler' + | 'LongPressGestureHandler' + | 'PanGestureHandler' + | 'PinchGestureHandler' + | 'RotationGestureHandler' + | 'FlingGestureHandler' + | 'ForceTouchGestureHandler' + | 'ManualGestureHandler' + | 'NativeViewGestureHandler'; + +export type GestureEvents = { + onGestureHandlerStateChange: (event: any) => void; + onGestureHandlerEvent: undefined | ((event: any) => void); + onGestureHandlerTouchEvent: (event: any) => void; + onGestureHandlerAnimatedEvent: undefined | AnimatedEvent; +}; + +export interface NativeGesture { + tag: number; + name: GestureType; + config: Record; + gestureEvents: GestureEvents; +} + export type GestureUpdateEventWithData = GestureEventPayload & { handlerData: T; }; @@ -48,3 +73,20 @@ export type CallbackHandlers = Omit< export type AnimatedEvent = ((...args: any[]) => void) & { _argMapping?: unknown; }; + +export interface LogicDetectorProps { + viewTag: number; + moduleId: number; + handlerTags: number[]; +} + +export interface NativeDetectorProps { + children?: React.ReactNode; + gesture: NativeGesture; +} + +export interface LogicMethods { + onGestureHandlerEvent?: (e: any) => void; + onGestureHandlerStateChange?: (e: any) => void; + onGestureHandlerTouchEvent?: (e: any) => void; +} From 7fb8ea94121c7086051e834aeae2173935dcf409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 5 Sep 2025 11:39:48 +0200 Subject: [PATCH 14/90] simplify ios --- .../apple/RNGestureHandler.h | 3 +++ .../apple/RNGestureHandler.mm | 11 +++++++++++ .../apple/RNGestureHandlerDetector.mm | 2 +- .../apple/RNGestureHandlerManager.mm | 2 +- .../apple/RNGestureHandlerRegistry.h | 2 -- .../apple/RNGestureHandlerRegistry.m | 12 ------------ yarn.lock | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.h b/packages/react-native-gesture-handler/apple/RNGestureHandler.h index 4de4ca6a51..c3f512cdeb 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandler.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandler.h @@ -103,6 +103,9 @@ - (nonnull RNGHUIView *)findViewForEvents; - (BOOL)wantsToAttachDirectlyToView; +- (void)setParentTag:(nonnull NSNumber *)parentTag; +- (nonnull NSNumber *)getParentTag; + #if !TARGET_OS_OSX - (BOOL)isUIScrollViewPanGestureRecognizer:(nonnull UIGestureRecognizer *)gestureRecognizer; #else diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.mm b/packages/react-native-gesture-handler/apple/RNGestureHandler.mm index c1c94bb7da..556ad0450b 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandler.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandler.mm @@ -76,6 +76,7 @@ @implementation RNGestureHandler { NSArray *_simultaneousHandlers; RNGHHitSlop _hitSlop; uint16_t _eventCoalescingKey; + NSNumber *_parentTag; } - (instancetype)initWithTag:(NSNumber *)tag @@ -674,4 +675,14 @@ - (BOOL)wantsToAttachDirectlyToView return NO; } +- (void)setParentTag:(NSNumber *)parentTag +{ + _parentTag = parentTag; +} + +- (NSNumber *)getParentTag +{ + return _parentTag; +} + @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index fcdf64f593..837ef26c28 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -244,7 +244,7 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar // Initialize the vector for a new logic child logicChildren[child.viewTag].handlerTags = {}; logicChildren[child.viewTag].attachedHandlers = [NSMutableSet set]; - [[handlerManager registry] registerLogicChild:@(child.viewTag) toParent:@(self.tag)]; + [[[handlerManager registry] handlerWithTag:@(child.viewTag)] setParentTag:@(self.tag)]; } shouldKeepLogicChild[child.viewTag] = true; [self updatePropsInternal:child.handlerTags diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index c9430f9578..8751d23462 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -318,7 +318,7 @@ - (void)sendEvent:(RNGestureHandlerStateChange *)event break; } case RNGestureHandlerActionTypeLogicDetector: { - NSNumber *parentTag = [_registry getLogicParent:@(detectorView.tag)]; + NSNumber *parentTag = [[_registry handlerWithTag:event.handlerTag] getParentTag]; RNGHUIView *parentView = [self viewForReactTag:parentTag]; if ([event isKindOfClass:[RNGestureHandlerEvent class]]) { // TODO: handle forAnimated diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h index 4f066a9066..92d1966c30 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h @@ -18,6 +18,4 @@ - (void)detachHandlerWithTag:(nonnull NSNumber *)handlerTag; - (void)dropHandlerWithTag:(nonnull NSNumber *)handlerTag; - (void)dropAllHandlers; -- (nullable NSNumber *)getLogicParent:(nonnull NSNumber *)child; -- (void)registerLogicChild:(nonnull NSNumber *)child toParent:(nonnull NSNumber *)parent; @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m index 357c0393e8..856c568e53 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m @@ -12,14 +12,12 @@ @implementation RNGestureHandlerRegistry { NSMutableDictionary *_handlers; - NSMutableDictionary *_logicChildMap; } - (instancetype)init { if ((self = [super init])) { _handlers = [NSMutableDictionary new]; - _logicChildMap = [NSMutableDictionary new]; } return self; } @@ -34,16 +32,6 @@ - (void)registerGestureHandler:(RNGestureHandler *)gestureHandler _handlers[gestureHandler.tag] = gestureHandler; } -- (void)registerLogicChild:(NSNumber *)child toParent:(NSNumber *)parent -{ - _logicChildMap[child] = parent; -} - -- (NSNumber *)getLogicParent:(NSNumber *)child -{ - return _logicChildMap[child]; -} - - (void)attachHandlerWithTag:(NSNumber *)handlerTag toView:(RNGHUIView *)view withActionType:(RNGestureHandlerActionType)actionType diff --git a/yarn.lock b/yarn.lock index bad6bbfcce..658a983f5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6527,7 +6527,7 @@ __metadata: jest: "npm:^29.6.3" prettier: "npm:3.3.3" react-native-gesture-handler: "workspace:*" - react-native-reanimated: "npm:^3.18.0" + react-native-reanimated: "npm:^3.19.1" react-native-safe-area-context: "npm:^5.4.0" react-native-screens: "npm:^4.10.0" react-native-svg: "npm:15.11.2" @@ -14298,7 +14298,7 @@ __metadata: languageName: node linkType: hard -"react-native-reanimated@npm:^3.18.0": +"react-native-reanimated@npm:^3.18.0, react-native-reanimated@npm:^3.19.1": version: 3.19.1 resolution: "react-native-reanimated@npm:3.19.1" dependencies: From 5c241514f539546cfac689644822b2384b833c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 8 Sep 2025 08:02:57 +0200 Subject: [PATCH 15/90] removed logic events web/android --- .../react/RNGestureHandlerEventDispatcher.kt | 4 +- .../react/RNGestureHandlerLogicEvent.kt | 76 -------------- .../RNGestureHandlerLogicStateChangeEvent.kt | 99 ------------------- .../react/RNGestureHandlerLogicTouchEvent.kt | 77 --------------- ...RNGestureHandlerDetectorNativeComponent.ts | 44 +-------- .../src/v3/HostGestureDetector.web.tsx | 12 +-- .../src/v3/LogicDetector.tsx | 15 ++- .../src/v3/LogicDetector.web.tsx | 15 ++- .../src/v3/NativeDetector.tsx | 30 +++--- 9 files changed, 53 insertions(+), 319 deletions(-) delete mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicEvent.kt delete mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicStateChangeEvent.kt delete mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicTouchEvent.kt diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEventDispatcher.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEventDispatcher.kt index 9b1a833be1..387885440c 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEventDispatcher.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEventDispatcher.kt @@ -153,7 +153,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React } GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { - val event = RNGestureHandlerLogicStateChangeEvent.obtain( + val event = RNGestureHandlerStateChangeEvent.obtain( handler, newState, oldState, @@ -198,7 +198,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React handler.viewForEvents!!.dispatchEvent(event) } GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { - val event = RNGestureHandlerLogicTouchEvent.obtain(handler, handler.actionType) + val event = RNGestureHandlerTouchEvent.obtain(handler, handler.actionType) handler.parentView?.dispatchEvent(event) } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicEvent.kt deleted file mode 100644 index d22f213b9a..0000000000 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicEvent.kt +++ /dev/null @@ -1,76 +0,0 @@ -// 1. RCTEventEmitter was deprecated in favor of RCTModernEventEmitter interface -// 2. Event#init() with only viewTag was deprecated in favor of two arg c-tor -// 3. Event#receiveEvent() with 3 args was deprecated in favor of 4 args version -// ref: https://github.com/facebook/react-native/commit/2fbbdbb2ce897e8da3f471b08b93f167d566db1d -@file:Suppress("DEPRECATION") - -package com.swmansion.gesturehandler.react - -import androidx.core.util.Pools -import com.facebook.react.bridge.Arguments -import com.facebook.react.bridge.WritableMap -import com.facebook.react.uimanager.UIManagerHelper -import com.facebook.react.uimanager.events.Event -import com.swmansion.gesturehandler.core.GestureHandler -import com.swmansion.gesturehandler.react.eventbuilders.GestureHandlerEventDataBuilder - -class RNGestureHandlerLogicEvent private constructor() : Event() { - private var dataBuilder: GestureHandlerEventDataBuilder<*>? = null - private var coalescingKey: Short = 0 - private var actionType: Int = GestureHandler.ACTION_TYPE_LOGIC_DETECTOR - private var childTag: Int? = null - - private fun init(handler: T, actionType: Int, dataBuilder: GestureHandlerEventDataBuilder) { - val view = handler.parentView!! - - super.init(UIManagerHelper.getSurfaceId(view), view.id) - - this.dataBuilder = dataBuilder - coalescingKey = handler.eventCoalescingKey - this.actionType = actionType - - this.childTag = handler.view!!.id - } - - override fun onDispose() { - dataBuilder = null - EVENTS_POOL.release(this) - } - - override fun getEventName() = EVENT_NAME - - override fun canCoalesce() = true - - override fun getCoalescingKey() = coalescingKey - - override fun getEventData(): WritableMap = createNativeEventData(dataBuilder!!, childTag) - - companion object { - const val EVENT_NAME = "onGestureHandlerLogicEvent" - private const val TOUCH_EVENTS_POOL_SIZE = 7 // magic - private val EVENTS_POOL = Pools.SynchronizedPool(TOUCH_EVENTS_POOL_SIZE) - - fun obtain( - handler: T, - actionType: Int, - dataBuilder: GestureHandlerEventDataBuilder, - ): RNGestureHandlerLogicEvent = (EVENTS_POOL.acquire() ?: RNGestureHandlerLogicEvent()).apply { - init(handler, actionType, dataBuilder) - } - - fun createNativeEventData(dataBuilder: GestureHandlerEventDataBuilder<*>, childTag: Int?): WritableMap = - Arguments.createMap().apply { - putMap( - "handlerData", - Arguments.createMap().apply { - dataBuilder.buildEventData(this) - }, - ) - putInt("handlerTag", dataBuilder.handlerTag) - putInt("state", dataBuilder.state) - if (childTag != null) { - putInt("childTag", childTag) - } - } - } -} diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicStateChangeEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicStateChangeEvent.kt deleted file mode 100644 index df6fa8f605..0000000000 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicStateChangeEvent.kt +++ /dev/null @@ -1,99 +0,0 @@ -// 1. RCTEventEmitter was deprecated in favor of RCTModernEventEmitter interface -// 2. Event#init() with only viewTag was deprecated in favor of two arg c-tor -// 3. Event#receiveEvent() with 3 args was deprecated in favor of 4 args version -// ref: https://github.com/facebook/react-native/commit/2fbbdbb2ce897e8da3f471b08b93f167d566db1d -@file:Suppress("DEPRECATION") - -package com.swmansion.gesturehandler.react - -import androidx.core.util.Pools -import com.facebook.react.bridge.Arguments -import com.facebook.react.bridge.WritableMap -import com.facebook.react.uimanager.UIManagerHelper -import com.facebook.react.uimanager.events.Event -import com.swmansion.gesturehandler.core.GestureHandler -import com.swmansion.gesturehandler.react.eventbuilders.GestureHandlerEventDataBuilder - -class RNGestureHandlerLogicStateChangeEvent private constructor() : Event() { - private var dataBuilder: GestureHandlerEventDataBuilder<*>? = null - private var newState: Int = GestureHandler.STATE_UNDETERMINED - private var oldState: Int = GestureHandler.STATE_UNDETERMINED - private var actionType: Int = GestureHandler.ACTION_TYPE_LOGIC_DETECTOR - private var childTag: Int? = null - private fun init( - handler: T, - newState: Int, - oldState: Int, - actionType: Int, - dataBuilder: GestureHandlerEventDataBuilder, - ) { - val view = handler.parentView!! - - super.init(UIManagerHelper.getSurfaceId(view), view.id) - - this.dataBuilder = dataBuilder - this.newState = newState - this.oldState = oldState - this.actionType = actionType - - this.childTag = handler.view!!.id - } - - override fun onDispose() { - dataBuilder = null - newState = GestureHandler.STATE_UNDETERMINED - oldState = GestureHandler.STATE_UNDETERMINED - EVENTS_POOL.release(this) - } - - override fun getEventName() = EVENT_NAME - - // TODO: coalescing - override fun canCoalesce() = false - - // TODO: coalescing - override fun getCoalescingKey(): Short = 0 - - override fun getEventData(): WritableMap = createNativeEventData(dataBuilder!!, newState, oldState, childTag) - - companion object { - const val EVENT_NAME = "onGestureHandlerLogicStateChange" - private const val TOUCH_EVENTS_POOL_SIZE = 7 // magic - private val EVENTS_POOL = Pools.SynchronizedPool( - TOUCH_EVENTS_POOL_SIZE, - ) - - fun obtain( - handler: T, - newState: Int, - oldState: Int, - actionType: Int, - dataBuilder: GestureHandlerEventDataBuilder, - ): RNGestureHandlerLogicStateChangeEvent = ( - EVENTS_POOL.acquire() - ?: RNGestureHandlerLogicStateChangeEvent() - ).apply { - init(handler, newState, oldState, actionType, dataBuilder) - } - - fun createNativeEventData( - dataBuilder: GestureHandlerEventDataBuilder<*>, - newState: Int, - oldState: Int, - childTag: Int?, - ): WritableMap = Arguments.createMap().apply { - putMap( - "handlerData", - Arguments.createMap().apply { - dataBuilder.buildEventData(this) - }, - ) - putInt("handlerTag", dataBuilder.handlerTag) - putInt("state", newState) - putInt("oldState", oldState) - if (childTag != null) { - putInt("childTag", childTag) - } - } - } -} diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicTouchEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicTouchEvent.kt deleted file mode 100644 index f6d74f9e61..0000000000 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerLogicTouchEvent.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.swmansion.gesturehandler.react - -import androidx.core.util.Pools -import com.facebook.react.bridge.Arguments -import com.facebook.react.bridge.WritableMap -import com.facebook.react.uimanager.UIManagerHelper -import com.facebook.react.uimanager.events.Event -import com.swmansion.gesturehandler.core.GestureHandler - -class RNGestureHandlerLogicTouchEvent private constructor() : Event() { - private var extraData: WritableMap? = null - private var coalescingKey: Short = 0 - private var actionType = GestureHandler.ACTION_TYPE_LOGIC_DETECTOR - - private var childTag: Int? = null - - private fun init(handler: T, actionType: Int) { - val view = handler.parentView!! - - super.init(UIManagerHelper.getSurfaceId(view), view.id) - - extraData = createEventData(handler, childTag) - coalescingKey = handler.eventCoalescingKey - this.actionType = actionType - - this.childTag = handler.view!!.id - } - - override fun onDispose() { - extraData = null - EVENTS_POOL.release(this) - } - - override fun getEventName() = NATIVE_EVENT_NAME - - override fun canCoalesce() = true - - override fun getCoalescingKey() = coalescingKey - override fun getEventData(): WritableMap? = extraData - - companion object { - const val NATIVE_EVENT_NAME = "onGestureHandlerTouchEvent" - private const val TOUCH_EVENTS_POOL_SIZE = 7 // magic - private val EVENTS_POOL = Pools.SynchronizedPool( - TOUCH_EVENTS_POOL_SIZE, - ) - - fun obtain(handler: T, actionType: Int): RNGestureHandlerLogicTouchEvent = - (EVENTS_POOL.acquire() ?: RNGestureHandlerLogicTouchEvent()).apply { - init(handler, actionType) - } - - fun createEventData(handler: T, childTag: Int?): WritableMap = Arguments.createMap().apply { - putInt("handlerTag", handler.tag) - putInt("state", handler.state) - putInt("numberOfTouches", handler.trackedPointersCount) - putInt("eventType", handler.touchEventType) - putInt("pointerType", handler.pointerType) - - handler.consumeChangedTouchesPayload()?.let { - putArray("changedTouches", it) - } - - handler.consumeAllTouchesPayload()?.let { - putArray("allTouches", it) - } - - if (handler.isAwaiting && handler.state == GestureHandler.STATE_ACTIVE) { - putInt("state", GestureHandler.STATE_BEGAN) - } - - if (childTag != null) { - putInt("childTag", childTag) - } - } - } -} diff --git a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts index 759dafff90..f6be1d048e 100644 --- a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts +++ b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts @@ -13,13 +13,6 @@ type GestureHandlerEvent = Readonly<{ handlerData: UnsafeMixed; }>; -type GestureHandlerLogicEvent = Readonly<{ - handlerTag: Int32; - state: Int32; - handlerData: UnsafeMixed; - childTag: Int32; -}>; - type GestureHandlerStateChangeEvent = Readonly<{ handlerTag: Int32; state: Int32; @@ -27,14 +20,6 @@ type GestureHandlerStateChangeEvent = Readonly<{ handlerData: UnsafeMixed; }>; -type GestureHandlerLogicStateChangeEvent = Readonly<{ - handlerTag: Int32; - state: Int32; - oldState: Int32; - handlerData: UnsafeMixed; - childTag: Int32; -}>; - type GestureHandlerTouchEvent = Readonly<{ handlerTag: Int32; numberOfTouches: Int32; @@ -57,29 +42,6 @@ type GestureHandlerTouchEvent = Readonly<{ pointerType: Int32; }>; -type GestureHandlerLogicTouchEvent = Readonly<{ - handlerTag: Int32; - numberOfTouches: Int32; - state: Int32; - eventType: Int32; - allTouches: { - id: Int32; - x: Double; - y: Double; - absoluteX: Double; - absoluteY: Double; - }[]; - changedTouches: { - id: Int32; - x: Double; - y: Double; - absoluteX: Double; - absoluteY: Double; - }[]; - pointerType: Int32; - childTag: Int32; -}>; - export interface LogicProps { handlerTags: Int32[]; moduleId: Int32; @@ -92,9 +54,9 @@ export interface NativeProps extends ViewProps { onGestureHandlerStateChange?: DirectEventHandler; onGestureHandlerTouchEvent?: DirectEventHandler; - onGestureHandlerLogicEvent?: DirectEventHandler; - onGestureHandlerLogicStateChange?: DirectEventHandler; - onGestureHandlerLogicTouchEvent?: DirectEventHandler; + onGestureHandlerLogicEvent?: DirectEventHandler; + onGestureHandlerLogicStateChange?: DirectEventHandler; + onGestureHandlerLogicTouchEvent?: DirectEventHandler; handlerTags: Int32[]; moduleId: Int32; diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index 2e2df16cef..cdb7c5fde0 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -52,7 +52,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { currentHandlerTags: Set, attachedHandlerTags: Set, attachedNativeHandlerTags: Set, - childTag?: number + isLogic: boolean ) => { const oldHandlerTags = attachedHandlerTags.difference(currentHandlerTags); const newHandlerTags = currentHandlerTags.difference(attachedHandlerTags); @@ -79,9 +79,8 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { RNGestureHandlerModule.attachGestureHandler( tag, viewRef.current, - childTag ? ActionType.LogicDetector : ActionType.NATIVE_DETECTOR, - propsRef, - childTag + isLogic ? ActionType.LogicDetector : ActionType.NATIVE_DETECTOR, + propsRef ); } attachedHandlerTags.add(tag); @@ -108,7 +107,8 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { propsRef, new Set(handlerTags), attachedHandlerTags.current, - attachedNativeHandlerTags.current + attachedNativeHandlerTags.current, + false ); }, [handlerTags, children]); @@ -140,7 +140,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { new Set(child.handlerTags), attachedHandlerTags, attachedNativeHandlerTags, - child.viewTag + true ); } }); diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 416ebcfe15..8706c7eefb 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -8,17 +8,28 @@ export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); const viewRef = useRef(null); const [viewTag, setViewTag] = useState(-1); - const logicMethods = { + const logicMethods = useRef({ onGestureHandlerStateChange: props.gesture.gestureEvents.onGestureHandlerStateChange, onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, - }; + }); + useEffect(() => { setViewTag(findNodeHandle(viewRef.current)!); }, []); + useEffect(() => { + logicMethods.current = { + onGestureHandlerStateChange: + props.gesture.gestureEvents.onGestureHandlerStateChange, + onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, + onGestureHandlerTouchEvent: + props.gesture.gestureEvents.onGestureHandlerTouchEvent, + }; + }, [props.gesture.gestureEvents]); + useEffect(() => { const logicProps = { viewTag: viewTag, diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx index c40be6f666..4c15a7bedd 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx @@ -7,18 +7,29 @@ export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); const viewRef = useRef(null); const [viewTag, setViewTag] = useState(-1); - const logicMethods = { + const logicMethods = useRef({ onGestureHandlerStateChange: props.gesture.gestureEvents.onGestureHandlerStateChange, onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, - }; + }); + useEffect(() => { // TODO: set tags from parent setViewTag(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)); }, []); + useEffect(() => { + logicMethods.current = { + onGestureHandlerStateChange: + props.gesture.gestureEvents.onGestureHandlerStateChange, + onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, + onGestureHandlerTouchEvent: + props.gesture.gestureEvents.onGestureHandlerTouchEvent, + }; + }, [props.gesture.gestureEvents]); + useEffect(() => { const logicProps = { viewRef: viewRef, diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index de8701935f..e938c41d3d 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -19,15 +19,18 @@ const ReanimatedNativeDetector = Reanimated?.default.createAnimatedComponent(HostGestureDetector); type DetectorContextType = { - register: (child: LogicDetectorProps, methods: LogicMethods) => void; - unregister: (child: number | RefObject) => void; + register: ( + child: LogicDetectorProps, + methods: RefObject + ) => void; + unregister: (child: number) => void; }; const DetectorContext = createContext(null); export function NativeDetector({ gesture, children }: NativeDetectorProps) { const [logicChildren, setLogicChildren] = useState([]); - const logicMethods = useRef>(new Map()); + const logicMethods = useRef>>(new Map()); const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents ? AnimatedNativeDetector @@ -37,7 +40,7 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { : HostGestureDetector; const register = useCallback( - (child: LogicDetectorProps, methods: LogicMethods) => { + (child: LogicDetectorProps, methods: RefObject) => { if (child.viewTag !== -1) { setLogicChildren((prev) => { if (prev.some((c) => c.viewTag === child.viewTag)) { @@ -46,17 +49,16 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { return [...prev, child]; }); } - logicMethods.current.set(child.viewTag, methods); + child.handlerTags.forEach((tag) => { + logicMethods.current.set(tag, methods); + }); }, [] ); - const unregister = useCallback( - (childTag: number | RefObject) => { - setLogicChildren((prev) => prev.filter((c) => c.viewTag !== childTag)); - }, - [] - ); + const unregister = useCallback((childTag: number) => { + setLogicChildren((prev) => prev.filter((c) => c.viewTag !== childTag)); + }, []); // It might happen only with ReanimatedNativeDetector if (!NativeDetectorComponent) { @@ -82,21 +84,21 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { } onGestureHandlerLogicEvent={(e) => { invokeNullableMethod( - logicMethods.current.get(e.nativeEvent.childTag) + logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onGestureHandlerEvent, e ); }} onGestureHandlerLogicStateChange={(e) => { invokeNullableMethod( - logicMethods.current.get(e.nativeEvent.childTag) + logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onGestureHandlerStateChange, e ); }} onGestureHandlerLogicTouchEvent={(e) => { invokeNullableMethod( - logicMethods.current.get(e.nativeEvent.childTag) + logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onGestureHandlerTouchEvent, e ); From 593c44f2b54e1a68613a0cb9e1fa750213be9878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 8 Sep 2025 10:14:22 +0200 Subject: [PATCH 16/90] android and web refactor --- .../react/RNGestureHandlerDetectorView.kt | 39 ++++---- .../RNGestureHandlerDetectorViewManager.kt | 4 +- .../src/ActionType.ts | 2 +- .../src/v3/HostGestureDetector.web.tsx | 95 ++++++------------- .../src/web/handlers/GestureHandler.ts | 12 +-- 5 files changed, 59 insertions(+), 93 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index 52cfe7eca9..1bc17e329a 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -16,12 +16,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { private var nativeHandlers: MutableSet = mutableSetOf() private var attachedHandlers: MutableSet = mutableSetOf() private var moduleId: Int = -1 - private var logicChildren: HashMap = hashMapOf() - - class LogicChild { - var handlerTags: List? = null - var attachedHandlers: MutableSet = mutableSetOf() - } + private var logicChildren: HashMap> = hashMapOf() data class LogicProps(val handlerTags: List, val moduleId: Int, val viewTag: Int) @@ -46,10 +41,12 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } fun setLogicChildren(newLogicChildren: ReadableArray?) { - val shouldKeepLogicChild = HashMap() + val logicChildrenToDelete = HashSet() + for (child in logicChildren) { - shouldKeepLogicChild[child.key] = false + logicChildrenToDelete.add(child.key) } + val mappedChildren = mutableListOf() for (i in 0 until newLogicChildren!!.size()) { @@ -73,16 +70,24 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { ) } - for (child in logicChildren) { - shouldKeepLogicChild[child.key] = false - } - for (child in mappedChildren) { if (!logicChildren.containsKey(child.viewTag)) { - logicChildren.put(child.viewTag, LogicChild()) + logicChildren.put(child.viewTag, mutableSetOf()) } - shouldKeepLogicChild[child.viewTag] = true - attachHandlers(child.handlerTags, child.viewTag, true, logicChildren[child.viewTag]!!.attachedHandlers) + logicChildrenToDelete.remove(child.viewTag) + attachHandlers( + child.handlerTags, + child.viewTag, + true, + logicChildren[child.viewTag]!!, + ) + } + + for (childTag in logicChildrenToDelete) { + val registry = RNGestureHandlerModule.registries[moduleId] + ?: throw Exception("Tried to access a non-existent registry") + registry.detachHandler(childTag) + logicChildren.remove(childTag) } } @@ -197,10 +202,10 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } for (child in logicChildren) { - for (tag in child.value.attachedHandlers) { + for (tag in child.value) { registry.detachHandler(tag) } - child.value.attachedHandlers.clear() + child.value.clear() } } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt index 6f7cab14f5..c8f4cb82d1 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt @@ -37,8 +37,8 @@ class RNGestureHandlerDetectorViewManager : view.setModuleId(value) } - override fun setLogicChildren(view: RNGestureHandlerDetectorView?, value: ReadableArray?) { - view?.setLogicChildren(value) + override fun setLogicChildren(view: RNGestureHandlerDetectorView, value: ReadableArray?) { + view.setLogicChildren(value) } override fun onDropViewInstance(view: RNGestureHandlerDetectorView) { diff --git a/packages/react-native-gesture-handler/src/ActionType.ts b/packages/react-native-gesture-handler/src/ActionType.ts index 06135378eb..81f2a3563d 100644 --- a/packages/react-native-gesture-handler/src/ActionType.ts +++ b/packages/react-native-gesture-handler/src/ActionType.ts @@ -4,7 +4,7 @@ export const ActionType = { JS_FUNCTION_OLD_API: 3, JS_FUNCTION_NEW_API: 4, NATIVE_DETECTOR: 5, - LogicDetector: 6, + LOGIC_DETECTOR: 6, } as const; // eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; it can be used as a type and as a value diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index cdb7c5fde0..e9caa5bef5 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -19,11 +19,6 @@ export interface LogicDetectorProps { viewRef: RefObject; } -interface LogicChild { - attachedHandlerTags: Set; - attachedNativeHandlerTags: Set; -} - const HostGestureDetector = (props: GestureHandlerDetectorProps) => { const { handlerTags, children } = props; @@ -32,16 +27,15 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { const attachedHandlerTags = useRef>(new Set()); const attachedNativeHandlerTags = useRef>(new Set()); - const logicChildren = useRef>(new Map()); + const logicChildren = useRef>>(new Map()); const detachHandlers = ( oldHandlerTags: Set, - attachedHandlerTags: Set, - attachedNativeHandlerTags: Set + attachedHandlerTags: Set ) => { oldHandlerTags.forEach((tag) => { RNGestureHandlerModule.detachGestureHandler(tag); - attachedNativeHandlerTags.delete(tag); + attachedNativeHandlerTags.current.delete(tag); attachedHandlerTags.delete(tag); }); }; @@ -51,16 +45,11 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { propsRef: RefObject, currentHandlerTags: Set, attachedHandlerTags: Set, - attachedNativeHandlerTags: Set, isLogic: boolean ) => { const oldHandlerTags = attachedHandlerTags.difference(currentHandlerTags); const newHandlerTags = currentHandlerTags.difference(attachedHandlerTags); - detachHandlers( - oldHandlerTags, - attachedHandlerTags, - attachedNativeHandlerTags - ); + detachHandlers(oldHandlerTags, attachedHandlerTags); newHandlerTags.forEach((tag) => { if ( @@ -74,12 +63,12 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { ActionType.NATIVE_DETECTOR, propsRef ); - attachedNativeHandlerTags.add(tag); + attachedNativeHandlerTags.current.add(tag); } else { RNGestureHandlerModule.attachGestureHandler( tag, viewRef.current, - isLogic ? ActionType.LogicDetector : ActionType.NATIVE_DETECTOR, + isLogic ? ActionType.LOGIC_DETECTOR : ActionType.NATIVE_DETECTOR, propsRef ); } @@ -90,8 +79,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { useEffect(() => { detachHandlers( attachedNativeHandlerTags.current, - attachedHandlerTags.current, - attachedNativeHandlerTags.current + attachedHandlerTags.current ); }, [children]); @@ -107,74 +95,47 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { propsRef, new Set(handlerTags), attachedHandlerTags.current, - attachedNativeHandlerTags.current, false ); }, [handlerTags, children]); useEffect(() => { - const shouldKeepLogicChild: Map = new Map(); + const logicChildrenToDelete: Set = new Set(); for (const key of logicChildren.current.keys()) { - shouldKeepLogicChild.set(key, false); + logicChildrenToDelete.add(key); } - props.logicChildren?.forEach((child, key) => { + props.logicChildren?.forEach((child) => { if (!logicChildren.current.has(child.viewTag)) { - logicChildren.current.set(child.viewTag, { - attachedHandlerTags: new Set(), - attachedNativeHandlerTags: new Set(), - }); - } - shouldKeepLogicChild.set(key.viewTag, true); - const attachedHandlerTags = logicChildren.current.get( - child.viewTag - )?.attachedHandlerTags; - const attachedNativeHandlerTags = logicChildren.current.get( - child.viewTag - )?.attachedNativeHandlerTags; - if (attachedHandlerTags && attachedNativeHandlerTags) { - attachHandlers( - child.viewRef, - propsRef, - new Set(child.handlerTags), - attachedHandlerTags, - attachedNativeHandlerTags, - true - ); + logicChildren.current.set(child.viewTag, new Set()); } + logicChildrenToDelete.delete(child.viewTag); + attachHandlers( + child.viewRef, + propsRef, + new Set(child.handlerTags), + logicChildren.current.get(child.viewTag)!, + true + ); }); - shouldKeepLogicChild.forEach((value, key) => { - if (value) { - const attachedHandlerTags = - logicChildren.current.get(key)?.attachedHandlerTags; - const attachedNativeHandlerTags = - logicChildren.current.get(key)?.attachedNativeHandlerTags; - if (attachedHandlerTags && attachedNativeHandlerTags) { - detachHandlers( - attachedHandlerTags, - attachedHandlerTags, - attachedNativeHandlerTags - ); - } + logicChildrenToDelete.forEach((childTag) => { + if (attachedHandlerTags && attachedNativeHandlerTags) { + detachHandlers( + logicChildren.current.get(childTag)!, + logicChildren.current.get(childTag)! + ); } + logicChildren.current.delete(childTag); }); }, [props.logicChildren]); useEffect(() => { return () => { - detachHandlers( - attachedHandlerTags.current, - attachedHandlerTags.current, - attachedNativeHandlerTags.current - ); + detachHandlers(attachedHandlerTags.current, attachedHandlerTags.current); logicChildren?.current.forEach((child) => { - detachHandlers( - child.attachedHandlerTags, - child.attachedHandlerTags, - child.attachedNativeHandlerTags - ); + detachHandlers(child, child); }); }; }, []); diff --git a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts index c842b59d1c..b53646339e 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts @@ -405,7 +405,7 @@ export default abstract class GestureHandler implements IGestureHandler { invokeNullableMethod(onGestureHandlerTouchEvent, touchEvent); } else if ( onGestureHandlerLogicTouchEvent && - this.actionType === ActionType.LogicDetector + this.actionType === ActionType.LOGIC_DETECTOR ) { invokeNullableMethod(onGestureHandlerLogicTouchEvent, touchEvent); } else { @@ -429,14 +429,14 @@ export default abstract class GestureHandler implements IGestureHandler { const resultEvent: ResultEvent = this.actionType !== ActionType.NATIVE_DETECTOR && - this.actionType !== ActionType.LogicDetector + this.actionType !== ActionType.LOGIC_DETECTOR ? this.transformEventData(newState, oldState) : this.lastSentState !== newState ? this.transformStateChangeEvent(newState, oldState) : this.transformUpdateEvent(newState); // TODO: cleanup the logic detector types - if (this.actionType === ActionType.LogicDetector) { + if (this.actionType === ActionType.LOGIC_DETECTOR) { resultEvent.nativeEvent = { ...resultEvent.nativeEvent, childTag: this.childTag, @@ -450,7 +450,7 @@ export default abstract class GestureHandler implements IGestureHandler { this.lastSentState = newState; if ( onGestureHandlerLogicStateChange && - this.actionType === ActionType.LogicDetector + this.actionType === ActionType.LOGIC_DETECTOR ) { invokeNullableMethod(onGestureHandlerLogicStateChange, resultEvent); } else { @@ -460,7 +460,7 @@ export default abstract class GestureHandler implements IGestureHandler { if (this.state === State.ACTIVE) { if ( this.actionType !== ActionType.NATIVE_DETECTOR && - this.actionType !== ActionType.LogicDetector + this.actionType !== ActionType.LOGIC_DETECTOR ) { (resultEvent.nativeEvent as GestureHandlerNativeEvent).oldState = undefined; @@ -471,7 +471,7 @@ export default abstract class GestureHandler implements IGestureHandler { if ( onGestureHandlerLogicEvent && - this.actionType === ActionType.LogicDetector + this.actionType === ActionType.LOGIC_DETECTOR ) { invokeNullableMethod(onGestureHandlerLogicEvent, resultEvent); } else { From fe1d67d2d4ba7958b7f9dcacc0696108337d8aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 8 Sep 2025 12:56:53 +0200 Subject: [PATCH 17/90] one path --- .../react/RNGestureHandlerEvent.kt | 4 +- .../react/RNGestureHandlerStateChangeEvent.kt | 4 +- .../react/RNGestureHandlerTouchEvent.kt | 4 +- .../apple/RNGestureHandlerDetector.h | 6 -- .../apple/RNGestureHandlerDetector.mm | 45 +++--------- .../apple/RNGestureHandlerManager.mm | 9 ++- .../apple/RNGestureHandlerNativeEventUtils.h | 4 -- .../apple/RNGestureHandlerNativeEventUtils.mm | 29 -------- ...RNGestureHandlerDetectorNativeComponent.ts | 4 -- .../src/v3/NativeDetector.tsx | 68 +++++++++++-------- .../src/web/handlers/GestureHandler.ts | 32 ++------- .../src/web/interfaces.ts | 4 -- 12 files changed, 66 insertions(+), 147 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEvent.kt index 153105fbff..be7a365d35 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerEvent.kt @@ -34,7 +34,9 @@ class RNGestureHandlerEvent private constructor() : Event useNativeAnimatedName: Boolean, ) { val view = if (handler.actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) { - handler.viewForEvents!! + handler.viewForEvents + } else if (handler.actionType == GestureHandler.ACTION_TYPE_LOGIC_DETECTOR) { + handler.parentView!! } else { handler.view!! } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerStateChangeEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerStateChangeEvent.kt index f63aa296a6..d865efe682 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerStateChangeEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerStateChangeEvent.kt @@ -28,7 +28,9 @@ class RNGestureHandlerStateChangeEvent private constructor() : Event, ) { val view = if (handler.actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) { - handler.viewForEvents!! + handler.viewForEvents + } else if (handler.actionType == GestureHandler.ACTION_TYPE_LOGIC_DETECTOR) { + handler.parentView!! } else { handler.view!! } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerTouchEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerTouchEvent.kt index bc0dec8705..edff891da2 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerTouchEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerTouchEvent.kt @@ -14,7 +14,9 @@ class RNGestureHandlerTouchEvent private constructor() : Event init(handler: T, actionType: Int) { val view = if (handler.actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) { - handler.viewForEvents!! + handler.viewForEvents + } else if (handler.actionType == GestureHandler.ACTION_TYPE_LOGIC_DETECTOR) { + handler.parentView!! } else { handler.view!! } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h index 637904fe32..9ee88d0eb2 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h @@ -20,12 +20,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)dispatchTouchEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerTouchEvent)event; -- (void)dispatchLogicStateChangeEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicStateChange)event; - -- (void)dispatchLogicGestureEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicEvent)event; - -- (void)dispatchLogicTouchEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicTouchEvent)event; - - (void)tryAttachNativeHandlersToChildView; - (void)detachNativeGestureHandlers; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index 837ef26c28..acfc2afba2 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -99,30 +99,6 @@ - (void)dispatchTouchEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandl } } -- (void)dispatchLogicStateChangeEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicStateChange)event -{ - if (_eventEmitter != nullptr) { - std::dynamic_pointer_cast(_eventEmitter) - ->onGestureHandlerLogicStateChange(event); - } -} - -- (void)dispatchLogicGestureEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicEvent)event -{ - if (_eventEmitter != nullptr) { - std::dynamic_pointer_cast(_eventEmitter) - ->onGestureHandlerLogicEvent(event); - } -} - -- (void)dispatchLogicTouchEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicTouchEvent)event -{ - if (_eventEmitter != nullptr) { - std::dynamic_pointer_cast(_eventEmitter) - ->onGestureHandlerLogicTouchEvent(event); - } -} - - (BOOL)shouldAttachGestureToSubview:(NSNumber *)handlerTag { RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; @@ -190,7 +166,7 @@ - (void)updatePropsInternal:(const std::vector &)handlerTags [_nativeHandlers addObject:handlerTag]; } else { if (isLogic) { - NSLog(@"attach logic child %@", handlerTag); + [[[handlerManager registry] handlerWithTag:handlerTag] setParentTag:@(self.tag)]; [handlerManager attachGestureHandler:handlerTag toViewWithTag:@(viewTag) @@ -233,20 +209,18 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") - std::unordered_map - shouldKeepLogicChild; + NSMutableSet *logicChildrenToDelete = [NSMutableSet set]; for (const std::pair &child : logicChildren) { - shouldKeepLogicChild[child.first] = false; + [logicChildrenToDelete addObject:@(child.first)]; } for (const RNGestureHandlerDetectorLogicChildrenStruct &child : newProps.logicChildren) { if (logicChildren.find(child.viewTag) == logicChildren.end()) { - // Initialize the vector for a new logic child logicChildren[child.viewTag].handlerTags = {}; logicChildren[child.viewTag].attachedHandlers = [NSMutableSet set]; - [[[handlerManager registry] handlerWithTag:@(child.viewTag)] setParentTag:@(self.tag)]; } - shouldKeepLogicChild[child.viewTag] = true; + + [logicChildrenToDelete removeObject:@(child.viewTag)]; [self updatePropsInternal:child.handlerTags oldHandlerTags:logicChildren[child.viewTag].handlerTags isLogic:true @@ -254,12 +228,9 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar attachedHandlers:logicChildren[child.viewTag].attachedHandlers]; } - for (const auto &child : shouldKeepLogicChild) { - if (!child.second) { - for (id handlerTag : logicChildren[child.first].attachedHandlers) { - [handlerManager.registry detachHandlerWithTag:handlerTag]; - } - logicChildren.erase(child.first); + for (const NSNumber *childTag : logicChildrenToDelete) { + for (id handlerTag : logicChildren[childTag.intValue].attachedHandlers) { + [handlerManager.registry detachHandlerWithTag:handlerTag]; } } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index 8751d23462..049c5e4b70 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -321,13 +321,12 @@ - (void)sendEvent:(RNGestureHandlerStateChange *)event NSNumber *parentTag = [[_registry handlerWithTag:event.handlerTag] getParentTag]; RNGHUIView *parentView = [self viewForReactTag:parentTag]; if ([event isKindOfClass:[RNGestureHandlerEvent class]]) { - // TODO: handle forAnimated RNGestureHandlerEvent *gestureEvent = (RNGestureHandlerEvent *)event; - auto nativeEvent = [gestureEvent getNativeLogicEvent:@(detectorView.tag)]; - [(RNGestureHandlerDetector *)parentView dispatchLogicGestureEvent:nativeEvent]; + auto nativeEvent = [gestureEvent getNativeEvent]; + [(RNGestureHandlerDetector *)parentView dispatchGestureEvent:nativeEvent]; } else { - auto nativeEvent = [event getNativeLogicEvent:@(detectorView.tag)]; - [(RNGestureHandlerDetector *)parentView dispatchLogicStateChangeEvent:nativeEvent]; + auto nativeEvent = [event getNativeEvent]; + [(RNGestureHandlerDetector *)parentView dispatchStateChangeEvent:nativeEvent]; } break; } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h index bb621bb1b9..9a10248b6c 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h @@ -5,13 +5,9 @@ @interface RNGestureHandlerEvent (NativeEvent) - (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerEvent)getNativeEvent; -- (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicEvent)getNativeLogicEvent: - (NSNumber *)childTag; @end @interface RNGestureHandlerStateChange (NativeEvent) - (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerStateChange)getNativeEvent; -- (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicStateChange)getNativeLogicEvent: - (NSNumber *)childTag; @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.mm index 691da918f3..feea2e7785 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.mm @@ -67,25 +67,10 @@ @implementation RNGestureHandlerEvent (NativeEvent) return nativeEvent; } -- (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicEvent)getNativeLogicEvent: - (NSNumber *)childTag -{ - folly::dynamic handlerData = rngh_dynamicFromId(self.extraData.data); - - facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicEvent nativeEvent = { - .handlerTag = [self.handlerTag intValue], - .state = static_cast(self.state), - .handlerData = handlerData, - .childTag = [childTag intValue]}; - - return nativeEvent; -} @end @implementation RNGestureHandlerStateChange (NativeEvent) -// TODO: unify logic getter - - (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerStateChange)getNativeEvent { folly::dynamic handlerData = rngh_dynamicFromId(self.extraData.data); @@ -100,18 +85,4 @@ @implementation RNGestureHandlerStateChange (NativeEvent) return nativeEvent; } -- (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicStateChange)getNativeLogicEvent: - (NSNumber *)childTag -{ - folly::dynamic handlerData = rngh_dynamicFromId(self.extraData.data); - - facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerLogicStateChange nativeEvent = { - .handlerTag = [self.handlerTag intValue], - .state = static_cast(self.state), - .oldState = static_cast(self.previousState), - .handlerData = handlerData, - .childTag = [childTag intValue]}; - - return nativeEvent; -} @end diff --git a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts index f6be1d048e..7d5a1dffe9 100644 --- a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts +++ b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts @@ -54,10 +54,6 @@ export interface NativeProps extends ViewProps { onGestureHandlerStateChange?: DirectEventHandler; onGestureHandlerTouchEvent?: DirectEventHandler; - onGestureHandlerLogicEvent?: DirectEventHandler; - onGestureHandlerLogicStateChange?: DirectEventHandler; - onGestureHandlerLogicTouchEvent?: DirectEventHandler; - handlerTags: Int32[]; moduleId: Int32; logicChildren: LogicProps[]; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index e938c41d3d..0b77dfa370 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -72,36 +72,50 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { return ( { + if (!logicMethods.current.has(e.nativeEvent.handlerTag)) { + invokeNullableMethod( + gesture.gestureEvents.onGestureHandlerStateChange, + e + ); + } else { + invokeNullableMethod( + logicMethods.current.get(e.nativeEvent.handlerTag)?.current + ?.onGestureHandlerStateChange, + e + ); + } + }} + onGestureHandlerEvent={(e) => { + if (!logicMethods.current.has(e.nativeEvent.handlerTag)) { + invokeNullableMethod( + gesture.gestureEvents.onGestureHandlerEvent, + e + ); + } else { + invokeNullableMethod( + logicMethods.current.get(e.nativeEvent.handlerTag)?.current + ?.onGestureHandlerEvent, + e + ); + } + }} onGestureHandlerAnimatedEvent={ gesture.gestureEvents.onGestureHandlerAnimatedEvent } - onGestureHandlerTouchEvent={ - gesture.gestureEvents.onGestureHandlerTouchEvent - } - onGestureHandlerLogicEvent={(e) => { - invokeNullableMethod( - logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerEvent, - e - ); - }} - onGestureHandlerLogicStateChange={(e) => { - invokeNullableMethod( - logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerStateChange, - e - ); - }} - onGestureHandlerLogicTouchEvent={(e) => { - invokeNullableMethod( - logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerTouchEvent, - e - ); + onGestureHandlerTouchEvent={(e) => { + if (!logicMethods.current.has(e.nativeEvent.handlerTag)) { + invokeNullableMethod( + gesture.gestureEvents.onGestureHandlerTouchEvent, + e + ); + } else { + invokeNullableMethod( + logicMethods.current.get(e.nativeEvent.handlerTag)?.current + ?.onGestureHandlerTouchEvent, + e + ); + } }} moduleId={globalThis._RNGH_MODULE_ID} handlerTags={[gesture.tag]} diff --git a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts index b53646339e..d5a2701763 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts @@ -388,11 +388,8 @@ export default abstract class GestureHandler implements IGestureHandler { return; } this.ensurePropsRef(); - const { - onGestureHandlerEvent, - onGestureHandlerTouchEvent, - onGestureHandlerLogicTouchEvent, - }: PropsRef = this.propsRef!.current; + const { onGestureHandlerEvent, onGestureHandlerTouchEvent }: PropsRef = + this.propsRef!.current; const touchEvent: ResultEvent | undefined = this.transformTouchEvent(event); @@ -403,11 +400,6 @@ export default abstract class GestureHandler implements IGestureHandler { this.actionType === ActionType.NATIVE_DETECTOR ) { invokeNullableMethod(onGestureHandlerTouchEvent, touchEvent); - } else if ( - onGestureHandlerLogicTouchEvent && - this.actionType === ActionType.LOGIC_DETECTOR - ) { - invokeNullableMethod(onGestureHandlerLogicTouchEvent, touchEvent); } else { invokeNullableMethod(onGestureHandlerEvent, touchEvent); } @@ -423,8 +415,6 @@ export default abstract class GestureHandler implements IGestureHandler { onGestureHandlerEvent, onGestureHandlerStateChange, onGestureHandlerAnimatedEvent, - onGestureHandlerLogicStateChange, - onGestureHandlerLogicEvent, }: PropsRef = this.propsRef!.current; const resultEvent: ResultEvent = @@ -448,14 +438,7 @@ export default abstract class GestureHandler implements IGestureHandler { // However, this may cause trouble in the future (but for now we don't know that) if (this.lastSentState !== newState) { this.lastSentState = newState; - if ( - onGestureHandlerLogicStateChange && - this.actionType === ActionType.LOGIC_DETECTOR - ) { - invokeNullableMethod(onGestureHandlerLogicStateChange, resultEvent); - } else { - invokeNullableMethod(onGestureHandlerStateChange, resultEvent); - } + invokeNullableMethod(onGestureHandlerStateChange, resultEvent); } if (this.state === State.ACTIVE) { if ( @@ -469,14 +452,7 @@ export default abstract class GestureHandler implements IGestureHandler { invokeNullableMethod(onGestureHandlerAnimatedEvent, resultEvent); } - if ( - onGestureHandlerLogicEvent && - this.actionType === ActionType.LOGIC_DETECTOR - ) { - invokeNullableMethod(onGestureHandlerLogicEvent, resultEvent); - } else { - invokeNullableMethod(onGestureHandlerEvent, resultEvent); - } + invokeNullableMethod(onGestureHandlerEvent, resultEvent); } }; diff --git a/packages/react-native-gesture-handler/src/web/interfaces.ts b/packages/react-native-gesture-handler/src/web/interfaces.ts index 420659da99..e358c946ce 100644 --- a/packages/react-native-gesture-handler/src/web/interfaces.ts +++ b/packages/react-native-gesture-handler/src/web/interfaces.ts @@ -136,10 +136,6 @@ export interface PropsRef { onGestureHandlerAnimatedEvent?: (e: ResultEvent) => void; onGestureHandlerStateChange: (e: ResultEvent) => void; onGestureHandlerTouchEvent?: (e: ResultEvent) => void; - - onGestureHandlerLogicEvent?: (e: ResultEvent) => void; - onGestureHandlerLogicStateChange?: (e: ResultEvent) => void; - onGestureHandlerLogicTouchEvent?: (e: ResultEvent) => void; } export interface AdaptedEvent { From 4e718b3baae9920c72695405505b36d56fbe5250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 8 Sep 2025 14:18:30 +0200 Subject: [PATCH 18/90] merge fix --- .../apple/RNGestureHandlerNativeEventUtils.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h index 989c00414a..87c97b56c0 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerNativeEventUtils.h @@ -5,22 +5,16 @@ @interface RNGestureHandlerEvent (NativeEvent) - (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerEvent)getNativeEvent; -<<<<<<< HEAD -======= - (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerReanimatedEvent)getReanimatedNativeEvent; ->>>>>>> next @end @interface RNGestureHandlerStateChange (NativeEvent) - (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerStateChange)getNativeEvent; -<<<<<<< HEAD -======= - (facebook::react::RNGestureHandlerDetectorEventEmitter::OnGestureHandlerReanimatedStateChange) getReanimatedNativeEvent; ->>>>>>> next @end From 8e9d2dc93bc930b97ef51f066b5aa00f31a41956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 9 Sep 2025 08:05:22 +0200 Subject: [PATCH 19/90] adjust for reanimated/animated separation --- .../events/RNGestureHandlerEventDispatcher.kt | 24 ++++- .../apple/RNGestureHandlerManager.mm | 17 +--- .../src/v3/LogicDetector.tsx | 11 +++ .../src/v3/LogicDetector.web.tsx | 11 +++ .../src/v3/NativeDetector.tsx | 89 ++++++++++--------- .../src/v3/types.ts | 3 + yarn.lock | 4 +- 7 files changed, 99 insertions(+), 60 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt index fd941ea10e..45a76d59cc 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt @@ -79,7 +79,6 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React } else { EventHandlerType.ForJS } - val event = RNGestureHandlerEvent.obtain( handler, handler.actionType, @@ -90,10 +89,18 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React handler.viewForEvents!!.dispatchEvent(event) } GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { + val eventHandlerType = if (handler.dispatchesAnimatedEvents) { + EventHandlerType.ForAnimated + } else if (handler.dispatchesReanimatedEvents) { + EventHandlerType.ForReanimated + } else { + EventHandlerType.ForJS + } val event = RNGestureHandlerEvent.obtain( handler, handler.actionType, handlerFactory.createEventBuilder(handler), + eventHandlerType, ) handler.parentView?.dispatchEvent(event) @@ -165,12 +172,19 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React } GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { + val eventHandlerType = if (handler.dispatchesReanimatedEvents) { + EventHandlerType.ForReanimated + } else { + EventHandlerType.ForJS + } + val event = RNGestureHandlerStateChangeEvent.obtain( handler, newState, oldState, handler.actionType, handlerFactory.createEventBuilder(handler), + eventHandlerType, ) handler.parentView?.dispatchEvent(event) } @@ -219,7 +233,13 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React handler.viewForEvents!!.dispatchEvent(event) } GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { - val event = RNGestureHandlerTouchEvent.obtain(handler, handler.actionType) + val eventHandlerType = if (handler.dispatchesReanimatedEvents) { + EventHandlerType.ForReanimated + } else { + EventHandlerType.ForJS + } + + val event = RNGestureHandlerTouchEvent.obtain(handler, handler.actionType, eventHandlerType) handler.parentView?.dispatchEvent(event) } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index f464e0fd8b..1e279eed04 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -303,6 +303,10 @@ - (void)sendEvent:(RNGestureHandlerStateChange *)event // but results in a compilation error. { switch (actionType) { + case RNGestureHandlerActionTypeLogicDetector: { + NSNumber *parentTag = [[_registry handlerWithTag:event.handlerTag] getParentTag]; + detectorView = [self viewForReactTag:parentTag]; + } case RNGestureHandlerActionTypeNativeDetector: { if ([event isKindOfClass:[RNGestureHandlerEvent class]]) { switch (eventHandlerType) { @@ -333,19 +337,6 @@ - (void)sendEvent:(RNGestureHandlerStateChange *)event } break; } - case RNGestureHandlerActionTypeLogicDetector: { - NSNumber *parentTag = [[_registry handlerWithTag:event.handlerTag] getParentTag]; - RNGHUIView *parentView = [self viewForReactTag:parentTag]; - if ([event isKindOfClass:[RNGestureHandlerEvent class]]) { - RNGestureHandlerEvent *gestureEvent = (RNGestureHandlerEvent *)event; - auto nativeEvent = [gestureEvent getNativeEvent]; - [(RNGestureHandlerDetector *)parentView dispatchGestureEvent:nativeEvent]; - } else { - auto nativeEvent = [event getNativeEvent]; - [(RNGestureHandlerDetector *)parentView dispatchStateChangeEvent:nativeEvent]; - } - break; - } case RNGestureHandlerActionTypeReanimatedWorklet: [self sendEventForReanimated:event]; diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 8706c7eefb..47aa8c10ea 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -14,6 +14,11 @@ export const LogicDetector = (props: NativeDetectorProps) => { onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, + onReanimatedStateChange: + props.gesture.gestureEvents.onReanimatedStateChange, + onReanimatedUpdateEvent: + props.gesture.gestureEvents.onReanimatedUpdateEvent, + onReanimatedTouchEvent: props.gesture.gestureEvents.onReanimatedTouchEvent, }); useEffect(() => { @@ -27,6 +32,12 @@ export const LogicDetector = (props: NativeDetectorProps) => { onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, + onReanimatedStateChange: + props.gesture.gestureEvents.onReanimatedStateChange, + onReanimatedUpdateEvent: + props.gesture.gestureEvents.onReanimatedUpdateEvent, + onReanimatedTouchEvent: + props.gesture.gestureEvents.onReanimatedTouchEvent, }; }, [props.gesture.gestureEvents]); diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx index 4c15a7bedd..8031b0c9b7 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx @@ -13,6 +13,11 @@ export const LogicDetector = (props: NativeDetectorProps) => { onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, + onReanimatedStateChange: + props.gesture.gestureEvents.onReanimatedStateChange, + onReanimatedUpdateEvent: + props.gesture.gestureEvents.onReanimatedUpdateEvent, + onReanimatedTouchEvent: props.gesture.gestureEvents.onReanimatedTouchEvent, }); useEffect(() => { @@ -27,6 +32,12 @@ export const LogicDetector = (props: NativeDetectorProps) => { onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, + onReanimatedStateChange: + props.gesture.gestureEvents.onReanimatedStateChange, + onReanimatedUpdateEvent: + props.gesture.gestureEvents.onReanimatedUpdateEvent, + onReanimatedTouchEvent: + props.gesture.gestureEvents.onReanimatedTouchEvent, }; }, [props.gesture.gestureEvents]); diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index b393aa15bf..c1fb2dc805 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -9,7 +9,7 @@ import React, { import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; -import { invokeNullableMethod, tagMessage } from '../utils'; +import { tagMessage } from '../utils'; import { LogicDetectorProps, LogicMethods, NativeDetectorProps } from './types'; const AnimatedNativeDetector = @@ -73,59 +73,62 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { { - if (!logicMethods.current.has(e.nativeEvent.handlerTag)) { - invokeNullableMethod( - gesture.gestureEvents.onGestureHandlerStateChange, - e - ); - } else { - invokeNullableMethod( - logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerStateChange, - e - ); + const method = !logicMethods.current.has(e.nativeEvent.handlerTag) + ? gesture.gestureEvents.onGestureHandlerStateChange + : logicMethods.current.get(e.nativeEvent.handlerTag)?.current + ?.onGestureHandlerStateChange; + if (method) { + method(e); } }} onGestureHandlerEvent={(e) => { - if (!logicMethods.current.has(e.nativeEvent.handlerTag)) { - invokeNullableMethod( - gesture.gestureEvents.onGestureHandlerEvent, - e - ); - } else { - invokeNullableMethod( - logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerEvent, - e - ); + const method = !logicMethods.current.has(e.nativeEvent.handlerTag) + ? gesture.gestureEvents.onGestureHandlerEvent + : logicMethods.current.get(e.nativeEvent.handlerTag)?.current + ?.onGestureHandlerEvent; + if (method) { + method(e); } }} onGestureHandlerAnimatedEvent={ gesture.gestureEvents.onGestureHandlerAnimatedEvent } onGestureHandlerTouchEvent={(e) => { - if (!logicMethods.current.has(e.nativeEvent.handlerTag)) { - invokeNullableMethod( - gesture.gestureEvents.onGestureHandlerTouchEvent, - e - ); - } else { - invokeNullableMethod( - logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerTouchEvent, - e - ); + const method = !logicMethods.current.has(e.nativeEvent.handlerTag) + ? gesture.gestureEvents.onGestureHandlerTouchEvent + : logicMethods.current.get(e.nativeEvent.handlerTag)?.current + ?.onGestureHandlerTouchEvent; + if (method) { + method(e); + } + }} + onGestureHandlerReanimatedStateChange={(e) => { + const method = !logicMethods.current.has(e.nativeEvent.handlerTag) + ? gesture.gestureEvents.onReanimatedStateChange + : logicMethods.current.get(e.nativeEvent.handlerTag)?.current + ?.onReanimatedStateChange; + if (method) { + method(e); + } + }} + onGestureHandlerReanimatedEvent={(e) => { + const method = !logicMethods.current.has(e.nativeEvent.handlerTag) + ? gesture.gestureEvents.onReanimatedUpdateEvent + : logicMethods.current.get(e.nativeEvent.handlerTag)?.current + ?.onReanimatedUpdateEvent; + if (method) { + method(e); + } + }} + onGestureHandlerReanimatedTouchEvent={(e) => { + const method = !logicMethods.current.has(e.nativeEvent.handlerTag) + ? gesture.gestureEvents.onReanimatedTouchEvent + : logicMethods.current.get(e.nativeEvent.handlerTag)?.current + ?.onReanimatedTouchEvent; + if (method) { + method(e); } }} - onGestureHandlerReanimatedStateChange={ - gesture.gestureEvents.onReanimatedStateChange - } - onGestureHandlerReanimatedEvent={ - gesture.gestureEvents.onReanimatedUpdateEvent - } - onGestureHandlerReanimatedTouchEvent={ - gesture.gestureEvents.onReanimatedTouchEvent - } moduleId={globalThis._RNGH_MODULE_ID} handlerTags={[gesture.tag]} style={styles.detector} diff --git a/packages/react-native-gesture-handler/src/v3/types.ts b/packages/react-native-gesture-handler/src/v3/types.ts index b5b0de9d6a..356d16e27a 100644 --- a/packages/react-native-gesture-handler/src/v3/types.ts +++ b/packages/react-native-gesture-handler/src/v3/types.ts @@ -92,4 +92,7 @@ export interface LogicMethods { onGestureHandlerEvent?: (e: any) => void; onGestureHandlerStateChange?: (e: any) => void; onGestureHandlerTouchEvent?: (e: any) => void; + onReanimatedStateChange?: (e: any) => void; + onReanimatedUpdateEvent?: (e: any) => void; + onReanimatedTouchEvent?: (e: any) => void; } diff --git a/yarn.lock b/yarn.lock index 658a983f5d..bad6bbfcce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6527,7 +6527,7 @@ __metadata: jest: "npm:^29.6.3" prettier: "npm:3.3.3" react-native-gesture-handler: "workspace:*" - react-native-reanimated: "npm:^3.19.1" + react-native-reanimated: "npm:^3.18.0" react-native-safe-area-context: "npm:^5.4.0" react-native-screens: "npm:^4.10.0" react-native-svg: "npm:15.11.2" @@ -14298,7 +14298,7 @@ __metadata: languageName: node linkType: hard -"react-native-reanimated@npm:^3.18.0, react-native-reanimated@npm:^3.19.1": +"react-native-reanimated@npm:^3.18.0": version: 3.19.1 resolution: "react-native-reanimated@npm:3.19.1" dependencies: From 97b7369c958c7cdef3b9c03ed73eee6b942957ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 9 Sep 2025 10:59:40 +0200 Subject: [PATCH 20/90] general refactor --- .../react/RNGestureHandlerDetectorView.kt | 77 ++++++++++--------- .../apple/RNGestureHandlerDetector.mm | 42 +++++----- ...RNGestureHandlerDetectorNativeComponent.ts | 1 - .../src/v3/HostGestureDetector.web.tsx | 11 ++- .../src/v3/LogicDetector.tsx | 26 +++++-- .../src/v3/LogicDetector.web.tsx | 60 --------------- .../src/v3/NativeDetector.tsx | 29 +++---- .../src/v3/types.ts | 1 - 8 files changed, 94 insertions(+), 153 deletions(-) delete mode 100644 packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index 1bc17e329a..ef22c04f20 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -16,9 +16,9 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { private var nativeHandlers: MutableSet = mutableSetOf() private var attachedHandlers: MutableSet = mutableSetOf() private var moduleId: Int = -1 - private var logicChildren: HashMap> = hashMapOf() + private var logicChildren: MutableMap> = mutableMapOf() - data class LogicProps(val handlerTags: List, val moduleId: Int, val viewTag: Int) + data class LogicProps(val handlerTags: List, val viewTag: Int) fun setHandlerTags(handlerTags: ReadableArray?) { val newHandlers = handlerTags?.toArrayList()?.map { (it as Double).toInt() } ?: emptyList() @@ -29,46 +29,25 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { return } - attachHandlers(newHandlers, this.id, false, attachedHandlers) + attachHandlers(newHandlers) } fun setModuleId(id: Int) { assert(this.moduleId == -1) { "Tried to change moduleId of a native detector" } this.moduleId = id - this.attachHandlers(handlersToAttach ?: return, this.id, false, attachedHandlers) + this.attachHandlers(handlersToAttach ?: return) handlersToAttach = null } fun setLogicChildren(newLogicChildren: ReadableArray?) { - val logicChildrenToDelete = HashSet() + val logicChildrenToDelete: MutableSet = mutableSetOf() for (child in logicChildren) { logicChildrenToDelete.add(child.key) } - val mappedChildren = mutableListOf() - - for (i in 0 until newLogicChildren!!.size()) { - val child = newLogicChildren.getMap(i) // Each element should be a ReadableMap - - val handlerTagsArray = child!!.getArray("handlerTags") - val handlerTags = mutableListOf() - for (j in 0 until handlerTagsArray!!.size()) { - handlerTags.add(handlerTagsArray.getInt(j)) - } - - val moduleId = child.getInt("moduleId") - val viewTag = child.getInt("viewTag") - - mappedChildren.add( - LogicProps( - handlerTags = handlerTags, - moduleId = moduleId, - viewTag = viewTag, - ), - ) - } + val mappedChildren = newLogicChildren?.mapLogicProps().orEmpty() for (child in mappedChildren) { if (!logicChildren.containsKey(child.viewTag)) { @@ -78,7 +57,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { attachHandlers( child.handlerTags, child.viewTag, - true, + GestureHandler.ACTION_TYPE_LOGIC_DETECTOR, logicChildren[child.viewTag]!!, ) } @@ -113,9 +92,9 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { private fun attachHandlers( newHandlers: List, - viewTag: Int, - isLogic: Boolean, - attachedHandlers: MutableSet, + viewTag: Int = this.id, + actionType: Int = GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, + attachedHandlers: MutableSet = this.attachedHandlers, ) { val registry = RNGestureHandlerModule.registries[moduleId] ?: throw Exception("Tried to access a non-existent registry") @@ -130,12 +109,6 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { changes[tag] = if (changes.containsKey(tag)) GestureHandlerMutation.Keep else GestureHandlerMutation.Attach } - val actionType = if (isLogic) { - GestureHandler.ACTION_TYPE_LOGIC_DETECTOR - } else { - GestureHandler.ACTION_TYPE_NATIVE_DETECTOR - } - for (entry in changes) { val tag = entry.key @@ -146,7 +119,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { nativeHandlers.add(tag) } else { registry.attachHandlerToView(tag, viewTag, actionType) - if (isLogic) { + if (actionType == GestureHandler.ACTION_TYPE_LOGIC_DETECTOR) { registry.getHandler(tag)?.parentView = this } attachedHandlers.add(tag) @@ -209,6 +182,34 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } } + private fun ReadableArray.mapLogicProps(): List { + val mappedChildren = mutableListOf() + + for (i in 0 until this.size()) { + val child = this.getMap(i) ?: continue + + val handlerTagsArray = child.getArray("handlerTags") + val handlerTags = mutableListOf() + + if (handlerTagsArray != null) { + for (j in 0 until handlerTagsArray.size()) { + handlerTags.add(handlerTagsArray.getInt(j)) + } + } + + val viewTag = child.getInt("viewTag") + + mappedChildren.add( + LogicProps( + handlerTags = handlerTags, + viewTag = viewTag, + ), + ) + } + + return mappedChildren + } + companion object { private enum class GestureHandlerMutation { Attach, diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index d402d54983..babea038ad 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -162,11 +162,11 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics [super updateLayoutMetrics:newLayoutMetrics oldLayoutMetrics:oldLayoutMetrics]; } -- (void)updatePropsInternal:(const std::vector &)handlerTags - oldHandlerTags:(const std::vector &)oldHandlerTags - isLogic:(bool)isLogic - viewTag:(const int)viewTag - attachedHandlers:(NSMutableSet *)attachedHandlers +- (void)attachHandlers:(const std::vector &)handlerTags + oldHandlerTags:(const std::vector &)oldHandlerTags + actionType:(RNGestureHandlerActionType)actionType + viewTag:(const int)viewTag + attachedHandlers:(NSMutableSet *)attachedHandlers { RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") @@ -190,16 +190,12 @@ - (void)updatePropsInternal:(const std::vector &)handlerTags // case we cannot attach `NativeViewGestureHandlers` here and we have to do it in `didAddSubview` method. [_nativeHandlers addObject:handlerTag]; } else { - if (isLogic) { + if (actionType == RNGestureHandlerActionTypeLogicDetector) { [[[handlerManager registry] handlerWithTag:handlerTag] setParentTag:@(self.tag)]; - [handlerManager attachGestureHandler:handlerTag - toViewWithTag:@(viewTag) - withActionType:RNGestureHandlerActionTypeLogicDetector]; + [handlerManager attachGestureHandler:handlerTag toViewWithTag:@(viewTag) withActionType:actionType]; } else { - // [handlerManager.registry attachHandlerWithTag:handlerTag - // toView:self - // withActionType:RNGestureHandlerActionTypeNativeDetector]; + [handlerManager.registry attachHandlerWithTag:handlerTag toView:self withActionType:actionType]; } [attachedHandlers addObject:handlerTag]; } @@ -225,11 +221,12 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar static std::vector emptyVector; const std::vector &oldHandlerTags = (oldPropsBase != nullptr) ? oldProps.handlerTags : emptyVector; - [self updatePropsInternal:newProps.handlerTags - oldHandlerTags:oldHandlerTags - isLogic:false - viewTag:-1 - attachedHandlers:_attachedHandlers]; + [self attachHandlers:newProps.handlerTags + oldHandlerTags:oldHandlerTags + actionType:RNGestureHandlerActionTypeNativeDetector + viewTag:-1 + attachedHandlers:_attachedHandlers]; + [super updateProps:propsBase oldProps:oldPropsBase]; RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") @@ -246,11 +243,12 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar } [logicChildrenToDelete removeObject:@(child.viewTag)]; - [self updatePropsInternal:child.handlerTags - oldHandlerTags:logicChildren[child.viewTag].handlerTags - isLogic:true - viewTag:child.viewTag - attachedHandlers:logicChildren[child.viewTag].attachedHandlers]; + + [self attachHandlers:child.handlerTags + oldHandlerTags:logicChildren[child.viewTag].handlerTags + actionType:RNGestureHandlerActionTypeLogicDetector + viewTag:child.viewTag + attachedHandlers:logicChildren[child.viewTag].attachedHandlers]; } for (const NSNumber *childTag : logicChildrenToDelete) { diff --git a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts index 02d1ea6200..355ecca7b6 100644 --- a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts +++ b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts @@ -44,7 +44,6 @@ type GestureHandlerTouchEvent = Readonly<{ export interface LogicProps { handlerTags: Int32[]; - moduleId: Int32; viewTag: Int32; } diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index e9caa5bef5..b846ea380e 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -14,7 +14,6 @@ export interface GestureHandlerDetectorProps extends PropsRef { export interface LogicDetectorProps { viewTag: number; - moduleId: number; handlerTags: number[]; viewRef: RefObject; } @@ -45,7 +44,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { propsRef: RefObject, currentHandlerTags: Set, attachedHandlerTags: Set, - isLogic: boolean + actionType: ActionType ) => { const oldHandlerTags = attachedHandlerTags.difference(currentHandlerTags); const newHandlerTags = currentHandlerTags.difference(attachedHandlerTags); @@ -60,7 +59,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { RNGestureHandlerModule.attachGestureHandler( tag, viewRef.current!.firstChild, - ActionType.NATIVE_DETECTOR, + actionType, propsRef ); attachedNativeHandlerTags.current.add(tag); @@ -68,7 +67,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { RNGestureHandlerModule.attachGestureHandler( tag, viewRef.current, - isLogic ? ActionType.LOGIC_DETECTOR : ActionType.NATIVE_DETECTOR, + actionType, propsRef ); } @@ -95,7 +94,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { propsRef, new Set(handlerTags), attachedHandlerTags.current, - false + ActionType.NATIVE_DETECTOR ); }, [handlerTags, children]); @@ -116,7 +115,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { propsRef, new Set(child.handlerTags), logicChildren.current.get(child.viewTag)!, - true + ActionType.LOGIC_DETECTOR ); }); diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 47aa8c10ea..20e83f42e7 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react'; import { useDetectorContext } from './NativeDetector'; import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; -import { findNodeHandle } from 'react-native'; +import { findNodeHandle, Platform } from 'react-native'; import { NativeDetectorProps } from './types'; export const LogicDetector = (props: NativeDetectorProps) => { @@ -22,7 +22,16 @@ export const LogicDetector = (props: NativeDetectorProps) => { }); useEffect(() => { - setViewTag(findNodeHandle(viewRef.current)!); + if (Platform.OS === 'web') { + if (viewRef.current != null) { + setViewTag(viewRef.current); + } + } else { + const tag = findNodeHandle(viewRef.current); + if (tag != null) { + setViewTag(tag); + } + } }, []); useEffect(() => { @@ -42,11 +51,14 @@ export const LogicDetector = (props: NativeDetectorProps) => { }, [props.gesture.gestureEvents]); useEffect(() => { - const logicProps = { - viewTag: viewTag, - moduleId: globalThis._RNGH_MODULE_ID, - handlerTags: [props.gesture.tag], - }; + if (viewTag === -1) { + return; + } + + const logicProps = + Platform.OS === 'web' + ? { viewRef, viewTag, handlerTags: [props.gesture.tag] } + : { viewTag, handlerTags: [props.gesture.tag] }; register(logicProps, logicMethods); diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx deleted file mode 100644 index 8031b0c9b7..0000000000 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.web.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { useEffect, useRef, useState } from 'react'; -import { useDetectorContext } from './NativeDetector'; -import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; -import { NativeDetectorProps } from './types'; - -export const LogicDetector = (props: NativeDetectorProps) => { - const { register, unregister } = useDetectorContext(); - const viewRef = useRef(null); - const [viewTag, setViewTag] = useState(-1); - const logicMethods = useRef({ - onGestureHandlerStateChange: - props.gesture.gestureEvents.onGestureHandlerStateChange, - onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, - onGestureHandlerTouchEvent: - props.gesture.gestureEvents.onGestureHandlerTouchEvent, - onReanimatedStateChange: - props.gesture.gestureEvents.onReanimatedStateChange, - onReanimatedUpdateEvent: - props.gesture.gestureEvents.onReanimatedUpdateEvent, - onReanimatedTouchEvent: props.gesture.gestureEvents.onReanimatedTouchEvent, - }); - - useEffect(() => { - // TODO: set tags from parent - setViewTag(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)); - }, []); - - useEffect(() => { - logicMethods.current = { - onGestureHandlerStateChange: - props.gesture.gestureEvents.onGestureHandlerStateChange, - onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, - onGestureHandlerTouchEvent: - props.gesture.gestureEvents.onGestureHandlerTouchEvent, - onReanimatedStateChange: - props.gesture.gestureEvents.onReanimatedStateChange, - onReanimatedUpdateEvent: - props.gesture.gestureEvents.onReanimatedUpdateEvent, - onReanimatedTouchEvent: - props.gesture.gestureEvents.onReanimatedTouchEvent, - }; - }, [props.gesture.gestureEvents]); - - useEffect(() => { - const logicProps = { - viewRef: viewRef, - viewTag: viewTag, - moduleId: globalThis._RNGH_MODULE_ID, - handlerTags: [props.gesture.tag], - }; - - register(logicProps, logicMethods); - - return () => { - unregister(viewTag); - }; - }, [viewTag, props.gesture.tag, register, unregister]); - - return {props.children}; -}; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index c1fb2dc805..5d11c280d5 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -9,7 +9,7 @@ import React, { import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; -import { tagMessage } from '../utils'; +import { invokeNullableMethod, tagMessage } from '../utils'; import { LogicDetectorProps, LogicMethods, NativeDetectorProps } from './types'; const AnimatedNativeDetector = @@ -41,14 +41,13 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { const register = useCallback( (child: LogicDetectorProps, methods: RefObject) => { - if (child.viewTag !== -1) { - setLogicChildren((prev) => { - if (prev.some((c) => c.viewTag === child.viewTag)) { - return prev; - } - return [...prev, child]; - }); - } + setLogicChildren((prev) => { + if (prev.some((c) => c.viewTag === child.viewTag)) { + return prev; + } + return [...prev, child]; + }); + child.handlerTags.forEach((tag) => { logicMethods.current.set(tag, methods); }); @@ -107,27 +106,21 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { ? gesture.gestureEvents.onReanimatedStateChange : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onReanimatedStateChange; - if (method) { - method(e); - } + invokeNullableMethod(method, e); }} onGestureHandlerReanimatedEvent={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) ? gesture.gestureEvents.onReanimatedUpdateEvent : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onReanimatedUpdateEvent; - if (method) { - method(e); - } + invokeNullableMethod(method, e); }} onGestureHandlerReanimatedTouchEvent={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) ? gesture.gestureEvents.onReanimatedTouchEvent : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onReanimatedTouchEvent; - if (method) { - method(e); - } + invokeNullableMethod(method, e); }} moduleId={globalThis._RNGH_MODULE_ID} handlerTags={[gesture.tag]} diff --git a/packages/react-native-gesture-handler/src/v3/types.ts b/packages/react-native-gesture-handler/src/v3/types.ts index 356d16e27a..d16c1b6f51 100644 --- a/packages/react-native-gesture-handler/src/v3/types.ts +++ b/packages/react-native-gesture-handler/src/v3/types.ts @@ -79,7 +79,6 @@ export type AnimatedEvent = ((...args: any[]) => void) & { export interface LogicDetectorProps { viewTag: number; - moduleId: number; handlerTags: number[]; } From 8b6df8c93df6b666284d3942335d4dff86f68021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 9 Sep 2025 13:46:03 +0200 Subject: [PATCH 21/90] simplified attaching --- .../react/RNGestureHandlerDetectorView.kt | 40 +++-------- .../apple/RNGestureHandlerDetector.mm | 67 ++++++------------- 2 files changed, 33 insertions(+), 74 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index ef22c04f20..9a2446cceb 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -41,11 +41,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } fun setLogicChildren(newLogicChildren: ReadableArray?) { - val logicChildrenToDelete: MutableSet = mutableSetOf() - - for (child in logicChildren) { - logicChildrenToDelete.add(child.key) - } + val logicChildrenToDelete = logicChildren.keys.toMutableSet() val mappedChildren = newLogicChildren?.mapLogicProps().orEmpty() @@ -99,20 +95,12 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { val registry = RNGestureHandlerModule.registries[moduleId] ?: throw Exception("Tried to access a non-existent registry") - val changes = mutableMapOf() - - for (tag in attachedHandlers) { - changes[tag] = GestureHandlerMutation.Detach - } + val handlersToDetach = attachedHandlers.toMutableSet() for (tag in newHandlers) { - changes[tag] = if (changes.containsKey(tag)) GestureHandlerMutation.Keep else GestureHandlerMutation.Attach - } - - for (entry in changes) { - val tag = entry.key - - if (entry.value == GestureHandlerMutation.Attach) { + handlersToDetach.remove(tag) + if (!attachedHandlers.contains(tag)) { + // attach handler if (shouldAttachGestureToChildView(tag)) { // It might happen that `attachHandlers` will be called before children are added into view hierarchy. In that case we cannot // attach `NativeViewGestureHandlers` here and we have to do it in `addView` method. @@ -124,13 +112,15 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } attachedHandlers.add(tag) } - } else if (entry.value == GestureHandlerMutation.Detach) { - registry.detachHandler(tag) - nativeHandlers.remove(tag) - attachedHandlers.remove(tag) } } + for (tag in handlersToDetach) { + registry.detachHandler(tag) + nativeHandlers.remove(tag) + attachedHandlers.remove(tag) + } + val child = getChildAt(0) // This covers the case where `NativeViewGestureHandlers` are attached after child views were created. @@ -209,12 +199,4 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { return mappedChildren } - - companion object { - private enum class GestureHandlerMutation { - Attach, - Detach, - Keep, - } - } } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index babea038ad..74632866db 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -18,20 +18,9 @@ @interface RNGestureHandlerDetector () @end -typedef NS_ENUM(NSInteger, RNGestureHandlerMutation) { - RNGestureHandlerMutationAttach = 1, - RNGestureHandlerMutationDetach, - RNGestureHandlerMutationKeep, -}; - -struct LogicChild { - std::vector handlerTags; - NSMutableSet *attachedHandlers; -}; - @implementation RNGestureHandlerDetector { int _moduleId; - std::unordered_map logicChildren; + std::unordered_map logicChildren; } #if TARGET_OS_OSX @@ -68,7 +57,7 @@ - (void)willMoveToWindow:(RNGHWindow *)newWindow [handlerManager.registry detachHandlerWithTag:handlerTag]; } for (const auto &child : logicChildren) { - for (id handlerTag : child.second.attachedHandlers) { + for (id handlerTag : child.second) { [handlerManager.registry detachHandlerWithTag:handlerTag]; } } @@ -163,7 +152,6 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics } - (void)attachHandlers:(const std::vector &)handlerTags - oldHandlerTags:(const std::vector &)oldHandlerTags actionType:(RNGestureHandlerActionType)actionType viewTag:(const int)viewTag attachedHandlers:(NSMutableSet *)attachedHandlers @@ -171,41 +159,34 @@ - (void)attachHandlers:(const std::vector &)handlerTags RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") - std::unordered_map - changes; - for (const auto oldHandler : oldHandlerTags) { - changes[oldHandler] = RNGestureHandlerMutationDetach; - } - - for (const auto newHandler : handlerTags) { - changes[newHandler] = changes.contains(newHandler) ? RNGestureHandlerMutationKeep : RNGestureHandlerMutationAttach; - } + NSMutableSet *handlersToDetach = [attachedHandlers mutableCopy]; - for (const auto handlerChange : changes) { - NSNumber *handlerTag = [NSNumber numberWithInt:handlerChange.first]; - - if (handlerChange.second == RNGestureHandlerMutationAttach) { - if ([self shouldAttachGestureToSubview:handlerTag]) { + for (const int tag : handlerTags) { + [handlersToDetach removeObject:@(tag)]; + if (![attachedHandlers containsObject:@(tag)]) { + if ([self shouldAttachGestureToSubview:@(tag)]) { // It might happen that `attachHandlers` will be called before children are added into view hierarchy. In that // case we cannot attach `NativeViewGestureHandlers` here and we have to do it in `didAddSubview` method. - [_nativeHandlers addObject:handlerTag]; + [_nativeHandlers addObject:@(tag)]; } else { if (actionType == RNGestureHandlerActionTypeLogicDetector) { - [[[handlerManager registry] handlerWithTag:handlerTag] setParentTag:@(self.tag)]; + [[[handlerManager registry] handlerWithTag:@(tag)] setParentTag:@(self.tag)]; - [handlerManager attachGestureHandler:handlerTag toViewWithTag:@(viewTag) withActionType:actionType]; + [handlerManager attachGestureHandler:@(tag) toViewWithTag:@(viewTag) withActionType:actionType]; } else { - [handlerManager.registry attachHandlerWithTag:handlerTag toView:self withActionType:actionType]; + [handlerManager.registry attachHandlerWithTag:@(tag) toView:self withActionType:actionType]; } - [attachedHandlers addObject:handlerTag]; + [attachedHandlers addObject:@(tag)]; } - } else if (handlerChange.second == RNGestureHandlerMutationDetach) { - [handlerManager.registry detachHandlerWithTag:handlerTag]; - [attachedHandlers removeObject:handlerTag]; - [_nativeHandlers removeObject:handlerTag]; } } + for (const id tag : handlersToDetach) { + [handlerManager.registry detachHandlerWithTag:tag]; + [attachedHandlers removeObject:tag]; + [_nativeHandlers removeObject:tag]; + } + // This covers the case where `NativeViewGestureHandlers` are attached after child views were created. if (!self.subviews[0]) { [self tryAttachNativeHandlersToChildView]; @@ -219,10 +200,8 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar _moduleId = newProps.moduleId; static std::vector emptyVector; - const std::vector &oldHandlerTags = (oldPropsBase != nullptr) ? oldProps.handlerTags : emptyVector; [self attachHandlers:newProps.handlerTags - oldHandlerTags:oldHandlerTags actionType:RNGestureHandlerActionTypeNativeDetector viewTag:-1 attachedHandlers:_attachedHandlers]; @@ -232,27 +211,25 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") NSMutableSet *logicChildrenToDelete = [NSMutableSet set]; - for (const std::pair &child : logicChildren) { + for (const std::pair &child : logicChildren) { [logicChildrenToDelete addObject:@(child.first)]; } for (const RNGestureHandlerDetectorLogicChildrenStruct &child : newProps.logicChildren) { if (logicChildren.find(child.viewTag) == logicChildren.end()) { - logicChildren[child.viewTag].handlerTags = {}; - logicChildren[child.viewTag].attachedHandlers = [NSMutableSet set]; + logicChildren[child.viewTag] = [NSMutableSet set]; } [logicChildrenToDelete removeObject:@(child.viewTag)]; [self attachHandlers:child.handlerTags - oldHandlerTags:logicChildren[child.viewTag].handlerTags actionType:RNGestureHandlerActionTypeLogicDetector viewTag:child.viewTag - attachedHandlers:logicChildren[child.viewTag].attachedHandlers]; + attachedHandlers:logicChildren[child.viewTag]]; } for (const NSNumber *childTag : logicChildrenToDelete) { - for (id handlerTag : logicChildren[childTag.intValue].attachedHandlers) { + for (id handlerTag : logicChildren[childTag.intValue]) { [handlerManager.registry detachHandlerWithTag:handlerTag]; } } From dab9e5f09732f932aceb7867aba9a1549b9c230e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 10 Sep 2025 09:38:52 +0200 Subject: [PATCH 22/90] tag in callback --- .../src/v3/LogicDetector.tsx | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 20e83f42e7..f385fa3920 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { useDetectorContext } from './NativeDetector'; import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; import { findNodeHandle, Platform } from 'react-native'; @@ -21,15 +21,16 @@ export const LogicDetector = (props: NativeDetectorProps) => { onReanimatedTouchEvent: props.gesture.gestureEvents.onReanimatedTouchEvent, }); - useEffect(() => { - if (Platform.OS === 'web') { - if (viewRef.current != null) { - setViewTag(viewRef.current); - } - } else { - const tag = findNodeHandle(viewRef.current); - if (tag != null) { - setViewTag(tag); + const handleRef = useCallback((node: any) => { + viewRef.current = node; + if (node) { + if (Platform.OS === 'web') { + setViewTag(node); + } else { + const tag = findNodeHandle(node); + if (tag != null) { + setViewTag(tag); + } } } }, []); @@ -67,5 +68,5 @@ export const LogicDetector = (props: NativeDetectorProps) => { }; }, [viewTag, props.gesture.tag, register, unregister]); - return {props.children}; + return {props.children}; }; From 089bbf24cd753a07da85dacbb4a2075df29256b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 10 Sep 2025 11:10:27 +0200 Subject: [PATCH 23/90] not sharing web invoke --- .../react-native-gesture-handler/src/utils.ts | 64 ------------------- .../src/v3/NativeDetector.tsx | 3 +- .../src/v3/hooks/utils.ts | 28 ++++++++ .../src/web/handlers/GestureHandler.ts | 55 +++++++++++++++- 4 files changed, 84 insertions(+), 66 deletions(-) diff --git a/packages/react-native-gesture-handler/src/utils.ts b/packages/react-native-gesture-handler/src/utils.ts index ddd84913cb..8faf8bdf64 100644 --- a/packages/react-native-gesture-handler/src/utils.ts +++ b/packages/react-native-gesture-handler/src/utils.ts @@ -105,68 +105,4 @@ export function deepEqual(obj1: any, obj2: any) { return true; } -export function invokeNullableMethod( - method: - | ((event: any) => void) - | { __getHandler: () => (event: any) => void } - | { __nodeConfig: { argMapping: unknown[] } } - | { workletEventHandler: { worklet: (event: any) => void } } - | null - | undefined, - event: any -): void { - if (!method) { - return; - } - - if (typeof method === 'function') { - method(event); - return; - } - - if ('__getHandler' in method && typeof method.__getHandler === 'function') { - const handler = method.__getHandler(); - invokeNullableMethod(handler, event); - return; - } - - if ('workletEventHandler' in method) { - const we = method.workletEventHandler; - if ('worklet' in we) { - invokeNullableMethod(we.worklet, event); - } - return; - } - - if (!('__nodeConfig' in method)) { - return; - } - - const { argMapping }: { argMapping: unknown } = method.__nodeConfig; - if (!Array.isArray(argMapping)) { - return; - } - - for (const [index, [key, value]] of argMapping.entries()) { - if (!(key in event.nativeEvent)) { - continue; - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - const nativeValue = event.nativeEvent[key]; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (value?.setValue) { - // Reanimated API - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call - value.setValue(nativeValue); - } else { - // RN Animated API - method.__nodeConfig.argMapping[index] = [key, nativeValue]; - } - } - - return; -} - export const INT32_MAX = 2 ** 31 - 1; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index 5d11c280d5..ab939abc90 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -9,8 +9,9 @@ import React, { import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; -import { invokeNullableMethod, tagMessage } from '../utils'; +import { tagMessage } from '../utils'; import { LogicDetectorProps, LogicMethods, NativeDetectorProps } from './types'; +import { invokeNullableMethod } from './hooks/utils'; const AnimatedNativeDetector = Animated.createAnimatedComponent(HostGestureDetector); diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts index c55c121f97..7d720b0c3b 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts @@ -187,3 +187,31 @@ export function hash(str: string) { } return h >>> 0; } + +export function invokeNullableMethod( + method: + | ((event: any) => void) + | { workletEventHandler: { worklet: (event: any) => void } } + | null + | undefined, + event: any +): void { + if (!method) { + return; + } + + if (typeof method === 'function') { + method(event); + return; + } + + if ('workletEventHandler' in method) { + const we = method.workletEventHandler; + if ('worklet' in we) { + invokeNullableMethod(we.worklet, event); + } + return; + } + + return; +} diff --git a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts index d5a2701763..84996222eb 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts @@ -25,7 +25,7 @@ import { import { PointerType } from '../../PointerType'; import { GestureHandlerDelegate } from '../tools/GestureHandlerDelegate'; import { ActionType } from '../../ActionType'; -import { invokeNullableMethod, tagMessage } from '../../utils'; +import { tagMessage } from '../../utils'; import { GestureStateChangeEventWithData, GestureUpdateEventWithData, @@ -1018,3 +1018,56 @@ export default abstract class GestureHandler implements IGestureHandler { ); } } + +function invokeNullableMethod( + method: + | ((event: ResultEvent) => void) + | { __getHandler: () => (event: ResultEvent) => void } + | { __nodeConfig: { argMapping: unknown[] } }, + event: ResultEvent +): void { + if (!method) { + return; + } + + if (typeof method === 'function') { + method(event); + return; + } + + if ('__getHandler' in method && typeof method.__getHandler === 'function') { + const handler = method.__getHandler(); + invokeNullableMethod(handler, event); + return; + } + + if (!('__nodeConfig' in method)) { + return; + } + + const { argMapping }: { argMapping: unknown } = method.__nodeConfig; + if (!Array.isArray(argMapping)) { + return; + } + + for (const [index, [key, value]] of argMapping.entries()) { + if (!(key in event.nativeEvent)) { + continue; + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const nativeValue = (event.nativeEvent as any)[key]; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (value?.setValue) { + // Reanimated API + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + value.setValue(nativeValue); + } else { + // RN Animated API + method.__nodeConfig.argMapping[index] = [key, nativeValue]; + } + } + + return; +} From 5fbe993ca5e28ebb4602943197a56f10c1a5c6e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 10 Sep 2025 13:02:07 +0200 Subject: [PATCH 24/90] renamed logic props --- .../src/specs/RNGestureHandlerDetectorNativeComponent.ts | 4 ++-- .../src/v3/HostGestureDetector.web.tsx | 4 ++-- .../src/v3/NativeDetector.tsx | 8 ++++---- packages/react-native-gesture-handler/src/v3/types.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts index 355ecca7b6..b48fa90821 100644 --- a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts +++ b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts @@ -42,7 +42,7 @@ type GestureHandlerTouchEvent = Readonly<{ pointerType: Int32; }>; -export interface LogicProps { +export interface LogicChildrenProps { handlerTags: Int32[]; viewTag: Int32; } @@ -58,7 +58,7 @@ export interface NativeProps extends ViewProps { handlerTags: Int32[]; moduleId: Int32; - logicChildren: LogicProps[]; + logicChildren: LogicChildrenProps[]; } export default codegenNativeComponent('RNGestureHandlerDetector', { diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index b846ea380e..a1293d199f 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -9,10 +9,10 @@ export interface GestureHandlerDetectorProps extends PropsRef { handlerTags: number[]; moduleId: number; children?: React.ReactNode; - logicChildren?: Set; + logicChildren?: Set; } -export interface LogicDetectorProps { +export interface LogicChildrenProps { viewTag: number; handlerTags: number[]; viewRef: RefObject; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index ab939abc90..cbb057ac10 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -10,7 +10,7 @@ import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../utils'; -import { LogicDetectorProps, LogicMethods, NativeDetectorProps } from './types'; +import { LogicChildrenProps, LogicMethods, NativeDetectorProps } from './types'; import { invokeNullableMethod } from './hooks/utils'; const AnimatedNativeDetector = @@ -21,7 +21,7 @@ const ReanimatedNativeDetector = type DetectorContextType = { register: ( - child: LogicDetectorProps, + child: LogicChildrenProps, methods: RefObject ) => void; unregister: (child: number) => void; @@ -30,7 +30,7 @@ type DetectorContextType = { const DetectorContext = createContext(null); export function NativeDetector({ gesture, children }: NativeDetectorProps) { - const [logicChildren, setLogicChildren] = useState([]); + const [logicChildren, setLogicChildren] = useState([]); const logicMethods = useRef>>(new Map()); const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents @@ -41,7 +41,7 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { : HostGestureDetector; const register = useCallback( - (child: LogicDetectorProps, methods: RefObject) => { + (child: LogicChildrenProps, methods: RefObject) => { setLogicChildren((prev) => { if (prev.some((c) => c.viewTag === child.viewTag)) { return prev; diff --git a/packages/react-native-gesture-handler/src/v3/types.ts b/packages/react-native-gesture-handler/src/v3/types.ts index d16c1b6f51..bfaa2e3acd 100644 --- a/packages/react-native-gesture-handler/src/v3/types.ts +++ b/packages/react-native-gesture-handler/src/v3/types.ts @@ -77,7 +77,7 @@ export type AnimatedEvent = ((...args: any[]) => void) & { _argMapping: (Animated.Mapping | null)[]; }; -export interface LogicDetectorProps { +export interface LogicChildrenProps { viewTag: number; handlerTags: number[]; } From ddce1fbfe6ea574cccd95acdd6d70836e1210b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 11 Sep 2025 11:25:46 +0200 Subject: [PATCH 25/90] logic animated event --- .../src/v3/LogicDetector.tsx | 4 +++ .../src/v3/NativeDetector.tsx | 26 +++++++++---------- .../src/v3/types.ts | 1 + 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index f385fa3920..98f91a25eb 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -12,6 +12,8 @@ export const LogicDetector = (props: NativeDetectorProps) => { onGestureHandlerStateChange: props.gesture.gestureEvents.onGestureHandlerStateChange, onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, + onGestureHandlerAnimatedEvent: + props.gesture.gestureEvents.onGestureHandlerAnimatedEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, onReanimatedStateChange: @@ -42,6 +44,8 @@ export const LogicDetector = (props: NativeDetectorProps) => { onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, onGestureHandlerTouchEvent: props.gesture.gestureEvents.onGestureHandlerTouchEvent, + onGestureHandlerAnimatedEvent: + props.gesture.gestureEvents.onGestureHandlerAnimatedEvent, onReanimatedStateChange: props.gesture.gestureEvents.onReanimatedStateChange, onReanimatedUpdateEvent: diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index cbb057ac10..c9f134550a 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -77,30 +77,28 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { ? gesture.gestureEvents.onGestureHandlerStateChange : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onGestureHandlerStateChange; - if (method) { - method(e); - } + invokeNullableMethod(method, e); }} onGestureHandlerEvent={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) ? gesture.gestureEvents.onGestureHandlerEvent : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onGestureHandlerEvent; - if (method) { - method(e); - } + invokeNullableMethod(method, e); + }} + onGestureHandlerAnimatedEvent={(e) => { + const method = !logicMethods.current.has(e.nativeEvent.handlerTag) + ? gesture.gestureEvents.onGestureHandlerAnimatedEvent + : logicMethods.current.get(e.nativeEvent.handlerTag)?.current + ?.onGestureHandlerAnimatedEvent; + invokeNullableMethod(method, e); }} - onGestureHandlerAnimatedEvent={ - gesture.gestureEvents.onGestureHandlerAnimatedEvent - } onGestureHandlerTouchEvent={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) - ? gesture.gestureEvents.onGestureHandlerTouchEvent + ? gesture.gestureEvents.onGestureHandlerEvent : logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerTouchEvent; - if (method) { - method(e); - } + ?.onGestureHandlerEvent; + invokeNullableMethod(method, e); }} onGestureHandlerReanimatedStateChange={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) diff --git a/packages/react-native-gesture-handler/src/v3/types.ts b/packages/react-native-gesture-handler/src/v3/types.ts index bfaa2e3acd..600721424c 100644 --- a/packages/react-native-gesture-handler/src/v3/types.ts +++ b/packages/react-native-gesture-handler/src/v3/types.ts @@ -91,6 +91,7 @@ export interface LogicMethods { onGestureHandlerEvent?: (e: any) => void; onGestureHandlerStateChange?: (e: any) => void; onGestureHandlerTouchEvent?: (e: any) => void; + onGestureHandlerAnimatedEvent?: (e: any) => void; onReanimatedStateChange?: (e: any) => void; onReanimatedUpdateEvent?: (e: any) => void; onReanimatedTouchEvent?: (e: any) => void; From 50c1af4d8bfee1d836f37f228755393e15932343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 11 Sep 2025 11:38:52 +0200 Subject: [PATCH 26/90] removed child tag from web --- .../src/RNGestureHandlerModule.web.ts | 10 ++-------- .../src/web/handlers/GestureHandler.ts | 13 +------------ .../src/web/handlers/LongPressGestureHandler.ts | 5 ++--- .../src/web/handlers/NativeViewGestureHandler.ts | 5 ++--- .../src/web/handlers/PinchGestureHandler.ts | 5 ++--- .../src/web/handlers/RotationGestureHandler.ts | 5 ++--- .../src/web/interfaces.ts | 6 +----- 7 files changed, 12 insertions(+), 37 deletions(-) diff --git a/packages/react-native-gesture-handler/src/RNGestureHandlerModule.web.ts b/packages/react-native-gesture-handler/src/RNGestureHandlerModule.web.ts index 8afd46eb79..04321a7ba6 100644 --- a/packages/react-native-gesture-handler/src/RNGestureHandlerModule.web.ts +++ b/packages/react-native-gesture-handler/src/RNGestureHandlerModule.web.ts @@ -48,8 +48,7 @@ export default { // eslint-disable-next-line @typescript-eslint/no-explicit-any newView: any, actionType: ActionType, - propsRef: React.RefObject, - childTag?: number + propsRef: React.RefObject ) { if (!(newView instanceof Element || newView instanceof React.Component)) { shouldPreventDrop = true; @@ -64,12 +63,7 @@ export default { } // @ts-ignore Types should be HTMLElement or React.Component - NodeManager.getHandler(handlerTag).init( - newView, - propsRef, - actionType, - childTag - ); + NodeManager.getHandler(handlerTag).init(newView, propsRef, actionType); }, detachGestureHandler(handlerTag: number) { if (shouldPreventDrop) { diff --git a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts index 84996222eb..a509c01143 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts @@ -40,7 +40,6 @@ export default abstract class GestureHandler implements IGestureHandler { private _shouldCancelWhenOutside = false; private _enabled = false; - private childTag?: number; private viewRef: number | null = null; private propsRef: React.RefObject | null = null; private actionType: ActionType | null = null; @@ -81,14 +80,12 @@ export default abstract class GestureHandler implements IGestureHandler { protected init( viewRef: number, propsRef: React.RefObject, - actionType: ActionType, - childTag?: number + actionType: ActionType ) { this.propsRef = propsRef; this.viewRef = viewRef; this.actionType = actionType; this.state = State.UNDETERMINED; - this.childTag = childTag; this.delegate.init(viewRef, this); } @@ -103,7 +100,6 @@ export default abstract class GestureHandler implements IGestureHandler { this.viewRef = null; this.actionType = null; this.state = State.UNDETERMINED; - this.childTag = undefined; this.dispatchesAnimatedEvents = false; this.delegate.detach(); @@ -425,13 +421,6 @@ export default abstract class GestureHandler implements IGestureHandler { ? this.transformStateChangeEvent(newState, oldState) : this.transformUpdateEvent(newState); - // TODO: cleanup the logic detector types - if (this.actionType === ActionType.LOGIC_DETECTOR) { - resultEvent.nativeEvent = { - ...resultEvent.nativeEvent, - childTag: this.childTag, - }; - } // In the v2 API oldState field has to be undefined, unless we send event state changed // Here the order is flipped to avoid workarounds such as making backup of the state and setting it to undefined first, then changing it back // Flipping order with setting oldState to undefined solves issue, when events were being sent twice instead of once diff --git a/packages/react-native-gesture-handler/src/web/handlers/LongPressGestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/LongPressGestureHandler.ts index 8abfcd51ff..e3b7936d8a 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/LongPressGestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/LongPressGestureHandler.ts @@ -25,14 +25,13 @@ export default class LongPressGestureHandler extends GestureHandler { public override init( ref: number, propsRef: React.RefObject, - actionType: ActionType, - childTag?: number + actionType: ActionType ) { if (this.enableContextMenu === undefined) { this.enableContextMenu = false; } - super.init(ref, propsRef, actionType, childTag); + super.init(ref, propsRef, actionType); } protected override transformNativeEvent() { diff --git a/packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts index 30b43012e0..48784c1299 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts @@ -20,10 +20,9 @@ export default class NativeViewGestureHandler extends GestureHandler { public override init( ref: number, propsRef: React.RefObject, - actionType: ActionType, - childTag?: number + actionType: ActionType ): void { - super.init(ref, propsRef, actionType, childTag); + super.init(ref, propsRef, actionType); this.shouldCancelWhenOutside = true; diff --git a/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts index 71a3401888..3461ec2377 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts @@ -52,10 +52,9 @@ export default class PinchGestureHandler extends GestureHandler { public override init( ref: number, propsRef: React.RefObject, - actionType: ActionType, - childTag?: number + actionType: ActionType ) { - super.init(ref, propsRef, actionType, childTag); + super.init(ref, propsRef, actionType); this.shouldCancelWhenOutside = false; } diff --git a/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts index 193e7a4416..795c31dd3a 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts @@ -48,10 +48,9 @@ export default class RotationGestureHandler extends GestureHandler { public override init( ref: number, propsRef: React.RefObject, - actionType: ActionType, - childTag?: number + actionType: ActionType ): void { - super.init(ref, propsRef, actionType, childTag); + super.init(ref, propsRef, actionType); this.shouldCancelWhenOutside = false; } diff --git a/packages/react-native-gesture-handler/src/web/interfaces.ts b/packages/react-native-gesture-handler/src/web/interfaces.ts index e358c946ce..49d0d81c76 100644 --- a/packages/react-native-gesture-handler/src/web/interfaces.ts +++ b/packages/react-native-gesture-handler/src/web/interfaces.ts @@ -121,11 +121,7 @@ type ResultEventType = | GestureTouchEvent | GestureHandlerNativeEvent; -type LogicResultEventType = ResultEventType & { - childTag?: number; -}; - -export interface ResultEvent +export interface ResultEvent extends Record { nativeEvent: T; timeStamp: number; From 5f3f1ebab9b24730a952017b575ddd628fc11887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 11 Sep 2025 14:29:37 +0200 Subject: [PATCH 27/90] safe detaching --- .../src/v3/HostGestureDetector.web.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index a1293d199f..25550f7b59 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -35,8 +35,15 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { oldHandlerTags.forEach((tag) => { RNGestureHandlerModule.detachGestureHandler(tag); attachedNativeHandlerTags.current.delete(tag); - attachedHandlerTags.delete(tag); }); + + if (oldHandlerTags === attachedHandlerTags) { + attachedHandlerTags.clear(); + } else { + for (const tag of oldHandlerTags) { + oldHandlerTags.delete(tag); + } + } }; const attachHandlers = ( From 1f22ef4cac63e19c86768703d64618017e2da733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 11 Sep 2025 14:33:58 +0200 Subject: [PATCH 28/90] typo fix --- .../src/v3/HostGestureDetector.web.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index 25550f7b59..8a928a44b5 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -41,7 +41,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { attachedHandlerTags.clear(); } else { for (const tag of oldHandlerTags) { - oldHandlerTags.delete(tag); + attachedHandlerTags.delete(tag); } } }; From 3e7bc43c755929c92a167bdf6051697f4c89bc2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 11 Sep 2025 14:40:51 +0200 Subject: [PATCH 29/90] more typos --- .../react-native-gesture-handler/src/v3/NativeDetector.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index c9f134550a..28c16aa56c 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -95,9 +95,9 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { }} onGestureHandlerTouchEvent={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) - ? gesture.gestureEvents.onGestureHandlerEvent + ? gesture.gestureEvents.onGestureHandlerTouchEvent : logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerEvent; + ?.onGestureHandlerTouchEvent; invokeNullableMethod(method, e); }} onGestureHandlerReanimatedStateChange={(e) => { From cd4d1581f266ece1c019a69a2ecb1bd7530ee756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 07:29:18 +0200 Subject: [PATCH 30/90] comment styling --- .../java/com/swmansion/gesturehandler/core/GestureHandler.kt | 2 +- .../gesturehandler/react/RNGestureHandlerDetectorView.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt index 76930c0fb4..a6ae47a3f7 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt @@ -32,7 +32,7 @@ open class GestureHandler { var view: View? = null private set - // parent view is used for logicDetector + // Parent view is used for Logic Detector var parentView: RNGestureHandlerDetectorView? = null val viewForEvents: RNGestureHandlerDetectorView diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index 9a2446cceb..9ec3da6006 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -100,7 +100,6 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { for (tag in newHandlers) { handlersToDetach.remove(tag) if (!attachedHandlers.contains(tag)) { - // attach handler if (shouldAttachGestureToChildView(tag)) { // It might happen that `attachHandlers` will be called before children are added into view hierarchy. In that case we cannot // attach `NativeViewGestureHandlers` here and we have to do it in `addView` method. From 164c4e8b08f91145341c7e54150451a4704ea571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 07:36:03 +0200 Subject: [PATCH 31/90] moved detector context --- .../src/v3/LogicDetector.tsx | 2 +- .../src/v3/NativeDetector.tsx | 28 ++----------------- .../src/v3/useDetectorContext.ts | 20 +++++++++++++ 3 files changed, 23 insertions(+), 27 deletions(-) create mode 100644 packages/react-native-gesture-handler/src/v3/useDetectorContext.ts diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 98f91a25eb..4409d98d80 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -1,8 +1,8 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import { useDetectorContext } from './NativeDetector'; import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; import { findNodeHandle, Platform } from 'react-native'; import { NativeDetectorProps } from './types'; +import useDetectorContext from './useDetectorContext'; export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index 28c16aa56c..be38b1c7a7 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -1,17 +1,11 @@ -import React, { - createContext, - RefObject, - useCallback, - useContext, - useRef, - useState, -} from 'react'; +import React, { RefObject, useCallback, useRef, useState } from 'react'; import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../utils'; import { LogicChildrenProps, LogicMethods, NativeDetectorProps } from './types'; import { invokeNullableMethod } from './hooks/utils'; +import { DetectorContext } from './useDetectorContext'; const AnimatedNativeDetector = Animated.createAnimatedComponent(HostGestureDetector); @@ -19,16 +13,6 @@ const AnimatedNativeDetector = const ReanimatedNativeDetector = Reanimated?.default.createAnimatedComponent(HostGestureDetector); -type DetectorContextType = { - register: ( - child: LogicChildrenProps, - methods: RefObject - ) => void; - unregister: (child: number) => void; -}; - -const DetectorContext = createContext(null); - export function NativeDetector({ gesture, children }: NativeDetectorProps) { const [logicChildren, setLogicChildren] = useState([]); const logicMethods = useRef>>(new Map()); @@ -131,14 +115,6 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { ); } -export function useDetectorContext() { - const ctx = useContext(DetectorContext); - if (!ctx) { - throw new Error('Logic detector must be under a Native Detector'); - } - return ctx; -} - const styles = StyleSheet.create({ detector: { display: 'contents', diff --git a/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts b/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts new file mode 100644 index 0000000000..ac6f7cfa3d --- /dev/null +++ b/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts @@ -0,0 +1,20 @@ +import { createContext, RefObject, useContext } from 'react'; +import { LogicChildrenProps, LogicMethods } from './types'; + +type DetectorContextType = { + register: ( + child: LogicChildrenProps, + methods: RefObject + ) => void; + unregister: (child: number) => void; +}; + +export const DetectorContext = createContext(null); + +export default function useDetectorContext() { + const ctx = useContext(DetectorContext); + if (!ctx) { + throw new Error('Logic detector must be under a Native Detector'); + } + return ctx; +} From 84bdf3d0689bddbf30827a59deae03db51a3edd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 07:39:40 +0200 Subject: [PATCH 32/90] renamed helper function --- .../src/v3/NativeDetector.tsx | 16 ++++++++-------- .../src/v3/hooks/utils.ts | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index be38b1c7a7..fbd3d5fcd8 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -4,7 +4,7 @@ import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../utils'; import { LogicChildrenProps, LogicMethods, NativeDetectorProps } from './types'; -import { invokeNullableMethod } from './hooks/utils'; +import { invokeDetectorEvent } from './hooks/utils'; import { DetectorContext } from './useDetectorContext'; const AnimatedNativeDetector = @@ -61,49 +61,49 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { ? gesture.gestureEvents.onGestureHandlerStateChange : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onGestureHandlerStateChange; - invokeNullableMethod(method, e); + invokeDetectorEvent(method, e); }} onGestureHandlerEvent={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) ? gesture.gestureEvents.onGestureHandlerEvent : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onGestureHandlerEvent; - invokeNullableMethod(method, e); + invokeDetectorEvent(method, e); }} onGestureHandlerAnimatedEvent={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) ? gesture.gestureEvents.onGestureHandlerAnimatedEvent : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onGestureHandlerAnimatedEvent; - invokeNullableMethod(method, e); + invokeDetectorEvent(method, e); }} onGestureHandlerTouchEvent={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) ? gesture.gestureEvents.onGestureHandlerTouchEvent : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onGestureHandlerTouchEvent; - invokeNullableMethod(method, e); + invokeDetectorEvent(method, e); }} onGestureHandlerReanimatedStateChange={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) ? gesture.gestureEvents.onReanimatedStateChange : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onReanimatedStateChange; - invokeNullableMethod(method, e); + invokeDetectorEvent(method, e); }} onGestureHandlerReanimatedEvent={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) ? gesture.gestureEvents.onReanimatedUpdateEvent : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onReanimatedUpdateEvent; - invokeNullableMethod(method, e); + invokeDetectorEvent(method, e); }} onGestureHandlerReanimatedTouchEvent={(e) => { const method = !logicMethods.current.has(e.nativeEvent.handlerTag) ? gesture.gestureEvents.onReanimatedTouchEvent : logicMethods.current.get(e.nativeEvent.handlerTag)?.current ?.onReanimatedTouchEvent; - invokeNullableMethod(method, e); + invokeDetectorEvent(method, e); }} moduleId={globalThis._RNGH_MODULE_ID} handlerTags={[gesture.tag]} diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts index 7d720b0c3b..0bb9b29222 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts @@ -188,7 +188,7 @@ export function hash(str: string) { return h >>> 0; } -export function invokeNullableMethod( +export function invokeDetectorEvent( method: | ((event: any) => void) | { workletEventHandler: { worklet: (event: any) => void } } @@ -208,7 +208,7 @@ export function invokeNullableMethod( if ('workletEventHandler' in method) { const we = method.workletEventHandler; if ('worklet' in we) { - invokeNullableMethod(we.worklet, event); + invokeDetectorEvent(we.worklet, event); } return; } From 4365e3ec32ed9e158b0d734802c534d3f375e4b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 08:02:36 +0200 Subject: [PATCH 33/90] explained invoke method --- packages/react-native-gesture-handler/src/v3/hooks/utils.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts index 0bb9b29222..dfce72d9c7 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts @@ -188,6 +188,11 @@ export function hash(str: string) { return h >>> 0; } +// Some event handlers are plain functions, whereas those marked as worklets +// are wrapped in objects under eventMethod.workletEventHandler.worklet. +// This function normalises invocation so that both forms can be called safely. +// Note: this worklet unpacking is essentially a workaround since we need to +// decide on the JS side which handle logic to execute. export function invokeDetectorEvent( method: | ((event: any) => void) From 33fe81834eda4ec19c1eb0c2c785e297ab123612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 08:14:35 +0200 Subject: [PATCH 34/90] isV3Api helper function --- .../src/ActionType.ts | 7 ++++++ .../src/web/handlers/GestureHandler.ts | 24 +++++++------------ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/react-native-gesture-handler/src/ActionType.ts b/packages/react-native-gesture-handler/src/ActionType.ts index 81f2a3563d..c787e6ee65 100644 --- a/packages/react-native-gesture-handler/src/ActionType.ts +++ b/packages/react-native-gesture-handler/src/ActionType.ts @@ -9,3 +9,10 @@ export const ActionType = { // eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; it can be used as a type and as a value export type ActionType = (typeof ActionType)[keyof typeof ActionType]; + +export function isV3Api(actionType: ActionType | null): boolean { + return ( + actionType === ActionType.NATIVE_DETECTOR || + actionType === ActionType.LOGIC_DETECTOR + ); +} diff --git a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts index a509c01143..ed2a946e53 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts @@ -24,7 +24,7 @@ import { } from '../../handlers/gestureHandlerCommon'; import { PointerType } from '../../PointerType'; import { GestureHandlerDelegate } from '../tools/GestureHandlerDelegate'; -import { ActionType } from '../../ActionType'; +import { ActionType, isV3Api } from '../../ActionType'; import { tagMessage } from '../../utils'; import { GestureStateChangeEventWithData, @@ -391,10 +391,7 @@ export default abstract class GestureHandler implements IGestureHandler { this.transformTouchEvent(event); if (touchEvent) { - if ( - onGestureHandlerTouchEvent && - this.actionType === ActionType.NATIVE_DETECTOR - ) { + if (onGestureHandlerTouchEvent && isV3Api(this.actionType)) { invokeNullableMethod(onGestureHandlerTouchEvent, touchEvent); } else { invokeNullableMethod(onGestureHandlerEvent, touchEvent); @@ -413,13 +410,11 @@ export default abstract class GestureHandler implements IGestureHandler { onGestureHandlerAnimatedEvent, }: PropsRef = this.propsRef!.current; - const resultEvent: ResultEvent = - this.actionType !== ActionType.NATIVE_DETECTOR && - this.actionType !== ActionType.LOGIC_DETECTOR - ? this.transformEventData(newState, oldState) - : this.lastSentState !== newState - ? this.transformStateChangeEvent(newState, oldState) - : this.transformUpdateEvent(newState); + const resultEvent: ResultEvent = !isV3Api(this.actionType) + ? this.transformEventData(newState, oldState) + : this.lastSentState !== newState + ? this.transformStateChangeEvent(newState, oldState) + : this.transformUpdateEvent(newState); // In the v2 API oldState field has to be undefined, unless we send event state changed // Here the order is flipped to avoid workarounds such as making backup of the state and setting it to undefined first, then changing it back @@ -430,10 +425,7 @@ export default abstract class GestureHandler implements IGestureHandler { invokeNullableMethod(onGestureHandlerStateChange, resultEvent); } if (this.state === State.ACTIVE) { - if ( - this.actionType !== ActionType.NATIVE_DETECTOR && - this.actionType !== ActionType.LOGIC_DETECTOR - ) { + if (!isV3Api(this.actionType)) { (resultEvent.nativeEvent as GestureHandlerNativeEvent).oldState = undefined; } From 6c42eae4113d7e113ea4f5903cee567288cace82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 08:30:00 +0200 Subject: [PATCH 35/90] renamed logic children --- .../react/RNGestureHandlerDetectorView.kt | 10 +++++----- .../src/v3/NativeDetector.tsx | 6 +++--- packages/react-native-gesture-handler/src/v3/types.ts | 2 +- .../src/v3/useDetectorContext.ts | 7 ++----- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index 9ec3da6006..051cfe49fc 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -18,7 +18,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { private var moduleId: Int = -1 private var logicChildren: MutableMap> = mutableMapOf() - data class LogicProps(val handlerTags: List, val viewTag: Int) + data class LogicChildren(val handlerTags: List, val viewTag: Int) fun setHandlerTags(handlerTags: ReadableArray?) { val newHandlers = handlerTags?.toArrayList()?.map { (it as Double).toInt() } ?: emptyList() @@ -43,7 +43,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { fun setLogicChildren(newLogicChildren: ReadableArray?) { val logicChildrenToDelete = logicChildren.keys.toMutableSet() - val mappedChildren = newLogicChildren?.mapLogicProps().orEmpty() + val mappedChildren = newLogicChildren?.mapLogicChildren().orEmpty() for (child in mappedChildren) { if (!logicChildren.containsKey(child.viewTag)) { @@ -171,8 +171,8 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } } - private fun ReadableArray.mapLogicProps(): List { - val mappedChildren = mutableListOf() + private fun ReadableArray.mapLogicChildren(): List { + val mappedChildren = mutableListOf() for (i in 0 until this.size()) { val child = this.getMap(i) ?: continue @@ -189,7 +189,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { val viewTag = child.getInt("viewTag") mappedChildren.add( - LogicProps( + LogicChildren( handlerTags = handlerTags, viewTag = viewTag, ), diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx index fbd3d5fcd8..3af7bad22e 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector.tsx @@ -3,7 +3,7 @@ import { Reanimated } from '../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../utils'; -import { LogicChildrenProps, LogicMethods, NativeDetectorProps } from './types'; +import { LogicChildren, LogicMethods, NativeDetectorProps } from './types'; import { invokeDetectorEvent } from './hooks/utils'; import { DetectorContext } from './useDetectorContext'; @@ -14,7 +14,7 @@ const ReanimatedNativeDetector = Reanimated?.default.createAnimatedComponent(HostGestureDetector); export function NativeDetector({ gesture, children }: NativeDetectorProps) { - const [logicChildren, setLogicChildren] = useState([]); + const [logicChildren, setLogicChildren] = useState([]); const logicMethods = useRef>>(new Map()); const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents @@ -25,7 +25,7 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { : HostGestureDetector; const register = useCallback( - (child: LogicChildrenProps, methods: RefObject) => { + (child: LogicChildren, methods: RefObject) => { setLogicChildren((prev) => { if (prev.some((c) => c.viewTag === child.viewTag)) { return prev; diff --git a/packages/react-native-gesture-handler/src/v3/types.ts b/packages/react-native-gesture-handler/src/v3/types.ts index 600721424c..09fb1529ef 100644 --- a/packages/react-native-gesture-handler/src/v3/types.ts +++ b/packages/react-native-gesture-handler/src/v3/types.ts @@ -77,7 +77,7 @@ export type AnimatedEvent = ((...args: any[]) => void) & { _argMapping: (Animated.Mapping | null)[]; }; -export interface LogicChildrenProps { +export interface LogicChildren { viewTag: number; handlerTags: number[]; } diff --git a/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts b/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts index ac6f7cfa3d..38f9144d2f 100644 --- a/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts +++ b/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts @@ -1,11 +1,8 @@ import { createContext, RefObject, useContext } from 'react'; -import { LogicChildrenProps, LogicMethods } from './types'; +import { LogicChildren, LogicMethods } from './types'; type DetectorContextType = { - register: ( - child: LogicChildrenProps, - methods: RefObject - ) => void; + register: (child: LogicChildren, methods: RefObject) => void; unregister: (child: number) => void; }; From a11b6d306051103bc0b1dc95f0cb6ff18cb56a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 09:32:04 +0200 Subject: [PATCH 36/90] renamed var containing logic handlers --- .../react/RNGestureHandlerDetectorView.kt | 14 +++---- .../apple/RNGestureHandlerDetector.mm | 16 ++++---- .../src/v3/HostGestureDetector.web.tsx | 38 +++++++++---------- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index 051cfe49fc..d3d3ce071b 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -15,8 +15,8 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { private var handlersToAttach: List? = null private var nativeHandlers: MutableSet = mutableSetOf() private var attachedHandlers: MutableSet = mutableSetOf() + private var attachedLogicHandlers: MutableMap> = mutableMapOf() private var moduleId: Int = -1 - private var logicChildren: MutableMap> = mutableMapOf() data class LogicChildren(val handlerTags: List, val viewTag: Int) @@ -41,20 +41,20 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } fun setLogicChildren(newLogicChildren: ReadableArray?) { - val logicChildrenToDelete = logicChildren.keys.toMutableSet() + val logicChildrenToDelete = attachedLogicHandlers.keys.toMutableSet() val mappedChildren = newLogicChildren?.mapLogicChildren().orEmpty() for (child in mappedChildren) { - if (!logicChildren.containsKey(child.viewTag)) { - logicChildren.put(child.viewTag, mutableSetOf()) + if (!attachedLogicHandlers.containsKey(child.viewTag)) { + attachedLogicHandlers.put(child.viewTag, mutableSetOf()) } logicChildrenToDelete.remove(child.viewTag) attachHandlers( child.handlerTags, child.viewTag, GestureHandler.ACTION_TYPE_LOGIC_DETECTOR, - logicChildren[child.viewTag]!!, + attachedLogicHandlers[child.viewTag]!!, ) } @@ -62,7 +62,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { val registry = RNGestureHandlerModule.registries[moduleId] ?: throw Exception("Tried to access a non-existent registry") registry.detachHandler(childTag) - logicChildren.remove(childTag) + attachedLogicHandlers.remove(childTag) } } @@ -163,7 +163,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { attachedHandlers.remove(tag) } - for (child in logicChildren) { + for (child in attachedLogicHandlers) { for (tag in child.value) { registry.detachHandler(tag) } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index 74632866db..eee71f129a 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -15,12 +15,12 @@ @interface RNGestureHandlerDetector () @property (nonatomic, nonnull) NSMutableSet *nativeHandlers; @property (nonatomic, nonnull) NSMutableSet *attachedHandlers; +@property (nonatomic) std::unordered_map attachedLogicHandlers; @end @implementation RNGestureHandlerDetector { int _moduleId; - std::unordered_map logicChildren; } #if TARGET_OS_OSX @@ -56,12 +56,12 @@ - (void)willMoveToWindow:(RNGHWindow *)newWindow NSNumber *handlerTag = [NSNumber numberWithInt:handler]; [handlerManager.registry detachHandlerWithTag:handlerTag]; } - for (const auto &child : logicChildren) { + for (const auto &child : _attachedLogicHandlers) { for (id handlerTag : child.second) { [handlerManager.registry detachHandlerWithTag:handlerTag]; } } - logicChildren.clear(); + _attachedLogicHandlers.clear(); } } @@ -211,13 +211,13 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") NSMutableSet *logicChildrenToDelete = [NSMutableSet set]; - for (const std::pair &child : logicChildren) { + for (const std::pair &child : _attachedLogicHandlers) { [logicChildrenToDelete addObject:@(child.first)]; } for (const RNGestureHandlerDetectorLogicChildrenStruct &child : newProps.logicChildren) { - if (logicChildren.find(child.viewTag) == logicChildren.end()) { - logicChildren[child.viewTag] = [NSMutableSet set]; + if (_attachedLogicHandlers.find(child.viewTag) == _attachedLogicHandlers.end()) { + _attachedLogicHandlers[child.viewTag] = [NSMutableSet set]; } [logicChildrenToDelete removeObject:@(child.viewTag)]; @@ -225,11 +225,11 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar [self attachHandlers:child.handlerTags actionType:RNGestureHandlerActionTypeLogicDetector viewTag:child.viewTag - attachedHandlers:logicChildren[child.viewTag]]; + attachedHandlers:_attachedLogicHandlers[child.viewTag]]; } for (const NSNumber *childTag : logicChildrenToDelete) { - for (id handlerTag : logicChildren[childTag.intValue]) { + for (id handlerTag : _attachedLogicHandlers[childTag.intValue]) { [handlerManager.registry detachHandlerWithTag:handlerTag]; } } diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index 8a928a44b5..03e131a766 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -23,10 +23,9 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { const viewRef = useRef(null); const propsRef = useRef(props); - const attachedHandlerTags = useRef>(new Set()); - const attachedNativeHandlerTags = useRef>(new Set()); - - const logicChildren = useRef>>(new Map()); + const attachedHandlers = useRef>(new Set()); + const attachedNativeHandlers = useRef>(new Set()); + const attachedLogicHandlers = useRef>>(new Map()); const detachHandlers = ( oldHandlerTags: Set, @@ -34,7 +33,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { ) => { oldHandlerTags.forEach((tag) => { RNGestureHandlerModule.detachGestureHandler(tag); - attachedNativeHandlerTags.current.delete(tag); + attachedNativeHandlers.current.delete(tag); }); if (oldHandlerTags === attachedHandlerTags) { @@ -69,7 +68,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { actionType, propsRef ); - attachedNativeHandlerTags.current.add(tag); + attachedNativeHandlers.current.add(tag); } else { RNGestureHandlerModule.attachGestureHandler( tag, @@ -83,10 +82,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }; useEffect(() => { - detachHandlers( - attachedNativeHandlerTags.current, - attachedHandlerTags.current - ); + detachHandlers(attachedNativeHandlers.current, attachedHandlers.current); }, [children]); useEffect(() => { @@ -100,7 +96,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { viewRef, propsRef, new Set(handlerTags), - attachedHandlerTags.current, + attachedHandlers.current, ActionType.NATIVE_DETECTOR ); }, [handlerTags, children]); @@ -108,39 +104,39 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { useEffect(() => { const logicChildrenToDelete: Set = new Set(); - for (const key of logicChildren.current.keys()) { + for (const key of attachedLogicHandlers.current.keys()) { logicChildrenToDelete.add(key); } props.logicChildren?.forEach((child) => { - if (!logicChildren.current.has(child.viewTag)) { - logicChildren.current.set(child.viewTag, new Set()); + if (!attachedLogicHandlers.current.has(child.viewTag)) { + attachedLogicHandlers.current.set(child.viewTag, new Set()); } logicChildrenToDelete.delete(child.viewTag); attachHandlers( child.viewRef, propsRef, new Set(child.handlerTags), - logicChildren.current.get(child.viewTag)!, + attachedLogicHandlers.current.get(child.viewTag)!, ActionType.LOGIC_DETECTOR ); }); logicChildrenToDelete.forEach((childTag) => { - if (attachedHandlerTags && attachedNativeHandlerTags) { + if (attachedHandlers && attachedNativeHandlers) { detachHandlers( - logicChildren.current.get(childTag)!, - logicChildren.current.get(childTag)! + attachedLogicHandlers.current.get(childTag)!, + attachedLogicHandlers.current.get(childTag)! ); } - logicChildren.current.delete(childTag); + attachedLogicHandlers.current.delete(childTag); }); }, [props.logicChildren]); useEffect(() => { return () => { - detachHandlers(attachedHandlerTags.current, attachedHandlerTags.current); - logicChildren?.current.forEach((child) => { + detachHandlers(attachedHandlers.current, attachedHandlers.current); + attachedLogicHandlers?.current.forEach((child) => { detachHandlers(child, child); }); }; From a70f8689ab212891eaf1ced8b0f1243d7216a698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 10:10:31 +0200 Subject: [PATCH 37/90] action type helper on android --- .../com/swmansion/gesturehandler/core/GestureHandler.kt | 6 +++++- .../gesturehandler/react/events/RNGestureHandlerEvent.kt | 4 ++-- .../react/events/RNGestureHandlerStateChangeEvent.kt | 4 ++-- .../react/events/RNGestureHandlerTouchEvent.kt | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt index a6ae47a3f7..ff62c431ef 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt @@ -37,7 +37,7 @@ open class GestureHandler { val viewForEvents: RNGestureHandlerDetectorView get() { - assert(actionType == ACTION_TYPE_NATIVE_DETECTOR || actionType == ACTION_TYPE_LOGIC_DETECTOR) { + assert(isV3Api(actionType)) { "[react-native-gesture-handler] `viewForEvents` can only be used with NativeDetector." } @@ -1045,6 +1045,10 @@ open class GestureHandler { } return null } + + @JvmStatic + fun isV3Api(actionType: Int): Boolean = + actionType == ACTION_TYPE_NATIVE_DETECTOR || actionType == ACTION_TYPE_LOGIC_DETECTOR } private data class PointerData( diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt index 8bf57f0b57..fa9fbf1818 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt @@ -47,7 +47,7 @@ class RNGestureHandlerEvent private constructor() : Event EVENTS_POOL.release(this) } - override fun getEventName() = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) { + override fun getEventName() = if (GestureHandler.isV3Api(actionType)) { if (eventHandlerType == EventHandlerType.ForAnimated) { NATIVE_DETECTOR_ANIMATED_EVENT_NAME } else if (eventHandlerType == EventHandlerType.ForReanimated) { @@ -65,7 +65,7 @@ class RNGestureHandlerEvent private constructor() : Event override fun getCoalescingKey() = coalescingKey - override fun getEventData(): WritableMap = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) { + override fun getEventData(): WritableMap = if (GestureHandler.isV3Api(actionType)) { createNativeEventData(dataBuilder!!) } else { createEventData(dataBuilder!!) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt index be66ac00c0..ff15423a6e 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt @@ -53,7 +53,7 @@ class RNGestureHandlerStateChangeEvent private constructor() : Event Date: Fri, 12 Sep 2025 10:29:07 +0200 Subject: [PATCH 38/90] simplify logic children to delete creation --- .../src/v3/HostGestureDetector.web.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index 03e131a766..7c5a3d02ff 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -102,11 +102,9 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }, [handlerTags, children]); useEffect(() => { - const logicChildrenToDelete: Set = new Set(); - - for (const key of attachedLogicHandlers.current.keys()) { - logicChildrenToDelete.add(key); - } + const logicChildrenToDelete: Set = new Set( + attachedLogicHandlers.current.keys() + ); props.logicChildren?.forEach((child) => { if (!attachedLogicHandlers.current.has(child.viewTag)) { From a2ee13de4bf781ae80d98f60030ba8a8fbde7df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 10:31:29 +0200 Subject: [PATCH 39/90] removed redundant check --- .../src/v3/HostGestureDetector.web.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index 7c5a3d02ff..4dff028589 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -121,12 +121,10 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }); logicChildrenToDelete.forEach((childTag) => { - if (attachedHandlers && attachedNativeHandlers) { - detachHandlers( - attachedLogicHandlers.current.get(childTag)!, - attachedLogicHandlers.current.get(childTag)! - ); - } + detachHandlers( + attachedLogicHandlers.current.get(childTag)!, + attachedLogicHandlers.current.get(childTag)! + ); attachedLogicHandlers.current.delete(childTag); }); }, [props.logicChildren]); From f0f24b87c5d425c139105cc46c7c1976af1babdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 11:46:36 +0200 Subject: [PATCH 40/90] renamed parent tag to host detector tag --- .../gesturehandler/core/GestureHandler.kt | 15 ++++- .../react/RNGestureHandlerDetectorView.kt | 2 +- .../react/events/RNGestureHandlerEvent.kt | 4 +- .../events/RNGestureHandlerEventDispatcher.kt | 58 ++----------------- .../RNGestureHandlerStateChangeEvent.kt | 4 +- .../events/RNGestureHandlerTouchEvent.kt | 4 +- .../apple/RNGestureHandler.h | 4 +- .../apple/RNGestureHandler.mm | 10 ++-- .../apple/RNGestureHandlerDetector.mm | 2 +- .../apple/RNGestureHandlerManager.mm | 4 +- 10 files changed, 32 insertions(+), 75 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt index ff62c431ef..26b0486f67 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt @@ -32,8 +32,9 @@ open class GestureHandler { var view: View? = null private set - // Parent view is used for Logic Detector - var parentView: RNGestureHandlerDetectorView? = null + // Host detector view is a reference to a Native Detector designated to handle events from a + // Logic Detector to which the gesture is assigned. + var hostDetectorView: RNGestureHandlerDetectorView? = null val viewForEvents: RNGestureHandlerDetectorView get() { @@ -41,7 +42,15 @@ open class GestureHandler { "[react-native-gesture-handler] `viewForEvents` can only be used with NativeDetector." } - val detector = if (this is NativeViewGestureHandler) this.view?.parent else view + val detector = if (actionType == + ACTION_TYPE_LOGIC_DETECTOR + ) { + this.hostDetectorView + } else if (this is NativeViewGestureHandler) { + this.view?.parent + } else { + view + } if (detector !is RNGestureHandlerDetectorView) { throw Error( diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index d3d3ce071b..d971574bf2 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -107,7 +107,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } else { registry.attachHandlerToView(tag, viewTag, actionType) if (actionType == GestureHandler.ACTION_TYPE_LOGIC_DETECTOR) { - registry.getHandler(tag)?.parentView = this + registry.getHandler(tag)?.hostDetectorView = this } attachedHandlers.add(tag) } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt index fa9fbf1818..8ab1431eb5 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt @@ -26,10 +26,8 @@ class RNGestureHandlerEvent private constructor() : Event dataBuilder: GestureHandlerEventDataBuilder, eventHandlerType: EventHandlerType, ) { - val view = if (handler.actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) { + val view = if (GestureHandler.isV3Api(handler.actionType)) { handler.viewForEvents - } else if (handler.actionType == GestureHandler.ACTION_TYPE_LOGIC_DETECTOR) { - handler.parentView!! } else { handler.view!! } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt index 45a76d59cc..5d93c01773 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt @@ -71,7 +71,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React RNGestureHandlerEvent.createEventData(handlerFactory.createEventBuilder(handler)) sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data) } - GestureHandler.ACTION_TYPE_NATIVE_DETECTOR -> { + GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { val eventHandlerType = if (handler.dispatchesAnimatedEvents) { EventHandlerType.ForAnimated } else if (handler.dispatchesReanimatedEvents) { @@ -86,24 +86,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React eventHandlerType, ) - handler.viewForEvents!!.dispatchEvent(event) - } - GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { - val eventHandlerType = if (handler.dispatchesAnimatedEvents) { - EventHandlerType.ForAnimated - } else if (handler.dispatchesReanimatedEvents) { - EventHandlerType.ForReanimated - } else { - EventHandlerType.ForJS - } - val event = RNGestureHandlerEvent.obtain( - handler, - handler.actionType, - handlerFactory.createEventBuilder(handler), - eventHandlerType, - ) - - handler.parentView?.dispatchEvent(event) + handler.viewForEvents.dispatchEvent(event) } } } @@ -152,7 +135,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React sendEventForDeviceEvent(RNGestureHandlerStateChangeEvent.EVENT_NAME, data) } - GestureHandler.ACTION_TYPE_NATIVE_DETECTOR -> { + GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { val eventHandlerType = if (handler.dispatchesReanimatedEvents) { EventHandlerType.ForReanimated } else { @@ -168,25 +151,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React eventHandlerType, ) - handler.viewForEvents!!.dispatchEvent(event) - } - - GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { - val eventHandlerType = if (handler.dispatchesReanimatedEvents) { - EventHandlerType.ForReanimated - } else { - EventHandlerType.ForJS - } - - val event = RNGestureHandlerStateChangeEvent.obtain( - handler, - newState, - oldState, - handler.actionType, - handlerFactory.createEventBuilder(handler), - eventHandlerType, - ) - handler.parentView?.dispatchEvent(event) + handler.viewForEvents.dispatchEvent(event) } } } @@ -222,26 +187,15 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React val data = RNGestureHandlerTouchEvent.createEventData(handler) sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data) } - GestureHandler.ACTION_TYPE_NATIVE_DETECTOR -> { - val eventHandlerType = if (handler.dispatchesReanimatedEvents) { - EventHandlerType.ForReanimated - } else { - EventHandlerType.ForJS - } - val event = RNGestureHandlerTouchEvent.obtain(handler, handler.actionType, eventHandlerType) - - handler.viewForEvents!!.dispatchEvent(event) - } - GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { + GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_LOGIC_DETECTOR -> { val eventHandlerType = if (handler.dispatchesReanimatedEvents) { EventHandlerType.ForReanimated } else { EventHandlerType.ForJS } - val event = RNGestureHandlerTouchEvent.obtain(handler, handler.actionType, eventHandlerType) - handler.parentView?.dispatchEvent(event) + handler.viewForEvents.dispatchEvent(event) } } } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt index ff15423a6e..9a36126b62 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt @@ -29,10 +29,8 @@ class RNGestureHandlerStateChangeEvent private constructor() : Event, eventHandlerType: EventHandlerType, ) { - val view = if (handler.actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) { + val view = if (GestureHandler.isV3Api(handler.actionType)) { handler.viewForEvents - } else if (handler.actionType == GestureHandler.ACTION_TYPE_LOGIC_DETECTOR) { - handler.parentView!! } else { handler.view!! } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerTouchEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerTouchEvent.kt index dbcfc72350..9e8e65fed7 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerTouchEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerTouchEvent.kt @@ -14,10 +14,8 @@ class RNGestureHandlerTouchEvent private constructor() : Event init(handler: T, actionType: Int, eventHandlerType: EventHandlerType) { - val view = if (handler.actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) { + val view = if (GestureHandler.isV3Api(handler.actionType)) { handler.viewForEvents - } else if (handler.actionType == GestureHandler.ACTION_TYPE_LOGIC_DETECTOR) { - handler.parentView!! } else { handler.view!! } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.h b/packages/react-native-gesture-handler/apple/RNGestureHandler.h index bbb3076a06..4455d84299 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandler.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandler.h @@ -106,8 +106,8 @@ - (nonnull RNGHUIView *)findViewForEvents; - (BOOL)wantsToAttachDirectlyToView; -- (void)setParentTag:(nonnull NSNumber *)parentTag; -- (nonnull NSNumber *)getParentTag; +- (void)setHostDetectorTag:(nonnull NSNumber *)hostDetectorTag; +- (nonnull NSNumber *)getHostDetectorTag; #if !TARGET_OS_OSX - (BOOL)isUIScrollViewPanGestureRecognizer:(nonnull UIGestureRecognizer *)gestureRecognizer; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.mm b/packages/react-native-gesture-handler/apple/RNGestureHandler.mm index 5558b7a915..ac686c0446 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandler.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandler.mm @@ -76,7 +76,7 @@ @implementation RNGestureHandler { NSArray *_simultaneousHandlers; RNGHHitSlop _hitSlop; uint16_t _eventCoalescingKey; - NSNumber *_parentTag; + NSNumber *_hostDetectorTag; } - (instancetype)initWithTag:(NSNumber *)tag @@ -691,14 +691,14 @@ - (BOOL)wantsToAttachDirectlyToView return NO; } -- (void)setParentTag:(NSNumber *)parentTag +- (void)setHostDetectorTag:(NSNumber *)hostDetectorTag { - _parentTag = parentTag; + _hostDetectorTag = hostDetectorTag; } -- (NSNumber *)getParentTag +- (NSNumber *)getHostDetectorTag { - return _parentTag; + return _hostDetectorTag; } @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index eee71f129a..50ab8e96e5 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -170,7 +170,7 @@ - (void)attachHandlers:(const std::vector &)handlerTags [_nativeHandlers addObject:@(tag)]; } else { if (actionType == RNGestureHandlerActionTypeLogicDetector) { - [[[handlerManager registry] handlerWithTag:@(tag)] setParentTag:@(self.tag)]; + [[[handlerManager registry] handlerWithTag:@(tag)] setHostDetectorTag:@(self.tag)]; [handlerManager attachGestureHandler:@(tag) toViewWithTag:@(viewTag) withActionType:actionType]; } else { diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index 1e279eed04..357162ceae 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -304,8 +304,8 @@ - (void)sendEvent:(RNGestureHandlerStateChange *)event { switch (actionType) { case RNGestureHandlerActionTypeLogicDetector: { - NSNumber *parentTag = [[_registry handlerWithTag:event.handlerTag] getParentTag]; - detectorView = [self viewForReactTag:parentTag]; + NSNumber *hostDetectorTag = [[_registry handlerWithTag:event.handlerTag] getHostDetectorTag]; + detectorView = [self viewForReactTag:hostDetectorTag]; } case RNGestureHandlerActionTypeNativeDetector: { if ([event isKindOfClass:[RNGestureHandlerEvent class]]) { From f0fb45845f848809f305eefb3fab508015ab1e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 11:56:05 +0200 Subject: [PATCH 41/90] removed unnecessary variable --- packages/react-native-gesture-handler/src/v3/hooks/utils.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts index dfce72d9c7..7504e17a92 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts @@ -211,9 +211,8 @@ export function invokeDetectorEvent( } if ('workletEventHandler' in method) { - const we = method.workletEventHandler; - if ('worklet' in we) { - invokeDetectorEvent(we.worklet, event); + if ('worklet' in method.workletEventHandler) { + invokeDetectorEvent(method.workletEventHandler.worklet, event); } return; } From 5e5e9176e15dbeaba3fa7d15c009043abafcb559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 12 Sep 2025 13:32:51 +0200 Subject: [PATCH 42/90] renamed set --- .../react/RNGestureHandlerDetectorView.kt | 10 +++++----- .../apple/RNGestureHandlerDetector.mm | 10 +++++----- .../src/v3/HostGestureDetector.web.tsx | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index d971574bf2..0c9ce9bc4c 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -41,7 +41,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } fun setLogicChildren(newLogicChildren: ReadableArray?) { - val logicChildrenToDelete = attachedLogicHandlers.keys.toMutableSet() + val logicHandlersToDetach = attachedLogicHandlers.keys.toMutableSet() val mappedChildren = newLogicChildren?.mapLogicChildren().orEmpty() @@ -49,7 +49,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { if (!attachedLogicHandlers.containsKey(child.viewTag)) { attachedLogicHandlers.put(child.viewTag, mutableSetOf()) } - logicChildrenToDelete.remove(child.viewTag) + logicHandlersToDetach.remove(child.viewTag) attachHandlers( child.handlerTags, child.viewTag, @@ -58,11 +58,11 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { ) } - for (childTag in logicChildrenToDelete) { + for (tag in logicHandlersToDetach) { val registry = RNGestureHandlerModule.registries[moduleId] ?: throw Exception("Tried to access a non-existent registry") - registry.detachHandler(childTag) - attachedLogicHandlers.remove(childTag) + registry.detachHandler(tag) + attachedLogicHandlers.remove(tag) } } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index 50ab8e96e5..5b449ee03f 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -210,9 +210,9 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") - NSMutableSet *logicChildrenToDelete = [NSMutableSet set]; + NSMutableSet *logicHandlersToDetach = [NSMutableSet set]; for (const std::pair &child : _attachedLogicHandlers) { - [logicChildrenToDelete addObject:@(child.first)]; + [logicHandlersToDetach addObject:@(child.first)]; } for (const RNGestureHandlerDetectorLogicChildrenStruct &child : newProps.logicChildren) { @@ -220,7 +220,7 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar _attachedLogicHandlers[child.viewTag] = [NSMutableSet set]; } - [logicChildrenToDelete removeObject:@(child.viewTag)]; + [logicHandlersToDetach removeObject:@(child.viewTag)]; [self attachHandlers:child.handlerTags actionType:RNGestureHandlerActionTypeLogicDetector @@ -228,8 +228,8 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar attachedHandlers:_attachedLogicHandlers[child.viewTag]]; } - for (const NSNumber *childTag : logicChildrenToDelete) { - for (id handlerTag : _attachedLogicHandlers[childTag.intValue]) { + for (const NSNumber *tag : logicHandlersToDetach) { + for (id handlerTag : _attachedLogicHandlers[tag.intValue]) { [handlerManager.registry detachHandlerWithTag:handlerTag]; } } diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index 4dff028589..16cb0c154d 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -102,7 +102,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }, [handlerTags, children]); useEffect(() => { - const logicChildrenToDelete: Set = new Set( + const logicHandlersToDetach: Set = new Set( attachedLogicHandlers.current.keys() ); @@ -110,7 +110,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { if (!attachedLogicHandlers.current.has(child.viewTag)) { attachedLogicHandlers.current.set(child.viewTag, new Set()); } - logicChildrenToDelete.delete(child.viewTag); + logicHandlersToDetach.delete(child.viewTag); attachHandlers( child.viewRef, propsRef, @@ -120,12 +120,12 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { ); }); - logicChildrenToDelete.forEach((childTag) => { + logicHandlersToDetach.forEach((tag) => { detachHandlers( - attachedLogicHandlers.current.get(childTag)!, - attachedLogicHandlers.current.get(childTag)! + attachedLogicHandlers.current.get(tag)!, + attachedLogicHandlers.current.get(tag)! ); - attachedLogicHandlers.current.delete(childTag); + attachedLogicHandlers.current.delete(tag); }); }, [props.logicChildren]); From abcac980516ade8a532347c59d9215536f65cf8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 07:55:16 +0200 Subject: [PATCH 43/90] separate function to detach all handlers --- .../src/v3/HostGestureDetector.web.tsx | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index 16cb0c154d..db03f8c76c 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -27,6 +27,15 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { const attachedNativeHandlers = useRef>(new Set()); const attachedLogicHandlers = useRef>>(new Map()); + const detachAllHandlers = (attachedHandlerTags: Set) => { + // Separate function to reduce the number of conditions + attachedHandlerTags.forEach((tag) => { + RNGestureHandlerModule.detachGestureHandler(tag); + attachedNativeHandlers.current.delete(tag); + }); + attachedHandlerTags.clear(); + }; + const detachHandlers = ( oldHandlerTags: Set, attachedHandlerTags: Set @@ -34,15 +43,8 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { oldHandlerTags.forEach((tag) => { RNGestureHandlerModule.detachGestureHandler(tag); attachedNativeHandlers.current.delete(tag); + attachedHandlerTags.delete(tag); }); - - if (oldHandlerTags === attachedHandlerTags) { - attachedHandlerTags.clear(); - } else { - for (const tag of oldHandlerTags) { - attachedHandlerTags.delete(tag); - } - } }; const attachHandlers = ( @@ -121,19 +123,15 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }); logicHandlersToDetach.forEach((tag) => { - detachHandlers( - attachedLogicHandlers.current.get(tag)!, - attachedLogicHandlers.current.get(tag)! - ); - attachedLogicHandlers.current.delete(tag); + detachAllHandlers(attachedLogicHandlers.current.get(tag)!); }); }, [props.logicChildren]); useEffect(() => { return () => { - detachHandlers(attachedHandlers.current, attachedHandlers.current); - attachedLogicHandlers?.current.forEach((child) => { - detachHandlers(child, child); + detachAllHandlers(attachedHandlers.current); + attachedLogicHandlers?.current.forEach((childHandlerTags) => { + detachAllHandlers(childHandlerTags); }); }; }, []); From 5c367b5808688ed2659ecb1861cc99b153fcc499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 08:01:16 +0200 Subject: [PATCH 44/90] fix testing remnant --- .../src/web/handlers/GestureHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts index ed2a946e53..5c7ef13693 100644 --- a/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts +++ b/packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts @@ -92,7 +92,7 @@ export default abstract class GestureHandler implements IGestureHandler { public detach() { if (this.state === State.ACTIVE) { - this.cancelTouches(); + this.cancel(); } else { this.fail(); } From b1d620a4b009b9274086b22c16082599877ae1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 08:21:40 +0200 Subject: [PATCH 45/90] non default useDetectorContext --- packages/react-native-gesture-handler/src/v3/LogicDetector.tsx | 2 +- .../react-native-gesture-handler/src/v3/useDetectorContext.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 4409d98d80..e80c7c309f 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; import { findNodeHandle, Platform } from 'react-native'; import { NativeDetectorProps } from './types'; -import useDetectorContext from './useDetectorContext'; +import { useDetectorContext } from './useDetectorContext'; export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); diff --git a/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts b/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts index 38f9144d2f..9dd0f210d2 100644 --- a/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts +++ b/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts @@ -8,7 +8,7 @@ type DetectorContextType = { export const DetectorContext = createContext(null); -export default function useDetectorContext() { +export function useDetectorContext() { const ctx = useContext(DetectorContext); if (!ctx) { throw new Error('Logic detector must be under a Native Detector'); From 157d87b05a8a6489464d790f837415ddd0e49a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 08:23:57 +0200 Subject: [PATCH 46/90] android action type whitespace --- .../java/com/swmansion/gesturehandler/core/GestureHandler.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt index 26b0486f67..ef6e6079a9 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt @@ -1018,7 +1018,6 @@ open class GestureHandler { const val ACTION_TYPE_JS_FUNCTION_OLD_API = 3 const val ACTION_TYPE_JS_FUNCTION_NEW_API = 4 const val ACTION_TYPE_NATIVE_DETECTOR = 5 - const val ACTION_TYPE_LOGIC_DETECTOR = 6 const val POINTER_TYPE_TOUCH = 0 const val POINTER_TYPE_STYLUS = 1 From a1b94d5a1d2a22a422183a7c1ec7fc2ec96d2ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 09:35:41 +0200 Subject: [PATCH 47/90] clean up extension --- .../react/RNGestureHandlerDetectorView.kt | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index 0c9ce9bc4c..1eb4da549a 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -173,29 +173,20 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { private fun ReadableArray.mapLogicChildren(): List { val mappedChildren = mutableListOf() - for (i in 0 until this.size()) { val child = this.getMap(i) ?: continue - - val handlerTagsArray = child.getArray("handlerTags") - val handlerTags = mutableListOf() - - if (handlerTagsArray != null) { - for (j in 0 until handlerTagsArray.size()) { - handlerTags.add(handlerTagsArray.getInt(j)) - } - } - + val handlerTags = child.getArray("handlerTags")?.toIntList().orEmpty() val viewTag = child.getInt("viewTag") mappedChildren.add( LogicChildren( - handlerTags = handlerTags, - viewTag = viewTag, + handlerTags, + viewTag, ), ) } - return mappedChildren } } + +private fun ReadableArray.toIntList(): List = (0 until size()).map { getInt(it) } From 75f218b7bb270423377f43266461bd2b59105c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 09:51:07 +0200 Subject: [PATCH 48/90] removed remnants --- .../apple/RNGestureHandlerDetector.mm | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index 5b449ee03f..6aa3b382c4 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -199,8 +199,6 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar const auto &oldProps = *std::static_pointer_cast(oldPropsBase); _moduleId = newProps.moduleId; - static std::vector emptyVector; - [self attachHandlers:newProps.handlerTags actionType:RNGestureHandlerActionTypeNativeDetector viewTag:-1 From 344617ad078cf5749f1a29a2c82a5bd4a4010f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 09:53:06 +0200 Subject: [PATCH 49/90] renamed logic children web --- .../src/v3/HostGestureDetector.web.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx index db03f8c76c..52c163d54d 100644 --- a/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/HostGestureDetector.web.tsx @@ -9,10 +9,10 @@ export interface GestureHandlerDetectorProps extends PropsRef { handlerTags: number[]; moduleId: number; children?: React.ReactNode; - logicChildren?: Set; + logicChildren?: Set; } -export interface LogicChildrenProps { +export interface LogicChildrenWeb { viewTag: number; handlerTags: number[]; viewRef: RefObject; From 03b37c77a1484cde885db6652a9a3d6ac62d0627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 10:05:36 +0200 Subject: [PATCH 50/90] android whitespace --- .../gesturehandler/react/RNGestureHandlerDetectorView.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index 1eb4da549a..2aa30e6492 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -49,7 +49,9 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { if (!attachedLogicHandlers.containsKey(child.viewTag)) { attachedLogicHandlers.put(child.viewTag, mutableSetOf()) } + logicHandlersToDetach.remove(child.viewTag) + attachHandlers( child.handlerTags, child.viewTag, @@ -61,6 +63,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { for (tag in logicHandlersToDetach) { val registry = RNGestureHandlerModule.registries[moduleId] ?: throw Exception("Tried to access a non-existent registry") + registry.detachHandler(tag) attachedLogicHandlers.remove(tag) } From 9420ead61de65d3311978380e2ff3426b500d12d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 10:10:16 +0200 Subject: [PATCH 51/90] removed unnecessary annotation --- .../java/com/swmansion/gesturehandler/core/GestureHandler.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt index ef6e6079a9..1b6454df00 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt @@ -1054,7 +1054,6 @@ open class GestureHandler { return null } - @JvmStatic fun isV3Api(actionType: Int): Boolean = actionType == ACTION_TYPE_NATIVE_DETECTOR || actionType == ACTION_TYPE_LOGIC_DETECTOR } From 06bb0e188f1e9c529cb35b9e557dc56104180ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 10:22:31 +0200 Subject: [PATCH 52/90] whitespace --- .../react/events/RNGestureHandlerEventDispatcher.kt | 1 + .../apple/RNGestureHandlerRegistry.h | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt index 5d93c01773..731818578b 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEventDispatcher.kt @@ -79,6 +79,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React } else { EventHandlerType.ForJS } + val event = RNGestureHandlerEvent.obtain( handler, handler.actionType, diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h index 92d1966c30..dd73a1ce89 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h @@ -18,4 +18,5 @@ - (void)detachHandlerWithTag:(nonnull NSNumber *)handlerTag; - (void)dropHandlerWithTag:(nonnull NSNumber *)handlerTag; - (void)dropAllHandlers; + @end From 474e301b1738c8988ea5fa2fea151e7eea61c5af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 10:25:31 +0200 Subject: [PATCH 53/90] delete obsolete --- .../apple/RNGestureHandlerDetector.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index 6aa3b382c4..b215c4c48e 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -196,7 +196,6 @@ - (void)attachHandlers:(const std::vector &)handlerTags - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shared &)oldPropsBase { const auto &newProps = *std::static_pointer_cast(propsBase); - const auto &oldProps = *std::static_pointer_cast(oldPropsBase); _moduleId = newProps.moduleId; [self attachHandlers:newProps.handlerTags From 1fef44a93db01ac2dc9b64fed472a04e5978f5e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 10:35:52 +0200 Subject: [PATCH 54/90] Fallthrough comment --- .../apple/RNGestureHandlerManager.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index 357162ceae..c970c697ae 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -306,6 +306,7 @@ - (void)sendEvent:(RNGestureHandlerStateChange *)event case RNGestureHandlerActionTypeLogicDetector: { NSNumber *hostDetectorTag = [[_registry handlerWithTag:event.handlerTag] getHostDetectorTag]; detectorView = [self viewForReactTag:hostDetectorTag]; + // intentionally fall through to RNGestureHandlerActionTypeNativeDetector } case RNGestureHandlerActionTypeNativeDetector: { if ([event isKindOfClass:[RNGestureHandlerEvent class]]) { From cb9ab7a1cd40630083a06ed80367b3a9b50a14b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 15 Sep 2025 10:39:22 +0200 Subject: [PATCH 55/90] defining registry outside of loop --- .../gesturehandler/react/RNGestureHandlerDetectorView.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index 2aa30e6492..c23c766e91 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -60,10 +60,10 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { ) } - for (tag in logicHandlersToDetach) { - val registry = RNGestureHandlerModule.registries[moduleId] - ?: throw Exception("Tried to access a non-existent registry") + val registry = RNGestureHandlerModule.registries[moduleId] + ?: throw Exception("Tried to access a non-existent registry") + for (tag in logicHandlersToDetach) { registry.detachHandler(tag) attachedLogicHandlers.remove(tag) } From 2a727cbdd04b37315ed3627fd121156ecae3a263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 16 Sep 2025 08:31:29 +0200 Subject: [PATCH 56/90] cleaner extension function --- .../react/RNGestureHandlerDetectorView.kt | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index c23c766e91..c29d176ee7 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -174,22 +174,13 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } } - private fun ReadableArray.mapLogicChildren(): List { - val mappedChildren = mutableListOf() - for (i in 0 until this.size()) { - val child = this.getMap(i) ?: continue - val handlerTags = child.getArray("handlerTags")?.toIntList().orEmpty() - val viewTag = child.getInt("viewTag") - - mappedChildren.add( - LogicChildren( - handlerTags, - viewTag, - ), - ) - } - return mappedChildren - } -} + private fun ReadableArray.mapLogicChildren(): List = List(size()) { i -> + val child = getMap(i) ?: return@List null + val handlerTags = child.getArray("handlerTags")?.toIntList().orEmpty() + val viewTag = child.getInt("viewTag") + + LogicChildren(handlerTags, viewTag) + }.filterNotNull() -private fun ReadableArray.toIntList(): List = (0 until size()).map { getInt(it) } + private fun ReadableArray.toIntList(): List = List(size()) { getInt(it) } +} From e6fbdba87cb75dbf9dd9b81082b3b83f39063d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 16 Sep 2025 12:52:10 +0200 Subject: [PATCH 57/90] extract repetition --- .../src/v3/NativeDetector/NativeDetector.tsx | 52 ++++++------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx index f439267f3e..76baa90355 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -62,57 +62,39 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { configureRelations(gesture); + const handleGestureEvent = (key: keyof LogicMethods, e: any) => { + const handlerTag = e.nativeEvent.handlerTag; + + const method = !logicMethods.current.has(handlerTag) + ? gesture.gestureEvents[key] + : logicMethods.current.get(handlerTag)?.current?.[key]; + + invokeDetectorEvent(method, e); + }; + return ( { - const method = !logicMethods.current.has(e.nativeEvent.handlerTag) - ? gesture.gestureEvents.onGestureHandlerStateChange - : logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerStateChange; - invokeDetectorEvent(method, e); + handleGestureEvent('onGestureHandlerStateChange', e); }} onGestureHandlerEvent={(e) => { - const method = !logicMethods.current.has(e.nativeEvent.handlerTag) - ? gesture.gestureEvents.onGestureHandlerEvent - : logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerEvent; - invokeDetectorEvent(method, e); + handleGestureEvent('onGestureHandlerEvent', e); }} onGestureHandlerAnimatedEvent={(e) => { - const method = !logicMethods.current.has(e.nativeEvent.handlerTag) - ? gesture.gestureEvents.onGestureHandlerAnimatedEvent - : logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerAnimatedEvent; - invokeDetectorEvent(method, e); + handleGestureEvent('onGestureHandlerAnimatedEvent', e); }} onGestureHandlerTouchEvent={(e) => { - const method = !logicMethods.current.has(e.nativeEvent.handlerTag) - ? gesture.gestureEvents.onGestureHandlerTouchEvent - : logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onGestureHandlerTouchEvent; - invokeDetectorEvent(method, e); + handleGestureEvent('onGestureHandlerTouchEvent', e); }} onGestureHandlerReanimatedStateChange={(e) => { - const method = !logicMethods.current.has(e.nativeEvent.handlerTag) - ? gesture.gestureEvents.onReanimatedStateChange - : logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onReanimatedStateChange; - invokeDetectorEvent(method, e); + handleGestureEvent('onReanimatedStateChange', e); }} onGestureHandlerReanimatedEvent={(e) => { - const method = !logicMethods.current.has(e.nativeEvent.handlerTag) - ? gesture.gestureEvents.onReanimatedUpdateEvent - : logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onReanimatedUpdateEvent; - invokeDetectorEvent(method, e); + handleGestureEvent('onReanimatedUpdateEvent', e); }} onGestureHandlerReanimatedTouchEvent={(e) => { - const method = !logicMethods.current.has(e.nativeEvent.handlerTag) - ? gesture.gestureEvents.onReanimatedTouchEvent - : logicMethods.current.get(e.nativeEvent.handlerTag)?.current - ?.onReanimatedTouchEvent; - invokeDetectorEvent(method, e); + handleGestureEvent('onReanimatedTouchEvent', e); }} moduleId={globalThis._RNGH_MODULE_ID} handlerTags={isComposedGesture(gesture) ? gesture.tags : [gesture.tag]} From 6fa67e3cb27fe28de18cf4512440ba1303bd60ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 16 Sep 2025 12:55:42 +0200 Subject: [PATCH 58/90] moved file --- packages/react-native-gesture-handler/src/v3/LogicDetector.tsx | 2 +- .../src/v3/NativeDetector/NativeDetector.tsx | 2 +- .../src/v3/{ => NativeDetector}/useDetectorContext.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename packages/react-native-gesture-handler/src/v3/{ => NativeDetector}/useDetectorContext.ts (89%) diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index c08d0200fc..8edabe42ba 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; import { findNodeHandle, Platform } from 'react-native'; -import { useDetectorContext } from './useDetectorContext'; +import { useDetectorContext } from './NativeDetector/useDetectorContext'; import { NativeDetectorProps } from './NativeDetector/NativeDetector'; import { isComposedGesture } from './hooks/utils/relationUtils'; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx index 76baa90355..5ce8aaeab0 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -4,7 +4,7 @@ import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../../utils'; import { LogicChildren, LogicMethods, Gesture } from '../types'; import { invokeDetectorEvent } from '../hooks/utils'; -import { DetectorContext } from '../useDetectorContext'; +import { DetectorContext } from './useDetectorContext'; import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; import { configureRelations } from './utils'; import { isComposedGesture } from '../hooks/utils/relationUtils'; diff --git a/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts b/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts similarity index 89% rename from packages/react-native-gesture-handler/src/v3/useDetectorContext.ts rename to packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts index 9dd0f210d2..8ffc74da82 100644 --- a/packages/react-native-gesture-handler/src/v3/useDetectorContext.ts +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts @@ -1,5 +1,5 @@ import { createContext, RefObject, useContext } from 'react'; -import { LogicChildren, LogicMethods } from './types'; +import { LogicChildren, LogicMethods } from '../types'; type DetectorContextType = { register: (child: LogicChildren, methods: RefObject) => void; From 31fad0238717190fcdc83c3bc3072c3f621c9999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 16 Sep 2025 12:59:52 +0200 Subject: [PATCH 59/90] fix bad merge --- .../src/v3/types.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/types.ts b/packages/react-native-gesture-handler/src/v3/types.ts index ec16672e63..d0e78ffafa 100644 --- a/packages/react-native-gesture-handler/src/v3/types.ts +++ b/packages/react-native-gesture-handler/src/v3/types.ts @@ -6,24 +6,6 @@ import { } from '../handlers/gestureHandlerCommon'; import { HandlerCallbacks } from '../handlers/gestures/gesture'; -export type GestureType = - | 'TapGestureHandler' - | 'LongPressGestureHandler' - | 'PanGestureHandler' - | 'PinchGestureHandler' - | 'RotationGestureHandler' - | 'FlingGestureHandler' - | 'ForceTouchGestureHandler' - | 'ManualGestureHandler' - | 'NativeViewGestureHandler'; - -export interface NativeGesture { - tag: number; - name: GestureType; - config: Record; - gestureEvents: GestureEvents; -} - export type GestureUpdateEventWithData = GestureEventPayload & { handlerData: T; }; From c9ffbdbef26f9737a65b506ff5837c6567ecdbb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 16 Sep 2025 13:00:40 +0200 Subject: [PATCH 60/90] better error --- .../src/v3/NativeDetector/useDetectorContext.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts b/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts index 8ffc74da82..9f74086188 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts @@ -11,7 +11,7 @@ export const DetectorContext = createContext(null); export function useDetectorContext() { const ctx = useContext(DetectorContext); if (!ctx) { - throw new Error('Logic detector must be under a Native Detector'); + throw new Error('Logic detector must be a descendant of a Native Detector'); } return ctx; } From 72e78134577f7a93d284eb90ec89ef172a92b5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 16 Sep 2025 14:32:32 +0200 Subject: [PATCH 61/90] updating logic children --- .../src/v3/LogicDetector.tsx | 10 ++++++++++ .../src/v3/NativeDetector/NativeDetector.tsx | 8 ++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 8edabe42ba..d440f482c3 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -56,6 +56,16 @@ export const LogicDetector = (props: NativeDetectorProps) => { }; }, [props.gesture.gestureEvents]); + useEffect(() => { + if (viewTag === -1) { + return; + } + + // Native Detector differentiates Logic Children through a viewTag, + // thus if viewTag changes we have to reregister + unregister(viewTag); + }, [viewTag]); + useEffect(() => { if (viewTag === -1) { return; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx index bb5757f52f..f32d9cb0b3 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -33,9 +33,13 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { const register = useCallback( (child: LogicChildren, methods: RefObject) => { setLogicChildren((prev) => { - if (prev.some((c) => c.viewTag === child.viewTag)) { - return prev; + const index = prev.findIndex((c) => c.viewTag === child.viewTag); + if (index !== -1) { + const updated = [...prev]; + updated[index] = child; + return updated; } + return [...prev, child]; }); From 79f1d9694e37189b2d30f89f59565f539939652a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Tue, 16 Sep 2025 14:40:45 +0200 Subject: [PATCH 62/90] removed unnecessary return --- packages/react-native-gesture-handler/src/v3/hooks/utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts index c0e12adab7..15249655df 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts @@ -124,6 +124,4 @@ export function invokeDetectorEvent( } return; } - - return; } From 86ff72556b8f7d387a39cb91bedabcee1f3dac3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 17 Sep 2025 08:27:04 +0200 Subject: [PATCH 63/90] hostGestureDetector property --- .../apple/RNGestureHandler.h | 4 +--- .../apple/RNGestureHandler.mm | 11 ----------- .../apple/RNGestureHandlerDetector.mm | 3 +-- .../apple/RNGestureHandlerManager.mm | 2 +- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.h b/packages/react-native-gesture-handler/apple/RNGestureHandler.h index 8ffeaf6b41..bc246de01d 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandler.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandler.h @@ -81,6 +81,7 @@ @property (nonatomic) BOOL manualActivation; @property (nonatomic) BOOL dispatchesAnimatedEvents; @property (nonatomic) BOOL dispatchesReanimatedEvents; +@property (nonatomic, nonnull, assign) NSNumber *hostDetectorTag; - (BOOL)isViewParagraphComponent:(nullable RNGHUIView *)view; - (nonnull RNGHUIView *)chooseViewForInteraction:(nonnull UIGestureRecognizer *)recognizer; @@ -107,9 +108,6 @@ - (nonnull RNGHUIView *)findViewForEvents; - (BOOL)wantsToAttachDirectlyToView; -- (void)setHostDetectorTag:(nonnull NSNumber *)hostDetectorTag; -- (nonnull NSNumber *)getHostDetectorTag; - #if !TARGET_OS_OSX - (BOOL)isUIScrollViewPanGestureRecognizer:(nonnull UIGestureRecognizer *)gestureRecognizer; #else diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.mm b/packages/react-native-gesture-handler/apple/RNGestureHandler.mm index a8d2f95cce..a2dd49fd93 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandler.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandler.mm @@ -76,7 +76,6 @@ @implementation RNGestureHandler { NSArray *_simultaneousHandlers; RNGHHitSlop _hitSlop; uint16_t _eventCoalescingKey; - NSNumber *_hostDetectorTag; } - (instancetype)initWithTag:(NSNumber *)tag @@ -691,14 +690,4 @@ - (BOOL)wantsToAttachDirectlyToView return NO; } -- (void)setHostDetectorTag:(NSNumber *)hostDetectorTag -{ - _hostDetectorTag = hostDetectorTag; -} - -- (NSNumber *)getHostDetectorTag -{ - return _hostDetectorTag; -} - @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index b215c4c48e..a77409e56f 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -170,8 +170,7 @@ - (void)attachHandlers:(const std::vector &)handlerTags [_nativeHandlers addObject:@(tag)]; } else { if (actionType == RNGestureHandlerActionTypeLogicDetector) { - [[[handlerManager registry] handlerWithTag:@(tag)] setHostDetectorTag:@(self.tag)]; - + [[handlerManager registry] handlerWithTag:@(tag)].hostDetectorTag = @(self.tag); [handlerManager attachGestureHandler:@(tag) toViewWithTag:@(viewTag) withActionType:actionType]; } else { [handlerManager.registry attachHandlerWithTag:@(tag) toView:self withActionType:actionType]; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index cbdcb7f1f0..998ebbac49 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -310,7 +310,7 @@ - (void)sendEvent:(RNGestureHandlerStateChange *)event { switch (actionType) { case RNGestureHandlerActionTypeLogicDetector: { - NSNumber *hostDetectorTag = [[_registry handlerWithTag:event.handlerTag] getHostDetectorTag]; + NSNumber *hostDetectorTag = [_registry handlerWithTag:event.handlerTag].hostDetectorTag; detectorView = [self viewForReactTag:hostDetectorTag]; // intentionally fall through to RNGestureHandlerActionTypeNativeDetector } From 7213108ba2bf8bbc8189ccfc7d56ba534280f60c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 17 Sep 2025 08:37:08 +0200 Subject: [PATCH 64/90] extracted update logic children --- .../apple/RNGestureHandlerDetector.mm | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index a77409e56f..ec5783bfb2 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -203,6 +203,14 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar attachedHandlers:_attachedHandlers]; [super updateProps:propsBase oldProps:oldPropsBase]; + [self updateLogicChildren:newProps.logicChildren]; + + // Override to force hittesting to work outside bounds + self.clipsToBounds = NO; +} + +- (void)updateLogicChildren:(const std::vector &)logicChildren +{ RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager") @@ -211,7 +219,7 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar [logicHandlersToDetach addObject:@(child.first)]; } - for (const RNGestureHandlerDetectorLogicChildrenStruct &child : newProps.logicChildren) { + for (const RNGestureHandlerDetectorLogicChildrenStruct &child : logicChildren) { if (_attachedLogicHandlers.find(child.viewTag) == _attachedLogicHandlers.end()) { _attachedLogicHandlers[child.viewTag] = [NSMutableSet set]; } @@ -229,9 +237,6 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar [handlerManager.registry detachHandlerWithTag:handlerTag]; } } - - // Override to force hittesting to work outside bounds - self.clipsToBounds = NO; } - (void)tryAttachNativeHandlersToChildView From 8015ea56b554dc43881d6e3b09e115cca96ea4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 17 Sep 2025 09:43:24 +0200 Subject: [PATCH 65/90] rename logic children deleton set --- .../gesturehandler/react/RNGestureHandlerDetectorView.kt | 6 +++--- .../apple/RNGestureHandlerDetector.mm | 8 ++++---- .../src/v3/NativeDetector/HostGestureDetector.web.tsx | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index c29d176ee7..9f8c9aa072 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -41,7 +41,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { } fun setLogicChildren(newLogicChildren: ReadableArray?) { - val logicHandlersToDetach = attachedLogicHandlers.keys.toMutableSet() + val logicChildrenToDetach = attachedLogicHandlers.keys.toMutableSet() val mappedChildren = newLogicChildren?.mapLogicChildren().orEmpty() @@ -50,7 +50,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { attachedLogicHandlers.put(child.viewTag, mutableSetOf()) } - logicHandlersToDetach.remove(child.viewTag) + logicChildrenToDetach.remove(child.viewTag) attachHandlers( child.handlerTags, @@ -63,7 +63,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { val registry = RNGestureHandlerModule.registries[moduleId] ?: throw Exception("Tried to access a non-existent registry") - for (tag in logicHandlersToDetach) { + for (tag in logicChildrenToDetach) { registry.detachHandler(tag) attachedLogicHandlers.remove(tag) } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index ec5783bfb2..e16c2917ac 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -214,9 +214,9 @@ - (void)updateLogicChildren:(const std::vector &child : _attachedLogicHandlers) { - [logicHandlersToDetach addObject:@(child.first)]; + [logicChildrenToDetach addObject:@(child.first)]; } for (const RNGestureHandlerDetectorLogicChildrenStruct &child : logicChildren) { @@ -224,7 +224,7 @@ - (void)updateLogicChildren:(const std::vector { }, [handlerTags, children]); useEffect(() => { - const logicHandlersToDetach: Set = new Set( + const logicChildrenToDetach: Set = new Set( attachedLogicHandlers.current.keys() ); @@ -112,7 +112,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { if (!attachedLogicHandlers.current.has(child.viewTag)) { attachedLogicHandlers.current.set(child.viewTag, new Set()); } - logicHandlersToDetach.delete(child.viewTag); + logicChildrenToDetach.delete(child.viewTag); attachHandlers( child.viewRef, propsRef, @@ -122,7 +122,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { ); }); - logicHandlersToDetach.forEach((tag) => { + logicChildrenToDetach.forEach((tag) => { detachAllHandlers(attachedLogicHandlers.current.get(tag)!); }); }, [props.logicChildren]); From e58904399abc210b492f139fa0b85b69339bbd33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 17 Sep 2025 09:56:46 +0200 Subject: [PATCH 66/90] move invoke detector event --- .../src/v3/NativeDetector/NativeDetector.tsx | 3 +- .../src/v3/NativeDetector/utils.ts | 30 +++++++++++++++++++ .../src/v3/hooks/utils.ts | 30 ------------------- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx index f32d9cb0b3..50c25cd4ff 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -3,10 +3,9 @@ import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../../utils'; import { LogicChildren, LogicMethods, Gesture } from '../types'; -import { invokeDetectorEvent } from '../hooks/utils'; import { DetectorContext } from './useDetectorContext'; import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; -import { configureRelations } from './utils'; +import { configureRelations, invokeDetectorEvent } from './utils'; import { isComposedGesture } from '../hooks/utils/relationUtils'; export interface NativeDetectorProps { diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts b/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts index 6fc42163f6..885d3206c5 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts @@ -145,3 +145,33 @@ export function configureRelations(gesture: Gesture) { ); } } + +// Some event handlers are plain functions, whereas those marked as worklets +// are wrapped in objects under eventMethod.workletEventHandler.worklet. +// This function normalises invocation so that both forms can be called safely. +// Note: this worklet unpacking is essentially a workaround since we need to +// decide on the JS side which handle logic to execute. +export function invokeDetectorEvent( + method: + | ((event: any) => void) + | { workletEventHandler: { worklet: (event: any) => void } } + | null + | undefined, + event: any +): void { + if (!method) { + return; + } + + if (typeof method === 'function') { + method(event); + return; + } + + if ('workletEventHandler' in method) { + if ('worklet' in method.workletEventHandler) { + invokeDetectorEvent(method.workletEventHandler.worklet, event); + } + return; + } +} diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts index 15249655df..d90fc773d1 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils.ts @@ -95,33 +95,3 @@ export function shouldHandleTouchEvents( !!config.onTouchesCancelled ); } - -// Some event handlers are plain functions, whereas those marked as worklets -// are wrapped in objects under eventMethod.workletEventHandler.worklet. -// This function normalises invocation so that both forms can be called safely. -// Note: this worklet unpacking is essentially a workaround since we need to -// decide on the JS side which handle logic to execute. -export function invokeDetectorEvent( - method: - | ((event: any) => void) - | { workletEventHandler: { worklet: (event: any) => void } } - | null - | undefined, - event: any -): void { - if (!method) { - return; - } - - if (typeof method === 'function') { - method(event); - return; - } - - if ('workletEventHandler' in method) { - if ('worklet' in method.workletEventHandler) { - invokeDetectorEvent(method.workletEventHandler.worklet, event); - } - return; - } -} From 57b6b57d2d8cc408f0bc5817f4d35d8243a701c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 17 Sep 2025 11:21:40 +0200 Subject: [PATCH 67/90] auto type --- .../apple/RNGestureHandlerDetector.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index e16c2917ac..746c9fdd6a 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -215,11 +215,11 @@ - (void)updateLogicChildren:(const std::vector &child : _attachedLogicHandlers) { + for (const auto &child : _attachedLogicHandlers) { [logicChildrenToDetach addObject:@(child.first)]; } - for (const RNGestureHandlerDetectorLogicChildrenStruct &child : logicChildren) { + for (const auto &child : logicChildren) { if (_attachedLogicHandlers.find(child.viewTag) == _attachedLogicHandlers.end()) { _attachedLogicHandlers[child.viewTag] = [NSMutableSet set]; } From a8fa63748751a4cfed2620fe4d4b601e74b543ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 17 Sep 2025 11:30:55 +0200 Subject: [PATCH 68/90] rename isv3api --- .../swmansion/gesturehandler/core/GestureHandler.kt | 4 ++-- .../react/events/RNGestureHandlerEvent.kt | 6 +++--- .../react/events/RNGestureHandlerStateChangeEvent.kt | 6 +++--- .../react/events/RNGestureHandlerTouchEvent.kt | 4 ++-- .../react-native-gesture-handler/src/ActionType.ts | 4 +++- .../src/web/handlers/GestureHandler.ts | 11 +++++++---- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt index 1b6454df00..1d7175887f 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt @@ -38,7 +38,7 @@ open class GestureHandler { val viewForEvents: RNGestureHandlerDetectorView get() { - assert(isV3Api(actionType)) { + assert(usesNativeOrLogicDetector(actionType)) { "[react-native-gesture-handler] `viewForEvents` can only be used with NativeDetector." } @@ -1054,7 +1054,7 @@ open class GestureHandler { return null } - fun isV3Api(actionType: Int): Boolean = + fun usesNativeOrLogicDetector(actionType: Int): Boolean = actionType == ACTION_TYPE_NATIVE_DETECTOR || actionType == ACTION_TYPE_LOGIC_DETECTOR } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt index 365b8f4464..63d33af7e1 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt @@ -26,7 +26,7 @@ class RNGestureHandlerEvent private constructor() : Event dataBuilder: GestureHandlerEventDataBuilder, eventHandlerType: EventHandlerType, ) { - val view = if (GestureHandler.isV3Api(handler.actionType)) { + val view = if (GestureHandler.usesNativeOrLogicDetector(handler.actionType)) { handler.viewForEvents } else { handler.view!! @@ -45,7 +45,7 @@ class RNGestureHandlerEvent private constructor() : Event EVENTS_POOL.release(this) } - override fun getEventName() = if (GestureHandler.isV3Api(actionType)) { + override fun getEventName() = if (GestureHandler.usesNativeOrLogicDetector(actionType)) { if (eventHandlerType == EventHandlerType.ForAnimated) { NATIVE_DETECTOR_ANIMATED_EVENT_NAME } else if (eventHandlerType == EventHandlerType.ForReanimated) { @@ -64,7 +64,7 @@ class RNGestureHandlerEvent private constructor() : Event override fun getCoalescingKey() = coalescingKey - override fun getEventData(): WritableMap = if (GestureHandler.isV3Api(actionType)) { + override fun getEventData(): WritableMap = if (GestureHandler.usesNativeOrLogicDetector(actionType)) { createNativeEventData(dataBuilder!!) } else { createEventData(dataBuilder!!) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt index 9a36126b62..a1d3313564 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerStateChangeEvent.kt @@ -29,7 +29,7 @@ class RNGestureHandlerStateChangeEvent private constructor() : Event, eventHandlerType: EventHandlerType, ) { - val view = if (GestureHandler.isV3Api(handler.actionType)) { + val view = if (GestureHandler.usesNativeOrLogicDetector(handler.actionType)) { handler.viewForEvents } else { handler.view!! @@ -51,7 +51,7 @@ class RNGestureHandlerStateChangeEvent private constructor() : Event init(handler: T, actionType: Int, eventHandlerType: EventHandlerType) { - val view = if (GestureHandler.isV3Api(handler.actionType)) { + val view = if (GestureHandler.usesNativeOrLogicDetector(handler.actionType)) { handler.viewForEvents } else { handler.view!! @@ -33,7 +33,7 @@ class RNGestureHandlerTouchEvent private constructor() : Event Date: Wed, 17 Sep 2025 11:38:15 +0200 Subject: [PATCH 69/90] no put --- .../gesturehandler/react/RNGestureHandlerDetectorView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt index 9f8c9aa072..f2f15d9711 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -47,7 +47,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) { for (child in mappedChildren) { if (!attachedLogicHandlers.containsKey(child.viewTag)) { - attachedLogicHandlers.put(child.viewTag, mutableSetOf()) + attachedLogicHandlers[child.viewTag] = mutableSetOf() } logicChildrenToDetach.remove(child.viewTag) From 63d44f7808c16d19b4eef9db8880f90a9cff1d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 17 Sep 2025 12:06:29 +0200 Subject: [PATCH 70/90] simplify mapping --- .../src/v3/LogicDetector.tsx | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index d440f482c3..ec953417e3 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -9,20 +9,7 @@ export const LogicDetector = (props: NativeDetectorProps) => { const { register, unregister } = useDetectorContext(); const viewRef = useRef(null); const [viewTag, setViewTag] = useState(-1); - const logicMethods = useRef({ - onGestureHandlerStateChange: - props.gesture.gestureEvents.onGestureHandlerStateChange, - onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, - onGestureHandlerAnimatedEvent: - props.gesture.gestureEvents.onGestureHandlerAnimatedEvent, - onGestureHandlerTouchEvent: - props.gesture.gestureEvents.onGestureHandlerTouchEvent, - onReanimatedStateChange: - props.gesture.gestureEvents.onReanimatedStateChange, - onReanimatedUpdateEvent: - props.gesture.gestureEvents.onReanimatedUpdateEvent, - onReanimatedTouchEvent: props.gesture.gestureEvents.onReanimatedTouchEvent, - }); + const logicMethods = useRef(props.gesture.gestureEvents); const handleRef = useCallback((node: any) => { viewRef.current = node; @@ -39,21 +26,7 @@ export const LogicDetector = (props: NativeDetectorProps) => { }, []); useEffect(() => { - logicMethods.current = { - onGestureHandlerStateChange: - props.gesture.gestureEvents.onGestureHandlerStateChange, - onGestureHandlerEvent: props.gesture.gestureEvents.onGestureHandlerEvent, - onGestureHandlerTouchEvent: - props.gesture.gestureEvents.onGestureHandlerTouchEvent, - onGestureHandlerAnimatedEvent: - props.gesture.gestureEvents.onGestureHandlerAnimatedEvent, - onReanimatedStateChange: - props.gesture.gestureEvents.onReanimatedStateChange, - onReanimatedUpdateEvent: - props.gesture.gestureEvents.onReanimatedUpdateEvent, - onReanimatedTouchEvent: - props.gesture.gestureEvents.onReanimatedTouchEvent, - }; + logicMethods.current = props.gesture.gestureEvents; }, [props.gesture.gestureEvents]); useEffect(() => { From 0b362be2a08227c547cfee66c16a0b28a4367f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 17 Sep 2025 12:53:29 +0200 Subject: [PATCH 71/90] better web detaching --- .../HostGestureDetector.web.tsx | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx index 260d4185d9..b61b7df1ed 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx @@ -27,22 +27,13 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { const attachedNativeHandlers = useRef>(new Set()); const attachedLogicHandlers = useRef>>(new Map()); - const detachAllHandlers = (attachedHandlerTags: Set) => { - // Separate function to reduce the number of conditions - attachedHandlerTags.forEach((tag) => { - RNGestureHandlerModule.detachGestureHandler(tag); - attachedNativeHandlers.current.delete(tag); - }); - attachedHandlerTags.clear(); - }; - const detachHandlers = ( - oldHandlerTags: Set, + currentHandlerTags: Set, attachedHandlerTags: Set ) => { + const oldHandlerTags = attachedHandlerTags.difference(currentHandlerTags); oldHandlerTags.forEach((tag) => { RNGestureHandlerModule.detachGestureHandler(tag); - attachedNativeHandlers.current.delete(tag); attachedHandlerTags.delete(tag); }); }; @@ -54,9 +45,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { attachedHandlerTags: Set, actionType: ActionType ) => { - const oldHandlerTags = attachedHandlerTags.difference(currentHandlerTags); const newHandlerTags = currentHandlerTags.difference(attachedHandlerTags); - detachHandlers(oldHandlerTags, attachedHandlerTags); newHandlerTags.forEach((tag) => { if ( @@ -84,7 +73,11 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }; useEffect(() => { - detachHandlers(attachedNativeHandlers.current, attachedHandlers.current); + const newHandlerTags = attachedHandlers.current.difference( + attachedNativeHandlers.current + ); + attachedNativeHandlers.current.clear(); + detachHandlers(newHandlerTags, attachedNativeHandlers.current); }, [children]); useEffect(() => { @@ -94,10 +87,13 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { ); } + const currentHandlerTags = new Set(handlerTags); + detachHandlers(currentHandlerTags, attachedHandlers.current); + attachHandlers( viewRef, propsRef, - new Set(handlerTags), + currentHandlerTags, attachedHandlers.current, ActionType.NATIVE_DETECTOR ); @@ -113,25 +109,32 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { attachedLogicHandlers.current.set(child.viewTag, new Set()); } logicChildrenToDetach.delete(child.viewTag); + + const currentHandlerTags = new Set(child.handlerTags); + detachHandlers( + currentHandlerTags, + attachedLogicHandlers.current.get(child.viewTag)! + ); + attachHandlers( child.viewRef, propsRef, - new Set(child.handlerTags), + currentHandlerTags, attachedLogicHandlers.current.get(child.viewTag)!, ActionType.LOGIC_DETECTOR ); }); logicChildrenToDetach.forEach((tag) => { - detachAllHandlers(attachedLogicHandlers.current.get(tag)!); + detachHandlers(new Set(), attachedLogicHandlers.current.get(tag)!); }); }, [props.logicChildren]); useEffect(() => { return () => { - detachAllHandlers(attachedHandlers.current); + detachHandlers(new Set(), attachedHandlers.current); attachedLogicHandlers?.current.forEach((childHandlerTags) => { - detachAllHandlers(childHandlerTags); + detachHandlers(new Set(), childHandlerTags); }); }; }, []); From 20038b1c5009aaf2b48fe9d25215fafb59995faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Wed, 17 Sep 2025 14:18:35 +0200 Subject: [PATCH 72/90] typing --- .../src/v3/NativeDetector/NativeDetector.tsx | 37 ++++++++++++++----- .../v3/NativeDetector/useDetectorContext.ts | 7 +++- .../src/v3/NativeDetector/utils.ts | 26 ++++++++++--- .../src/v3/types.ts | 10 ----- 4 files changed, 54 insertions(+), 26 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx index 50c25cd4ff..c00a7025d7 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -2,10 +2,19 @@ import React, { RefObject, useCallback, useRef, useState } from 'react'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../../utils'; -import { LogicChildren, LogicMethods, Gesture } from '../types'; +import { + LogicChildren, + Gesture, + GestureEvents, + GestureHandlerEvent, +} from '../types'; import { DetectorContext } from './useDetectorContext'; import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; -import { configureRelations, invokeDetectorEvent } from './utils'; +import { + configureRelations, + getHandlerTag, + invokeDetectorEvent, +} from './utils'; import { isComposedGesture } from '../hooks/utils/relationUtils'; export interface NativeDetectorProps { @@ -21,7 +30,9 @@ const ReanimatedNativeDetector = export function NativeDetector({ gesture, children }: NativeDetectorProps) { const [logicChildren, setLogicChildren] = useState([]); - const logicMethods = useRef>>(new Map()); + const logicMethods = useRef>>>( + new Map() + ); const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents ? AnimatedNativeDetector @@ -30,7 +41,7 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { : HostGestureDetector; const register = useCallback( - (child: LogicChildren, methods: RefObject) => { + (child: LogicChildren, methods: RefObject>) => { setLogicChildren((prev) => { const index = prev.findIndex((c) => c.viewTag === child.viewTag); if (index !== -1) { @@ -64,38 +75,46 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { configureRelations(gesture); - const handleGestureEvent = (key: keyof LogicMethods, e: any) => { - const handlerTag = e.nativeEvent.handlerTag; - + const handleGestureEvent = ( + key: keyof GestureEvents, + e: GestureHandlerEvent + ) => { + const handlerTag = getHandlerTag(e); const method = !logicMethods.current.has(handlerTag) ? gesture.gestureEvents[key] : logicMethods.current.get(handlerTag)?.current?.[key]; - - invokeDetectorEvent(method, e); + invokeDetectorEvent(method as (e: GestureHandlerEvent) => void, e); }; return ( { + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types handleGestureEvent('onGestureHandlerStateChange', e); }} onGestureHandlerEvent={(e) => { + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types handleGestureEvent('onGestureHandlerEvent', e); }} onGestureHandlerAnimatedEvent={(e) => { + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types handleGestureEvent('onGestureHandlerAnimatedEvent', e); }} onGestureHandlerTouchEvent={(e) => { + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types handleGestureEvent('onGestureHandlerTouchEvent', e); }} onGestureHandlerReanimatedStateChange={(e) => { + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types handleGestureEvent('onReanimatedStateChange', e); }} onGestureHandlerReanimatedEvent={(e) => { + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types handleGestureEvent('onReanimatedUpdateEvent', e); }} onGestureHandlerReanimatedTouchEvent={(e) => { + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types handleGestureEvent('onReanimatedTouchEvent', e); }} moduleId={globalThis._RNGH_MODULE_ID} diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts b/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts index 9f74086188..97c2c90dec 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts @@ -1,8 +1,11 @@ import { createContext, RefObject, useContext } from 'react'; -import { LogicChildren, LogicMethods } from '../types'; +import { GestureEvents, LogicChildren } from '../types'; type DetectorContextType = { - register: (child: LogicChildren, methods: RefObject) => void; + register: ( + child: LogicChildren, + methods: RefObject> + ) => void; unregister: (child: number) => void; }; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts b/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts index 885d3206c5..b9be018633 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts @@ -5,11 +5,12 @@ // For `simultaneousHandlers` we use Set as the order doesn't matter. import RNGestureHandlerModule from '../../RNGestureHandlerModule'; +import { tagMessage } from '../../utils'; import { isComposedGesture, prepareRelations, } from '../hooks/utils/relationUtils'; -import { ComposedGestureName, Gesture } from '../types'; +import { ComposedGestureName, Gesture, GestureHandlerEvent } from '../types'; // The tree consists of ComposedGestures and NativeGestures. NativeGestures are always leaf nodes. export const traverseAndConfigureRelations = ( @@ -151,13 +152,17 @@ export function configureRelations(gesture: Gesture) { // This function normalises invocation so that both forms can be called safely. // Note: this worklet unpacking is essentially a workaround since we need to // decide on the JS side which handle logic to execute. -export function invokeDetectorEvent( +export function invokeDetectorEvent( method: - | ((event: any) => void) - | { workletEventHandler: { worklet: (event: any) => void } } + | ((event: GestureHandlerEvent) => void) + | { + workletEventHandler: { + worklet: (event: GestureHandlerEvent) => void; + }; + } | null | undefined, - event: any + event: GestureHandlerEvent ): void { if (!method) { return; @@ -175,3 +180,14 @@ export function invokeDetectorEvent( return; } } + +export function getHandlerTag(e: GestureHandlerEvent): number { + if ('nativeEvent' in e) { + return (e.nativeEvent as any).handlerTag; + } + if ('handlerTag' in e) { + return (e as any).handlerTag; + } + + throw new Error(tagMessage('Cannot extract handler tag from event')); +} diff --git a/packages/react-native-gesture-handler/src/v3/types.ts b/packages/react-native-gesture-handler/src/v3/types.ts index 0dfd8535e4..2bce74bad9 100644 --- a/packages/react-native-gesture-handler/src/v3/types.ts +++ b/packages/react-native-gesture-handler/src/v3/types.ts @@ -47,16 +47,6 @@ export interface LogicChildren { handlerTags: number[]; } -export interface LogicMethods { - onGestureHandlerEvent?: (e: any) => void; - onGestureHandlerStateChange?: (e: any) => void; - onGestureHandlerTouchEvent?: (e: any) => void; - onGestureHandlerAnimatedEvent?: (e: any) => void; - onReanimatedStateChange?: (e: any) => void; - onReanimatedUpdateEvent?: (e: any) => void; - onReanimatedTouchEvent?: (e: any) => void; -} - export enum SingleGestureName { Tap = 'TapGestureHandler', LongPress = 'LongPressGestureHandler', From 832a03934617f6c929add8a5177a88bbf49b252b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 18 Sep 2025 12:08:50 +0200 Subject: [PATCH 73/90] passing helper function directly --- .../src/v3/NativeDetector/NativeDetector.tsx | 76 ++++++++++--------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx index c00a7025d7..e14614c766 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -75,48 +75,50 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { configureRelations(gesture); - const handleGestureEvent = ( - key: keyof GestureEvents, - e: GestureHandlerEvent - ) => { - const handlerTag = getHandlerTag(e); - const method = !logicMethods.current.has(handlerTag) - ? gesture.gestureEvents[key] - : logicMethods.current.get(handlerTag)?.current?.[key]; - invokeDetectorEvent(method as (e: GestureHandlerEvent) => void, e); + const handleGestureEvent = (key: keyof GestureEvents) => { + return (e: GestureHandlerEvent) => { + const handlerTag = getHandlerTag(e); + + const method = !logicMethods.current.has(handlerTag) + ? gesture.gestureEvents[key] + : logicMethods.current.get(handlerTag)?.current?.[key]; + + invokeDetectorEvent( + method as (e: GestureHandlerEvent) => void, + e + ); + }; }; return ( { - // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types - handleGestureEvent('onGestureHandlerStateChange', e); - }} - onGestureHandlerEvent={(e) => { - // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types - handleGestureEvent('onGestureHandlerEvent', e); - }} - onGestureHandlerAnimatedEvent={(e) => { - // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types - handleGestureEvent('onGestureHandlerAnimatedEvent', e); - }} - onGestureHandlerTouchEvent={(e) => { - // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types - handleGestureEvent('onGestureHandlerTouchEvent', e); - }} - onGestureHandlerReanimatedStateChange={(e) => { - // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types - handleGestureEvent('onReanimatedStateChange', e); - }} - onGestureHandlerReanimatedEvent={(e) => { - // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types - handleGestureEvent('onReanimatedUpdateEvent', e); - }} - onGestureHandlerReanimatedTouchEvent={(e) => { - // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types - handleGestureEvent('onReanimatedTouchEvent', e); - }} + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerStateChange={handleGestureEvent( + 'onGestureHandlerStateChange' + )} + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerEvent={handleGestureEvent('onGestureHandlerEvent')} + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerAnimatedEvent={handleGestureEvent( + 'onGestureHandlerAnimatedEvent' + )} + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerTouchEvent={handleGestureEvent( + 'onGestureHandlerTouchEvent' + )} + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerReanimatedStateChange={handleGestureEvent( + 'onReanimatedStateChange' + )} + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerReanimatedEvent={handleGestureEvent( + 'onReanimatedUpdateEvent' + )} + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerReanimatedTouchEvent={handleGestureEvent( + 'onReanimatedTouchEvent' + )} moduleId={globalThis._RNGH_MODULE_ID} handlerTags={isComposedGesture(gesture) ? gesture.tags : [gesture.tag]} style={styles.detector} From 590a5cdb5ca975f0055f7cf3e9e9a689b7dc0762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 19 Sep 2025 11:43:27 +0200 Subject: [PATCH 74/90] resolved workaround --- .../src/v3/NativeDetector/NativeDetector.tsx | 72 ++++++++++++------- .../src/v3/NativeDetector/utils.ts | 48 +------------ 2 files changed, 49 insertions(+), 71 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx index e14614c766..5e52e2013e 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -10,11 +10,7 @@ import { } from '../types'; import { DetectorContext } from './useDetectorContext'; import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; -import { - configureRelations, - getHandlerTag, - invokeDetectorEvent, -} from './utils'; +import { configureRelations } from './utils'; import { isComposedGesture } from '../hooks/utils/relationUtils'; export interface NativeDetectorProps { @@ -77,19 +73,53 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) { const handleGestureEvent = (key: keyof GestureEvents) => { return (e: GestureHandlerEvent) => { - const handlerTag = getHandlerTag(e); - - const method = !logicMethods.current.has(handlerTag) - ? gesture.gestureEvents[key] - : logicMethods.current.get(handlerTag)?.current?.[key]; - - invokeDetectorEvent( - method as (e: GestureHandlerEvent) => void, - e - ); + if (gesture.gestureEvents[key]) { + gesture.gestureEvents[key](e); + } + + logicMethods.current.forEach((ref) => { + const method = ref.current?.[key]; + if (method) { + method(e); + } + }); }; }; + const getHandlers = useCallback( + (key: keyof GestureEvents) => { + const handlers: ((e: GestureHandlerEvent) => void)[] = []; + + if (gesture.gestureEvents[key]) { + handlers.push( + gesture.gestureEvents[key] as ( + e: GestureHandlerEvent + ) => void + ); + } + + logicMethods.current.forEach((ref) => { + const handler = ref.current?.[key]; + if (handler) { + handlers.push(handler as (e: GestureHandlerEvent) => void); + } + }); + + return handlers; + }, + [logicChildren, gesture.gestureEvents] + ); + + const reanimatedEventHandler = Reanimated?.useComposedEventHandler( + getHandlers('onReanimatedUpdateEvent') + ); + const reanimedStateChangeHandler = Reanimated?.useComposedEventHandler( + getHandlers('onReanimatedStateChange') + ); + const reanimatedTouchEventHandler = Reanimated?.useComposedEventHandler( + getHandlers('onReanimatedTouchEvent') + ); + return ( ( - method: - | ((event: GestureHandlerEvent) => void) - | { - workletEventHandler: { - worklet: (event: GestureHandlerEvent) => void; - }; - } - | null - | undefined, - event: GestureHandlerEvent -): void { - if (!method) { - return; - } - - if (typeof method === 'function') { - method(event); - return; - } - - if ('workletEventHandler' in method) { - if ('worklet' in method.workletEventHandler) { - invokeDetectorEvent(method.workletEventHandler.worklet, event); - } - return; - } -} - -export function getHandlerTag(e: GestureHandlerEvent): number { - if ('nativeEvent' in e) { - return (e.nativeEvent as any).handlerTag; - } - if ('handlerTag' in e) { - return (e as any).handlerTag; - } - - throw new Error(tagMessage('Cannot extract handler tag from event')); -} From 0f58258e6f8b36d84982729ae7977b36ebbd4255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 22 Sep 2025 07:47:33 +0200 Subject: [PATCH 75/90] empty set array --- .../src/v3/NativeDetector/HostGestureDetector.web.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx index b61b7df1ed..03a071e6d2 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx @@ -26,6 +26,7 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { const attachedHandlers = useRef>(new Set()); const attachedNativeHandlers = useRef>(new Set()); const attachedLogicHandlers = useRef>>(new Map()); + const emptySet = new Set(); const detachHandlers = ( currentHandlerTags: Set, @@ -126,15 +127,15 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }); logicChildrenToDetach.forEach((tag) => { - detachHandlers(new Set(), attachedLogicHandlers.current.get(tag)!); + detachHandlers(emptySet, attachedLogicHandlers.current.get(tag)!); }); }, [props.logicChildren]); useEffect(() => { return () => { - detachHandlers(new Set(), attachedHandlers.current); + detachHandlers(emptySet, attachedHandlers.current); attachedLogicHandlers?.current.forEach((childHandlerTags) => { - detachHandlers(new Set(), childHandlerTags); + detachHandlers(emptySet, childHandlerTags); }); }; }, []); From 93412f4ec458dd15d58d30f603ca9b0d7c8ad5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 22 Sep 2025 07:50:17 +0200 Subject: [PATCH 76/90] avoiding indentation --- .../src/v3/LogicDetector.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 3ae8005ca0..041dd86d09 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -16,14 +16,16 @@ export function LogicDetector( const handleRef = useCallback((node: any) => { viewRef.current = node; - if (node) { - if (Platform.OS === 'web') { - setViewTag(node); - } else { - const tag = findNodeHandle(node); - if (tag != null) { - setViewTag(tag); - } + if (!node) { + return; + } + + if (Platform.OS === 'web') { + setViewTag(node); + } else { + const tag = findNodeHandle(node); + if (tag != null) { + setViewTag(tag); } } }, []); From df8c9b71cc92449b3fc5b0fb360e3065320b1712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 22 Sep 2025 08:21:05 +0200 Subject: [PATCH 77/90] reformated native children detach --- .../src/v3/NativeDetector/HostGestureDetector.web.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx index 03a071e6d2..6fbd4adae3 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx @@ -74,11 +74,10 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }; useEffect(() => { - const newHandlerTags = attachedHandlers.current.difference( + attachedHandlers.current = attachedHandlers.current.difference( attachedNativeHandlers.current ); - attachedNativeHandlers.current.clear(); - detachHandlers(newHandlerTags, attachedNativeHandlers.current); + detachHandlers(attachedHandlers.current, attachedNativeHandlers.current); }, [children]); useEffect(() => { From 0cabeb7d0b0a403760d959027916d6d95b1514d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 22 Sep 2025 09:30:06 +0200 Subject: [PATCH 78/90] fix logic error --- .../apple/RNGestureHandlerDetector.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index 746c9fdd6a..a0b6a53adf 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -170,13 +170,13 @@ - (void)attachHandlers:(const std::vector &)handlerTags [_nativeHandlers addObject:@(tag)]; } else { if (actionType == RNGestureHandlerActionTypeLogicDetector) { - [[handlerManager registry] handlerWithTag:@(tag)].hostDetectorTag = @(self.tag); [handlerManager attachGestureHandler:@(tag) toViewWithTag:@(viewTag) withActionType:actionType]; } else { [handlerManager.registry attachHandlerWithTag:@(tag) toView:self withActionType:actionType]; } [attachedHandlers addObject:@(tag)]; } + [[handlerManager registry] handlerWithTag:@(tag)].hostDetectorTag = @(self.tag); } } @@ -187,7 +187,7 @@ - (void)attachHandlers:(const std::vector &)handlerTags } // This covers the case where `NativeViewGestureHandlers` are attached after child views were created. - if (!self.subviews[0]) { + if (self.subviews[0]) { [self tryAttachNativeHandlersToChildView]; } } From adc3c2205eb821b140c7be3024c06793047769fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 29 Sep 2025 10:03:27 +0200 Subject: [PATCH 79/90] send event refactor --- .../apple/RNGestureHandlerManager.mm | 71 +++++++++++-------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index 998ebbac49..e9dc313905 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -312,36 +312,17 @@ - (void)sendEvent:(RNGestureHandlerStateChange *)event case RNGestureHandlerActionTypeLogicDetector: { NSNumber *hostDetectorTag = [_registry handlerWithTag:event.handlerTag].hostDetectorTag; detectorView = [self viewForReactTag:hostDetectorTag]; - // intentionally fall through to RNGestureHandlerActionTypeNativeDetector + [self sendNativeOrLogicEvent:event + withActionType:actionType + forHandlerType:eventHandlerType + forView:detectorView]; + break; } case RNGestureHandlerActionTypeNativeDetector: { - if ([event isKindOfClass:[RNGestureHandlerEvent class]]) { - switch (eventHandlerType) { - case RNGestureHandlerEventHandlerTypeAnimated: - [self sendEventForNativeAnimatedEvent:event]; - break; - case RNGestureHandlerEventHandlerTypeReanimated: { - RNGestureHandlerEvent *gestureEvent = (RNGestureHandlerEvent *)event; - auto nativeEvent = [gestureEvent getReanimatedNativeEvent]; - [(RNGestureHandlerDetector *)detectorView dispatchReanimatedGestureEvent:nativeEvent]; - break; - } - case RNGestureHandlerEventHandlerTypeJS: { - RNGestureHandlerEvent *gestureEvent = (RNGestureHandlerEvent *)event; - auto nativeEvent = [gestureEvent getNativeEvent]; - [(RNGestureHandlerDetector *)detectorView dispatchGestureEvent:nativeEvent]; - break; - } - } - } else { - if (eventHandlerType == RNGestureHandlerEventHandlerTypeReanimated) { - auto nativeEvent = [event getReanimatedNativeEvent]; - [(RNGestureHandlerDetector *)detectorView dispatchReanimatedStateChangeEvent:nativeEvent]; - } else { - auto nativeEvent = [event getNativeEvent]; - [(RNGestureHandlerDetector *)detectorView dispatchStateChangeEvent:nativeEvent]; - } - } + [self sendNativeOrLogicEvent:event + withActionType:actionType + forHandlerType:eventHandlerType + forView:detectorView]; break; } @@ -488,6 +469,40 @@ - (void)sendEventForDeviceEvent:(RNGestureHandlerStateChange *)event [_eventDispatcher sendDeviceEventWithName:@"onGestureHandlerStateChange" body:body]; } +- (void)sendNativeOrLogicEvent:(RNGestureHandlerStateChange *)event + withActionType:(RNGestureHandlerActionType)actionType + forHandlerType:(RNGestureHandlerEventHandlerType)eventHandlerType + forView:(RNGHUIView *)detectorView +{ + if ([event isKindOfClass:[RNGestureHandlerEvent class]]) { + switch (eventHandlerType) { + case RNGestureHandlerEventHandlerTypeAnimated: + [self sendEventForNativeAnimatedEvent:event]; + break; + case RNGestureHandlerEventHandlerTypeReanimated: { + RNGestureHandlerEvent *gestureEvent = (RNGestureHandlerEvent *)event; + auto nativeEvent = [gestureEvent getReanimatedNativeEvent]; + [(RNGestureHandlerDetector *)detectorView dispatchReanimatedGestureEvent:nativeEvent]; + break; + } + case RNGestureHandlerEventHandlerTypeJS: { + RNGestureHandlerEvent *gestureEvent = (RNGestureHandlerEvent *)event; + auto nativeEvent = [gestureEvent getNativeEvent]; + [(RNGestureHandlerDetector *)detectorView dispatchGestureEvent:nativeEvent]; + break; + } + } + } else { + if (eventHandlerType == RNGestureHandlerEventHandlerTypeReanimated) { + auto nativeEvent = [event getReanimatedNativeEvent]; + [(RNGestureHandlerDetector *)detectorView dispatchReanimatedStateChangeEvent:nativeEvent]; + } else { + auto nativeEvent = [event getNativeEvent]; + [(RNGestureHandlerDetector *)detectorView dispatchStateChangeEvent:nativeEvent]; + } + } +} + - (RNGHUIView *)viewForReactTag:(NSNumber *)reactTag { return [_viewRegistry viewForReactTag:reactTag]; From 149c323301d580b0aabb2b546e5b1c9df2382989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 29 Sep 2025 10:27:56 +0200 Subject: [PATCH 80/90] moved empty set to utils --- .../src/v3/NativeDetector/HostGestureDetector.web.tsx | 8 ++++---- .../src/v3/NativeDetector/utils.ts | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx index 6fbd4adae3..57b98234f8 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx @@ -4,6 +4,7 @@ import { ActionType } from '../../ActionType'; import { PropsRef } from '../../web/interfaces'; import { View } from 'react-native'; import { tagMessage } from '../../utils'; +import { EMPTY_SET } from './utils'; export interface GestureHandlerDetectorProps extends PropsRef { handlerTags: number[]; @@ -26,7 +27,6 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { const attachedHandlers = useRef>(new Set()); const attachedNativeHandlers = useRef>(new Set()); const attachedLogicHandlers = useRef>>(new Map()); - const emptySet = new Set(); const detachHandlers = ( currentHandlerTags: Set, @@ -126,15 +126,15 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => { }); logicChildrenToDetach.forEach((tag) => { - detachHandlers(emptySet, attachedLogicHandlers.current.get(tag)!); + detachHandlers(EMPTY_SET, attachedLogicHandlers.current.get(tag)!); }); }, [props.logicChildren]); useEffect(() => { return () => { - detachHandlers(emptySet, attachedHandlers.current); + detachHandlers(EMPTY_SET, attachedHandlers.current); attachedLogicHandlers?.current.forEach((childHandlerTags) => { - detachHandlers(emptySet, childHandlerTags); + detachHandlers(EMPTY_SET, childHandlerTags); }); }; }, []); diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts b/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts index 05a2936b61..78c4ea5778 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts @@ -147,3 +147,5 @@ export function configureRelations( ); } } + +export const EMPTY_SET = new Set(); From 1bcda3c9cd36897df7f725f94b318c94b69f723f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 29 Sep 2025 10:33:09 +0200 Subject: [PATCH 81/90] fix can coalesce check to v3 --- .../gesturehandler/react/events/RNGestureHandlerEvent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt index 63d33af7e1..cf1ff05cab 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerEvent.kt @@ -60,7 +60,7 @@ class RNGestureHandlerEvent private constructor() : Event } // Unfortunately getCoalescingKey is not considered when sending event to C++, therefore we have to disable coalescing in v3 - override fun canCoalesce() = actionType != GestureHandler.ACTION_TYPE_NATIVE_DETECTOR + override fun canCoalesce() = !GestureHandler.usesNativeOrLogicDetector(actionType) override fun getCoalescingKey() = coalescingKey From f0ea23f777e37147d2ec69fbd6dd0a5077298b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 29 Sep 2025 14:31:58 +0200 Subject: [PATCH 82/90] coalesce check in touch event --- .../gesturehandler/react/events/RNGestureHandlerTouchEvent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerTouchEvent.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerTouchEvent.kt index 9bc121cf9e..5d3d47a63c 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerTouchEvent.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerTouchEvent.kt @@ -39,7 +39,7 @@ class RNGestureHandlerTouchEvent private constructor() : Event Date: Wed, 1 Oct 2025 08:48:38 +0200 Subject: [PATCH 83/90] simplify logic props creation --- .../src/v3/LogicDetector.tsx | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx index 041dd86d09..c24bdb9b00 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx @@ -49,21 +49,16 @@ export function LogicDetector( return; } - const logicProps = - Platform.OS === 'web' - ? { - viewRef, - viewTag, - handlerTags: isComposedGesture(props.gesture) - ? props.gesture.tags - : [props.gesture.tag], - } - : { - viewTag, - handlerTags: isComposedGesture(props.gesture) - ? props.gesture.tags - : [props.gesture.tag], - }; + const logicProps = { + viewTag, + handlerTags: isComposedGesture(props.gesture) + ? props.gesture.tags + : [props.gesture.tag], + }; + + if (Platform.OS === 'web') { + Object.assign(logicProps, { viewRef }); + } register(logicProps, logicMethods as RefObject>); From fae6478a435270da14a04112451db5f3e98a14b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 3 Oct 2025 17:37:18 +0200 Subject: [PATCH 84/90] added delegate detector --- .../react-native-gesture-handler/src/index.ts | 4 +- .../src/v3/LogicDetector/DelegateDetector.tsx | 167 ++++++++++++++++++ .../v3/{ => LogicDetector}/LogicDetector.tsx | 10 +- .../useDetectorContext.ts | 4 +- .../src/v3/NativeDetector/NativeDetector.tsx | 150 ++++------------ 5 files changed, 211 insertions(+), 124 deletions(-) create mode 100644 packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx rename packages/react-native-gesture-handler/src/v3/{ => LogicDetector}/LogicDetector.tsx (84%) rename packages/react-native-gesture-handler/src/v3/{NativeDetector => LogicDetector}/useDetectorContext.ts (83%) diff --git a/packages/react-native-gesture-handler/src/index.ts b/packages/react-native-gesture-handler/src/index.ts index 8c9d8b0ef3..3fd235504b 100644 --- a/packages/react-native-gesture-handler/src/index.ts +++ b/packages/react-native-gesture-handler/src/index.ts @@ -165,7 +165,9 @@ export { default as DrawerLayout } from './components/DrawerLayout'; export type { NativeDetectorProps } from './v3/NativeDetector/NativeDetector'; export { NativeDetector } from './v3/NativeDetector/NativeDetector'; -export { LogicDetector } from './v3/LogicDetector'; +export { LogicDetector } from './v3/LogicDetector/LogicDetector'; +export { DelegateDetector } from './v3/LogicDetector/DelegateDetector'; + export * from './v3/hooks/useGesture'; export * from './v3/hooks/relations'; diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx new file mode 100644 index 0000000000..a14318c2e8 --- /dev/null +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx @@ -0,0 +1,167 @@ +import React, { RefObject, useCallback, useRef, useState } from 'react'; +import { Animated, StyleSheet } from 'react-native'; +import HostGestureDetector from '../NativeDetector/HostGestureDetector'; +import { tagMessage } from '../../utils'; +import { + LogicChildren, + Gesture, + GestureEvents, + GestureHandlerEvent, +} from '../types'; +import { DetectorContext } from './useDetectorContext'; +import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; +import { configureRelations } from '../NativeDetector/utils'; +import { isComposedGesture } from '../hooks/utils/relationUtils'; + +export interface NativeDetectorProps { + children?: React.ReactNode; + gesture: Gesture; +} + +const AnimatedNativeDetector = + Animated.createAnimatedComponent(HostGestureDetector); + +const ReanimatedNativeDetector = + Reanimated?.default.createAnimatedComponent(HostGestureDetector); + +export function DelegateDetector({ + gesture, + children, +}: NativeDetectorProps) { + const [logicChildren, setLogicChildren] = useState([]); + const logicMethods = useRef>>>( + new Map() + ); + + const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents + ? AnimatedNativeDetector + : gesture.config.shouldUseReanimated + ? ReanimatedNativeDetector + : HostGestureDetector; + + const register = useCallback( + (child: LogicChildren, methods: RefObject>) => { + setLogicChildren((prev) => { + const index = prev.findIndex((c) => c.viewTag === child.viewTag); + if (index !== -1) { + const updated = [...prev]; + updated[index] = child; + return updated; + } + + return [...prev, child]; + }); + + child.handlerTags.forEach((tag) => { + logicMethods.current.set(tag, methods); + }); + }, + [] + ); + + const unregister = useCallback((childTag: number) => { + setLogicChildren((prev) => prev.filter((c) => c.viewTag !== childTag)); + }, []); + + // It might happen only with ReanimatedNativeDetector + if (!NativeDetectorComponent) { + throw new Error( + tagMessage( + 'Gesture expects to run on the UI thread, but failed to create the Reanimated NativeDetector.' + ) + ); + } + + configureRelations(gesture); + + const handleGestureEvent = (key: keyof GestureEvents) => { + return (e: GestureHandlerEvent) => { + if (gesture.gestureEvents[key]) { + gesture.gestureEvents[key](e); + } + + logicMethods.current.forEach((ref) => { + const method = ref.current?.[key]; + if (method) { + method(e); + } + }); + }; + }; + + const getHandlers = useCallback( + (key: keyof GestureEvents) => { + const handlers: ((e: GestureHandlerEvent) => void)[] = []; + + if (gesture.gestureEvents[key]) { + handlers.push( + gesture.gestureEvents[key] as ( + e: GestureHandlerEvent + ) => void + ); + } + + logicMethods.current.forEach((ref) => { + const handler = ref.current?.[key]; + if (handler) { + handlers.push( + handler as (e: GestureHandlerEvent) => void + ); + } + }); + + return handlers; + }, + [logicChildren, gesture.gestureEvents] + ); + + const reanimatedEventHandler = Reanimated?.useComposedEventHandler( + getHandlers('onReanimatedUpdateEvent') + ); + const reanimedStateChangeHandler = Reanimated?.useComposedEventHandler( + getHandlers('onReanimatedStateChange') + ); + const reanimatedTouchEventHandler = Reanimated?.useComposedEventHandler( + getHandlers('onReanimatedTouchEvent') + ); + + return ( + + + {children} + + + ); +} + +const styles = StyleSheet.create({ + detector: { + display: 'contents', + // TODO: remove, debug info only + backgroundColor: 'red', + }, +}); diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector/LogicDetector.tsx similarity index 84% rename from packages/react-native-gesture-handler/src/v3/LogicDetector.tsx rename to packages/react-native-gesture-handler/src/v3/LogicDetector/LogicDetector.tsx index c24bdb9b00..50aa16a488 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector/LogicDetector.tsx @@ -1,10 +1,10 @@ import { RefObject, useCallback, useEffect, useRef, useState } from 'react'; -import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; +import { Wrap } from '../../handlers/gestures/GestureDetector/Wrap'; import { findNodeHandle, Platform } from 'react-native'; -import { useDetectorContext } from './NativeDetector/useDetectorContext'; -import { NativeDetectorProps } from './NativeDetector/NativeDetector'; -import { isComposedGesture } from './hooks/utils/relationUtils'; -import { GestureEvents } from './types'; +import { useDetectorContext } from './useDetectorContext'; +import { NativeDetectorProps } from '../NativeDetector/NativeDetector'; +import { isComposedGesture } from '../hooks/utils/relationUtils'; +import { GestureEvents } from '../types'; export function LogicDetector( props: NativeDetectorProps diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts b/packages/react-native-gesture-handler/src/v3/LogicDetector/useDetectorContext.ts similarity index 83% rename from packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts rename to packages/react-native-gesture-handler/src/v3/LogicDetector/useDetectorContext.ts index 97c2c90dec..65ceee2a75 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector/useDetectorContext.ts @@ -14,7 +14,9 @@ export const DetectorContext = createContext(null); export function useDetectorContext() { const ctx = useContext(DetectorContext); if (!ctx) { - throw new Error('Logic detector must be a descendant of a Native Detector'); + throw new Error( + 'Logic detector must be a descendant of a delegate detector' + ); } return ctx; } diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx index 6bd6b3a120..01af531761 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -1,15 +1,9 @@ -import React, { RefObject, useCallback, useRef, useState } from 'react'; +import React from 'react'; +import { Gesture } from '../types'; +import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { tagMessage } from '../../utils'; -import { - LogicChildren, - Gesture, - GestureEvents, - GestureHandlerEvent, -} from '../types'; -import { DetectorContext } from './useDetectorContext'; -import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; import { configureRelations } from './utils'; import { isComposedGesture } from '../hooks/utils/relationUtils'; @@ -28,41 +22,12 @@ export function NativeDetector({ gesture, children, }: NativeDetectorProps) { - const [logicChildren, setLogicChildren] = useState([]); - const logicMethods = useRef>>>( - new Map() - ); - const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents ? AnimatedNativeDetector : gesture.config.shouldUseReanimated ? ReanimatedNativeDetector : HostGestureDetector; - const register = useCallback( - (child: LogicChildren, methods: RefObject>) => { - setLogicChildren((prev) => { - const index = prev.findIndex((c) => c.viewTag === child.viewTag); - if (index !== -1) { - const updated = [...prev]; - updated[index] = child; - return updated; - } - - return [...prev, child]; - }); - - child.handlerTags.forEach((tag) => { - logicMethods.current.set(tag, methods); - }); - }, - [] - ); - - const unregister = useCallback((childTag: number) => { - setLogicChildren((prev) => prev.filter((c) => c.viewTag !== childTag)); - }, []); - // It might happen only with ReanimatedNativeDetector if (!NativeDetectorComponent) { throw new Error( @@ -74,87 +39,38 @@ export function NativeDetector({ configureRelations(gesture); - const handleGestureEvent = (key: keyof GestureEvents) => { - return (e: GestureHandlerEvent) => { - if (gesture.gestureEvents[key]) { - gesture.gestureEvents[key](e); + return ( + { - const method = ref.current?.[key]; - if (method) { - method(e); - } - }); - }; - }; - - const getHandlers = useCallback( - (key: keyof GestureEvents) => { - const handlers: ((e: GestureHandlerEvent) => void)[] = []; - - if (gesture.gestureEvents[key]) { - handlers.push( - gesture.gestureEvents[key] as ( - e: GestureHandlerEvent - ) => void - ); + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerEvent={gesture.gestureEvents.onGestureHandlerEvent} + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerTouchEvent={ + gesture.gestureEvents.onGestureHandlerTouchEvent } - - logicMethods.current.forEach((ref) => { - const handler = ref.current?.[key]; - if (handler) { - handlers.push( - handler as (e: GestureHandlerEvent) => void - ); - } - }); - - return handlers; - }, - [logicChildren, gesture.gestureEvents] - ); - - const reanimatedEventHandler = Reanimated?.useComposedEventHandler( - getHandlers('onReanimatedUpdateEvent') - ); - const reanimedStateChangeHandler = Reanimated?.useComposedEventHandler( - getHandlers('onReanimatedStateChange') - ); - const reanimatedTouchEventHandler = Reanimated?.useComposedEventHandler( - getHandlers('onReanimatedTouchEvent') - ); - - return ( - - - {children} - - + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerReanimatedStateChange={ + gesture.gestureEvents.onReanimatedStateChange + } + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerReanimatedEvent={ + gesture.gestureEvents.onReanimatedUpdateEvent + } + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerReanimatedTouchEvent={ + gesture.gestureEvents.onReanimatedTouchEvent + } + onGestureHandlerAnimatedEvent={ + gesture.gestureEvents.onGestureHandlerAnimatedEvent + } + moduleId={globalThis._RNGH_MODULE_ID} + handlerTags={isComposedGesture(gesture) ? gesture.tags : [gesture.tag]} + style={styles.detector}> + {children} + ); } From f084754e3613c61e58a7326dd01268c45521efdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Fri, 3 Oct 2025 18:15:50 +0200 Subject: [PATCH 85/90] extract common --- .../src/v3/LogicDetector/DelegateDetector.tsx | 55 +++++-------------- .../src/v3/NativeDetector/NativeDetector.tsx | 21 ++----- .../src/v3/NativeDetector/utils.ts | 13 +++++ 3 files changed, 34 insertions(+), 55 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx index a14318c2e8..f023d4f36f 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx @@ -1,28 +1,19 @@ import React, { RefObject, useCallback, useRef, useState } from 'react'; -import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from '../NativeDetector/HostGestureDetector'; -import { tagMessage } from '../../utils'; -import { - LogicChildren, - Gesture, - GestureEvents, - GestureHandlerEvent, -} from '../types'; +import { LogicChildren, GestureEvents, GestureHandlerEvent } from '../types'; import { DetectorContext } from './useDetectorContext'; import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; -import { configureRelations } from '../NativeDetector/utils'; +import { + configureRelations, + ensureNativeDetectorComponent, +} from '../NativeDetector/utils'; import { isComposedGesture } from '../hooks/utils/relationUtils'; - -export interface NativeDetectorProps { - children?: React.ReactNode; - gesture: Gesture; -} - -const AnimatedNativeDetector = - Animated.createAnimatedComponent(HostGestureDetector); - -const ReanimatedNativeDetector = - Reanimated?.default.createAnimatedComponent(HostGestureDetector); +import { + AnimatedNativeDetector, + NativeDetectorProps, + nativeDetectorStyles, + ReanimatedNativeDetector, +} from '../NativeDetector/NativeDetector'; export function DelegateDetector({ gesture, @@ -63,17 +54,6 @@ export function DelegateDetector({ setLogicChildren((prev) => prev.filter((c) => c.viewTag !== childTag)); }, []); - // It might happen only with ReanimatedNativeDetector - if (!NativeDetectorComponent) { - throw new Error( - tagMessage( - 'Gesture expects to run on the UI thread, but failed to create the Reanimated NativeDetector.' - ) - ); - } - - configureRelations(gesture); - const handleGestureEvent = (key: keyof GestureEvents) => { return (e: GestureHandlerEvent) => { if (gesture.gestureEvents[key]) { @@ -125,6 +105,9 @@ export function DelegateDetector({ getHandlers('onReanimatedTouchEvent') ); + ensureNativeDetectorComponent(NativeDetectorComponent); + configureRelations(gesture); + return ( ({ onGestureHandlerReanimatedTouchEvent={reanimatedTouchEventHandler} moduleId={globalThis._RNGH_MODULE_ID} handlerTags={isComposedGesture(gesture) ? gesture.tags : [gesture.tag]} - style={styles.detector} + style={nativeDetectorStyles.detector} logicChildren={logicChildren}> {children} ); } - -const styles = StyleSheet.create({ - detector: { - display: 'contents', - // TODO: remove, debug info only - backgroundColor: 'red', - }, -}); diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx index 01af531761..6fd667507f 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -3,8 +3,7 @@ import { Gesture } from '../types'; import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; -import { tagMessage } from '../../utils'; -import { configureRelations } from './utils'; +import { configureRelations, ensureNativeDetectorComponent } from './utils'; import { isComposedGesture } from '../hooks/utils/relationUtils'; export interface NativeDetectorProps { @@ -12,10 +11,10 @@ export interface NativeDetectorProps { gesture: Gesture; } -const AnimatedNativeDetector = +export const AnimatedNativeDetector = Animated.createAnimatedComponent(HostGestureDetector); -const ReanimatedNativeDetector = +export const ReanimatedNativeDetector = Reanimated?.default.createAnimatedComponent(HostGestureDetector); export function NativeDetector({ @@ -28,15 +27,7 @@ export function NativeDetector({ ? ReanimatedNativeDetector : HostGestureDetector; - // It might happen only with ReanimatedNativeDetector - if (!NativeDetectorComponent) { - throw new Error( - tagMessage( - 'Gesture expects to run on the UI thread, but failed to create the Reanimated NativeDetector.' - ) - ); - } - + ensureNativeDetectorComponent(NativeDetectorComponent); configureRelations(gesture); return ( @@ -68,13 +59,13 @@ export function NativeDetector({ } moduleId={globalThis._RNGH_MODULE_ID} handlerTags={isComposedGesture(gesture) ? gesture.tags : [gesture.tag]} - style={styles.detector}> + style={nativeDetectorStyles.detector}> {children} ); } -const styles = StyleSheet.create({ +export const nativeDetectorStyles = StyleSheet.create({ detector: { display: 'contents', // TODO: remove, debug info only diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts b/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts index d6abd31075..cdf442e592 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts @@ -5,6 +5,7 @@ // For `simultaneousHandlers` we use Set as the order doesn't matter. import RNGestureHandlerModule from '../../RNGestureHandlerModule'; +import { tagMessage } from '../../utils'; import { isComposedGesture, prepareRelations, @@ -150,4 +151,16 @@ export function configureRelations( RNGestureHandlerModule.flushOperations(); } +export function ensureNativeDetectorComponent( + NativeDetectorComponent: unknown +): asserts NativeDetectorComponent { + if (!NativeDetectorComponent) { + throw new Error( + tagMessage( + 'Gesture expects to run on the UI thread, but failed to create the Reanimated NativeDetector.' + ) + ); + } +} + export const EMPTY_SET = new Set(); From 94d9079706b7d5d5af4cedf65c0662f0107373db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 6 Oct 2025 11:06:50 +0200 Subject: [PATCH 86/90] merge could have been better --- .../src/v3/NativeDetector/NativeDetector.tsx | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx index b2a7b6714c..6fd667507f 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -1,4 +1,6 @@ -import React, { RefObject, useCallback, useRef, useState } from 'react'; +import React from 'react'; +import { Gesture } from '../types'; +import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { configureRelations, ensureNativeDetectorComponent } from './utils'; @@ -19,11 +21,6 @@ export function NativeDetector({ gesture, children, }: NativeDetectorProps) { - const [logicChildren, setLogicChildren] = useState([]); - const logicMethods = useRef>>>( - new Map() - ); - const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents ? AnimatedNativeDetector : gesture.config.shouldUseReanimated @@ -33,31 +30,32 @@ export function NativeDetector({ ensureNativeDetectorComponent(NativeDetectorComponent); configureRelations(gesture); - const handleGestureEvent = (key: keyof GestureEvents) => { - return (e: GestureHandlerEvent) => { - if (gesture.gestureEvents[key]) { - gesture.gestureEvents[key](e); + return ( + { - const method = ref.current?.[key]; - if (method) { - method(e); - } - }); - }; - }; - - const getHandlers = useCallback( - (key: keyof GestureEvents) => { - const handlers: ((e: GestureHandlerEvent) => void)[] = []; - - if (gesture.gestureEvents[key]) { - handlers.push( - gesture.gestureEvents[key] as ( - e: GestureHandlerEvent - ) => void - ); + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerEvent={gesture.gestureEvents.onGestureHandlerEvent} + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerTouchEvent={ + gesture.gestureEvents.onGestureHandlerTouchEvent + } + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerReanimatedStateChange={ + gesture.gestureEvents.onReanimatedStateChange + } + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerReanimatedEvent={ + gesture.gestureEvents.onReanimatedUpdateEvent + } + // @ts-ignore This is a type mismatch between RNGH types and RN Codegen types + onGestureHandlerReanimatedTouchEvent={ + gesture.gestureEvents.onReanimatedTouchEvent + } + onGestureHandlerAnimatedEvent={ + gesture.gestureEvents.onGestureHandlerAnimatedEvent } moduleId={globalThis._RNGH_MODULE_ID} handlerTags={isComposedGesture(gesture) ? gesture.tags : [gesture.tag]} From a9ab3a50892124c2e8198d87d1c879a01da65a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 6 Oct 2025 12:51:54 +0200 Subject: [PATCH 87/90] changed folder structure --- .../src/__tests__/RelationsTraversal.test.tsx | 2 +- .../react-native-gesture-handler/src/index.ts | 8 +-- .../HostGestureDetector.tsx | 0 .../HostGestureDetector.web.tsx | 0 .../LogicDetector/DelegateDetector.tsx | 15 ++-- .../LogicDetector/LogicDetector.tsx | 8 +-- .../LogicDetector/useDetectorContext.ts | 2 +- .../NativeDetector.tsx | 0 .../v3/{NativeDetector => Detectors}/utils.ts | 0 .../src/v3/LogicDetector.tsx | 71 ------------------- .../v3/NativeDetector/useDetectorContext.ts | 20 ------ 11 files changed, 16 insertions(+), 110 deletions(-) rename packages/react-native-gesture-handler/src/v3/{NativeDetector => Detectors}/HostGestureDetector.tsx (100%) rename packages/react-native-gesture-handler/src/v3/{NativeDetector => Detectors}/HostGestureDetector.web.tsx (100%) rename packages/react-native-gesture-handler/src/v3/{ => Detectors}/LogicDetector/DelegateDetector.tsx (92%) rename packages/react-native-gesture-handler/src/v3/{ => Detectors}/LogicDetector/LogicDetector.tsx (87%) rename packages/react-native-gesture-handler/src/v3/{ => Detectors}/LogicDetector/useDetectorContext.ts (89%) rename packages/react-native-gesture-handler/src/v3/{NativeDetector => Detectors}/NativeDetector.tsx (100%) rename packages/react-native-gesture-handler/src/v3/{NativeDetector => Detectors}/utils.ts (100%) delete mode 100644 packages/react-native-gesture-handler/src/v3/LogicDetector.tsx delete mode 100644 packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts diff --git a/packages/react-native-gesture-handler/src/__tests__/RelationsTraversal.test.tsx b/packages/react-native-gesture-handler/src/__tests__/RelationsTraversal.test.tsx index fb8128dbdd..c49d8f6622 100644 --- a/packages/react-native-gesture-handler/src/__tests__/RelationsTraversal.test.tsx +++ b/packages/react-native-gesture-handler/src/__tests__/RelationsTraversal.test.tsx @@ -1,7 +1,7 @@ import { tagMessage } from '../utils'; import { useExclusive, useRace, useSimultaneous } from '../v3/hooks/relations'; import { useGesture } from '../v3/hooks/useGesture'; -import { configureRelations } from '../v3/NativeDetector/utils'; +import { configureRelations } from '../v3/Detectors/utils'; import { SingleGesture, SingleGestureName } from '../v3/types'; import { renderHook } from '@testing-library/react-native'; diff --git a/packages/react-native-gesture-handler/src/index.ts b/packages/react-native-gesture-handler/src/index.ts index 3fd235504b..ef65dd2189 100644 --- a/packages/react-native-gesture-handler/src/index.ts +++ b/packages/react-native-gesture-handler/src/index.ts @@ -162,11 +162,11 @@ export type { } from './components/DrawerLayout'; export { default as DrawerLayout } from './components/DrawerLayout'; -export type { NativeDetectorProps } from './v3/NativeDetector/NativeDetector'; -export { NativeDetector } from './v3/NativeDetector/NativeDetector'; +export type { NativeDetectorProps } from './v3/Detectors/NativeDetector'; +export { NativeDetector } from './v3/Detectors/NativeDetector'; -export { LogicDetector } from './v3/LogicDetector/LogicDetector'; -export { DelegateDetector } from './v3/LogicDetector/DelegateDetector'; +export { LogicDetector } from './v3/Detectors/LogicDetector/LogicDetector'; +export { DelegateDetector } from './v3/Detectors/LogicDetector/DelegateDetector'; export * from './v3/hooks/useGesture'; export * from './v3/hooks/relations'; diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.tsx b/packages/react-native-gesture-handler/src/v3/Detectors/HostGestureDetector.tsx similarity index 100% rename from packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.tsx rename to packages/react-native-gesture-handler/src/v3/Detectors/HostGestureDetector.tsx diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/Detectors/HostGestureDetector.web.tsx similarity index 100% rename from packages/react-native-gesture-handler/src/v3/NativeDetector/HostGestureDetector.web.tsx rename to packages/react-native-gesture-handler/src/v3/Detectors/HostGestureDetector.web.tsx diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx b/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/DelegateDetector.tsx similarity index 92% rename from packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx rename to packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/DelegateDetector.tsx index f023d4f36f..6658775039 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector/DelegateDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/DelegateDetector.tsx @@ -1,19 +1,16 @@ import React, { RefObject, useCallback, useRef, useState } from 'react'; -import HostGestureDetector from '../NativeDetector/HostGestureDetector'; -import { LogicChildren, GestureEvents, GestureHandlerEvent } from '../types'; +import HostGestureDetector from '../HostGestureDetector'; +import { LogicChildren, GestureEvents, GestureHandlerEvent } from '../../types'; import { DetectorContext } from './useDetectorContext'; -import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; -import { - configureRelations, - ensureNativeDetectorComponent, -} from '../NativeDetector/utils'; -import { isComposedGesture } from '../hooks/utils/relationUtils'; +import { Reanimated } from '../../../handlers/gestures/reanimatedWrapper'; +import { configureRelations, ensureNativeDetectorComponent } from '../utils'; +import { isComposedGesture } from '../../hooks/utils/relationUtils'; import { AnimatedNativeDetector, NativeDetectorProps, nativeDetectorStyles, ReanimatedNativeDetector, -} from '../NativeDetector/NativeDetector'; +} from '../NativeDetector'; export function DelegateDetector({ gesture, diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/LogicDetector.tsx similarity index 87% rename from packages/react-native-gesture-handler/src/v3/LogicDetector/LogicDetector.tsx rename to packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/LogicDetector.tsx index 50aa16a488..166c16ea18 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/LogicDetector.tsx @@ -1,10 +1,10 @@ import { RefObject, useCallback, useEffect, useRef, useState } from 'react'; -import { Wrap } from '../../handlers/gestures/GestureDetector/Wrap'; +import { Wrap } from '../../../handlers/gestures/GestureDetector/Wrap'; import { findNodeHandle, Platform } from 'react-native'; import { useDetectorContext } from './useDetectorContext'; -import { NativeDetectorProps } from '../NativeDetector/NativeDetector'; -import { isComposedGesture } from '../hooks/utils/relationUtils'; -import { GestureEvents } from '../types'; +import { NativeDetectorProps } from '../NativeDetector'; +import { isComposedGesture } from '../../hooks/utils/relationUtils'; +import { GestureEvents } from '../../types'; export function LogicDetector( props: NativeDetectorProps diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector/useDetectorContext.ts b/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/useDetectorContext.ts similarity index 89% rename from packages/react-native-gesture-handler/src/v3/LogicDetector/useDetectorContext.ts rename to packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/useDetectorContext.ts index 65ceee2a75..869c2a1904 100644 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector/useDetectorContext.ts +++ b/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/useDetectorContext.ts @@ -1,5 +1,5 @@ import { createContext, RefObject, useContext } from 'react'; -import { GestureEvents, LogicChildren } from '../types'; +import { GestureEvents, LogicChildren } from '../../types'; type DetectorContextType = { register: ( diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/Detectors/NativeDetector.tsx similarity index 100% rename from packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx rename to packages/react-native-gesture-handler/src/v3/Detectors/NativeDetector.tsx diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts b/packages/react-native-gesture-handler/src/v3/Detectors/utils.ts similarity index 100% rename from packages/react-native-gesture-handler/src/v3/NativeDetector/utils.ts rename to packages/react-native-gesture-handler/src/v3/Detectors/utils.ts diff --git a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx deleted file mode 100644 index c24bdb9b00..0000000000 --- a/packages/react-native-gesture-handler/src/v3/LogicDetector.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { RefObject, useCallback, useEffect, useRef, useState } from 'react'; -import { Wrap } from '../handlers/gestures/GestureDetector/Wrap'; -import { findNodeHandle, Platform } from 'react-native'; -import { useDetectorContext } from './NativeDetector/useDetectorContext'; -import { NativeDetectorProps } from './NativeDetector/NativeDetector'; -import { isComposedGesture } from './hooks/utils/relationUtils'; -import { GestureEvents } from './types'; - -export function LogicDetector( - props: NativeDetectorProps -) { - const { register, unregister } = useDetectorContext(); - const viewRef = useRef(null); - const [viewTag, setViewTag] = useState(-1); - const logicMethods = useRef(props.gesture.gestureEvents); - - const handleRef = useCallback((node: any) => { - viewRef.current = node; - if (!node) { - return; - } - - if (Platform.OS === 'web') { - setViewTag(node); - } else { - const tag = findNodeHandle(node); - if (tag != null) { - setViewTag(tag); - } - } - }, []); - - useEffect(() => { - logicMethods.current = props.gesture.gestureEvents; - }, [props.gesture.gestureEvents]); - - useEffect(() => { - if (viewTag === -1) { - return; - } - - // Native Detector differentiates Logic Children through a viewTag, - // thus if viewTag changes we have to reregister - unregister(viewTag); - }, [viewTag]); - - useEffect(() => { - if (viewTag === -1) { - return; - } - - const logicProps = { - viewTag, - handlerTags: isComposedGesture(props.gesture) - ? props.gesture.tags - : [props.gesture.tag], - }; - - if (Platform.OS === 'web') { - Object.assign(logicProps, { viewRef }); - } - - register(logicProps, logicMethods as RefObject>); - - return () => { - unregister(viewTag); - }; - }, [viewTag, props.gesture, register, unregister]); - - return {props.children}; -} diff --git a/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts b/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts deleted file mode 100644 index 97c2c90dec..0000000000 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/useDetectorContext.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createContext, RefObject, useContext } from 'react'; -import { GestureEvents, LogicChildren } from '../types'; - -type DetectorContextType = { - register: ( - child: LogicChildren, - methods: RefObject> - ) => void; - unregister: (child: number) => void; -}; - -export const DetectorContext = createContext(null); - -export function useDetectorContext() { - const ctx = useContext(DetectorContext); - if (!ctx) { - throw new Error('Logic detector must be a descendant of a Native Detector'); - } - return ctx; -} From 743124e399ca9ea4e816231e5c49ea952c93b1c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Mon, 6 Oct 2025 13:28:20 +0200 Subject: [PATCH 88/90] extracted common --- .../react-native-gesture-handler/src/index.ts | 2 +- .../LogicDetector/DelegateDetector.tsx | 2 +- .../Detectors/LogicDetector/LogicDetector.tsx | 2 +- .../src/v3/Detectors/NativeDetector.tsx | 28 ++++--------------- .../src/v3/Detectors/common.ts | 24 ++++++++++++++++ 5 files changed, 33 insertions(+), 25 deletions(-) create mode 100644 packages/react-native-gesture-handler/src/v3/Detectors/common.ts diff --git a/packages/react-native-gesture-handler/src/index.ts b/packages/react-native-gesture-handler/src/index.ts index ef65dd2189..08c3efbe53 100644 --- a/packages/react-native-gesture-handler/src/index.ts +++ b/packages/react-native-gesture-handler/src/index.ts @@ -162,7 +162,7 @@ export type { } from './components/DrawerLayout'; export { default as DrawerLayout } from './components/DrawerLayout'; -export type { NativeDetectorProps } from './v3/Detectors/NativeDetector'; +export type { NativeDetectorProps } from './v3/Detectors/common'; export { NativeDetector } from './v3/Detectors/NativeDetector'; export { LogicDetector } from './v3/Detectors/LogicDetector/LogicDetector'; diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/DelegateDetector.tsx b/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/DelegateDetector.tsx index 6658775039..dd370e9e04 100644 --- a/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/DelegateDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/DelegateDetector.tsx @@ -10,7 +10,7 @@ import { NativeDetectorProps, nativeDetectorStyles, ReanimatedNativeDetector, -} from '../NativeDetector'; +} from '../common'; export function DelegateDetector({ gesture, diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/LogicDetector.tsx index 166c16ea18..87004dea6d 100644 --- a/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/LogicDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/LogicDetector.tsx @@ -2,9 +2,9 @@ import { RefObject, useCallback, useEffect, useRef, useState } from 'react'; import { Wrap } from '../../../handlers/gestures/GestureDetector/Wrap'; import { findNodeHandle, Platform } from 'react-native'; import { useDetectorContext } from './useDetectorContext'; -import { NativeDetectorProps } from '../NativeDetector'; import { isComposedGesture } from '../../hooks/utils/relationUtils'; import { GestureEvents } from '../../types'; +import { NativeDetectorProps } from '../common'; export function LogicDetector( props: NativeDetectorProps diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/Detectors/NativeDetector.tsx index 6fd667507f..41e73c144a 100644 --- a/packages/react-native-gesture-handler/src/v3/Detectors/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/Detectors/NativeDetector.tsx @@ -1,21 +1,13 @@ import React from 'react'; -import { Gesture } from '../types'; -import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; -import { Animated, StyleSheet } from 'react-native'; import HostGestureDetector from './HostGestureDetector'; import { configureRelations, ensureNativeDetectorComponent } from './utils'; import { isComposedGesture } from '../hooks/utils/relationUtils'; - -export interface NativeDetectorProps { - children?: React.ReactNode; - gesture: Gesture; -} - -export const AnimatedNativeDetector = - Animated.createAnimatedComponent(HostGestureDetector); - -export const ReanimatedNativeDetector = - Reanimated?.default.createAnimatedComponent(HostGestureDetector); +import { + AnimatedNativeDetector, + NativeDetectorProps, + nativeDetectorStyles, + ReanimatedNativeDetector, +} from './common'; export function NativeDetector({ gesture, @@ -64,11 +56,3 @@ export function NativeDetector({ ); } - -export const nativeDetectorStyles = StyleSheet.create({ - detector: { - display: 'contents', - // TODO: remove, debug info only - backgroundColor: 'red', - }, -}); diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/common.ts b/packages/react-native-gesture-handler/src/v3/Detectors/common.ts new file mode 100644 index 0000000000..f1543572f3 --- /dev/null +++ b/packages/react-native-gesture-handler/src/v3/Detectors/common.ts @@ -0,0 +1,24 @@ +import React from 'react'; +import { Gesture } from '../types'; +import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; +import { Animated, StyleSheet } from 'react-native'; +import HostGestureDetector from './HostGestureDetector'; + +export interface NativeDetectorProps { + children?: React.ReactNode; + gesture: Gesture; +} + +export const AnimatedNativeDetector = + Animated.createAnimatedComponent(HostGestureDetector); + +export const ReanimatedNativeDetector = + Reanimated?.default.createAnimatedComponent(HostGestureDetector); + +export const nativeDetectorStyles = StyleSheet.create({ + detector: { + display: 'contents', + // TODO: remove, debug info only + backgroundColor: 'red', + }, +}); From 88d4290f9de24e8107cdd00efa680b9f8f389268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 9 Oct 2025 22:16:09 +0200 Subject: [PATCH 89/90] renamed detectors folder --- .../src/__tests__/RelationsTraversal.test.tsx | 2 +- packages/react-native-gesture-handler/src/index.ts | 8 ++++---- .../v3/{Detectors => detectors}/HostGestureDetector.tsx | 0 .../{Detectors => detectors}/HostGestureDetector.web.tsx | 0 .../LogicDetector/DelegateDetector.tsx | 0 .../LogicDetector/LogicDetector.tsx | 0 .../LogicDetector/useDetectorContext.ts | 0 .../src/v3/{Detectors => detectors}/NativeDetector.tsx | 0 .../src/v3/{Detectors => detectors}/common.ts | 0 .../src/v3/{Detectors => detectors}/utils.ts | 0 10 files changed, 5 insertions(+), 5 deletions(-) rename packages/react-native-gesture-handler/src/v3/{Detectors => detectors}/HostGestureDetector.tsx (100%) rename packages/react-native-gesture-handler/src/v3/{Detectors => detectors}/HostGestureDetector.web.tsx (100%) rename packages/react-native-gesture-handler/src/v3/{Detectors => detectors}/LogicDetector/DelegateDetector.tsx (100%) rename packages/react-native-gesture-handler/src/v3/{Detectors => detectors}/LogicDetector/LogicDetector.tsx (100%) rename packages/react-native-gesture-handler/src/v3/{Detectors => detectors}/LogicDetector/useDetectorContext.ts (100%) rename packages/react-native-gesture-handler/src/v3/{Detectors => detectors}/NativeDetector.tsx (100%) rename packages/react-native-gesture-handler/src/v3/{Detectors => detectors}/common.ts (100%) rename packages/react-native-gesture-handler/src/v3/{Detectors => detectors}/utils.ts (100%) diff --git a/packages/react-native-gesture-handler/src/__tests__/RelationsTraversal.test.tsx b/packages/react-native-gesture-handler/src/__tests__/RelationsTraversal.test.tsx index c49d8f6622..dbb7001b15 100644 --- a/packages/react-native-gesture-handler/src/__tests__/RelationsTraversal.test.tsx +++ b/packages/react-native-gesture-handler/src/__tests__/RelationsTraversal.test.tsx @@ -1,7 +1,7 @@ import { tagMessage } from '../utils'; import { useExclusive, useRace, useSimultaneous } from '../v3/hooks/relations'; import { useGesture } from '../v3/hooks/useGesture'; -import { configureRelations } from '../v3/Detectors/utils'; +import { configureRelations } from '../v3/detectors/utils'; import { SingleGesture, SingleGestureName } from '../v3/types'; import { renderHook } from '@testing-library/react-native'; diff --git a/packages/react-native-gesture-handler/src/index.ts b/packages/react-native-gesture-handler/src/index.ts index 08c3efbe53..c89d7f8b7a 100644 --- a/packages/react-native-gesture-handler/src/index.ts +++ b/packages/react-native-gesture-handler/src/index.ts @@ -162,11 +162,11 @@ export type { } from './components/DrawerLayout'; export { default as DrawerLayout } from './components/DrawerLayout'; -export type { NativeDetectorProps } from './v3/Detectors/common'; -export { NativeDetector } from './v3/Detectors/NativeDetector'; +export type { NativeDetectorProps } from './v3/detectors/common'; +export { NativeDetector } from './v3/detectors/NativeDetector'; -export { LogicDetector } from './v3/Detectors/LogicDetector/LogicDetector'; -export { DelegateDetector } from './v3/Detectors/LogicDetector/DelegateDetector'; +export { LogicDetector } from './v3/detectors/LogicDetector/LogicDetector'; +export { DelegateDetector } from './v3/detectors/LogicDetector/DelegateDetector'; export * from './v3/hooks/useGesture'; export * from './v3/hooks/relations'; diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/HostGestureDetector.tsx b/packages/react-native-gesture-handler/src/v3/detectors/HostGestureDetector.tsx similarity index 100% rename from packages/react-native-gesture-handler/src/v3/Detectors/HostGestureDetector.tsx rename to packages/react-native-gesture-handler/src/v3/detectors/HostGestureDetector.tsx diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/HostGestureDetector.web.tsx b/packages/react-native-gesture-handler/src/v3/detectors/HostGestureDetector.web.tsx similarity index 100% rename from packages/react-native-gesture-handler/src/v3/Detectors/HostGestureDetector.web.tsx rename to packages/react-native-gesture-handler/src/v3/detectors/HostGestureDetector.web.tsx diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/DelegateDetector.tsx b/packages/react-native-gesture-handler/src/v3/detectors/LogicDetector/DelegateDetector.tsx similarity index 100% rename from packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/DelegateDetector.tsx rename to packages/react-native-gesture-handler/src/v3/detectors/LogicDetector/DelegateDetector.tsx diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/LogicDetector.tsx b/packages/react-native-gesture-handler/src/v3/detectors/LogicDetector/LogicDetector.tsx similarity index 100% rename from packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/LogicDetector.tsx rename to packages/react-native-gesture-handler/src/v3/detectors/LogicDetector/LogicDetector.tsx diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/useDetectorContext.ts b/packages/react-native-gesture-handler/src/v3/detectors/LogicDetector/useDetectorContext.ts similarity index 100% rename from packages/react-native-gesture-handler/src/v3/Detectors/LogicDetector/useDetectorContext.ts rename to packages/react-native-gesture-handler/src/v3/detectors/LogicDetector/useDetectorContext.ts diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/NativeDetector.tsx b/packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx similarity index 100% rename from packages/react-native-gesture-handler/src/v3/Detectors/NativeDetector.tsx rename to packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/common.ts b/packages/react-native-gesture-handler/src/v3/detectors/common.ts similarity index 100% rename from packages/react-native-gesture-handler/src/v3/Detectors/common.ts rename to packages/react-native-gesture-handler/src/v3/detectors/common.ts diff --git a/packages/react-native-gesture-handler/src/v3/Detectors/utils.ts b/packages/react-native-gesture-handler/src/v3/detectors/utils.ts similarity index 100% rename from packages/react-native-gesture-handler/src/v3/Detectors/utils.ts rename to packages/react-native-gesture-handler/src/v3/detectors/utils.ts From b7e9fcdd10838724661a611358cf622e00d8ebfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Antoni=20Kwa=C5=9Bniewski?= Date: Thu, 9 Oct 2025 22:19:11 +0200 Subject: [PATCH 90/90] detector index --- packages/react-native-gesture-handler/src/index.ts | 6 +----- .../react-native-gesture-handler/src/v3/detectors/index.ts | 5 +++++ 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 packages/react-native-gesture-handler/src/v3/detectors/index.ts diff --git a/packages/react-native-gesture-handler/src/index.ts b/packages/react-native-gesture-handler/src/index.ts index c89d7f8b7a..2b374e64e1 100644 --- a/packages/react-native-gesture-handler/src/index.ts +++ b/packages/react-native-gesture-handler/src/index.ts @@ -162,11 +162,7 @@ export type { } from './components/DrawerLayout'; export { default as DrawerLayout } from './components/DrawerLayout'; -export type { NativeDetectorProps } from './v3/detectors/common'; -export { NativeDetector } from './v3/detectors/NativeDetector'; - -export { LogicDetector } from './v3/detectors/LogicDetector/LogicDetector'; -export { DelegateDetector } from './v3/detectors/LogicDetector/DelegateDetector'; +export * from './v3/detectors'; export * from './v3/hooks/useGesture'; export * from './v3/hooks/relations'; diff --git a/packages/react-native-gesture-handler/src/v3/detectors/index.ts b/packages/react-native-gesture-handler/src/v3/detectors/index.ts new file mode 100644 index 0000000000..1bac824c82 --- /dev/null +++ b/packages/react-native-gesture-handler/src/v3/detectors/index.ts @@ -0,0 +1,5 @@ +export type { NativeDetectorProps } from './common'; +export { NativeDetector } from './NativeDetector'; + +export { LogicDetector } from './LogicDetector/LogicDetector'; +export { DelegateDetector } from './LogicDetector/DelegateDetector';