]> cat aescling's git repositories - mastodon.git/blob - app/javascript/mastodon/components/status_list.js
Merge pull request #1700 from ClearlyClaire/glitch-soc/merge-upstream
[mastodon.git] / app / javascript / mastodon / components / status_list.js
1 import { debounce } from 'lodash';
2 import React from 'react';
3 import ImmutablePropTypes from 'react-immutable-proptypes';
4 import PropTypes from 'prop-types';
5 import StatusContainer from '../containers/status_container';
6 import ImmutablePureComponent from 'react-immutable-pure-component';
7 import LoadGap from './load_gap';
8 import ScrollableList from './scrollable_list';
9 import RegenerationIndicator from 'mastodon/components/regeneration_indicator';
10
11 export default class StatusList extends ImmutablePureComponent {
12
13 static propTypes = {
14 scrollKey: PropTypes.string.isRequired,
15 statusIds: ImmutablePropTypes.list.isRequired,
16 featuredStatusIds: ImmutablePropTypes.list,
17 onLoadMore: PropTypes.func,
18 onScrollToTop: PropTypes.func,
19 onScroll: PropTypes.func,
20 trackScroll: PropTypes.bool,
21 isLoading: PropTypes.bool,
22 isPartial: PropTypes.bool,
23 hasMore: PropTypes.bool,
24 prepend: PropTypes.node,
25 emptyMessage: PropTypes.node,
26 alwaysPrepend: PropTypes.bool,
27 timelineId: PropTypes.string,
28 };
29
30 static defaultProps = {
31 trackScroll: true,
32 };
33
34 getFeaturedStatusCount = () => {
35 return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0;
36 }
37
38 getCurrentStatusIndex = (id, featured) => {
39 if (featured) {
40 return this.props.featuredStatusIds.indexOf(id);
41 } else {
42 return this.props.statusIds.indexOf(id) + this.getFeaturedStatusCount();
43 }
44 }
45
46 handleMoveUp = (id, featured) => {
47 const elementIndex = this.getCurrentStatusIndex(id, featured) - 1;
48 this._selectChild(elementIndex, true);
49 }
50
51 handleMoveDown = (id, featured) => {
52 const elementIndex = this.getCurrentStatusIndex(id, featured) + 1;
53 this._selectChild(elementIndex, false);
54 }
55
56 handleLoadOlder = debounce(() => {
57 this.props.onLoadMore(this.props.statusIds.size > 0 ? this.props.statusIds.last() : undefined);
58 }, 300, { leading: true })
59
60 _selectChild (index, align_top) {
61 const container = this.node.node;
62 const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
63
64 if (element) {
65 if (align_top && container.scrollTop > element.offsetTop) {
66 element.scrollIntoView(true);
67 } else if (!align_top && container.scrollTop + container.clientHeight < element.offsetTop + element.offsetHeight) {
68 element.scrollIntoView(false);
69 }
70 element.focus();
71 }
72 }
73
74 setRef = c => {
75 this.node = c;
76 }
77
78 render () {
79 const { statusIds, featuredStatusIds, onLoadMore, timelineId, ...other } = this.props;
80 const { isLoading, isPartial } = other;
81
82 if (isPartial) {
83 return <RegenerationIndicator />;
84 }
85
86 let scrollableContent = (isLoading || statusIds.size > 0) ? (
87 statusIds.map((statusId, index) => statusId === null ? (
88 <LoadGap
89 key={'gap:' + statusIds.get(index + 1)}
90 disabled={isLoading}
91 maxId={index > 0 ? statusIds.get(index - 1) : null}
92 onClick={onLoadMore}
93 />
94 ) : (
95 <StatusContainer
96 key={statusId}
97 id={statusId}
98 onMoveUp={this.handleMoveUp}
99 onMoveDown={this.handleMoveDown}
100 contextType={timelineId}
101 scrollKey={this.props.scrollKey}
102 showThread
103 />
104 ))
105 ) : null;
106
107 if (scrollableContent && featuredStatusIds) {
108 scrollableContent = featuredStatusIds.map(statusId => (
109 <StatusContainer
110 key={`f-${statusId}`}
111 id={statusId}
112 featured
113 onMoveUp={this.handleMoveUp}
114 onMoveDown={this.handleMoveDown}
115 contextType={timelineId}
116 showThread
117 />
118 )).concat(scrollableContent);
119 }
120
121 return (
122 <ScrollableList {...other} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
123 {scrollableContent}
124 </ScrollableList>
125 );
126 }
127
128 }
This page took 0.117253 seconds and 4 git commands to generate.