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.

167 lines
5.5 KiB

7 years ago
7 years ago
  1. import {
  2. TIMELINE_REFRESH_SUCCESS,
  3. TIMELINE_UPDATE,
  4. TIMELINE_DELETE,
  5. TIMELINE_EXPAND_SUCCESS
  6. } from '../actions/timelines';
  7. import {
  8. REBLOG_SUCCESS,
  9. UNREBLOG_SUCCESS,
  10. FAVOURITE_SUCCESS,
  11. UNFAVOURITE_SUCCESS
  12. } from '../actions/interactions';
  13. import {
  14. ACCOUNT_FETCH_SUCCESS,
  15. ACCOUNT_TIMELINE_FETCH_SUCCESS,
  16. ACCOUNT_TIMELINE_EXPAND_SUCCESS
  17. } from '../actions/accounts';
  18. import {
  19. STATUS_FETCH_SUCCESS,
  20. CONTEXT_FETCH_SUCCESS
  21. } from '../actions/statuses';
  22. import Immutable from 'immutable';
  23. const initialState = Immutable.Map({
  24. home: Immutable.List(),
  25. mentions: Immutable.List(),
  26. public: Immutable.List(),
  27. accounts_timelines: Immutable.Map(),
  28. ancestors: Immutable.Map(),
  29. descendants: Immutable.Map()
  30. });
  31. const normalizeStatus = (state, status) => {
  32. const replyToId = status.get('in_reply_to_id');
  33. const id = status.get('id');
  34. if (replyToId) {
  35. if (!state.getIn(['descendants', replyToId], Immutable.List()).includes(id)) {
  36. state = state.updateIn(['descendants', replyToId], Immutable.List(), set => set.push(id));
  37. }
  38. if (!state.getIn(['ancestors', id], Immutable.List()).includes(replyToId)) {
  39. state = state.updateIn(['ancestors', id], Immutable.List(), set => set.push(replyToId));
  40. }
  41. }
  42. return state;
  43. };
  44. const normalizeTimeline = (state, timeline, statuses, replace = false) => {
  45. let ids = Immutable.List();
  46. statuses.forEach((status, i) => {
  47. state = normalizeStatus(state, status);
  48. ids = ids.set(i, status.get('id'));
  49. });
  50. return state.update(timeline, list => (replace ? ids : list.unshift(...ids)));
  51. };
  52. const appendNormalizedTimeline = (state, timeline, statuses) => {
  53. let moreIds = Immutable.List();
  54. statuses.forEach((status, i) => {
  55. state = normalizeStatus(state, status);
  56. moreIds = moreIds.set(i, status.get('id'));
  57. });
  58. return state.update(timeline, list => list.push(...moreIds));
  59. };
  60. const normalizeAccountTimeline = (state, accountId, statuses, replace = false) => {
  61. let ids = Immutable.List();
  62. statuses.forEach((status, i) => {
  63. state = normalizeStatus(state, status);
  64. ids = ids.set(i, status.get('id'));
  65. });
  66. return state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => (replace ? ids : list.unshift(...ids)));
  67. };
  68. const appendNormalizedAccountTimeline = (state, accountId, statuses) => {
  69. let moreIds = Immutable.List([]);
  70. statuses.forEach((status, i) => {
  71. state = normalizeStatus(state, status);
  72. moreIds = moreIds.set(i, status.get('id'));
  73. });
  74. return state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => list.push(...moreIds));
  75. };
  76. const updateTimeline = (state, timeline, status, references) => {
  77. state = normalizeStatus(state, status);
  78. state = state.update(timeline, list => {
  79. const reblogOfId = status.getIn(['reblog', 'id'], null);
  80. if (reblogOfId !== null) {
  81. list = list.filterNot(itemId => references.includes(itemId));
  82. }
  83. return list.unshift(status.get('id'));
  84. });
  85. return state;
  86. };
  87. const deleteStatus = (state, id, accountId, references) => {
  88. // Remove references from timelines
  89. ['home', 'mentions', 'public'].forEach(function (timeline) {
  90. state = state.update(timeline, list => list.filterNot(item => item === id));
  91. });
  92. // Remove references from account timelines
  93. state = state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => list.filterNot(item => item === id));
  94. // Remove references from context
  95. state.getIn(['descendants', id], Immutable.List()).forEach(descendantId => {
  96. state = state.updateIn(['ancestors', descendantId], Immutable.List(), list => list.filterNot(itemId => itemId === id));
  97. });
  98. state.getIn(['ancestors', id], Immutable.List()).forEach(ancestorId => {
  99. state = state.updateIn(['descendants', ancestorId], Immutable.List(), list => list.filterNot(itemId => itemId === id));
  100. });
  101. state = state.deleteIn(['descendants', id]).deleteIn(['ancestors', id]);
  102. // Remove reblogs of deleted status
  103. references.forEach(ref => {
  104. state = deleteStatus(state, ref[0], ref[1], []);
  105. });
  106. return state;
  107. };
  108. const normalizeContext = (state, id, ancestors, descendants) => {
  109. const ancestorsIds = ancestors.map(ancestor => ancestor.get('id'));
  110. const descendantsIds = descendants.map(descendant => descendant.get('id'));
  111. return state.withMutations(map => {
  112. map.setIn(['ancestors', id], ancestorsIds);
  113. map.setIn(['descendants', id], descendantsIds);
  114. });
  115. };
  116. export default function timelines(state = initialState, action) {
  117. switch(action.type) {
  118. case TIMELINE_REFRESH_SUCCESS:
  119. return normalizeTimeline(state, action.timeline, Immutable.fromJS(action.statuses), action.replace);
  120. case TIMELINE_EXPAND_SUCCESS:
  121. return appendNormalizedTimeline(state, action.timeline, Immutable.fromJS(action.statuses));
  122. case TIMELINE_UPDATE:
  123. return updateTimeline(state, action.timeline, Immutable.fromJS(action.status), action.references);
  124. case TIMELINE_DELETE:
  125. return deleteStatus(state, action.id, action.accountId, action.references);
  126. case CONTEXT_FETCH_SUCCESS:
  127. return normalizeContext(state, action.id, Immutable.fromJS(action.ancestors), Immutable.fromJS(action.descendants));
  128. case ACCOUNT_TIMELINE_FETCH_SUCCESS:
  129. return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses), action.replace);
  130. case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
  131. return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
  132. default:
  133. return state;
  134. }
  135. };