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.

130 lines
3.5 KiB

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