|
|
|
@ -4,16 +4,28 @@ import React from 'react'; |
|
|
|
|
import { Text, View } from 'react-native'; |
|
|
|
|
|
|
|
|
|
import { Avatar } from '../../../base/avatar'; |
|
|
|
|
import { ColorSchemeRegistry } from '../../../base/color-scheme'; |
|
|
|
|
import { translate } from '../../../base/i18n'; |
|
|
|
|
import { Linkify } from '../../../base/react'; |
|
|
|
|
import { connect } from '../../../base/redux'; |
|
|
|
|
import { type StyleType } from '../../../base/styles'; |
|
|
|
|
|
|
|
|
|
import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants'; |
|
|
|
|
import { replaceNonUnicodeEmojis } from '../../functions'; |
|
|
|
|
|
|
|
|
|
import AbstractChatMessage, { type Props } from '../AbstractChatMessage'; |
|
|
|
|
import AbstractChatMessage, { type Props as AbstractProps } from '../AbstractChatMessage'; |
|
|
|
|
import PrivateMessageButton from '../PrivateMessageButton'; |
|
|
|
|
|
|
|
|
|
import styles from './styles'; |
|
|
|
|
|
|
|
|
|
type Props = AbstractProps & { |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* The color-schemed stylesheet of the feature. |
|
|
|
|
*/ |
|
|
|
|
_styles: StyleType |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Renders a single chat message. |
|
|
|
|
*/ |
|
|
|
@ -24,55 +36,58 @@ class ChatMessage extends AbstractChatMessage<Props> { |
|
|
|
|
* @inheritdoc |
|
|
|
|
*/ |
|
|
|
|
render() { |
|
|
|
|
const { message } = this.props; |
|
|
|
|
const localMessage = message.messageType === 'local'; |
|
|
|
|
const { _styles, message } = this.props; |
|
|
|
|
const localMessage = message.messageType === MESSAGE_TYPE_LOCAL; |
|
|
|
|
const { privateMessage } = message; |
|
|
|
|
|
|
|
|
|
// Style arrays that need to be updated in various scenarios, such as
|
|
|
|
|
// error messages or others.
|
|
|
|
|
const detailsWrapperStyle = [ |
|
|
|
|
styles.detailsWrapper |
|
|
|
|
]; |
|
|
|
|
const textWrapperStyle = [ |
|
|
|
|
styles.textWrapper |
|
|
|
|
const messageBubbleStyle = [ |
|
|
|
|
styles.messageBubble |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
if (localMessage) { |
|
|
|
|
// This is a message sent by the local participant.
|
|
|
|
|
|
|
|
|
|
// The wrapper needs to be aligned to the right.
|
|
|
|
|
detailsWrapperStyle.push(styles.ownMessageDetailsWrapper); |
|
|
|
|
|
|
|
|
|
// The bubble needs to be differently styled.
|
|
|
|
|
textWrapperStyle.push(styles.ownTextWrapper); |
|
|
|
|
} else if (message.messageType === 'error') { |
|
|
|
|
// The bubble needs to be differently styled.
|
|
|
|
|
textWrapperStyle.push(styles.systemTextWrapper); |
|
|
|
|
// 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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<View style = { styles.messageWrapper } > |
|
|
|
|
{ this._renderAvatar() } |
|
|
|
|
<View style = { detailsWrapperStyle }> |
|
|
|
|
<View style = { styles.replyWrapper }> |
|
|
|
|
<View style = { textWrapperStyle } > |
|
|
|
|
{ |
|
|
|
|
this.props.showDisplayName |
|
|
|
|
&& this._renderDisplayName() |
|
|
|
|
} |
|
|
|
|
<View style = { messageBubbleStyle }> |
|
|
|
|
<View style = { styles.textWrapper } > |
|
|
|
|
{ this._renderDisplayName() } |
|
|
|
|
<Linkify linkStyle = { styles.chatLink }> |
|
|
|
|
{ replaceNonUnicodeEmojis(this._getMessageText()) } |
|
|
|
|
</Linkify> |
|
|
|
|
{ |
|
|
|
|
message.privateMessage |
|
|
|
|
&& this._renderPrivateNotice() |
|
|
|
|
} |
|
|
|
|
{ this._renderPrivateNotice() } |
|
|
|
|
</View> |
|
|
|
|
{ message.privateMessage && !localMessage |
|
|
|
|
&& <PrivateMessageButton |
|
|
|
|
participantID = { message.id } |
|
|
|
|
reply = { true } |
|
|
|
|
showLabel = { false } |
|
|
|
|
toggledStyles = { styles.replyStyles } /> } |
|
|
|
|
{ this._renderPrivateReplyButton() } |
|
|
|
|
</View> |
|
|
|
|
{ this.props.showTimestamp && this._renderTimestamp() } |
|
|
|
|
{ this._renderTimestamp() } |
|
|
|
|
</View> |
|
|
|
|
</View> |
|
|
|
|
); |
|
|
|
@ -104,37 +119,77 @@ class ChatMessage extends AbstractChatMessage<Props> { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Renders the display name of the sender. |
|
|
|
|
* Renders the display name of the sender if necessary. |
|
|
|
|
* |
|
|
|
|
* @returns {React$Element<*>} |
|
|
|
|
* @returns {React$Element<*> | null} |
|
|
|
|
*/ |
|
|
|
|
_renderDisplayName() { |
|
|
|
|
const { _styles, message, showDisplayName } = this.props; |
|
|
|
|
|
|
|
|
|
if (!showDisplayName) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<Text style = { styles.displayName }> |
|
|
|
|
{ this.props.message.displayName } |
|
|
|
|
<Text style = { _styles.displayName }> |
|
|
|
|
{ message.displayName } |
|
|
|
|
</Text> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Renders the message privacy notice. |
|
|
|
|
* Renders the message privacy notice, if necessary. |
|
|
|
|
* |
|
|
|
|
* @returns {React$Element<*>} |
|
|
|
|
* @returns {React$Element<*> | null} |
|
|
|
|
*/ |
|
|
|
|
_renderPrivateNotice() { |
|
|
|
|
const { _styles, message } = this.props; |
|
|
|
|
|
|
|
|
|
if (!message.privateMessage) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<Text style = { styles.privateNotice }> |
|
|
|
|
<Text style = { _styles.privateNotice }> |
|
|
|
|
{ this._getPrivateNoticeMessage() } |
|
|
|
|
</Text> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Renders the time at which the message was sent. |
|
|
|
|
* Renders the private reply button, if necessary. |
|
|
|
|
* |
|
|
|
|
* @returns {React$Element<*>} |
|
|
|
|
* @returns {React$Element<*> | null} |
|
|
|
|
*/ |
|
|
|
|
_renderPrivateReplyButton() { |
|
|
|
|
const { _styles, message } = this.props; |
|
|
|
|
const { messageType, privateMessage } = message; |
|
|
|
|
|
|
|
|
|
if (!privateMessage || messageType === MESSAGE_TYPE_LOCAL) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<View style = { _styles.replyContainer }> |
|
|
|
|
<PrivateMessageButton |
|
|
|
|
participantID = { message.id } |
|
|
|
|
reply = { true } |
|
|
|
|
showLabel = { false } |
|
|
|
|
toggledStyles = { _styles.replyStyles } /> |
|
|
|
|
</View> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Renders the time at which the message was sent, if necessary. |
|
|
|
|
* |
|
|
|
|
* @returns {React$Element<*> | null} |
|
|
|
|
*/ |
|
|
|
|
_renderTimestamp() { |
|
|
|
|
if (!this.props.showTimestamp) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<Text style = { styles.timeText }> |
|
|
|
|
{ this._getFormattedTimestamp() } |
|
|
|
@ -143,4 +198,16 @@ class ChatMessage extends AbstractChatMessage<Props> { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export default translate(ChatMessage); |
|
|
|
|
/** |
|
|
|
|
* Maps part of the redux state to the props of this component. |
|
|
|
|
* |
|
|
|
|
* @param {Object} state - The Redux state. |
|
|
|
|
* @returns {Props} |
|
|
|
|
*/ |
|
|
|
|
function _mapStateToProps(state) { |
|
|
|
|
return { |
|
|
|
|
_styles: ColorSchemeRegistry.get(state, 'Chat') |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export default translate(connect(_mapStateToProps)(ChatMessage)); |
|
|
|
|