You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import React, { Component } from 'react';
  2. import { Text, View, ViewStyle } from 'react-native';
  3. import { connect } from 'react-redux';
  4. import { IReduxState } from '../../../app/types';
  5. import Avatar from '../../../base/avatar/components/Avatar';
  6. import { translate } from '../../../base/i18n/functions';
  7. import Linkify from '../../../base/react/components/native/Linkify';
  8. import { isGifMessage } from '../../../gifs/functions.native';
  9. import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants';
  10. import {
  11. getFormattedTimestamp,
  12. getMessageText,
  13. getPrivateNoticeMessage,
  14. replaceNonUnicodeEmojis
  15. } from '../../functions';
  16. import { IChatMessageProps } from '../../types';
  17. import GifMessage from './GifMessage';
  18. import PrivateMessageButton from './PrivateMessageButton';
  19. import styles from './styles';
  20. /**
  21. * Renders a single chat message.
  22. */
  23. class ChatMessage extends Component<IChatMessageProps> {
  24. /**
  25. * Implements {@code Component#render}.
  26. *
  27. * @inheritdoc
  28. */
  29. render() {
  30. const { message, knocking } = this.props;
  31. const localMessage = message.messageType === MESSAGE_TYPE_LOCAL;
  32. const { privateMessage, lobbyChat } = message;
  33. // Style arrays that need to be updated in various scenarios, such as
  34. // error messages or others.
  35. const detailsWrapperStyle: ViewStyle[] = [
  36. styles.detailsWrapper as ViewStyle
  37. ];
  38. const messageBubbleStyle: ViewStyle[] = [
  39. styles.messageBubble as ViewStyle
  40. ];
  41. if (localMessage) {
  42. // This is a message sent by the local participant.
  43. // The wrapper needs to be aligned to the right.
  44. detailsWrapperStyle.push(styles.ownMessageDetailsWrapper as ViewStyle);
  45. // The bubble needs some additional styling
  46. messageBubbleStyle.push(styles.localMessageBubble);
  47. } else if (message.messageType === MESSAGE_TYPE_ERROR) {
  48. // This is a system message.
  49. // The bubble needs some additional styling
  50. messageBubbleStyle.push(styles.systemMessageBubble);
  51. } else {
  52. // This is a remote message sent by a remote participant.
  53. // The bubble needs some additional styling
  54. messageBubbleStyle.push(styles.remoteMessageBubble);
  55. }
  56. if (privateMessage) {
  57. messageBubbleStyle.push(styles.privateMessageBubble);
  58. }
  59. if (lobbyChat && !knocking) {
  60. messageBubbleStyle.push(styles.lobbyMessageBubble);
  61. }
  62. const messageText = replaceNonUnicodeEmojis(getMessageText(this.props.message));
  63. return (
  64. <View style = { styles.messageWrapper as ViewStyle } >
  65. { this._renderAvatar() }
  66. <View style = { detailsWrapperStyle }>
  67. <View style = { messageBubbleStyle }>
  68. <View style = { styles.textWrapper as ViewStyle } >
  69. { this._renderDisplayName() }
  70. { isGifMessage(messageText)
  71. ? <GifMessage message = { messageText } />
  72. : (
  73. <Linkify
  74. linkStyle = { styles.chatLink }
  75. style = { styles.chatMessage }>
  76. { messageText }
  77. </Linkify>
  78. )}
  79. { this._renderPrivateNotice() }
  80. </View>
  81. { this._renderPrivateReplyButton() }
  82. </View>
  83. { this._renderTimestamp() }
  84. </View>
  85. </View>
  86. );
  87. }
  88. /**
  89. * Renders the avatar of the sender.
  90. *
  91. * @returns {React$Element<*>}
  92. */
  93. _renderAvatar() {
  94. const { message } = this.props;
  95. return (
  96. <View style = { styles.avatarWrapper }>
  97. { this.props.showAvatar && <Avatar
  98. displayName = { message.displayName }
  99. participantId = { message.id }
  100. size = { styles.avatarWrapper.width } />
  101. }
  102. </View>
  103. );
  104. }
  105. /**
  106. * Renders the display name of the sender if necessary.
  107. *
  108. * @returns {React$Element<*> | null}
  109. */
  110. _renderDisplayName() {
  111. const { message, showDisplayName } = this.props;
  112. if (!showDisplayName) {
  113. return null;
  114. }
  115. return (
  116. <Text style = { styles.senderDisplayName }>
  117. { message.displayName }
  118. </Text>
  119. );
  120. }
  121. /**
  122. * Renders the message privacy notice, if necessary.
  123. *
  124. * @returns {React$Element<*> | null}
  125. */
  126. _renderPrivateNotice() {
  127. const { message, knocking } = this.props;
  128. if (!(message.privateMessage || (message.lobbyChat && !knocking))) {
  129. return null;
  130. }
  131. return (
  132. <Text style = { message.lobbyChat ? styles.lobbyMsgNotice : styles.privateNotice }>
  133. { getPrivateNoticeMessage(this.props.message) }
  134. </Text>
  135. );
  136. }
  137. /**
  138. * Renders the private reply button, if necessary.
  139. *
  140. * @returns {React$Element<*> | null}
  141. */
  142. _renderPrivateReplyButton() {
  143. const { message, knocking } = this.props;
  144. const { messageType, privateMessage, lobbyChat } = message;
  145. if (!(privateMessage || lobbyChat) || messageType === MESSAGE_TYPE_LOCAL || knocking) {
  146. return null;
  147. }
  148. return (
  149. <View style = { styles.replyContainer as ViewStyle }>
  150. <PrivateMessageButton
  151. isLobbyMessage = { lobbyChat }
  152. participantID = { message.id }
  153. reply = { true }
  154. showLabel = { false }
  155. toggledStyles = { styles.replyStyles } />
  156. </View>
  157. );
  158. }
  159. /**
  160. * Renders the time at which the message was sent, if necessary.
  161. *
  162. * @returns {React$Element<*> | null}
  163. */
  164. _renderTimestamp() {
  165. if (!this.props.showTimestamp) {
  166. return null;
  167. }
  168. return (
  169. <Text style = { styles.timeText }>
  170. { getFormattedTimestamp(this.props.message) }
  171. </Text>
  172. );
  173. }
  174. }
  175. /**
  176. * Maps part of the redux state to the props of this component.
  177. *
  178. * @param {Object} state - The Redux state.
  179. * @returns {IProps}
  180. */
  181. function _mapStateToProps(state: IReduxState) {
  182. return {
  183. knocking: state['features/lobby'].knocking
  184. };
  185. }
  186. export default translate(connect(_mapStateToProps)(ChatMessage));