You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
3.3 KiB

  1. import Status from './status';
  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. class StatusList extends React.PureComponent {
  8. constructor (props, context) {
  9. super(props, context);
  10. this.handleScroll = this.handleScroll.bind(this);
  11. this.setRef = this.setRef.bind(this);
  12. this.handleLoadMore = this.handleLoadMore.bind(this);
  13. }
  14. handleScroll (e) {
  15. const { scrollTop, scrollHeight, clientHeight } = e.target;
  16. const offset = scrollHeight - scrollTop - clientHeight;
  17. this._oldScrollPosition = scrollHeight - scrollTop;
  18. if (250 > offset && this.props.onScrollToBottom && !this.props.isLoading) {
  19. this.props.onScrollToBottom();
  20. } else if (scrollTop < 100 && this.props.onScrollToTop) {
  21. this.props.onScrollToTop();
  22. } else if (this.props.onScroll) {
  23. this.props.onScroll();
  24. }
  25. }
  26. componentDidMount () {
  27. this.attachScrollListener();
  28. }
  29. componentDidUpdate (prevProps) {
  30. if (this.node.scrollTop > 0 && (prevProps.statusIds.size < this.props.statusIds.size && prevProps.statusIds.first() !== this.props.statusIds.first() && !!this._oldScrollPosition)) {
  31. this.node.scrollTop = this.node.scrollHeight - this._oldScrollPosition;
  32. }
  33. }
  34. componentWillUnmount () {
  35. this.detachScrollListener();
  36. }
  37. attachScrollListener () {
  38. this.node.addEventListener('scroll', this.handleScroll);
  39. }
  40. detachScrollListener () {
  41. this.node.removeEventListener('scroll', this.handleScroll);
  42. }
  43. setRef (c) {
  44. this.node = c;
  45. }
  46. handleLoadMore (e) {
  47. e.preventDefault();
  48. this.props.onScrollToBottom();
  49. }
  50. render () {
  51. const { statusIds, onScrollToBottom, trackScroll, isLoading, isUnread, hasMore, prepend, emptyMessage } = this.props;
  52. let loadMore = '';
  53. let scrollableArea = '';
  54. let unread = '';
  55. if (!isLoading && statusIds.size > 0 && hasMore) {
  56. loadMore = <LoadMore onClick={this.handleLoadMore} />;
  57. }
  58. if (isUnread) {
  59. unread = <div className='status-list__unread-indicator' />;
  60. }
  61. if (isLoading || statusIds.size > 0 || !emptyMessage) {
  62. scrollableArea = (
  63. <div className='scrollable' ref={this.setRef}>
  64. {unread}
  65. <div>
  66. {prepend}
  67. {statusIds.map((statusId) => {
  68. return <StatusContainer key={statusId} id={statusId} />;
  69. })}
  70. {loadMore}
  71. </div>
  72. </div>
  73. );
  74. } else {
  75. scrollableArea = (
  76. <div className='empty-column-indicator' ref={this.setRef}>
  77. {emptyMessage}
  78. </div>
  79. );
  80. }
  81. if (trackScroll) {
  82. return (
  83. <ScrollContainer scrollKey='status-list'>
  84. {scrollableArea}
  85. </ScrollContainer>
  86. );
  87. } else {
  88. return scrollableArea;
  89. }
  90. }
  91. }
  92. StatusList.propTypes = {
  93. statusIds: ImmutablePropTypes.list.isRequired,
  94. onScrollToBottom: PropTypes.func,
  95. onScrollToTop: PropTypes.func,
  96. onScroll: PropTypes.func,
  97. trackScroll: PropTypes.bool,
  98. isLoading: PropTypes.bool,
  99. isUnread: PropTypes.bool,
  100. hasMore: PropTypes.bool,
  101. prepend: PropTypes.node,
  102. emptyMessage: PropTypes.node
  103. };
  104. StatusList.defaultProps = {
  105. trackScroll: true
  106. };
  107. export default StatusList;