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';
11 export default class StatusList
extends ImmutablePureComponent
{
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 shouldUpdateScroll: PropTypes
.func
,
22 isLoading: PropTypes
.bool
,
23 isPartial: PropTypes
.bool
,
24 hasMore: PropTypes
.bool
,
25 prepend: PropTypes
.node
,
26 emptyMessage: PropTypes
.node
,
27 alwaysPrepend: PropTypes
.bool
,
28 timelineId: PropTypes
.string
,
29 currentlyViewing: PropTypes
.number
,
30 updateCurrentlyViewing: PropTypes
.func
,
33 static defaultProps
= {
37 getFeaturedStatusCount
= () => {
38 return this.props
.featuredStatusIds
? this.props
.featuredStatusIds
.size : 0;
41 getCurrentStatusIndex
= (id
, featured
) => {
43 return this.props
.featuredStatusIds
.indexOf(id
);
45 return this.props
.statusIds
.indexOf(id
) + this.getFeaturedStatusCount();
49 handleMoveUp
= (id
, featured
) => {
50 const elementIndex
= this.getCurrentStatusIndex(id
, featured
) - 1;
51 this._selectChild(elementIndex
, true);
54 handleMoveDown
= (id
, featured
) => {
55 const elementIndex
= this.getCurrentStatusIndex(id
, featured
) + 1;
56 this._selectChild(elementIndex
, false);
59 handleLoadOlder
= debounce(() => {
60 this.props
.onLoadMore(this.props
.statusIds
.size
> 0 ? this.props
.statusIds
.last() : undefined);
61 }, 300, { leading: true })
63 updateCurrentlyViewingWithCache
= (id
) => {
64 if(this.cachedCurrentlyViewing
=== id
) return;
65 this.cachedCurrentlyViewing
= id
;
66 this.props
.updateCurrentlyViewing(id
);
69 _selectChild (index
, align_top
) {
70 const container
= this.node
.node
;
71 const element
= container
.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
74 if (align_top
&& container
.scrollTop
> element
.offsetTop
) {
75 element
.scrollIntoView(true);
76 } else if (!align_top
&& container
.scrollTop
+ container
.clientHeight
< element
.offsetTop
+ element
.offsetHeight
) {
77 element
.scrollIntoView(false);
88 const { statusIds
, featuredStatusIds
, shouldUpdateScroll
, onLoadMore
, timelineId
, ...other
} = this.props
;
89 const { isLoading
, isPartial
} = other
;
90 other
.updateCurrentlyViewing
= this.updateCurrentlyViewingWithCache
;
93 return <RegenerationIndicator
/>;
96 let scrollableContent
= (isLoading
|| statusIds
.size
> 0) ? (
97 statusIds
.map((statusId
, index
) => statusId
=== null ? (
99 key
={'gap:' + statusIds
.get(index
+ 1)}
101 maxId
={index
> 0 ? statusIds
.get(index
- 1) : null}
108 onMoveUp
={this.handleMoveUp
}
109 onMoveDown
={this.handleMoveDown
}
110 contextType
={timelineId
}
116 if (scrollableContent
&& featuredStatusIds
) {
117 scrollableContent
= featuredStatusIds
.map(statusId
=> (
119 key
={`f-${statusId}`}
122 onMoveUp
={this.handleMoveUp
}
123 onMoveDown
={this.handleMoveDown
}
124 contextType
={timelineId
}
127 )).concat(scrollableContent
);
131 <ScrollableList
{...other
} showLoading
={isLoading
&& statusIds
.size
=== 0} onLoadMore
={onLoadMore
&& this.handleLoadOlder
} shouldUpdateScroll
={shouldUpdateScroll
} ref
={this.setRef
}>