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.
 
 
 
 

149 line
5.1 KiB

  1. import {
  2. NOTIFICATIONS_UPDATE,
  3. NOTIFICATIONS_EXPAND_SUCCESS,
  4. NOTIFICATIONS_EXPAND_REQUEST,
  5. NOTIFICATIONS_EXPAND_FAIL,
  6. NOTIFICATIONS_FILTER_SET,
  7. NOTIFICATIONS_CLEAR,
  8. NOTIFICATIONS_SCROLL_TOP,
  9. NOTIFICATIONS_LOAD_PENDING,
  10. NOTIFICATIONS_MOUNT,
  11. NOTIFICATIONS_UNMOUNT,
  12. } from '../actions/notifications';
  13. import {
  14. ACCOUNT_BLOCK_SUCCESS,
  15. ACCOUNT_MUTE_SUCCESS,
  16. } from '../actions/accounts';
  17. import { DOMAIN_BLOCK_SUCCESS } from 'mastodon/actions/domain_blocks';
  18. import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from '../actions/timelines';
  19. import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
  20. import compareId from '../compare_id';
  21. const initialState = ImmutableMap({
  22. pendingItems: ImmutableList(),
  23. items: ImmutableList(),
  24. hasMore: true,
  25. top: false,
  26. mounted: false,
  27. unread: 0,
  28. isLoading: false,
  29. });
  30. const notificationToMap = notification => ImmutableMap({
  31. id: notification.id,
  32. type: notification.type,
  33. account: notification.account.id,
  34. created_at: notification.created_at,
  35. status: notification.status ? notification.status.id : null,
  36. });
  37. const normalizeNotification = (state, notification, usePendingItems) => {
  38. const top = state.get('top');
  39. const mounted = state.get('mounted');
  40. if (usePendingItems || (!top && mounted) || !state.get('pendingItems').isEmpty()) {
  41. return state.update('pendingItems', list => list.unshift(notificationToMap(notification))).update('unread', unread => unread + 1);
  42. }
  43. if (!top) {
  44. state = state.update('unread', unread => unread + 1);
  45. }
  46. return state.update('items', list => {
  47. if (top && list.size > 40) {
  48. list = list.take(20);
  49. }
  50. return list.unshift(notificationToMap(notification));
  51. });
  52. };
  53. const expandNormalizedNotifications = (state, notifications, next, isLoadingRecent, usePendingItems) => {
  54. let items = ImmutableList();
  55. notifications.forEach((n, i) => {
  56. items = items.set(i, notificationToMap(n));
  57. });
  58. return state.withMutations(mutable => {
  59. if (!items.isEmpty()) {
  60. usePendingItems = isLoadingRecent && (usePendingItems || (!mutable.get('top') && mutable.get('mounted')) || !mutable.get('pendingItems').isEmpty());
  61. mutable.update(usePendingItems ? 'pendingItems' : 'items', list => {
  62. const lastIndex = 1 + list.findLastIndex(
  63. item => item !== null && (compareId(item.get('id'), items.last().get('id')) > 0 || item.get('id') === items.last().get('id'))
  64. );
  65. const firstIndex = 1 + list.take(lastIndex).findLastIndex(
  66. item => item !== null && compareId(item.get('id'), items.first().get('id')) > 0
  67. );
  68. return list.take(firstIndex).concat(items, list.skip(lastIndex));
  69. });
  70. }
  71. if (!next) {
  72. mutable.set('hasMore', false);
  73. }
  74. mutable.set('isLoading', false);
  75. });
  76. };
  77. const filterNotifications = (state, accountIds) => {
  78. const helper = list => list.filterNot(item => item !== null && accountIds.includes(item.get('account')));
  79. return state.update('items', helper).update('pendingItems', helper);
  80. };
  81. const updateTop = (state, top) => {
  82. if (top) {
  83. state = state.set('unread', state.get('pendingItems').size);
  84. }
  85. return state.set('top', top);
  86. };
  87. const deleteByStatus = (state, statusId) => {
  88. const helper = list => list.filterNot(item => item !== null && item.get('status') === statusId);
  89. return state.update('items', helper).update('pendingItems', helper);
  90. };
  91. export default function notifications(state = initialState, action) {
  92. switch(action.type) {
  93. case NOTIFICATIONS_LOAD_PENDING:
  94. return state.update('items', list => state.get('pendingItems').concat(list.take(40))).set('pendingItems', ImmutableList()).set('unread', 0);
  95. case NOTIFICATIONS_EXPAND_REQUEST:
  96. return state.set('isLoading', true);
  97. case NOTIFICATIONS_EXPAND_FAIL:
  98. return state.set('isLoading', false);
  99. case NOTIFICATIONS_FILTER_SET:
  100. return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', true);
  101. case NOTIFICATIONS_SCROLL_TOP:
  102. return updateTop(state, action.top);
  103. case NOTIFICATIONS_UPDATE:
  104. return normalizeNotification(state, action.notification, action.usePendingItems);
  105. case NOTIFICATIONS_EXPAND_SUCCESS:
  106. return expandNormalizedNotifications(state, action.notifications, action.next, action.isLoadingRecent, action.usePendingItems);
  107. case ACCOUNT_BLOCK_SUCCESS:
  108. return filterNotifications(state, [action.relationship.id]);
  109. case ACCOUNT_MUTE_SUCCESS:
  110. return action.relationship.muting_notifications ? filterNotifications(state, [action.relationship.id]) : state;
  111. case DOMAIN_BLOCK_SUCCESS:
  112. return filterNotifications(state, action.accounts);
  113. case NOTIFICATIONS_CLEAR:
  114. return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', false);
  115. case TIMELINE_DELETE:
  116. return deleteByStatus(state, action.id);
  117. case TIMELINE_DISCONNECT:
  118. return action.timeline === 'home' ?
  119. state.update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) :
  120. state;
  121. case NOTIFICATIONS_MOUNT:
  122. return state.set('mounted', true);
  123. case NOTIFICATIONS_UNMOUNT:
  124. return state.set('mounted', false);
  125. default:
  126. return state;
  127. }
  128. };