diff --git a/app/soapbox/actions/statuses.js b/app/soapbox/actions/statuses.js index 176f7325f..ba1c3e74d 100644 --- a/app/soapbox/actions/statuses.js +++ b/app/soapbox/actions/statuses.js @@ -179,10 +179,18 @@ export function fetchContext(id) { }; } -export function fetchNext(next) { +export function fetchNext(statusId, next) { return async(dispatch, getState) => { const response = await api(getState).get(next); dispatch(importFetchedStatuses(response.data)); + + dispatch({ + type: CONTEXT_FETCH_SUCCESS, + id: statusId, + ancestors: [], + descendants: response.data, + }); + return { next: getNextLink(response) }; }; } @@ -213,6 +221,14 @@ export function fetchStatusWithContext(id) { dispatch(fetchDescendants(id)), dispatch(fetchStatus(id)), ]); + + dispatch({ + type: CONTEXT_FETCH_SUCCESS, + id, + ancestors: responses[0].data, + descendants: responses[1].data, + }); + const next = getNextLink(responses[1]); return { next }; } else { diff --git a/app/soapbox/features/status/index.tsx b/app/soapbox/features/status/index.tsx index 25247ac6f..49df34423 100644 --- a/app/soapbox/features/status/index.tsx +++ b/app/soapbox/features/status/index.tsx @@ -654,10 +654,11 @@ class Status extends ImmutablePureComponent { } handleLoadMore = () => { + const { status } = this.props; const { next } = this.state; if (next) { - this.props.dispatch(fetchNext(next)).then(({ next }) => { + this.props.dispatch(fetchNext(status.id, next)).then(({ next }) => { this.setState({ next }); }).catch(() => {}); } diff --git a/app/soapbox/reducers/contexts.js b/app/soapbox/reducers/contexts.js index c92ae503d..18ee80caf 100644 --- a/app/soapbox/reducers/contexts.js +++ b/app/soapbox/reducers/contexts.js @@ -67,15 +67,41 @@ const insertTombstone = (state, ancestorId, descendantId) => { }); }; -const importBranch = (state, statuses, rootId) => { +/** Find the highest level status from this statusId. */ +const getRootNode = (state, statusId, initialId = statusId) => { + const parent = state.getIn(['inReplyTos', statusId]); + + if (!parent) { + return statusId; + } else if (parent === initialId) { + // Prevent cycles + return parent; + } else { + return getRootNode(state, parent, initialId); + } +}; + +/** Route fromId to toId by inserting tombstones. */ +const connectNodes = (state, fromId, toId) => { + const root = getRootNode(state, fromId); + + if (root !== toId) { + return insertTombstone(state, toId, fromId); + } else { + return state; + } +}; + +const importBranch = (state, statuses, statusId) => { return state.withMutations(state => { statuses.forEach((status, i) => { - const lastId = rootId && i === 0 ? rootId : (statuses[i - 1] || {}).id; + const prevId = statusId && i === 0 ? statusId : (statuses[i - 1] || {}).id; if (status.in_reply_to_id) { importStatus(state, status); - } else if (lastId) { - insertTombstone(state, lastId, status.id); + connectNodes(state, status.id, statusId); + } else if (prevId) { + insertTombstone(state, prevId, status.id); } }); });