diff --git a/app/lib/components/Chat/MessageList.jsx b/app/lib/components/Chat/MessageList.jsx index 3f59d1a..e80e17b 100644 --- a/app/lib/components/Chat/MessageList.jsx +++ b/app/lib/components/Chat/MessageList.jsx @@ -1,14 +1,9 @@ import React, { Component } from 'react'; +import { compose } from 'redux'; import PropTypes from 'prop-types'; import marked from 'marked'; import { connect } from 'react-redux'; - -const scrollToBottom = () => -{ - const messagesDiv = document.getElementById('messages'); - - messagesDiv.scrollTop = messagesDiv.scrollHeight; -}; +import scrollToBottom from './scrollToBottom'; const linkRenderer = new marked.Renderer(); @@ -22,16 +17,6 @@ linkRenderer.link = (href, title, text) => class MessageList extends Component { - componentDidMount() - { - scrollToBottom(); - } - - componentDidUpdate() - { - scrollToBottom(); - } - getTimeString(time) { return `${(time.getHours() < 10 ? '0' : '')}${time.getHours()}:${(time.getMinutes() < 10 ? '0' : '')}${time.getMinutes()}`; @@ -96,8 +81,9 @@ const mapStateToProps = (state) => }; }; -const MessageListContainer = connect( - mapStateToProps +const MessageListContainer = compose( + connect(mapStateToProps), + scrollToBottom() )(MessageList); export default MessageListContainer; diff --git a/app/lib/components/Chat/scrollToBottom.jsx b/app/lib/components/Chat/scrollToBottom.jsx new file mode 100644 index 0000000..2a107a4 --- /dev/null +++ b/app/lib/components/Chat/scrollToBottom.jsx @@ -0,0 +1,63 @@ +import React, { Component } from 'react'; +import { findDOMNode } from 'react-dom'; + +/** + * A higher order component which scrolls the user to the bottom of the + * wrapped component, provided that the user already was at the bottom + * of the wrapped component. Useful for chats and similar use cases. + * @param {number} treshold The required distance from the bottom required. + */ +const scrollToBottom = (treshold = 0) => (WrappedComponent) => +{ + return class AutoScroller extends Component + { + constructor(props) + { + super(props); + + this.ref = React.createRef(); + } + + getSnapshotBeforeUpdate() + { + // Check if the user has scrolled close enough to the bottom for + // us to scroll to the bottom or not. + return this.elem.scrollHeight - this.elem.scrollTop <= + this.elem.clientHeight - treshold; + } + + scrollToBottom = () => + { + // Scroll the user to the bottom of the wrapped element. + this.elem.scrollTop = this.elem.scrollHeight; + }; + + componentDidMount() + { + // eslint-disable-next-line react/no-find-dom-node + this.elem = findDOMNode(this.ref.current); + + this.scrollToBottom(); + } + + componentDidUpdate(prevProps, prevState, atBottom) + { + if (atBottom) + { + this.scrollToBottom(); + } + } + + render() + { + return ( + + ); + } + }; +}; + +export default scrollToBottom; \ No newline at end of file