Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
daa017c
Separate Reanimated from JS in NativeDetector
m-bert Aug 21, 2025
1e17cf8
Separate Reanimated on Android
m-bert Aug 21, 2025
08d1e2b
Separate Reanimated on iOS
m-bert Aug 21, 2025
256f707
Reorganize structure
m-bert Aug 21, 2025
19fd985
Create ceparate hooks for Reanimated
m-bert Aug 21, 2025
133319b
Add checks in useGesture
m-bert Aug 22, 2025
e50f6cc
use isAnimatedEvent
m-bert Aug 22, 2025
b3ef022
Extract handlers in separate function
m-bert Aug 22, 2025
952fbe1
Pass correct touch event callback
m-bert Aug 22, 2025
ec9eaa7
Merge branch 'next' into @mbert/extract-reanimated-handlers
m-bert Aug 22, 2025
60a56e6
Merge branch 'next' into @mbert/extract-reanimated-handlers
m-bert Aug 25, 2025
fda23cc
Fix crash when onUpdate is Animated.Event
m-bert Aug 25, 2025
e46554b
Unify lateinit
m-bert Aug 25, 2025
13f3af4
Rename EventTarget on Android
m-bert Aug 25, 2025
d7a2b1a
Rename EventTarget on iOS
m-bert Aug 25, 2025
1b04d3c
Add reset for animated and reanimated events flags
m-bert Aug 25, 2025
b8df015
Rename getter for event handlers
m-bert Aug 25, 2025
93343da
Correctly handle disableReanimated
m-bert Aug 26, 2025
d1d3efb
Unpack nativeEvent
m-bert Aug 26, 2025
03a1e64
Bring back hooks
m-bert Aug 26, 2025
cc1313a
Use old name
m-bert Aug 26, 2025
122613f
Merge branch 'next' into @mbert/extract-reanimated-handlers
m-bert Aug 26, 2025
53ce245
Merge branch 'next' into @mbert/extract-reanimated-handlers
m-bert Sep 4, 2025
fb88c8c
Change hook eslint rule to warning
m-bert Sep 4, 2025
8e5f07e
Extract choosing handler type into function
m-bert Sep 4, 2025
feaa167
Rename event handlers
m-bert Sep 4, 2025
3f2ea8c
Improve error message
m-bert Sep 4, 2025
4022b96
Move for loop inside function
m-bert Sep 4, 2025
fab60e5
Rename method
m-bert Sep 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"@eslint-react/no-array-index-key": "warn",
"@eslint-react/hooks-extra/no-direct-set-state-in-use-effect": "warn",
"@eslint-react/hooks-extra/prefer-use-state-lazy-initialization": "warn",
"@eslint-react/hooks-extra/ensure-custom-hooks-using-other-hooks": "warn",
"no-redeclare": "off",
"@typescript-eslint/no-redeclare": "error",
"no-use-before-define": "off",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import android.os.Looper
import android.view.MotionEvent
import android.view.VelocityTracker
import com.facebook.react.bridge.ReadableMap
import com.swmansion.gesturehandler.react.eventbuilders.FlingGestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.events.eventbuilders.FlingGestureHandlerEventDataBuilder

