Repository navigation
Commit f4038e9
authored
Stop the Pressable long press timer on touch up (#4644)
## Description
When a `Pressable` has `requireToFail`, the native button finalizes only
after the other gesture fails, so the long press timer started at
press-in keeps running after the finger lifted. With the default tap
`maxDelay` (200 ms) and `delayLongPress` (500 ms), a tap held for 300 to
500 ms logs `onLongPress` about 100 ms after the lift and never gets
`onPress`. This was always the case on iOS; #4639 brought Android to the
same state.
The long press gesture now stops the pending timer on touch up. To keep
a real long press suppressing `onPress`, the fired timer clears its own
id, `cancelLongPress` no longer touches `isOnPressAllowed`, and every
press-in resets the flag instead. No new hooks.
Follow-up to #4633 and #4639, surfaced by the CodeRabbit review on
#4639.
## Test plan
Drop the snippet below into the empty example and hold each box.
- 400 ms: onPressIn, onPressOut, onPress on both boxes, no onLongPress
- 700 ms: onPressIn, onLongPress, onPressOut, no onPress on both boxes
- 400 ms again after the long press: onPress is back
Before this change the lower box logged onLongPress after the lift and
no onPress for the 400 ms hold, on iOS and Android.
Also rerun the #4630 regression matrix (disable under the finger,
pressDelay, RN and RNGH ScrollView drag, parent pan, requireToFail
double tap, modal, unmount, nested Pressable, hitSlop), each row
followed by a plain tap: every row identical to #4639 on both platforms.
<details>
<summary>Repro</summary>
```tsx
import React, { useRef, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {
GestureDetector,
GestureHandlerRootView,
Pressable,
useTapGesture,
} from 'react-native-gesture-handler';
export default function Repro() {
const [lines, setLines] = useState<string[]>([]);
const pressInTime = useRef(0);
const log = (line: string) => {
const stamped = `${line} +${Date.now() - pressInTime.current}ms`;
setLines((current) => [...current.slice(-7), stamped]);
};
const logPressIn = (name: string) => () => {
pressInTime.current = Date.now();
log(`${name}: onPressIn`);
};
const doubleTap = useTapGesture({ numberOfTaps: 2, runOnJS: true });
return (
<GestureHandlerRootView style={styles.container}>
<Pressable
onPressIn={logPressIn('plain')}
onLongPress={() => log('plain: onLongPress')}
onPressOut={() => log('plain: onPressOut')}
onPress={() => log('plain: onPress')}>
<View style={styles.box}>
<Text>Plain Pressable</Text>
</View>
</Pressable>
<GestureDetector gesture={doubleTap}>
<View>
<Pressable
requireToFail={doubleTap}
onPressIn={logPressIn('requireToFail')}
onLongPress={() => log('requireToFail: onLongPress')}
onPressOut={() => log('requireToFail: onPressOut')}
onPress={() => log('requireToFail: onPress')}>
<View style={styles.box}>
<Text>Pressable with requireToFail</Text>
</View>
</Pressable>
</View>
</GestureDetector>
<Text style={styles.log}>{lines.join('\n')}</Text>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', paddingTop: 40, gap: 24 },
box: {
width: 300,
height: 80,
backgroundColor: '#b58df1',
alignItems: 'center',
justifyContent: 'center',
},
log: { width: 300, fontSize: 14 },
});
```1 parent 653d519 commit f4038e9
1 file changed
Lines changed: 7 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
125 | 125 | | |
126 | 126 | | |
127 | 127 | | |
128 | | - | |
129 | 128 | | |
130 | 129 | | |
131 | 130 | | |
| |||
141 | 140 | | |
142 | 141 | | |
143 | 142 | | |
| 143 | + | |
144 | 144 | | |
145 | 145 | | |
146 | 146 | | |
| |||
188 | 188 | | |
189 | 189 | | |
190 | 190 | | |
| 191 | + | |
191 | 192 | | |
192 | 193 | | |
193 | 194 | | |
| |||
311 | 312 | | |
312 | 313 | | |
313 | 314 | | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
314 | 320 | | |
315 | 321 | | |
316 | 322 | | |
| |||
0 commit comments