From 7e849943fe0326577cf2a145193571c8684c65da Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 13 Dec 2022 15:53:04 -0300 Subject: [PATCH] Close swipeable on recycle --- app/containers/RoomItem/RoomItem.tsx | 4 +- app/containers/RoomItem/Touchable.tsx | 400 +++++++++++++------------- app/containers/RoomItem/index.tsx | 9 +- app/containers/RoomItem/interfaces.ts | 5 + 4 files changed, 220 insertions(+), 198 deletions(-) diff --git a/app/containers/RoomItem/RoomItem.tsx b/app/containers/RoomItem/RoomItem.tsx index 3dbb3408f..bcc8ea62a 100644 --- a/app/containers/RoomItem/RoomItem.tsx +++ b/app/containers/RoomItem/RoomItem.tsx @@ -51,9 +51,11 @@ const RoomItem = ({ showAvatar, displayMode, sourceType, - hideMentionStatus + hideMentionStatus, + touchableRef }: IRoomItemProps) => ( { - const { colors } = useTheme(); - const { width: deviceWidth } = useWindowDimensions(); - const isMasterDetail = useAppSelector(state => state.app.isMasterDetail); - const width = isMasterDetail ? MAX_SIDEBAR_WIDTH : deviceWidth; +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 rowOffSet = useSharedValue(0); + const transX = useSharedValue(0); + const rowState = useSharedValue(0); // 0: closed, 1: right opened, -1: left opened + let _value = 0; - const close = () => { - rowState.value = 0; - transX.value = withSpring(0, { overshootClamping: true }); - rowOffSet.value = 0; - }; + const close = () => { + console.log(`${rid} close`); + rowState.value = 0; + transX.value = withSpring(0, { overshootClamping: true }); + rowOffSet.value = 0; + }; - const handleToggleFav = () => { - if (toggleFav) { - toggleFav(rid, favorite); - } - close(); - }; + useImperativeHandle(ref, () => ({ + 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) { + const handleToggleFav = () => { + if (toggleFav) { + toggleFav(rid, favorite); + } close(); - return; - } - if (onPress) { - onPress(); - } - }; + }; - const handleLongPress = () => { - if (rowState.value !== 0) { + const handleToggleRead = () => { + if (toggleRead) { + toggleRead(rid, isRead); + } + }; + + const handleHideChannel = () => { + if (hideChannel) { + hideChannel(rid, type); + } + }; + + const onToggleReadPress = () => { + handleToggleRead(); close(); - return; - } + }; - if (onLongPress) { - onLongPress(); - } - }; + const onHidePress = () => { + handleHideChannel(); + close(); + }; - const onLongPressHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => { - if (nativeEvent.state === State.ACTIVE) { - handleLongPress(); - } - }; + const handlePress = () => { + if (rowState.value !== 0) { + close(); + return; + } + if (onPress) { + onPress(); + } + }; - 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) { + 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; } - 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) { + } 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; } - 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; + 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); } - } 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 }] })); - const animatedStyles = useAnimatedStyle(() => ({ transform: [{ translateX: transX.value }] })); - - return ( - - - - - - - - - {children} - + return ( + + + + + + + + + {children} + + - - - - - ); -}; + + + + ); + } +); export default Touchable; diff --git a/app/containers/RoomItem/index.tsx b/app/containers/RoomItem/index.tsx index 2534894d5..c5334c408 100644 --- a/app/containers/RoomItem/index.tsx +++ b/app/containers/RoomItem/index.tsx @@ -1,11 +1,11 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef } from 'react'; import I18n from '../../i18n'; import { useAppSelector } from '../../lib/hooks'; import { getUserPresence } from '../../lib/methods'; import { isGroupChat } from '../../lib/methods/helpers'; import { formatDate } from '../../lib/methods/helpers/room'; -import { IRoomItemContainerProps } from './interfaces'; +import { IRoomItemContainerProps, ITouchableRef } from './interfaces'; import RoomItem from './RoomItem'; import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles'; @@ -40,6 +40,7 @@ const RoomItemContainer = ({ const connected = useAppSelector(state => state.meteor.connected); const userStatus = useAppSelector(state => state.activeUsers[id || '']?.status); const isDirect = !!(item.t === 'd' && id && !isGroupChat(item)); + const touchableRef = useRef(null); // When app reconnects, we need to fetch the rendered user's presence useEffect(() => { @@ -56,6 +57,9 @@ const RoomItemContainer = ({ if (!userStatus && isDirect) { getUserPresence(id); } + + // TODO: Remove this when we have a better way to close the swipeable + touchableRef?.current?.close(); }, [item.rid]); const handleOnPress = () => onPress(item); @@ -79,6 +83,7 @@ const RoomItemContainer = ({ return ( ; } export interface ILastMessageProps { @@ -126,6 +127,10 @@ export interface ILastMessageProps { alert: boolean; } +export interface ITouchableRef { + close: () => void; +} + export interface ITouchableProps extends IRoomItemTouchables { children: JSX.Element; type: SubscriptionType;