diff --git a/components/post_view/post_list.jsx b/components/post_view/post_list.jsx index 09214ce68287..512041e1f999 100644 --- a/components/post_view/post_list.jsx +++ b/components/post_view/post_list.jsx @@ -5,6 +5,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; import {DynamicSizeList} from 'react-window'; +import {debounce} from 'mattermost-redux/actions/helpers'; import LoadingScreen from 'components/loading_screen.jsx'; @@ -25,6 +26,18 @@ const OVERSCAN_COUNT_BACKWARD = window.OVERSCAN_COUNT_BACKWARD || 50; // Exposin const OVERSCAN_COUNT_FORWARD = window.OVERSCAN_COUNT_FORWARD || 100; // Exposing the value for PM to test will be removed soon const HEIGHT_TRIGGER_FOR_MORE_POSTS = window.HEIGHT_TRIGGER_FOR_MORE_POSTS || 1000; // Exposing the value for PM to test will be removed soon +const postListHeightChangeForPadding = 21; + +const virtListStyles = { + position: 'absolute', + bottom: '0', + maxHeight: '100%', +}; + +const postListStyle = { + padding: '14px 0px 7px', //21px of height difference from autosized list below at DynamicSizeList height change postListHeightChangeForPadding accordingly +}; + export default class PostList extends React.PureComponent { static propTypes = { @@ -101,6 +114,7 @@ export default class PostList extends React.PureComponent { }; this.listRef = React.createRef(); + this.postlistRef = React.createRef(); if (isMobile) { this.scrollStopAction = new DelayedAction(this.handleScrollStop); } @@ -116,10 +130,41 @@ export default class PostList extends React.PureComponent { window.addEventListener('resize', this.handleWindowResize); } - componentDidUpdate(prevProps) { + getSnapshotBeforeUpdate(prevProps, prevState) { + if (this.postlistRef && this.postlistRef.current) { + const postsAddedAtTop = this.state.postListIds.length !== prevState.postListIds.length && this.state.postListIds[0] === prevState.postListIds[0]; + const channelHeaderAdded = this.state.atEnd !== prevState.atEnd && this.state.postListIds.length === prevState.postListIds.length; + if (postsAddedAtTop || channelHeaderAdded) { + const previousScrollTop = this.postlistRef.current.scrollTop; + const previousScrollHeight = this.postlistRef.current.scrollHeight; + + return { + previousScrollTop, + previousScrollHeight, + }; + } + } + return null; + } + + componentDidUpdate(prevProps, prevState, snapshot) { if (prevProps.channelLoading && !this.props.channelLoading) { this.loadPosts(this.props.channel.id, this.props.focusedPostId); } + + if (!this.postlistRef.current || !snapshot) { + return; + } + + const postlistScrollHeight = this.postlistRef.current.scrollHeight; + const postsAddedAtTop = this.state.postListIds.length !== prevState.postListIds.length && this.state.postListIds[0] === prevState.postListIds[0]; + const channelHeaderAdded = this.state.atEnd !== prevState.atEnd && this.state.postListIds.length === prevState.postListIds.length; + if (postsAddedAtTop || channelHeaderAdded) { + const scrollValue = snapshot.previousScrollTop + (postlistScrollHeight - snapshot.previousScrollHeight); + if (scrollValue !== 0 && (scrollValue - snapshot.previousScrollTop) !== 0) { + this.listRef.current.scrollTo(scrollValue, scrollValue - snapshot.previousScrollTop, !this.state.atEnd); + } + } } componentWillUnmount() { @@ -196,7 +241,9 @@ export default class PostList extends React.PureComponent { if (error) { if (this.autoRetriesCount < MAX_NUMBER_OF_AUTO_RETRIES) { this.autoRetriesCount++; - this.loadMorePosts(); + debounce(() => { + this.loadMorePosts(); + }); } else if (this.mounted) { this.setState({autoRetryEnable: false}); } @@ -317,8 +364,8 @@ export default class PostList extends React.PureComponent { visibleStartIndex, visibleStopIndex, }) => { - this.updateFloatingTimestamp(visibleStopIndex); - this.checkBottom(visibleStartIndex); + this.updateFloatingTimestamp(visibleStartIndex); + this.checkBottom(visibleStopIndex); } initScrollToIndex = () => { @@ -443,7 +490,7 @@ export default class PostList extends React.PureComponent { {({height, width}) => ( {this.renderRow} diff --git a/components/post_view/post_list.test.jsx b/components/post_view/post_list.test.jsx index ccd36dd2b0db..dab20ab07cb8 100644 --- a/components/post_view/post_list.test.jsx +++ b/components/post_view/post_list.test.jsx @@ -116,6 +116,7 @@ describe('PostList', () => { describe('initScrollToIndex', () => { test('should return index of start of new messages and call increasePostVisibility when all posts are unread', () => { + baseProps.actions.increasePostVisibility.mockResolvedValue({moreToLoad: false}); const postListIds = []; for (let i = 0; i < 30; i++) { postListIds.push(`post${i}`); @@ -152,4 +153,33 @@ describe('PostList', () => { expect(wrapper.state('atEnd')).toEqual(true); }); }); + + describe('onItemsRendered', () => { + test('should set state atBottom when not visibleStopIndex is not 0', async () => { + const wrapper = shallow(); + wrapper.setState({atBottom: true}); + wrapper.instance().onItemsRendered({visibleStartIndex: 4, visibleStopIndex: 1}); + expect(wrapper.state('atBottom')).toEqual(false); + }); + }); + + describe('store snapshot values on change of props for correcting scroll', () => { + test('Should call scrollTo when posts are added at the top', async () => { + const wrapper = shallow(); + wrapper.instance().postlistRef = { + current: { + scrollTop: 1000, + scrollHeight: 4000, + }, + }; + wrapper.setProps({postListIds: [ + 'post1', + 'post2', + 'post3', + 'post4', + DATE_LINE + 1551711600000, + ]}); + wrapper.update(); + }); + }); }); diff --git a/components/post_view/show_more/show_more.jsx b/components/post_view/show_more/show_more.jsx index 8de53a5058d8..3a04d3914647 100644 --- a/components/post_view/show_more/show_more.jsx +++ b/components/post_view/show_more/show_more.jsx @@ -41,6 +41,9 @@ export default class ShowMore extends React.PureComponent { componentWillUnmount() { window.removeEventListener('resize', this.handleResize); + if (this.overflowRef) { + window.cancelAnimationFrame(this.overflowRef); + } } toggleCollapse = (e) => { @@ -53,18 +56,23 @@ export default class ShowMore extends React.PureComponent { }; checkTextOverflow = () => { - const textContainer = this.refs.textContainer; - let isOverflow = false; - - if (textContainer && textContainer.scrollHeight > this.props.maxHeight) { - isOverflow = true; + if (this.overflowRef) { + window.cancelAnimationFrame(this.overflowRef); } + this.overflowRef = window.requestAnimationFrame(() => { + const textContainer = this.refs.textContainer; + let isOverflow = false; - if (isOverflow !== this.state.isOverflow) { - this.setState({ - isOverflow, - }); - } + if (textContainer && textContainer.scrollHeight > this.props.maxHeight) { + isOverflow = true; + } + + if (isOverflow !== this.state.isOverflow) { + this.setState({ + isOverflow, + }); + } + }); }; handleResize = () => { diff --git a/package-lock.json b/package-lock.json index ee54f3916a3b..8029a86197ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13059,8 +13059,8 @@ "integrity": "sha512-MYXhTY1BZpdJFjUovvYHVBmkq79szK/k7V3MO+36gJkWGkrXKtyr4vCPtpphaTLRAdDNoYEYFZWE8LjN+PIHNg==" }, "react-window": { - "version": "github:mattermost/react-window#22cab3a8a0f4e3793ba778a990f1c0723b4c8129", - "from": "github:mattermost/react-window#22cab3a8a0f4e3793ba778a990f1c0723b4c8129", + "version": "github:mattermost/react-window#aa71079c988be134735060d890fecff1479ede2a", + "from": "github:mattermost/react-window#aa71079c988be134735060d890fecff1479ede2a", "requires": { "@babel/runtime": "^7.0.0", "memoize-one": "^3.1.1" diff --git a/package.json b/package.json index 93c6d7839ceb..504c2906694b 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "react-select": "2.4.2", "react-transition-group": "2.7.1", "react-virtualized-auto-sizer": "^1.0.2", - "react-window": "github:mattermost/react-window#22cab3a8a0f4e3793ba778a990f1c0723b4c8129", + "react-window": "github:mattermost/react-window#aa71079c988be134735060d890fecff1479ede2a", "rebound": "0.1.0", "redux": "4.0.1", "redux-batched-actions": "0.4.1", diff --git a/sass/layout/_post.scss b/sass/layout/_post.scss index c9bd90b1db70..d7ee1b117d93 100644 --- a/sass/layout/_post.scss +++ b/sass/layout/_post.scss @@ -284,7 +284,6 @@ } .post-list-holder-by-time { - -webkit-overflow-scrolling: touch; height: 100%; width: 100%; position: absolute; @@ -413,14 +412,6 @@ padding: 15px; } -.post-list__dynamic { - > div { - > div { - margin-bottom: 8px; - } - } -} - .post-create__container { @include flex(0 0 auto); width: 100%; @@ -700,13 +691,12 @@ .post-list__table { @include clearfix; - display: table; height: 100%; - table-layout: fixed; width: 100%; .post-list__content { - display: table-cell; + height: 100%; + overflow: hidden; .dropdown-menu { &.bottom {