Close swipeable on recycle
This commit is contained in:
parent
2b06b01bd6
commit
7e849943fe
|
@ -51,9 +51,11 @@ const RoomItem = ({
|
|||
showAvatar,
|
||||
displayMode,
|
||||
sourceType,
|
||||
hideMentionStatus
|
||||
hideMentionStatus,
|
||||
touchableRef
|
||||
}: IRoomItemProps) => (
|
||||
<Touchable
|
||||
ref={touchableRef}
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
favorite={favorite}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||
import Animated, {
|
||||
useAnimatedGestureHandler,
|
||||
useSharedValue,
|
||||
|
@ -18,227 +18,237 @@ 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 } from './interfaces';
|
||||
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 = ({
|
||||
children,
|
||||
type,
|
||||
onPress,
|
||||
onLongPress,
|
||||
testID,
|
||||
favorite,
|
||||
isRead,
|
||||
rid,
|
||||
toggleFav,
|
||||
toggleRead,
|
||||
hideChannel,
|
||||
isFocused,
|
||||
swipeEnabled,
|
||||
displayMode
|
||||
}: ITouchableProps): React.ReactElement => {
|
||||
const { colors } = useTheme();
|
||||
const { width: deviceWidth } = useWindowDimensions();
|
||||
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||
const width = isMasterDetail ? MAX_SIDEBAR_WIDTH : deviceWidth;
|
||||
const Touchable = forwardRef<ITouchableRef, ITouchableProps>(
|
||||
(
|
||||
{
|
||||
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 (
|
||||
<LongPressGestureHandler onHandlerStateChange={onLongPressHandlerStateChange}>
|
||||
<Animated.View>
|
||||
<PanGestureHandler activeOffsetX={[-20, 20]} onGestureEvent={onGestureEvent} enabled={swipeEnabled}>
|
||||
<Animated.View>
|
||||
<LeftActions
|
||||
transX={transX}
|
||||
isRead={isRead}
|
||||
width={width}
|
||||
onToggleReadPress={onToggleReadPress}
|
||||
displayMode={displayMode}
|
||||
/>
|
||||
<RightActions
|
||||
transX={transX}
|
||||
favorite={favorite}
|
||||
width={width}
|
||||
toggleFav={handleToggleFav}
|
||||
onHidePress={onHidePress}
|
||||
displayMode={displayMode}
|
||||
/>
|
||||
<Animated.View style={animatedStyles}>
|
||||
<Touch
|
||||
onPress={handlePress}
|
||||
testID={testID}
|
||||
style={{
|
||||
backgroundColor: isFocused ? colors.chatComponentBackground : colors.backgroundColor
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Touch>
|
||||
return (
|
||||
<LongPressGestureHandler onHandlerStateChange={onLongPressHandlerStateChange}>
|
||||
<Animated.View>
|
||||
<PanGestureHandler activeOffsetX={[-20, 20]} onGestureEvent={onGestureEvent} enabled={swipeEnabled}>
|
||||
<Animated.View>
|
||||
<LeftActions
|
||||
transX={transX}
|
||||
isRead={isRead}
|
||||
width={width}
|
||||
onToggleReadPress={onToggleReadPress}
|
||||
displayMode={displayMode}
|
||||
/>
|
||||
<RightActions
|
||||
transX={transX}
|
||||
favorite={favorite}
|
||||
width={width}
|
||||
toggleFav={handleToggleFav}
|
||||
onHidePress={onHidePress}
|
||||
displayMode={displayMode}
|
||||
/>
|
||||
<Animated.View style={animatedStyles}>
|
||||
<Touch
|
||||
onPress={handlePress}
|
||||
testID={testID}
|
||||
style={{
|
||||
backgroundColor: isFocused ? colors.chatComponentBackground : colors.backgroundColor
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Touch>
|
||||
</Animated.View>
|
||||
</Animated.View>
|
||||
</Animated.View>
|
||||
</PanGestureHandler>
|
||||
</Animated.View>
|
||||
</LongPressGestureHandler>
|
||||
);
|
||||
};
|
||||
</PanGestureHandler>
|
||||
</Animated.View>
|
||||
</LongPressGestureHandler>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default Touchable;
|
||||
|
|
|
@ -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<ITouchableRef>(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 (
|
||||
<RoomItem
|
||||
touchableRef={touchableRef}
|
||||
name={name}
|
||||
avatar={avatar}
|
||||
isGroupChat={isGroupChat(item)}
|
||||
|
|
|
@ -115,6 +115,7 @@ export interface IRoomItemProps extends IBaseRoomItem {
|
|||
size?: number;
|
||||
sourceType: IOmnichannelSource;
|
||||
hideMentionStatus?: boolean;
|
||||
touchableRef: React.RefObject<ITouchableRef>;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue