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 Pressables's interference with other gestures when nested #3015

Merged
merged 5 commits into from
Aug 12, 2024

Conversation

latekvo
Copy link
Contributor

@latekvo latekvo commented Jul 29, 2024

Description

LongPress was found to be using excessively high value for it's minDuration and maxDistance configs.

While maxDistance config caused no issues, minDuration config caused an instantaneous activation.
This behaviour stems from setTimeout handling values up to 2^31-1 while MAX_SAFE_INTEGER is much higher than that.
This unintended activation blocked some nested gestures underneath the Pressable from activating on web.

By replacing Number.MAX_SAFE_INTEGER with 2 ** 31 - 1 which represents the largest possible 32-bit integer, those issues were resolved.

Found while investigating this issue.

Both issues - the one reported by @milan-digiuseppe-level and the one described in this PR might be related, but it's not immidiately clear as I couldn't replicate the former one.

closes #2863

Test plan

  • open the EmptyExample in both an editor and a web browser
  • replace the default exported function with the attached code
  • without this fix, only 2 of the 4 Swipeables can be moved by their nested Pressable, with this fix, all of them can be swiped
Before After
Screen.Recording.2024-07-29.at.16.58.16.mov
Screen.Recording.2024-07-29.at.17.30.39.mov

Attached code

/* eslint-disable no-alert */
import React from 'react';
import { Text, Animated, StyleSheet, View, Pressable } from 'react-native';

import {
  Pressable as GHPressable,
  Swipeable,
  GestureHandlerRootView,
} from 'react-native-gesture-handler';
import ReanimatedSwipeable from 'react-native-gesture-handler/ReanimatedSwipeable';
import Reanimated, {
  SharedValue,
  useAnimatedStyle,
} from 'react-native-reanimated';

function LeftAction(prog: SharedValue<number>, drag: SharedValue<number>) {
  const styleAnimation = useAnimatedStyle(() => {
    console.log('[R] showLeftProgress:', prog.value);
    console.log('[R] appliedTranslation:', drag.value);

    return {
      transform: [{ translateX: drag.value - 50 }],
    };
  });

  return (
    <Reanimated.View style={styleAnimation}>
      <Text style={styles.leftAction}>Text</Text>
    </Reanimated.View>
  );
}

function RightAction(prog: SharedValue<number>, drag: SharedValue<number>) {
  const styleAnimation = useAnimatedStyle(() => {
    console.log('[R] showRightProgress:', prog.value);
    console.log('[R] appliedTranslation:', drag.value);

    return {
      transform: [{ translateX: drag.value + 50 }],
    };
  });

  return (
    <Reanimated.View style={styleAnimation}>
      <Text style={styles.rightAction}>Text</Text>
    </Reanimated.View>
  );
}

function LegacyLeftAction(prog: any, drag: any) {
  prog.addListener((value: any) => {
    console.log('[L] showLeftProgress:', value.value);
  });
  drag.addListener((value: any) => {
    console.log('[L] appliedTranslation:', value.value);
  });

  const trans = Animated.subtract(drag, 50);

  return (
    <Animated.Text
      style={[
        styles.leftAction,
        {
          transform: [{ translateX: trans }],
        },
      ]}>
      Text
    </Animated.Text>
  );
}

function LegacyRightAction(prog: any, drag: any) {
  prog.addListener((value: any) => {
    console.log('[L] showRightProgress:', value.value);
  });
  drag.addListener((value: any) => {
    console.log('[L] appliedTranslation:', value.value);
  });

  const trans = Animated.add(drag, 50);

  return (
    <Animated.Text
      style={[
        styles.rightAction,
        {
          transform: [{ translateX: trans }],
        },
      ]}>
      Text
    </Animated.Text>
  );
}

