import React from 'react'; import { Animated } from 'react-native'; import { GestureEvent, HandlerStateChangeEventPayload, LongPressGestureHandler, PanGestureHandler, PanGestureHandlerEventPayload, State } from 'react-native-gesture-handler'; import Touch from '../../utils/touch'; import { ACTION_WIDTH, LONG_SWIPE, SMALL_SWIPE } from './styles'; import { isRTL } from '../../i18n'; import { themes } from '../../lib/constants'; import { LeftActions, RightActions } from './Actions'; import { ITouchableProps } from './interfaces'; class Touchable extends React.Component { private dragX: Animated.Value; private rowOffSet: Animated.Value; private reverse: Animated.Value; private transX: Animated.AnimatedAddition; private transXReverse: Animated.AnimatedMultiplication; private _onGestureEvent: (event: GestureEvent) => void; private _value: number; constructor(props: ITouchableProps) { super(props); this.dragX = new Animated.Value(0); this.rowOffSet = new Animated.Value(0); this.reverse = new Animated.Value(isRTL() ? -1 : 1); this.transX = Animated.add(this.rowOffSet, this.dragX); this.transXReverse = Animated.multiply(this.transX, this.reverse); this.state = { rowState: 0 // 0: closed, 1: right opened, -1: left opened }; this._onGestureEvent = Animated.event([{ nativeEvent: { translationX: this.dragX } }], { useNativeDriver: true }); this._value = 0; } _onHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload & PanGestureHandlerEventPayload }) => { if (nativeEvent.oldState === State.ACTIVE) { this._handleRelease(nativeEvent); } }; onLongPressHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => { if (nativeEvent.state === State.ACTIVE) { this.onLongPress(); } }; _handleRelease = (nativeEvent: PanGestureHandlerEventPayload) => { const { translationX } = nativeEvent; const { rowState } = this.state; this._value += translationX; let toValue = 0; if (rowState === 0) { // if no option is opened if (translationX > 0 && translationX < LONG_SWIPE) { // open leading option if he swipe right but not enough to trigger action if (isRTL()) { toValue = 2 * ACTION_WIDTH; } else { toValue = ACTION_WIDTH; } this.setState({ rowState: -1 }); } else if (translationX >= LONG_SWIPE) { toValue = 0; if (isRTL()) { this.hideChannel(); } else { this.toggleRead(); } } else if (translationX < 0 && translationX > -LONG_SWIPE) { // open trailing option if he swipe left if (isRTL()) { toValue = -ACTION_WIDTH; } else { toValue = -2 * ACTION_WIDTH; } this.setState({ rowState: 1 }); } else if (translationX <= -LONG_SWIPE) { toValue = 0; this.setState({ rowState: 0 }); if (isRTL()) { this.toggleRead(); } else { this.hideChannel(); } } else { toValue = 0; } } if (rowState === -1) { // if left option is opened if (this._value < SMALL_SWIPE) { toValue = 0; this.setState({ rowState: 0 }); } else if (this._value > LONG_SWIPE) { toValue = 0; this.setState({ rowState: 0 }); if (isRTL()) { this.hideChannel(); } else { this.toggleRead(); } } else if (isRTL()) { toValue = 2 * ACTION_WIDTH; } else { toValue = ACTION_WIDTH; } } if (rowState === 1) { // if right option is opened if (this._value > -2 * SMALL_SWIPE) { toValue = 0; this.setState({ rowState: 0 }); } else if (this._value < -LONG_SWIPE) { toValue = 0; this.setState({ rowState: 0 }); if (isRTL()) { this.toggleRead(); } else { this.hideChannel(); } } else if (isRTL()) { toValue = -ACTION_WIDTH; } else { toValue = -2 * ACTION_WIDTH; } } this._animateRow(toValue); }; _animateRow = (toValue: number) => { this.rowOffSet.setValue(this._value); this._value = toValue; this.dragX.setValue(0); Animated.spring(this.rowOffSet, { toValue, bounciness: 0, useNativeDriver: true }).start(); }; close = () => { this.setState({ rowState: 0 }); this._animateRow(0); }; toggleFav = () => { const { toggleFav, rid, favorite } = this.props; if (toggleFav) { toggleFav(rid, favorite); } this.close(); }; toggleRead = () => { const { toggleRead, rid, isRead } = this.props; if (toggleRead) { toggleRead(rid, isRead); } }; hideChannel = () => { const { hideChannel, rid, type } = this.props; if (hideChannel) { hideChannel(rid, type); } }; onToggleReadPress = () => { this.toggleRead(); this.close(); }; onHidePress = () => { this.hideChannel(); this.close(); }; onPress = () => { const { rowState } = this.state; if (rowState !== 0) { this.close(); return; } const { onPress } = this.props; if (onPress) { onPress(); } }; onLongPress = () => { const { rowState } = this.state; const { onLongPress } = this.props; if (rowState !== 0) { this.close(); return; } if (onLongPress) { onLongPress(); } }; render() { const { testID, isRead, width, favorite, children, theme, isFocused, swipeEnabled, displayMode } = this.props; return ( {children} ); } } export default Touchable;