import React from 'react'; import PropTypes from 'prop-types'; import { View, Text, Animated } from 'react-native'; import { RectButton, PanGestureHandler, State } from 'react-native-gesture-handler'; import Avatar from '../../containers/Avatar'; import I18n from '../../i18n'; import styles, { ROW_HEIGHT, ACTION_WIDTH, SMALL_SWIPE, LONG_SWIPE } from './styles'; import UnreadBadge from './UnreadBadge'; import TypeIcon from './TypeIcon'; import LastMessage from './LastMessage'; import { capitalize, formatDate } from '../../utils/room'; import { LeftActions, RightActions } from './Actions'; export { ROW_HEIGHT }; const attrs = ['name', 'unread', 'userMentions', 'showLastMessage', 'alert', 'type', 'width', 'isRead', 'favorite']; export default class RoomItem extends React.Component { static propTypes = { type: PropTypes.string.isRequired, name: PropTypes.string.isRequired, baseUrl: PropTypes.string.isRequired, showLastMessage: PropTypes.bool, _updatedAt: PropTypes.string, lastMessage: PropTypes.object, alert: PropTypes.bool, unread: PropTypes.number, userMentions: PropTypes.number, id: PropTypes.string, prid: PropTypes.string, onPress: PropTypes.func, userId: PropTypes.string, username: PropTypes.string, token: PropTypes.string, avatarSize: PropTypes.number, testID: PropTypes.string, width: PropTypes.number, favorite: PropTypes.bool, isRead: PropTypes.bool, rid: PropTypes.string, toggleFav: PropTypes.func, toggleRead: PropTypes.func, hideChannel: PropTypes.func } static defaultProps = { avatarSize: 48 } // Making jest happy: https://github.com/facebook/react-native/issues/22175 // eslint-disable-next-line no-useless-constructor constructor(props) { super(props); this.dragX = new Animated.Value(0); this.rowOffSet = new Animated.Value(0); this.transX = Animated.add( this.rowOffSet, this.dragX ); this.state = { rowState: 0 // 0: closed, 1: right opened, -1: left opened }; this._onGestureEvent = Animated.event( [{ nativeEvent: { translationX: this.dragX } }] ); this._value = 0; } shouldComponentUpdate(nextProps) { const { lastMessage, _updatedAt } = this.props; const oldlastMessage = lastMessage; const newLastmessage = nextProps.lastMessage; if (oldlastMessage && newLastmessage && oldlastMessage.ts !== newLastmessage.ts) { return true; } if (_updatedAt && nextProps._updatedAt && nextProps._updatedAt.toISOString() !== _updatedAt.toISOString()) { return true; } // eslint-disable-next-line react/destructuring-assignment return attrs.some(key => nextProps[key] !== this.props[key]); } _onHandlerStateChange = ({ nativeEvent }) => { if (nativeEvent.oldState === State.ACTIVE) { this._handleRelease(nativeEvent); } }; _handleRelease = (nativeEvent) => { const { translationX } = nativeEvent; const { rowState } = this.state; this._value = this._value + translationX; let toValue = 0; if (rowState === 0) { // if no option is opened if (translationX > 0 && translationX < LONG_SWIPE) { toValue = ACTION_WIDTH; // open left option if he swipe right but not enough to trigger action this.setState({ rowState: -1 }); } else if (translationX >= LONG_SWIPE) { toValue = 0; this.toggleRead(); } else if (translationX < 0 && translationX > -LONG_SWIPE) { toValue = -2 * ACTION_WIDTH; // open right option if he swipe left this.setState({ rowState: 1 }); } else if (translationX <= -LONG_SWIPE) { toValue = 0; this.setState({ rowState: 0 }); 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 }); this.toggleRead(); } 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 }); this.hideChannel(); } else { toValue = -2 * ACTION_WIDTH; } } this._animateRow(toValue); } _animateRow = (toValue) => { 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(); } } render() { const { unread, userMentions, name, _updatedAt, alert, testID, type, avatarSize, baseUrl, userId, username, token, id, prid, showLastMessage, lastMessage, isRead, width, favorite } = this.props; const date = formatDate(_updatedAt); let accessibilityLabel = name; if (unread === 1) { accessibilityLabel += `, ${ unread } ${ I18n.t('alert') }`; } else if (unread > 1) { accessibilityLabel += `, ${ unread } ${ I18n.t('alerts') }`; } if (userMentions > 0) { accessibilityLabel += `, ${ I18n.t('you_were_mentioned') }`; } if (date) { accessibilityLabel += `, ${ I18n.t('last_message') } ${ date }`; } return ( { name } {_updatedAt ? { capitalize(date) } : null} ); } }