import React, { Component } from 'react'; import { Text, View, ViewStyle } from 'react-native'; import { connect } from 'react-redux'; import { IReduxState } from '../../../app/types'; import Avatar from '../../../base/avatar/components/Avatar'; import { translate } from '../../../base/i18n/functions'; import Linkify from '../../../base/react/components/native/Linkify'; import { isGifEnabled, isGifMessage } from '../../../gifs/functions.native'; import { CHAR_LIMIT, MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants'; import { getCanReplyToMessage, getFormattedTimestamp, getMessageText, getPrivateNoticeMessage, replaceNonUnicodeEmojis } from '../../functions'; import { IChatMessageProps } from '../../types'; import GifMessage from './GifMessage'; import PrivateMessageButton from './PrivateMessageButton'; import styles from './styles'; /** * Renders a single chat message. */ class ChatMessage extends Component { /** * Implements {@code Component#render}. * * @inheritdoc */ override render() { const { gifEnabled, message, knocking } = this.props; const localMessage = message.messageType === MESSAGE_TYPE_LOCAL; const { privateMessage, lobbyChat } = message; // Style arrays that need to be updated in various scenarios, such as // error messages or others. const detailsWrapperStyle: ViewStyle[] = [ styles.detailsWrapper as ViewStyle ]; const messageBubbleStyle: ViewStyle[] = [ styles.messageBubble as ViewStyle ]; if (localMessage) { // This is a message sent by the local participant. // The wrapper needs to be aligned to the right. detailsWrapperStyle.push(styles.ownMessageDetailsWrapper as ViewStyle); // The bubble needs some additional styling messageBubbleStyle.push(styles.localMessageBubble); } else if (message.messageType === MESSAGE_TYPE_ERROR) { // This is a system message. // The bubble needs some additional styling messageBubbleStyle.push(styles.systemMessageBubble); } else { // This is a remote message sent by a remote participant. // The bubble needs some additional styling messageBubbleStyle.push(styles.remoteMessageBubble); } if (privateMessage) { messageBubbleStyle.push(styles.privateMessageBubble); } if (lobbyChat && !knocking) { messageBubbleStyle.push(styles.lobbyMessageBubble); } const messageText = getMessageText(this.props.message); return ( { this._renderAvatar() } { this._renderDisplayName() } { gifEnabled && isGifMessage(messageText) ? : this._renderMessageTextComponent(messageText) } { this._renderPrivateNotice() } { this._renderPrivateReplyButton() } { this._renderTimestamp() } ); } /** * Renders the avatar of the sender. * * @returns {React.ReactElement<*>} */ _renderAvatar() { const { message } = this.props; return ( { this.props.showAvatar && } ); } /** * Renders the display name of the sender if necessary. * * @returns {React.ReactElement<*> | null} */ _renderDisplayName() { const { message, showDisplayName, t } = this.props; if (!showDisplayName) { return null; } const { displayName, isFromVisitor } = message; return ( { `${displayName}${isFromVisitor ? ` ${t('visitors.chatIndicator')}` : ''}` } ); } /** * Renders the message text based on number of characters. * * @param {string} messageText - The message text. * @returns {React.ReactElement<*>} */ _renderMessageTextComponent(messageText: string) { if (messageText.length >= CHAR_LIMIT) { return ( { messageText } ); } return ( { replaceNonUnicodeEmojis(messageText) } ); } /** * Renders the message privacy notice, if necessary. * * @returns {React.ReactElement<*> | null} */ _renderPrivateNotice() { const { message, knocking } = this.props; if (!(message.privateMessage || (message.lobbyChat && !knocking))) { return null; } return ( { getPrivateNoticeMessage(this.props.message) } ); } /** * Renders the private reply button, if necessary. * * @returns {React.ReactElement<*> | null} */ _renderPrivateReplyButton() { const { message, canReply } = this.props; const { lobbyChat } = message; if (!canReply) { return null; } return ( ); } /** * Renders the time at which the message was sent, if necessary. * * @returns {React.ReactElement<*> | null} */ _renderTimestamp() { if (!this.props.showTimestamp) { return null; } return ( { getFormattedTimestamp(this.props.message) } ); } } /** * Maps part of the redux state to the props of this component. * * @param {Object} state - The Redux state. * @param {IChatMessageProps} message - Message object. * @returns {IProps} */ function _mapStateToProps(state: IReduxState, { message }: IChatMessageProps) { return { canReply: getCanReplyToMessage(state, message), gifEnabled: isGifEnabled(state), knocking: state['features/lobby'].knocking }; } export default translate(connect(_mapStateToProps)(ChatMessage));