diff --git a/app/soapbox/__fixtures__/status-unordered-mentions.json b/app/soapbox/__fixtures__/status-unordered-mentions.json
new file mode 100644
index 000000000..40bdbe8b6
--- /dev/null
+++ b/app/soapbox/__fixtures__/status-unordered-mentions.json
@@ -0,0 +1,122 @@
+{
+ "account": {
+ "acct": "apropos@freespeechextremist.com",
+ "avatar": "https://gleasonator.com/proxy/WVdkCbG7AOZ_eqMzskzXQoyjq8o/aHR0cHM6Ly9mcmVlc3BlZWNoZXh0cmVtaXN0LmNvbS9tZWRpYS8zN2I4MDMzZC03OGQ1LTQ0YmMtYmY5NC0xYTI2NzY5NTQwM2YvYmxvYi5wbmc_bmFtZT1ibG9iLnBuZw/blob.png",
+ "avatar_static": "https://gleasonator.com/proxy/WVdkCbG7AOZ_eqMzskzXQoyjq8o/aHR0cHM6Ly9mcmVlc3BlZWNoZXh0cmVtaXN0LmNvbS9tZWRpYS8zN2I4MDMzZC03OGQ1LTQ0YmMtYmY5NC0xYTI2NzY5NTQwM2YvYmxvYi5wbmc_bmFtZT1ibG9iLnBuZw/blob.png",
+ "bot": false,
+ "created_at": "2020-05-21T07:20:46.000Z",
+ "display_name": "of nothing",
+ "emojis": [],
+ "fields": [],
+ "followers_count": 87,
+ "following_count": 85,
+ "fqn": "apropos@freespeechextremist.com",
+ "header": "https://gleasonator.com/proxy/pIracLGWm_skCfOOgdwcCNqES5s/aHR0cHM6Ly9mcmVlc3BlZWNoZXh0cmVtaXN0LmNvbS9tZWRpYS8yZDEwYmRjZC01NDUwLTRjZjYtYWFhZS1hNTJjMzYwYjk2YjYvdHJhY2tzb25tYXJzLmpwZz9uYW1lPXRyYWNrc29ubWFycy5qcGc/tracksonmars.jpg",
+ "header_static": "https://gleasonator.com/proxy/pIracLGWm_skCfOOgdwcCNqES5s/aHR0cHM6Ly9mcmVlc3BlZWNoZXh0cmVtaXN0LmNvbS9tZWRpYS8yZDEwYmRjZC01NDUwLTRjZjYtYWFhZS1hNTJjMzYwYjk2YjYvdHJhY2tzb25tYXJzLmpwZz9uYW1lPXRyYWNrc29ubWFycy5qcGc/tracksonmars.jpg",
+ "id": "9vGR3IWmWVYRkKUZ4i",
+ "last_status_at": "2022-01-07T21:47:39",
+ "locked": false,
+ "note": "If you wait by the river long enough, the bodies of your enemies will float by.
Deo Vindice",
+ "pleroma": {
+ "accepts_chat_messages": true,
+ "also_known_as": [],
+ "ap_id": "https://freespeechextremist.com/users/apropos",
+ "background_image": null,
+ "favicon": "https://gleasonator.com/proxy/EN7BSaEEYTRpmRj4lITIjgWp2sg/aHR0cHM6Ly9mcmVlc3BlZWNoZXh0cmVtaXN0LmNvbS9mYXZpY29uLnBuZw/favicon.png",
+ "hide_favorites": true,
+ "hide_followers": false,
+ "hide_followers_count": false,
+ "hide_follows": false,
+ "hide_follows_count": false,
+ "is_admin": false,
+ "is_confirmed": true,
+ "is_moderator": false,
+ "is_suggested": false,
+ "relationship": {},
+ "skip_thread_containment": false,
+ "tags": []
+ },
+ "source": {
+ "fields": [],
+ "note": "",
+ "pleroma": {
+ "actor_type": "Person",
+ "discoverable": false
+ },
+ "sensitive": false
+ },
+ "statuses_count": 7087,
+ "url": "https://freespeechextremist.com/users/apropos",
+ "username": "apropos"
+ },
+ "application": null,
+ "bookmarked": false,
+ "card": null,
+ "content": "@NEETzsche @alex @Lumeinshin @sneeden
>seething
'posting', just like you.",
+ "created_at": "2022-01-07T17:29:58.000Z",
+ "emojis": [],
+ "favourited": false,
+ "favourites_count": 1,
+ "id": "AFChectaqZjmOVkXZ2",
+ "in_reply_to_account_id": "9v5bw7hEGBPc9nrpzc",
+ "in_reply_to_id": "AFChbnWqrAZ2VIlPJw",
+ "language": null,
+ "media_attachments": [],
+ "mentions": [
+ {
+ "acct": "alex",
+ "id": "9v5bmRalQvjOy0ECcC",
+ "url": "https://gleasonator.com/users/alex",
+ "username": "alex"
+ },
+ {
+ "acct": "NEETzsche@iddqd.social",
+ "id": "9v5bw7hEGBPc9nrpzc",
+ "url": "https://iddqd.social/users/NEETzsche",
+ "username": "NEETzsche"
+ },
+ {
+ "acct": "Lumeinshin@pleroma.skyshanty.xyz",
+ "id": "A3dFSwTkwgRfd998iG",
+ "url": "https://pleroma.skyshanty.xyz/users/Lumeinshin",
+ "username": "Lumeinshin"
+ },
+ {
+ "acct": "sneeden@social.silkky.cloud",
+ "id": "ACrsPAbAOPh3GbKZhQ",
+ "url": "https://social.silkky.cloud/users/sneeden",
+ "username": "sneeden"
+ }
+ ],
+ "muted": false,
+ "pinned": false,
+ "pleroma": {
+ "content": {
+ "text/plain": "@NEETzsche @alex @Lumeinshin @sneeden >seething'posting', just like you."
+ },
+ "conversation_id": "AFCYCBFN9SgOwoIWTg",
+ "direct_conversation_id": null,
+ "emoji_reactions": [],
+ "expires_at": null,
+ "in_reply_to_account_acct": "NEETzsche@iddqd.social",
+ "local": false,
+ "parent_visible": true,
+ "pinned_at": null,
+ "spoiler_text": {
+ "text/plain": ""
+ },
+ "thread_muted": false
+ },
+ "poll": null,
+ "reblog": null,
+ "reblogged": false,
+ "reblogs_count": 0,
+ "replies_count": 0,
+ "sensitive": false,
+ "spoiler_text": "",
+ "tags": [],
+ "text": null,
+ "uri": "https://freespeechextremist.com/objects/714b0e04-bec4-4a2a-9514-312814380064",
+ "url": "https://freespeechextremist.com/objects/714b0e04-bec4-4a2a-9514-312814380064",
+ "visibility": "public"
+}
diff --git a/app/soapbox/reducers/__tests__/statuses-test.js b/app/soapbox/reducers/__tests__/statuses-test.js
index 7bcc67c9e..525ca50e6 100644
--- a/app/soapbox/reducers/__tests__/statuses-test.js
+++ b/app/soapbox/reducers/__tests__/statuses-test.js
@@ -4,13 +4,29 @@ import {
STATUS_CREATE_REQUEST,
STATUS_CREATE_FAIL,
} from 'soapbox/actions/statuses';
-
+import { STATUS_IMPORT } from 'soapbox/actions/importer';
describe('statuses reducer', () => {
it('should return the initial state', () => {
expect(reducer(undefined, {})).toEqual(ImmutableMap());
});
+ describe('STATUS_IMPORT', () => {
+ it('fixes the order of mentions', () => {
+ const status = require('soapbox/__fixtures__/status-unordered-mentions.json');
+ const action = { type: STATUS_IMPORT, status };
+
+ const expected = ['NEETzsche', 'alex', 'Lumeinshin', 'sneeden'];
+
+ const result = reducer(undefined, action)
+ .getIn(['AFChectaqZjmOVkXZ2', 'mentions'])
+ .map(mention => mention.get('username'))
+ .toJS();
+
+ expect(result).toEqual(expected);
+ });
+ });
+
describe('STATUS_CREATE_REQUEST', () => {
it('increments the replies_count of its parent', () => {
const state = fromJS({ '123': { replies_count: 4 } });
diff --git a/app/soapbox/reducers/statuses.js b/app/soapbox/reducers/statuses.js
index 6f9215337..ebf405806 100644
--- a/app/soapbox/reducers/statuses.js
+++ b/app/soapbox/reducers/statuses.js
@@ -24,7 +24,30 @@ import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
import { Map as ImmutableMap, fromJS } from 'immutable';
import { simulateEmojiReact, simulateUnEmojiReact } from 'soapbox/utils/emoji_reacts';
-const importStatus = (state, status) => state.set(status.id, fromJS(status));
+// Fix order of mentions
+const fixMentions = status => {
+ const mentions = status.get('mentions');
+ const inReplyToAccountId = status.get('in_reply_to_account_id');
+
+ // Sort the replied-to mention to the top
+ const sorted = mentions.sort((a, b) => {
+ if (a.get('id') === inReplyToAccountId) {
+ return -1;
+ } else {
+ return 0;
+ }
+ });
+
+ return status.set('mentions', sorted);
+};
+
+const fixStatus = status => {
+ return status.withMutations(status => {
+ fixMentions(status);
+ });
+};
+
+const importStatus = (state, status) => state.set(status.id, fixStatus(fromJS(status)));
const importStatuses = (state, statuses) =>
state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status)));