diff --git a/native/chat/swipeable-thread.react.js b/native/chat/swipeable-thread.react.js index f04f9c100..e0a1f0f76 100644 --- a/native/chat/swipeable-thread.react.js +++ b/native/chat/swipeable-thread.react.js @@ -1,118 +1,118 @@ // @flow import { useNavigation } from '@react-navigation/native'; import * as React from 'react'; import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons'; import { setThreadUnreadStatus, setThreadUnreadStatusActionTypes, } from 'lib/actions/activity-actions'; import type { SetThreadUnreadStatusPayload, SetThreadUnreadStatusRequest, } from 'lib/types/activity-types'; import type { ThreadInfo } from 'lib/types/thread-types'; import { useDispatchActionPromise, useServerCall, } from 'lib/utils/action-utils'; import Swipeable from '../components/swipeable'; import { useColors } from '../themes/colors'; type Props = {| +threadInfo: ThreadInfo, +mostRecentNonLocalMessage: ?string, +onSwipeableWillOpen: (threadInfo: ThreadInfo) => void, +currentlyOpenedSwipeableId?: string, +iconSize: number, +children: React.Node, |}; function SwipeableThread(props: Props) { - const swipeable = React.useRef(); + const swipeable = React.useRef(); const navigation = useNavigation(); React.useEffect(() => { return navigation.addListener('blur', () => { if (swipeable.current) { swipeable.current.close(); } }); }, [navigation, swipeable]); const { threadInfo, currentlyOpenedSwipeableId } = props; React.useEffect(() => { if (swipeable.current && threadInfo.id !== currentlyOpenedSwipeableId) { swipeable.current.close(); } }, [currentlyOpenedSwipeableId, swipeable, threadInfo.id]); const { onSwipeableWillOpen } = props; const onSwipeableRightWillOpen = React.useCallback(() => { onSwipeableWillOpen(threadInfo); }, [onSwipeableWillOpen, threadInfo]); const colors = useColors(); const { mostRecentNonLocalMessage, iconSize } = props; const updateUnreadStatus: ( request: SetThreadUnreadStatusRequest, ) => Promise = useServerCall( setThreadUnreadStatus, ); const dispatchActionPromise = useDispatchActionPromise(); const swipeableActions = React.useMemo(() => { const isUnread = threadInfo.currentUser.unread; const toggleUnreadStatus = () => { const request = { unread: !isUnread, threadID: threadInfo.id, latestMessage: mostRecentNonLocalMessage, }; dispatchActionPromise( setThreadUnreadStatusActionTypes, updateUnreadStatus(request), undefined, { threadID: threadInfo.id, unread: !isUnread, }, ); if (swipeable.current) { swipeable.current.close(); } }; return [ { key: 'action1', onPress: toggleUnreadStatus, color: isUnread ? colors.redButton : colors.greenButton, content: ( ), }, ]; }, [ colors, threadInfo, mostRecentNonLocalMessage, iconSize, updateUnreadStatus, dispatchActionPromise, ]); return ( {props.children} ); } export default SwipeableThread; diff --git a/native/components/swipeable.js b/native/components/swipeable.js index 8ac52a75d..257034782 100644 --- a/native/components/swipeable.js +++ b/native/components/swipeable.js @@ -1,132 +1,115 @@ // @flow -import PropTypes from 'prop-types'; import * as React from 'react'; import { Animated, View } from 'react-native'; import SwipeableComponent from 'react-native-gesture-handler/Swipeable'; +import { useSelector } from 'react-redux'; -import { connect } from 'lib/utils/redux-utils'; - -import type { AppState } from '../redux/redux-setup'; -import { - type Colors, - colorsPropType, - colorsSelector, - styleSelector, -} from '../themes/colors'; +import { type Colors, useColors, useStyles } from '../themes/colors'; import Button from './button.react'; -type Props = { +type BaseProps = {| +buttonWidth: number, +rightActions: $ReadOnlyArray<{| +key: string, +onPress: () => mixed, +color: ?string, +content: React.Node, |}>, +onSwipeableRightWillOpen?: () => void, +innerRef: {| current: ?SwipeableComponent, |}, +children?: React.Node, - // Redux state +|}; +type Props = {| + ...BaseProps, +windowWidth: number, +colors: Colors, - +styles: typeof styles, - ... -}; + +styles: typeof unboundStyles, +|}; class Swipeable extends React.PureComponent { - static propTypes = { - buttonWidth: PropTypes.number.isRequired, - rightActions: PropTypes.arrayOf( - PropTypes.exact({ - key: PropTypes.string.isRequired, - onPress: PropTypes.func.isRequired, - color: PropTypes.string, - content: PropTypes.node.isRequired, - }), - ), - onSwipeableRightWillOpen: PropTypes.func, - innerRef: PropTypes.exact({ - current: PropTypes.instanceOf(SwipeableComponent), - }), - children: PropTypes.node, - windowWidth: PropTypes.number.isRequired, - colors: colorsPropType.isRequired, - styles: PropTypes.objectOf(PropTypes.object).isRequired, - }; - static defaultProps = { rightActions: [], }; renderRightActions = (progress) => { const actions = this.props.rightActions.map( ({ key, content, color, onPress }, i) => { const translation = progress.interpolate({ inputRange: [0, 1], outputRange: [ (this.props.rightActions.length - i) * this.props.buttonWidth, 0, ], }); return ( ); }, ); return {actions}; }; render() { return ( {this.props.children} ); } } -const styles = { +const unboundStyles = { action: { height: '100%', alignItems: 'center', justifyContent: 'center', }, actionsContainer: { flexDirection: 'row', }, }; -const stylesSelector = styleSelector(styles); -export default connect((state: AppState) => ({ - windowWidth: state.dimensions.width, - colors: colorsSelector(state), - styles: stylesSelector(state), -}))(Swipeable); +export default React.memo(function ConnectedSwipeable( + props: BaseProps, +) { + const styles = useStyles(unboundStyles); + const windowWidth = useSelector((state) => state.dimensions.width); + const colors = useColors(); + + return ( + + ); +});