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.

182 lines
5.8 KiB

  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. FAVOURITE_SUCCESS
  10. } from '../actions/interactions';
  11. import {
  12. ACCOUNT_SET_SELF,
  13. ACCOUNT_FETCH_SUCCESS,
  14. ACCOUNT_FOLLOW_SUCCESS,
  15. ACCOUNT_UNFOLLOW_SUCCESS,
  16. ACCOUNT_TIMELINE_FETCH_SUCCESS
  17. } from '../actions/accounts';
  18. import { STATUS_FETCH_SUCCESS } from '../actions/statuses';
  19. import { FOLLOW_SUBMIT_SUCCESS } from '../actions/follow';
  20. import Immutable from 'immutable';
  21. const initialState = Immutable.Map({
  22. home: Immutable.List([]),
  23. mentions: Immutable.List([]),
  24. statuses: Immutable.Map(),
  25. accounts: Immutable.Map(),
  26. accounts_timelines: Immutable.Map(),
  27. me: null,
  28. ancestors: Immutable.Map(),
  29. descendants: Immutable.Map()
  30. });
  31. export function selectStatus(state, id) {
  32. let status = state.getIn(['timelines', 'statuses', id], null);
  33. if (status === null) {
  34. return null;
  35. }
  36. status = status.set('account', state.getIn(['timelines', 'accounts', status.get('account')]));
  37. if (status.get('reblog') !== null) {
  38. status = status.set('reblog', selectStatus(state, status.get('reblog')));
  39. }
  40. return status;
  41. };
  42. function normalizeStatus(state, status) {
  43. // Separate account
  44. let account = status.get('account');
  45. status = status.set('account', account.get('id'));
  46. // Separate reblog, repeat for reblog
  47. let reblog = status.get('reblog');
  48. if (reblog !== null) {
  49. status = status.set('reblog', reblog.get('id'));
  50. state = normalizeStatus(state, reblog);
  51. }
  52. // Replies
  53. if (status.get('in_reply_to_id')) {
  54. state = state.updateIn(['descendants', status.get('in_reply_to_id')], set => {
  55. if (!Immutable.OrderedSet.isOrderedSet(set)) {
  56. return Immutable.OrderedSet([status.get('id')]);
  57. } else {
  58. return set.add(status.get('id'));
  59. }
  60. });
  61. }
  62. return state.withMutations(map => {
  63. if (status.get('in_reply_to_id')) {
  64. map.updateIn(['descendants', status.get('in_reply_to_id')], Immutable.OrderedSet(), set => set.add(status.get('id')));
  65. map.updateIn(['ancestors', status.get('id')], Immutable.OrderedSet(), set => set.add(status.get('in_reply_to_id')));
  66. }
  67. map.setIn(['accounts', account.get('id')], account);
  68. map.setIn(['statuses', status.get('id')], status);
  69. });
  70. };
  71. function normalizeTimeline(state, timeline, statuses) {
  72. statuses.forEach((status, i) => {
  73. state = normalizeStatus(state, status);
  74. state = state.setIn([timeline, i], status.get('id'));
  75. });
  76. return state;
  77. };
  78. function appendNormalizedTimeline(state, timeline, statuses) {
  79. let moreIds = Immutable.List();
  80. statuses.forEach((status, i) => {
  81. state = normalizeStatus(state, status);
  82. moreIds = moreIds.set(i, status.get('id'));
  83. });
  84. return state.update(timeline, list => list.push(...moreIds));
  85. };
  86. function normalizeAccountTimeline(state, accountId, statuses) {
  87. statuses.forEach((status, i) => {
  88. state = normalizeStatus(state, status);
  89. state = state.updateIn(['accounts_timelines', accountId], Immutable.List(), list => list.set(i, status.get('id')));
  90. });
  91. return state;
  92. };
  93. function updateTimeline(state, timeline, status) {
  94. state = normalizeStatus(state, status);
  95. state = state.update(timeline, list => list.unshift(status.get('id')));
  96. state = state.updateIn(['accounts_timelines', status.getIn(['account', 'id'])], Immutable.List(), list => list.unshift(status.get('id')));
  97. return state;
  98. };
  99. function deleteStatus(state, id) {
  100. ['home', 'mentions'].forEach(function (timeline) {
  101. state = state.update(timeline, list => list.filterNot(item => item === id));
  102. });
  103. return state.deleteIn(['statuses', id]);
  104. };
  105. function normalizeAccount(state, account) {
  106. return state.setIn(['accounts', account.get('id')], account);
  107. };
  108. function normalizeContext(state, status, ancestors, descendants) {
  109. state = normalizeStatus(state, status);
  110. let ancestorsIds = ancestors.map(ancestor => {
  111. state = normalizeStatus(state, ancestor);
  112. return ancestor.get('id');
  113. }).toOrderedSet();
  114. let descendantsIds = descendants.map(descendant => {
  115. state = normalizeStatus(state, descendant);
  116. return descendant.get('id');
  117. }).toOrderedSet();
  118. return state.withMutations(map => {
  119. map.setIn(['ancestors', status.get('id')], ancestorsIds);
  120. map.setIn(['descendants', status.get('id')], descendantsIds);
  121. });
  122. };
  123. export default function timelines(state = initialState, action) {
  124. switch(action.type) {
  125. case TIMELINE_REFRESH_SUCCESS:
  126. return normalizeTimeline(state, action.timeline, Immutable.fromJS(action.statuses));
  127. case TIMELINE_EXPAND_SUCCESS:
  128. return appendNormalizedTimeline(state, action.timeline, Immutable.fromJS(action.statuses));
  129. case TIMELINE_UPDATE:
  130. return updateTimeline(state, action.timeline, Immutable.fromJS(action.status));
  131. case TIMELINE_DELETE:
  132. return deleteStatus(state, action.id);
  133. case REBLOG_SUCCESS:
  134. case FAVOURITE_SUCCESS:
  135. return normalizeStatus(state, Immutable.fromJS(action.response));
  136. case ACCOUNT_SET_SELF:
  137. return state.withMutations(map => {
  138. map.setIn(['accounts', action.account.id], Immutable.fromJS(action.account));
  139. map.set('me', action.account.id);
  140. });
  141. case ACCOUNT_FETCH_SUCCESS:
  142. case FOLLOW_SUBMIT_SUCCESS:
  143. case ACCOUNT_FOLLOW_SUCCESS:
  144. case ACCOUNT_UNFOLLOW_SUCCESS:
  145. return normalizeAccount(state, Immutable.fromJS(action.account));
  146. case STATUS_FETCH_SUCCESS:
  147. return normalizeContext(state, Immutable.fromJS(action.status), Immutable.fromJS(action.context.ancestors), Immutable.fromJS(action.context.descendants));
  148. case ACCOUNT_TIMELINE_FETCH_SUCCESS:
  149. return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
  150. default:
  151. return state;
  152. }
  153. };