Files
jitsi-meet/react/features/chat/components/ChatMessage.web.js
Pratik Shah b7b43e8d9c feat(chat): convert to use React
- Change "features/chat" to support listening for new chat messages
  and storing them, removing that logic from conference.js.
- Combine chat.scss and side_toolbar_container.css, and remove unused
  scss files. Chat is the only side panel so the two concepts have
  been merged.
- Remove direct access to the chat feature from non-react and non-redux
  flows.
- Modify the i18n translate function to take in an options object.
  By default the option "wait" is set to true, but that causes
  components to mount after the parent has been notified of
  an update, which means autoscrolling down to the latest rendered
  messages does not work. With "wait" set to false, the children
  will mount and then the parent will trigger componentDidUpdate.
- Create react components for chat. Chat is the side panel
  plus the entiren chat feature. ChatInput is a child of Chat and
  is used for composing messages. ChatMessage displays one message
  and extends PureComponent to limit re-renders.
- Fix a bug where the toolbar was not showing automatically when
  chat is closed and a new message is received.
- Import react-transition-group to time the animation of the
  side panel showing/hiding and unmounting the Chat component.
  This gets around the issue of having to control autofocus if the
  component were always mounted and visibility toggled, but
  introduces not being able to store previous scroll state
  (without additional work or re-work).
2018-09-26 14:48:10 -05:00

115 lines
3.0 KiB
JavaScript

// @flow
import React, { PureComponent } from 'react';
import { translate } from '../../base/i18n';
import { processReplacements } from '../replacement';
/**
* The type of the React {@code Component} props of {@link Chat}.
*/
type Props = {
/**
* The redux representation of a chat message.
*/
message: Object,
/**
* Invoked to receive translated strings.
*/
t: Function
};
/**
* Displays as passed in chat message.
*
* @extends Component
*/
class ChatMessage extends PureComponent<Props> {
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { message } = this.props;
let messageTypeClassname = '';
let messagetoDisplay = message.message;
switch (message.messageType) {
case 'local':
messageTypeClassname = 'localuser';
break;
case 'error':
messageTypeClassname = 'error';
messagetoDisplay = this.props.t('chat.error', {
error: message.error,
originalText: messagetoDisplay
});
break;
default:
messageTypeClassname = 'remoteuser';
}
// replace links and smileys
// Strophe already escapes special symbols on sending,
// so we escape here only tags to avoid double &amp;
const escMessage = messagetoDisplay.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '<br/>');
const messageWithHTML = processReplacements(escMessage);
return (
<div className = { `chatmessage ${messageTypeClassname}` }>
<img
className = 'chatArrow'
src = '../../../../images/chatArrow.svg' />
<div className = 'display-name'>
{ message.displayName }
</div>
<div className = { 'timestamp' }>
{ ChatMessage.formatTimestamp(message.timestamp) }
</div>
<div
className = 'usermessage'
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML = {{ __html: messageWithHTML }} />
</div>
);
}
/**
* Returns a timestamp formatted for display.
*
* @param {number} timestamp - The timestamp for the chat message.
* @private
* @returns {string}
*/
static formatTimestamp(timestamp) {
const now = new Date(timestamp);
let hour = now.getHours();
let minute = now.getMinutes();
let second = now.getSeconds();
if (hour.toString().length === 1) {
hour = `0${hour}`;
}
if (minute.toString().length === 1) {
minute = `0${minute}`;
}
if (second.toString().length === 1) {
second = `0${second}`;
}
return `${hour}:${minute}:${second}`;
}
}
export default translate(ChatMessage, { wait: false });