ref(chat): create web MessageContainer component
This commit is contained in:
parent
e66b596a0d
commit
0e8b0a9c5c
|
@ -12,8 +12,8 @@ import AbstractChat, {
|
||||||
type Props
|
type Props
|
||||||
} from '../AbstractChat';
|
} from '../AbstractChat';
|
||||||
import ChatInput from './ChatInput';
|
import ChatInput from './ChatInput';
|
||||||
import ChatMessageGroup from './ChatMessageGroup';
|
|
||||||
import DisplayNameForm from './DisplayNameForm';
|
import DisplayNameForm from './DisplayNameForm';
|
||||||
|
import MessageContainer from './MessageContainer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* React Component for holding the chat feature in a side panel that slides in
|
* 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;
|
_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.
|
* Initializes a new {@code Chat} instance.
|
||||||
*
|
*
|
||||||
|
@ -43,32 +37,9 @@ class Chat extends AbstractChat<Props> {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._isExited = true;
|
this._isExited = true;
|
||||||
this._messagesListEnd = null;
|
|
||||||
|
|
||||||
// Bind event handlers so they are only bound once for every instance.
|
// Bind event handlers so they are only bound once for every instance.
|
||||||
this._renderPanelContent = this._renderPanelContent.bind(this);
|
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}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
_renderChat() {
|
_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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<div id = 'chatconversation'>
|
<MessageContainer messages = { this.props._messages } />
|
||||||
{ messages }
|
|
||||||
</div>
|
|
||||||
<ChatInput />
|
<ChatInput />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -210,33 +162,6 @@ class Chat extends AbstractChat<Props> {
|
||||||
</div>
|
</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));
|
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