From 3fbc5716db1f0fb604f28e67ec88ccca9d8967c7 Mon Sep 17 00:00:00 2001 From: Chewbacca Date: Mon, 7 Nov 2022 15:41:14 -0500 Subject: [PATCH 1/6] Update colors in sidebar --- app/soapbox/components/sidebar-navigation-link.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/soapbox/components/sidebar-navigation-link.tsx b/app/soapbox/components/sidebar-navigation-link.tsx index 9a20ca4829..5c9100f851 100644 --- a/app/soapbox/components/sidebar-navigation-link.tsx +++ b/app/soapbox/components/sidebar-navigation-link.tsx @@ -37,16 +37,17 @@ const SidebarNavigationLink = React.forwardRef((props: ISidebarNavigationLink, r ref={ref} onClick={handleClick} className={classNames({ - 'flex items-center px-4 py-3.5 text-base font-semibold space-x-4 rounded-full group text-gray-600 hover:text-primary-600 dark:text-gray-500 dark:hover:text-gray-100 hover:bg-primary-100 dark:hover:bg-primary-700': true, - 'dark:text-gray-100 text-primary-600': isActive, + 'flex items-center px-4 py-3.5 text-base font-semibold space-x-4 rounded-full group text-gray-600 hover:text-gray-900 dark:text-gray-500 dark:hover:text-gray-100 hover:bg-primary-200 dark:hover:bg-primary-900': true, + 'dark:text-gray-100 text-gray-900': isActive, })} > From 0e1a369c36adc38132e73ca4a1cde30e08d458db Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 8 Nov 2022 11:48:56 -0600 Subject: [PATCH 2/6] translationRunner: fix root directory --- translationRunner.js | Bin 6232 -> 6226 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/translationRunner.js b/translationRunner.js index 26f3b221474965badcef0d8c662364a7ffd94e42..63c8022bec18ebdfe71b6e68b7a4013ebdd3abac 100644 GIT binary patch delta 12 Tcmca%aLHgp8sp|1#y4UBC6EO~ delta 19 acmca)aKm6j8Y7#Ig1Vlb`sOsoH(~%o!3H`2 From 4772f012534725700b64410f18690227226a5557 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 8 Nov 2022 12:57:12 -0600 Subject: [PATCH 3/6] translationRunner: convert to TypeScript --- package.json | 2 +- translationRunner.js => translationRunner.ts | Bin 6226 -> 6740 bytes .../index.d.ts | 154 ++++++++++++++++++ 3 files changed, 155 insertions(+), 1 deletion(-) rename translationRunner.js => translationRunner.ts (75%) create mode 100644 types/react-intl-translations-manager/index.d.ts diff --git a/package.json b/package.json index e4813bf503..576eec262f 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "dev": "${npm_execpath} run start", "build": "npx webpack", "jsdoc": "npx jsdoc -c jsdoc.conf.js", - "manage:translations": "node ./translationRunner.js", + "manage:translations": "npx ts-node ./translationRunner.ts", "test": "npx cross-env NODE_ENV=test npx jest", "test:coverage": "${npm_execpath} run test --coverage", "test:all": "${npm_execpath} run test:coverage && ${npm_execpath} run lint", diff --git a/translationRunner.js b/translationRunner.ts similarity index 75% rename from translationRunner.js rename to translationRunner.ts index 63c8022bec18ebdfe71b6e68b7a4013ebdd3abac..8cbda7a1a9d18c7044bf46ba8e8e5e4a90800eea 100644 GIT binary patch delta 1059 zcmZ`&O=}ZD7$#{%yAh44rlN>~@fQfrB)cY-WKXm2=kq-C%(v@5R@QAd@R5VXz(U^z z-D3J3Eer;Px5BVG%ETyxjN&kNxP5u+UyDmT(xeq69&-q{eUBAj50H|^W6GFJZQ2f% zv5n^(NuATi1Ip@X2i!;459b|advX)+1{9tW$8HdnEJ4m8Ub8Jn7{EB%UNaA+HR$+u zL*eo`MT^uaY=u=jl(iiWdm6y;#)6DdL>~7P|K{q->a({eWAWQS zSh%Qa=H-fE38##QWyWN9d7;m=FHTLqn|XCLVo)^;(1|Fru@p9Tf9-@wXrm3{^X#qO5c`x>QroIjWQVOe1QEZ`r}N6F!Em#*Oo%~R x;&uAyQdT(?2A{gUF@nOJyAjKa!@2Lt@o4M(FnZ933yZO&(m9;iU(clCe*rz1WW4|Y delta 545 zcmYjOL2DC17-eI6+0ZmLbrmmuy`;Oa*+g3-T12WCkm5xJ4<&~$&9`x2b~nzEK~eB1_$NGga5fdYb9?jNy!XCuzHNQ&zueDKZ6MZg6BIqog(80= z)*HdLd&11nJgIUeF4_OI(xj>aks8@eNNb!@oT&s&cZQ|t)bkJ~6k{&U4Y-_OYK4y! zrdpyAwoPb@;N<>^!%#@l@S+khKVI(y+tBL)X=%f{6QOY|scQj&NM@Nby-ZC=71y0s z4Nkin(&oYN&eg%z!1_r{r@4*h*JtuZ6|dCa@@`|9Ki7Bo`_*{N6mk~rZT&dMDwe)kEM diff --git a/types/react-intl-translations-manager/index.d.ts b/types/react-intl-translations-manager/index.d.ts new file mode 100644 index 0000000000..92d901d7c9 --- /dev/null +++ b/types/react-intl-translations-manager/index.d.ts @@ -0,0 +1,154 @@ +declare module 'react-intl-translations-manager' { + import type { MessageDescriptor } from 'react-intl'; + + export interface ExtractedDescriptor extends Omit { + variables: Set, + descriptors?: ExtractedDescriptor[], + defaultMessage: string, + } + + export interface ExtractedMessage { + path: string, + descriptors: ExtractedDescriptor[], + } + + export interface ManageTranslationsConfig { + /** + * Directory where the babel plugin puts the extracted messages. This path is relative to your projects root. + * + * example: `src/locales/extractedMessages` + */ + messagesDirectory: string, + /** + * Directory of the translation files the translation manager needs to maintain. + * + * example: `src/locales/lang` + */ + translationsDirectory: string, + /** + * Directory of the whitelist files the translation manager needs to maintain. These files contain the key of translations that have the exact same text in a specific language as the defaultMessage. Specifying this key will suppress `unmaintained translation` warnings. + * + * example: `Dashboard` in english is also accepted as a valid translation for dutch. + * + * (optional, default: `translationsDirectory`) + */ + whitelistsDirectory?: string, + /** + * What languages the translation manager needs to maintain. Specifying no languages actually doesn't make sense, but won't break the translationManager either. (Please do not include the default language, react-intl will automatically include it.) + * + * example: for `['nl', 'fr']` the translation manager will maintain a `nl.json`, `fr.json`, `whitelist_nl.json` and a w`hitelist_fr.json` file + * + * (optional, default: `[]`) + */ + languages?: string[], + /** + * Option to output a single JSON file containing the aggregate of all extracted messages, grouped by the file they were extracted from. + * + * example: + * + * ```json + * [ + * { + * "path": "src/components/foo.json", + * "descriptors": [ + * { + * "id": "bar", + * "description": "Text for bar", + * "defaultMessage": "Bar" + * } + * ] + * } + * ] + * ``` + * + * (optional, default: `false`) + */ + singleMessagesFile?: boolean, + /** + * If you want the translationManager to log duplicate message ids or not + * + * (optional, default: `true`) + */ + detectDuplicateIds?: boolean, + /** + * If you want the translationManager to sort it's output, both json and console output + * + * (optional, default: `true`) + */ + sortKeys?: boolean, + /** (optional, default: `{ space: 2, trailingNewline: false }`)) */ + jsonOptions?: any, + /** + * Here you can specify custom logging methods. If not specified a default printer is used. + * + * Possible printers to configure: + * + * ```js + * const printers = { + * printDuplicateIds: duplicateIds => { + * console.log(`You have ${duplicateIds.length} duplicate IDs`); + * }, + * printLanguageReport: report => { + * console.log('Log report for a language'); + * }, + * printNoLanguageFile: lang => { + * console.log( + * `No existing ${lang} translation file found. A new one is created.` + * ); + * }, + * printNoLanguageWhitelistFile: lang => { + * console.log(`No existing ${lang} file found. A new one is created.`); + * } + * }; + * ``` + * + * (optional, default: `{}`) + */ + overridePrinters?: any, + /** + * Here you can specify overrides for the core hooks. If not specified, the default methods will be used. + * + * Possible overrides to configure: + * + * ```js + * const overrideCoreMethods = { + * provideExtractedMessages: () => {}, + * outputSingleFile: () => {}, + * outputDuplicateKeys: () => {}, + * beforeReporting: () => {}, + * provideLangTemplate: () => {}, + * provideTranslationsFile: () => {}, + * provideWhitelistFile: () => {}, + * reportLanguage: () => {}, + * afterReporting: () => {} + * }; + * ``` + */ + overrideCoreMethods?: any, + } + + /** This will maintain all translation files. Based on your config you will get output for duplicate ids, and per specified language you will get the deleted translations, added messages (new messages that need to be translated), and not yet translated messages. It will also maintain a whitelist file per language where you can specify translation keys where the translation is identical to the default message. This way you can avoid untranslated message warnings for these messages. */ + export default function manageTranslations(config: ManageTranslationsConfig): void; + + /** + * This is a `babel-plugin-react-intl` specific helper method. It will read all extracted JSON file for the specified directory, filter out all files without any messages, and output an array with all messages. + * + * Example output: + * + * ```js + * const extractedMessages = [ + * { + * path: 'src/components/Foo.json', + * descriptors: [ + * { + * id: 'foo_ok', + * description: 'Ok text', + * defaultMessage: 'OK' + * } + * ] + * } + * ]; + * ``` + */ + export function readMessageFiles(messagesDirectory: string): ExtractedMessage[]; +} \ No newline at end of file From 51fc34ddea458f51bd9884f7824cd03bd9c5c80a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 8 Nov 2022 13:10:13 -0600 Subject: [PATCH 4/6] translationRunner: fix import of parser --- translationRunner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translationRunner.ts b/translationRunner.ts index 8cbda7a1a9..ad88f3db32 100644 --- a/translationRunner.ts +++ b/translationRunner.ts @@ -1,7 +1,7 @@ import fs from 'fs'; import path from 'path'; -import parser from 'intl-messageformat-parser'; +import * as parser from 'intl-messageformat-parser'; import manageTranslations, { readMessageFiles, ExtractedDescriptor } from 'react-intl-translations-manager'; type Validator = (language: string) => void; From 8ff1dddc7e3a53171454ee8ff51c786f717e1766 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 8 Nov 2022 15:33:47 -0600 Subject: [PATCH 5/6] translationRunner: improve types --- app/soapbox/locales/whitelist_is.json | 2 ++ translationRunner.ts | 15 ++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 app/soapbox/locales/whitelist_is.json diff --git a/app/soapbox/locales/whitelist_is.json b/app/soapbox/locales/whitelist_is.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/app/soapbox/locales/whitelist_is.json @@ -0,0 +1,2 @@ +[ +] diff --git a/translationRunner.ts b/translationRunner.ts index ad88f3db32..c4994648b1 100644 --- a/translationRunner.ts +++ b/translationRunner.ts @@ -116,8 +116,8 @@ manageTranslations({ // used in translations which are not used in the default message. /* eslint-disable no-console */ -function findVariablesinAST(tree: parser.MessageFormatElement[]) { - const result = new Set(); +function findVariablesinAST(tree: parser.MessageFormatElement[]): Set { + const result = new Set(); tree.forEach((element) => { switch (element.type) { case parser.TYPE.argument: @@ -142,7 +142,7 @@ function findVariablesinAST(tree: parser.MessageFormatElement[]) { return result; } -function findVariables(string: string) { +function findVariables(string: string): Set { return findVariablesinAST(parser.parse(string)); } @@ -161,14 +161,19 @@ const extractedMessages = extractedMessagesFiles.reduce((acc, messageFile) => { return acc; }, [] as ExtractedDescriptor[]); -const translations = languages.map((language: string) => { +interface Translation { + language: string, + data: Record, +} + +const translations: Translation[] = languages.map((language: string) => { return { language: language, data: JSON.parse(fs.readFileSync(path.join(translationsDirectory, language + '.json'), 'utf8')), }; }); -function difference(a: Set, b: Set) { +function difference(a: Set, b: Set): Set { return new Set(Array.from(a).filter(x => !b.has(x))); } From a93196b316b0a52d34cc1cb0476c0fd39690c8f8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 8 Nov 2022 22:34:17 -0600 Subject: [PATCH 6/6] Add support for Akkoma --- app/soapbox/__fixtures__/akkoma-instance.json | 105 ++++++++++++++++++ .../normalizers/__tests__/instance.test.ts | 8 ++ app/soapbox/normalizers/instance.ts | 12 ++ app/soapbox/utils/features.ts | 8 +- 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 app/soapbox/__fixtures__/akkoma-instance.json diff --git a/app/soapbox/__fixtures__/akkoma-instance.json b/app/soapbox/__fixtures__/akkoma-instance.json new file mode 100644 index 0000000000..a4da0dc94d --- /dev/null +++ b/app/soapbox/__fixtures__/akkoma-instance.json @@ -0,0 +1,105 @@ +{ + "approval_required": false, + "avatar_upload_limit": 2000000, + "background_image": "https://fe.disroot.org/images/city.jpg", + "background_upload_limit": 4000000, + "banner_upload_limit": 4000000, + "description": "FEDIsroot - Federated social network powered by Pleroma (open beta)", + "description_limit": 5000, + "email": "admin@example.lan", + "languages": [ + "en" + ], + "max_toot_chars": 5000, + "pleroma": { + "metadata": { + "account_activation_required": false, + "features": [ + "pleroma_api", + "akkoma_api", + "mastodon_api", + "mastodon_api_streaming", + "polls", + "v2_suggestions", + "pleroma_explicit_addressing", + "shareable_emoji_packs", + "multifetch", + "pleroma:api/v1/notifications:include_types_filter", + "editing", + "media_proxy", + "relay", + "pleroma_emoji_reactions", + "exposable_reactions", + "profile_directory", + "custom_emoji_reactions", + "pleroma:get:main/ostatus" + ], + "federation": { + "enabled": true, + "exclusions": false, + "mrf_hashtag": { + "federated_timeline_removal": [], + "reject": [], + "sensitive": [ + "nsfw" + ] + }, + "mrf_object_age": { + "actions": [ + "delist", + "strip_followers" + ], + "threshold": 604800 + }, + "mrf_policies": [ + "ObjectAgePolicy", + "TagPolicy", + "HashtagPolicy", + "InlineQuotePolicy" + ], + "quarantined_instances": [], + "quarantined_instances_info": { + "quarantined_instances": {} + } + }, + "fields_limits": { + "max_fields": 10, + "max_remote_fields": 20, + "name_length": 512, + "value_length": 2048 + }, + "post_formats": [ + "text/plain", + "text/html", + "text/markdown", + "text/bbcode", + "text/x.misskeymarkdown" + ], + "privileged_staff": false + }, + "stats": { + "mau": 83 + }, + "vapid_public_key": null + }, + "poll_limits": { + "max_expiration": 31536000, + "max_option_chars": 200, + "max_options": 20, + "min_expiration": 0 + }, + "registrations": false, + "stats": { + "domain_count": 6972, + "status_count": 8081, + "user_count": 357 + }, + "thumbnail": "https://fe.disroot.org/instance/thumbnail.jpeg", + "title": "FEDIsroot", + "upload_limit": 16000000, + "uri": "https://fe.disroot.org", + "urls": { + "streaming_api": "wss://fe.disroot.org" + }, + "version": "2.7.2 (compatible; Akkoma 3.3.1-0-gaf90a4e51)" +} diff --git a/app/soapbox/normalizers/__tests__/instance.test.ts b/app/soapbox/normalizers/__tests__/instance.test.ts index ab51c9159f..0df95fcb39 100644 --- a/app/soapbox/normalizers/__tests__/instance.test.ts +++ b/app/soapbox/normalizers/__tests__/instance.test.ts @@ -192,4 +192,12 @@ describe('normalizeInstance()', () => { const result = normalizeInstance(instance); expect(result.title).toBe('pixelfed'); }); + + it('renames Akkoma to Pleroma', () => { + const instance = require('soapbox/__fixtures__/akkoma-instance.json'); + const result = normalizeInstance(instance); + + expect(result.version).toEqual('2.7.2 (compatible; Pleroma 2.4.5+akkoma)'); + + }); }); diff --git a/app/soapbox/normalizers/instance.ts b/app/soapbox/normalizers/instance.ts index e137c21651..ff55ef4a52 100644 --- a/app/soapbox/normalizers/instance.ts +++ b/app/soapbox/normalizers/instance.ts @@ -98,6 +98,17 @@ const normalizeVersion = (instance: ImmutableMap) => { }); }; +/** Rename Akkoma to Pleroma+akkoma */ +const fixAkkoma = (instance: ImmutableMap) => { + const version: string = instance.get('version', ''); + + if (version.includes('Akkoma')) { + return instance.set('version', '2.7.2 (compatible; Pleroma 2.4.5+akkoma)'); + } else { + return instance; + } +}; + // Normalize instance (Pleroma, Mastodon, etc.) to Mastodon's format export const normalizeInstance = (instance: Record) => { return InstanceRecord( @@ -117,6 +128,7 @@ export const normalizeInstance = (instance: Record) => { // Normalize version normalizeVersion(instance); + fixAkkoma(instance); // Merge defaults instance.mergeDeepWith(mergeDefined, InstanceRecord()); diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index 823f8df1ab..3cb7e98dcb 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -57,6 +57,12 @@ export const SOAPBOX = 'soapbox'; */ export const GLITCH = 'glitch'; +/** + * Akkoma, a Pleroma fork. + * @see {@link https://akkoma.dev/AkkomaGang/akkoma} + */ +export const AKKOMA = 'akkoma'; + /** Parse features for the given instance */ const getInstanceFeatures = (instance: Instance) => { const v = parseVersion(instance.version); @@ -202,7 +208,7 @@ const getInstanceFeatures = (instance: Instance) => { * Pleroma chats API. * @see {@link https://docs.pleroma.social/backend/development/API/chats/} */ - chats: v.software === PLEROMA && gte(v.version, '2.1.0'), + chats: v.software === PLEROMA && gte(v.version, '2.1.0') && v.build !== AKKOMA, /** * Paginated chats API.