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_DISCONNECT = 'TIMELINE_DISCONNECT';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TIMELINE_CONTEXT_UPDATE = 'CONTEXT_UPDATE';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function updateTimeline(timeline, status) {
 | 
					export function updateTimeline(timeline, status) {
 | 
				
			||||||
  return (dispatch, getState) => {
 | 
					  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 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));
 | 
					    dispatch(importFetchedStatus(status));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,14 +25,6 @@ export function updateTimeline(timeline, status) {
 | 
				
			||||||
      status,
 | 
					      status,
 | 
				
			||||||
      references,
 | 
					      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 React from 'react';
 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
| 
						 | 
					@ -54,11 +55,47 @@ const messages = defineMessages({
 | 
				
			||||||
const makeMapStateToProps = () => {
 | 
					const makeMapStateToProps = () => {
 | 
				
			||||||
  const getStatus = makeGetStatus();
 | 
					  const getStatus = makeGetStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const mapStateToProps = (state, props) => ({
 | 
					  const mapStateToProps = (state, props) => {
 | 
				
			||||||
    status: getStatus(state, props.params.statusId),
 | 
					    const status = getStatus(state, props.params.statusId);
 | 
				
			||||||
    ancestorsIds: state.getIn(['contexts', 'ancestors', props.params.statusId]),
 | 
					    let ancestorsIds = Immutable.List();
 | 
				
			||||||
    descendantsIds: state.getIn(['contexts', 'descendants', props.params.statusId]),
 | 
					    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;
 | 
					  return mapStateToProps;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,38 +3,62 @@ import {
 | 
				
			||||||
  ACCOUNT_MUTE_SUCCESS,
 | 
					  ACCOUNT_MUTE_SUCCESS,
 | 
				
			||||||
} from '../actions/accounts';
 | 
					} from '../actions/accounts';
 | 
				
			||||||
import { CONTEXT_FETCH_SUCCESS } from '../actions/statuses';
 | 
					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';
 | 
					import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initialState = ImmutableMap({
 | 
					const initialState = ImmutableMap({
 | 
				
			||||||
  ancestors: ImmutableMap(),
 | 
					  inReplyTos: ImmutableMap(),
 | 
				
			||||||
  descendants: ImmutableMap(),
 | 
					  replies: ImmutableMap(),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const normalizeContext = (state, id, ancestors, descendants) => {
 | 
					const normalizeContext = (immutableState, id, ancestors, descendants) => immutableState.withMutations(state => {
 | 
				
			||||||
  const ancestorsIds   = ImmutableList(ancestors.map(ancestor => ancestor.id));
 | 
					  state.update('inReplyTos', immutableAncestors => immutableAncestors.withMutations(inReplyTos => {
 | 
				
			||||||
  const descendantsIds = ImmutableList(descendants.map(descendant => descendant.id));
 | 
					    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 => {
 | 
					          if (!siblings.includes(id)) {
 | 
				
			||||||
    map.setIn(['ancestors', id], ancestorsIds);
 | 
					            const index = siblings.findLastIndex(sibling => sibling.id < id);
 | 
				
			||||||
    map.setIn(['descendants', id], descendantsIds);
 | 
					            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 => {
 | 
					const deleteFromContexts = (immutableState, ids) => immutableState.withMutations(state => {
 | 
				
			||||||
  state.update('ancestors', immutableAncestors => immutableAncestors.withMutations(ancestors => {
 | 
					  state.update('inReplyTos', immutableAncestors => immutableAncestors.withMutations(inReplyTos => {
 | 
				
			||||||
    state.update('descendants', immutableDescendants => immutableDescendants.withMutations(descendants => {
 | 
					    state.update('replies', immutableDescendants => immutableDescendants.withMutations(replies => {
 | 
				
			||||||
      ids.forEach(id => {
 | 
					      ids.forEach(id => {
 | 
				
			||||||
        descendants.get(id, ImmutableList()).forEach(descendantId => {
 | 
					        const inReplyToIdOfId = inReplyTos.get(id);
 | 
				
			||||||
          ancestors.update(descendantId, ImmutableList(), list => list.filterNot(itemId => itemId === id));
 | 
					        const repliesOfId = replies.get(id);
 | 
				
			||||||
        });
 | 
					        const siblings = replies.get(inReplyToIdOfId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ancestors.get(id, ImmutableList()).forEach(ancestorId => {
 | 
					        if (siblings) {
 | 
				
			||||||
          descendants.update(ancestorId, ImmutableList(), list => list.filterNot(itemId => itemId === id));
 | 
					          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);
 | 
					  return deleteFromContexts(state, ownedStatusIds);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const updateContext = (state, status, references) => {
 | 
					const updateContext = (state, status) => {
 | 
				
			||||||
  return state.update('descendants', map => {
 | 
					  if (status.in_reply_to_id) {
 | 
				
			||||||
    references.forEach(parentId => {
 | 
					    return state.withMutations(mutable => {
 | 
				
			||||||
      map = map.update(parentId, ImmutableList(), list => {
 | 
					      const replies = mutable.getIn(['replies', status.in_reply_to_id], ImmutableList());
 | 
				
			||||||
        if (list.includes(status.id)) {
 | 
					 | 
				
			||||||
          return list;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return list.push(status.id);
 | 
					      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 map;
 | 
					  return state;
 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function contexts(state = initialState, action) {
 | 
					export default function replies(state = initialState, action) {
 | 
				
			||||||
  switch(action.type) {
 | 
					  switch(action.type) {
 | 
				
			||||||
  case ACCOUNT_BLOCK_SUCCESS:
 | 
					  case ACCOUNT_BLOCK_SUCCESS:
 | 
				
			||||||
  case ACCOUNT_MUTE_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);
 | 
					    return normalizeContext(state, action.id, action.ancestors, action.descendants);
 | 
				
			||||||
  case TIMELINE_DELETE:
 | 
					  case TIMELINE_DELETE:
 | 
				
			||||||
    return deleteFromContexts(state, [action.id]);
 | 
					    return deleteFromContexts(state, [action.id]);
 | 
				
			||||||
  case TIMELINE_CONTEXT_UPDATE:
 | 
					  case TIMELINE_UPDATE:
 | 
				
			||||||
    return updateContext(state, action.status, action.references);
 | 
					    return updateContext(state, action.status);
 | 
				
			||||||
  default:
 | 
					  default:
 | 
				
			||||||
    return state;
 | 
					    return state;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue