diff --git a/react/features/chat/components/web/Chat.js b/react/features/chat/components/web/Chat.js index 1a69fec6a..7f9734b3d 100644 --- a/react/features/chat/components/web/Chat.js +++ b/react/features/chat/components/web/Chat.js @@ -47,6 +47,9 @@ class Chat extends AbstractChat { // Bind event handlers so they are only bound once for every instance. this._renderPanelContent = this._renderPanelContent.bind(this); + + // Bind event handlers so they are only bound once for every instance. + this._onChatInputResize = this._onChatInputResize.bind(this); } /** @@ -87,6 +90,19 @@ class Chat extends AbstractChat { ); } + _onChatInputResize: () => void; + + /** + * Callback invoked when {@code ChatInput} changes height. Preserves + * displaying the latest message if it is scrolled to. + * + * @private + * @returns {void} + */ + _onChatInputResize() { + this._messageContainerRef.current.maybeUpdateBottomScroll(); + } + /** * Returns a React Element for showing chat messages and a form to send new * chat messages. @@ -100,7 +116,7 @@ class Chat extends AbstractChat { - + ); } diff --git a/react/features/chat/components/web/ChatInput.js b/react/features/chat/components/web/ChatInput.js index 997641d0d..9b8b7e2e9 100644 --- a/react/features/chat/components/web/ChatInput.js +++ b/react/features/chat/components/web/ChatInput.js @@ -22,6 +22,12 @@ type Props = { */ dispatch: Dispatch, + /** + * Optional callback to invoke when the chat textarea has auto-resized to + * fit overflowing text. + */ + onResize: ?Function, + /** * Invoked to obtain translated strings. */ @@ -120,6 +126,7 @@ class ChatInput extends Component { inputRef = { this._setTextAreaRef } maxRows = { 5 } onChange = { this._onMessageChange } + onHeightChange = { this.props.onResize } onKeyDown = { this._onDetectSubmit } placeholder = { this.props.t('chat.messagebox') } value = { this.state.message } /> diff --git a/react/features/chat/components/web/MessageContainer.js b/react/features/chat/components/web/MessageContainer.js index 759cc7e39..fef9e43c4 100644 --- a/react/features/chat/components/web/MessageContainer.js +++ b/react/features/chat/components/web/MessageContainer.js @@ -13,12 +13,25 @@ import ChatMessageGroup from './ChatMessageGroup'; * @extends AbstractMessageContainer */ export default class MessageContainer extends AbstractMessageContainer { + /** + * Whether or not chat has been scrolled to the bottom of the screen. Used + * to determine if chat should be scrolled automatically to the bottom when + * the {@code ChatInput} resizes. + */ + _isScrolledToBottom: 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. */ _messagesListEndRef: Object; + /** + * A React ref to the HTML element containing all {@code ChatMessageGroup} + * instances. + */ + _messageListRef: Object; + /** * Initializes a new {@code MessageContainer} instance. * @@ -28,7 +41,12 @@ export default class MessageContainer extends AbstractMessageContainer { constructor(props: Props) { super(props); + this._isScrolledToBottom = true; + + this._messageListRef = React.createRef(); this._messagesListEndRef = React.createRef(); + + this._onChatScroll = this._onChatScroll.bind(this); } /** @@ -50,13 +68,29 @@ export default class MessageContainer extends AbstractMessageContainer { }); return ( -
+
{ messages }
); } + /** + * Scrolls to the bottom again if the instance had previously been scrolled + * to the bottom. This method is used when a resize has occurred below the + * instance and bottom scroll needs to be maintained. + * + * @returns {void} + */ + maybeUpdateBottomScroll() { + if (this._isScrolledToBottom) { + this.scrollToBottom(false); + } + } + /** * Automatically scrolls the displayed chat messages down to the latest. * @@ -71,4 +105,19 @@ export default class MessageContainer extends AbstractMessageContainer { } _getMessagesGroupedBySender: () => Array>; + + _onChatScroll: () => void; + + /** + * Callback invoked to listen to the current scroll location. + * + * @private + * @returns {void} + */ + _onChatScroll() { + const element = this._messageListRef.current; + + this._isScrolledToBottom + = element.scrollHeight - element.scrollTop === element.clientHeight; + } }