jump to bottom of list when the DOM observes that a new message node has been added (#369)
This commit is contained in:
@@ -33,12 +33,17 @@ export default class Chat extends Component {
|
|||||||
this.submitChat = this.submitChat.bind(this);
|
this.submitChat = this.submitChat.bind(this);
|
||||||
this.scrollToBottom = this.scrollToBottom.bind(this);
|
this.scrollToBottom = this.scrollToBottom.bind(this);
|
||||||
this.handleWindowResize = debounce(this.handleWindowResize.bind(this), 500);
|
this.handleWindowResize = debounce(this.handleWindowResize.bind(this), 500);
|
||||||
|
|
||||||
|
this.messageListCallback = this.messageListCallback.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.setupWebSocketCallbacks();
|
this.setupWebSocketCallbacks();
|
||||||
this.getChatHistory();
|
this.getChatHistory();
|
||||||
window.addEventListener('resize', this.handleWindowResize);
|
window.addEventListener('resize', this.handleWindowResize);
|
||||||
|
|
||||||
|
this.messageListObserver = new MutationObserver(this.messageListCallback);
|
||||||
|
this.messageListObserver.observe(this.scrollableMessagesContainer.current, { childList: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
@@ -52,15 +57,15 @@ export default class Chat extends Component {
|
|||||||
if (prevName !== username) {
|
if (prevName !== username) {
|
||||||
this.sendUsernameChange(prevName, username);
|
this.sendUsernameChange(prevName, username);
|
||||||
}
|
}
|
||||||
|
|
||||||
// scroll to bottom of messages list when new ones come in
|
// scroll to bottom of messages list when new ones come in
|
||||||
if (messages.length > prevMessages.length) {
|
if (messages.length > prevMessages.length) {
|
||||||
if (!prevMessages.length || this.checkShouldScroll()) {
|
this.newMessagesReceived = true;
|
||||||
this.scrollToBottom();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('resize', this.handleWindowResize);
|
window.removeEventListener('resize', this.handleWindowResize);
|
||||||
|
this.messageListObserver.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
setupWebSocketCallbacks() {
|
setupWebSocketCallbacks() {
|
||||||
@@ -139,7 +144,6 @@ export default class Chat extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
submitChat(content) {
|
submitChat(content) {
|
||||||
if (!content) {
|
if (!content) {
|
||||||
return;
|
return;
|
||||||
@@ -177,14 +181,28 @@ export default class Chat extends Component {
|
|||||||
checkShouldScroll() {
|
checkShouldScroll() {
|
||||||
const { scrollTop, scrollHeight, clientHeight } = this.scrollableMessagesContainer.current;
|
const { scrollTop, scrollHeight, clientHeight } = this.scrollableMessagesContainer.current;
|
||||||
const fullyScrolled = scrollHeight - clientHeight;
|
const fullyScrolled = scrollHeight - clientHeight;
|
||||||
|
const shoudlScroll = scrollHeight >= clientHeight && fullyScrolled - scrollTop < MESSAGE_JUMPTOBOTTOM_BUFFER;
|
||||||
return scrollHeight >= clientHeight && fullyScrolled - scrollTop < MESSAGE_JUMPTOBOTTOM_BUFFER;
|
return shoudlScroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWindowResize() {
|
handleWindowResize() {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the messages list grows in number of child message nodes due to new messages received, scroll to bottom.
|
||||||
|
messageListCallback(mutations) {
|
||||||
|
const numMutations = mutations.length;
|
||||||
|
if (numMutations) {
|
||||||
|
const item = mutations[numMutations - 1];
|
||||||
|
if (item.type === 'childList' && item.addedNodes.length) {
|
||||||
|
if (this.newMessagesReceived || this.checkShouldScroll()) {
|
||||||
|
this.scrollToBottom();
|
||||||
|
this.newMessagesReceived = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render(props, state) {
|
render(props, state) {
|
||||||
const { username, messagesOnly, chatInputEnabled } = props;
|
const { username, messagesOnly, chatInputEnabled } = props;
|
||||||
const { messages, chatUserNames, webSocketConnected } = state;
|
const { messages, chatUserNames, webSocketConnected } = state;
|
||||||
|
|||||||
Reference in New Issue
Block a user