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.

Chat.js 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // @flow
  2. import React from 'react';
  3. import Transition from 'react-transition-group/Transition';
  4. import { translate } from '../../../base/i18n';
  5. import { connect } from '../../../base/redux';
  6. import AbstractChat, {
  7. _mapDispatchToProps,
  8. _mapStateToProps,
  9. type Props
  10. } from '../AbstractChat';
  11. import ChatInput from './ChatInput';
  12. import DisplayNameForm from './DisplayNameForm';
  13. import MessageContainer from './MessageContainer';
  14. /**
  15. * React Component for holding the chat feature in a side panel that slides in
  16. * and out of view.
  17. */
  18. class Chat extends AbstractChat<Props> {
  19. /**
  20. * Whether or not the {@code Chat} component is off-screen, having finished
  21. * its hiding animation.
  22. */
  23. _isExited: boolean;
  24. /**
  25. * Initializes a new {@code Chat} instance.
  26. *
  27. * @param {Object} props - The read-only properties with which the new
  28. * instance is to be initialized.
  29. */
  30. constructor(props: Props) {
  31. super(props);
  32. this._isExited = true;
  33. // Bind event handlers so they are only bound once for every instance.
  34. this._renderPanelContent = this._renderPanelContent.bind(this);
  35. }
  36. /**
  37. * Implements React's {@link Component#render()}.
  38. *
  39. * @inheritdoc
  40. * @returns {ReactElement}
  41. */
  42. render() {
  43. return (
  44. <Transition
  45. in = { this.props._isOpen }
  46. timeout = { 500 }>
  47. { this._renderPanelContent }
  48. </Transition>
  49. );
  50. }
  51. /**
  52. * Iterates over all the messages and creates nested arrays which hold
  53. * consecutive messages sent be the same participant.
  54. *
  55. * @private
  56. * @returns {Array<Array<Object>>}
  57. */
  58. _getMessagesGroupedBySender() {
  59. const messagesCount = this.props._messages.length;
  60. const groups = [];
  61. let currentGrouping = [];
  62. let currentGroupParticipantId;
  63. for (let i = 0; i < messagesCount; i++) {
  64. const message = this.props._messages[i];
  65. if (message.id === currentGroupParticipantId) {
  66. currentGrouping.push(message);
  67. } else {
  68. groups.push(currentGrouping);
  69. currentGrouping = [ message ];
  70. currentGroupParticipantId = message.id;
  71. }
  72. }
  73. groups.push(currentGrouping);
  74. return groups;
  75. }
  76. /**
  77. * Returns a React Element for showing chat messages and a form to send new
  78. * chat messages.
  79. *
  80. * @private
  81. * @returns {ReactElement}
  82. */
  83. _renderChat() {
  84. return (
  85. <>
  86. <MessageContainer messages = { this.props._messages } />
  87. <ChatInput />
  88. </>
  89. );
  90. }
  91. /**
  92. * Instantiates a React Element to display at the top of {@code Chat} to
  93. * close {@code Chat}.
  94. *
  95. * @private
  96. * @returns {ReactElement}
  97. */
  98. _renderChatHeader() {
  99. return (
  100. <div className = 'chat-header'>
  101. <div
  102. className = 'chat-close'
  103. onClick = { this.props._onToggleChat }>X</div>
  104. </div>
  105. );
  106. }
  107. _renderPanelContent: (string) => React$Node | null;
  108. /**
  109. * Renders the contents of the chat panel, depending on the current
  110. * animation state provided by {@code Transition}.
  111. *
  112. * @param {string} state - The current display transition state of the
  113. * {@code Chat} component, as provided by {@code Transition}.
  114. * @private
  115. * @returns {ReactElement | null}
  116. */
  117. _renderPanelContent(state) {
  118. this._isExited = state === 'exited';
  119. const { _isOpen, _showNamePrompt } = this.props;
  120. const ComponentToRender = !_isOpen && state === 'exited'
  121. ? null
  122. : (
  123. <>
  124. { this._renderChatHeader() }
  125. { _showNamePrompt
  126. ? <DisplayNameForm /> : this._renderChat() }
  127. </>
  128. );
  129. let className = '';
  130. if (_isOpen) {
  131. className = 'slideInExt';
  132. } else if (this._isExited) {
  133. className = 'invisible';
  134. }
  135. return (
  136. <div
  137. className = { `sideToolbarContainer ${className}` }
  138. id = 'sideToolbarContainer'>
  139. { ComponentToRender }
  140. </div>
  141. );
  142. }
  143. }
  144. export default translate(connect(_mapStateToProps, _mapDispatchToProps)(Chat));