]> cat aescling's git repositories - mastodon.git/blob - app/javascript/mastodon/components/status_list.js
Prevent pinned columns from scroll to top on URL changing (#3586)
[mastodon.git] / app / javascript / mastodon / components / status_list.js
1 import React from 'react';
2 import ImmutablePropTypes from 'react-immutable-proptypes';
3 import { ScrollContainer } from 'react-router-scroll';
4 import PropTypes from 'prop-types';
5 import StatusContainer from '../containers/status_container';
6 import LoadMore from './load_more';
7 import ImmutablePureComponent from 'react-immutable-pure-component';
8 import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper';
9
10 class StatusList extends ImmutablePureComponent {
11
12 static propTypes = {
13 scrollKey: PropTypes.string.isRequired,
14 statusIds: ImmutablePropTypes.list.isRequired,
15 onScrollToBottom: PropTypes.func,
16 onScrollToTop: PropTypes.func,
17 onScroll: PropTypes.func,
18 trackScroll: PropTypes.bool,
19 shouldUpdateScroll: PropTypes.func,
20 isLoading: PropTypes.bool,
21 isUnread: PropTypes.bool,
22 hasMore: PropTypes.bool,
23 prepend: PropTypes.node,
24 emptyMessage: PropTypes.node,
25 };
26
27 static defaultProps = {
28 trackScroll: true,
29 };
30
31 intersectionObserverWrapper = new IntersectionObserverWrapper();
32
33 handleScroll = (e) => {
34 const { scrollTop, scrollHeight, clientHeight } = e.target;
35 const offset = scrollHeight - scrollTop - clientHeight;
36 this._oldScrollPosition = scrollHeight - scrollTop;
37
38 if (250 > offset && this.props.onScrollToBottom && !this.props.isLoading) {
39 this.props.onScrollToBottom();
40 } else if (scrollTop < 100 && this.props.onScrollToTop) {
41 this.props.onScrollToTop();
42 } else if (this.props.onScroll) {
43 this.props.onScroll();
44 }
45 }
46
47 componentDidMount () {
48 this.attachScrollListener();
49 this.attachIntersectionObserver();
50 }
51
52 componentDidUpdate (prevProps) {
53 if ((prevProps.statusIds.size < this.props.statusIds.size && prevProps.statusIds.first() !== this.props.statusIds.first() && !!this._oldScrollPosition) && this.node.scrollTop > 0) {
54 this.node.scrollTop = this.node.scrollHeight - this._oldScrollPosition;
55 }
56 }
57
58 componentWillUnmount () {
59 this.detachScrollListener();
60 this.detachIntersectionObserver();
61 }
62
63 attachIntersectionObserver () {
64 this.intersectionObserverWrapper.connect({
65 root: this.node,
66 rootMargin: '300% 0px',
67 });
68 }
69
70 detachIntersectionObserver () {
71 this.intersectionObserverWrapper.disconnect();
72 }
73
74 attachScrollListener () {
75 this.node.addEventListener('scroll', this.handleScroll);
76 }
77
78 detachScrollListener () {
79 this.node.removeEventListener('scroll', this.handleScroll);
80 }
81
82 setRef = (c) => {
83 this.node = c;
84 }
85
86 handleLoadMore = (e) => {
87 e.preventDefault();
88 this.props.onScrollToBottom();
89 }
90
91 render () {
92 const { statusIds, onScrollToBottom, scrollKey, trackScroll, shouldUpdateScroll, isLoading, isUnread, hasMore, prepend, emptyMessage } = this.props;
93
94 let loadMore = null;
95 let scrollableArea = null;
96 let unread = null;
97
98 if (!isLoading && statusIds.size > 0 && hasMore) {
99 loadMore = <LoadMore onClick={this.handleLoadMore} />;
100 }
101
102 if (isUnread) {
103 unread = <div className='status-list__unread-indicator' />;
104 }
105
106 if (isLoading || statusIds.size > 0 || !emptyMessage) {
107 scrollableArea = (
108 <div className='scrollable' ref={this.setRef}>
109 {unread}
110
111 <div className='status-list'>
112 {prepend}
113
114 {statusIds.map((statusId) => {
115 return <StatusContainer key={statusId} id={statusId} intersectionObserverWrapper={this.intersectionObserverWrapper} />;
116 })}
117
118 {loadMore}
119 </div>
120 </div>
121 );
122 } else {
123 scrollableArea = (
124 <div className='empty-column-indicator' ref={this.setRef}>
125 {emptyMessage}
126 </div>
127 );
128 }
129
130 if (trackScroll) {
131 return (
132 <ScrollContainer scrollKey={scrollKey} shouldUpdateScroll={shouldUpdateScroll}>
133 {scrollableArea}
134 </ScrollContainer>
135 );
136 } else {
137 return scrollableArea;
138 }
139 }
140
141 }
142
143 export default StatusList;
This page took 0.091074 seconds and 4 git commands to generate.