ref(chat): create web MessageContainer component
This commit is contained in:
parent
e66b596a0d
commit
0e8b0a9c5c
|
@ -12,8 +12,8 @@ import AbstractChat, {
|
|||
type Props
|
||||
} from '../AbstractChat';
|
||||
import ChatInput from './ChatInput';
|
||||
import ChatMessageGroup from './ChatMessageGroup';
|
||||
import DisplayNameForm from './DisplayNameForm';
|
||||
import MessageContainer from './MessageContainer';
|
||||
|
||||
/**
|
||||
* React Component for holding the chat feature in a side panel that slides in
|
||||
|
@ -27,12 +27,6 @@ class Chat extends AbstractChat<Props> {
|
|||
*/
|
||||
_isExited: boolean;
|
||||
|
||||
/**
|
||||
* Reference to the HTML element at the end of the list of displayed chat
|
||||
* messages. Used for scrolling to the end of the chat messages.
|
||||
*/
|
||||
_messagesListEnd: ?HTMLElement;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code Chat} instance.
|
||||
*
|
||||
|
@ -43,32 +37,9 @@ class Chat extends AbstractChat<Props> {
|
|||
super(props);
|
||||
|
||||
this._isExited = true;
|
||||
this._messagesListEnd = null;
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._renderPanelContent = this._renderPanelContent.bind(this);
|
||||
this._setMessageListEndRef = this._setMessageListEndRef.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidMount()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidMount() {
|
||||
this._scrollMessagesToBottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates chat input focus.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props._messages !== prevProps._messages) {
|
||||
this._scrollMessagesToBottom();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,28 +97,9 @@ class Chat extends AbstractChat<Props> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderChat() {
|
||||
const groupedMessages = this._getMessagesGroupedBySender();
|
||||
|
||||
const messages = groupedMessages.map((group, index) => {
|
||||
const messageType = group[0] && group[0].messageType;
|
||||
|
||||
return (
|
||||
<ChatMessageGroup
|
||||
className = { messageType || 'remote' }
|
||||
key = { index }
|
||||
messages = { group } />
|
||||
);
|
||||
});
|
||||
|
||||
messages.push(<div
|
||||
key = 'end-marker'
|
||||
ref = { this._setMessageListEndRef } />);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id = 'chatconversation'>
|
||||
{ messages }
|
||||
</div>
|
||||
<MessageContainer messages = { this.props._messages } />
|
||||
<ChatInput />
|
||||
</>
|
||||
);
|
||||
|
@ -210,33 +162,6 @@ class Chat extends AbstractChat<Props> {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically scrolls the displayed chat messages down to the latest.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_scrollMessagesToBottom() {
|
||||
if (this._messagesListEnd) {
|
||||
this._messagesListEnd.scrollIntoView({
|
||||
behavior: this._isExited ? 'auto' : 'smooth'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_setMessageListEndRef: (?HTMLElement) => void;
|
||||
|
||||
/**
|
||||
* Sets a reference to the HTML element at the bottom of the message list.
|
||||
*
|
||||
* @param {Object} messageListEnd - The HTML element.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_setMessageListEndRef(messageListEnd: ?HTMLElement) {
|
||||
this._messagesListEnd = messageListEnd;
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(Chat));
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import ChatMessageGroup from './ChatMessageGroup';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The messages array to render.
|
||||
*/
|
||||
messages: Array<Object>
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays all received chat messages, grouped by sender.
|
||||
*
|
||||
* @extends PureComponent
|
||||
*/
|
||||
export default class MessageContainer extends PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
messages: []
|
||||
};
|
||||
|
||||
/**
|
||||
* Reference to the HTML element at the end of the list of displayed chat
|
||||
* messages. Used for scrolling to the end of the chat messages.
|
||||
*/
|
||||
_messagesListEndRef: Object;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code MessageContainer} instance.
|
||||
*
|
||||
* @param {Props} props - The React {@code Component} props to initialize
|
||||
* the new {@code MessageContainer} instance with.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._messagesListEndRef = React.createRef();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidMount()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidMount() {
|
||||
this._scrollMessagesToBottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates chat input focus.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidUpdate() {
|
||||
this._scrollMessagesToBottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@code Component#render}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const groupedMessages = this._getMessagesGroupedBySender();
|
||||
const messages = groupedMessages.map((group, index) => {
|
||||
const messageType = group[0] && group[0].messageType;
|
||||
|
||||
return (
|
||||
<ChatMessageGroup
|
||||
className = { messageType || 'remote' }
|
||||
key = { index }
|
||||
messages = { group } />
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div id = 'chatconversation'>
|
||||
{ messages }
|
||||
<div ref = { this._messagesListEndRef } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all the messages and creates nested arrays which hold
|
||||
* consecutive messages sent by the same participant.
|
||||
*
|
||||
* @private
|
||||
* @returns {Array<Array<Object>>}
|
||||
*/
|
||||
_getMessagesGroupedBySender() {
|
||||
const messagesCount = this.props.messages.length;
|
||||
const groups = [];
|
||||
let currentGrouping = [];
|
||||
let currentGroupParticipantId;
|
||||
|
||||
for (let i = 0; i < messagesCount; i++) {
|
||||
const message = this.props.messages[i];
|
||||
|
||||
if (message.id === currentGroupParticipantId) {
|
||||
currentGrouping.push(message);
|
||||
} else {
|
||||
groups.push(currentGrouping);
|
||||
|
||||
currentGrouping = [ message ];
|
||||
currentGroupParticipantId = message.id;
|
||||
}
|
||||
}
|
||||
|
||||
groups.push(currentGrouping);
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically scrolls the displayed chat messages down to the latest.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_scrollMessagesToBottom() {
|
||||
this._messagesListEndRef.current.scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue