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.

172 lines
5.6 KiB

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