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 left action panel of ReanimatedSwipeable being non-interactive #3236

Closed

Conversation

latekvo
Copy link
Contributor

@latekvo latekvo commented Nov 21, 2024

Description



Please do not review, I found some issues with this approach.



The left action panel of ReanimatedSwipeable has an issue, where it's not interactive.

This issue is a result of the StyleSheet.absoluteFillObject styles messing with the hitboxes of the Swipeable's movable part.
The central hitboxes are picking up presses landing on the left action panel, despite the two Views having no overlap,
since they were out of bounds of the swipeable, and blocked from pressing the left action panel, the touch events got lost every time the left panel was clicked.

This PR fixes this issue by separating the action panels from the movable part of the swipeable.

I could not find a more elegant way of resolving this issue - both StyleSheet changes, and structural changes within the individual action panel render functions usually resulted in the onLayout functions returning LayoutChangeEvents with completely zeroed values.

Closes #3223

Test plan

Collapsed repro
import React from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import Swipeable from 'react-native-gesture-handler/ReanimatedSwipeable';

export default function Example() {
  return (
    <View style={{ flex: 1, marginTop: 200, backgroundColor: 'pink' }}>
      <Swipeable
        onSwipeableWillClose={(dir) => {
          console.log('will close', dir);
        }}
        onSwipeableWillOpen={(dir) => {
          console.log('will open', dir);
        }}
        onSwipeableOpen={(dir) => {
          console.log('open', dir);
        }}
        onSwipeableClose={(dir) => {
          console.log('close', dir);
        }}
        leftThreshold={50}
        rightThreshold={50}
        renderLeftActions={() => {
          return (
            <TouchableOpacity
              onPress={() => {
                console.log('press left');
              }}>
              <View
                style={{ height: 80, width: 80, backgroundColor: 'yellow' }}>
                <Text>Left</Text>
              </View>
            </TouchableOpacity>
          );
        }}
        renderRightActions={() => {
          return (
            <TouchableOpacity
              onPress={() => {
                console.log('press right');
              }}>
              <View
                style={{ height: 80, width: 80, backgroundColor: 'magenta' }}>
                <Text>Right</Text>
              </View>
            </TouchableOpacity>
          );
        }}>
        <TouchableOpacity
          onPress={() => {
            console.log('press outer');
          }}>
          <View
            style={{
              width: '100%',
              height: 80,
              backgroundColor: 'blue',
            }}>
            <TouchableOpacity
              onPress={() => {
                console.log('press inner');
              }}
              style={{ width: 80, height: 80, alignSelf: 'center' }}>
              <View
                style={{ width: 80, height: 80, backgroundColor: 'white' }}
              />
            </TouchableOpacity>
          </View>
        </TouchableOpacity>
      </Swipeable>
    </View>
  );
}

Potential issue with reanimated Animated.View - minimal repo

Collapsed code
import React, { forwardRef, useCallback } from 'react';
import Animated from 'react-native-reanimated';
import {
  Text,
  I18nManager,
  StyleSheet,
  TouchableOpacity,
  View,
} from 'react-native';

interface SwipeableMethods {
  close: () => void;
  openLeft: () => void;
  openRight: () => void;
  reset: () => void;
}

const Example = forwardRef<SwipeableMethods, unknown>((_props, _ref) => {
  const leftElement = useCallback(
    () => (
      <View
        style={{
          ...StyleSheet.absoluteFillObject,
          flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
        }}>
        <TouchableOpacity
          onPress={() => {
            console.log('press left');
          }}>
          <View style={{ height: 80, width: 80, backgroundColor: 'yellow' }}>
            <Text>Left</Text>
          </View>
        </TouchableOpacity>
      </View>
    ),
    []
  );

  const rightElement = useCallback(
    () => (
      <Animated.View
        style={{
          ...StyleSheet.absoluteFillObject,
          flexDirection: I18nManager.isRTL ? 'row' : 'row-reverse',
        }}>
        <TouchableOpacity
          onPress={() => {
            console.log('press right');
          }}>
          <View style={{ height: 80, width: 80, backgroundColor: 'magenta' }}>
            <Text>Right</Text>
          </View>
        </TouchableOpacity>
      </Animated.View>
    ),
    []
  );

  return (
    <View>
      {leftElement()}
      {rightElement()}
      <View
        style={{
          transform: [{ translateX: 0 }, { scale: 0.5 }],
          width: 393,
          height: 80,
          backgroundColor: 'blue',
        }}
      />
    </View>
  );
});

export default Example;

@latekvo
Copy link
Contributor Author

latekvo commented Nov 21, 2024

This is likely an external issue, blocked until this is either resolved or closed.

@latekvo latekvo closed this Nov 21, 2024
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.

Swipeable renderLeftActions button not firing
1 participant