1- import React from 'react' ;
2- import Animated , {
3- useAnimatedGestureHandler ,
4- useSharedValue ,
5- useAnimatedStyle ,
6- withSpring ,
7- runOnJS
8- } from 'react-native-reanimated' ;
9- import {
10- LongPressGestureHandler ,
11- PanGestureHandler ,
12- State ,
13- HandlerStateChangeEventPayload ,
14- PanGestureHandlerEventPayload
15- } from 'react-native-gesture-handler' ;
1+ import React , { useRef , memo } from 'react' ;
2+ import Animated , { useSharedValue , useAnimatedStyle , withSpring , runOnJS } from 'react-native-reanimated' ;
3+ import { Gesture , GestureDetector , GestureUpdateEvent , PanGestureHandlerEventPayload } from 'react-native-gesture-handler' ;
164
175import Touch from '../Touch' ;
186import { ACTION_WIDTH , LONG_SWIPE , SMALL_SWIPE } from './styles' ;
@@ -44,12 +32,13 @@ const Touchable = ({
4432 const rowOffSet = useSharedValue ( 0 ) ;
4533 const transX = useSharedValue ( 0 ) ;
4634 const rowState = useSharedValue ( 0 ) ; // 0: closed, 1: right opened, -1: left opened
47- let _value = 0 ;
35+ const valueRef = useRef ( 0 ) ;
4836
4937 const close = ( ) => {
5038 rowState . value = 0 ;
5139 transX . value = withSpring ( 0 , { overshootClamping : true } ) ;
5240 rowOffSet . value = 0 ;
41+ valueRef . current = 0 ;
5342 } ;
5443
5544 const handleToggleFav = ( ) => {
@@ -96,15 +85,9 @@ const Touchable = ({
9685 }
9786 } ;
9887
99- const onLongPressHandlerStateChange = ( { nativeEvent } : { nativeEvent : HandlerStateChangeEventPayload } ) => {
100- if ( nativeEvent . state === State . ACTIVE ) {
101- handleLongPress ( ) ;
102- }
103- } ;
104-
105- const handleRelease = ( event : PanGestureHandlerEventPayload ) => {
88+ const handleRelease = ( event : GestureUpdateEvent < PanGestureHandlerEventPayload > ) => {
10689 const { translationX } = event ;
107- _value += translationX ;
90+ valueRef . current += translationX ;
10891 let toValue = 0 ;
10992 if ( rowState . value === 0 ) {
11093 // if no option is opened
@@ -143,10 +126,10 @@ const Touchable = ({
143126 }
144127 } else if ( rowState . value === - 1 ) {
145128 // if left option is opened
146- if ( _value < SMALL_SWIPE ) {
129+ if ( valueRef . current < SMALL_SWIPE ) {
147130 toValue = 0 ;
148131 rowState . value = 0 ;
149- } else if ( _value > LONG_SWIPE ) {
132+ } else if ( valueRef . current > LONG_SWIPE ) {
150133 toValue = 0 ;
151134 rowState . value = 0 ;
152135 if ( I18n . isRTL ) {
@@ -161,10 +144,10 @@ const Touchable = ({
161144 }
162145 } else if ( rowState . value === 1 ) {
163146 // if right option is opened
164- if ( _value > - 2 * SMALL_SWIPE ) {
147+ if ( valueRef . current > - 2 * SMALL_SWIPE ) {
165148 toValue = 0 ;
166149 rowState . value = 0 ;
167- } else if ( _value < - LONG_SWIPE ) {
150+ } else if ( valueRef . current < - LONG_SWIPE ) {
168151 if ( I18n . isRTL ) {
169152 handleToggleRead ( ) ;
170153 } else {
@@ -178,56 +161,66 @@ const Touchable = ({
178161 }
179162 transX . value = withSpring ( toValue , { overshootClamping : true } ) ;
180163 rowOffSet . value = toValue ;
181- _value = toValue ;
164+ valueRef . current = toValue ;
182165 } ;
183166
184- const onGestureEvent = useAnimatedGestureHandler ( {
185- onActive : event => {
167+ const longPressGesture = Gesture . LongPress ( )
168+ . minDuration ( 500 )
169+ . onStart ( ( ) => {
170+ runOnJS ( handleLongPress ) ( ) ;
171+ } ) ;
172+
173+ const panGesture = Gesture . Pan ( )
174+ . activeOffsetX ( [ - 10 , 10 ] ) // More sensitive horizontal detection
175+ . failOffsetY ( [ - 20 , 20 ] ) // Fail on vertical movement to distinguish scrolling
176+ . enabled ( swipeEnabled )
177+ . onUpdate ( event => {
186178 transX . value = event . translationX + rowOffSet . value ;
187179 if ( transX . value > 2 * width ) transX . value = 2 * width ;
188- } ,
189- onEnd : event => {
180+ } )
181+ . onEnd ( event => {
190182 runOnJS ( handleRelease ) ( event ) ;
191- }
192- } ) ;
183+ } ) ;
184+
185+ // Use Race instead of Simultaneous to prevent conflicts
186+ // Pan gesture will take priority over long press for horizontal swipes
187+ const composedGesture = Gesture . Race ( panGesture , longPressGesture ) ;
193188
194- const animatedStyles = useAnimatedStyle ( ( ) => ( { transform : [ { translateX : transX . value } ] } ) ) ;
189+ const animatedStyles = useAnimatedStyle ( ( ) => ( {
190+ transform : [ { translateX : transX . value } ]
191+ } ) ) ;
195192
196193 return (
197- < LongPressGestureHandler onHandlerStateChange = { onLongPressHandlerStateChange } >
194+ < GestureDetector gesture = { composedGesture } >
198195 < Animated . View >
199- < PanGestureHandler activeOffsetX = { [ - 20 , 20 ] } onGestureEvent = { onGestureEvent } enabled = { swipeEnabled } >
200- < Animated . View >
201- < LeftActions
202- transX = { transX }
203- isRead = { isRead }
204- width = { width }
205- onToggleReadPress = { onToggleReadPress }
206- displayMode = { displayMode }
207- />
208- < RightActions
209- transX = { transX }
210- favorite = { favorite }
211- width = { width }
212- toggleFav = { handleToggleFav }
213- onHidePress = { onHidePress }
214- displayMode = { displayMode }
215- />
216- < Animated . View style = { animatedStyles } >
217- < Touch
218- onPress = { handlePress }
219- testID = { testID }
220- style = { {
221- backgroundColor : isFocused ? colors . surfaceTint : colors . surfaceRoom
222- } } >
223- { children }
224- </ Touch >
225- </ Animated . View >
226- </ Animated . View >
227- </ PanGestureHandler >
196+ < LeftActions
197+ transX = { transX }
198+ isRead = { isRead }
199+ width = { width }
200+ onToggleReadPress = { onToggleReadPress }
201+ displayMode = { displayMode }
202+ />
203+ < RightActions
204+ transX = { transX }
205+ favorite = { favorite }
206+ width = { width }
207+ toggleFav = { handleToggleFav }
208+ onHidePress = { onHidePress }
209+ displayMode = { displayMode }
210+ />
211+ < Animated . View style = { animatedStyles } >
212+ < Touch
213+ onPress = { handlePress }
214+ testID = { testID }
215+ style = { {
216+ backgroundColor : isFocused ? colors . surfaceTint : colors . surfaceRoom
217+ } } >
218+ { children }
219+ </ Touch >
220+ </ Animated . View >
228221 </ Animated . View >
229- </ LongPressGestureHandler >
222+ </ GestureDetector >
230223 ) ;
231224} ;
232225
233- export default Touchable ;
226+ export default memo ( Touchable ) ;
0 commit comments