summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2023-12-04 21:59:24 +0100
committerEugen Rochko <eugen@zeonfederated.com>2023-12-04 21:59:24 +0100
commit587e3a049e045a84df5b0c4161340797e41afd91 (patch)
tree28f737c1975fac98bf91de883ffb65bd38600259
parent456597dae5251af841e46ab0608e0d44a7de1197 (diff)
Change threads to retain sorting returned by the API in web UIfix-web-thread-sort
-rw-r--r--app/javascript/mastodon/features/status/index.jsx74
-rw-r--r--app/javascript/mastodon/reducers/contexts.js91
2 files changed, 32 insertions, 133 deletions
diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx
index 67a9697311e..089cf58f7b6 100644
--- a/app/javascript/mastodon/features/status/index.jsx
+++ b/app/javascript/mastodon/features/status/index.jsx
@@ -6,11 +6,10 @@ import classNames from 'classnames';
import { Helmet } from 'react-helmet';
import { withRouter } from 'react-router-dom';
-import Immutable from 'immutable';
+import { List as ImmutableList } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
-import { createSelector } from 'reselect';
import { ReactComponent as VisibilityIcon } from '@material-symbols/svg-600/outlined/visibility.svg';
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
@@ -72,7 +71,6 @@ import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from
import ActionBar from './components/action_bar';
import DetailedStatus from './components/detailed_status';
-
const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
@@ -91,83 +89,19 @@ const makeMapStateToProps = () => {
const getStatus = makeGetStatus();
const getPictureInPicture = makeGetPictureInPicture();
- const getAncestorsIds = createSelector([
- (_, { id }) => id,
- state => state.getIn(['contexts', 'inReplyTos']),
- ], (statusId, inReplyTos) => {
- let ancestorsIds = Immutable.List();
- ancestorsIds = ancestorsIds.withMutations(mutable => {
- let id = statusId;
-
- while (id && !mutable.includes(id)) {
- mutable.unshift(id);
- id = inReplyTos.get(id);
- }
- });
-
- return ancestorsIds;
- });
-
- const getDescendantsIds = createSelector([
- (_, { id }) => id,
- state => state.getIn(['contexts', 'replies']),
- state => state.get('statuses'),
- ], (statusId, contextReplies, statuses) => {
- let descendantsIds = [];
- const ids = [statusId];
-
- while (ids.length > 0) {
- let id = ids.pop();
- const replies = contextReplies.get(id);
-
- if (statusId !== id) {
- descendantsIds.push(id);
- }
-
- if (replies) {
- replies.reverse().forEach(reply => {
- if (!ids.includes(reply) && !descendantsIds.includes(reply) && statusId !== reply) ids.push(reply);
- });
- }
- }
-
- let insertAt = descendantsIds.findIndex((id) => statuses.get(id).get('in_reply_to_account_id') !== statuses.get(id).get('account'));
- if (insertAt !== -1) {
- descendantsIds.forEach((id, idx) => {
- if (idx > insertAt && statuses.get(id).get('in_reply_to_account_id') === statuses.get(id).get('account')) {
- descendantsIds.splice(idx, 1);
- descendantsIds.splice(insertAt, 0, id);
- insertAt += 1;
- }
- });
- }
-
- return Immutable.List(descendantsIds);
- });
-
- const mapStateToProps = (state, props) => {
+ return (state, props) => {
const status = getStatus(state, { id: props.params.statusId });
- let ancestorsIds = Immutable.List();
- let descendantsIds = Immutable.List();
-
- if (status) {
- ancestorsIds = getAncestorsIds(state, { id: status.get('in_reply_to_id') });
- descendantsIds = getDescendantsIds(state, { id: status.get('id') });
- }
-
return {
isLoading: state.getIn(['statuses', props.params.statusId, 'isLoading']),
status,
- ancestorsIds,
- descendantsIds,
+ ancestorsIds: state.getIn(['contexts', props.params.statusId, 'ancestors'], ImmutableList()),
+ descendantsIds: state.getIn(['contexts', props.params.statusId, 'descendants'], ImmutableList()),
askReplyConfirmation: state.getIn(['compose', 'text']).trim().length !== 0,
domain: state.getIn(['meta', 'domain']),
pictureInPicture: getPictureInPicture(state, { id: props.params.statusId }),
};
};
-
- return mapStateToProps;
};
const truncate = (str, num) => {
diff --git a/app/javascript/mastodon/reducers/contexts.js b/app/javascript/mastodon/reducers/contexts.js
index f7d7419a4e3..5640432629e 100644
--- a/app/javascript/mastodon/reducers/contexts.js
+++ b/app/javascript/mastodon/reducers/contexts.js
@@ -6,65 +6,20 @@ import {
} from '../actions/accounts';
import { CONTEXT_FETCH_SUCCESS } from '../actions/statuses';
import { TIMELINE_DELETE, TIMELINE_UPDATE } from '../actions/timelines';
-import { compareId } from '../compare_id';
-const initialState = ImmutableMap({
- inReplyTos: ImmutableMap(),
- replies: ImmutableMap(),
-});
+const initialState = ImmutableMap();
-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 && !inReplyTos.has(id)) {
+const normalizeContext = (state, id, ancestors, descendants) => state.set(id, ImmutableMap({
+ ancestors: ImmutableList(ancestors.map(x => x.id)),
+ descendants: ImmutableList(descendants.map(x => x.id)),
+}));
- replies.update(in_reply_to_id, ImmutableList(), siblings => {
- const index = siblings.findLastIndex(sibling => compareId(sibling, id) < 0);
- return siblings.insert(index + 1, id);
- });
-
- inReplyTos.set(id, in_reply_to_id);
- }
- }
-
- // We know in_reply_to_id of statuses but `id` itself.
- // So we assume that the status of the id replies to last ancestors.
-
- ancestors.forEach(addReply);
-
- if (ancestors[0]) {
- addReply({ id, in_reply_to_id: ancestors[ancestors.length - 1].id });
- }
-
- descendants.forEach(addReply);
- }));
- }));
-});
-
-const deleteFromContexts = (immutableState, ids) => immutableState.withMutations(state => {
- state.update('inReplyTos', immutableAncestors => immutableAncestors.withMutations(inReplyTos => {
- state.update('replies', immutableDescendants => immutableDescendants.withMutations(replies => {
- ids.forEach(id => {
- const inReplyToIdOfId = inReplyTos.get(id);
- const repliesOfId = replies.get(id);
- const siblings = replies.get(inReplyToIdOfId);
-
- if (siblings) {
- replies.set(inReplyToIdOfId, siblings.filterNot(sibling => sibling === id));
- }
-
-
- if (repliesOfId) {
- repliesOfId.forEach(reply => inReplyTos.delete(reply));
- }
-
- inReplyTos.delete(id);
- replies.delete(id);
- });
- }));
- }));
-});
+const deleteFromContexts = (state, deletedIds) => state.update(contexts =>
+ contexts.map(context =>
+ context.update(map => ImmutableMap({
+ ancestors: map.get('ancestors').filterNot(id => deletedIds.includes(id)),
+ descendants: map.get('descendants').filterNot(id => deletedIds.includes(id)),
+ }))));
const filterContexts = (state, relationship, statuses) => {
const ownedStatusIds = statuses
@@ -75,16 +30,26 @@ const filterContexts = (state, relationship, statuses) => {
};
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());
+ const inReplyToId = status.in_reply_to_id;
+
+ if (inReplyToId) {
+ return state.update(contexts => contexts.map((context, rootStatusId) => {
+ if (context.get('descendants').includes(status.id)) {
+ return context;
+ }
+
+ if (rootStatusId === inReplyToId) {
+ return context.update('descendants', list => list.push(status.id));
+ }
- mutable.setIn(['inReplyTos', status.id], status.in_reply_to_id);
+ const ancestorIndex = context.get('descendants').indexOf(inReplyToId);
- if (!replies.includes(status.id)) {
- mutable.setIn(['replies', status.in_reply_to_id], replies.push(status.id));
+ if (ancestorIndex !== -1) {
+ return context.update('descendants', list => list.insert(ancestorIndex + 1, status.id));
}
- });
+
+ return context;
+ }));
}
return state;