From 894bf1e880d20727e52dca66d24436ed049e23e2 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 9 Mar 2022 12:16:24 -0600 Subject: [PATCH 1/7] Account normalizer: convert to Typescript, add type --- .../normalizers/{account.js => account.ts} | 58 ++++++++++++++++--- 1 file changed, 51 insertions(+), 7 deletions(-) rename app/soapbox/normalizers/{account.js => account.ts} (58%) diff --git a/app/soapbox/normalizers/account.js b/app/soapbox/normalizers/account.ts similarity index 58% rename from app/soapbox/normalizers/account.js rename to app/soapbox/normalizers/account.ts index 9515e1217..61db66915 100644 --- a/app/soapbox/normalizers/account.js +++ b/app/soapbox/normalizers/account.ts @@ -2,6 +2,44 @@ import { Map as ImmutableMap, List as ImmutableList, Record } from 'immutable'; import { mergeDefined } from 'soapbox/utils/normalizers'; +interface Account { + acct: string; + avatar: string; + avatar_static: string; + birthday: Date | undefined; + bot: boolean; + created_at: Date; + display_name: string; + emojis: ImmutableList; + fields: ImmutableList; + followers_count: number; + following_count: number; + fqn: string; + header: string; + header_static: string; + id: string; + last_status_at: Date; + location: string; + locked: boolean; + moved: null; + note: string; + pleroma: ImmutableMap; + source: ImmutableMap; + statuses_count: number; + uri: string; + url: string; + username: string; + verified: boolean; + + // Internal fields + display_name_html: string; + note_emojified: string; + note_plain: string; + patron: ImmutableMap; + relationship: ImmutableList; + should_refetch: boolean; +} + const AccountRecord = Record({ acct: '', avatar: '', @@ -41,8 +79,8 @@ const AccountRecord = Record({ }); // https://gitlab.com/soapbox-pub/soapbox-fe/-/issues/549 -const normalizePleromaLegacyFields = account => { - return account.update('pleroma', ImmutableMap(), pleroma => { +const normalizePleromaLegacyFields = (account: ImmutableMap) => { + return account.update('pleroma', ImmutableMap(), (pleroma: ImmutableMap) => { return pleroma.withMutations(pleroma => { const legacy = ImmutableMap({ is_active: !pleroma.get('deactivated'), @@ -57,7 +95,7 @@ const normalizePleromaLegacyFields = account => { }; // Normalize Pleroma/Fedibird birthday -const normalizeBirthday = account => { +const normalizeBirthday = (account: ImmutableMap) => { const birthday = [ account.getIn(['pleroma', 'birthday']), account.getIn(['other_settings', 'birthday']), @@ -66,18 +104,24 @@ const normalizeBirthday = account => { return account.set('birthday', birthday); }; +// Get Pleroma tags +const getTags = (account: ImmutableMap): ImmutableList => { + const tags = account.getIn(['pleroma', 'tags']); + return ImmutableList(ImmutableList.isList(tags) ? tags : []); +}; + // Normalize Truth Social/Pleroma verified -const normalizeVerified = account => { +const normalizeVerified = (account: ImmutableMap) => { return account.update('verified', verified => { return [ verified === true, - account.getIn(['pleroma', 'tags'], ImmutableList()).includes('verified'), + getTags(account).includes('verified'), ].some(Boolean); }); }; // Normalize Fedibird/Truth Social location -const normalizeLocation = account => { +const normalizeLocation = (account: ImmutableMap) => { return account.update('location', location => { return [ location, @@ -86,7 +130,7 @@ const normalizeLocation = account => { }); }; -export const normalizeAccount = account => { +export const normalizeAccount = (account: ImmutableMap): Account => { return AccountRecord( account.withMutations(account => { normalizePleromaLegacyFields(account); From 8c7a7fd7dca16424d9a8ed0a3a7c3602a7802cbf Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 9 Mar 2022 13:23:52 -0600 Subject: [PATCH 2/7] eslint: fix typescript undefined globals --- .eslintrc.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b478bcc3f..f4b59595a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -40,9 +40,7 @@ module.exports = { react: { version: 'detect', }, - 'import/extensions': [ - '.js', - ], + 'import/extensions': ['.js', '.jsx', '.ts', '.tsx'], 'import/ignore': [ 'node_modules', '\\.(css|scss|json)$', @@ -257,9 +255,17 @@ module.exports = { overrides: [ { files: ['**/*.tsx'], - 'rules': { + rules: { 'react/prop-types': 'off', }, }, + // Disable no-undef in TypeScript + // https://stackoverflow.com/a/69155899 + { + files: ['*.ts', '*.tsx'], + rules: { + 'no-undef': 'off', + }, + }, ], }; From 6e61cb525c405ca41df37c411a7b0bd1c0b414f1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 9 Mar 2022 13:26:48 -0600 Subject: [PATCH 3/7] Move Account interface to types/ directory --- app/soapbox/normalizers/account.ts | 41 ++-------------------------- app/soapbox/types/account.ts | 44 ++++++++++++++++++++++++++++++ app/soapbox/types/index.ts | 3 ++ 3 files changed, 49 insertions(+), 39 deletions(-) create mode 100644 app/soapbox/types/account.ts create mode 100644 app/soapbox/types/index.ts diff --git a/app/soapbox/normalizers/account.ts b/app/soapbox/normalizers/account.ts index 61db66915..def1b497e 100644 --- a/app/soapbox/normalizers/account.ts +++ b/app/soapbox/normalizers/account.ts @@ -1,45 +1,8 @@ import { Map as ImmutableMap, List as ImmutableList, Record } from 'immutable'; +import { IAccount } from 'soapbox/types'; import { mergeDefined } from 'soapbox/utils/normalizers'; -interface Account { - acct: string; - avatar: string; - avatar_static: string; - birthday: Date | undefined; - bot: boolean; - created_at: Date; - display_name: string; - emojis: ImmutableList; - fields: ImmutableList; - followers_count: number; - following_count: number; - fqn: string; - header: string; - header_static: string; - id: string; - last_status_at: Date; - location: string; - locked: boolean; - moved: null; - note: string; - pleroma: ImmutableMap; - source: ImmutableMap; - statuses_count: number; - uri: string; - url: string; - username: string; - verified: boolean; - - // Internal fields - display_name_html: string; - note_emojified: string; - note_plain: string; - patron: ImmutableMap; - relationship: ImmutableList; - should_refetch: boolean; -} - const AccountRecord = Record({ acct: '', avatar: '', @@ -130,7 +93,7 @@ const normalizeLocation = (account: ImmutableMap) => { }); }; -export const normalizeAccount = (account: ImmutableMap): Account => { +export const normalizeAccount = (account: ImmutableMap): IAccount => { return AccountRecord( account.withMutations(account => { normalizePleromaLegacyFields(account); diff --git a/app/soapbox/types/account.ts b/app/soapbox/types/account.ts new file mode 100644 index 000000000..b17935c35 --- /dev/null +++ b/app/soapbox/types/account.ts @@ -0,0 +1,44 @@ +/** + * Account entity. + * https://docs.joinmastodon.org/entities/account/ + **/ + +interface IAccount { + acct: string; + avatar: string; + avatar_static: string; + birthday: Date | undefined; + bot: boolean; + created_at: Date; + display_name: string; + emojis: Iterable; + fields: Iterable; + followers_count: number; + following_count: number; + fqn: string; + header: string; + header_static: string; + id: string; + last_status_at: Date; + location: string; + locked: boolean; + moved: null; + note: string; + pleroma: Record; + source: Record; + statuses_count: number; + uri: string; + url: string; + username: string; + verified: boolean; + + // Internal fields + display_name_html: string; + note_emojified: string; + note_plain: string; + patron: Record; + relationship: Iterable; + should_refetch: boolean; +} + +export { IAccount }; diff --git a/app/soapbox/types/index.ts b/app/soapbox/types/index.ts new file mode 100644 index 000000000..bc8cbd94e --- /dev/null +++ b/app/soapbox/types/index.ts @@ -0,0 +1,3 @@ +import { IAccount } from './account'; + +export { IAccount }; From a2adaf2ffd7b10b161c430bc9e9ab6997d213ac9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 9 Mar 2022 14:26:57 -0600 Subject: [PATCH 4/7] Convert Status Normalizer to TypeScript --- .../normalizers/{status.js => status.ts} | 25 ++++++----- app/soapbox/types/index.ts | 3 +- app/soapbox/types/status.ts | 45 +++++++++++++++++++ 3 files changed, 60 insertions(+), 13 deletions(-) rename app/soapbox/normalizers/{status.js => status.ts} (81%) create mode 100644 app/soapbox/types/status.ts diff --git a/app/soapbox/normalizers/status.js b/app/soapbox/normalizers/status.ts similarity index 81% rename from app/soapbox/normalizers/status.js rename to app/soapbox/normalizers/status.ts index 494a0ced7..a4d8a8d3f 100644 --- a/app/soapbox/normalizers/status.js +++ b/app/soapbox/normalizers/status.ts @@ -1,5 +1,6 @@ import { Map as ImmutableMap, List as ImmutableList, Record } from 'immutable'; +import { IStatus } from 'soapbox/types'; import { accountToMention } from 'soapbox/utils/accounts'; import { mergeDefined } from 'soapbox/utils/normalizers'; @@ -56,7 +57,7 @@ const basePoll = ImmutableMap({ // Ensure attachments have required fields // https://docs.joinmastodon.org/entities/attachment/ -const normalizeAttachment = attachment => { +const normalizeAttachment = (attachment: ImmutableMap) => { const url = [ attachment.get('url'), attachment.get('preview_url'), @@ -72,14 +73,14 @@ const normalizeAttachment = attachment => { return attachment.mergeWith(mergeDefined, base); }; -const normalizeAttachments = status => { +const normalizeAttachments = (status: ImmutableMap) => { return status.update('media_attachments', ImmutableList(), attachments => { return attachments.map(normalizeAttachment); }); }; // Normalize mentions -const normalizeMention = mention => { +const normalizeMention = (mention: ImmutableMap) => { const base = ImmutableMap({ acct: '', username: (mention.get('acct') || '').split('@')[0], @@ -89,22 +90,22 @@ const normalizeMention = mention => { return mention.mergeWith(mergeDefined, base); }; -const normalizeMentions = status => { +const normalizeMentions = (status: ImmutableMap) => { return status.update('mentions', ImmutableList(), mentions => { return mentions.map(normalizeMention); }); }; // Normalize poll option -const normalizePollOption = option => { +const normalizePollOption = (option: ImmutableMap) => { return option.mergeWith(mergeDefined, basePollOption); }; // Normalize poll -const normalizePoll = status => { +const normalizePoll = (status: ImmutableMap) => { if (status.hasIn(['poll', 'options'])) { return status.update('poll', ImmutableMap(), poll => { - return poll.mergeWith(mergeDefined, basePoll).update('options', options => { + return poll.mergeWith(mergeDefined, basePoll).update('options', (options: ImmutableList>) => { return options.map(normalizePollOption); }); }); @@ -113,12 +114,12 @@ const normalizePoll = status => { } }; // Fix order of mentions -const fixMentionsOrder = status => { +const fixMentionsOrder = (status: ImmutableMap) => { const mentions = status.get('mentions', ImmutableList()); const inReplyToAccountId = status.get('in_reply_to_account_id'); // Sort the replied-to mention to the top - const sorted = mentions.sort((a, b) => { + const sorted = mentions.sort((a: ImmutableMap, _b: ImmutableMap) => { if (a.get('id') === inReplyToAccountId) { return -1; } else { @@ -130,7 +131,7 @@ const fixMentionsOrder = status => { }; // Add self to mentions if it's a reply to self -const addSelfMention = status => { +const addSelfMention = (status: ImmutableMap) => { const accountId = status.getIn(['account', 'id']); const isSelfReply = accountId === status.get('in_reply_to_account_id'); @@ -147,14 +148,14 @@ const addSelfMention = status => { }; // Move the quote to the top-level -const fixQuote = status => { +const fixQuote = (status: ImmutableMap) => { return status.withMutations(status => { status.update('quote', quote => quote || status.getIn(['pleroma', 'quote']) || null); status.deleteIn(['pleroma', 'quote']); }); }; -export const normalizeStatus = status => { +export const normalizeStatus = (status: ImmutableMap): IStatus => { return StatusRecord( status.withMutations(status => { normalizeAttachments(status); diff --git a/app/soapbox/types/index.ts b/app/soapbox/types/index.ts index bc8cbd94e..df14c2720 100644 --- a/app/soapbox/types/index.ts +++ b/app/soapbox/types/index.ts @@ -1,3 +1,4 @@ import { IAccount } from './account'; +import { IStatus } from './status'; -export { IAccount }; +export { IAccount, IStatus }; diff --git a/app/soapbox/types/status.ts b/app/soapbox/types/status.ts new file mode 100644 index 000000000..a1a1ed061 --- /dev/null +++ b/app/soapbox/types/status.ts @@ -0,0 +1,45 @@ +/** + * Status entity. + * https://docs.joinmastodon.org/entities/status/ + **/ + +interface IStatus { + account: Record; + application: Record | null; + bookmarked: boolean; + card: Record | null; + content: string; + created_at: Date; + emojis: Iterable; + favourited: boolean; + favourites_count: number; + in_reply_to_account_id: string | null; + in_reply_to_id: string | null; + id: string; + language: null; + media_attachments: Iterable; + mentions: Iterable; + muted: boolean; + pinned: boolean; + pleroma: Record; + poll: null; + quote: null; + reblog: null; + reblogged: boolean; + reblogs_count: number; + replies_count: number; + sensitive: boolean; + spoiler_text: string; + tags: Iterable; + uri: string; + url: string; + visibility: string; + + // Internal fields + contentHtml: string; + hidden: boolean; + search_index: string; + spoilerHtml: string; +} + +export { IStatus }; From 9537c879099d75d134c2465001c1913a333dc82e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 9 Mar 2022 16:00:43 -0600 Subject: [PATCH 5/7] Record --> ImmutableRecord --- app/soapbox/normalizers/account.ts | 8 ++++++-- app/soapbox/normalizers/instance.js | 8 ++++++-- app/soapbox/normalizers/status.ts | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/soapbox/normalizers/account.ts b/app/soapbox/normalizers/account.ts index def1b497e..f3006f3c2 100644 --- a/app/soapbox/normalizers/account.ts +++ b/app/soapbox/normalizers/account.ts @@ -1,9 +1,13 @@ -import { Map as ImmutableMap, List as ImmutableList, Record } from 'immutable'; +import { + Map as ImmutableMap, + List as ImmutableList, + Record as ImmutableRecord, +} from 'immutable'; import { IAccount } from 'soapbox/types'; import { mergeDefined } from 'soapbox/utils/normalizers'; -const AccountRecord = Record({ +const AccountRecord = ImmutableRecord({ acct: '', avatar: '', avatar_static: '', diff --git a/app/soapbox/normalizers/instance.js b/app/soapbox/normalizers/instance.js index 22f90fc8d..537c60931 100644 --- a/app/soapbox/normalizers/instance.js +++ b/app/soapbox/normalizers/instance.js @@ -1,11 +1,15 @@ -import { Map as ImmutableMap, List as ImmutableList, Record } from 'immutable'; +import { + Map as ImmutableMap, + List as ImmutableList, + Record as ImmutableRecord, +} from 'immutable'; import { parseVersion, PLEROMA } from 'soapbox/utils/features'; import { mergeDefined } from 'soapbox/utils/normalizers'; import { isNumber } from 'soapbox/utils/numbers'; // Use Mastodon defaults -const InstanceRecord = Record({ +const InstanceRecord = ImmutableRecord({ approval_required: false, contact_account: ImmutableMap(), configuration: ImmutableMap({ diff --git a/app/soapbox/normalizers/status.ts b/app/soapbox/normalizers/status.ts index a4d8a8d3f..02c9b1f54 100644 --- a/app/soapbox/normalizers/status.ts +++ b/app/soapbox/normalizers/status.ts @@ -1,10 +1,14 @@ -import { Map as ImmutableMap, List as ImmutableList, Record } from 'immutable'; +import { + Map as ImmutableMap, + List as ImmutableList, + Record as ImmutableRecord, +} from 'immutable'; import { IStatus } from 'soapbox/types'; import { accountToMention } from 'soapbox/utils/accounts'; import { mergeDefined } from 'soapbox/utils/normalizers'; -const StatusRecord = Record({ +const StatusRecord = ImmutableRecord({ account: ImmutableMap(), application: null, bookmarked: false, From f75c0738a7f6da325dcbaab4b70cf7373f37373b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 9 Mar 2022 16:02:50 -0600 Subject: [PATCH 6/7] Instance normalizer: add default stats, remove default media_attachments --- app/soapbox/normalizers/__tests__/instance-test.js | 14 ++++++-------- app/soapbox/normalizers/instance.js | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/app/soapbox/normalizers/__tests__/instance-test.js b/app/soapbox/normalizers/__tests__/instance-test.js index f30f27492..597fbbeee 100644 --- a/app/soapbox/normalizers/__tests__/instance-test.js +++ b/app/soapbox/normalizers/__tests__/instance-test.js @@ -8,13 +8,7 @@ describe('normalizeInstance()', () => { approval_required: false, contact_account: {}, configuration: { - media_attachments: { - image_size_limit: 10485760, - image_matrix_limit: 16777216, - video_size_limit: 41943040, - video_frame_rate_limit: 60, - video_matrix_limit: 2304000, - }, + media_attachments: {}, polls: { max_options: 4, max_characters_per_option: 25, @@ -48,7 +42,11 @@ describe('normalizeInstance()', () => { registrations: false, rules: [], short_description: '', - stats: {}, + stats: { + domain_count: 0, + status_count: 0, + user_count: 0, + }, title: '', thumbnail: '', uri: '', diff --git a/app/soapbox/normalizers/instance.js b/app/soapbox/normalizers/instance.js index 537c60931..8ec6fde85 100644 --- a/app/soapbox/normalizers/instance.js +++ b/app/soapbox/normalizers/instance.js @@ -13,13 +13,7 @@ const InstanceRecord = ImmutableRecord({ approval_required: false, contact_account: ImmutableMap(), configuration: ImmutableMap({ - media_attachments: ImmutableMap({ - image_size_limit: 10485760, - image_matrix_limit: 16777216, - video_size_limit: 41943040, - video_frame_rate_limit: 60, - video_matrix_limit: 2304000, - }), + media_attachments: ImmutableMap(), polls: ImmutableMap({ max_options: 4, max_characters_per_option: 25, @@ -53,7 +47,11 @@ const InstanceRecord = ImmutableRecord({ registrations: false, rules: ImmutableList(), short_description: '', - stats: ImmutableMap(), + stats: ImmutableMap({ + domain_count: 0, + status_count: 0, + user_count: 0, + }), title: '', thumbnail: '', uri: '', From 4961433f7bf63d63d2453346305468733698767a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 9 Mar 2022 16:07:24 -0600 Subject: [PATCH 7/7] Actually, we can access state.instance.version directly now --- app/soapbox/reducers/__tests__/index-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/reducers/__tests__/index-test.js b/app/soapbox/reducers/__tests__/index-test.js index 2b4a21266..15572f337 100644 --- a/app/soapbox/reducers/__tests__/index-test.js +++ b/app/soapbox/reducers/__tests__/index-test.js @@ -7,6 +7,6 @@ describe('root reducer', () => { const result = reducer(undefined, {}); expect(ImmutableRecord.isRecord(result)).toBe(true); expect(result.accounts.get('')).toBe(undefined); - expect(result.instance.get('version')).toEqual('0.0.0'); + expect(result.instance.version).toEqual('0.0.0'); }); });