import React, { forwardRef, useImperativeHandle } from 'react'; import Animated, { useAnimatedGestureHandler, useSharedValue, useAnimatedStyle, withSpring, runOnJS } from 'react-native-reanimated'; import { LongPressGestureHandler, PanGestureHandler, State, HandlerStateChangeEventPayload, PanGestureHandlerEventPayload } from 'react-native-gesture-handler'; import { useWindowDimensions } from 'react-native'; import Touch from '../Touch'; import { ACTION_WIDTH, LONG_SWIPE, SMALL_SWIPE } from './styles'; import { LeftActions, RightActions } from './Actions'; import { ITouchableProps, ITouchableRef } from './interfaces'; import { useTheme } from '../../theme'; import I18n from '../../i18n'; import { MAX_SIDEBAR_WIDTH } from '../../lib/constants'; import { useAppSelector } from '../../lib/hooks'; const Touchable = forwardRef( ( { children, type, onPress, onLongPress, testID, favorite, isRead, rid, toggleFav, toggleRead, hideChannel, isFocused, swipeEnabled, displayMode }, ref ): React.ReactElement => { const { colors } = useTheme(); const { width: deviceWidth } = useWindowDimensions(); const isMasterDetail = useAppSelector(state => state.app.isMasterDetail); const width = isMasterDetail ? MAX_SIDEBAR_WIDTH : deviceWidth; const rowOffSet = useSharedValue(0); const transX = useSharedValue(0); const rowState = useSharedValue(0); // 0: closed, 1: right opened, -1: left opened let _value = 0; const close = () => { console.log(`${rid} close`); rowState.value = 0; transX.value = withSpring(0, { overshootClamping: true }); rowOffSet.value = 0; }; useImperativeHandle(ref, () => ({ close })); const handleToggleFav = () => { if (toggleFav) { toggleFav(rid, favorite); } close(); }; const handleToggleRead = () => { if (toggleRead) { toggleRead(rid, isRead); } }; const handleHideChannel = () => { if (hideChannel) { hideChannel(rid, type); } }; const onToggleReadPress = () => { handleToggleRead(); close(); }; const onHidePress = () => { handleHideChannel(); close(); }; const handlePress = () => { if (rowState.value !== 0) { close(); return; } if (onPress) { onPress(); } }; const handleLongPress = () => { if (rowState.value !== 0) { close(); return; } if (onLongPress) { onLongPress(); } }; const onLongPressHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => { if (nativeEvent.state === State.ACTIVE) { handleLongPress(); } }; const handleRelease = (event: PanGestureHandlerEventPayload) => { const { translationX } = event; _value += translationX; let toValue = 0; if (rowState.value === 0) { // if no option is opened if (translationX > 0 && translationX < LONG_SWIPE) { if (I18n.isRTL) { toValue = 2 * ACTION_WIDTH; } else { toValue = ACTION_WIDTH; } rowState.value = -1; } else if (translationX >= LONG_SWIPE) { toValue = 0; if (I18n.isRTL) { handleHideChannel(); } else { handleToggleRead(); } } else if (translationX < 0 && translationX > -LONG_SWIPE) { // open trailing option if he swipe left if (I18n.isRTL) { toValue = -ACTION_WIDTH; } else { toValue = -2 * ACTION_WIDTH; } rowState.value = 1; } else if (translationX <= -LONG_SWIPE) { toValue = 0; rowState.value = 1; if (I18n.isRTL) { handleToggleRead(); } else { handleHideChannel(); } } else { toValue = 0; } } else if (rowState.value === -1) { // if left option is opened if (_value < SMALL_SWIPE) { toValue = 0; rowState.value = 0; } else if (_value > LONG_SWIPE) { toValue = 0; rowState.value = 0; if (I18n.isRTL) { handleHideChannel(); } else { handleToggleRead(); } } else if (I18n.isRTL) { toValue = 2 * ACTION_WIDTH; } else { toValue = ACTION_WIDTH; } } else if (rowState.value === 1) { // if right option is opened if (_value > -2 * SMALL_SWIPE) { toValue = 0; rowState.value = 0; } else if (_value < -LONG_SWIPE) { if (I18n.isRTL) { handleToggleRead(); } else { handleHideChannel(); } } else if (I18n.isRTL) { toValue = -ACTION_WIDTH; } else { toValue = -2 * ACTION_WIDTH; } } transX.value = withSpring(toValue, { overshootClamping: true }); rowOffSet.value = toValue; _value = toValue; }; const onGestureEvent = useAnimatedGestureHandler({ onActive: event => { transX.value = event.translationX + rowOffSet.value; if (transX.value > 2 * width) transX.value = 2 * width; }, onEnd: event => { runOnJS(handleRelease)(event); } }); const animatedStyles = useAnimatedStyle(() => ({ transform: [{ translateX: transX.value }] })); return ( {children} ); } ); export default Touchable;