Merge branch 'records' into 'develop'

Records: Status, Account, Instance

See merge request soapbox-pub/soapbox-fe!1083
next-old
Alex Gleason 2022-03-09 18:16:03 +00:00
commit cd6fd8c0db
10 zmienionych plików z 220 dodań i 89 usunięć

Wyświetl plik

@ -116,7 +116,7 @@ class EditProfile extends ImmutablePureComponent {
const birthday = account.get('birthday'); const birthday = account.get('birthday');
const showBirthday = account.getIn(['source', 'pleroma', 'show_birthday']); const showBirthday = account.getIn(['source', 'pleroma', 'show_birthday']);
const initialState = account.withMutations(map => { const initialState = ImmutableMap(account).withMutations(map => {
map.merge(map.get('source')); map.merge(map.get('source'));
map.delete('source'); map.delete('source');
map.set('fields', normalizeFields(map.get('fields'), Math.min(maxFields, 4))); map.set('fields', normalizeFields(map.get('fields'), Math.min(maxFields, 4)));

Wyświetl plik

@ -4,25 +4,60 @@ import { normalizeInstance } from '../instance';
describe('normalizeInstance()', () => { describe('normalizeInstance()', () => {
it('normalizes an empty Map', () => { it('normalizes an empty Map', () => {
const expected = ImmutableMap({ const expected = {
description_limit: 1500, approval_required: false,
configuration: ImmutableMap({ contact_account: {},
statuses: ImmutableMap({ configuration: {
max_characters: 500, media_attachments: {
max_media_attachments: 4, image_size_limit: 10485760,
}), image_matrix_limit: 16777216,
polls: ImmutableMap({ video_size_limit: 41943040,
video_frame_rate_limit: 60,
video_matrix_limit: 2304000,
},
polls: {
max_options: 4, max_options: 4,
max_characters_per_option: 25, max_characters_per_option: 25,
min_expiration: 300, min_expiration: 300,
max_expiration: 2629746, max_expiration: 2629746,
}), },
}), statuses: {
max_characters: 500,
max_media_attachments: 4,
},
},
description: '',
description_limit: 1500,
email: '',
fedibird_capabilities: [],
invites_enabled: false,
languages: [],
pleroma: {
metadata: {
account_activation_required: false,
birthday_min_age: 0,
birthday_required: false,
features: [],
federation: {
enabled: true,
exclusions: false,
},
},
stats: {},
},
registrations: false,
rules: [],
short_description: '',
stats: {},
title: '',
thumbnail: '',
uri: '',
urls: {},
version: '0.0.0', version: '0.0.0',
}); };
const result = normalizeInstance(ImmutableMap()); const result = normalizeInstance(ImmutableMap());
expect(result).toEqual(expected); expect(result.toJS()).toEqual(expected);
}); });
it('normalizes Pleroma instance with Mastodon configuration format', () => { it('normalizes Pleroma instance with Mastodon configuration format', () => {
@ -104,10 +139,10 @@ describe('normalizeInstance()', () => {
const result = normalizeInstance(instance); const result = normalizeInstance(instance);
// Sets description_limit // Sets description_limit
expect(result.get('description_limit')).toEqual(1500); expect(result.description_limit).toEqual(1500);
// But otherwise, it's the same // Preserves fedibird_capabilities
expect(result.delete('description_limit')).toEqual(instance); expect(result.fedibird_capabilities).toEqual(instance.get('fedibird_capabilities'));
}); });
it('normalizes Mitra instance', () => { it('normalizes Mitra instance', () => {

Wyświetl plik

@ -1,7 +1,45 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList, Record } from 'immutable';
import { mergeDefined } from 'soapbox/utils/normalizers'; import { mergeDefined } from 'soapbox/utils/normalizers';
const AccountRecord = Record({
acct: '',
avatar: '',
avatar_static: '',
birthday: undefined,
bot: false,
created_at: new Date(),
display_name: '',
emojis: ImmutableList(),
fields: ImmutableList(),
followers_count: 0,
following_count: 0,
fqn: '',
header: '',
header_static: '',
id: '',
last_status_at: new Date(),
location: '',
locked: false,
moved: null,
note: '',
pleroma: ImmutableMap(),
source: ImmutableMap(),
statuses_count: 0,
uri: '',
url: '',
username: '',
verified: false,
// Internal fields
display_name_html: '',
note_emojified: '',
note_plain: '',
patron: ImmutableMap(),
relationship: ImmutableList(),
should_refetch: false,
});
// https://gitlab.com/soapbox-pub/soapbox-fe/-/issues/549 // https://gitlab.com/soapbox-pub/soapbox-fe/-/issues/549
const normalizePleromaLegacyFields = account => { const normalizePleromaLegacyFields = account => {
return account.update('pleroma', ImmutableMap(), pleroma => { return account.update('pleroma', ImmutableMap(), pleroma => {
@ -49,10 +87,12 @@ const normalizeLocation = account => {
}; };
export const normalizeAccount = account => { export const normalizeAccount = account => {
return account.withMutations(account => { return AccountRecord(
normalizePleromaLegacyFields(account); account.withMutations(account => {
normalizeVerified(account); normalizePleromaLegacyFields(account);
normalizeBirthday(account); normalizeVerified(account);
normalizeLocation(account); normalizeBirthday(account);
}); normalizeLocation(account);
}),
);
}; };

Wyświetl plik

@ -1,16 +1,20 @@
import { Map as ImmutableMap } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList, Record } from 'immutable';
import { parseVersion, PLEROMA } from 'soapbox/utils/features'; import { parseVersion, PLEROMA } from 'soapbox/utils/features';
import { mergeDefined } from 'soapbox/utils/normalizers'; import { mergeDefined } from 'soapbox/utils/normalizers';
import { isNumber } from 'soapbox/utils/numbers'; import { isNumber } from 'soapbox/utils/numbers';
// Use Mastodon defaults // Use Mastodon defaults
const baseInstance = ImmutableMap({ const InstanceRecord = Record({
description_limit: 1500, approval_required: false,
contact_account: ImmutableMap(),
configuration: ImmutableMap({ configuration: ImmutableMap({
statuses: ImmutableMap({ media_attachments: ImmutableMap({
max_characters: 500, image_size_limit: 10485760,
max_media_attachments: 4, image_matrix_limit: 16777216,
video_size_limit: 41943040,
video_frame_rate_limit: 60,
video_matrix_limit: 2304000,
}), }),
polls: ImmutableMap({ polls: ImmutableMap({
max_options: 4, max_options: 4,
@ -18,7 +22,38 @@ const baseInstance = ImmutableMap({
min_expiration: 300, min_expiration: 300,
max_expiration: 2629746, max_expiration: 2629746,
}), }),
statuses: ImmutableMap({
max_characters: 500,
max_media_attachments: 4,
}),
}), }),
description: '',
description_limit: 1500,
email: '',
fedibird_capabilities: ImmutableList(),
invites_enabled: false,
languages: ImmutableList(),
pleroma: ImmutableMap({
metadata: ImmutableMap({
account_activation_required: false,
birthday_min_age: 0,
birthday_required: false,
features: ImmutableList(),
federation: ImmutableMap({
enabled: true,
exclusions: false,
}),
}),
stats: ImmutableMap(),
}),
registrations: false,
rules: ImmutableList(),
short_description: '',
stats: ImmutableMap(),
title: '',
thumbnail: '',
uri: '',
urls: ImmutableMap(),
version: '0.0.0', version: '0.0.0',
}); });
@ -45,19 +80,21 @@ export const normalizeInstance = instance => {
const { software } = parseVersion(instance.get('version')); const { software } = parseVersion(instance.get('version'));
const mastodonConfig = pleromaToMastodonConfig(instance); const mastodonConfig = pleromaToMastodonConfig(instance);
return instance.withMutations(instance => { return InstanceRecord(
// Merge configuration instance.withMutations(instance => {
instance.update('configuration', ImmutableMap(), configuration => ( // Merge configuration
configuration.mergeDeepWith(mergeDefined, mastodonConfig) instance.update('configuration', ImmutableMap(), configuration => (
)); configuration.mergeDeepWith(mergeDefined, mastodonConfig)
));
// If max attachments isn't set, check the backend software // If max attachments isn't set, check the backend software
instance.updateIn(['configuration', 'statuses', 'max_media_attachments'], value => { instance.updateIn(['configuration', 'statuses', 'max_media_attachments'], value => {
return isNumber(value) ? value : getAttachmentLimit(software); return isNumber(value) ? value : getAttachmentLimit(software);
}); });
// Merge defaults & cleanup // Merge defaults & cleanup
instance.mergeDeepWith(mergeDefined, baseInstance); instance.mergeDeepWith(mergeDefined, InstanceRecord());
instance.deleteAll(['max_toot_chars', 'poll_limits']); instance.deleteAll(['max_toot_chars', 'poll_limits']);
}); }),
);
}; };

Wyświetl plik

@ -1,32 +1,45 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList, Record } from 'immutable';
import { accountToMention } from 'soapbox/utils/accounts'; import { accountToMention } from 'soapbox/utils/accounts';
import { mergeDefined } from 'soapbox/utils/normalizers'; import { mergeDefined } from 'soapbox/utils/normalizers';
// Some backends can return null, or omit these required fields const StatusRecord = Record({
const baseStatus = ImmutableMap({ account: ImmutableMap(),
application: null, application: null,
bookmarked: false, bookmarked: false,
card: null, card: null,
content: '',
created_at: new Date(), created_at: new Date(),
emojis: ImmutableList(), emojis: ImmutableList(),
favourited: false, favourited: false,
favourites_count: 0, favourites_count: 0,
in_reply_to_account_id: null, in_reply_to_account_id: null,
in_reply_to_id: null, in_reply_to_id: null,
id: '',
language: null, language: null,
media_attachments: ImmutableList(),
mentions: ImmutableList(), mentions: ImmutableList(),
muted: false, muted: false,
pinned: false, pinned: false,
pleroma: ImmutableMap(),
poll: null,
quote: null,
reblog: null, reblog: null,
reblogged: false, reblogged: false,
reblogs_count: 0, reblogs_count: 0,
replies_count: 0, replies_count: 0,
sensitive: false,
spoiler_text: '', spoiler_text: '',
tags: ImmutableList(), tags: ImmutableList(),
uri: '', uri: '',
url: '', url: '',
visibility: 'public', visibility: 'public',
// Internal fields
contentHtml: '',
hidden: false,
search_index: '',
spoilerHtml: '',
}); });
const basePollOption = ImmutableMap({ title: '', votes_count: 0 }); const basePollOption = ImmutableMap({ title: '', votes_count: 0 });
@ -41,11 +54,6 @@ const basePoll = ImmutableMap({
votes_count: 0, votes_count: 0,
}); });
// Merge base status
const mergeBase = status => {
return status.mergeDeepWith(mergeDefined, baseStatus);
};
// Ensure attachments have required fields // Ensure attachments have required fields
// https://docs.joinmastodon.org/entities/attachment/ // https://docs.joinmastodon.org/entities/attachment/
const normalizeAttachment = attachment => { const normalizeAttachment = attachment => {
@ -147,13 +155,14 @@ const fixQuote = status => {
}; };
export const normalizeStatus = status => { export const normalizeStatus = status => {
return status.withMutations(status => { return StatusRecord(
mergeBase(status); status.withMutations(status => {
normalizeAttachments(status); normalizeAttachments(status);
normalizeMentions(status); normalizeMentions(status);
normalizePoll(status); normalizePoll(status);
fixMentionsOrder(status); fixMentionsOrder(status);
addSelfMention(status); addSelfMention(status);
fixQuote(status); fixQuote(status);
}); }),
);
}; };

Wyświetl plik

@ -1,4 +1,6 @@
import { Map as ImmutableMap } from 'immutable'; import { Map as ImmutableMap, Record } from 'immutable';
import { ACCOUNT_IMPORT } from 'soapbox/actions/importer';
import reducer from '../accounts'; import reducer from '../accounts';
// import * as actions from 'soapbox/actions/importer'; // import * as actions from 'soapbox/actions/importer';
@ -10,6 +12,16 @@ describe('accounts reducer', () => {
expect(reducer(undefined, {})).toEqual(ImmutableMap()); expect(reducer(undefined, {})).toEqual(ImmutableMap());
}); });
describe('ACCOUNT_IMPORT', () => {
it('parses the account as a Record', () => {
const account = require('soapbox/__fixtures__/pleroma-account.json');
const action = { type: ACCOUNT_IMPORT, account };
const result = reducer(undefined, action).get('9v5bmRalQvjOy0ECcC');
expect(Record.isRecord(result)).toBe(true);
});
});
// fails to add normalized accounts to state // fails to add normalized accounts to state
// it('should handle ACCOUNT_IMPORT', () => { // it('should handle ACCOUNT_IMPORT', () => {
// const state = ImmutableMap({ }); // const state = ImmutableMap({ });

Wyświetl plik

@ -1,4 +1,4 @@
import { Map as ImmutableMap } from 'immutable'; import { Record } from 'immutable';
import { INSTANCE_REMEMBER_SUCCESS } from 'soapbox/actions/instance'; import { INSTANCE_REMEMBER_SUCCESS } from 'soapbox/actions/instance';
@ -6,22 +6,27 @@ import reducer from '../instance';
describe('instance reducer', () => { describe('instance reducer', () => {
it('should return the initial state', () => { it('should return the initial state', () => {
expect(reducer(undefined, {})).toEqual(ImmutableMap({ const result = reducer(undefined, {});
const expected = {
description_limit: 1500, description_limit: 1500,
configuration: ImmutableMap({ configuration: {
statuses: ImmutableMap({ statuses: {
max_characters: 500, max_characters: 500,
max_media_attachments: 4, max_media_attachments: 4,
}), },
polls: ImmutableMap({ polls: {
max_options: 4, max_options: 4,
max_characters_per_option: 25, max_characters_per_option: 25,
min_expiration: 300, min_expiration: 300,
max_expiration: 2629746, max_expiration: 2629746,
}), },
}), },
version: '0.0.0', version: '0.0.0',
})); };
expect(Record.isRecord(result)).toBe(true);
expect(result.toJS()).toMatchObject(expected);
}); });
describe('INSTANCE_REMEMBER_SUCCESS', () => { describe('INSTANCE_REMEMBER_SUCCESS', () => {

Wyświetl plik

@ -1,4 +1,4 @@
import { Map as ImmutableMap, fromJS } from 'immutable'; import { Map as ImmutableMap, Record, fromJS } from 'immutable';
import { STATUS_IMPORT } from 'soapbox/actions/importer'; import { STATUS_IMPORT } from 'soapbox/actions/importer';
import { import {
@ -14,6 +14,14 @@ describe('statuses reducer', () => {
}); });
describe('STATUS_IMPORT', () => { describe('STATUS_IMPORT', () => {
it('parses the status as a Record', () => {
const status = require('soapbox/__fixtures__/pleroma-quote-post.json');
const action = { type: STATUS_IMPORT, status };
const result = reducer(undefined, action).get('AFmFMSpITT9xcOJKcK');
expect(Record.isRecord(result)).toBe(true);
});
it('fixes the order of mentions', () => { it('fixes the order of mentions', () => {
const status = require('soapbox/__fixtures__/status-unordered-mentions.json'); const status = require('soapbox/__fixtures__/status-unordered-mentions.json');
const action = { type: STATUS_IMPORT, status }; const action = { type: STATUS_IMPORT, status };

Wyświetl plik

@ -40,17 +40,8 @@ import {
const initialState = ImmutableMap(); const initialState = ImmutableMap();
const minifyAccount = account => {
return account.deleteAll([
'followers_count',
'following_count',
'statuses_count',
'source',
]);
};
const fixAccount = (state, account) => { const fixAccount = (state, account) => {
const normalized = minifyAccount(normalizeAccount(fromJS(account))); const normalized = normalizeAccount(fromJS(account));
return state.set(account.id, normalized); return state.set(account.id, normalized);
}; };
@ -125,20 +116,15 @@ const removePermission = (state, accountIds, permissionGroup) => {
}); });
}; };
const buildAccount = adminUser => fromJS({ const buildAccount = adminUser => normalizeAccount(fromJS({
id: adminUser.get('id'), id: adminUser.get('id'),
username: adminUser.get('nickname').split('@')[0], username: adminUser.get('nickname').split('@')[0],
acct: adminUser.get('nickname'), acct: adminUser.get('nickname'),
display_name: adminUser.get('display_name'), display_name: adminUser.get('display_name'),
display_name_html: adminUser.get('display_name'), display_name_html: adminUser.get('display_name'),
note: '',
url: adminUser.get('url'), url: adminUser.get('url'),
avatar: adminUser.get('avatar'), avatar: adminUser.get('avatar'),
avatar_static: adminUser.get('avatar'), avatar_static: adminUser.get('avatar'),
header: '',
header_static: '',
emojis: [],
fields: [],
created_at: adminUser.get('created_at'), created_at: adminUser.get('created_at'),
pleroma: { pleroma: {
is_active: adminUser.get('is_active'), is_active: adminUser.get('is_active'),
@ -153,7 +139,7 @@ const buildAccount = adminUser => fromJS({
}, },
}, },
should_refetch: true, should_refetch: true,
}); }));
const mergeAdminUser = (account, adminUser) => { const mergeAdminUser = (account, adminUser) => {
return account.withMutations(account => { return account.withMutations(account => {

Wyświetl plik

@ -89,8 +89,7 @@ const fixQuote = (status, oldStatus) => {
const fixStatus = (state, status, expandSpoilers) => { const fixStatus = (state, status, expandSpoilers) => {
const oldStatus = state.get(status.get('id')); const oldStatus = state.get(status.get('id'));
return status.withMutations(status => { return normalizeStatus(status).withMutations(status => {
normalizeStatus(status);
fixQuote(status, oldStatus); fixQuote(status, oldStatus);
calculateStatus(status, oldStatus, expandSpoilers); calculateStatus(status, oldStatus, expandSpoilers);
minifyStatus(status); minifyStatus(status);