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 1d7175887f..63f44732a5 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 @@ -902,8 +902,8 @@ open class GestureHandler { if (config.hasKey(KEY_DISPATCHES_ANIMATED_EVENTS)) { handler.dispatchesAnimatedEvents = config.getBoolean(KEY_DISPATCHES_ANIMATED_EVENTS) } - if (config.hasKey(KEY_SHOULD_USE_REANIMATED)) { - handler.dispatchesReanimatedEvents = config.getBoolean(KEY_SHOULD_USE_REANIMATED) + if (config.hasKey(KEY_DISPATCHES_REANIMATED_EVENTS)) { + handler.dispatchesReanimatedEvents = config.getBoolean(KEY_DISPATCHES_REANIMATED_EVENTS) } if (config.hasKey(KEY_MANUAL_ACTIVATION)) { handler.manualActivation = config.getBoolean(KEY_MANUAL_ACTIVATION) @@ -920,7 +920,7 @@ open class GestureHandler { private const val KEY_ENABLED = "enabled" private const val KEY_NEEDS_POINTER_DATA = "needsPointerData" private const val KEY_DISPATCHES_ANIMATED_EVENTS = "dispatchesAnimatedEvents" - private const val KEY_SHOULD_USE_REANIMATED = "shouldUseReanimated" + private const val KEY_DISPATCHES_REANIMATED_EVENTS = "dispatchesReanimatedEvents" private const val KEY_MANUAL_ACTIVATION = "manualActivation" private const val KEY_MOUSE_BUTTON = "mouseButton" private const val KEY_HIT_SLOP = "hitSlop" diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.mm b/packages/react-native-gesture-handler/apple/RNGestureHandler.mm index a2dd49fd93..4c27ab3032 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandler.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandler.mm @@ -147,7 +147,7 @@ - (void)updateConfig:(NSDictionary *)config _dispatchesAnimatedEvents = [RCTConvert BOOL:prop]; } - prop = config[@"shouldUseReanimated"]; + prop = config[@"dispatchesReanimatedEvents"]; if (prop != nil) { _dispatchesReanimatedEvents = [RCTConvert BOOL:prop]; } 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 ea2f73e40b..0a6e37aeaa 100644 --- a/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/v3/NativeDetector/NativeDetector.tsx @@ -35,7 +35,7 @@ export function NativeDetector({ const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents ? AnimatedNativeDetector - : gesture.config.shouldUseReanimated + : gesture.config.shouldUseReanimatedDetector ? ReanimatedNativeDetector : HostGestureDetector; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/relations/useComposedGesture.ts b/packages/react-native-gesture-handler/src/v3/hooks/relations/useComposedGesture.ts index f942b51e28..eb724c2ef2 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/relations/useComposedGesture.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/relations/useComposedGesture.ts @@ -5,6 +5,7 @@ import { ComposedGesture, ComposedGestureName, AnyGesture, + ComposedGestureConfig, } from '../../types'; import { tagMessage } from '../../../utils'; import { Reanimated } from '../../../handlers/gestures/reanimatedWrapper'; @@ -27,16 +28,16 @@ export function useComposedGesture( ); } - const config = { - shouldUseReanimated: gestures.some( - (gesture) => gesture.config.shouldUseReanimated + const config: ComposedGestureConfig = { + shouldUseReanimatedDetector: gestures.some( + (gesture) => gesture.config.shouldUseReanimatedDetector ), dispatchesAnimatedEvents: gestures.some( (gesture) => gesture.config.dispatchesAnimatedEvents ), }; - if (config.shouldUseReanimated && config.dispatchesAnimatedEvents) { + if (config.shouldUseReanimatedDetector && config.dispatchesAnimatedEvents) { throw new Error( tagMessage( 'Composed gestures cannot use both Reanimated and Animated events at the same time.' 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 0d994c7a36..9f13e42f00 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts @@ -2,15 +2,12 @@ import { useEffect, useMemo } from 'react'; import { getNextHandlerTag } from '../../handlers/getNextHandlerTag'; import RNGestureHandlerModule from '../../RNGestureHandlerModule'; import { useGestureCallbacks } from './useGestureCallbacks'; -import { Reanimated } from '../../handlers/gestures/reanimatedWrapper'; import { prepareConfig, - isAnimatedEvent, - shouldHandleTouchEvents, prepareRelations, bindSharedValues, - hasWorkletEventHandlers, unbindSharedValues, + prepareConfigForNativeSide, } from './utils'; import { tagMessage } from '../../utils'; import { BaseGestureConfig, SingleGesture, SingleGestureName } from '../types'; @@ -30,15 +27,10 @@ export function useGesture( ); } - // This has to be done ASAP as other hooks depend `shouldUseReanimated`. - config.shouldUseReanimated = - !config.disableReanimated && - Reanimated !== undefined && - hasWorkletEventHandlers(config); - config.needsPointerData = shouldHandleTouchEvents(config); - config.dispatchesAnimatedEvents = isAnimatedEvent(config.onUpdate); + // This has to be done ASAP as other hooks depend `shouldUseReanimatedDetector`. + prepareConfig(config); - if (config.dispatchesAnimatedEvents && config.shouldUseReanimated) { + if (config.dispatchesAnimatedEvents && config.shouldUseReanimatedDetector) { throw new Error( tagMessage( `${type}: You cannot use Animated.Event together with callbacks running on the UI thread. Either remove Animated.Event from onUpdate, or set runOnJS property to true on the gesture.` @@ -69,7 +61,7 @@ export function useGesture( } if ( - config.shouldUseReanimated && + config.shouldUseReanimatedDetector && (!onReanimatedStateChange || !onReanimatedUpdateEvent || !onReanimatedTouchEvent) @@ -92,7 +84,7 @@ export function useGesture( }, [type, tag]); useEffect(() => { - const preparedConfig = prepareConfig(type, config); + const preparedConfig = prepareConfigForNativeSide(type, config); RNGestureHandlerModule.setGestureHandlerConfig(tag, preparedConfig); RNGestureHandlerModule.flushOperations(); diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils/configUtils.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils/configUtils.ts index 39c2dd5dcc..e94d64a59f 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils/configUtils.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils/configUtils.ts @@ -2,83 +2,34 @@ import { Reanimated } from '../../../handlers/gestures/reanimatedWrapper'; import { tagMessage } from '../../../utils'; import { BaseGestureConfig, - CommonGestureConfig, ExcludeInternalConfigProps, - GestureCallbacks, - HandlersPropsWhiteList, - InternalConfigProps, SingleGestureName, } from '../../types'; -import { PanNativeProperties } from '../gestures/pan/PanProperties'; -import { FlingNativeProperties } from '../gestures/fling/FlingProperties'; -import { HoverNativeProperties } from '../gestures/hover/HoverProperties'; -import { LongPressNativeProperties } from '../gestures/longPress/LongPressProperties'; -import { NativeHandlerNativeProperties } from '../gestures/native/NativeProperties'; -import { TapNativeProperties } from '../gestures/tap/TapProperties'; - -const allowedNativeProps = new Set< - keyof CommonGestureConfig | keyof InternalConfigProps ->([ - // CommonGestureConfig - 'disableReanimated', - 'enabled', - 'shouldCancelWhenOutside', - 'hitSlop', - 'userSelect', - 'activeCursor', - 'mouseButton', - 'enableContextMenu', - 'touchAction', - - // InternalConfigProps - 'shouldUseReanimated', - 'dispatchesAnimatedEvents', - 'needsPointerData', - 'changeEventCalculator', -]); - -export const HandlerCallbacks = new Set< - keyof Required> ->([ - 'onBegin', - 'onStart', - 'onUpdate', - 'onEnd', - 'onFinalize', - 'onTouchesDown', - 'onTouchesMove', - 'onTouchesUp', - 'onTouchesCancelled', -]); - -const PropsToFilter = new Set>([ - ...HandlerCallbacks, - - // Config props - 'changeEventCalculator', - 'disableReanimated', - - // Relations - 'simultaneousWithExternalGesture', - 'requireExternalGestureToFail', - 'blocksExternalGesture', -]); - -export const PropsWhiteLists = new Map< - SingleGestureName, - HandlersPropsWhiteList ->([ - [SingleGestureName.Pan, PanNativeProperties], - [SingleGestureName.Tap, TapNativeProperties], - [SingleGestureName.Native, NativeHandlerNativeProperties], - [SingleGestureName.Fling, FlingNativeProperties], - [SingleGestureName.Hover, HoverNativeProperties], - [SingleGestureName.LongPress, LongPressNativeProperties], -]); - -const EMPTY_WHITE_LIST = new Set(); +import { hasWorkletEventHandlers, maybeUnpackValue } from './reanimatedUtils'; +import { isAnimatedEvent, shouldHandleTouchEvents } from './eventUtils'; +import { + allowedNativeProps, + EMPTY_WHITE_LIST, + PropsToFilter, + PropsWhiteLists, +} from './propsWhiteList'; export function prepareConfig( + config: BaseGestureConfig +) { + const runOnJS = maybeUnpackValue(config.runOnJS); + + config.shouldUseReanimatedDetector = + !config.disableReanimated && + Reanimated !== undefined && + hasWorkletEventHandlers(config); + config.needsPointerData = shouldHandleTouchEvents(config); + config.dispatchesAnimatedEvents = isAnimatedEvent(config.onUpdate); + config.dispatchesReanimatedEvents = + config.shouldUseReanimatedDetector && !runOnJS; +} + +export function prepareConfigForNativeSide( handlerType: SingleGestureName, config: BaseGestureConfig ) { diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils/index.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils/index.ts index 753d4398e2..6210dbec1f 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils/index.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils/index.ts @@ -3,3 +3,4 @@ export * from './eventHandlersUtils'; export * from './eventUtils'; export * from './reanimatedUtils'; export * from './relationUtils'; +export * from './propsWhiteList'; diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils/propsWhiteList.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils/propsWhiteList.ts new file mode 100644 index 0000000000..e873107bd7 --- /dev/null +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils/propsWhiteList.ts @@ -0,0 +1,76 @@ +import { + BaseGestureConfig, + CommonGestureConfig, + GestureCallbacks, + HandlersPropsWhiteList, + InternalConfigProps, + SingleGestureName, +} from '../../types'; +import { FlingNativeProperties } from '../gestures/fling/FlingProperties'; +import { HoverNativeProperties } from '../gestures/hover/HoverProperties'; +import { LongPressNativeProperties } from '../gestures/longPress/LongPressProperties'; +import { NativeHandlerNativeProperties } from '../gestures/native/NativeProperties'; +import { PanNativeProperties } from '../gestures/pan/PanProperties'; +import { TapNativeProperties } from '../gestures/tap/TapProperties'; + +export const allowedNativeProps = new Set< + keyof CommonGestureConfig | keyof InternalConfigProps +>([ + // CommonGestureConfig + 'disableReanimated', + 'enabled', + 'shouldCancelWhenOutside', + 'hitSlop', + 'userSelect', + 'activeCursor', + 'mouseButton', + 'enableContextMenu', + 'touchAction', + + // InternalConfigProps + 'dispatchesReanimatedEvents', + 'dispatchesAnimatedEvents', + 'needsPointerData', + 'changeEventCalculator', +]); + +export const HandlerCallbacks = new Set< + keyof Required> +>([ + 'onBegin', + 'onStart', + 'onUpdate', + 'onEnd', + 'onFinalize', + 'onTouchesDown', + 'onTouchesMove', + 'onTouchesUp', + 'onTouchesCancelled', +]); + +export const PropsToFilter = new Set>([ + ...HandlerCallbacks, + + // Config props + 'changeEventCalculator', + 'disableReanimated', + + // Relations + 'simultaneousWithExternalGesture', + 'requireExternalGestureToFail', + 'blocksExternalGesture', +]); + +export const PropsWhiteLists = new Map< + SingleGestureName, + HandlersPropsWhiteList +>([ + [SingleGestureName.Pan, PanNativeProperties], + [SingleGestureName.Tap, TapNativeProperties], + [SingleGestureName.Native, NativeHandlerNativeProperties], + [SingleGestureName.Fling, FlingNativeProperties], + [SingleGestureName.Hover, HoverNativeProperties], + [SingleGestureName.LongPress, LongPressNativeProperties], +]); + +export const EMPTY_WHITE_LIST = new Set(); diff --git a/packages/react-native-gesture-handler/src/v3/hooks/utils/reanimatedUtils.ts b/packages/react-native-gesture-handler/src/v3/hooks/utils/reanimatedUtils.ts index 03e9b4dc75..300ec5f0ad 100644 --- a/packages/react-native-gesture-handler/src/v3/hooks/utils/reanimatedUtils.ts +++ b/packages/react-native-gesture-handler/src/v3/hooks/utils/reanimatedUtils.ts @@ -1,7 +1,7 @@ import RNGestureHandlerModule from '../../../RNGestureHandlerModule'; import { Reanimated } from '../../../handlers/gestures/reanimatedWrapper'; import { BaseGestureConfig, SharedValue, SharedValueOrT } from '../../types'; -import { HandlerCallbacks } from './configUtils'; +import { HandlerCallbacks } from './propsWhiteList'; // Variant of djb2 hash function. // Taken from https://gist.github.com/eplawless/52813b1d8ad9af510d85?permalink_comment_id=3367765#gistcomment-3367765 @@ -37,7 +37,16 @@ export function bindSharedValues( const listenerId = baseListenerId + keyHash; sharedValue.addListener(listenerId, (value) => { - updateGestureHandlerConfig(handlerTag, { [configKey]: value }); + if (configKey === 'runOnJS') { + config.dispatchesReanimatedEvents = + config.shouldUseReanimatedDetector && !value; + + updateGestureHandlerConfig(handlerTag, { + dispatchesReanimatedEvents: config.dispatchesReanimatedEvents, + }); + } else { + updateGestureHandlerConfig(handlerTag, { [configKey]: value }); + } flushOperations(); }); }; diff --git a/packages/react-native-gesture-handler/src/v3/types.ts b/packages/react-native-gesture-handler/src/v3/types.ts index 67814ab4ab..574cb4a65c 100644 --- a/packages/react-native-gesture-handler/src/v3/types.ts +++ b/packages/react-native-gesture-handler/src/v3/types.ts @@ -122,13 +122,15 @@ export type SingleGesture = { gestureRelations: GestureRelations; }; +export type ComposedGestureConfig = { + shouldUseReanimatedDetector: boolean; + dispatchesAnimatedEvents: boolean; +}; + export type ComposedGesture = { tags: number[]; type: ComposedGestureName; - config: { - shouldUseReanimated: boolean; - dispatchesAnimatedEvents: boolean; - }; + config: ComposedGestureConfig; gestureEvents: GestureEvents; externalSimultaneousHandlers: number[]; gestures: Gesture[]; @@ -176,15 +178,18 @@ export interface GestureCallbacks { } export type InternalConfigProps = { - shouldUseReanimated?: boolean; + shouldUseReanimatedDetector?: boolean; + dispatchesReanimatedEvents?: boolean; dispatchesAnimatedEvents?: boolean; needsPointerData?: boolean; changeEventCalculator?: ChangeCalculatorType; }; -export type CommonGestureConfig = WithSharedValue< +export type CommonGestureConfig = { + disableReanimated?: boolean; +} & WithSharedValue< { - disableReanimated?: boolean; + runOnJS?: boolean; enabled?: boolean; shouldCancelWhenOutside?: boolean; hitSlop?: HitSlop;