Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: onPressIn and onPressOut are triggered at the same time on Android #3286

Merged
merged 1 commit into from
Dec 13, 2024

Conversation

coado
Copy link
Contributor

@coado coado commented Dec 13, 2024

Description

Fixes #3280

The pressInHandler method returns early if the isTouchPropagationAllowed is false. On Android pressInHandler is called initially before the isTouchPropagationAllowed is set to true in buttonGesture. Changing the order of gestures seems to fix it.

Test plan

Code
import * as React from 'react';
import {
  View,
  Text,
  Pressable as RNPressable,
  SafeAreaView,
  StyleSheet,
} from 'react-native';
import {
  GestureHandlerRootView,
  Pressable as RNGHPressable,
} from 'react-native-gesture-handler';

export default function HomeScreen() {
  return (
    <GestureHandlerRootView>
      <SafeAreaView style={{ flex: 1 }}>
        <View style={styles.container}>
          <RNGHPressable
            style={styles.pressableStyles}
            onPressIn={() => console.log(JSON.stringify('RNGH: onPressIn'))}
            onPressOut={() => console.log(JSON.stringify('RNGH: onPressOut'))}
            onPress={() => console.log(JSON.stringify('RNGH: onPress'))}>
            <Text style={styles.pressableText}>RNGH Pressable</Text>
          </RNGHPressable>
          <RNPressable
            style={styles.pressableStyles}
            onPressIn={() => console.log(JSON.stringify('RN: onPressIn'))}
            onPressOut={() => console.log(JSON.stringify('RN: onPressOut'))}
            onPress={() => console.log(JSON.stringify('RN: onPress'))}>
            <Text style={styles.pressableText}>RN Pressable</Text>
          </RNPressable>
        </View>
      </SafeAreaView>
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    gap: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
  pressableStyles: {
    backgroundColor: '#3BE7BB',
    paddingVertical: 10,
    paddingHorizontal: 15,
    height: 50,
    alignItems: 'center',
    justifyContent: 'center',
  },
  pressableText: {
    color: '#000',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

@j-piasecki j-piasecki requested a review from latekvo December 13, 2024 10:53
Copy link
Contributor

@latekvo latekvo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works great, this PR also fixes functional styles on fabric on android.
While testing i noticed the touch propagation mechanism seems to be completely broken for onPress events on fabric, but it does work for the functional styles, related to #3282

collapsed test code
import * as React from 'react';
import {
  View,
  Text,
  Pressable as RNPressable,
  SafeAreaView,
  StyleSheet,
  ScrollView,
  PressableStateCallbackType,
} from 'react-native';
import {
  GestureHandlerRootView,
  Pressable as RNGHPressable,
  TouchableHighlight,
  TouchableOpacity,
} from 'react-native-gesture-handler';

const innerStyle = ({ pressed }: PressableStateCallbackType) => [
  styles.innerPressable,
  pressed ? { backgroundColor: '#c00' } : { backgroundColor: 'orange' },
];
const outerStyle = ({ pressed }: PressableStateCallbackType) => [
  styles.pressable,
  pressed ? { backgroundColor: '#00c' } : { backgroundColor: 'pink' },
];

export default function HomeScreen() {
  return (
    <GestureHandlerRootView>
      <SafeAreaView style={{ flex: 1 }}>
        <ScrollView contentContainerStyle={styles.rowContainer}>
          <RNGHPressable
            style={outerStyle}
            onPressIn={() => console.log(JSON.stringify('RNGH: onPressIn'))}
            onPressOut={() => console.log(JSON.stringify('RNGH: onPressOut'))}
            onPress={() => console.log(JSON.stringify('RNGH: onPress'))}>
            <Text style={styles.pressableText}>RNGH Pressable</Text>
          </RNGHPressable>

          <RNPressable
            style={outerStyle}
            onPressIn={() => console.log(JSON.stringify('RN: onPressIn'))}
            onPressOut={() => console.log(JSON.stringify('RN: onPressOut'))}
            onPress={() => console.log(JSON.stringify('RN: onPress'))}>
            <Text style={styles.pressableText}>RN Pressable</Text>
          </RNPressable>

          <RNGHPressable
            style={outerStyle}
            onPress={() =>
              console.log('TouchableOpacity in Pressable: Pressable')
            }>
            <TouchableOpacity
              style={styles.innerPressable}
              onPress={() =>
                console.log('TouchableOpacity in Pressable: TouchableOpacity')
              }>
              <Text style={styles.pressableText}>
                TouchableOpacity in Pressable
              </Text>
            </TouchableOpacity>
          </RNGHPressable>

          <TouchableOpacity
            style={styles.pressable}
            onPress={() =>
              console.log('Pressable in TouchableOpacity: Pressable')
            }>
            <RNGHPressable
              style={innerStyle}
              onPress={() =>
                console.log('Pressable in TouchableOpacity: TouchableOpacity')
              }>
              <Text style={styles.pressableText}>
                Pressable in TouchableOpacity
              </Text>
            </RNGHPressable>
          </TouchableOpacity>

          <RNGHPressable
            style={outerStyle}
            onPress={() =>
              console.log('TouchableHighlight in Pressable: Pressable')
            }>
            <TouchableHighlight
              style={styles.innerPressable}
              onPress={() =>
                console.log(
                  'TouchableHighlight in Pressable: TouchableHighlight'
                )
              }>
              <Text style={styles.pressableText}>
                TouchableHighlight in Pressable
              </Text>
            </TouchableHighlight>
          </RNGHPressable>

          <TouchableHighlight
            style={styles.pressable}
            onPress={() =>
              console.log('Pressable in TouchableHighlight: Pressable')
            }>
            <RNGHPressable
              style={innerStyle}
              onPress={() =>
                console.log(
                  'Pressable in TouchableHighlight: TouchableHighlight'
                )
              }>
              <Text style={styles.pressableText}>
                Pressable in TouchableHighlight
              </Text>
            </RNGHPressable>
          </TouchableHighlight>

          <RNGHPressable
            style={outerStyle}
            onPress={() =>
              console.log('Pressable in TouchableHighlight: Parent')
            }>
            <RNGHPressable
              style={innerStyle}
              onPress={() => console.log('Pressable in Pressable: Child')}>
              <Text style={styles.pressableText}>Pressable in Pressable</Text>
            </RNGHPressable>
          </RNGHPressable>
        </ScrollView>

        <View style={styles.rowContainer}>
          <RNGHPressable
            style={outerStyle}
            onPressIn={() => console.log(JSON.stringify('RNGH: onPressIn'))}
            onPressOut={() => console.log(JSON.stringify('RNGH: onPressOut'))}
            onPress={() => console.log(JSON.stringify('RNGH: onPress'))}>
            <Text style={styles.pressableText}>RNGH Pressable</Text>
          </RNGHPressable>
          <RNPressable
            style={outerStyle}
            onPressIn={() => console.log(JSON.stringify('RN: onPressIn'))}
            onPressOut={() => console.log(JSON.stringify('RN: onPressOut'))}
            onPress={() => console.log(JSON.stringify('RN: onPress'))}>
            <Text style={styles.pressableText}>RN Pressable</Text>
          </RNPressable>

          <RNGHPressable
            style={outerStyle}
            onPress={() =>
              console.log('TouchableOpacity in Pressable: Pressable')
            }>
            <TouchableOpacity
              style={styles.innerPressable}
              onPress={() =>
                console.log('TouchableOpacity in Pressable: TouchableOpacity')
              }>
              <Text style={styles.pressableText}>
                TouchableOpacity in Pressable
              </Text>
            </TouchableOpacity>
          </RNGHPressable>

          <TouchableOpacity
            style={styles.pressable}
            onPress={() =>
              console.log('Pressable in TouchableOpacity: Pressable')
            }>
            <RNGHPressable
              style={innerStyle}
              onPress={() =>
                console.log('Pressable in TouchableOpacity: TouchableOpacity')
              }>
              <Text style={styles.pressableText}>
                Pressable in TouchableOpacity
              </Text>
            </RNGHPressable>
          </TouchableOpacity>

          <RNGHPressable
            style={outerStyle}
            onPress={() =>
              console.log('TouchableHighlight in Pressable: Pressable')
            }>
            <TouchableHighlight
              style={styles.innerPressable}
              onPress={() =>
                console.log(
                  'TouchableHighlight in Pressable: TouchableHighlight'
                )
              }>
              <Text style={styles.pressableText}>
                TouchableHighlight in Pressable
              </Text>
            </TouchableHighlight>
          </RNGHPressable>

          <TouchableHighlight
            style={styles.pressable}
            onPress={() =>
              console.log('Pressable in TouchableHighlight: Pressable')
            }>
            <RNGHPressable
              style={innerStyle}
              onPress={() =>
                console.log(
                  'Pressable in TouchableHighlight: TouchableHighlight'
                )
              }>
              <Text style={styles.pressableText}>
                Pressable in TouchableHighlight
              </Text>
            </RNGHPressable>
          </TouchableHighlight>

          <RNGHPressable
            style={outerStyle}
            onPress={() =>
              console.log('Pressable in TouchableHighlight: Parent')
            }>
            <RNGHPressable
              style={innerStyle}
              onPress={() => console.log('Pressable in Pressable: Child')}>
              <Text style={styles.pressableText}>Pressable in Pressable</Text>
            </RNGHPressable>
          </RNGHPressable>
        </View>
      </SafeAreaView>
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    gap: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
  rowContainer: {
    flex: 1,
    flexDirection: 'column',
    gap: 5,
    alignItems: 'center',
    justifyContent: 'center',
  },
  pressable: {
    backgroundColor: 'pink',
    paddingHorizontal: 15,
    height: 45,
    width: 380,
    alignItems: 'center',
    justifyContent: 'center',
  },
  innerPressable: {
    backgroundColor: 'orange',
    paddingVertical: 10,
    paddingHorizontal: 15,
    height: 45,
    width: 300,
    alignItems: 'center',
    justifyContent: 'center',
  },
  pressableText: {
    color: '#000',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

@coado coado merged commit 1665fc9 into main Dec 13, 2024
1 check passed
@coado coado deleted the @dmalecki/pressable-callbacks branch December 13, 2024 13:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pressable component trigger onPressIn and onPressOut at the same time even if the touch is held
2 participants