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.

120 lines
3.5 KiB

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