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.
 
 
 
 

119 lines
4.0 KiB

  1. import {
  2. ANNOUNCEMENTS_FETCH_REQUEST,
  3. ANNOUNCEMENTS_FETCH_SUCCESS,
  4. ANNOUNCEMENTS_FETCH_FAIL,
  5. ANNOUNCEMENTS_UPDATE,
  6. ANNOUNCEMENTS_REACTION_UPDATE,
  7. ANNOUNCEMENTS_REACTION_ADD_REQUEST,
  8. ANNOUNCEMENTS_REACTION_ADD_FAIL,
  9. ANNOUNCEMENTS_REACTION_REMOVE_REQUEST,
  10. ANNOUNCEMENTS_REACTION_REMOVE_FAIL,
  11. ANNOUNCEMENTS_TOGGLE_SHOW,
  12. ANNOUNCEMENTS_DELETE,
  13. } from '../actions/announcements';
  14. import { Map as ImmutableMap, List as ImmutableList, Set as ImmutableSet, fromJS } from 'immutable';
  15. const initialState = ImmutableMap({
  16. items: ImmutableList(),
  17. isLoading: false,
  18. show: false,
  19. unread: ImmutableSet(),
  20. });
  21. const updateReaction = (state, id, name, updater) => state.update('items', list => list.map(announcement => {
  22. if (announcement.get('id') === id) {
  23. return announcement.update('reactions', reactions => {
  24. const idx = reactions.findIndex(reaction => reaction.get('name') === name);
  25. if (idx > -1) {
  26. return reactions.update(idx, reaction => updater(reaction));
  27. }
  28. return reactions.push(updater(fromJS({ name, count: 0 })));
  29. });
  30. }
  31. return announcement;
  32. }));
  33. const updateReactionCount = (state, reaction) => updateReaction(state, reaction.announcement_id, reaction.name, x => x.set('count', reaction.count));
  34. const addReaction = (state, id, name) => updateReaction(state, id, name, x => x.set('me', true).update('count', y => y + 1));
  35. const removeReaction = (state, id, name) => updateReaction(state, id, name, x => x.set('me', false).update('count', y => y - 1));
  36. const addUnread = (state, items) => {
  37. if (state.get('show')) {
  38. return state;
  39. }
  40. const newIds = ImmutableSet(items.map(x => x.get('id')));
  41. const oldIds = ImmutableSet(state.get('items').map(x => x.get('id')));
  42. return state.update('unread', unread => unread.union(newIds.subtract(oldIds)));
  43. };
  44. const sortAnnouncements = list => list.sortBy(x => x.get('starts_at') || x.get('published_at'));
  45. const updateAnnouncement = (state, announcement) => {
  46. const idx = state.get('items').findIndex(x => x.get('id') === announcement.get('id'));
  47. state = addUnread(state, [announcement]);
  48. if (idx > -1) {
  49. // Deep merge is used because announcements from the streaming API do not contain
  50. // personalized data about which reactions have been selected by the given user,
  51. // and that is information we want to preserve
  52. return state.update('items', list => sortAnnouncements(list.update(idx, x => x.mergeDeep(announcement))));
  53. }
  54. return state.update('items', list => sortAnnouncements(list.unshift(announcement)));
  55. };
  56. export default function announcementsReducer(state = initialState, action) {
  57. switch(action.type) {
  58. case ANNOUNCEMENTS_TOGGLE_SHOW:
  59. return state.withMutations(map => {
  60. if (!map.get('show')) map.set('unread', ImmutableSet());
  61. map.set('show', !map.get('show'));
  62. });
  63. case ANNOUNCEMENTS_FETCH_REQUEST:
  64. return state.set('isLoading', true);
  65. case ANNOUNCEMENTS_FETCH_SUCCESS:
  66. return state.withMutations(map => {
  67. const items = fromJS(action.announcements);
  68. map.set('unread', ImmutableSet());
  69. addUnread(map, items);
  70. map.set('items', items);
  71. map.set('isLoading', false);
  72. });
  73. case ANNOUNCEMENTS_FETCH_FAIL:
  74. return state.set('isLoading', false);
  75. case ANNOUNCEMENTS_UPDATE:
  76. return updateAnnouncement(state, fromJS(action.announcement));
  77. case ANNOUNCEMENTS_REACTION_UPDATE:
  78. return updateReactionCount(state, action.reaction);
  79. case ANNOUNCEMENTS_REACTION_ADD_REQUEST:
  80. case ANNOUNCEMENTS_REACTION_REMOVE_FAIL:
  81. return addReaction(state, action.id, action.name);
  82. case ANNOUNCEMENTS_REACTION_REMOVE_REQUEST:
  83. case ANNOUNCEMENTS_REACTION_ADD_FAIL:
  84. return removeReaction(state, action.id, action.name);
  85. case ANNOUNCEMENTS_DELETE:
  86. return state.update('unread', set => set.delete(action.id)).update('items', list => {
  87. const idx = list.findIndex(x => x.get('id') === action.id);
  88. if (idx > -1) {
  89. return list.delete(idx);
  90. }
  91. return list;
  92. });
  93. default:
  94. return state;
  95. }
  96. };