export default function Example() {
  return (
    <GestureHandlerRootView>
      <View style={styles.separator} />

      <ReanimatedSwipeable
        containerStyle={styles.swipeable}
        friction={2}
        leftThreshold={80}
        enableTrackpadTwoFingerGesture
        rightThreshold={56}
        renderLeftActions={LeftAction}
        renderRightActions={RightAction}>
        <Pressable onPress={() => alert('pressed!')} style={styles.pressable}>
          <Text>[new] with RN pressable</Text>
        </Pressable>
      </ReanimatedSwipeable>

      <View style={styles.separator} />

      <ReanimatedSwipeable
        containerStyle={styles.swipeable}
        friction={2}
        leftThreshold={80}
        enableTrackpadTwoFingerGesture
        rightThreshold={56}
        renderLeftActions={LeftAction}
        renderRightActions={RightAction}>
        <GHPressable onPress={() => alert('pressed!')} style={styles.pressable}>
          <Text>[new] with GH pressable</Text>
        </GHPressable>
      </ReanimatedSwipeable>

      <View style={styles.separator} />

      <Swipeable
        containerStyle={styles.swipeable}
        friction={2}
        leftThreshold={80}
        enableTrackpadTwoFingerGesture
        rightThreshold={56}
        renderLeftActions={LegacyLeftAction}
        renderRightActions={LegacyRightAction}>
        <Pressable onPress={() => alert('pressed!')} style={styles.pressable}>
          <Text>[Legacy] with RN pressable</Text>
        </Pressable>
      </Swipeable>

      <View style={styles.separator} />

      <Swipeable
        containerStyle={styles.swipeable}
        friction={2}
        leftThreshold={80}
        enableTrackpadTwoFingerGesture
        rightThreshold={56}
        renderLeftActions={LegacyLeftAction}
        renderRightActions={LegacyRightAction}>
        <GHPressable onPress={() => alert('pressed!')} style={styles.pressable}>
          <Text>[Legacy] with GH pressable</Text>
        </GHPressable>
      </Swipeable>

      <View style={styles.separator} />
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  leftAction: { width: 46, height: 56, backgroundColor: 'crimson' },
  rightAction: { width: 46, height: 56, backgroundColor: 'purple' },
  separator: {
    width: '100%',
    borderTopWidth: 1,
  },
  swipeable: {
    height: 56,
    backgroundColor: 'papayawhip',
    alignItems: 'center',
  },
  pressable: {
    padding: 20,
    width: 200,
    height: 56,
    backgroundColor: 'pink',
    alignItems: 'center',
  },
});

@latekvo latekvo marked this pull request as ready for review July 29, 2024 15:20
@latekvo latekvo requested review from j-piasecki and m-bert and removed request for j-piasecki July 29, 2024 15:20
@latekvo
Copy link
Contributor Author

latekvo commented Jul 29, 2024

After further testing, it appears that on iOS, when Pressable is nested inside a native Scroll, only every second press is registered. I'll address this issue asap.

@j-piasecki
Copy link
Member

Doesn't changing LongPress to Manual change the behavior on native? I believe we were talking about it and Manual was causing other gestures to misbehave.

@j-piasecki
Copy link
Member

Also, the title is a bit confusing as it suggests changes to Swipeable component.

@latekvo
Copy link
Contributor Author

latekvo commented Jul 29, 2024

Doesn't changing LongPress to Manual change the behavior on native? I believe we were talking about it and Manual was causing other gestures to misbehave. - @j-piasecki

It does!
But I found that by adding .manualActivation(true) to the Manual gesture, most of those issues can be alleviated. The only one that I have to fix after this change is the one I mentioned in the comment above.

Also, the title is a bit confusing as it suggests changes to Swipeable component.

I'll fix the title.

@latekvo latekvo changed the title Fix new Swipeable's interference with nested gestures Fix Pressables's interference with other gestures when nested Jul 29, 2024
@latekvo latekvo marked this pull request as draft July 30, 2024 07:44
@latekvo latekvo marked this pull request as ready for review August 12, 2024 09:53
src/utils.ts Outdated Show resolved Hide resolved
@m-bert
Copy link
Contributor

m-bert commented Aug 12, 2024

Also, please update PR description 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Web] Nested Pressable inside a Swipeable captures press when swiping
3 participants