Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
MM-14949 Correct scroll when new posts are loaded
Browse files Browse the repository at this point in the history
* Check for state change in posts instead of props
* Correct scroll when posts change at top or when header changes
* use snapshot for taking height before updating postslist
  • Loading branch information
sudheerDev committed Apr 29, 2019
1 parent 20233ab commit 904e300
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 32 deletions.
62 changes: 55 additions & 7 deletions components/post_view/post_list.jsx
Expand Up @@ -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';

Expand All @@ -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 = {

Expand Down Expand Up @@ -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);
}
Expand All @@ -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() {
Expand Down Expand Up @@ -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});
}
Expand Down Expand Up @@ -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 = () => {
Expand Down Expand Up @@ -443,7 +490,7 @@ export default class PostList extends React.PureComponent {
{({height, width}) => (
<DynamicSizeList
ref={this.listRef}
height={height}
height={height - postListHeightChangeForPadding}
width={width}
className='post-list__dynamic'
itemCount={this.state.postListIds.length}
Expand All @@ -454,10 +501,11 @@ export default class PostList extends React.PureComponent {
onScroll={this.onScroll}
onItemsRendered={this.onItemsRendered}
initScrollToIndex={this.initScrollToIndex}
onNewItemsMounted={this.onNewItemsMounted}
canLoadMorePosts={this.canLoadMorePosts}
skipResizeClass='col__reply'
style={dynamicListStyle}
innerRef={this.postlistRef}
style={{...virtListStyles, ...dynamicListStyle}}
innerListStyle={postListStyle}
>
{this.renderRow}
</DynamicSizeList>
Expand Down
30 changes: 30 additions & 0 deletions components/post_view/post_list.test.jsx
Expand Up @@ -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}`);
Expand Down Expand Up @@ -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(<PostList {...baseProps}/>);
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(<PostList {...baseProps}/>);
wrapper.instance().postlistRef = {
current: {
scrollTop: 1000,
scrollHeight: 4000,
},
};
wrapper.setProps({postListIds: [
'post1',
'post2',
'post3',
'post4',
DATE_LINE + 1551711600000,
]});
wrapper.update();
});
});
});
28 changes: 18 additions & 10 deletions components/post_view/show_more/show_more.jsx
Expand Up @@ -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) => {
Expand All @@ -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 = () => {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -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",
Expand Down
14 changes: 2 additions & 12 deletions sass/layout/_post.scss
Expand Up @@ -284,7 +284,6 @@
}

.post-list-holder-by-time {
-webkit-overflow-scrolling: touch;
height: 100%;
width: 100%;
position: absolute;
Expand Down Expand Up @@ -413,14 +412,6 @@
padding: 15px;
}

.post-list__dynamic {
> div {
> div {
margin-bottom: 8px;
}
}
}

.post-create__container {
@include flex(0 0 auto);
width: 100%;
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 904e300

Please sign in to comment.