The code powering m.abunchtell.com https://m.abunchtell.com
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.
 
 
 
 

177 lines
5.4 KiB

  1. import React from 'react';
  2. import { connect } from 'react-redux';
  3. import PropTypes from 'prop-types';
  4. import ImmutablePropTypes from 'react-immutable-proptypes';
  5. import StatusListContainer from '../ui/containers/status_list_container';
  6. import Column from '../../components/column';
  7. import ColumnBackButton from '../../components/column_back_button';
  8. import ColumnHeader from '../../components/column_header';
  9. import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
  10. import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
  11. import { connectListStream } from '../../actions/streaming';
  12. import { expandListTimeline } from '../../actions/timelines';
  13. import { fetchList, deleteList } from '../../actions/lists';
  14. import { openModal } from '../../actions/modal';
  15. import MissingIndicator from '../../components/missing_indicator';
  16. import LoadingIndicator from '../../components/loading_indicator';
  17. const messages = defineMessages({
  18. deleteMessage: { id: 'confirmations.delete_list.message', defaultMessage: 'Are you sure you want to permanently delete this list?' },
  19. deleteConfirm: { id: 'confirmations.delete_list.confirm', defaultMessage: 'Delete' },
  20. });
  21. const mapStateToProps = (state, props) => ({
  22. list: state.getIn(['lists', props.params.id]),
  23. hasUnread: state.getIn(['timelines', `list:${props.params.id}`, 'unread']) > 0,
  24. });
  25. export default @connect(mapStateToProps)
  26. @injectIntl
  27. class ListTimeline extends React.PureComponent {
  28. static contextTypes = {
  29. router: PropTypes.object,
  30. };
  31. static propTypes = {
  32. params: PropTypes.object.isRequired,
  33. dispatch: PropTypes.func.isRequired,
  34. shouldUpdateScroll: PropTypes.func,
  35. columnId: PropTypes.string,
  36. hasUnread: PropTypes.bool,
  37. multiColumn: PropTypes.bool,
  38. list: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
  39. intl: PropTypes.object.isRequired,
  40. };
  41. handlePin = () => {
  42. const { columnId, dispatch } = this.props;
  43. if (columnId) {
  44. dispatch(removeColumn(columnId));
  45. } else {
  46. dispatch(addColumn('LIST', { id: this.props.params.id }));
  47. this.context.router.history.push('/');
  48. }
  49. }
  50. handleMove = (dir) => {
  51. const { columnId, dispatch } = this.props;
  52. dispatch(moveColumn(columnId, dir));
  53. }
  54. handleHeaderClick = () => {
  55. this.column.scrollTop();
  56. }
  57. componentDidMount () {
  58. const { dispatch } = this.props;
  59. const { id } = this.props.params;
  60. dispatch(fetchList(id));
  61. dispatch(expandListTimeline(id));
  62. this.disconnect = dispatch(connectListStream(id));
  63. }
  64. componentWillUnmount () {
  65. if (this.disconnect) {
  66. this.disconnect();
  67. this.disconnect = null;
  68. }
  69. }
  70. setRef = c => {
  71. this.column = c;
  72. }
  73. handleLoadMore = maxId => {
  74. const { id } = this.props.params;
  75. this.props.dispatch(expandListTimeline(id, { maxId }));
  76. }
  77. handleEditClick = () => {
  78. this.props.dispatch(openModal('LIST_EDITOR', { listId: this.props.params.id }));
  79. }
  80. handleDeleteClick = () => {
  81. const { dispatch, columnId, intl } = this.props;
  82. const { id } = this.props.params;
  83. dispatch(openModal('CONFIRM', {
  84. message: intl.formatMessage(messages.deleteMessage),
  85. confirm: intl.formatMessage(messages.deleteConfirm),
  86. onConfirm: () => {
  87. dispatch(deleteList(id));
  88. if (!!columnId) {
  89. dispatch(removeColumn(columnId));
  90. } else {
  91. this.context.router.history.push('/lists');
  92. }
  93. },
  94. }));
  95. }
  96. render () {
  97. const { shouldUpdateScroll, hasUnread, columnId, multiColumn, list } = this.props;
  98. const { id } = this.props.params;
  99. const pinned = !!columnId;
  100. const title = list ? list.get('title') : id;
  101. if (typeof list === 'undefined') {
  102. return (
  103. <Column>
  104. <div className='scrollable'>
  105. <LoadingIndicator />
  106. </div>
  107. </Column>
  108. );
  109. } else if (list === false) {
  110. return (
  111. <Column>
  112. <ColumnBackButton />
  113. <MissingIndicator />
  114. </Column>
  115. );
  116. }
  117. return (
  118. <Column ref={this.setRef} label={title}>
  119. <ColumnHeader
  120. icon='list-ul'
  121. active={hasUnread}
  122. title={title}
  123. onPin={this.handlePin}
  124. onMove={this.handleMove}
  125. onClick={this.handleHeaderClick}
  126. pinned={pinned}
  127. multiColumn={multiColumn}
  128. >
  129. <div className='column-header__links'>
  130. <button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.handleEditClick}>
  131. <i className='fa fa-pencil' /> <FormattedMessage id='lists.edit' defaultMessage='Edit list' />
  132. </button>
  133. <button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.handleDeleteClick}>
  134. <i className='fa fa-trash' /> <FormattedMessage id='lists.delete' defaultMessage='Delete list' />
  135. </button>
  136. </div>
  137. <hr />
  138. </ColumnHeader>
  139. <StatusListContainer
  140. trackScroll={!pinned}
  141. scrollKey={`list_timeline-${columnId}`}
  142. timelineId={`list:${id}`}
  143. onLoadMore={this.handleLoadMore}
  144. emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.' />}
  145. shouldUpdateScroll={shouldUpdateScroll}
  146. />
  147. </Column>
  148. );
  149. }
  150. }