Introduce flat layout to contexts reducer (#7150)
This allows to filter out replies in threads even if contexts of those replies are not fetched.
This commit is contained in:
		
							parent
							
								
									8182b61518
								
							
						
					
					
						commit
						023fe5181b
					
				
					 3 changed files with 101 additions and 60 deletions
				
			
		| 
						 | 
				
			
			@ -13,21 +13,9 @@ export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
 | 
			
		|||
 | 
			
		||||
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
 | 
			
		||||
 | 
			
		||||
export const TIMELINE_CONTEXT_UPDATE = 'CONTEXT_UPDATE';
 | 
			
		||||
 | 
			
		||||
export function updateTimeline(timeline, status) {
 | 
			
		||||
  return (dispatch, getState) => {
 | 
			
		||||
    const references = status.reblog ? getState().get('statuses').filter((item, itemId) => (itemId === status.reblog.id || item.get('reblog') === status.reblog.id)).map((_, itemId) => itemId) : [];
 | 
			
		||||
    const parents = [];
 | 
			
		||||
 | 
			
		||||
    if (status.in_reply_to_id) {
 | 
			
		||||
      let parent = getState().getIn(['statuses', status.in_reply_to_id]);
 | 
			
		||||
 | 
			
		||||
      while (parent && parent.get('in_reply_to_id')) {
 | 
			
		||||
        parents.push(parent.get('id'));
 | 
			
		||||
        parent = getState().getIn(['statuses', parent.get('in_reply_to_id')]);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dispatch(importFetchedStatus(status));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,14 +25,6 @@ export function updateTimeline(timeline, status) {
 | 
			
		|||
      status,
 | 
			
		||||
      references,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (parents.length > 0) {
 | 
			
		||||
      dispatch({
 | 
			
		||||
        type: TIMELINE_CONTEXT_UPDATE,
 | 
			
		||||
        status,
 | 
			
		||||
        references: parents,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
import Immutable from 'immutable';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { connect } from 'react-redux';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
| 
						 | 
				
			
			@ -54,12 +55,48 @@ const messages = defineMessages({
 | 
			
		|||
const makeMapStateToProps = () => {
 | 
			
		||||
  const getStatus = makeGetStatus();
 | 
			
		||||
 | 
			
		||||
  const mapStateToProps = (state, props) => ({
 | 
			
		||||
    status: getStatus(state, props.params.statusId),
 | 
			
		||||
    ancestorsIds: state.getIn(['contexts', 'ancestors', props.params.statusId]),
 | 
			
		||||
    descendantsIds: state.getIn(['contexts', 'descendants', props.params.statusId]),
 | 
			
		||||
  const mapStateToProps = (state, props) => {
 | 
			
		||||
    const status = getStatus(state, props.params.statusId);
 | 
			
		||||
    let ancestorsIds = Immutable.List();
 | 
			
		||||
    let descendantsIds = Immutable.List();
 | 
			
		||||
 | 
			
		||||
    if (status) {
 | 
			
		||||
      ancestorsIds = ancestorsIds.withMutations(mutable => {
 | 
			
		||||
        function addAncestor(id) {
 | 
			
		||||
          if (id) {
 | 
			
		||||
            const inReplyTo = state.getIn(['contexts', 'inReplyTos', id]);
 | 
			
		||||
 | 
			
		||||
            mutable.unshift(id);
 | 
			
		||||
            addAncestor(inReplyTo);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        addAncestor(status.get('in_reply_to_id'));
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      descendantsIds = descendantsIds.withMutations(mutable => {
 | 
			
		||||
        function addDescendantOf(id) {
 | 
			
		||||
          const replies = state.getIn(['contexts', 'replies', id]);
 | 
			
		||||
 | 
			
		||||
          if (replies) {
 | 
			
		||||
            replies.forEach(reply => {
 | 
			
		||||
              mutable.push(reply);
 | 
			
		||||
              addDescendantOf(reply);
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        addDescendantOf(status.get('id'));
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      status,
 | 
			
		||||
      ancestorsIds,
 | 
			
		||||
      descendantsIds,
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return mapStateToProps;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,38 +3,62 @@ import {
 | 
			
		|||
  ACCOUNT_MUTE_SUCCESS,
 | 
			
		||||
} from '../actions/accounts';
 | 
			
		||||
import { CONTEXT_FETCH_SUCCESS } from '../actions/statuses';
 | 
			
		||||
import { TIMELINE_DELETE, TIMELINE_CONTEXT_UPDATE } from '../actions/timelines';
 | 
			
		||||
import { TIMELINE_DELETE, TIMELINE_UPDATE } from '../actions/timelines';
 | 
			
		||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
 | 
			
		||||
 | 
			
		||||
const initialState = ImmutableMap({
 | 
			
		||||
  ancestors: ImmutableMap(),
 | 
			
		||||
  descendants: ImmutableMap(),
 | 
			
		||||
  inReplyTos: ImmutableMap(),
 | 
			
		||||
  replies: ImmutableMap(),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const normalizeContext = (state, id, ancestors, descendants) => {
 | 
			
		||||
  const ancestorsIds   = ImmutableList(ancestors.map(ancestor => ancestor.id));
 | 
			
		||||
  const descendantsIds = ImmutableList(descendants.map(descendant => descendant.id));
 | 
			
		||||
const normalizeContext = (immutableState, id, ancestors, descendants) => immutableState.withMutations(state => {
 | 
			
		||||
  state.update('inReplyTos', immutableAncestors => immutableAncestors.withMutations(inReplyTos => {
 | 
			
		||||
    state.update('replies', immutableDescendants => immutableDescendants.withMutations(replies => {
 | 
			
		||||
      function addReply({ id, in_reply_to_id }) {
 | 
			
		||||
        if (in_reply_to_id) {
 | 
			
		||||
          const siblings = replies.get(in_reply_to_id, ImmutableList());
 | 
			
		||||
 | 
			
		||||
  return state.withMutations(map => {
 | 
			
		||||
    map.setIn(['ancestors', id], ancestorsIds);
 | 
			
		||||
    map.setIn(['descendants', id], descendantsIds);
 | 
			
		||||
          if (!siblings.includes(id)) {
 | 
			
		||||
            const index = siblings.findLastIndex(sibling => sibling.id < id);
 | 
			
		||||
            replies.set(in_reply_to_id, siblings.insert(index + 1, id));
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          inReplyTos.set(id, in_reply_to_id);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (ancestors[0]) {
 | 
			
		||||
        addReply({ id, in_reply_to_id: ancestors[0].id });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (descendants[0]) {
 | 
			
		||||
        addReply({ id: descendants[0].id, in_reply_to_id: id });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      [ancestors, descendants].forEach(statuses => statuses.forEach(addReply));
 | 
			
		||||
    }));
 | 
			
		||||
  }));
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const deleteFromContexts = (immutableState, ids) => immutableState.withMutations(state => {
 | 
			
		||||
  state.update('ancestors', immutableAncestors => immutableAncestors.withMutations(ancestors => {
 | 
			
		||||
    state.update('descendants', immutableDescendants => immutableDescendants.withMutations(descendants => {
 | 
			
		||||
  state.update('inReplyTos', immutableAncestors => immutableAncestors.withMutations(inReplyTos => {
 | 
			
		||||
    state.update('replies', immutableDescendants => immutableDescendants.withMutations(replies => {
 | 
			
		||||
      ids.forEach(id => {
 | 
			
		||||
        descendants.get(id, ImmutableList()).forEach(descendantId => {
 | 
			
		||||
          ancestors.update(descendantId, ImmutableList(), list => list.filterNot(itemId => itemId === id));
 | 
			
		||||
        });
 | 
			
		||||
        const inReplyToIdOfId = inReplyTos.get(id);
 | 
			
		||||
        const repliesOfId = replies.get(id);
 | 
			
		||||
        const siblings = replies.get(inReplyToIdOfId);
 | 
			
		||||
 | 
			
		||||
        ancestors.get(id, ImmutableList()).forEach(ancestorId => {
 | 
			
		||||
          descendants.update(ancestorId, ImmutableList(), list => list.filterNot(itemId => itemId === id));
 | 
			
		||||
        });
 | 
			
		||||
        if (siblings) {
 | 
			
		||||
          replies.set(inReplyToIdOfId, siblings.filterNot(sibling => sibling === id));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        descendants.delete(id);
 | 
			
		||||
        ancestors.delete(id);
 | 
			
		||||
 | 
			
		||||
        if (repliesOfId) {
 | 
			
		||||
          repliesOfId.forEach(reply => inReplyTos.delete(reply));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        inReplyTos.delete(id);
 | 
			
		||||
        replies.delete(id);
 | 
			
		||||
      });
 | 
			
		||||
    }));
 | 
			
		||||
  }));
 | 
			
		||||
| 
						 | 
				
			
			@ -48,23 +72,23 @@ const filterContexts = (state, relationship, statuses) => {
 | 
			
		|||
  return deleteFromContexts(state, ownedStatusIds);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const updateContext = (state, status, references) => {
 | 
			
		||||
  return state.update('descendants', map => {
 | 
			
		||||
    references.forEach(parentId => {
 | 
			
		||||
      map = map.update(parentId, ImmutableList(), list => {
 | 
			
		||||
        if (list.includes(status.id)) {
 | 
			
		||||
          return list;
 | 
			
		||||
const updateContext = (state, status) => {
 | 
			
		||||
  if (status.in_reply_to_id) {
 | 
			
		||||
    return state.withMutations(mutable => {
 | 
			
		||||
      const replies = mutable.getIn(['replies', status.in_reply_to_id], ImmutableList());
 | 
			
		||||
 | 
			
		||||
      mutable.setIn(['inReplyTos', status.id], status.in_reply_to_id);
 | 
			
		||||
 | 
			
		||||
      if (!replies.includes(status.id)) {
 | 
			
		||||
        mutable.setIn(['replies', status.id], replies.push(status.id));
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
        return list.push(status.id);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return map;
 | 
			
		||||
  });
 | 
			
		||||
  return state;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default function contexts(state = initialState, action) {
 | 
			
		||||
export default function replies(state = initialState, action) {
 | 
			
		||||
  switch(action.type) {
 | 
			
		||||
  case ACCOUNT_BLOCK_SUCCESS:
 | 
			
		||||
  case ACCOUNT_MUTE_SUCCESS:
 | 
			
		||||
| 
						 | 
				
			
			@ -73,8 +97,8 @@ export default function contexts(state = initialState, action) {
 | 
			
		|||
    return normalizeContext(state, action.id, action.ancestors, action.descendants);
 | 
			
		||||
  case TIMELINE_DELETE:
 | 
			
		||||
    return deleteFromContexts(state, [action.id]);
 | 
			
		||||
  case TIMELINE_CONTEXT_UPDATE:
 | 
			
		||||
    return updateContext(state, action.status, action.references);
 | 
			
		||||
  case TIMELINE_UPDATE:
 | 
			
		||||
    return updateContext(state, action.status);
 | 
			
		||||
  default:
 | 
			
		||||
    return state;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue