Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes incorrect end/finalize behavior for Pinch and Rotation gestures on web, ensuring gestures properly transition to END only when they were active and otherwise transition to FAILED so onFinalize receives the correct success flag.
Changes:
- Rotation: end the gesture only when
State.ACTIVE, otherwise fail, and drive this via the detector’sonRotationEndcallback. - Pinch: ensure a gesture that began but never activated is failed on pointer release (instead of being left in a non-finished state).
- Rotation detector: always call
finish()onUPso end/fail logic reliably runs.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| packages/react-native-gesture-handler/src/web/handlers/RotationGestureHandler.ts | Moves end/fail decision to onRotationEnd and fails non-active rotations so finalize state is correct. |
| packages/react-native-gesture-handler/src/web/handlers/PinchGestureHandler.ts | Ensures pinch transitions to FAILED when it never became active but pointers are released. |
| packages/react-native-gesture-handler/src/web/detectors/RotationGestureDetector.ts | Ensures finish() is invoked on UP and consistently triggers onRotationEnd. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| this.scaleGestureDetector.onTouchEvent(event, this.tracker); | ||
|
|
||
| if (this.state === State.ACTIVE) { | ||
| this.scaleGestureDetector.onTouchEvent(event, this.tracker); |
There was a problem hiding this comment.
This means that the scale detector will not be receiving up events in the active state. Is that intended?
There was a problem hiding this comment.
Is that intended?
Yes, it was. If you look at the removed code it didn't make any sense and now it resembles the same logic as it had previously. I've also checked if it should be moved up, but if you look at ScaleGestureDetector it will simply return true in case of inactive handler, so we don't have to call it. I've added comment in 02ed360.
As for moving this logic to onScaleEnd it is not as easy as with Rotation gesture, as it is only called if handler was active.
## Description Follow up for #4078 I've noticed that when `Rotation` does not activate, but pointers are released, `onFinalize` is called with `true`. This happens because we try to move it to `END` state, but our internal logic does not send `onDeactivate`. Code in `onHandle` that I removed was effectively dead, as `ACTION_UP` was handled in rotation detector - the only possible branch was `else` statement which fails handler if only one pointer was present. ## Test plan <details> <summary>Tested on Transformations example and the following code:</summary> ```tsx import { StyleSheet, View } from 'react-native'; import { GestureDetector, GestureHandlerRootView, usePinchGesture, useRotationGesture, } from 'react-native-gesture-handler'; export default function App() { const g = useRotationGesture({ onBegin: () => console.log('onBegin'), onActivate: () => console.log('onActivate'), onUpdate: (e) => console.log('onUpdate', e.rotation), onDeactivate: () => console.log('onDeactivate'), onFinalize: (_, s) => console.log('onFinalize', s), }); return ( <GestureHandlerRootView style={styles.container}> <GestureDetector gesture={g}> <View style={styles.box} /> </GestureDetector> </GestureHandlerRootView> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, box: { width: 200, height: 200, backgroundColor: 'blue', borderRadius: 12, }, }); ``` </details>
Description
I've noticed that
PinchandRotationbehave weirdly when it comes to ending gesture on web. I've took a look and fixed some minor issues.Note
This PR may introduce mismatch between
androidandwebversions ofRotation. I still think that this is how it is supposed to work, so we may want to make required changes toandroidas well (or drop this PR if you prefer to).Pinch
I've noticed that
Pinchis not reset when pointers are placed on view and then released. This was because for some reasonPinchsimply ignored finished states if it had not activated earlier.Rotation
Rotation, always finalized withtrue. This was because we always tried to move it toendstate. Our internal event handler hadn't been sendingonDeactivate, buttruewas still passed intoonFinalize.Test plan
Tested on Transformations example and code below: