logo

mastofe

My custom branche(s) on git.pleroma.social/pleroma/mastofe git clone https://hacktivis.me/git/mastofe.git

index.js (5939B)


  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 Column from '../../components/column';
  6. import ColumnHeader from '../../components/column_header';
  7. import { expandNotifications, scrollTopNotifications } from '../../actions/notifications';
  8. import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
  9. import NotificationContainer from './containers/notification_container';
  10. import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
  11. import ColumnSettingsContainer from './containers/column_settings_container';
  12. import { createSelector } from 'reselect';
  13. import { List as ImmutableList } from 'immutable';
  14. import { debounce } from 'lodash';
  15. import ScrollableList from '../../components/scrollable_list';
  16. import LoadGap from '../../components/load_gap';
  17. const messages = defineMessages({
  18. title: { id: 'column.notifications', defaultMessage: 'Notifications' },
  19. });
  20. const getNotifications = createSelector([
  21. state => ImmutableList(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
  22. state => state.getIn(['notifications', 'items']),
  23. ], (excludedTypes, notifications) => notifications.filterNot(item => item !== null && excludedTypes.includes(item.get('type'))));
  24. const mapStateToProps = state => ({
  25. notifications: getNotifications(state),
  26. isLoading: state.getIn(['notifications', 'isLoading'], true),
  27. isUnread: state.getIn(['notifications', 'unread']) > 0,
  28. hasMore: state.getIn(['notifications', 'hasMore']),
  29. });
  30. @connect(mapStateToProps)
  31. @injectIntl
  32. export default class Notifications extends React.PureComponent {
  33. static propTypes = {
  34. columnId: PropTypes.string,
  35. notifications: ImmutablePropTypes.list.isRequired,
  36. dispatch: PropTypes.func.isRequired,
  37. shouldUpdateScroll: PropTypes.func,
  38. intl: PropTypes.object.isRequired,
  39. isLoading: PropTypes.bool,
  40. isUnread: PropTypes.bool,
  41. multiColumn: PropTypes.bool,
  42. hasMore: PropTypes.bool,
  43. };
  44. static defaultProps = {
  45. trackScroll: true,
  46. };
  47. componentWillUnmount () {
  48. this.handleLoadOlder.cancel();
  49. this.handleScrollToTop.cancel();
  50. this.handleScroll.cancel();
  51. this.props.dispatch(scrollTopNotifications(false));
  52. }
  53. handleLoadGap = (maxId) => {
  54. this.props.dispatch(expandNotifications({ maxId }));
  55. };
  56. handleLoadOlder = debounce(() => {
  57. const last = this.props.notifications.last();
  58. this.props.dispatch(expandNotifications({ maxId: last && last.get('id') }));
  59. }, 300, { leading: true });
  60. handleScrollToTop = debounce(() => {
  61. this.props.dispatch(scrollTopNotifications(true));
  62. }, 100);
  63. handleScroll = debounce(() => {
  64. this.props.dispatch(scrollTopNotifications(false));
  65. }, 100);
  66. handlePin = () => {
  67. const { columnId, dispatch } = this.props;
  68. if (columnId) {
  69. dispatch(removeColumn(columnId));
  70. } else {
  71. dispatch(addColumn('NOTIFICATIONS', {}));
  72. }
  73. }
  74. handleMove = (dir) => {
  75. const { columnId, dispatch } = this.props;
  76. dispatch(moveColumn(columnId, dir));
  77. }
  78. handleHeaderClick = () => {
  79. this.column.scrollTop();
  80. }
  81. setColumnRef = c => {
  82. this.column = c;
  83. }
  84. handleMoveUp = id => {
  85. const elementIndex = this.props.notifications.findIndex(item => item !== null && item.get('id') === id) - 1;
  86. this._selectChild(elementIndex);
  87. }
  88. handleMoveDown = id => {
  89. const elementIndex = this.props.notifications.findIndex(item => item !== null && item.get('id') === id) + 1;
  90. this._selectChild(elementIndex);
  91. }
  92. _selectChild (index) {
  93. const element = this.column.node.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
  94. if (element) {
  95. element.focus();
  96. }
  97. }
  98. render () {
  99. const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore } = this.props;
  100. const pinned = !!columnId;
  101. const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />;
  102. let scrollableContent = null;
  103. if (isLoading && this.scrollableContent) {
  104. scrollableContent = this.scrollableContent;
  105. } else if (notifications.size > 0 || hasMore) {
  106. scrollableContent = notifications.map((item, index) => item === null ? (
  107. <LoadGap
  108. key={'gap:' + notifications.getIn([index + 1, 'id'])}
  109. disabled={isLoading}
  110. maxId={index > 0 ? notifications.getIn([index - 1, 'id']) : null}
  111. onClick={this.handleLoadGap}
  112. />
  113. ) : (
  114. <NotificationContainer
  115. key={item.get('id')}
  116. notification={item}
  117. accountId={item.get('account')}
  118. onMoveUp={this.handleMoveUp}
  119. onMoveDown={this.handleMoveDown}
  120. />
  121. ));
  122. } else {
  123. scrollableContent = null;
  124. }
  125. this.scrollableContent = scrollableContent;
  126. const scrollContainer = (
  127. <ScrollableList
  128. scrollKey={`notifications-${columnId}`}
  129. trackScroll={!pinned}
  130. isLoading={isLoading}
  131. hasMore={hasMore}
  132. emptyMessage={emptyMessage}
  133. onLoadMore={this.handleLoadOlder}
  134. onScrollToTop={this.handleScrollToTop}
  135. onScroll={this.handleScroll}
  136. shouldUpdateScroll={shouldUpdateScroll}
  137. >
  138. {scrollableContent}
  139. </ScrollableList>
  140. );
  141. return (
  142. <Column ref={this.setColumnRef}>
  143. <ColumnHeader
  144. icon='bell'
  145. active={isUnread}
  146. title={intl.formatMessage(messages.title)}
  147. onPin={this.handlePin}
  148. onMove={this.handleMove}
  149. onClick={this.handleHeaderClick}
  150. pinned={pinned}
  151. multiColumn={multiColumn}
  152. >
  153. <ColumnSettingsContainer />
  154. </ColumnHeader>
  155. {scrollContainer}
  156. </Column>
  157. );
  158. }
  159. }