+import Immutable from 'immutable';
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
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;
};
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));
+ }
+
+
+ if (repliesOfId) {
+ repliesOfId.forEach(reply => inReplyTos.delete(reply));
+ }
- descendants.delete(id);
- ancestors.delete(id);
+ inReplyTos.delete(id);
+ replies.delete(id);
});
}));
}));
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());
- 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) {
case ACCOUNT_BLOCK_SUCCESS:
case ACCOUNT_MUTE_SUCCESS:
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;
}