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.

105 lines
3.5 KiB

  1. import {
  2. ACCOUNT_BLOCK_SUCCESS,
  3. ACCOUNT_MUTE_SUCCESS,
  4. } from 'flavours/glitch/actions/accounts';
  5. import { CONTEXT_FETCH_SUCCESS } from 'flavours/glitch/actions/statuses';
  6. import { TIMELINE_DELETE, TIMELINE_UPDATE } from 'flavours/glitch/actions/timelines';
  7. import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
  8. import compareId from 'flavours/glitch/util/compare_id';
  9. const initialState = ImmutableMap({
  10. inReplyTos: ImmutableMap(),
  11. replies: ImmutableMap(),
  12. });
  13. const normalizeContext = (immutableState, id, ancestors, descendants) => immutableState.withMutations(state => {
  14. state.update('inReplyTos', immutableAncestors => immutableAncestors.withMutations(inReplyTos => {
  15. state.update('replies', immutableDescendants => immutableDescendants.withMutations(replies => {
  16. function addReply({ id, in_reply_to_id }) {
  17. if (in_reply_to_id && !inReplyTos.has(id)) {
  18. replies.update(in_reply_to_id, ImmutableList(), siblings => {
  19. const index = siblings.findLastIndex(sibling => compareId(sibling, id) < 0);
  20. return siblings.insert(index + 1, id);
  21. });
  22. inReplyTos.set(id, in_reply_to_id);
  23. }
  24. }
  25. // We know in_reply_to_id of statuses but `id` itself.
  26. // So we assume that the status of the id replies to last ancestors.
  27. ancestors.forEach(addReply);
  28. if (ancestors[0]) {
  29. addReply({ id, in_reply_to_id: ancestors[ancestors.length - 1].id });
  30. }
  31. descendants.forEach(addReply);
  32. }));
  33. }));
  34. });
  35. const deleteFromContexts = (immutableState, ids) => immutableState.withMutations(state => {
  36. state.update('inReplyTos', immutableAncestors => immutableAncestors.withMutations(inReplyTos => {
  37. state.update('replies', immutableDescendants => immutableDescendants.withMutations(replies => {
  38. ids.forEach(id => {
  39. const inReplyToIdOfId = inReplyTos.get(id);
  40. const repliesOfId = replies.get(id);
  41. const siblings = replies.get(inReplyToIdOfId);
  42. if (siblings) {
  43. replies.set(inReplyToIdOfId, siblings.filterNot(sibling => sibling === id));
  44. }
  45. if (repliesOfId) {
  46. repliesOfId.forEach(reply => inReplyTos.delete(reply));
  47. }
  48. inReplyTos.delete(id);
  49. replies.delete(id);
  50. });
  51. }));
  52. }));
  53. });
  54. const filterContexts = (state, relationship, statuses) => {
  55. const ownedStatusIds = statuses.filter(status => status.get('account') === relationship.id)
  56. .map(status => status.get('id'));
  57. return deleteFromContexts(state, ownedStatusIds);
  58. };
  59. const updateContext = (state, status) => {
  60. if (status.in_reply_to_id) {
  61. return state.withMutations(mutable => {
  62. const replies = mutable.getIn(['replies', status.in_reply_to_id], ImmutableList());
  63. mutable.setIn(['inReplyTos', status.id], status.in_reply_to_id);
  64. if (!replies.includes(status.id)) {
  65. mutable.setIn(['replies', status.in_reply_to_id], replies.push(status.id));
  66. }
  67. });
  68. }
  69. return state;
  70. };
  71. export default function replies(state = initialState, action) {
  72. switch(action.type) {
  73. case ACCOUNT_BLOCK_SUCCESS:
  74. case ACCOUNT_MUTE_SUCCESS:
  75. return filterContexts(state, action.relationship, action.statuses);
  76. case CONTEXT_FETCH_SUCCESS:
  77. return normalizeContext(state, action.id, action.ancestors, action.descendants);
  78. case TIMELINE_DELETE:
  79. return deleteFromContexts(state, [action.id]);
  80. case TIMELINE_UPDATE:
  81. return updateContext(state, action.status);
  82. default:
  83. return state;
  84. }
  85. };