class FlingGestureHandler : GestureHandler() {
var numberOfPointersRequired = DEFAULT_NUMBER_OF_TOUCHES_REQUIRED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import com.facebook.react.uimanager.PixelUtil
import com.swmansion.gesturehandler.BuildConfig
import com.swmansion.gesturehandler.RNSVGHitTester
import com.swmansion.gesturehandler.react.RNGestureHandlerDetectorView
import com.swmansion.gesturehandler.react.RNGestureHandlerTouchEvent
import com.swmansion.gesturehandler.react.eventbuilders.GestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.events.RNGestureHandlerTouchEvent
import com.swmansion.gesturehandler.react.events.eventbuilders.GestureHandlerEventDataBuilder
import java.lang.IllegalStateException
import java.util.*

Expand Down Expand Up @@ -80,6 +80,7 @@ open class GestureHandler {
private val trackedPointers: Array<PointerData?> = Array(MAX_POINTERS_COUNT) { null }
var needsPointerData = false
var dispatchesAnimatedEvents = false
var dispatchesReanimatedEvents = false

private var hitSlop: FloatArray? = null
var eventCoalescingKey: Short = 0
Expand Down Expand Up @@ -131,6 +132,8 @@ open class GestureHandler {
isEnabled = DEFAULT_IS_ENABLED
hitSlop = DEFAULT_HIT_SLOP
mouseButton = DEFAULT_MOUSE_BUTTON
dispatchesAnimatedEvents = DEFAULT_DISPATCHES_ANIMATED_EVENTS
dispatchesReanimatedEvents = DEFAULT_DISPATCHES_REANIMATED_EVENTS
}

fun hasCommonPointers(other: GestureHandler): Boolean {
Expand Down Expand Up @@ -886,6 +889,9 @@ 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_MANUAL_ACTIVATION)) {
handler.manualActivation = config.getBoolean(KEY_MANUAL_ACTIVATION)
}
Expand All @@ -901,6 +907,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_MANUAL_ACTIVATION = "manualActivation"
private const val KEY_MOUSE_BUTTON = "mouseButton"
private const val KEY_HIT_SLOP = "hitSlop"
Expand Down Expand Up @@ -973,6 +980,8 @@ open class GestureHandler {
private const val DEFAULT_IS_ENABLED = true
private val DEFAULT_HIT_SLOP = null
private const val DEFAULT_MOUSE_BUTTON = 0
private const val DEFAULT_DISPATCHES_ANIMATED_EVENTS = false
private const val DEFAULT_DISPATCHES_REANIMATED_EVENTS = false

const val STATE_UNDETERMINED = 0
const val STATE_FAILED = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import android.view.View
import android.view.ViewGroup
import com.swmansion.gesturehandler.react.RNGestureHandlerRootHelper
import com.swmansion.gesturehandler.react.RNViewConfigurationHelper
import com.swmansion.gesturehandler.react.eventbuilders.HoverGestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.events.eventbuilders.HoverGestureHandlerEventDataBuilder

class HoverGestureHandler : GestureHandler() {
private var handler: Handler? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import android.os.SystemClock
import android.view.MotionEvent
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.PixelUtil
import com.swmansion.gesturehandler.react.eventbuilders.LongPressGestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.events.eventbuilders.LongPressGestureHandlerEventDataBuilder

class LongPressGestureHandler(context: Context) : GestureHandler() {
var minDurationMs = DEFAULT_MIN_DURATION_MS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.swmansion.gesturehandler.core

import android.content.Context
import android.view.MotionEvent
import com.swmansion.gesturehandler.react.eventbuilders.ManualGestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.events.eventbuilders.ManualGestureHandlerEventDataBuilder

class ManualGestureHandler : GestureHandler() {
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import com.facebook.react.views.text.ReactTextView
import com.facebook.react.views.textinput.ReactEditText
import com.facebook.react.views.view.ReactViewGroup
import com.swmansion.gesturehandler.react.RNGestureHandlerButtonViewManager
import com.swmansion.gesturehandler.react.eventbuilders.NativeGestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.events.eventbuilders.NativeGestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.isScreenReaderOn

class NativeViewGestureHandler : GestureHandler() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.PixelUtil
import com.swmansion.gesturehandler.core.GestureUtils.getLastPointerX
import com.swmansion.gesturehandler.core.GestureUtils.getLastPointerY
import com.swmansion.gesturehandler.react.eventbuilders.PanGestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.events.eventbuilders.PanGestureHandlerEventDataBuilder

class PanGestureHandler(context: Context?) : GestureHandler() {
var velocityX = 0f
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.content.Context
import android.graphics.PointF
import android.view.MotionEvent
import android.view.ViewConfiguration
import com.swmansion.gesturehandler.react.eventbuilders.PinchGestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.events.eventbuilders.PinchGestureHandlerEventDataBuilder
import kotlin.math.abs

class PinchGestureHandler : GestureHandler() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.content.Context
import android.graphics.PointF
import android.view.MotionEvent
import com.swmansion.gesturehandler.core.RotationGestureDetector.OnRotationGestureListener
import com.swmansion.gesturehandler.react.eventbuilders.RotationGestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.events.eventbuilders.RotationGestureHandlerEventDataBuilder
import kotlin.math.abs

class RotationGestureHandler : GestureHandler() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.PixelUtil
import com.swmansion.gesturehandler.core.GestureUtils.getLastPointerX
import com.swmansion.gesturehandler.core.GestureUtils.getLastPointerY
import com.swmansion.gesturehandler.react.eventbuilders.TapGestureHandlerEventDataBuilder
import com.swmansion.gesturehandler.react.events.eventbuilders.TapGestureHandlerEventDataBuilder
import kotlin.math.abs

class TapGestureHandler : GestureHandler() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.facebook.soloader.SoLoader
import com.swmansion.gesturehandler.NativeRNGestureHandlerModuleSpec
import com.swmansion.gesturehandler.ReanimatedProxy
import com.swmansion.gesturehandler.core.GestureHandler
import com.swmansion.gesturehandler.react.events.RNGestureHandlerEventDispatcher

// UIManagerModule.resolveRootTagFromReactTag() was deprecated and will be removed in the next RN release
// ref: https://github.com/facebook/react-native/commit/acbf9e18ea666b07c1224a324602a41d0a66985e
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import com.facebook.react.uimanager.ViewGroupManager
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.viewmanagers.RNGestureHandlerRootViewManagerDelegate
import com.facebook.react.viewmanagers.RNGestureHandlerRootViewManagerInterface
import com.swmansion.gesturehandler.react.events.RNGestureHandlerEvent
import com.swmansion.gesturehandler.react.events.RNGestureHandlerStateChangeEvent

/**
* React native's view manager used for creating instances of []RNGestureHandlerRootView}. It
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.swmansion.gesturehandler.react.events

enum class EventHandlerType {
ForJS,
ForReanimated,
ForAnimated,
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,27 @@
// ref: https://github.com/facebook/react-native/commit/2fbbdbb2ce897e8da3f471b08b93f167d566db1d
@file:Suppress("DEPRECATION")

package com.swmansion.gesturehandler.react
package com.swmansion.gesturehandler.react.events

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
import com.swmansion.gesturehandler.react.events.eventbuilders.GestureHandlerEventDataBuilder

class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>() {
private var dataBuilder: GestureHandlerEventDataBuilder<*>? = null
private var coalescingKey: Short = 0
private var actionType: Int = GestureHandler.ACTION_TYPE_NATIVE_ANIMATED_EVENT
private var useAnimatedEvent = false

// On the new architecture, native animated expects event names prefixed with `top` instead of `on`,
// since we know when the native animated node is the target of the event we can use the different
// event name where appropriate.
// TODO: This is a workaround not as solution, but doing this properly would require a total overhaul of
// how GH sends events (which needs to be done, but maybe wait until the RN's apis stop changing)
private var useTopPrefixedName: Boolean = false
private lateinit var eventHandlerType: EventHandlerType

private fun <T : GestureHandler> init(
handler: T,
actionType: Int,
dataBuilder: GestureHandlerEventDataBuilder<T>,
useNativeAnimatedName: Boolean,
eventHandlerType: EventHandlerType,
) {
val view = if (handler.actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) {
handler.viewForEvents!!
Expand All @@ -43,8 +36,7 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>

this.actionType = actionType
this.dataBuilder = dataBuilder
this.useTopPrefixedName = useNativeAnimatedName
this.useAnimatedEvent = useAnimatedEvent
this.eventHandlerType = eventHandlerType
coalescingKey = handler.eventCoalescingKey
}

Expand All @@ -53,9 +45,15 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>
EVENTS_POOL.release(this)
}

override fun getEventName() = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR && useTopPrefixedName) {
NATIVE_DETECTOR_ANIMATED_EVENT_NAME
} else if (useTopPrefixedName) {
override fun getEventName() = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) {
if (eventHandlerType == EventHandlerType.ForAnimated) {
NATIVE_DETECTOR_ANIMATED_EVENT_NAME
} else if (eventHandlerType == EventHandlerType.ForReanimated) {
REANIMATED_EVENT_NAME
} else {
EVENT_NAME
}
} else if (eventHandlerType == EventHandlerType.ForAnimated) {
NATIVE_ANIMATED_EVENT_NAME
} else {
EVENT_NAME
Expand All @@ -73,6 +71,13 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>

companion object {
const val EVENT_NAME = "onGestureHandlerEvent"
const val REANIMATED_EVENT_NAME = "onGestureHandlerReanimatedEvent"

// On the new architecture, native animated expects event names prefixed with `top` instead of `on`,
// since we know when the native animated node is the target of the event we can use the different
// event name where appropriate.
// TODO: This is a workaround not as solution, but doing this properly would require a total overhaul of
// how GH sends events (which needs to be done, but maybe wait until the RN's apis stop changing)
const val NATIVE_ANIMATED_EVENT_NAME = "topGestureHandlerEvent"
const val NATIVE_DETECTOR_ANIMATED_EVENT_NAME = "topGestureHandlerAnimatedEvent"
private const val TOUCH_EVENTS_POOL_SIZE = 7 // magic
Expand All @@ -82,9 +87,9 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>
handler: T,
actionType: Int,
dataBuilder: GestureHandlerEventDataBuilder<T>,
useTopPrefixedName: Boolean = false,
eventHandlerType: EventHandlerType,
): RNGestureHandlerEvent = (EVENTS_POOL.acquire() ?: RNGestureHandlerEvent()).apply {
init(handler, actionType, dataBuilder, useTopPrefixedName)
init(handler, actionType, dataBuilder, eventHandlerType)
}

fun createEventData(dataBuilder: GestureHandlerEventDataBuilder<*>): WritableMap = Arguments.createMap().apply {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.swmansion.gesturehandler.react
package com.swmansion.gesturehandler.react.events

import android.view.MotionEvent
import com.facebook.react.bridge.ReactApplicationContext
Expand All @@ -8,6 +8,8 @@ import com.swmansion.gesturehandler.ReanimatedProxy
import com.swmansion.gesturehandler.core.GestureHandler
import com.swmansion.gesturehandler.core.OnTouchEventListener
import com.swmansion.gesturehandler.dispatchEvent
import com.swmansion.gesturehandler.react.RNGestureHandlerFactoryUtil
import com.swmansion.gesturehandler.react.deviceEventEmitter

class RNGestureHandlerEventDispatcher(private val reactApplicationContext: ReactApplicationContext) :
OnTouchEventListener {
Expand All @@ -33,14 +35,16 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
return
}

val handlerFactory = RNGestureHandlerFactoryUtil.findFactoryForHandler<GestureHandler>(handler) ?: return
val handlerFactory = RNGestureHandlerFactoryUtil.findFactoryForHandler<GestureHandler>(handler)
?: return
when (handler.actionType) {
GestureHandler.ACTION_TYPE_REANIMATED_WORKLET -> {
// Reanimated worklet
val event = RNGestureHandlerEvent.obtain(
handler,
handler.actionType,
handlerFactory.createEventBuilder(handler),
EventHandlerType.ForJS, // For API v2 compatibility
)
sendEventForReanimated(event)
}
Expand All @@ -50,7 +54,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
handler,
handler.actionType,
handlerFactory.createEventBuilder(handler),
true,
EventHandlerType.ForAnimated,
)
sendEventForNativeAnimatedEvent(event)
}
Expand All @@ -68,20 +72,19 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data)
}
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR -> {
if (handler.dispatchesAnimatedEvents) {
val animatedEvent = RNGestureHandlerEvent.obtain(
handler,
handler.actionType,
handlerFactory.createEventBuilder(handler),
true,
)
handler.viewForEvents!!.dispatchEvent(animatedEvent)
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.viewForEvents!!.dispatchEvent(event)
Expand All @@ -96,7 +99,8 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
// root containers use negative tags, we don't need to dispatch events for them to the JS
return
}
val handlerFactory = RNGestureHandlerFactoryUtil.findFactoryForHandler<GestureHandler>(handler) ?: return
val handlerFactory = RNGestureHandlerFactoryUtil.findFactoryForHandler<GestureHandler>(handler)
?: return

when (handler.actionType) {
GestureHandler.ACTION_TYPE_REANIMATED_WORKLET -> {
Expand All @@ -107,6 +111,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
oldState,
handler.actionType,
handlerFactory.createEventBuilder(handler),
EventHandlerType.ForJS, // For API v2 compatibility
)
sendEventForReanimated(event)
}
Expand All @@ -132,12 +137,19 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
}

GestureHandler.ACTION_TYPE_NATIVE_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.viewForEvents!!.dispatchEvent(event)
Expand All @@ -164,7 +176,11 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
when (handler.actionType) {
GestureHandler.ACTION_TYPE_REANIMATED_WORKLET -> {
// Reanimated worklet
val event = RNGestureHandlerTouchEvent.obtain(handler, handler.actionType)
val event = RNGestureHandlerTouchEvent.obtain(
handler,
handler.actionType,
EventHandlerType.ForJS, // For API v2 compatibility
)
sendEventForReanimated(event)
}
GestureHandler.ACTION_TYPE_JS_FUNCTION_NEW_API -> {
Expand All @@ -173,7 +189,12 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data)
}
GestureHandler.ACTION_TYPE_NATIVE_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.viewForEvents!!.dispatchEvent(event)
}
Expand Down
Loading
Loading