import React from 'react'; import PropTypes from 'prop-types'; import { KeyboardUtils } from 'react-native-keyboard-input'; import Message from './Message'; import debounce from '../../utils/debounce'; import { SYSTEM_MESSAGES, getCustomEmoji, getMessageTranslation } from './utils'; import messagesStatus from '../../constants/messagesStatus'; export default class MessageContainer extends React.Component { static propTypes = { item: PropTypes.object.isRequired, user: PropTypes.shape({ id: PropTypes.string.isRequired, username: PropTypes.string.isRequired, token: PropTypes.string.isRequired }), timeFormat: PropTypes.string, customThreadTimeFormat: PropTypes.string, style: PropTypes.any, archived: PropTypes.bool, broadcast: PropTypes.bool, previousItem: PropTypes.object, _updatedAt: PropTypes.instanceOf(Date), baseUrl: PropTypes.string, Message_GroupingPeriod: PropTypes.number, isReadReceiptEnabled: PropTypes.bool, useRealName: PropTypes.bool, useMarkdown: PropTypes.bool, autoTranslateRoom: PropTypes.bool, autoTranslateLanguage: PropTypes.string, status: PropTypes.number, onLongPress: PropTypes.func, onReactionPress: PropTypes.func, onDiscussionPress: PropTypes.func, onThreadPress: PropTypes.func, errorActionsShow: PropTypes.func, replyBroadcast: PropTypes.func, toggleReactionPicker: PropTypes.func, fetchThreadName: PropTypes.func, onOpenFileModal: PropTypes.func, onReactionLongPress: PropTypes.func, navToRoomInfo: PropTypes.func } static defaultProps = { onLongPress: () => {}, _updatedAt: new Date(), archived: false, broadcast: false } shouldComponentUpdate(nextProps) { const { status, item, _updatedAt, autoTranslateRoom } = this.props; if (status !== nextProps.status) { return true; } if (autoTranslateRoom !== nextProps.autoTranslateRoom) { return true; } if (item.tmsg !== nextProps.item.tmsg) { return true; } if (item.unread !== nextProps.item.unread) { return true; } return _updatedAt.toISOString() !== nextProps._updatedAt.toISOString(); } onPress = debounce(() => { const { item } = this.props; KeyboardUtils.dismiss(); if ((item.tlm || item.tmid)) { this.onThreadPress(); } }, 300, true); onLongPress = () => { const { archived, onLongPress } = this.props; if (this.isInfo || this.hasError || archived) { return; } if (onLongPress) { onLongPress(this.parseMessage()); } } onErrorPress = () => { const { errorActionsShow } = this.props; if (errorActionsShow) { errorActionsShow(this.parseMessage()); } } onReactionPress = (emoji) => { const { onReactionPress, item } = this.props; if (onReactionPress) { onReactionPress(emoji, item._id); } } onReactionLongPress = () => { const { onReactionLongPress, item } = this.props; if (onReactionLongPress) { onReactionLongPress(item); } } onDiscussionPress = () => { const { onDiscussionPress, item } = this.props; if (onDiscussionPress) { onDiscussionPress(item); } } onThreadPress = () => { const { onThreadPress, item } = this.props; if (onThreadPress) { onThreadPress(item); } } get isHeader() { const { item, previousItem, broadcast, Message_GroupingPeriod } = this.props; if (this.hasError || (previousItem && previousItem.status === messagesStatus.ERROR)) { return true; } if (previousItem && ( (previousItem.ts.toDateString() === item.ts.toDateString()) && (previousItem.u.username === item.u.username) && !(previousItem.groupable === false || item.groupable === false || broadcast === true) && (item.ts - previousItem.ts < Message_GroupingPeriod * 1000) && (previousItem.tmid === item.tmid) )) { return false; } return true; } get isThreadReply() { const { item, previousItem } = this.props; if (previousItem && item.tmid && (previousItem.tmid !== item.tmid) && (previousItem._id !== item.tmid)) { return true; } return false; } get isThreadSequential() { const { item, previousItem } = this.props; if (previousItem && item.tmid && ((previousItem.tmid === item.tmid) || (previousItem._id === item.tmid))) { return true; } return false; } get isInfo() { const { item } = this.props; return SYSTEM_MESSAGES.includes(item.t); } get isTemp() { const { item } = this.props; return item.status === messagesStatus.TEMP || item.status === messagesStatus.ERROR; } get hasError() { const { item } = this.props; return item.status === messagesStatus.ERROR; } parseMessage = () => { const { item } = this.props; return JSON.parse(JSON.stringify(item)); } toggleReactionPicker = () => { const { toggleReactionPicker } = this.props; if (toggleReactionPicker) { toggleReactionPicker(this.parseMessage()); } } replyBroadcast = () => { const { replyBroadcast } = this.props; if (replyBroadcast) { replyBroadcast(this.parseMessage()); } } render() { const { item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown, isReadReceiptEnabled, autoTranslateRoom, autoTranslateLanguage, navToRoomInfo } = this.props; const { _id, msg, ts, attachments, urls, reactions, t, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread, autoTranslate: autoTranslateMessage } = item; let message = msg; // "autoTranslateRoom" and "autoTranslateLanguage" are properties from the subscription // "autoTranslateMessage" is a toggle between "View Original" and "Translate" state if (autoTranslateRoom && autoTranslateMessage) { message = getMessageTranslation(item, autoTranslateLanguage) || message; } return ( ); } }