fix(chat): maintain bottom scroll on input resize
This commit is contained in:
parent
dfe5fbb702
commit
d86b60ea72
|
@ -47,6 +47,9 @@ class Chat extends AbstractChat<Props> {
|
|||
|
||||
// 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<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
_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<Props> {
|
|||
<MessageContainer
|
||||
messages = { this.props._messages }
|
||||
ref = { this._messageContainerRef } />
|
||||
<ChatInput />
|
||||
<ChatInput onResize = { this._onChatInputResize } />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,12 @@ type Props = {
|
|||
*/
|
||||
dispatch: Dispatch<any>,
|
||||
|
||||
/**
|
||||
* 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<Props, State> {
|
|||
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 } />
|
||||
|
|
|
@ -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 (
|
||||
<div id = 'chatconversation'>
|
||||
<div
|
||||
id = 'chatconversation'
|
||||
onScroll = { this._onChatScroll }
|
||||
ref = { this._messageListRef }>
|
||||
{ messages }
|
||||
<div ref = { this._messagesListEndRef } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<Array<Object>>;
|
||||
|
||||
_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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue