From c2ea0dc65fe31db4af0d43fd74f3482d931d7b08 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 31 May 2023 08:42:40 +0200 Subject: [PATCH 01/69] Drop support for Node.js 14 (#25198) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f9b78b42b2..bdf34dcf01 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@mastodon/mastodon", "license": "AGPL-3.0-or-later", "engines": { - "node": ">=14" + "node": ">=16" }, "scripts": { "postversion": "git push --tags", From 89d6341c80f5b6cb88ef7efd90622891200249f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 09:27:21 +0200 Subject: [PATCH 02/69] Bump eslint-plugin-jsdoc from 44.2.5 to 45.0.0 (#25172) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Renaud Chaput --- .eslintrc.js | 2 +- app/javascript/mastodon/utils/numbers.ts | 13 +++++++------ package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2b0907b1d5..bfade8976f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -323,7 +323,7 @@ module.exports = { 'plugin:import/recommended', 'plugin:import/typescript', 'plugin:promise/recommended', - 'plugin:jsdoc/recommended', + 'plugin:jsdoc/recommended-typescript', 'plugin:prettier/recommended', ], diff --git a/app/javascript/mastodon/utils/numbers.ts b/app/javascript/mastodon/utils/numbers.ts index a4a028c30e..4c8465f577 100644 --- a/app/javascript/mastodon/utils/numbers.ts +++ b/app/javascript/mastodon/utils/numbers.ts @@ -14,14 +14,15 @@ export type DecimalUnits = ValueOf; const TEN_THOUSAND = DECIMAL_UNITS.THOUSAND * 10; const TEN_MILLIONS = DECIMAL_UNITS.MILLION * 10; +export type ShortNumber = [number, DecimalUnits, 0 | 1]; // Array of: shorten number, unit of shorten number and maximum fraction digits + /** - * @param {number} sourceNumber Number to convert to short number - * @returns {ShortNumber} Calculated short number + * @param sourceNumber Number to convert to short number + * @returns Calculated short number * @example * shortNumber(5936); * // => [5.936, 1000, 1] */ -export type ShortNumber = [number, DecimalUnits, 0 | 1]; // Array of: shorten number, unit of shorten number and maximum fraction digits export function toShortNumber(sourceNumber: number): ShortNumber { if (sourceNumber < DECIMAL_UNITS.THOUSAND) { return [sourceNumber, DECIMAL_UNITS.ONE, 0]; @@ -45,9 +46,9 @@ export function toShortNumber(sourceNumber: number): ShortNumber { } /** - * @param {number} sourceNumber Original number that is shortened - * @param {number} division The scale in which short number is displayed - * @returns {number} Number that can be used for plurals when short form used + * @param sourceNumber Original number that is shortened + * @param division The scale in which short number is displayed + * @returns Number that can be used for plurals when short form used * @example * pluralReady(1793, DECIMAL_UNITS.THOUSAND) * // => 1790 diff --git a/package.json b/package.json index bdf34dcf01..ea8f98cd0a 100644 --- a/package.json +++ b/package.json @@ -182,7 +182,7 @@ "eslint-import-resolver-typescript": "^3.5.5", "eslint-plugin-formatjs": "^4.10.1", "eslint-plugin-import": "~2.27.5", - "eslint-plugin-jsdoc": "^44.2.5", + "eslint-plugin-jsdoc": "^45.0.0", "eslint-plugin-jsx-a11y": "~6.7.1", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-promise": "~6.1.1", diff --git a/yarn.lock b/yarn.lock index b2c4ced872..f0040f5de5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5070,10 +5070,10 @@ eslint-plugin-import@~2.27.5: semver "^6.3.0" tsconfig-paths "^3.14.1" -eslint-plugin-jsdoc@^44.2.5: - version "44.2.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-44.2.5.tgz#f3186f57f112a230b3b7af34bf236d207bc8d5d7" - integrity sha512-KtuhaYy2GmdY2IQE5t+1lup8O4P05c+V4gKcj45PCxFM0OxmRq2uQlfOS1AgYVgPYIBKGE86DxrbKP24HKpORA== +eslint-plugin-jsdoc@^45.0.0: + version "45.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-45.0.0.tgz#6be84e4842a7138cc571a907ea9c31c42eaac5c0" + integrity sha512-l2+Jcs/Ps7oFA+SWY+0sweU/e5LgricnEl6EsDlyRTF5y0+NWL1y9Qwz9PHwHAxtdJq6lxPjEQWmYLMkvhzD4g== dependencies: "@es-joy/jsdoccomment" "~0.39.4" are-docs-informative "^0.0.2" From 98e5e011aab13a830a0dc48b66beed5df50dea01 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 31 May 2023 09:29:16 +0200 Subject: [PATCH 03/69] Upgrade Workbox to 6.6.0 (#25197) --- package.json | 12 +-- yarn.lock | 216 +++++++++++++++++++++++++-------------------------- 2 files changed, 114 insertions(+), 114 deletions(-) diff --git a/package.json b/package.json index ea8f98cd0a..540311ded2 100644 --- a/package.json +++ b/package.json @@ -130,12 +130,12 @@ "webpack-cli": "^3.3.12", "webpack-merge": "^5.9.0", "wicg-inert": "^3.1.2", - "workbox-expiration": "^6.5.4", - "workbox-precaching": "^6.5.4", - "workbox-routing": "^6.5.4", - "workbox-strategies": "^6.5.4", - "workbox-webpack-plugin": "^6.5.4", - "workbox-window": "^6.5.4", + "workbox-expiration": "^6.6.0", + "workbox-precaching": "^6.6.0", + "workbox-routing": "^6.6.0", + "workbox-strategies": "^6.6.0", + "workbox-webpack-plugin": "^6.6.0", + "workbox-window": "^6.6.0", "ws": "^8.12.1" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index f0040f5de5..1879ecdc63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12120,25 +12120,25 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -workbox-background-sync@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz#3141afba3cc8aa2ae14c24d0f6811374ba8ff6a9" - integrity sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g== +workbox-background-sync@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.6.1.tgz#08d603a33717ce663e718c30cc336f74909aff2f" + integrity sha512-trJd3ovpWCvzu4sW0E8rV3FUyIcC0W8G+AZ+VcqzzA890AsWZlUGOTSxIMmIHVusUw/FDq1HFWfy/kC/WTRqSg== dependencies: idb "^7.0.1" - workbox-core "6.5.4" + workbox-core "6.6.1" -workbox-broadcast-update@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz#8441cff5417cd41f384ba7633ca960a7ffe40f66" - integrity sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw== +workbox-broadcast-update@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.6.1.tgz#0fad9454cf8e4ace0c293e5617c64c75d8a8c61e" + integrity sha512-fBhffRdaANdeQ1V8s692R9l/gzvjjRtydBOvR6WCSB0BNE2BacA29Z4r9/RHd9KaXCPl6JTdI9q0bR25YKP8TQ== dependencies: - workbox-core "6.5.4" + workbox-core "6.6.1" -workbox-build@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.5.4.tgz#7d06d31eb28a878817e1c991c05c5b93409f0389" - integrity sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA== +workbox-build@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.6.1.tgz#6010e9ce550910156761448f2dbea8cfcf759cb0" + integrity sha512-INPgDx6aRycAugUixbKgiEQBWD0MPZqU5r0jyr24CehvNuLPSXp/wGOpdRJmts656lNiXwqV7dC2nzyrzWEDnw== dependencies: "@apideck/better-ajv-errors" "^0.3.1" "@babel/core" "^7.11.1" @@ -12162,132 +12162,132 @@ workbox-build@6.5.4: strip-comments "^2.0.1" tempy "^0.6.0" upath "^1.2.0" - workbox-background-sync "6.5.4" - workbox-broadcast-update "6.5.4" - workbox-cacheable-response "6.5.4" - workbox-core "6.5.4" - workbox-expiration "6.5.4" - workbox-google-analytics "6.5.4" - workbox-navigation-preload "6.5.4" - workbox-precaching "6.5.4" - workbox-range-requests "6.5.4" - workbox-recipes "6.5.4" - workbox-routing "6.5.4" - workbox-strategies "6.5.4" - workbox-streams "6.5.4" - workbox-sw "6.5.4" - workbox-window "6.5.4" + workbox-background-sync "6.6.1" + workbox-broadcast-update "6.6.1" + workbox-cacheable-response "6.6.1" + workbox-core "6.6.1" + workbox-expiration "6.6.1" + workbox-google-analytics "6.6.1" + workbox-navigation-preload "6.6.1" + workbox-precaching "6.6.1" + workbox-range-requests "6.6.1" + workbox-recipes "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" + workbox-streams "6.6.1" + workbox-sw "6.6.1" + workbox-window "6.6.1" -workbox-cacheable-response@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz#a5c6ec0c6e2b6f037379198d4ef07d098f7cf137" - integrity sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug== +workbox-cacheable-response@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.6.1.tgz#284c2b86be3f4fd191970ace8c8e99797bcf58e9" + integrity sha512-85LY4veT2CnTCDxaVG7ft3NKaFbH6i4urZXgLiU4AiwvKqS2ChL6/eILiGRYXfZ6gAwDnh5RkuDbr/GMS4KSag== dependencies: - workbox-core "6.5.4" + workbox-core "6.6.1" -workbox-core@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.5.4.tgz#df48bf44cd58bb1d1726c49b883fb1dffa24c9ba" - integrity sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q== +workbox-core@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.6.1.tgz#7184776d4134c5ed2f086878c882728fc9084265" + integrity sha512-ZrGBXjjaJLqzVothoE12qTbVnOAjFrHDXpZe7coCb6q65qI/59rDLwuFMO4PcZ7jcbxY+0+NhUVztzR/CbjEFw== -workbox-expiration@6.5.4, workbox-expiration@^6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.5.4.tgz#501056f81e87e1d296c76570bb483ce5e29b4539" - integrity sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ== +workbox-expiration@6.6.1, workbox-expiration@^6.6.0: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.6.1.tgz#a841fa36676104426dbfb9da1ef6a630b4f93739" + integrity sha512-qFiNeeINndiOxaCrd2DeL1Xh1RFug3JonzjxUHc5WkvkD2u5abY3gZL1xSUNt3vZKsFFGGORItSjVTVnWAZO4A== dependencies: idb "^7.0.1" - workbox-core "6.5.4" + workbox-core "6.6.1" -workbox-google-analytics@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz#c74327f80dfa4c1954cbba93cd7ea640fe7ece7d" - integrity sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg== +workbox-google-analytics@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.6.1.tgz#a07a6655ab33d89d1b0b0a935ffa5dea88618c5d" + integrity sha512-1TjSvbFSLmkpqLcBsF7FuGqqeDsf+uAXO/pjiINQKg3b1GN0nBngnxLcXDYo1n/XxK4N7RaRrpRlkwjY/3ocuA== dependencies: - workbox-background-sync "6.5.4" - workbox-core "6.5.4" - workbox-routing "6.5.4" - workbox-strategies "6.5.4" + workbox-background-sync "6.6.1" + workbox-core "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" -workbox-navigation-preload@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz#ede56dd5f6fc9e860a7e45b2c1a8f87c1c793212" - integrity sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng== +workbox-navigation-preload@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.6.1.tgz#61a34fe125558dd88cf09237f11bd966504ea059" + integrity sha512-DQCZowCecO+wRoIxJI2V6bXWK6/53ff+hEXLGlQL4Rp9ZaPDLrgV/32nxwWIP7QpWDkVEtllTAK5h6cnhxNxDA== dependencies: - workbox-core "6.5.4" + workbox-core "6.6.1" -workbox-precaching@6.5.4, workbox-precaching@^6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.5.4.tgz#740e3561df92c6726ab5f7471e6aac89582cab72" - integrity sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg== +workbox-precaching@6.6.1, workbox-precaching@^6.6.0: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.6.1.tgz#dedeeba10a2d163d990bf99f1c2066ac0d1a19e2" + integrity sha512-K4znSJ7IKxCnCYEdhNkMr7X1kNh8cz+mFgx9v5jFdz1MfI84pq8C2zG+oAoeE5kFrUf7YkT5x4uLWBNg0DVZ5A== dependencies: - workbox-core "6.5.4" - workbox-routing "6.5.4" - workbox-strategies "6.5.4" + workbox-core "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" -workbox-range-requests@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz#86b3d482e090433dab38d36ae031b2bb0bd74399" - integrity sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg== +workbox-range-requests@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.6.1.tgz#ddaf7e73af11d362fbb2f136a9063a4c7f507a39" + integrity sha512-4BDzk28govqzg2ZpX0IFkthdRmCKgAKreontYRC5YsAPB2jDtPNxqx3WtTXgHw1NZalXpcH/E4LqUa9+2xbv1g== dependencies: - workbox-core "6.5.4" + workbox-core "6.6.1" -workbox-recipes@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.5.4.tgz#cca809ee63b98b158b2702dcfb741b5cc3e24acb" - integrity sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA== +workbox-recipes@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.6.1.tgz#ea70d2b2b0b0bce8de0a9d94f274d4a688e69fae" + integrity sha512-/oy8vCSzromXokDA+X+VgpeZJvtuf8SkQ8KL0xmRivMgJZrjwM3c2tpKTJn6PZA6TsbxGs3Sc7KwMoZVamcV2g== dependencies: - workbox-cacheable-response "6.5.4" - workbox-core "6.5.4" - workbox-expiration "6.5.4" - workbox-precaching "6.5.4" - workbox-routing "6.5.4" - workbox-strategies "6.5.4" + workbox-cacheable-response "6.6.1" + workbox-core "6.6.1" + workbox-expiration "6.6.1" + workbox-precaching "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" -workbox-routing@6.5.4, workbox-routing@^6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.5.4.tgz#6a7fbbd23f4ac801038d9a0298bc907ee26fe3da" - integrity sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg== +workbox-routing@6.6.1, workbox-routing@^6.6.0: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.6.1.tgz#cba9a1c7e0d1ea11e24b6f8c518840efdc94f581" + integrity sha512-j4ohlQvfpVdoR8vDYxTY9rA9VvxTHogkIDwGdJ+rb2VRZQ5vt1CWwUUZBeD/WGFAni12jD1HlMXvJ8JS7aBWTg== dependencies: - workbox-core "6.5.4" + workbox-core "6.6.1" -workbox-strategies@6.5.4, workbox-strategies@^6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.5.4.tgz#4edda035b3c010fc7f6152918370699334cd204d" - integrity sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw== +workbox-strategies@6.6.1, workbox-strategies@^6.6.0: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.6.1.tgz#38d0f0fbdddba97bd92e0c6418d0b1a2ccd5b8bf" + integrity sha512-WQLXkRnsk4L81fVPkkgon1rZNxnpdO5LsO+ws7tYBC6QQQFJVI6v98klrJEjFtZwzw/mB/HT5yVp7CcX0O+mrw== dependencies: - workbox-core "6.5.4" + workbox-core "6.6.1" -workbox-streams@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.5.4.tgz#1cb3c168a6101df7b5269d0353c19e36668d7d69" - integrity sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg== +workbox-streams@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.6.1.tgz#b2f7ba7b315c27a6e3a96a476593f99c5d227d26" + integrity sha512-maKG65FUq9e4BLotSKWSTzeF0sgctQdYyTMq529piEN24Dlu9b6WhrAfRpHdCncRS89Zi2QVpW5V33NX8PgH3Q== dependencies: - workbox-core "6.5.4" - workbox-routing "6.5.4" + workbox-core "6.6.1" + workbox-routing "6.6.1" -workbox-sw@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.5.4.tgz#d93e9c67924dd153a61367a4656ff4d2ae2ed736" - integrity sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA== +workbox-sw@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.6.1.tgz#d4c4ca3125088e8b9fd7a748ed537fa0247bd72c" + integrity sha512-R7whwjvU2abHH/lR6kQTTXLHDFU2izht9kJOvBRYK65FbwutT4VvnUAJIgHvfWZ/fokrOPhfoWYoPCMpSgUKHQ== -workbox-webpack-plugin@^6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz#baf2d3f4b8f435f3469887cf4fba2b7fac3d0fd7" - integrity sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg== +workbox-webpack-plugin@^6.6.0: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.1.tgz#4f81cc1ad4e5d2cd7477a86ba83c84ee2d187531" + integrity sha512-zpZ+ExFj9NmiI66cFEApyjk7hGsfJ1YMOaLXGXBoZf0v7Iu6hL0ZBe+83mnDq3YYWAfA3fnyFejritjOHkFcrA== dependencies: fast-json-stable-stringify "^2.1.0" pretty-bytes "^5.4.1" upath "^1.2.0" webpack-sources "^1.4.3" - workbox-build "6.5.4" + workbox-build "6.6.1" -workbox-window@6.5.4, workbox-window@^6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.5.4.tgz#d991bc0a94dff3c2dbb6b84558cff155ca878e91" - integrity sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug== +workbox-window@6.6.1, workbox-window@^6.6.0: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.6.1.tgz#f22a394cbac36240d0dadcbdebc35f711bb7b89e" + integrity sha512-wil4nwOY58nTdCvif/KEZjQ2NP8uk3gGeRNy2jPBbzypU4BT4D9L8xiwbmDBpZlSgJd2xsT9FvSNU0gsxV51JQ== dependencies: "@types/trusted-types" "^2.0.2" - workbox-core "6.5.4" + workbox-core "6.6.1" wrap-ansi@^5.1.0: version "5.1.0" From bf9a7ce8e8bcb3ac57322e13fc35095fcce60fba Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Wed, 31 May 2023 04:33:57 -0300 Subject: [PATCH 04/69] Add test coverage for `Mastodon::CLI::Accounts#unfollow` (#25162) --- spec/lib/mastodon/cli/accounts_spec.rb | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/spec/lib/mastodon/cli/accounts_spec.rb b/spec/lib/mastodon/cli/accounts_spec.rb index bcb353aeee..a7fcc281fd 100644 --- a/spec/lib/mastodon/cli/accounts_spec.rb +++ b/spec/lib/mastodon/cli/accounts_spec.rb @@ -577,4 +577,51 @@ describe Mastodon::CLI::Accounts do end end end + + describe '#unfollow' do + context 'when the given username is not found' do + let(:arguments) { ['non_existent_username'] } + + it 'exits with an error message indicating that no account with the given username was found' do + expect { cli.invoke(:unfollow, arguments) }.to output( + a_string_including('No such account') + ).to_stdout + .and raise_error(SystemExit) + end + end + + context 'when the given username is found' do + let!(:target_account) { Fabricate(:account) } + let!(:follower_chris) { Fabricate(:account, username: 'chris') } + let!(:follower_rambo) { Fabricate(:account, username: 'rambo') } + let!(:follower_ana) { Fabricate(:account, username: 'ana') } + let(:unfollow_service) { instance_double(UnfollowService, call: nil) } + let(:scope) { target_account.followers.local } + + before do + accounts = [follower_chris, follower_rambo, follower_ana] + accounts.each { |account| target_account.follow!(account) } + allow(cli).to receive(:parallelize_with_progress).and_yield(follower_chris) + .and_yield(follower_rambo) + .and_yield(follower_ana) + .and_return([3, nil]) + allow(UnfollowService).to receive(:new).and_return(unfollow_service) + end + + it 'makes all local accounts unfollow the target account' do + cli.unfollow(target_account.username) + + expect(cli).to have_received(:parallelize_with_progress).with(scope).once + expect(unfollow_service).to have_received(:call).with(follower_chris, target_account).once + expect(unfollow_service).to have_received(:call).with(follower_rambo, target_account).once + expect(unfollow_service).to have_received(:call).with(follower_ana, target_account).once + end + + it 'displays a successful message' do + expect { cli.unfollow(target_account.username) }.to output( + a_string_including('OK, unfollowed target from 3 accounts') + ).to_stdout + end + end + end end From 0ed8164777fa7a38879e946ace86f1a7013424eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 09:36:43 +0200 Subject: [PATCH 05/69] Bump @babel/preset-env from 7.21.5 to 7.22.4 (#25167) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claire --- babel.config.js | 6 +- package.json | 4 +- yarn.lock | 573 +++++++++++++++++++++++++++++++----------------- 3 files changed, 372 insertions(+), 211 deletions(-) diff --git a/babel.config.js b/babel.config.js index 0a2e69a476..986d605495 100644 --- a/babel.config.js +++ b/babel.config.js @@ -11,7 +11,7 @@ module.exports = (api) => { modules: false, debug: false, include: [ - 'proposal-numeric-separator', + 'transform-numeric-separator', ], }; @@ -24,8 +24,8 @@ module.exports = (api) => { plugins: [ ['react-intl', { messagesDir: './build/messages' }], 'preval', - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-nullish-coalescing-operator', + '@babel/plugin-transform-optional-chaining', + '@babel/plugin-transform-nullish-coalescing-operator', ], overrides: [ { diff --git a/package.json b/package.json index 540311ded2..1b32a58096 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,10 @@ "private": true, "dependencies": { "@babel/core": "^7.21.8", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.3", "@babel/plugin-transform-react-inline-elements": "^7.21.0", "@babel/plugin-transform-runtime": "^7.21.4", - "@babel/preset-env": "^7.21.5", + "@babel/preset-env": "^7.22.4", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.5", "@babel/runtime": "^7.21.5", diff --git a/yarn.lock b/yarn.lock index 1879ecdc63..3ad49f0228 100644 --- a/yarn.lock +++ b/yarn.lock @@ -31,10 +31,10 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5": - version "7.21.7" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc" - integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.22.0", "@babel/compat-data@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.3.tgz#cd502a6a0b6e37d7ad72ce7e71a7160a3ae36f7e" + integrity sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ== "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.21.8", "@babel/core@^7.7.2": version "7.21.8" @@ -67,6 +67,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e" + integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A== + dependencies: + "@babel/types" "^7.22.3" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -90,31 +100,17 @@ "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/types" "^7.19.0" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz#631e6cc784c7b660417421349aac304c94115366" - integrity sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5", "@babel/helper-compilation-targets@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz#bfcd6b7321ffebe33290d68550e2c9d7eb7c7a58" + integrity sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ== dependencies: - "@babel/compat-data" "^7.21.5" + "@babel/compat-data" "^7.22.0" "@babel/helper-validator-option" "^7.21.0" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9" - integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-member-expression-to-functions" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-create-class-features-plugin@^7.21.0": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" @@ -129,6 +125,21 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" +"@babel/helper-create-class-features-plugin@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz#ae3de70586cc757082ae3eba57240d42f468c41b" + integrity sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.22.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.22.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-split-export-declaration" "^7.18.6" + semver "^6.3.0" + "@babel/helper-create-regexp-features-plugin@^7.18.6": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" @@ -137,13 +148,14 @@ "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.1.0" -"@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz#40411a8ab134258ad2cf3a3d987ec6aa0723cee5" - integrity sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA== +"@babel/helper-create-regexp-features-plugin@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz#a7ed9a8488b45b467fca353cd1a44dc5f0cf5c70" + integrity sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.3.1" + semver "^6.3.0" "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" @@ -157,6 +169,18 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-define-polyfill-provider@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz#487053f103110f25b9755c5980e031e93ced24d8" + integrity sha512-RnanLx5ETe6aybRi1cO/edaRH+bNYWaryCEmjDDYyNr4wnSzyOp8T0dWipmqVHKEY3AbVKUom50AKSlj1zmKbg== + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + "@babel/helper-environment-visitor@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" @@ -167,6 +191,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba" integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ== +"@babel/helper-environment-visitor@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8" + integrity sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA== + "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" @@ -211,6 +240,13 @@ dependencies: "@babel/types" "^7.21.0" +"@babel/helper-member-expression-to-functions@^7.22.0": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz#4b77a12c1b4b8e9e28736ed47d8b91f00976911f" + integrity sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA== + dependencies: + "@babel/types" "^7.22.3" + "@babel/helper-module-imports@^7.0.0-beta.49", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" @@ -232,6 +268,20 @@ "@babel/traverse" "^7.21.5" "@babel/types" "^7.21.5" +"@babel/helper-module-transforms@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz#e0cad47fedcf3cae83c11021696376e2d5a50c63" + integrity sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-simple-access" "^7.21.5" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -266,6 +316,18 @@ "@babel/traverse" "^7.20.7" "@babel/types" "^7.20.7" +"@babel/helper-replace-supers@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz#38cf6e56f7dc614af63a21b45565dd623f0fdc95" + integrity sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-member-expression-to-functions" "^7.22.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" + "@babel/helper-simple-access@^7.21.5": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" @@ -335,6 +397,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== +"@babel/parser@^7.21.9", "@babel/parser@^7.22.4": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" + integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -342,125 +409,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" - integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz#a75be1365c0c3188c51399a662168c1c98108659" + integrity sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-proposal-optional-chaining" "^7.20.7" - -"@babel/plugin-proposal-async-generator-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" - integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-class-static-block@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" - integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.21.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" - integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" - integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" - integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" - integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" - integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" - integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" - integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.7" - -"@babel/plugin-proposal-optional-catch-binding@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" - integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" - integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-optional-chaining" "^7.22.3" "@babel/plugin-proposal-private-property-in-object@^7.21.0": version "7.21.0" @@ -472,7 +428,7 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== @@ -529,6 +485,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" +"@babel/plugin-syntax-import-attributes@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.3.tgz#d7168f22b9b49a6cc1792cec78e06a18ad2e7b4b" + integrity sha512-i35jZJv6aO7hxEbIWQ41adVfOzjm9dcYDNeWlBMd8p0ZQRtNUCBrmGwZt+H5lb+oOC9a3svp956KP0oWGA1YsA== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -620,6 +583,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-arrow-functions@^7.21.5": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929" @@ -627,6 +598,16 @@ dependencies: "@babel/helper-plugin-utils" "^7.21.5" +"@babel/plugin-transform-async-generator-functions@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.3.tgz#3ed99924c354fb9e80dabb2cc8d002c702e94527" + integrity sha512-36A4Aq48t66btydbZd5Fk0/xJqbpg/v4QWI4AH4cYHBXy9Mu42UOupZpebKFiCFNT9S9rJFcsld0gsv0ayLjtA== + dependencies: + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-transform-async-to-generator@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" @@ -650,6 +631,23 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-class-properties@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.3.tgz#3407145e513830df77f0cef828b8b231c166fe4c" + integrity sha512-mASLsd6rhOrLZ5F3WbCxkzl67mmOnqik0zrg5W6D/X0QMW7HtvnoL1dRARLKIbMP3vXwkwziuLesPqWVGIl6Bw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-class-static-block@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.3.tgz#e352cf33567385c731a8f21192efeba760358773" + integrity sha512-5BirgNWNOx7cwbTJCOmKFJ1pZjwk5MUfMIwiBBvsirCJMZeQgs5pk6i1OlkVg+1Vef5LfBahFOrdCnAWvkVKMw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-transform-classes@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" @@ -695,6 +693,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-dynamic-import@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.1.tgz#6c56afaf896a07026330cf39714532abed8d9ed1" + integrity sha512-rlhWtONnVBPdmt+jeewS0qSnMz/3yLFrqAP8hHC6EDcrYRSyuz9f9yQhHvVn2Ad6+yO9fHXac5piudeYrInxwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-exponentiation-operator@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" @@ -703,6 +709,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-export-namespace-from@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.3.tgz#9b8700aa495007d3bebac8358d1c562434b680b9" + integrity sha512-5Ti1cHLTDnt3vX61P9KZ5IG09bFXp4cDVFJIAeCZuxu9OXXJJZp5iP0n/rzM2+iAutJY+KWEyyHcRaHlpQ/P5g== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-transform-for-of@^7.21.5": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc" @@ -719,6 +733,14 @@ "@babel/helper-function-name" "^7.18.9" "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-json-strings@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.3.tgz#a181b8679cf7c93e9d0e3baa5b1776d65be601a9" + integrity sha512-IuvOMdeOOY2X4hRNAT6kwbePtK21BUyrAEgLKviL8pL6AEEVUVcqtRdN/HJXBLGIbt9T3ETmXRnFedRRmQNTYw== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-transform-literals@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" @@ -726,6 +748,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-logical-assignment-operators@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.3.tgz#9e021455810f33b0baccb82fb759b194f5dc36f0" + integrity sha512-CbayIfOw4av2v/HYZEsH+Klks3NC2/MFIR3QR8gnpGNNPEaq2fdlVCRYG/paKs7/5hvBLQ+H70pGWOHtlNEWNA== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-transform-member-expression-literals@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" @@ -750,14 +780,14 @@ "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-simple-access" "^7.21.5" -"@babel/plugin-transform-modules-systemjs@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" - integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== +"@babel/plugin-transform-modules-systemjs@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz#cc507e03e88d87b016feaeb5dae941e6ef50d91e" + integrity sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw== dependencies: "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-module-transforms" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-validator-identifier" "^7.19.1" "@babel/plugin-transform-modules-umd@^7.18.6": @@ -768,20 +798,47 @@ "@babel/helper-module-transforms" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" - integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz#db6fb77e6b3b53ec3b8d370246f0b7cf67d35ab4" + integrity sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-create-regexp-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" -"@babel/plugin-transform-new-target@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" - integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== +"@babel/plugin-transform-new-target@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz#deb0377d741cbee2f45305868b9026dcd6dd96e2" + integrity sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.3.tgz#8c519f8bf5af94a9ca6f65cf422a9d3396e542b9" + integrity sha512-CpaoNp16nX7ROtLONNuCyenYdY/l7ZsR6aoVa7rW7nMWisoNoQNIH5Iay/4LDyRjKMuElMqXiBoOQCDLTMGZiw== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.3.tgz#02493070ca6685884b0eee705363ee4da2132ab0" + integrity sha512-+AF88fPDJrnseMh5vD9+SH6wq4ZMvpiTMHh58uLs+giMEyASFVhcT3NkoyO+NebFCNnpHJEq5AXO2txV4AGPDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.3.tgz#da6fba693effb8c203d8c3bdf7bf4e2567e802e9" + integrity sha512-38bzTsqMMCI46/TQnJwPPpy33EjLCc1Gsm2hRTF6zTMWnKsN61vdrpuzIEGQyKEhDSYDKyZHrrd5FMj4gcUHhw== + dependencies: + "@babel/compat-data" "^7.22.3" + "@babel/helper-compilation-targets" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.22.3" "@babel/plugin-transform-object-super@^7.18.6": version "7.18.6" @@ -791,12 +848,47 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" - integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== +"@babel/plugin-transform-optional-catch-binding@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.3.tgz#e971a083fc7d209d9cd18253853af1db6d8dc42f" + integrity sha512-bnDFWXFzWY0BsOyqaoSXvMQ2F35zutQipugog/rqotL2S4ciFOKlRYUu9djt4iq09oh2/34hqfRR2k1dIvuu4g== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz#5fd24a4a7843b76da6aeec23c7f551da5d365290" + integrity sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz#24477acfd2fd2bc901df906c9bf17fbcfeee900d" + integrity sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-private-methods@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.3.tgz#adac38020bab5047482d3297107c1f58e9c574f6" + integrity sha512-fC7jtjBPFqhqpPAE+O4LKwnLq7gGkD3ZmC2E3i4qWH34mH3gOg2Xrq5YMHUq6DM30xhqM1DNftiRaSqVjEG+ug== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-private-property-in-object@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.3.tgz#031621b02c7b7d95389de1a3dba2fe9e8c548e56" + integrity sha512-C7MMl4qWLpgVCbXfj3UW8rR1xeCnisQ0cU7YJHV//8oNBS0aCIVg1vFnZXxOckHhEpQyqNNkWmvSEWnMLlc+Vw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-transform-property-literals@^7.18.6": version "7.18.6" @@ -926,6 +1018,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.21.5" +"@babel/plugin-transform-unicode-property-regex@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.3.tgz#597b6a614dc93eaae605ee293e674d79d32eb380" + integrity sha512-5ScJ+OmdX+O6HRuMGW4kv7RL9vIKdtdAj9wuWUKy1wbHY3jaM/UlyIiC1G7J6UJiiyMukjjK0QwL3P0vBd0yYg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-transform-unicode-regex@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" @@ -934,38 +1034,33 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.5.tgz#db2089d99efd2297716f018aeead815ac3decffb" - integrity sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg== +"@babel/plugin-transform-unicode-sets-regex@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.3.tgz#7c14ee33fa69782b0101d0f7143d3fc73ce00700" + integrity sha512-hNufLdkF8vqywRp+P55j4FHXqAX2LRUccoZHH7AFn1pq5ZOO2ISKW9w13bFZVjBoTqeve2HOgoJCcaziJVhGNw== dependencies: - "@babel/compat-data" "^7.21.5" - "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.22.4": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.4.tgz#c86a82630f0e8c61d9bb9327b7b896732028cbed" + integrity sha512-c3lHOjbwBv0TkhYCr+XCR6wKcSZ1QbQTVdSkZUaVpLv8CVWotBMArWUi5UAJrcrQaEnleVkkvaV8F/pmc/STZQ== + dependencies: + "@babel/compat-data" "^7.22.3" + "@babel/helper-compilation-targets" "^7.22.1" "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-validator-option" "^7.21.0" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" - "@babel/plugin-proposal-async-generator-functions" "^7.20.7" - "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/plugin-proposal-class-static-block" "^7.21.0" - "@babel/plugin-proposal-dynamic-import" "^7.18.6" - "@babel/plugin-proposal-export-namespace-from" "^7.18.9" - "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" - "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.20.7" - "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.21.0" - "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.3" "@babel/plugin-proposal-private-property-in-object" "^7.21.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-import-attributes" "^7.22.3" "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" @@ -976,28 +1071,43 @@ "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.21.5" + "@babel/plugin-transform-async-generator-functions" "^7.22.3" "@babel/plugin-transform-async-to-generator" "^7.20.7" "@babel/plugin-transform-block-scoped-functions" "^7.18.6" "@babel/plugin-transform-block-scoping" "^7.21.0" + "@babel/plugin-transform-class-properties" "^7.22.3" + "@babel/plugin-transform-class-static-block" "^7.22.3" "@babel/plugin-transform-classes" "^7.21.0" "@babel/plugin-transform-computed-properties" "^7.21.5" "@babel/plugin-transform-destructuring" "^7.21.3" "@babel/plugin-transform-dotall-regex" "^7.18.6" "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-dynamic-import" "^7.22.1" "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-export-namespace-from" "^7.22.3" "@babel/plugin-transform-for-of" "^7.21.5" "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-json-strings" "^7.22.3" "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.3" "@babel/plugin-transform-member-expression-literals" "^7.18.6" "@babel/plugin-transform-modules-amd" "^7.20.11" "@babel/plugin-transform-modules-commonjs" "^7.21.5" - "@babel/plugin-transform-modules-systemjs" "^7.20.11" + "@babel/plugin-transform-modules-systemjs" "^7.22.3" "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" - "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.3" + "@babel/plugin-transform-new-target" "^7.22.3" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.3" + "@babel/plugin-transform-numeric-separator" "^7.22.3" + "@babel/plugin-transform-object-rest-spread" "^7.22.3" "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.21.3" + "@babel/plugin-transform-optional-catch-binding" "^7.22.3" + "@babel/plugin-transform-optional-chaining" "^7.22.3" + "@babel/plugin-transform-parameters" "^7.22.3" + "@babel/plugin-transform-private-methods" "^7.22.3" + "@babel/plugin-transform-private-property-in-object" "^7.22.3" "@babel/plugin-transform-property-literals" "^7.18.6" "@babel/plugin-transform-regenerator" "^7.21.5" "@babel/plugin-transform-reserved-words" "^7.18.6" @@ -1007,13 +1117,15 @@ "@babel/plugin-transform-template-literals" "^7.18.9" "@babel/plugin-transform-typeof-symbol" "^7.18.9" "@babel/plugin-transform-unicode-escapes" "^7.21.5" + "@babel/plugin-transform-unicode-property-regex" "^7.22.3" "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/plugin-transform-unicode-sets-regex" "^7.22.3" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.21.5" - babel-plugin-polyfill-corejs2 "^0.3.3" - babel-plugin-polyfill-corejs3 "^0.6.0" - babel-plugin-polyfill-regenerator "^0.4.1" - core-js-compat "^3.25.1" + "@babel/types" "^7.22.4" + babel-plugin-polyfill-corejs2 "^0.4.3" + babel-plugin-polyfill-corejs3 "^0.8.1" + babel-plugin-polyfill-regenerator "^0.5.0" + core-js-compat "^3.30.2" semver "^6.3.0" "@babel/preset-modules@^0.1.5": @@ -1078,6 +1190,15 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" +"@babel/template@^7.21.9": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" + integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/parser" "^7.21.9" + "@babel/types" "^7.21.5" + "@babel/traverse@^7.18.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.5", "@babel/traverse@^7.7.2": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" @@ -1094,10 +1215,26 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" - integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== +"@babel/traverse@^7.22.1": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.4.tgz#c3cf96c5c290bd13b55e29d025274057727664c0" + integrity sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.22.3" + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.22.4" + "@babel/types" "^7.22.4" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071" + integrity sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA== dependencies: "@babel/helper-string-parser" "^7.21.5" "@babel/helper-validator-identifier" "^7.19.1" @@ -3218,6 +3355,15 @@ babel-plugin-polyfill-corejs2@^0.3.3: "@babel/helper-define-polyfill-provider" "^0.3.3" semver "^6.1.1" +babel-plugin-polyfill-corejs2@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz#75044d90ba5043a5fb559ac98496f62f3eb668fd" + integrity sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.4.0" + semver "^6.1.1" + babel-plugin-polyfill-corejs3@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" @@ -3226,6 +3372,14 @@ babel-plugin-polyfill-corejs3@^0.6.0: "@babel/helper-define-polyfill-provider" "^0.3.3" core-js-compat "^3.25.1" +babel-plugin-polyfill-corejs3@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz#39248263c38191f0d226f928d666e6db1b4b3a8a" + integrity sha512-ikFrZITKg1xH6pLND8zT14UPgjKHiGLqex7rGEZCH2EvhsneJaJPemmpQaIZV5AL03II+lXylw3UmddDK8RU5Q== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.0" + core-js-compat "^3.30.1" + babel-plugin-polyfill-regenerator@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" @@ -3233,6 +3387,13 @@ babel-plugin-polyfill-regenerator@^0.4.1: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.3" +babel-plugin-polyfill-regenerator@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz#e7344d88d9ef18a3c47ded99362ae4a757609380" + integrity sha512-hDJtKjMLVa7Z+LwnTCxoDLQj6wdc+B8dun7ayF2fYieI6OzfuvcLMB32ihJZ4UhCBwNYGl5bg/x/P9cMdnkc2g== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.0" + babel-plugin-preval@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-preval/-/babel-plugin-preval-5.1.0.tgz#6efb89bf6b97af592cd1400c6df49c0e9e6ab027" @@ -4083,12 +4244,12 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-js-compat@^3.25.1: - version "3.25.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.2.tgz#7875573586809909c69e03ef310810c1969ee138" - integrity sha512-TxfyECD4smdn3/CjWxczVtJqVLEEC2up7/82t7vC0AzNogr+4nQ8vyF7abxAuTXWvjTClSbvGhU0RgqA4ToQaQ== +core-js-compat@^3.25.1, core-js-compat@^3.30.1, core-js-compat@^3.30.2: + version "3.30.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.2.tgz#83f136e375babdb8c80ad3c22d67c69098c1dd8b" + integrity sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA== dependencies: - browserslist "^4.21.4" + browserslist "^4.21.5" core-js@^2.5.0: version "2.6.12" From e1d0343354a7052de5abbe480e65855427a44ada Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 09:39:35 +0200 Subject: [PATCH 06/69] Bump @babel/runtime from 7.21.5 to 7.22.3 (#25170) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1b32a58096..237943a845 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@babel/preset-env": "^7.22.4", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.5", - "@babel/runtime": "^7.21.5", + "@babel/runtime": "^7.22.3", "@gamestdio/websocket": "^0.3.2", "@github/webauthn-json": "^2.1.1", "@rails/ujs": "^6.1.7", diff --git a/yarn.lock b/yarn.lock index 3ad49f0228..6005eee30e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1174,10 +1174,10 @@ dependencies: regenerator-runtime "^0.12.0" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" - integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.22.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb" + integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ== dependencies: regenerator-runtime "^0.13.11" From e24adacea1f60b7f91e492a1968d4c2e16c031d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 09:42:14 +0200 Subject: [PATCH 07/69] Bump @babel/preset-react from 7.18.6 to 7.22.3 (#25171) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 237943a845..a73adb5f02 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@babel/plugin-transform-react-inline-elements": "^7.21.0", "@babel/plugin-transform-runtime": "^7.21.4", "@babel/preset-env": "^7.22.4", - "@babel/preset-react": "^7.18.6", + "@babel/preset-react": "^7.22.3", "@babel/preset-typescript": "^7.21.5", "@babel/runtime": "^7.22.3", "@gamestdio/websocket": "^0.3.2", diff --git a/yarn.lock b/yarn.lock index 6005eee30e..a4586cddaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -359,7 +359,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": +"@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== @@ -506,7 +506,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2": +"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== @@ -919,16 +919,16 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.18.6" -"@babel/plugin-transform-react-jsx@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz#2721e96d31df96e3b7ad48ff446995d26bc028ff" - integrity sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw== +"@babel/plugin-transform-react-jsx@^7.18.6", "@babel/plugin-transform-react-jsx@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.3.tgz#5a1f380df3703ba92eb1a930a539c6d88836f690" + integrity sha512-JEulRWG2f04a7L8VWaOngWiK6p+JOSpB+DAtwfJgOaej1qdbNxqtK7MwTBHjUA10NeFcszlFNqCdbRcirzh2uQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-jsx" "^7.21.4" + "@babel/types" "^7.22.3" "@babel/plugin-transform-react-pure-annotations@^7.18.6": version "7.18.6" @@ -1139,15 +1139,15 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" - integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== +"@babel/preset-react@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.22.3.tgz#2ec7f91d0c924fa2ea0c7cfbbf690bc62b79cd84" + integrity sha512-lxDz1mnZ9polqClBCVBjIVUypoB4qV3/tZUDb/IlYbW1kiiLaXaX+bInbRjl+lNQ/iUZraQ3+S8daEmoELMWug== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-validator-option" "^7.18.6" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-validator-option" "^7.21.0" "@babel/plugin-transform-react-display-name" "^7.18.6" - "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.22.3" "@babel/plugin-transform-react-jsx-development" "^7.18.6" "@babel/plugin-transform-react-pure-annotations" "^7.18.6" From b86f3596ba0e3fccc5446bdf3d75eee8da597695 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 09:51:35 +0200 Subject: [PATCH 08/69] Bump @babel/plugin-transform-runtime from 7.21.4 to 7.22.4 (#25173) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 54 +++++++++------------------------------------------- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index a73adb5f02..1dedb140c0 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@babel/core": "^7.21.8", "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.3", "@babel/plugin-transform-react-inline-elements": "^7.21.0", - "@babel/plugin-transform-runtime": "^7.21.4", + "@babel/plugin-transform-runtime": "^7.22.4", "@babel/preset-env": "^7.22.4", "@babel/preset-react": "^7.22.3", "@babel/preset-typescript": "^7.21.5", diff --git a/yarn.lock b/yarn.lock index a4586cddaf..937b2bddfd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -157,18 +157,6 @@ regexpu-core "^5.3.1" semver "^6.3.0" -"@babel/helper-define-polyfill-provider@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" - integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== - dependencies: - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - "@babel/helper-define-polyfill-provider@^0.4.0": version "0.4.0" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz#487053f103110f25b9755c5980e031e93ced24d8" @@ -953,16 +941,16 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-runtime@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz#2e1da21ca597a7d01fc96b699b21d8d2023191aa" - integrity sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA== +"@babel/plugin-transform-runtime@^7.22.4": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.4.tgz#f8353f313f18c3ce1315688631ec48657b97af42" + integrity sha512-Urkiz1m4zqiRo17klj+l3nXgiRTFQng91Bc1eiLF7BMQu1e7wE5Gcq9xSv062IF068NHjcutSbIMev60gXxAvA== dependencies: "@babel/helper-module-imports" "^7.21.4" - "@babel/helper-plugin-utils" "^7.20.2" - babel-plugin-polyfill-corejs2 "^0.3.3" - babel-plugin-polyfill-corejs3 "^0.6.0" - babel-plugin-polyfill-regenerator "^0.4.1" + "@babel/helper-plugin-utils" "^7.21.5" + babel-plugin-polyfill-corejs2 "^0.4.3" + babel-plugin-polyfill-corejs3 "^0.8.1" + babel-plugin-polyfill-regenerator "^0.5.0" semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.18.6": @@ -3346,15 +3334,6 @@ babel-plugin-macros@^3.0.1: cosmiconfig "^7.0.0" resolve "^1.19.0" -babel-plugin-polyfill-corejs2@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" - integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== - dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.3" - semver "^6.1.1" - babel-plugin-polyfill-corejs2@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz#75044d90ba5043a5fb559ac98496f62f3eb668fd" @@ -3364,14 +3343,6 @@ babel-plugin-polyfill-corejs2@^0.4.3: "@babel/helper-define-polyfill-provider" "^0.4.0" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" - integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" - core-js-compat "^3.25.1" - babel-plugin-polyfill-corejs3@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz#39248263c38191f0d226f928d666e6db1b4b3a8a" @@ -3380,13 +3351,6 @@ babel-plugin-polyfill-corejs3@^0.8.1: "@babel/helper-define-polyfill-provider" "^0.4.0" core-js-compat "^3.30.1" -babel-plugin-polyfill-regenerator@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" - integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" - babel-plugin-polyfill-regenerator@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz#e7344d88d9ef18a3c47ded99362ae4a757609380" @@ -4244,7 +4208,7 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-js-compat@^3.25.1, core-js-compat@^3.30.1, core-js-compat@^3.30.2: +core-js-compat@^3.30.1, core-js-compat@^3.30.2: version "3.30.2" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.2.tgz#83f136e375babdb8c80ad3c22d67c69098c1dd8b" integrity sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA== From b6519ab3f5be944451d9a0f45f0db66c705a04af Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 31 May 2023 09:57:24 +0200 Subject: [PATCH 09/69] Fix inconsistent naming of Instance.by_domain_and_subdomain (#25159) --- app/models/account.rb | 2 +- app/models/instance.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index 8530e664c7..062beb98da 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -119,7 +119,7 @@ class Account < ApplicationRecord scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc, accounts.id desc')) } scope :by_recent_sign_in, -> { order(Arel.sql('(case when users.current_sign_in_at is null then 1 else 0 end) asc, users.current_sign_in_at desc, accounts.id desc')) } scope :popular, -> { order('account_stats.followers_count desc') } - scope :by_domain_and_subdomains, ->(domain) { where(domain: Instance.by_domain_and_subdomain(domain).select(:domain)) } + scope :by_domain_and_subdomains, ->(domain) { where(domain: Instance.by_domain_and_subdomains(domain).select(:domain)) } scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) } scope :not_domain_blocked_by_account, ->(account) { where(arel_table[:domain].eq(nil).or(arel_table[:domain].not_in(account.excluded_from_timeline_domains))) } diff --git a/app/models/instance.rb b/app/models/instance.rb index a80e91e94c..5e6a93f646 100644 --- a/app/models/instance.rb +++ b/app/models/instance.rb @@ -22,7 +22,7 @@ class Instance < ApplicationRecord end scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } - scope :by_domain_and_subdomain, ->(domain) { where("reverse('.' || domain) LIKE reverse(?)", "%.#{domain}") } + scope :by_domain_and_subdomains, ->(domain) { where("reverse('.' || domain) LIKE reverse(?)", "%.#{domain}") } def self.refresh Scenic.database.refresh_materialized_view(table_name, concurrently: true, cascade: false) From 82f6d4c418c32b7e2a90cd707570a3efd1cf97e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 09:57:48 +0200 Subject: [PATCH 10/69] Bump @babel/core and @types/babel__core (#25183) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 130 ++++++++++++--------------------------------------- 2 files changed, 32 insertions(+), 102 deletions(-) diff --git a/package.json b/package.json index 1dedb140c0..b8265a549a 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ }, "private": true, "dependencies": { - "@babel/core": "^7.21.8", + "@babel/core": "^7.22.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.3", "@babel/plugin-transform-react-inline-elements": "^7.21.0", "@babel/plugin-transform-runtime": "^7.22.4", @@ -141,7 +141,7 @@ "devDependencies": { "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", - "@types/babel__core": "^7.20.0", + "@types/babel__core": "^7.20.1", "@types/emoji-mart": "^3.0.9", "@types/escape-html": "^1.0.2", "@types/express": "^4.17.17", diff --git a/yarn.lock b/yarn.lock index 937b2bddfd..2611f068b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24,7 +24,7 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== @@ -36,38 +36,28 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.3.tgz#cd502a6a0b6e37d7ad72ce7e71a7160a3ae36f7e" integrity sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ== -"@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.21.8", "@babel/core@^7.7.2": - version "7.21.8" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" - integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== +"@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.22.1", "@babel/core@^7.7.2": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.1.tgz#5de51c5206f4c6f5533562838337a603c1033cfd" + integrity sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.5" - "@babel/helper-compilation-targets" "^7.21.5" - "@babel/helper-module-transforms" "^7.21.5" - "@babel/helpers" "^7.21.5" - "@babel/parser" "^7.21.8" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" + "@babel/generator" "^7.22.0" + "@babel/helper-compilation-targets" "^7.22.1" + "@babel/helper-module-transforms" "^7.22.1" + "@babel/helpers" "^7.22.0" + "@babel/parser" "^7.22.0" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.21.5", "@babel/generator@^7.7.2": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.5.tgz#c0c0e5449504c7b7de8236d99338c3e2a340745f" - integrity sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w== - dependencies: - "@babel/types" "^7.21.5" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.22.3": +"@babel/generator@^7.22.0", "@babel/generator@^7.22.3", "@babel/generator@^7.7.2": version "7.22.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e" integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A== @@ -100,7 +90,7 @@ "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/types" "^7.19.0" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5", "@babel/helper-compilation-targets@^7.22.1": +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.1": version "7.22.1" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz#bfcd6b7321ffebe33290d68550e2c9d7eb7c7a58" integrity sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ== @@ -174,11 +164,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-environment-visitor@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba" - integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ== - "@babel/helper-environment-visitor@^7.22.1": version "7.22.1" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8" @@ -242,21 +227,7 @@ dependencies: "@babel/types" "^7.21.4" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz#d937c82e9af68d31ab49039136a222b17ac0b420" - integrity sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw== - dependencies: - "@babel/helper-environment-visitor" "^7.21.5" - "@babel/helper-module-imports" "^7.21.4" - "@babel/helper-simple-access" "^7.21.5" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" - -"@babel/helper-module-transforms@^7.22.1": +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.1": version "7.22.1" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz#e0cad47fedcf3cae83c11021696376e2d5a50c63" integrity sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw== @@ -362,14 +333,14 @@ "@babel/traverse" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/helpers@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.5.tgz#5bac66e084d7a4d2d9696bdf0175a93f7fb63c08" - integrity sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA== +"@babel/helpers@^7.22.0": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.3.tgz#53b74351da9684ea2f694bf0877998da26dd830e" + integrity sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w== dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.3" "@babel/highlight@^7.18.6": version "7.18.6" @@ -380,12 +351,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8": - version "7.21.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" - integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== - -"@babel/parser@^7.21.9", "@babel/parser@^7.22.4": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.9", "@babel/parser@^7.22.0", "@babel/parser@^7.22.4": version "7.22.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== @@ -1169,16 +1135,7 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/template@^7.21.9": +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.21.9", "@babel/template@^7.3.3": version "7.21.9" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== @@ -1187,23 +1144,7 @@ "@babel/parser" "^7.21.9" "@babel/types" "^7.21.5" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.5", "@babel/traverse@^7.7.2": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" - integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== - dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.5" - "@babel/helper-environment-visitor" "^7.21.5" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.5" - "@babel/types" "^7.21.5" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.22.1": +"@babel/traverse@^7.18.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.22.1", "@babel/traverse@^7.7.2": version "7.22.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.4.tgz#c3cf96c5c290bd13b55e29d025274057727664c0" integrity sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ== @@ -1992,21 +1933,10 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q== -"@types/babel__core@^7.1.12", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.3": - version "7.1.18" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" - integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__core@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" - integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== +"@types/babel__core@^7.1.12", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.3", "@types/babel__core@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b" + integrity sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw== dependencies: "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" From 665bb237a8b23f8267eb8fcbcae8da32358e6ba4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 31 May 2023 04:23:32 -0400 Subject: [PATCH 11/69] Increase spec coverage for controllers - admin/ip_blocks, admin/relays, admin/rules (#25192) --- .../admin/ip_blocks_controller_spec.rb | 33 ++++++++++ .../admin/relays_controller_spec.rb | 38 +++++++++++ .../admin/rules_controller_spec.rb | 64 +++++++++++++++++++ 3 files changed, 135 insertions(+) diff --git a/spec/controllers/admin/ip_blocks_controller_spec.rb b/spec/controllers/admin/ip_blocks_controller_spec.rb index 873888afc7..05190f1340 100644 --- a/spec/controllers/admin/ip_blocks_controller_spec.rb +++ b/spec/controllers/admin/ip_blocks_controller_spec.rb @@ -18,4 +18,37 @@ describe Admin::IpBlocksController do expect(response).to have_http_status(:success) end end + + describe 'GET #new' do + it 'returns http success and renders view' do + get :new + + expect(response).to have_http_status(:success) + expect(response).to render_template(:new) + end + end + + describe 'POST #create' do + context 'with valid data' do + it 'creates a new ip block and redirects' do + expect do + post :create, params: { ip_block: { ip: '1.1.1.1', severity: 'no_access', expires_in: 1.day.to_i.to_s } } + end.to change(IpBlock, :count).by(1) + + expect(response).to redirect_to(admin_ip_blocks_path) + expect(flash.notice).to match(I18n.t('admin.ip_blocks.created_msg')) + end + end + + context 'with invalid data' do + it 'does not create new a ip block and renders new' do + expect do + post :create, params: { ip_block: { ip: '1.1.1.1' } } + end.to_not change(IpBlock, :count) + + expect(response).to have_http_status(:success) + expect(response).to render_template(:new) + end + end + end end diff --git a/spec/controllers/admin/relays_controller_spec.rb b/spec/controllers/admin/relays_controller_spec.rb index dfb9f3c048..261f302c05 100644 --- a/spec/controllers/admin/relays_controller_spec.rb +++ b/spec/controllers/admin/relays_controller_spec.rb @@ -18,4 +18,42 @@ describe Admin::RelaysController do expect(response).to have_http_status(:success) end end + + describe 'GET #new' do + it 'returns http success and renders view' do + get :new + + expect(response).to have_http_status(:success) + expect(response).to render_template(:new) + end + end + + describe 'POST #create' do + context 'with valid data' do + let(:inbox_url) { 'https://example.com/inbox' } + + before do + stub_request(:post, inbox_url).to_return(status: 200) + end + + it 'creates a new relay and redirects' do + expect do + post :create, params: { relay: { inbox_url: inbox_url } } + end.to change(Relay, :count).by(1) + + expect(response).to redirect_to(admin_relays_path) + end + end + + context 'with invalid data' do + it 'does not create new a relay and renders new' do + expect do + post :create, params: { relay: { inbox_url: 'invalid' } } + end.to_not change(Relay, :count) + + expect(response).to have_http_status(:success) + expect(response).to render_template(:new) + end + end + end end diff --git a/spec/controllers/admin/rules_controller_spec.rb b/spec/controllers/admin/rules_controller_spec.rb index d7b633c049..92ffb41567 100644 --- a/spec/controllers/admin/rules_controller_spec.rb +++ b/spec/controllers/admin/rules_controller_spec.rb @@ -18,4 +18,68 @@ describe Admin::RulesController do expect(response).to have_http_status(:success) end end + + describe 'GET #edit' do + let(:rule) { Fabricate(:rule) } + + it 'returns http success and renders edit' do + get :edit, params: { id: rule.id } + + expect(response).to have_http_status(:success) + expect(response).to render_template(:edit) + end + end + + describe 'POST #create' do + context 'with valid data' do + it 'creates a new rule and redirects' do + expect do + post :create, params: { rule: { text: 'The rule text.' } } + end.to change(Rule, :count).by(1) + + expect(response).to redirect_to(admin_rules_path) + end + end + + context 'with invalid data' do + it 'does creates a new rule and renders index' do + expect do + post :create, params: { rule: { text: '' } } + end.to_not change(Rule, :count) + + expect(response).to render_template(:index) + end + end + end + + describe 'PUT #update' do + let(:rule) { Fabricate(:rule, text: 'Original text') } + + context 'with valid data' do + it 'updates the rule and redirects' do + put :update, params: { id: rule.id, rule: { text: 'Updated text.' } } + + expect(response).to redirect_to(admin_rules_path) + end + end + + context 'with invalid data' do + it 'does not update the rule and renders index' do + put :update, params: { id: rule.id, rule: { text: '' } } + + expect(response).to render_template(:edit) + end + end + end + + describe 'DELETE #destroy' do + let!(:rule) { Fabricate(:rule) } + + it 'destroys the rule and redirects' do + delete :destroy, params: { id: rule.id } + + expect(rule.reload).to be_discarded + expect(response).to redirect_to(admin_rules_path) + end + end end From 8b1bfaed3ee30e753779e1f428e8c291a7ddf93c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 31 May 2023 09:23:49 -0400 Subject: [PATCH 12/69] Add specs for admin/webhooks CRUD actions (#25133) --- .../admin/webhooks_controller_spec.rb | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/spec/controllers/admin/webhooks_controller_spec.rb b/spec/controllers/admin/webhooks_controller_spec.rb index 12727e142b..5e45c74082 100644 --- a/spec/controllers/admin/webhooks_controller_spec.rb +++ b/spec/controllers/admin/webhooks_controller_spec.rb @@ -18,4 +18,82 @@ describe Admin::WebhooksController do expect(response).to have_http_status(:success) end end + + describe 'GET #new' do + it 'returns http success and renders view' do + get :new + + expect(response).to have_http_status(:success) + expect(response).to render_template(:new) + end + end + + describe 'POST #create' do + it 'creates a new webhook record with valid data' do + expect do + post :create, params: { webhook: { url: 'https://example.com/hook', events: ['account.approved'] } } + end.to change(Webhook, :count).by(1) + + expect(response).to be_redirect + end + + it 'does not create a new webhook record with invalid data' do + expect do + post :create, params: { webhook: { url: 'https://example.com/hook', events: [] } } + end.to_not change(Webhook, :count) + + expect(response).to have_http_status(:success) + expect(response).to render_template(:new) + end + end + + context 'with an existing record' do + let!(:webhook) { Fabricate :webhook } + + describe 'GET #show' do + it 'returns http success and renders view' do + get :show, params: { id: webhook.id } + + expect(response).to have_http_status(:success) + expect(response).to render_template(:show) + end + end + + describe 'GET #edit' do + it 'returns http success and renders view' do + get :edit, params: { id: webhook.id } + + expect(response).to have_http_status(:success) + expect(response).to render_template(:edit) + end + end + + describe 'PUT #update' do + it 'updates the record with valid data' do + put :update, params: { id: webhook.id, webhook: { url: 'https://example.com/new/location' } } + + expect(webhook.reload.url).to match(%r{new/location}) + expect(response).to redirect_to(admin_webhook_path(webhook)) + end + + it 'does not update the record with invalid data' do + expect do + put :update, params: { id: webhook.id, webhook: { url: '' } } + end.to_not change(webhook, :url) + + expect(response).to have_http_status(:success) + expect(response).to render_template(:show) + end + end + + describe 'DELETE #destroy' do + it 'destroys the record' do + expect do + delete :destroy, params: { id: webhook.id } + end.to change(Webhook, :count).by(-1) + + expect(response).to redirect_to(admin_webhooks_path) + end + end + end end From 3c41547f491e08a1781682a539de2b6e4d8a38a7 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Wed, 31 May 2023 10:32:37 -0300 Subject: [PATCH 13/69] Add test coverage for `Mastodon::CLI::Accounts#backup` (#25163) --- spec/lib/mastodon/cli/accounts_spec.rb | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/spec/lib/mastodon/cli/accounts_spec.rb b/spec/lib/mastodon/cli/accounts_spec.rb index a7fcc281fd..8eee6f5381 100644 --- a/spec/lib/mastodon/cli/accounts_spec.rb +++ b/spec/lib/mastodon/cli/accounts_spec.rb @@ -624,4 +624,42 @@ describe Mastodon::CLI::Accounts do end end end + + describe '#backup' do + context 'when the given username is not found' do + let(:arguments) { ['non_existent_username'] } + + it 'exits with an error message indicating that there is no such account' do + expect { cli.invoke(:backup, arguments) }.to output( + a_string_including('No user with such username') + ).to_stdout + .and raise_error(SystemExit) + end + end + + context 'when the given username is found' do + let(:account) { Fabricate(:account) } + let(:user) { account.user } + let(:arguments) { [account.username] } + + it 'creates a new backup for the specified user' do + expect { cli.invoke(:backup, arguments) }.to change { user.backups.count }.by(1) + end + + it 'creates a backup job' do + allow(BackupWorker).to receive(:perform_async) + + cli.invoke(:backup, arguments) + latest_backup = user.backups.last + + expect(BackupWorker).to have_received(:perform_async).with(latest_backup.id).once + end + + it 'displays a successful message' do + expect { cli.invoke(:backup, arguments) }.to output( + a_string_including('OK') + ).to_stdout + end + end + end end From dc26140d54742dcec47a115bb27b9651414e30b9 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 31 May 2023 13:40:16 -0400 Subject: [PATCH 14/69] Use thor methods instead of tty prompt in maintenance cli (#25207) --- lib/mastodon/cli/maintenance.rb | 117 ++++++++++++++++---------------- 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb index d604973320..e9badfb8d1 100644 --- a/lib/mastodon/cli/maintenance.rb +++ b/lib/mastodon/cli/maintenance.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'tty-prompt' require_relative 'base' module Mastodon::CLI @@ -134,25 +133,23 @@ module Mastodon::CLI Mastodon has to be stopped to run this task, which will take a long time and may be destructive. LONG_DESC def fix_duplicates - @prompt = TTY::Prompt.new - if ActiveRecord::Migrator.current_version < MIN_SUPPORTED_VERSION - @prompt.error 'Your version of the database schema is too old and is not supported by this script.' - @prompt.error 'Please update to at least Mastodon 3.0.0 before running this script.' + say 'Your version of the database schema is too old and is not supported by this script.', :red + say 'Please update to at least Mastodon 3.0.0 before running this script.', :red exit(1) elsif ActiveRecord::Migrator.current_version > MAX_SUPPORTED_VERSION - @prompt.warn 'Your version of the database schema is more recent than this script, this may cause unexpected errors.' - exit(1) unless @prompt.yes?('Continue anyway? (Yes/No)') + say 'Your version of the database schema is more recent than this script, this may cause unexpected errors.', :yellow + exit(1) unless yes?('Continue anyway? (Yes/No)') end if Sidekiq::ProcessSet.new.any? - @prompt.error 'It seems Sidekiq is running. All Mastodon processes need to be stopped when using this script.' + say 'It seems Sidekiq is running. All Mastodon processes need to be stopped when using this script.', :red exit(1) end - @prompt.warn 'This task will take a long time to run and is potentially destructive.' - @prompt.warn 'Please make sure to stop Mastodon and have a backup.' - exit(1) unless @prompt.yes?('Continue? (Yes/No)') + say 'This task will take a long time to run and is potentially destructive.', :yellow + say 'Please make sure to stop Mastodon and have a backup.', :yellow + exit(1) unless yes?('Continue? (Yes/No)') deduplicate_users! deduplicate_account_domain_blocks! @@ -176,7 +173,7 @@ module Mastodon::CLI Scenic.database.refresh_materialized_view('instances', concurrently: true, cascade: false) if ActiveRecord::Migrator.current_version >= 2020_12_06_004238 Rails.cache.clear - @prompt.say 'Finished!' + say 'Finished!' end private @@ -184,7 +181,7 @@ module Mastodon::CLI def deduplicate_accounts! remove_index_if_exists!(:accounts, 'index_accounts_on_username_and_domain_lower') - @prompt.say 'Deduplicating accounts… for local accounts, you will be asked to chose which account to keep unchanged.' + say 'Deduplicating accounts… for local accounts, you will be asked to chose which account to keep unchanged.' find_duplicate_accounts.each do |row| accounts = Account.where(id: row['ids'].split(',')).to_a @@ -196,14 +193,14 @@ module Mastodon::CLI end end - @prompt.say 'Restoring index_accounts_on_username_and_domain_lower…' + say 'Restoring index_accounts_on_username_and_domain_lower…' if ActiveRecord::Migrator.current_version < 2020_06_20_164023 ActiveRecord::Base.connection.add_index :accounts, 'lower (username), lower(domain)', name: 'index_accounts_on_username_and_domain_lower', unique: true else ActiveRecord::Base.connection.add_index :accounts, "lower (username), COALESCE(lower(domain), '')", name: 'index_accounts_on_username_and_domain_lower', unique: true end - @prompt.say 'Reindexing textual indexes on accounts…' + say 'Reindexing textual indexes on accounts…' ActiveRecord::Base.connection.execute('REINDEX INDEX search_index;') ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_uri;') ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_url;') @@ -215,15 +212,15 @@ module Mastodon::CLI remove_index_if_exists!(:users, 'index_users_on_remember_token') remove_index_if_exists!(:users, 'index_users_on_reset_password_token') - @prompt.say 'Deduplicating user records…' + say 'Deduplicating user records…' # Deduplicating email ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users GROUP BY email HAVING count(*) > 1").each do |row| users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse ref_user = users.shift - @prompt.warn "Multiple users registered with e-mail address #{ref_user.email}." - @prompt.warn "e-mail will be disabled for the following accounts: #{user.map(&:account).map(&:acct).join(', ')}" - @prompt.warn 'Please reach out to them and set another address with `tootctl account modify` or delete them.' + say "Multiple users registered with e-mail address #{ref_user.email}.", :yellow + say "e-mail will be disabled for the following accounts: #{user.map(&:account).map(&:acct).join(', ')}", :yellow + say 'Please reach out to them and set another address with `tootctl account modify` or delete them.', :yellow users.each_with_index do |user, index| user.update!(email: "#{index} " + user.email) @@ -234,7 +231,7 @@ module Mastodon::CLI deduplicate_users_process_remember_token deduplicate_users_process_password_token - @prompt.say 'Restoring users indexes…' + say 'Restoring users indexes…' ActiveRecord::Base.connection.add_index :users, ['confirmation_token'], name: 'index_users_on_confirmation_token', unique: true ActiveRecord::Base.connection.add_index :users, ['email'], name: 'index_users_on_email', unique: true ActiveRecord::Base.connection.add_index :users, ['remember_token'], name: 'index_users_on_remember_token', unique: true if ActiveRecord::Migrator.current_version < 2022_01_18_183010 @@ -249,7 +246,7 @@ module Mastodon::CLI def deduplicate_users_process_confirmation_token ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE confirmation_token IS NOT NULL GROUP BY confirmation_token HAVING count(*) > 1").each do |row| users = User.where(id: row['ids'].split(',')).sort_by(&:created_at).reverse.drop(1) - @prompt.warn "Unsetting confirmation token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}" + say "Unsetting confirmation token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}", :yellow users.each do |user| user.update!(confirmation_token: nil) @@ -261,7 +258,7 @@ module Mastodon::CLI if ActiveRecord::Migrator.current_version < 2022_01_18_183010 ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE remember_token IS NOT NULL GROUP BY remember_token HAVING count(*) > 1").each do |row| users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse.drop(1) - @prompt.warn "Unsetting remember token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}" + say "Unsetting remember token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}", :yellow users.each do |user| user.update!(remember_token: nil) @@ -273,7 +270,7 @@ module Mastodon::CLI def deduplicate_users_process_password_token ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE reset_password_token IS NOT NULL GROUP BY reset_password_token HAVING count(*) > 1").each do |row| users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse.drop(1) - @prompt.warn "Unsetting password reset token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}" + say "Unsetting password reset token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}", :yellow users.each do |user| user.update!(reset_password_token: nil) @@ -284,12 +281,12 @@ module Mastodon::CLI def deduplicate_account_domain_blocks! remove_index_if_exists!(:account_domain_blocks, 'index_account_domain_blocks_on_account_id_and_domain') - @prompt.say 'Removing duplicate account domain blocks…' + say 'Removing duplicate account domain blocks…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM account_domain_blocks GROUP BY account_id, domain HAVING count(*) > 1").each do |row| AccountDomainBlock.where(id: row['ids'].split(',').drop(1)).delete_all end - @prompt.say 'Restoring account domain blocks indexes…' + say 'Restoring account domain blocks indexes…' ActiveRecord::Base.connection.add_index :account_domain_blocks, %w(account_id domain), name: 'index_account_domain_blocks_on_account_id_and_domain', unique: true end @@ -298,12 +295,12 @@ module Mastodon::CLI remove_index_if_exists!(:account_identity_proofs, 'index_account_proofs_on_account_and_provider_and_username') - @prompt.say 'Removing duplicate account identity proofs…' + say 'Removing duplicate account identity proofs…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM account_identity_proofs GROUP BY account_id, provider, provider_username HAVING count(*) > 1").each do |row| AccountIdentityProof.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) end - @prompt.say 'Restoring account identity proofs indexes…' + say 'Restoring account identity proofs indexes…' ActiveRecord::Base.connection.add_index :account_identity_proofs, %w(account_id provider provider_username), name: 'index_account_proofs_on_account_and_provider_and_username', unique: true end @@ -312,19 +309,19 @@ module Mastodon::CLI remove_index_if_exists!(:announcement_reactions, 'index_announcement_reactions_on_account_id_and_announcement_id') - @prompt.say 'Removing duplicate account identity proofs…' + say 'Removing duplicate account identity proofs…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM announcement_reactions GROUP BY account_id, announcement_id, name HAVING count(*) > 1").each do |row| AnnouncementReaction.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) end - @prompt.say 'Restoring announcement_reactions indexes…' + say 'Restoring announcement_reactions indexes…' ActiveRecord::Base.connection.add_index :announcement_reactions, %w(account_id announcement_id name), name: 'index_announcement_reactions_on_account_id_and_announcement_id', unique: true end def deduplicate_conversations! remove_index_if_exists!(:conversations, 'index_conversations_on_uri') - @prompt.say 'Deduplicating conversations…' + say 'Deduplicating conversations…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM conversations WHERE uri IS NOT NULL GROUP BY uri HAVING count(*) > 1").each do |row| conversations = Conversation.where(id: row['ids'].split(',')).sort_by(&:id).reverse @@ -336,7 +333,7 @@ module Mastodon::CLI end end - @prompt.say 'Restoring conversations indexes…' + say 'Restoring conversations indexes…' if ActiveRecord::Migrator.current_version < 2022_03_07_083603 ActiveRecord::Base.connection.add_index :conversations, ['uri'], name: 'index_conversations_on_uri', unique: true else @@ -347,7 +344,7 @@ module Mastodon::CLI def deduplicate_custom_emojis! remove_index_if_exists!(:custom_emojis, 'index_custom_emojis_on_shortcode_and_domain') - @prompt.say 'Deduplicating custom_emojis…' + say 'Deduplicating custom_emojis…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM custom_emojis GROUP BY shortcode, domain HAVING count(*) > 1").each do |row| emojis = CustomEmoji.where(id: row['ids'].split(',')).sort_by(&:id).reverse @@ -359,14 +356,14 @@ module Mastodon::CLI end end - @prompt.say 'Restoring custom_emojis indexes…' + say 'Restoring custom_emojis indexes…' ActiveRecord::Base.connection.add_index :custom_emojis, %w(shortcode domain), name: 'index_custom_emojis_on_shortcode_and_domain', unique: true end def deduplicate_custom_emoji_categories! remove_index_if_exists!(:custom_emoji_categories, 'index_custom_emoji_categories_on_name') - @prompt.say 'Deduplicating custom_emoji_categories…' + say 'Deduplicating custom_emoji_categories…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM custom_emoji_categories GROUP BY name HAVING count(*) > 1").each do |row| categories = CustomEmojiCategory.where(id: row['ids'].split(',')).sort_by(&:id).reverse @@ -378,26 +375,26 @@ module Mastodon::CLI end end - @prompt.say 'Restoring custom_emoji_categories indexes…' + say 'Restoring custom_emoji_categories indexes…' ActiveRecord::Base.connection.add_index :custom_emoji_categories, ['name'], name: 'index_custom_emoji_categories_on_name', unique: true end def deduplicate_domain_allows! remove_index_if_exists!(:domain_allows, 'index_domain_allows_on_domain') - @prompt.say 'Deduplicating domain_allows…' + say 'Deduplicating domain_allows…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM domain_allows GROUP BY domain HAVING count(*) > 1").each do |row| DomainAllow.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) end - @prompt.say 'Restoring domain_allows indexes…' + say 'Restoring domain_allows indexes…' ActiveRecord::Base.connection.add_index :domain_allows, ['domain'], name: 'index_domain_allows_on_domain', unique: true end def deduplicate_domain_blocks! remove_index_if_exists!(:domain_blocks, 'index_domain_blocks_on_domain') - @prompt.say 'Deduplicating domain_allows…' + say 'Deduplicating domain_allows…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM domain_blocks GROUP BY domain HAVING count(*) > 1").each do |row| domain_blocks = DomainBlock.where(id: row['ids'].split(',')).by_severity.reverse.to_a @@ -414,7 +411,7 @@ module Mastodon::CLI domain_blocks.each(&:destroy) end - @prompt.say 'Restoring domain_blocks indexes…' + say 'Restoring domain_blocks indexes…' ActiveRecord::Base.connection.add_index :domain_blocks, ['domain'], name: 'index_domain_blocks_on_domain', unique: true end @@ -423,37 +420,37 @@ module Mastodon::CLI remove_index_if_exists!(:unavailable_domains, 'index_unavailable_domains_on_domain') - @prompt.say 'Deduplicating unavailable_domains…' + say 'Deduplicating unavailable_domains…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM unavailable_domains GROUP BY domain HAVING count(*) > 1").each do |row| UnavailableDomain.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) end - @prompt.say 'Restoring domain_allows indexes…' + say 'Restoring domain_allows indexes…' ActiveRecord::Base.connection.add_index :unavailable_domains, ['domain'], name: 'index_unavailable_domains_on_domain', unique: true end def deduplicate_email_domain_blocks! remove_index_if_exists!(:email_domain_blocks, 'index_email_domain_blocks_on_domain') - @prompt.say 'Deduplicating email_domain_blocks…' + say 'Deduplicating email_domain_blocks…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM email_domain_blocks GROUP BY domain HAVING count(*) > 1").each do |row| domain_blocks = EmailDomainBlock.where(id: row['ids'].split(',')).sort_by { |b| b.parent.nil? ? 1 : 0 }.to_a domain_blocks.drop(1).each(&:destroy) end - @prompt.say 'Restoring email_domain_blocks indexes…' + say 'Restoring email_domain_blocks indexes…' ActiveRecord::Base.connection.add_index :email_domain_blocks, ['domain'], name: 'index_email_domain_blocks_on_domain', unique: true end def deduplicate_media_attachments! remove_index_if_exists!(:media_attachments, 'index_media_attachments_on_shortcode') - @prompt.say 'Deduplicating media_attachments…' + say 'Deduplicating media_attachments…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM media_attachments WHERE shortcode IS NOT NULL GROUP BY shortcode HAVING count(*) > 1").each do |row| MediaAttachment.where(id: row['ids'].split(',').drop(1)).update_all(shortcode: nil) end - @prompt.say 'Restoring media_attachments indexes…' + say 'Restoring media_attachments indexes…' if ActiveRecord::Migrator.current_version < 2022_03_10_060626 ActiveRecord::Base.connection.add_index :media_attachments, ['shortcode'], name: 'index_media_attachments_on_shortcode', unique: true else @@ -464,19 +461,19 @@ module Mastodon::CLI def deduplicate_preview_cards! remove_index_if_exists!(:preview_cards, 'index_preview_cards_on_url') - @prompt.say 'Deduplicating preview_cards…' + say 'Deduplicating preview_cards…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM preview_cards GROUP BY url HAVING count(*) > 1").each do |row| PreviewCard.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) end - @prompt.say 'Restoring preview_cards indexes…' + say 'Restoring preview_cards indexes…' ActiveRecord::Base.connection.add_index :preview_cards, ['url'], name: 'index_preview_cards_on_url', unique: true end def deduplicate_statuses! remove_index_if_exists!(:statuses, 'index_statuses_on_uri') - @prompt.say 'Deduplicating statuses…' + say 'Deduplicating statuses…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM statuses WHERE uri IS NOT NULL GROUP BY uri HAVING count(*) > 1").each do |row| statuses = Status.where(id: row['ids'].split(',')).sort_by(&:id) ref_status = statuses.shift @@ -486,7 +483,7 @@ module Mastodon::CLI end end - @prompt.say 'Restoring statuses indexes…' + say 'Restoring statuses indexes…' if ActiveRecord::Migrator.current_version < 2022_03_10_060706 ActiveRecord::Base.connection.add_index :statuses, ['uri'], name: 'index_statuses_on_uri', unique: true else @@ -498,7 +495,7 @@ module Mastodon::CLI remove_index_if_exists!(:tags, 'index_tags_on_name_lower') remove_index_if_exists!(:tags, 'index_tags_on_name_lower_btree') - @prompt.say 'Deduplicating tags…' + say 'Deduplicating tags…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM tags GROUP BY lower((name)::text) HAVING count(*) > 1").each do |row| tags = Tag.where(id: row['ids'].split(',')).sort_by { |t| [t.usable?, t.trendable?, t.listable?].count(false) } ref_tag = tags.shift @@ -508,7 +505,7 @@ module Mastodon::CLI end end - @prompt.say 'Restoring tags indexes…' + say 'Restoring tags indexes…' if ActiveRecord::Migrator.current_version < 2021_04_21_121431 ActiveRecord::Base.connection.add_index :tags, 'lower((name)::text)', name: 'index_tags_on_name_lower', unique: true else @@ -521,12 +518,12 @@ module Mastodon::CLI remove_index_if_exists!(:webauthn_credentials, 'index_webauthn_credentials_on_external_id') - @prompt.say 'Deduplicating webauthn_credentials…' + say 'Deduplicating webauthn_credentials…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM webauthn_credentials GROUP BY external_id HAVING count(*) > 1").each do |row| WebauthnCredential.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) end - @prompt.say 'Restoring webauthn_credentials indexes…' + say 'Restoring webauthn_credentials indexes…' ActiveRecord::Base.connection.add_index :webauthn_credentials, ['external_id'], name: 'index_webauthn_credentials_on_external_id', unique: true end @@ -535,23 +532,23 @@ module Mastodon::CLI remove_index_if_exists!(:webhooks, 'index_webhooks_on_url') - @prompt.say 'Deduplicating webhooks…' + say 'Deduplicating webhooks…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM webhooks GROUP BY url HAVING count(*) > 1").each do |row| Webhooks.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) end - @prompt.say 'Restoring webhooks indexes…' + say 'Restoring webhooks indexes…' ActiveRecord::Base.connection.add_index :webhooks, ['url'], name: 'index_webhooks_on_url', unique: true end def deduplicate_local_accounts!(accounts) accounts = accounts.sort_by(&:id).reverse - @prompt.warn "Multiple local accounts were found for username '#{accounts.first.username}'." - @prompt.warn 'All those accounts are distinct accounts but only the most recently-created one is fully-functional.' + say "Multiple local accounts were found for username '#{accounts.first.username}'.", :yellow + say 'All those accounts are distinct accounts but only the most recently-created one is fully-functional.', :yellow accounts.each_with_index do |account, idx| - @prompt.say format( + say format( '%2d. %s: created at: %s; updated at: %s; last logged in at: %s; statuses: %5d; last status at: %s', index: idx, username: account.username, @@ -563,9 +560,9 @@ module Mastodon::CLI ) end - @prompt.say 'Please chose the one to keep unchanged, other ones will be automatically renamed.' + say 'Please chose the one to keep unchanged, other ones will be automatically renamed.' - ref_id = @prompt.ask('Account to keep unchanged:') do |q| + ref_id = ask('Account to keep unchanged:') do |q| q.required true q.default 0 q.convert :int From b0104e4c33e4f411fcb487e070e7cdc0818a3aad Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 31 May 2023 14:01:11 -0400 Subject: [PATCH 15/69] Silence output to stdout during cli specs (#25211) --- spec/rails_helper.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 22078a6cbc..d7e2b5c185 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -62,6 +62,10 @@ RSpec.configure do |config| config.infer_spec_type_from_file_location! config.filter_rails_from_backtrace! + config.define_derived_metadata(file_path: Regexp.new('spec/lib/mastodon/cli')) do |metadata| + metadata[:type] = :cli + end + config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::ControllerHelpers, type: :helper config.include Devise::Test::ControllerHelpers, type: :view @@ -73,6 +77,10 @@ RSpec.configure do |config| config.include Redisable config.include SignedRequestHelpers, type: :request + config.before :each, type: :cli do + stub_stdout + end + config.before :each, type: :feature do https = ENV['LOCAL_HTTPS'] == 'true' Capybara.app_host = "http#{https ? 's' : ''}://#{ENV.fetch('LOCAL_DOMAIN')}" @@ -106,6 +114,10 @@ def attachment_fixture(name) Rails.root.join('spec', 'fixtures', 'files', name).open end +def stub_stdout + allow($stdout).to receive(:write) +end + def stub_jsonld_contexts! stub_request(:get, 'https://www.w3.org/ns/activitystreams').to_return(request_fixture('json-ld.activitystreams.txt')) stub_request(:get, 'https://w3id.org/identity/v1').to_return(request_fixture('json-ld.identity.txt')) From b7d995cb0078c2bbbbefd90f0b4a9818b58a1377 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 31 May 2023 23:33:10 +0200 Subject: [PATCH 16/69] Disable some metrics cops (#25215) --- .rubocop.yml | 111 +++++++------------------------- .rubocop_todo.yml | 27 -------- app/helpers/languages_helper.rb | 2 - 3 files changed, 25 insertions(+), 115 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 4eef9d63e6..bd561df1d2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -53,6 +53,28 @@ Lint/UselessAccessModifier: ContextCreatingMethods: - class_methods +## Disable most Metrics/*Length cops +# Reason: those are often triggered and force significant refactors when this happend +# but the team feel they are not really improving the code quality. + +# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocklength +Metrics/BlockLength: + Enabled: false + +# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength +Metrics/ClassLength: + Enabled: false + +# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength +Metrics/MethodLength: + Enabled: false + +# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmodulelength +Metrics/ModuleLength: + Enabled: false + +## End Disable Metrics/*Length cops + # Reason: Currently disabled in .rubocop_todo.yml # https://docs.rubocop.org/rubocop/cops_metrics.html#metricsabcsize Metrics/AbcSize: @@ -60,88 +82,12 @@ Metrics/AbcSize: - 'lib/mastodon/cli/*.rb' - db/*migrate/**/* -# Reason: Some functions cannot be broken up, but others may be refactor candidates -# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocklength -Metrics/BlockLength: - CountAsOne: ['array', 'hash', 'heredoc', 'method_call'] - Exclude: - - 'config/routes.rb' - - 'lib/mastodon/cli/*.rb' - - 'lib/tasks/*.rake' - - 'app/models/concerns/account_associations.rb' - - 'app/models/concerns/account_interactions.rb' - - 'app/models/concerns/ldap_authenticable.rb' - - 'app/models/concerns/omniauthable.rb' - - 'app/models/concerns/pam_authenticable.rb' - - 'app/models/concerns/remotable.rb' - - 'app/services/suspend_account_service.rb' - - 'app/services/unsuspend_account_service.rb' - - 'app/views/accounts/show.rss.ruby' - - 'app/views/tags/show.rss.ruby' - - 'config/environments/development.rb' - - 'config/environments/production.rb' - - 'config/initializers/devise.rb' - - 'config/initializers/doorkeeper.rb' - - 'config/initializers/omniauth.rb' - - 'config/initializers/simple_form.rb' - - 'config/navigation.rb' - - 'config/routes.rb' - - 'config/routes/*.rb' - - 'db/post_migrate/20221101190723_backfill_admin_action_logs.rb' - - 'db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb' - - 'lib/paperclip/gif_transcoder.rb' - # Reason: # https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocknesting Metrics/BlockNesting: Exclude: - 'lib/mastodon/cli/*.rb' -# Reason: Some Excluded files would be candidates for refactoring but not currently addressed -# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength -Metrics/ClassLength: - CountAsOne: ['array', 'hash', 'heredoc', 'method_call'] - Exclude: - - 'lib/mastodon/cli/*.rb' - - 'app/controllers/admin/accounts_controller.rb' - - 'app/controllers/api/base_controller.rb' - - 'app/controllers/api/v1/admin/accounts_controller.rb' - - 'app/controllers/application_controller.rb' - - 'app/controllers/auth/registrations_controller.rb' - - 'app/controllers/auth/sessions_controller.rb' - - 'app/lib/activitypub/activity.rb' - - 'app/lib/activitypub/activity/create.rb' - - 'app/lib/activitypub/tag_manager.rb' - - 'app/lib/feed_manager.rb' - - 'app/lib/link_details_extractor.rb' - - 'app/lib/request.rb' - - 'app/lib/text_formatter.rb' - - 'app/lib/user_settings_decorator.rb' - - 'app/mailers/user_mailer.rb' - - 'app/models/account.rb' - - 'app/models/admin/account_action.rb' - - 'app/models/form/account_batch.rb' - - 'app/models/media_attachment.rb' - - 'app/models/status.rb' - - 'app/models/tag.rb' - - 'app/models/user.rb' - - 'app/serializers/activitypub/actor_serializer.rb' - - 'app/serializers/activitypub/note_serializer.rb' - - 'app/serializers/rest/status_serializer.rb' - - 'app/services/account_search_service.rb' - - 'app/services/activitypub/process_account_service.rb' - - 'app/services/activitypub/process_status_update_service.rb' - - 'app/services/backup_service.rb' - - 'app/services/bulk_import_service.rb' - - 'app/services/delete_account_service.rb' - - 'app/services/fan_out_on_write_service.rb' - - 'app/services/fetch_link_card_service.rb' - - 'app/services/import_service.rb' - - 'app/services/notify_service.rb' - - 'app/services/post_status_service.rb' - - 'app/services/update_status_service.rb' - - 'lib/paperclip/color_extractor.rb' - # Reason: Currently disabled in .rubocop_todo.yml # https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity Metrics/CyclomaticComplexity: @@ -149,17 +95,10 @@ Metrics/CyclomaticComplexity: - lib/mastodon/cli/*.rb - db/*migrate/**/* -# Reason: Currently disabled in .rubocop_todo.yml -# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength -Metrics/MethodLength: - CountAsOne: [array, heredoc] - Exclude: - - 'lib/mastodon/cli/*.rb' - # Reason: -# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmodulelength -Metrics/ModuleLength: - CountAsOne: [array, heredoc] +# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsparameterlists +Metrics/ParameterLists: + CountKeywordArgs: false # Reason: Prevailing style is argument file paths # https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsfilepath diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 59186681c9..c8b287f90d 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -154,12 +154,6 @@ Lint/Void: Metrics/AbcSize: Max: 150 -# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. -# AllowedMethods: refine -Metrics/BlockLength: - Exclude: - - 'app/models/concerns/status_safe_reblog_insert.rb' - # Configuration parameters: CountBlocks, Max. Metrics/BlockNesting: Exclude: @@ -169,27 +163,6 @@ Metrics/BlockNesting: Metrics/CyclomaticComplexity: Max: 25 -# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. -Metrics/MethodLength: - Max: 58 - -# Configuration parameters: CountComments, Max, CountAsOne. -Metrics/ModuleLength: - Exclude: - - 'app/controllers/concerns/signature_verification.rb' - - 'app/helpers/application_helper.rb' - - 'app/helpers/jsonld_helper.rb' - - 'app/models/concerns/account_interactions.rb' - - 'app/models/concerns/has_user_settings.rb' - -# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters. -Metrics/ParameterLists: - Exclude: - - 'app/models/concerns/account_interactions.rb' - - 'app/services/activitypub/fetch_remote_account_service.rb' - - 'app/services/activitypub/fetch_remote_actor_service.rb' - - 'app/services/activitypub/fetch_remote_status_service.rb' - # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/PerceivedComplexity: Max: 28 diff --git a/app/helpers/languages_helper.rb b/app/helpers/languages_helper.rb index 00e1e5178b..840a18d3e6 100644 --- a/app/helpers/languages_helper.rb +++ b/app/helpers/languages_helper.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -# rubocop:disable Metrics/ModuleLength - module LanguagesHelper ISO_639_1 = { aa: ['Afar', 'Afaraf'].freeze, From 00c222377db0e305ac3f4a15bf1c18eb89c1f45f Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 31 May 2023 17:34:48 -0400 Subject: [PATCH 17/69] Remove unused Settings::Extend module (#25214) --- app/lib/settings/extend.rb | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 app/lib/settings/extend.rb diff --git a/app/lib/settings/extend.rb b/app/lib/settings/extend.rb deleted file mode 100644 index 5fb2c8aae0..0000000000 --- a/app/lib/settings/extend.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module Settings - module Extend - def settings - @settings ||= ScopedSettings.new(self) - end - end -end From 44cd88adc4e2f4028dcc2b08b98368f0dc90cee4 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 31 May 2023 23:43:39 +0200 Subject: [PATCH 18/69] Upgrade react-intl (#24906) --- .eslintrc.js | 1 + .github/dependabot.yml | 12 - .github/workflows/check-i18n.yml | 3 +- .../mastodon/actions/notifications.js | 2 +- app/javascript/mastodon/components/domain.tsx | 11 +- .../mastodon/components/load_gap.tsx | 10 +- .../components/relative_timestamp.tsx | 14 +- .../mastodon/containers/admin_component.jsx | 9 +- .../mastodon/containers/compose_container.jsx | 9 +- .../mastodon/containers/mastodon.jsx | 9 +- .../mastodon/containers/media_container.jsx | 9 +- .../mastodon/features/onboarding/follows.jsx | 4 +- .../mastodon/features/onboarding/share.jsx | 8 +- app/javascript/mastodon/load_locale.js | 14 + .../mastodon/locales/defaultMessages.json | 4484 ----------------- app/javascript/mastodon/locales/index.js | 13 + .../mastodon/locales/whitelist_af.json | 2 - .../mastodon/locales/whitelist_an.json | 2 - .../mastodon/locales/whitelist_ar.json | 2 - .../mastodon/locales/whitelist_ast.json | 2 - .../mastodon/locales/whitelist_be.json | 2 - .../mastodon/locales/whitelist_bg.json | 2 - .../mastodon/locales/whitelist_bn.json | 2 - .../mastodon/locales/whitelist_br.json | 2 - .../mastodon/locales/whitelist_bs.json | 2 - .../mastodon/locales/whitelist_ca.json | 2 - .../mastodon/locales/whitelist_ckb.json | 2 - .../mastodon/locales/whitelist_co.json | 2 - .../mastodon/locales/whitelist_cs.json | 2 - .../mastodon/locales/whitelist_csb.json | 2 - .../mastodon/locales/whitelist_cy.json | 2 - .../mastodon/locales/whitelist_da.json | 2 - .../mastodon/locales/whitelist_de.json | 5 - .../mastodon/locales/whitelist_el.json | 2 - .../mastodon/locales/whitelist_en-GB.json | 2 - .../mastodon/locales/whitelist_en.json | 2 - .../mastodon/locales/whitelist_eo.json | 2 - .../mastodon/locales/whitelist_es-AR.json | 2 - .../mastodon/locales/whitelist_es-MX.json | 2 - .../mastodon/locales/whitelist_es.json | 2 - .../mastodon/locales/whitelist_et.json | 2 - .../mastodon/locales/whitelist_eu.json | 2 - .../mastodon/locales/whitelist_fa.json | 2 - .../mastodon/locales/whitelist_fi.json | 2 - .../mastodon/locales/whitelist_fo.json | 2 - .../mastodon/locales/whitelist_fr-QC.json | 2 - .../mastodon/locales/whitelist_fr.json | 2 - .../mastodon/locales/whitelist_fy.json | 2 - .../mastodon/locales/whitelist_ga.json | 2 - .../mastodon/locales/whitelist_gd.json | 2 - .../mastodon/locales/whitelist_gl.json | 2 - .../mastodon/locales/whitelist_he.json | 2 - .../mastodon/locales/whitelist_hi.json | 2 - .../mastodon/locales/whitelist_hr.json | 2 - .../mastodon/locales/whitelist_hu.json | 2 - .../mastodon/locales/whitelist_hy.json | 2 - .../mastodon/locales/whitelist_id.json | 2 - .../mastodon/locales/whitelist_ig.json | 2 - .../mastodon/locales/whitelist_io.json | 2 - .../mastodon/locales/whitelist_is.json | 2 - .../mastodon/locales/whitelist_it.json | 2 - .../mastodon/locales/whitelist_ja.json | 2 - .../mastodon/locales/whitelist_ka.json | 2 - .../mastodon/locales/whitelist_kab.json | 2 - .../mastodon/locales/whitelist_kk.json | 2 - .../mastodon/locales/whitelist_kn.json | 2 - .../mastodon/locales/whitelist_ko.json | 2 - .../mastodon/locales/whitelist_ku.json | 2 - .../mastodon/locales/whitelist_kw.json | 2 - .../mastodon/locales/whitelist_la.json | 2 - .../mastodon/locales/whitelist_lt.json | 2 - .../mastodon/locales/whitelist_lv.json | 2 - .../mastodon/locales/whitelist_mk.json | 2 - .../mastodon/locales/whitelist_ml.json | 2 - .../mastodon/locales/whitelist_mr.json | 2 - .../mastodon/locales/whitelist_ms.json | 2 - .../mastodon/locales/whitelist_my.json | 2 - .../mastodon/locales/whitelist_nl.json | 2 - .../mastodon/locales/whitelist_nn.json | 2 - .../mastodon/locales/whitelist_no.json | 2 - .../mastodon/locales/whitelist_oc.json | 2 - .../mastodon/locales/whitelist_pa.json | 2 - .../mastodon/locales/whitelist_pl.json | 2 - .../mastodon/locales/whitelist_pt-BR.json | 2 - .../mastodon/locales/whitelist_pt-PT.json | 2 - .../mastodon/locales/whitelist_ro.json | 2 - .../mastodon/locales/whitelist_ru.json | 2 - .../mastodon/locales/whitelist_sa.json | 2 - .../mastodon/locales/whitelist_sc.json | 2 - .../mastodon/locales/whitelist_sco.json | 2 - .../mastodon/locales/whitelist_si.json | 2 - .../mastodon/locales/whitelist_sk.json | 2 - .../mastodon/locales/whitelist_sl.json | 2 - .../mastodon/locales/whitelist_sq.json | 2 - .../mastodon/locales/whitelist_sr-Latn.json | 2 - .../mastodon/locales/whitelist_sr.json | 2 - .../mastodon/locales/whitelist_sv.json | 2 - .../mastodon/locales/whitelist_szl.json | 2 - .../mastodon/locales/whitelist_ta.json | 2 - .../mastodon/locales/whitelist_tai.json | 2 - .../mastodon/locales/whitelist_te.json | 2 - .../mastodon/locales/whitelist_th.json | 2 - .../mastodon/locales/whitelist_tr.json | 2 - .../mastodon/locales/whitelist_tt.json | 2 - .../mastodon/locales/whitelist_ug.json | 2 - .../mastodon/locales/whitelist_uk.json | 2 - .../mastodon/locales/whitelist_ur.json | 2 - .../mastodon/locales/whitelist_uz.json | 2 - .../mastodon/locales/whitelist_vi.json | 2 - .../mastodon/locales/whitelist_zgh.json | 2 - .../mastodon/locales/whitelist_zh-CN.json | 2 - .../mastodon/locales/whitelist_zh-HK.json | 2 - .../mastodon/locales/whitelist_zh-TW.json | 2 - .../mastodon/polyfills/base_polyfills.ts | 2 - app/javascript/mastodon/polyfills/index.ts | 4 +- app/javascript/mastodon/polyfills/intl.ts | 105 + .../service_worker/web_push_locales.js | 2 +- .../service_worker/web_push_notifications.js | 2 +- app/javascript/packs/application.js | 3 +- app/javascript/packs/public.jsx | 6 +- app/javascript/packs/share.jsx | 4 +- app/views/layouts/application.html.haml | 2 +- app/views/layouts/embedded.html.haml | 2 +- babel.config.js | 2 +- config/formatjs-formatter.js | 11 + config/webpack/generateLocalePacks.js | 51 - config/webpack/shared.js | 6 - config/webpack/translationRunner.js | 102 +- package.json | 13 +- yarn.lock | 333 +- 130 files changed, 413 insertions(+), 5046 deletions(-) create mode 100644 app/javascript/mastodon/load_locale.js delete mode 100644 app/javascript/mastodon/locales/defaultMessages.json delete mode 100644 app/javascript/mastodon/locales/whitelist_af.json delete mode 100644 app/javascript/mastodon/locales/whitelist_an.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ar.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ast.json delete mode 100644 app/javascript/mastodon/locales/whitelist_be.json delete mode 100644 app/javascript/mastodon/locales/whitelist_bg.json delete mode 100644 app/javascript/mastodon/locales/whitelist_bn.json delete mode 100644 app/javascript/mastodon/locales/whitelist_br.json delete mode 100644 app/javascript/mastodon/locales/whitelist_bs.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ca.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ckb.json delete mode 100644 app/javascript/mastodon/locales/whitelist_co.json delete mode 100644 app/javascript/mastodon/locales/whitelist_cs.json delete mode 100644 app/javascript/mastodon/locales/whitelist_csb.json delete mode 100644 app/javascript/mastodon/locales/whitelist_cy.json delete mode 100644 app/javascript/mastodon/locales/whitelist_da.json delete mode 100644 app/javascript/mastodon/locales/whitelist_de.json delete mode 100644 app/javascript/mastodon/locales/whitelist_el.json delete mode 100644 app/javascript/mastodon/locales/whitelist_en-GB.json delete mode 100644 app/javascript/mastodon/locales/whitelist_en.json delete mode 100644 app/javascript/mastodon/locales/whitelist_eo.json delete mode 100644 app/javascript/mastodon/locales/whitelist_es-AR.json delete mode 100644 app/javascript/mastodon/locales/whitelist_es-MX.json delete mode 100644 app/javascript/mastodon/locales/whitelist_es.json delete mode 100644 app/javascript/mastodon/locales/whitelist_et.json delete mode 100644 app/javascript/mastodon/locales/whitelist_eu.json delete mode 100644 app/javascript/mastodon/locales/whitelist_fa.json delete mode 100644 app/javascript/mastodon/locales/whitelist_fi.json delete mode 100644 app/javascript/mastodon/locales/whitelist_fo.json delete mode 100644 app/javascript/mastodon/locales/whitelist_fr-QC.json delete mode 100644 app/javascript/mastodon/locales/whitelist_fr.json delete mode 100644 app/javascript/mastodon/locales/whitelist_fy.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ga.json delete mode 100644 app/javascript/mastodon/locales/whitelist_gd.json delete mode 100644 app/javascript/mastodon/locales/whitelist_gl.json delete mode 100644 app/javascript/mastodon/locales/whitelist_he.json delete mode 100644 app/javascript/mastodon/locales/whitelist_hi.json delete mode 100644 app/javascript/mastodon/locales/whitelist_hr.json delete mode 100644 app/javascript/mastodon/locales/whitelist_hu.json delete mode 100644 app/javascript/mastodon/locales/whitelist_hy.json delete mode 100644 app/javascript/mastodon/locales/whitelist_id.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ig.json delete mode 100644 app/javascript/mastodon/locales/whitelist_io.json delete mode 100644 app/javascript/mastodon/locales/whitelist_is.json delete mode 100644 app/javascript/mastodon/locales/whitelist_it.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ja.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ka.json delete mode 100644 app/javascript/mastodon/locales/whitelist_kab.json delete mode 100644 app/javascript/mastodon/locales/whitelist_kk.json delete mode 100644 app/javascript/mastodon/locales/whitelist_kn.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ko.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ku.json delete mode 100644 app/javascript/mastodon/locales/whitelist_kw.json delete mode 100644 app/javascript/mastodon/locales/whitelist_la.json delete mode 100644 app/javascript/mastodon/locales/whitelist_lt.json delete mode 100644 app/javascript/mastodon/locales/whitelist_lv.json delete mode 100644 app/javascript/mastodon/locales/whitelist_mk.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ml.json delete mode 100644 app/javascript/mastodon/locales/whitelist_mr.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ms.json delete mode 100644 app/javascript/mastodon/locales/whitelist_my.json delete mode 100644 app/javascript/mastodon/locales/whitelist_nl.json delete mode 100644 app/javascript/mastodon/locales/whitelist_nn.json delete mode 100644 app/javascript/mastodon/locales/whitelist_no.json delete mode 100644 app/javascript/mastodon/locales/whitelist_oc.json delete mode 100644 app/javascript/mastodon/locales/whitelist_pa.json delete mode 100644 app/javascript/mastodon/locales/whitelist_pl.json delete mode 100644 app/javascript/mastodon/locales/whitelist_pt-BR.json delete mode 100644 app/javascript/mastodon/locales/whitelist_pt-PT.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ro.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ru.json delete mode 100644 app/javascript/mastodon/locales/whitelist_sa.json delete mode 100644 app/javascript/mastodon/locales/whitelist_sc.json delete mode 100644 app/javascript/mastodon/locales/whitelist_sco.json delete mode 100644 app/javascript/mastodon/locales/whitelist_si.json delete mode 100644 app/javascript/mastodon/locales/whitelist_sk.json delete mode 100644 app/javascript/mastodon/locales/whitelist_sl.json delete mode 100644 app/javascript/mastodon/locales/whitelist_sq.json delete mode 100644 app/javascript/mastodon/locales/whitelist_sr-Latn.json delete mode 100644 app/javascript/mastodon/locales/whitelist_sr.json delete mode 100644 app/javascript/mastodon/locales/whitelist_sv.json delete mode 100644 app/javascript/mastodon/locales/whitelist_szl.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ta.json delete mode 100644 app/javascript/mastodon/locales/whitelist_tai.json delete mode 100644 app/javascript/mastodon/locales/whitelist_te.json delete mode 100644 app/javascript/mastodon/locales/whitelist_th.json delete mode 100644 app/javascript/mastodon/locales/whitelist_tr.json delete mode 100644 app/javascript/mastodon/locales/whitelist_tt.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ug.json delete mode 100644 app/javascript/mastodon/locales/whitelist_uk.json delete mode 100644 app/javascript/mastodon/locales/whitelist_ur.json delete mode 100644 app/javascript/mastodon/locales/whitelist_uz.json delete mode 100644 app/javascript/mastodon/locales/whitelist_vi.json delete mode 100644 app/javascript/mastodon/locales/whitelist_zgh.json delete mode 100644 app/javascript/mastodon/locales/whitelist_zh-CN.json delete mode 100644 app/javascript/mastodon/locales/whitelist_zh-HK.json delete mode 100644 app/javascript/mastodon/locales/whitelist_zh-TW.json create mode 100644 app/javascript/mastodon/polyfills/intl.ts create mode 100644 config/formatjs-formatter.js delete mode 100644 config/webpack/generateLocalePacks.js diff --git a/.eslintrc.js b/.eslintrc.js index bfade8976f..24961cdd9d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -293,6 +293,7 @@ module.exports = { '.*rc.js', 'ide-helper.js', 'config/webpack/**/*', + 'config/formatjs-formatter.js', ], env: { diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 39ae6bc080..5e6556cb53 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -25,18 +25,6 @@ updates: - dependency-name: 'react-hotkeys' versions: - '>= 2' - # TODO: This version has breaking changes - - dependency-name: 'intl-messageformat' - versions: - - '>= 3' - # TODO: This version has breaking changes - - dependency-name: 'react-intl' - versions: - - '>= 3' - # TODO: This version has breaking changes - - dependency-name: 'babel-plugin-react-intl' - versions: - - '>= 7' # TODO: This version requires code changes - dependency-name: 'webpack-dev-server' versions: diff --git a/.github/workflows/check-i18n.yml b/.github/workflows/check-i18n.yml index e282e2ab72..b67c503e95 100644 --- a/.github/workflows/check-i18n.yml +++ b/.github/workflows/check-i18n.yml @@ -41,8 +41,7 @@ jobs: - name: Check for missing strings in English JSON run: | - yarn build:development - yarn manage:translations en + yarn i18n:extract --throws git diff --exit-code - name: Check locale file normalization diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index c040edb58b..6e8ddb2279 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -1,4 +1,4 @@ -import IntlMessageFormat from 'intl-messageformat'; +import { IntlMessageFormat } from 'intl-messageformat'; import { defineMessages } from 'react-intl'; import { List as ImmutableList } from 'immutable'; diff --git a/app/javascript/mastodon/components/domain.tsx b/app/javascript/mastodon/components/domain.tsx index db18635be1..f4a3b9d4b6 100644 --- a/app/javascript/mastodon/components/domain.tsx +++ b/app/javascript/mastodon/components/domain.tsx @@ -1,7 +1,6 @@ import { useCallback } from 'react'; -import type { InjectedIntl } from 'react-intl'; -import { defineMessages, injectIntl } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; import { IconButton } from './icon_button'; @@ -15,9 +14,11 @@ const messages = defineMessages({ interface Props { domain: string; onUnblockDomain: (domain: string) => void; - intl: InjectedIntl; } -const _Domain: React.FC = ({ domain, onUnblockDomain, intl }) => { + +export const Domain: React.FC = ({ domain, onUnblockDomain }) => { + const intl = useIntl(); + const handleDomainUnblock = useCallback(() => { onUnblockDomain(domain); }, [domain, onUnblockDomain]); @@ -41,5 +42,3 @@ const _Domain: React.FC = ({ domain, onUnblockDomain, intl }) => { ); }; - -export const Domain = injectIntl(_Domain); diff --git a/app/javascript/mastodon/components/load_gap.tsx b/app/javascript/mastodon/components/load_gap.tsx index e6d3060eb3..7e2cd447b9 100644 --- a/app/javascript/mastodon/components/load_gap.tsx +++ b/app/javascript/mastodon/components/load_gap.tsx @@ -1,7 +1,6 @@ import { useCallback } from 'react'; -import type { InjectedIntl } from 'react-intl'; -import { injectIntl, defineMessages } from 'react-intl'; +import { useIntl, defineMessages } from 'react-intl'; import { Icon } from 'mastodon/components/icon'; @@ -13,10 +12,11 @@ interface Props { disabled: boolean; maxId: string; onClick: (maxId: string) => void; - intl: InjectedIntl; } -const _LoadGap: React.FC = ({ disabled, maxId, onClick, intl }) => { +export const LoadGap: React.FC = ({ disabled, maxId, onClick }) => { + const intl = useIntl(); + const handleClick = useCallback(() => { onClick(maxId); }, [maxId, onClick]); @@ -32,5 +32,3 @@ const _LoadGap: React.FC = ({ disabled, maxId, onClick, intl }) => { ); }; - -export const LoadGap = injectIntl(_LoadGap); diff --git a/app/javascript/mastodon/components/relative_timestamp.tsx b/app/javascript/mastodon/components/relative_timestamp.tsx index aaa424dca6..e4a8437d0e 100644 --- a/app/javascript/mastodon/components/relative_timestamp.tsx +++ b/app/javascript/mastodon/components/relative_timestamp.tsx @@ -1,6 +1,6 @@ import { Component } from 'react'; -import type { InjectedIntl } from 'react-intl'; +import type { IntlShape } from 'react-intl'; import { injectIntl, defineMessages } from 'react-intl'; const messages = defineMessages({ @@ -103,7 +103,7 @@ const getUnitDelay = (units: string) => { }; export const timeAgoString = ( - intl: InjectedIntl, + intl: IntlShape, date: Date, now: number, year: number, @@ -155,7 +155,7 @@ export const timeAgoString = ( }; const timeRemainingString = ( - intl: InjectedIntl, + intl: IntlShape, date: Date, now: number, timeGiven = true @@ -190,7 +190,7 @@ const timeRemainingString = ( }; interface Props { - intl: InjectedIntl; + intl: IntlShape; timestamp: string; year: number; futureDate?: boolean; @@ -201,7 +201,7 @@ interface States { } class RelativeTimestamp extends Component { state = { - now: this.props.intl.now(), + now: Date.now(), }; static defaultProps = { @@ -223,7 +223,7 @@ class RelativeTimestamp extends Component { UNSAFE_componentWillReceiveProps(nextProps: Props) { if (this.props.timestamp !== nextProps.timestamp) { - this.setState({ now: this.props.intl.now() }); + this.setState({ now: Date.now() }); } } @@ -253,7 +253,7 @@ class RelativeTimestamp extends Component { : Math.max(updateInterval, unitRemainder); this._timer = window.setTimeout(() => { - this.setState({ now: this.props.intl.now() }); + this.setState({ now: Date.now() }); }, delay); } diff --git a/app/javascript/mastodon/containers/admin_component.jsx b/app/javascript/mastodon/containers/admin_component.jsx index f5fa53f08e..562151fe24 100644 --- a/app/javascript/mastodon/containers/admin_component.jsx +++ b/app/javascript/mastodon/containers/admin_component.jsx @@ -1,12 +1,11 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { IntlProvider, addLocaleData } from 'react-intl'; +import { IntlProvider } from 'react-intl'; -import { getLocale } from '../locales'; +import { getLocale, onProviderError } from '../locales'; -const { localeData, messages } = getLocale(); -addLocaleData(localeData); +const { messages } = getLocale(); export default class AdminComponent extends PureComponent { @@ -19,7 +18,7 @@ export default class AdminComponent extends PureComponent { const { locale, children } = this.props; return ( - + {children} ); diff --git a/app/javascript/mastodon/containers/compose_container.jsx b/app/javascript/mastodon/containers/compose_container.jsx index b93399aa91..751015d18d 100644 --- a/app/javascript/mastodon/containers/compose_container.jsx +++ b/app/javascript/mastodon/containers/compose_container.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { IntlProvider, addLocaleData } from 'react-intl'; +import { IntlProvider } from 'react-intl'; import { Provider } from 'react-redux'; @@ -9,11 +9,10 @@ import { fetchCustomEmojis } from '../actions/custom_emojis'; import { hydrateStore } from '../actions/store'; import Compose from '../features/standalone/compose'; import initialState from '../initial_state'; -import { getLocale } from '../locales'; +import { getLocale, onProviderError } from '../locales'; import { store } from '../store'; -const { localeData, messages } = getLocale(); -addLocaleData(localeData); +const { messages } = getLocale(); if (initialState) { store.dispatch(hydrateStore(initialState)); @@ -31,7 +30,7 @@ export default class TimelineContainer extends PureComponent { const { locale } = this.props; return ( - + diff --git a/app/javascript/mastodon/containers/mastodon.jsx b/app/javascript/mastodon/containers/mastodon.jsx index 5be163f5a4..c4d4611a2d 100644 --- a/app/javascript/mastodon/containers/mastodon.jsx +++ b/app/javascript/mastodon/containers/mastodon.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { IntlProvider, addLocaleData } from 'react-intl'; +import { IntlProvider } from 'react-intl'; import { Helmet } from 'react-helmet'; import { BrowserRouter, Route } from 'react-router-dom'; @@ -16,11 +16,10 @@ import { connectUserStream } from 'mastodon/actions/streaming'; import ErrorBoundary from 'mastodon/components/error_boundary'; import UI from 'mastodon/features/ui'; import initialState, { title as siteTitle } from 'mastodon/initial_state'; -import { getLocale } from 'mastodon/locales'; +import { getLocale, onProviderError } from 'mastodon/locales'; import { store } from 'mastodon/store'; -const { localeData, messages } = getLocale(); -addLocaleData(localeData); +const { messages } = getLocale(); const title = process.env.NODE_ENV === 'production' ? siteTitle : `${siteTitle} (Dev)`; @@ -83,7 +82,7 @@ export default class Mastodon extends PureComponent { const { locale } = this.props; return ( - + diff --git a/app/javascript/mastodon/containers/media_container.jsx b/app/javascript/mastodon/containers/media_container.jsx index 7ed8f1719d..84eab1cae1 100644 --- a/app/javascript/mastodon/containers/media_container.jsx +++ b/app/javascript/mastodon/containers/media_container.jsx @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; import { createPortal } from 'react-dom'; -import { IntlProvider, addLocaleData } from 'react-intl'; +import { IntlProvider } from 'react-intl'; import { fromJS } from 'immutable'; @@ -14,11 +14,10 @@ import Audio from 'mastodon/features/audio'; import Card from 'mastodon/features/status/components/card'; import MediaModal from 'mastodon/features/ui/components/media_modal'; import Video from 'mastodon/features/video'; -import { getLocale } from 'mastodon/locales'; +import { getLocale, onProviderError } from 'mastodon/locales'; import { getScrollbarWidth } from 'mastodon/utils/scrollbar'; -const { localeData, messages } = getLocale(); -addLocaleData(localeData); +const { messages } = getLocale(); const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll, Hashtag, Audio }; @@ -84,7 +83,7 @@ export default class MediaContainer extends PureComponent { } return ( - + <> {[].map.call(components, (component, i) => { const componentName = component.getAttribute('data-component'); diff --git a/app/javascript/mastodon/features/onboarding/follows.jsx b/app/javascript/mastodon/features/onboarding/follows.jsx index 3807ce9227..8b4ad0b087 100644 --- a/app/javascript/mastodon/features/onboarding/follows.jsx +++ b/app/javascript/mastodon/features/onboarding/follows.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { FormattedMessage, FormattedHTMLMessage } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; @@ -77,7 +77,7 @@ class Follows extends PureComponent { {loadedContent} -

+

{chunks} }} />

diff --git a/app/javascript/mastodon/features/onboarding/share.jsx b/app/javascript/mastodon/features/onboarding/share.jsx index 1895af912b..6871793026 100644 --- a/app/javascript/mastodon/features/onboarding/share.jsx +++ b/app/javascript/mastodon/features/onboarding/share.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { defineMessages, injectIntl, FormattedMessage, FormattedHTMLMessage } from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import { Link } from 'react-router-dom'; @@ -168,9 +168,9 @@ class Share extends PureComponent { -

-

-

+

{chunks} }} />

+

{chunks} }} />

+

{chunks} }} />

diff --git a/app/javascript/mastodon/load_locale.js b/app/javascript/mastodon/load_locale.js new file mode 100644 index 0000000000..cb14acd622 --- /dev/null +++ b/app/javascript/mastodon/load_locale.js @@ -0,0 +1,14 @@ +import { setLocale } from "./locales"; + +export async function loadLocale() { + const locale = document.querySelector('html').lang || 'en'; + + const localeData = await import( + /* webpackMode: "lazy" */ + /* webpackChunkName: "locale/[request]" */ + /* webpackInclude: /\.json$/ */ + /* webpackPreload: true */ + `mastodon/locales/${locale}.json`); + + setLocale({ messages: localeData }); +} diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json deleted file mode 100644 index d446989ab6..0000000000 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ /dev/null @@ -1,4484 +0,0 @@ -[ - { - "descriptors": [ - { - "defaultMessage": "Oops!", - "id": "alert.unexpected.title" - }, - { - "defaultMessage": "An unexpected error occurred.", - "id": "alert.unexpected.message" - }, - { - "defaultMessage": "Rate limited", - "id": "alert.rate_limited.title" - }, - { - "defaultMessage": "Please retry after {retry_time, time, medium}.", - "id": "alert.rate_limited.message" - } - ], - "path": "app/javascript/mastodon/actions/alerts.json" - }, - { - "descriptors": [ - { - "defaultMessage": "File upload limit exceeded.", - "id": "upload_error.limit" - }, - { - "defaultMessage": "File upload not allowed with polls.", - "id": "upload_error.poll" - } - ], - "path": "app/javascript/mastodon/actions/compose.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{name} mentioned you", - "id": "notification.mention" - }, - { - "defaultMessage": "{count} notifications", - "id": "notifications.group" - } - ], - "path": "app/javascript/mastodon/actions/notifications.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Follow", - "id": "account.follow" - }, - { - "defaultMessage": "Unfollow", - "id": "account.unfollow" - }, - { - "defaultMessage": "Awaiting approval. Click to cancel follow request", - "id": "account.requested" - }, - { - "defaultMessage": "Unblock @{name}", - "id": "account.unblock" - }, - { - "defaultMessage": "Unmute @{name}", - "id": "account.unmute" - }, - { - "defaultMessage": "Mute notifications from @{name}", - "id": "account.mute_notifications" - }, - { - "defaultMessage": "Unmute notifications from @{name}", - "id": "account.unmute_notifications" - }, - { - "defaultMessage": "Mute @{name}", - "id": "account.mute" - }, - { - "defaultMessage": "Block @{name}", - "id": "account.block" - } - ], - "path": "app/javascript/mastodon/components/account.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Other", - "id": "report.categories.other" - }, - { - "defaultMessage": "Spam", - "id": "report.categories.spam" - }, - { - "defaultMessage": "Content violates one or more server rules", - "id": "report.categories.violation" - } - ], - "path": "app/javascript/mastodon/components/admin/ReportReasonSelector.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Loading...", - "id": "loading_indicator.label" - }, - { - "defaultMessage": "Sign-up month", - "id": "admin.dashboard.retention.cohort" - }, - { - "defaultMessage": "New users", - "id": "admin.dashboard.retention.cohort_size" - }, - { - "defaultMessage": "Average", - "id": "admin.dashboard.retention.average" - }, - { - "defaultMessage": "User retention rate by day after sign-up", - "id": "admin.dashboard.daily_retention" - }, - { - "defaultMessage": "User retention rate by month after sign-up", - "id": "admin.dashboard.monthly_retention" - } - ], - "path": "app/javascript/mastodon/components/admin/Retention.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Trending now", - "id": "trends.trending_now" - } - ], - "path": "app/javascript/mastodon/components/admin/Trends.json" - }, - { - "descriptors": [ - { - "defaultMessage": "(unprocessed)", - "id": "attachments_list.unprocessed" - } - ], - "path": "app/javascript/mastodon/components/attachment_list.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{count} per week", - "id": "autosuggest_hashtag.per_week" - } - ], - "path": "app/javascript/mastodon/components/autosuggest_hashtag.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Back", - "id": "column_back_button.label" - } - ], - "path": "app/javascript/mastodon/components/column_back_button_slim.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Back", - "id": "column_back_button.label" - } - ], - "path": "app/javascript/mastodon/components/column_back_button.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Show settings", - "id": "column_header.show_settings" - }, - { - "defaultMessage": "Hide settings", - "id": "column_header.hide_settings" - }, - { - "defaultMessage": "Move column to the left", - "id": "column_header.moveLeft_settings" - }, - { - "defaultMessage": "Move column to the right", - "id": "column_header.moveRight_settings" - }, - { - "defaultMessage": "Unpin", - "id": "column_header.unpin" - }, - { - "defaultMessage": "Pin", - "id": "column_header.pin" - }, - { - "defaultMessage": "Back", - "id": "column_back_button.label" - } - ], - "path": "app/javascript/mastodon/components/column_header.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{count, plural, one {{counter} Post} other {{counter} Posts}}", - "id": "account.statuses_counter" - }, - { - "defaultMessage": "{count, plural, one {{counter} Following} other {{counter} Following}}", - "id": "account.following_counter" - }, - { - "defaultMessage": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", - "id": "account.followers_counter" - } - ], - "path": "app/javascript/mastodon/components/common_counter.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Dismiss", - "id": "dismissable_banner.dismiss" - } - ], - "path": "app/javascript/mastodon/components/dismissable_banner.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Unblock domain {domain}", - "id": "account.unblock_domain" - } - ], - "path": "app/javascript/mastodon/components/domain.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Edited {count, plural, one {# time} other {# times}}", - "id": "status.edited_x_times" - }, - { - "defaultMessage": "{name} created {date}", - "id": "status.history.created" - }, - { - "defaultMessage": "{name} edited {date}", - "id": "status.history.edited" - }, - { - "defaultMessage": "Edited {date}", - "id": "status.edited" - } - ], - "path": "app/javascript/mastodon/components/edited_timestamp/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.", - "id": "error.unexpected_crash.explanation_addons" - }, - { - "defaultMessage": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", - "id": "error.unexpected_crash.explanation" - }, - { - "defaultMessage": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", - "id": "error.unexpected_crash.next_steps_addons" - }, - { - "defaultMessage": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", - "id": "error.unexpected_crash.next_steps" - }, - { - "defaultMessage": "Report issue", - "id": "errors.unexpected_crash.report_issue" - }, - { - "defaultMessage": "Copy stacktrace to clipboard", - "id": "errors.unexpected_crash.copy_stacktrace" - } - ], - "path": "app/javascript/mastodon/components/error_boundary.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}", - "id": "trends.counter_by_accounts" - } - ], - "path": "app/javascript/mastodon/components/hashtag.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Load more", - "id": "status.load_more" - } - ], - "path": "app/javascript/mastodon/components/load_gap.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Load more", - "id": "status.load_more" - } - ], - "path": "app/javascript/mastodon/components/load_more.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{count, plural, one {# new item} other {# new items}}", - "id": "load_pending" - } - ], - "path": "app/javascript/mastodon/components/load_pending.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{number, plural, one {Hide image} other {Hide images}}", - "id": "media_gallery.toggle_visible" - }, - { - "defaultMessage": "Not available", - "id": "status.uncached_media_warning" - }, - { - "defaultMessage": "Sensitive content", - "id": "status.sensitive_warning" - }, - { - "defaultMessage": "Media hidden", - "id": "status.media_hidden" - } - ], - "path": "app/javascript/mastodon/components/media_gallery.json" - }, - { - "descriptors": [ - { - "defaultMessage": "You need to login to access this resource.", - "id": "not_signed_in_indicator.not_signed_in" - } - ], - "path": "app/javascript/mastodon/components/not_signed_in_indicator.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Put it back", - "id": "picture_in_picture.restore" - } - ], - "path": "app/javascript/mastodon/components/picture_in_picture_placeholder.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Closed", - "id": "poll.closed" - }, - { - "defaultMessage": "You voted for this answer", - "id": "poll.voted" - }, - { - "defaultMessage": "{votes, plural, one {# vote} other {# votes}}", - "id": "poll.votes" - }, - { - "defaultMessage": "{count, plural, one {# person} other {# people}}", - "id": "poll.total_people" - }, - { - "defaultMessage": "{count, plural, one {# vote} other {# votes}}", - "id": "poll.total_votes" - }, - { - "defaultMessage": "Vote", - "id": "poll.vote" - }, - { - "defaultMessage": "Refresh", - "id": "poll.refresh" - } - ], - "path": "app/javascript/mastodon/components/poll.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Loading…", - "id": "regeneration_indicator.label" - }, - { - "defaultMessage": "Your home feed is being prepared!", - "id": "regeneration_indicator.sublabel" - } - ], - "path": "app/javascript/mastodon/components/regeneration_indicator.json" - }, - { - "descriptors": [ - { - "defaultMessage": "today", - "id": "relative_time.today" - }, - { - "defaultMessage": "now", - "id": "relative_time.just_now" - }, - { - "defaultMessage": "just now", - "id": "relative_time.full.just_now" - }, - { - "defaultMessage": "{number}s", - "id": "relative_time.seconds" - }, - { - "defaultMessage": "{number, plural, one {# second} other {# seconds}} ago", - "id": "relative_time.full.seconds" - }, - { - "defaultMessage": "{number}m", - "id": "relative_time.minutes" - }, - { - "defaultMessage": "{number, plural, one {# minute} other {# minutes}} ago", - "id": "relative_time.full.minutes" - }, - { - "defaultMessage": "{number}h", - "id": "relative_time.hours" - }, - { - "defaultMessage": "{number, plural, one {# hour} other {# hours}} ago", - "id": "relative_time.full.hours" - }, - { - "defaultMessage": "{number}d", - "id": "relative_time.days" - }, - { - "defaultMessage": "{number, plural, one {# day} other {# days}} ago", - "id": "relative_time.full.days" - }, - { - "defaultMessage": "Moments remaining", - "id": "time_remaining.moments" - }, - { - "defaultMessage": "{number, plural, one {# second} other {# seconds}} left", - "id": "time_remaining.seconds" - }, - { - "defaultMessage": "{number, plural, one {# minute} other {# minutes}} left", - "id": "time_remaining.minutes" - }, - { - "defaultMessage": "{number, plural, one {# hour} other {# hours}} left", - "id": "time_remaining.hours" - }, - { - "defaultMessage": "{number, plural, one {# day} other {# days}} left", - "id": "time_remaining.days" - } - ], - "path": "app/javascript/mastodon/components/relative_timestamp.json" - }, - { - "descriptors": [ - { - "defaultMessage": "People using this server during the last 30 days (Monthly Active Users)", - "id": "server_banner.about_active_users" - }, - { - "defaultMessage": "{domain} is part of the decentralized social network powered by {mastodon}.", - "id": "server_banner.introduction" - }, - { - "defaultMessage": "Administered by:", - "id": "server_banner.administered_by" - }, - { - "defaultMessage": "Server stats:", - "id": "server_banner.server_stats" - }, - { - "defaultMessage": "active users", - "id": "server_banner.active_users" - }, - { - "defaultMessage": "Learn more", - "id": "server_banner.learn_more" - } - ], - "path": "app/javascript/mastodon/components/server_banner.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{count}K", - "id": "units.short.thousand" - }, - { - "defaultMessage": "{count}M", - "id": "units.short.million" - }, - { - "defaultMessage": "{count}B", - "id": "units.short.billion" - } - ], - "path": "app/javascript/mastodon/components/short_number.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Delete", - "id": "status.delete" - }, - { - "defaultMessage": "Delete & re-draft", - "id": "status.redraft" - }, - { - "defaultMessage": "Edit", - "id": "status.edit" - }, - { - "defaultMessage": "Privately mention @{name}", - "id": "status.direct" - }, - { - "defaultMessage": "Mention @{name}", - "id": "status.mention" - }, - { - "defaultMessage": "Mute @{name}", - "id": "account.mute" - }, - { - "defaultMessage": "Block @{name}", - "id": "account.block" - }, - { - "defaultMessage": "Reply", - "id": "status.reply" - }, - { - "defaultMessage": "Share", - "id": "status.share" - }, - { - "defaultMessage": "More", - "id": "status.more" - }, - { - "defaultMessage": "Reply to thread", - "id": "status.replyAll" - }, - { - "defaultMessage": "Boost", - "id": "status.reblog" - }, - { - "defaultMessage": "Boost with original visibility", - "id": "status.reblog_private" - }, - { - "defaultMessage": "Unboost", - "id": "status.cancel_reblog_private" - }, - { - "defaultMessage": "This post cannot be boosted", - "id": "status.cannot_reblog" - }, - { - "defaultMessage": "Favourite", - "id": "status.favourite" - }, - { - "defaultMessage": "Bookmark", - "id": "status.bookmark" - }, - { - "defaultMessage": "Remove bookmark", - "id": "status.remove_bookmark" - }, - { - "defaultMessage": "Expand this status", - "id": "status.open" - }, - { - "defaultMessage": "Report @{name}", - "id": "status.report" - }, - { - "defaultMessage": "Mute conversation", - "id": "status.mute_conversation" - }, - { - "defaultMessage": "Unmute conversation", - "id": "status.unmute_conversation" - }, - { - "defaultMessage": "Pin on profile", - "id": "status.pin" - }, - { - "defaultMessage": "Unpin from profile", - "id": "status.unpin" - }, - { - "defaultMessage": "Embed", - "id": "status.embed" - }, - { - "defaultMessage": "Open moderation interface for @{name}", - "id": "status.admin_account" - }, - { - "defaultMessage": "Open this post in the moderation interface", - "id": "status.admin_status" - }, - { - "defaultMessage": "Open moderation interface for {domain}", - "id": "status.admin_domain" - }, - { - "defaultMessage": "Copy link to post", - "id": "status.copy" - }, - { - "defaultMessage": "Hide post", - "id": "status.hide" - }, - { - "defaultMessage": "Block domain {domain}", - "id": "account.block_domain" - }, - { - "defaultMessage": "Unblock domain {domain}", - "id": "account.unblock_domain" - }, - { - "defaultMessage": "Unmute @{name}", - "id": "account.unmute" - }, - { - "defaultMessage": "Unblock @{name}", - "id": "account.unblock" - }, - { - "defaultMessage": "Filter this post", - "id": "status.filter" - }, - { - "defaultMessage": "Open original page", - "id": "account.open_original_page" - } - ], - "path": "app/javascript/mastodon/components/status_action_bar.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Translated from {lang} using {provider}", - "id": "status.translated_from_with" - }, - { - "defaultMessage": "Show original", - "id": "status.show_original" - }, - { - "defaultMessage": "Translate", - "id": "status.translate" - }, - { - "defaultMessage": "Read more", - "id": "status.read_more" - }, - { - "defaultMessage": "Show more", - "id": "status.show_more" - }, - { - "defaultMessage": "Show less", - "id": "status.show_less" - } - ], - "path": "app/javascript/mastodon/components/status_content.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Public", - "id": "privacy.public.short" - }, - { - "defaultMessage": "Unlisted", - "id": "privacy.unlisted.short" - }, - { - "defaultMessage": "Followers only", - "id": "privacy.private.short" - }, - { - "defaultMessage": "Mentioned people only", - "id": "privacy.direct.short" - }, - { - "defaultMessage": "Edited {date}", - "id": "status.edited" - }, - { - "defaultMessage": "Filtered", - "id": "status.filtered" - }, - { - "defaultMessage": "Show anyway", - "id": "status.show_filter_reason" - }, - { - "defaultMessage": "Pinned post", - "id": "status.pinned" - }, - { - "defaultMessage": "{name} boosted", - "id": "status.reblogged_by" - }, - { - "defaultMessage": "Private mention", - "id": "status.direct_indicator" - }, - { - "defaultMessage": "Replied to {name}", - "id": "status.replied_to" - } - ], - "path": "app/javascript/mastodon/components/status.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{resource} from other servers are not displayed.", - "id": "timeline_hint.remote_resource_not_displayed" - }, - { - "defaultMessage": "Browse more on the original profile", - "id": "account.browse_more_on_origin_server" - } - ], - "path": "app/javascript/mastodon/components/timeline_hint.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Unfollow", - "id": "confirmations.unfollow.confirm" - }, - { - "defaultMessage": "Are you sure you want to unfollow {name}?", - "id": "confirmations.unfollow.message" - } - ], - "path": "app/javascript/mastodon/containers/account_container.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Block entire domain", - "id": "confirmations.domain_block.confirm" - }, - { - "defaultMessage": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", - "id": "confirmations.domain_block.message" - } - ], - "path": "app/javascript/mastodon/containers/domain_container.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Delete", - "id": "confirmations.delete.confirm" - }, - { - "defaultMessage": "Are you sure you want to delete this status?", - "id": "confirmations.delete.message" - }, - { - "defaultMessage": "Delete & redraft", - "id": "confirmations.redraft.confirm" - }, - { - "defaultMessage": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", - "id": "confirmations.redraft.message" - }, - { - "defaultMessage": "Reply", - "id": "confirmations.reply.confirm" - }, - { - "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", - "id": "confirmations.reply.message" - }, - { - "defaultMessage": "Edit", - "id": "confirmations.edit.confirm" - }, - { - "defaultMessage": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?", - "id": "confirmations.edit.message" - }, - { - "defaultMessage": "Block entire domain", - "id": "confirmations.domain_block.confirm" - }, - { - "defaultMessage": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", - "id": "confirmations.domain_block.message" - } - ], - "path": "app/javascript/mastodon/containers/status_container.json" - }, - { - "descriptors": [ - { - "defaultMessage": "About", - "id": "column.about" - }, - { - "defaultMessage": "Server rules", - "id": "about.rules" - }, - { - "defaultMessage": "Moderated servers", - "id": "about.blocks" - }, - { - "defaultMessage": "Limited", - "id": "about.domain_blocks.silenced.title" - }, - { - "defaultMessage": "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following.", - "id": "about.domain_blocks.silenced.explanation" - }, - { - "defaultMessage": "Suspended", - "id": "about.domain_blocks.suspended.title" - }, - { - "defaultMessage": "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible.", - "id": "about.domain_blocks.suspended.explanation" - }, - { - "defaultMessage": "Decentralized social media powered by {mastodon}", - "id": "about.powered_by" - }, - { - "defaultMessage": "Administered by:", - "id": "server_banner.administered_by" - }, - { - "defaultMessage": "Contact:", - "id": "about.contact" - }, - { - "defaultMessage": "This information has not been made available on this server.", - "id": "about.not_available" - }, - { - "defaultMessage": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.", - "id": "about.domain_blocks.preamble" - }, - { - "defaultMessage": "Reason not available", - "id": "about.domain_blocks.no_reason_available" - }, - { - "defaultMessage": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.", - "id": "about.disclaimer" - } - ], - "path": "app/javascript/mastodon/features/about/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Account suspended", - "id": "empty_column.account_suspended" - }, - { - "defaultMessage": "Profile unavailable", - "id": "empty_column.account_unavailable" - } - ], - "path": "app/javascript/mastodon/features/account_gallery/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Posts", - "id": "account.posts" - }, - { - "defaultMessage": "Posts and replies", - "id": "account.posts_with_replies" - }, - { - "defaultMessage": "Media", - "id": "account.media" - } - ], - "path": "app/javascript/mastodon/features/account_timeline/components/header.json" - }, - { - "descriptors": [ - { - "defaultMessage": "This profile has been hidden by the moderators of {domain}.", - "id": "limited_account_hint.title" - }, - { - "defaultMessage": "Show profile anyway", - "id": "limited_account_hint.action" - } - ], - "path": "app/javascript/mastodon/features/account_timeline/components/limited_account_hint.json" - }, - { - "descriptors": [ - { - "defaultMessage": "In Memoriam.", - "id": "account.in_memoriam" - } - ], - "path": "app/javascript/mastodon/features/account_timeline/components/memorial_note.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{name} has indicated that their new account is now:", - "id": "account.moved_to" - }, - { - "defaultMessage": "Go to profile", - "id": "account.go_to_profile" - } - ], - "path": "app/javascript/mastodon/features/account_timeline/components/moved_note.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Withdraw request", - "id": "confirmations.cancel_follow_request.confirm" - }, - { - "defaultMessage": "Unfollow", - "id": "confirmations.unfollow.confirm" - }, - { - "defaultMessage": "Block entire domain", - "id": "confirmations.domain_block.confirm" - }, - { - "defaultMessage": "Are you sure you want to unfollow {name}?", - "id": "confirmations.unfollow.message" - }, - { - "defaultMessage": "Are you sure you want to withdraw your request to follow {name}?", - "id": "confirmations.cancel_follow_request.message" - }, - { - "defaultMessage": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", - "id": "confirmations.domain_block.message" - } - ], - "path": "app/javascript/mastodon/features/account_timeline/containers/header_container.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Older posts", - "id": "timeline_hint.resources.statuses" - }, - { - "defaultMessage": "Account suspended", - "id": "empty_column.account_suspended" - }, - { - "defaultMessage": "Profile unavailable", - "id": "empty_column.account_unavailable" - }, - { - "defaultMessage": "No posts found", - "id": "empty_column.account_timeline" - } - ], - "path": "app/javascript/mastodon/features/account_timeline/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Click to add a note", - "id": "account_note.placeholder" - }, - { - "defaultMessage": "Saved", - "id": "generic.saved" - }, - { - "defaultMessage": "Note", - "id": "account.account_note_header" - } - ], - "path": "app/javascript/mastodon/features/account/components/account_note.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Last post on {date}", - "id": "account.featured_tags.last_status_at" - }, - { - "defaultMessage": "No posts", - "id": "account.featured_tags.last_status_never" - }, - { - "defaultMessage": "{name}'s featured hashtags", - "id": "account.featured_tags.title" - } - ], - "path": "app/javascript/mastodon/features/account/components/featured_tags.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{name} has requested to follow you", - "id": "account.requested_follow" - }, - { - "defaultMessage": "Authorize", - "id": "follow_request.authorize" - }, - { - "defaultMessage": "Reject", - "id": "follow_request.reject" - } - ], - "path": "app/javascript/mastodon/features/account/components/follow_request_note.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Unfollow", - "id": "account.unfollow" - }, - { - "defaultMessage": "Follow", - "id": "account.follow" - }, - { - "defaultMessage": "Withdraw follow request", - "id": "account.cancel_follow_request" - }, - { - "defaultMessage": "Awaiting approval. Click to cancel follow request", - "id": "account.requested" - }, - { - "defaultMessage": "Unblock @{name}", - "id": "account.unblock" - }, - { - "defaultMessage": "Edit profile", - "id": "account.edit_profile" - }, - { - "defaultMessage": "Ownership of this link was checked on {date}", - "id": "account.link_verified_on" - }, - { - "defaultMessage": "This account privacy status is set to locked. The owner manually reviews who can follow them.", - "id": "account.locked_info" - }, - { - "defaultMessage": "Mention @{name}", - "id": "account.mention" - }, - { - "defaultMessage": "Privately mention @{name}", - "id": "account.direct" - }, - { - "defaultMessage": "Unmute @{name}", - "id": "account.unmute" - }, - { - "defaultMessage": "Block @{name}", - "id": "account.block" - }, - { - "defaultMessage": "Mute @{name}", - "id": "account.mute" - }, - { - "defaultMessage": "Report @{name}", - "id": "account.report" - }, - { - "defaultMessage": "Share @{name}'s profile", - "id": "account.share" - }, - { - "defaultMessage": "Media", - "id": "account.media" - }, - { - "defaultMessage": "Block domain {domain}", - "id": "account.block_domain" - }, - { - "defaultMessage": "Unblock domain {domain}", - "id": "account.unblock_domain" - }, - { - "defaultMessage": "Hide boosts from @{name}", - "id": "account.hide_reblogs" - }, - { - "defaultMessage": "Show boosts from @{name}", - "id": "account.show_reblogs" - }, - { - "defaultMessage": "Notify me when @{name} posts", - "id": "account.enable_notifications" - }, - { - "defaultMessage": "Stop notifying me when @{name} posts", - "id": "account.disable_notifications" - }, - { - "defaultMessage": "Pinned posts", - "id": "navigation_bar.pins" - }, - { - "defaultMessage": "Preferences", - "id": "navigation_bar.preferences" - }, - { - "defaultMessage": "Follow requests", - "id": "navigation_bar.follow_requests" - }, - { - "defaultMessage": "Favourites", - "id": "navigation_bar.favourites" - }, - { - "defaultMessage": "Lists", - "id": "navigation_bar.lists" - }, - { - "defaultMessage": "Followed hashtags", - "id": "navigation_bar.followed_tags" - }, - { - "defaultMessage": "Blocked users", - "id": "navigation_bar.blocks" - }, - { - "defaultMessage": "Blocked domains", - "id": "navigation_bar.domain_blocks" - }, - { - "defaultMessage": "Muted users", - "id": "navigation_bar.mutes" - }, - { - "defaultMessage": "Feature on profile", - "id": "account.endorse" - }, - { - "defaultMessage": "Don't feature on profile", - "id": "account.unendorse" - }, - { - "defaultMessage": "Add or Remove from lists", - "id": "account.add_or_remove_from_list" - }, - { - "defaultMessage": "Open moderation interface for @{name}", - "id": "status.admin_account" - }, - { - "defaultMessage": "Open moderation interface for {domain}", - "id": "status.admin_domain" - }, - { - "defaultMessage": "Change subscribed languages", - "id": "account.languages" - }, - { - "defaultMessage": "Open original page", - "id": "account.open_original_page" - }, - { - "defaultMessage": "Follows you", - "id": "account.follows_you" - }, - { - "defaultMessage": "Blocked", - "id": "account.blocked" - }, - { - "defaultMessage": "Muted", - "id": "account.muted" - }, - { - "defaultMessage": "Domain blocked", - "id": "account.domain_blocked" - }, - { - "defaultMessage": "Bot", - "id": "account.badges.bot" - }, - { - "defaultMessage": "Group", - "id": "account.badges.group" - }, - { - "defaultMessage": "Joined", - "id": "account.joined_short" - } - ], - "path": "app/javascript/mastodon/features/account/components/header.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Play", - "id": "video.play" - }, - { - "defaultMessage": "Pause", - "id": "video.pause" - }, - { - "defaultMessage": "Mute sound", - "id": "video.mute" - }, - { - "defaultMessage": "Unmute sound", - "id": "video.unmute" - }, - { - "defaultMessage": "Download file", - "id": "video.download" - }, - { - "defaultMessage": "Hide audio", - "id": "audio.hide" - }, - { - "defaultMessage": "Sensitive content", - "id": "status.sensitive_warning" - }, - { - "defaultMessage": "Media hidden", - "id": "status.media_hidden" - } - ], - "path": "app/javascript/mastodon/features/audio/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Blocked users", - "id": "column.blocks" - }, - { - "defaultMessage": "You haven't blocked any users yet.", - "id": "empty_column.blocks" - } - ], - "path": "app/javascript/mastodon/features/blocks/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Bookmarks", - "id": "column.bookmarks" - }, - { - "defaultMessage": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.", - "id": "empty_column.bookmarked_statuses" - } - ], - "path": "app/javascript/mastodon/features/bookmarked_statuses/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Creating an account on {domain} is currently not possible, but please keep in mind that you do not need an account specifically on {domain} to use Mastodon.", - "id": "closed_registrations_modal.description" - }, - { - "defaultMessage": "Signing up on Mastodon", - "id": "closed_registrations_modal.title" - }, - { - "defaultMessage": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!", - "id": "closed_registrations_modal.preamble" - }, - { - "defaultMessage": "On this server", - "id": "interaction_modal.on_this_server" - }, - { - "defaultMessage": "On a different server", - "id": "interaction_modal.on_another_server" - }, - { - "defaultMessage": "Since Mastodon is decentralized, you can create an account on another server and still interact with this one.", - "id": "closed_registrations.other_server_instructions" - }, - { - "defaultMessage": "Find another server", - "id": "closed_registrations_modal.find_another_server" - } - ], - "path": "app/javascript/mastodon/features/closed_registrations_modal/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Media only", - "id": "community.column_settings.media_only" - } - ], - "path": "app/javascript/mastodon/features/community_timeline/components/column_settings.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Local timeline", - "id": "column.community" - }, - { - "defaultMessage": "These are the most recent public posts from people whose accounts are hosted by {domain}.", - "id": "dismissable_banner.community_timeline" - }, - { - "defaultMessage": "The local timeline is empty. Write something publicly to get the ball rolling!", - "id": "empty_column.community" - } - ], - "path": "app/javascript/mastodon/features/community_timeline/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Edit profile", - "id": "account.edit_profile" - }, - { - "defaultMessage": "Pinned posts", - "id": "navigation_bar.pins" - }, - { - "defaultMessage": "Preferences", - "id": "navigation_bar.preferences" - }, - { - "defaultMessage": "Follow requests", - "id": "navigation_bar.follow_requests" - }, - { - "defaultMessage": "Favourites", - "id": "navigation_bar.favourites" - }, - { - "defaultMessage": "Lists", - "id": "navigation_bar.lists" - }, - { - "defaultMessage": "Followed hashtags", - "id": "navigation_bar.followed_tags" - }, - { - "defaultMessage": "Blocked users", - "id": "navigation_bar.blocks" - }, - { - "defaultMessage": "Blocked domains", - "id": "navigation_bar.domain_blocks" - }, - { - "defaultMessage": "Muted users", - "id": "navigation_bar.mutes" - }, - { - "defaultMessage": "Muted words", - "id": "navigation_bar.filters" - }, - { - "defaultMessage": "Logout", - "id": "navigation_bar.logout" - }, - { - "defaultMessage": "Bookmarks", - "id": "navigation_bar.bookmarks" - } - ], - "path": "app/javascript/mastodon/features/compose/components/action_bar.json" - }, - { - "descriptors": [ - { - "defaultMessage": "What is on your mind?", - "id": "compose_form.placeholder" - }, - { - "defaultMessage": "Write your warning here", - "id": "compose_form.spoiler_placeholder" - }, - { - "defaultMessage": "Publish", - "id": "compose_form.publish" - }, - { - "defaultMessage": "{publish}!", - "id": "compose_form.publish_loud" - }, - { - "defaultMessage": "Save changes", - "id": "compose_form.save_changes" - } - ], - "path": "app/javascript/mastodon/features/compose/components/compose_form.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Insert emoji", - "id": "emoji_button.label" - }, - { - "defaultMessage": "Search...", - "id": "emoji_button.search" - }, - { - "defaultMessage": "Custom", - "id": "emoji_button.custom" - }, - { - "defaultMessage": "Frequently used", - "id": "emoji_button.recent" - }, - { - "defaultMessage": "Search results", - "id": "emoji_button.search_results" - }, - { - "defaultMessage": "People", - "id": "emoji_button.people" - }, - { - "defaultMessage": "Nature", - "id": "emoji_button.nature" - }, - { - "defaultMessage": "Food & Drink", - "id": "emoji_button.food" - }, - { - "defaultMessage": "Activity", - "id": "emoji_button.activity" - }, - { - "defaultMessage": "Travel & Places", - "id": "emoji_button.travel" - }, - { - "defaultMessage": "Objects", - "id": "emoji_button.objects" - }, - { - "defaultMessage": "Symbols", - "id": "emoji_button.symbols" - }, - { - "defaultMessage": "Flags", - "id": "emoji_button.flags" - }, - { - "defaultMessage": "No matching emojis found", - "id": "emoji_button.not_found" - } - ], - "path": "app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Change language", - "id": "compose.language.change" - }, - { - "defaultMessage": "Search languages...", - "id": "compose.language.search" - }, - { - "defaultMessage": "Clear", - "id": "emoji_button.clear" - } - ], - "path": "app/javascript/mastodon/features/compose/components/language_dropdown.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Edit profile", - "id": "navigation_bar.edit_profile" - } - ], - "path": "app/javascript/mastodon/features/compose/components/navigation_bar.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Add a poll", - "id": "poll_button.add_poll" - }, - { - "defaultMessage": "Remove poll", - "id": "poll_button.remove_poll" - } - ], - "path": "app/javascript/mastodon/features/compose/components/poll_button.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Choice {number}", - "id": "compose_form.poll.option_placeholder" - }, - { - "defaultMessage": "Add a choice", - "id": "compose_form.poll.add_option" - }, - { - "defaultMessage": "Remove this choice", - "id": "compose_form.poll.remove_option" - }, - { - "defaultMessage": "Poll duration", - "id": "compose_form.poll.duration" - }, - { - "defaultMessage": "Change poll to allow multiple choices", - "id": "compose_form.poll.switch_to_multiple" - }, - { - "defaultMessage": "Change poll to allow for a single choice", - "id": "compose_form.poll.switch_to_single" - }, - { - "defaultMessage": "{number, plural, one {# minute} other {# minutes}}", - "id": "intervals.full.minutes" - }, - { - "defaultMessage": "{number, plural, one {# hour} other {# hours}}", - "id": "intervals.full.hours" - }, - { - "defaultMessage": "{number, plural, one {# day} other {# days}}", - "id": "intervals.full.days" - } - ], - "path": "app/javascript/mastodon/features/compose/components/poll_form.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Public", - "id": "privacy.public.short" - }, - { - "defaultMessage": "Visible for all", - "id": "privacy.public.long" - }, - { - "defaultMessage": "Unlisted", - "id": "privacy.unlisted.short" - }, - { - "defaultMessage": "Visible for all, but opted-out of discovery features", - "id": "privacy.unlisted.long" - }, - { - "defaultMessage": "Followers only", - "id": "privacy.private.short" - }, - { - "defaultMessage": "Visible for followers only", - "id": "privacy.private.long" - }, - { - "defaultMessage": "Mentioned people only", - "id": "privacy.direct.short" - }, - { - "defaultMessage": "Visible for mentioned users only", - "id": "privacy.direct.long" - }, - { - "defaultMessage": "Adjust status privacy", - "id": "privacy.change" - } - ], - "path": "app/javascript/mastodon/features/compose/components/privacy_dropdown.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Cancel", - "id": "reply_indicator.cancel" - } - ], - "path": "app/javascript/mastodon/features/compose/components/reply_indicator.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Dismiss suggestion", - "id": "suggestions.dismiss" - }, - { - "defaultMessage": "You might be interested in…", - "id": "suggestions.header" - }, - { - "defaultMessage": "Profiles", - "id": "search_results.accounts" - }, - { - "defaultMessage": "Posts", - "id": "search_results.statuses" - }, - { - "defaultMessage": "Searching posts by their content is not enabled on this Mastodon server.", - "id": "search_results.statuses_fts_disabled" - }, - { - "defaultMessage": "Hashtags", - "id": "search_results.hashtags" - }, - { - "defaultMessage": "{count, plural, one {# result} other {# results}}", - "id": "search_results.total" - } - ], - "path": "app/javascript/mastodon/features/compose/components/search_results.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Search", - "id": "search.placeholder" - }, - { - "defaultMessage": "Search or paste URL", - "id": "search.search_or_paste" - }, - { - "defaultMessage": "Open URL in Mastodon", - "id": "search.quick_action.open_url" - }, - { - "defaultMessage": "Go to hashtag {x}", - "id": "search.quick_action.go_to_hashtag" - }, - { - "defaultMessage": "Go to profile {x}", - "id": "search.quick_action.go_to_account" - }, - { - "defaultMessage": "Posts matching {x}", - "id": "search.quick_action.status_search" - }, - { - "defaultMessage": "Profiles matching {x}", - "id": "search.quick_action.account_search" - }, - { - "defaultMessage": "Recent searches", - "id": "search_popout.recent" - }, - { - "defaultMessage": "No recent searches", - "id": "search.no_recent_searches" - }, - { - "defaultMessage": "Quick actions", - "id": "search_popout.quick_actions" - } - ], - "path": "app/javascript/mastodon/features/compose/components/search.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Add images, a video or an audio file", - "id": "upload_button.label" - } - ], - "path": "app/javascript/mastodon/features/compose/components/upload_button.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Processing…", - "id": "upload_progress.processing" - }, - { - "defaultMessage": "Uploading…", - "id": "upload_progress.label" - } - ], - "path": "app/javascript/mastodon/features/compose/components/upload_progress.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Delete", - "id": "upload_form.undo" - }, - { - "defaultMessage": "Edit", - "id": "upload_form.edit" - }, - { - "defaultMessage": "No description added", - "id": "upload_form.description_missing" - } - ], - "path": "app/javascript/mastodon/features/compose/components/upload.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Are you sure you want to log out?", - "id": "confirmations.logout.message" - }, - { - "defaultMessage": "Log out", - "id": "confirmations.logout.confirm" - } - ], - "path": "app/javascript/mastodon/features/compose/containers/navigation_container.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}", - "id": "compose_form.sensitive.marked" - }, - { - "defaultMessage": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}", - "id": "compose_form.sensitive.unmarked" - }, - { - "defaultMessage": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}", - "id": "compose_form.sensitive.hide" - } - ], - "path": "app/javascript/mastodon/features/compose/containers/sensitive_button_container.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Text is hidden behind warning", - "id": "compose_form.spoiler.marked" - }, - { - "defaultMessage": "Text is not hidden", - "id": "compose_form.spoiler.unmarked" - } - ], - "path": "app/javascript/mastodon/features/compose/containers/spoiler_button_container.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", - "id": "compose_form.lock_disclaimer" - }, - { - "defaultMessage": "locked", - "id": "compose_form.lock_disclaimer.lock" - }, - { - "defaultMessage": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.", - "id": "compose_form.hashtag_warning" - }, - { - "defaultMessage": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.", - "id": "compose_form.encryption_warning" - }, - { - "defaultMessage": "Learn more", - "id": "compose_form.direct_message_warning_learn_more" - } - ], - "path": "app/javascript/mastodon/features/compose/containers/warning_container.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Getting started", - "id": "getting_started.heading" - }, - { - "defaultMessage": "Home", - "id": "tabs_bar.home" - }, - { - "defaultMessage": "Notifications", - "id": "tabs_bar.notifications" - }, - { - "defaultMessage": "Federated timeline", - "id": "navigation_bar.public_timeline" - }, - { - "defaultMessage": "Local timeline", - "id": "navigation_bar.community_timeline" - }, - { - "defaultMessage": "Preferences", - "id": "navigation_bar.preferences" - }, - { - "defaultMessage": "Logout", - "id": "navigation_bar.logout" - }, - { - "defaultMessage": "Compose new post", - "id": "navigation_bar.compose" - }, - { - "defaultMessage": "Are you sure you want to log out?", - "id": "confirmations.logout.message" - }, - { - "defaultMessage": "Log out", - "id": "confirmations.logout.confirm" - } - ], - "path": "app/javascript/mastodon/features/compose/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "More", - "id": "status.more" - }, - { - "defaultMessage": "View conversation", - "id": "conversation.open" - }, - { - "defaultMessage": "Reply", - "id": "status.reply" - }, - { - "defaultMessage": "Mark as read", - "id": "conversation.mark_as_read" - }, - { - "defaultMessage": "Delete conversation", - "id": "conversation.delete" - }, - { - "defaultMessage": "Mute conversation", - "id": "status.mute_conversation" - }, - { - "defaultMessage": "Unmute conversation", - "id": "status.unmute_conversation" - }, - { - "defaultMessage": "With {names}", - "id": "conversation.with" - } - ], - "path": "app/javascript/mastodon/features/direct_timeline/components/conversation.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Reply", - "id": "confirmations.reply.confirm" - }, - { - "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", - "id": "confirmations.reply.message" - } - ], - "path": "app/javascript/mastodon/features/direct_timeline/containers/conversation_container.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Private mentions", - "id": "column.direct" - }, - { - "defaultMessage": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.", - "id": "compose_form.encryption_warning" - }, - { - "defaultMessage": "Learn more", - "id": "compose_form.direct_message_warning_learn_more" - }, - { - "defaultMessage": "You don't have any private mentions yet. When you send or receive one, it will show up here.", - "id": "empty_column.direct" - } - ], - "path": "app/javascript/mastodon/features/direct_timeline/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Unfollow", - "id": "account.unfollow" - }, - { - "defaultMessage": "Follow", - "id": "account.follow" - }, - { - "defaultMessage": "Withdraw follow request", - "id": "account.cancel_follow_request" - }, - { - "defaultMessage": "Withdraw request", - "id": "confirmations.cancel_follow_request.confirm" - }, - { - "defaultMessage": "Awaiting approval. Click to cancel follow request", - "id": "account.requested" - }, - { - "defaultMessage": "Unblock", - "id": "account.unblock_short" - }, - { - "defaultMessage": "Unmute", - "id": "account.unmute_short" - }, - { - "defaultMessage": "Unfollow", - "id": "confirmations.unfollow.confirm" - }, - { - "defaultMessage": "Edit profile", - "id": "account.edit_profile" - }, - { - "defaultMessage": "Are you sure you want to unfollow {name}?", - "id": "confirmations.unfollow.message" - }, - { - "defaultMessage": "Are you sure you want to withdraw your request to follow {name}?", - "id": "confirmations.cancel_follow_request.message" - }, - { - "defaultMessage": "Posts", - "id": "account.posts" - }, - { - "defaultMessage": "Followers", - "id": "account.followers" - }, - { - "defaultMessage": "Following", - "id": "account.following" - } - ], - "path": "app/javascript/mastodon/features/directory/components/account_card.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Browse profiles", - "id": "column.directory" - }, - { - "defaultMessage": "Recently active", - "id": "directory.recently_active" - }, - { - "defaultMessage": "New arrivals", - "id": "directory.new_arrivals" - }, - { - "defaultMessage": "From {domain} only", - "id": "directory.local" - }, - { - "defaultMessage": "From known fediverse", - "id": "directory.federated" - } - ], - "path": "app/javascript/mastodon/features/directory/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Blocked domains", - "id": "column.domain_blocks" - }, - { - "defaultMessage": "Unblock domain {domain}", - "id": "account.unblock_domain" - }, - { - "defaultMessage": "There are no blocked domains yet.", - "id": "empty_column.domain_blocks" - } - ], - "path": "app/javascript/mastodon/features/domain_blocks/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Explore", - "id": "explore.title" - }, - { - "defaultMessage": "Search results", - "id": "explore.search_results" - }, - { - "defaultMessage": "Posts", - "id": "explore.trending_statuses" - }, - { - "defaultMessage": "Hashtags", - "id": "explore.trending_tags" - }, - { - "defaultMessage": "People", - "id": "explore.suggested_follows" - }, - { - "defaultMessage": "News", - "id": "explore.trending_links" - } - ], - "path": "app/javascript/mastodon/features/explore/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", - "id": "dismissable_banner.explore_links" - }, - { - "defaultMessage": "Nothing is trending right now. Check back later!", - "id": "empty_column.explore_statuses" - } - ], - "path": "app/javascript/mastodon/features/explore/links.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Search for {q}", - "id": "search_results.title" - }, - { - "defaultMessage": "Could not find anything for these search terms", - "id": "search_results.nothing_found" - }, - { - "defaultMessage": "All", - "id": "search_results.all" - }, - { - "defaultMessage": "Profiles", - "id": "search_results.accounts" - }, - { - "defaultMessage": "Hashtags", - "id": "search_results.hashtags" - }, - { - "defaultMessage": "Posts", - "id": "search_results.statuses" - } - ], - "path": "app/javascript/mastodon/features/explore/results.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Nothing is trending right now. Check back later!", - "id": "empty_column.explore_statuses" - }, - { - "defaultMessage": "These posts from this and other servers in the decentralized network are gaining traction on this server right now.", - "id": "dismissable_banner.explore_statuses" - } - ], - "path": "app/javascript/mastodon/features/explore/statuses.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Nothing is trending right now. Check back later!", - "id": "empty_column.explore_statuses" - } - ], - "path": "app/javascript/mastodon/features/explore/suggestions.json" - }, - { - "descriptors": [ - { - "defaultMessage": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", - "id": "dismissable_banner.explore_tags" - }, - { - "defaultMessage": "Nothing is trending right now. Check back later!", - "id": "empty_column.explore_statuses" - } - ], - "path": "app/javascript/mastodon/features/explore/tags.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Favourites", - "id": "column.favourites" - }, - { - "defaultMessage": "You don't have any favourite posts yet. When you favourite one, it will show up here.", - "id": "empty_column.favourited_statuses" - } - ], - "path": "app/javascript/mastodon/features/favourited_statuses/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Refresh", - "id": "refresh" - }, - { - "defaultMessage": "No one has favourited this post yet. When someone does, they will show up here.", - "id": "empty_column.favourites" - } - ], - "path": "app/javascript/mastodon/features/favourites/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Expired filter!", - "id": "filter_modal.added.expired_title" - }, - { - "defaultMessage": "This filter category has expired, you will need to change the expiration date for it to apply.", - "id": "filter_modal.added.expired_explanation" - }, - { - "defaultMessage": "Context mismatch!", - "id": "filter_modal.added.context_mismatch_title" - }, - { - "defaultMessage": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", - "id": "filter_modal.added.context_mismatch_explanation" - }, - { - "defaultMessage": "settings page", - "id": "filter_modal.added.settings_link" - }, - { - "defaultMessage": "Filter added!", - "id": "filter_modal.added.title" - }, - { - "defaultMessage": "This post has been added to the following filter category: {title}.", - "id": "filter_modal.added.short_explanation" - }, - { - "defaultMessage": "Filter settings", - "id": "filter_modal.added.review_and_configure_title" - }, - { - "defaultMessage": "To review and further configure this filter category, go to the {settings_link}.", - "id": "filter_modal.added.review_and_configure" - }, - { - "defaultMessage": "Done", - "id": "report.close" - } - ], - "path": "app/javascript/mastodon/features/filters/added_to_filter.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Search or create", - "id": "filter_modal.select_filter.search" - }, - { - "defaultMessage": "Clear", - "id": "emoji_button.clear" - }, - { - "defaultMessage": "expired", - "id": "filter_modal.select_filter.expired" - }, - { - "defaultMessage": "does not apply to this context", - "id": "filter_modal.select_filter.context_mismatch" - }, - { - "defaultMessage": "New category: {name}", - "id": "filter_modal.select_filter.prompt_new" - }, - { - "defaultMessage": "Filter this post", - "id": "filter_modal.select_filter.title" - }, - { - "defaultMessage": "Use an existing category or create a new one", - "id": "filter_modal.select_filter.subtitle" - } - ], - "path": "app/javascript/mastodon/features/filters/select_filter.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Authorize", - "id": "follow_request.authorize" - }, - { - "defaultMessage": "Reject", - "id": "follow_request.reject" - } - ], - "path": "app/javascript/mastodon/features/follow_requests/components/account_authorize.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Follow requests", - "id": "column.follow_requests" - }, - { - "defaultMessage": "You don't have any follow requests yet. When you receive one, it will show up here.", - "id": "empty_column.follow_requests" - }, - { - "defaultMessage": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.", - "id": "follow_requests.unlocked_explanation" - } - ], - "path": "app/javascript/mastodon/features/follow_requests/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Followed hashtags", - "id": "followed_tags" - }, - { - "defaultMessage": "You have not followed any hashtags yet. When you do, they will show up here.", - "id": "empty_column.followed_tags" - } - ], - "path": "app/javascript/mastodon/features/followed_tags/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Followers", - "id": "timeline_hint.resources.followers" - }, - { - "defaultMessage": "Account suspended", - "id": "empty_column.account_suspended" - }, - { - "defaultMessage": "Profile unavailable", - "id": "empty_column.account_unavailable" - }, - { - "defaultMessage": "No one follows this user yet.", - "id": "account.followers.empty" - } - ], - "path": "app/javascript/mastodon/features/followers/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Follows", - "id": "timeline_hint.resources.follows" - }, - { - "defaultMessage": "Account suspended", - "id": "empty_column.account_suspended" - }, - { - "defaultMessage": "Profile unavailable", - "id": "empty_column.account_unavailable" - }, - { - "defaultMessage": "This user doesn't follow anyone yet.", - "id": "account.follows.empty" - } - ], - "path": "app/javascript/mastodon/features/following/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Previous", - "id": "lightbox.previous" - }, - { - "defaultMessage": "Next", - "id": "lightbox.next" - }, - { - "defaultMessage": "Announcement", - "id": "announcement.announcement" - } - ], - "path": "app/javascript/mastodon/features/getting_started/components/announcements.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Trending now", - "id": "trends.trending_now" - } - ], - "path": "app/javascript/mastodon/features/getting_started/components/trends.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Home", - "id": "tabs_bar.home" - }, - { - "defaultMessage": "Notifications", - "id": "tabs_bar.notifications" - }, - { - "defaultMessage": "Federated timeline", - "id": "navigation_bar.public_timeline" - }, - { - "defaultMessage": "Settings", - "id": "column_subheading.settings" - }, - { - "defaultMessage": "Local timeline", - "id": "navigation_bar.community_timeline" - }, - { - "defaultMessage": "Explore", - "id": "navigation_bar.explore" - }, - { - "defaultMessage": "Private mentions", - "id": "navigation_bar.direct" - }, - { - "defaultMessage": "Bookmarks", - "id": "navigation_bar.bookmarks" - }, - { - "defaultMessage": "Preferences", - "id": "navigation_bar.preferences" - }, - { - "defaultMessage": "Follow requests", - "id": "navigation_bar.follow_requests" - }, - { - "defaultMessage": "Favourites", - "id": "navigation_bar.favourites" - }, - { - "defaultMessage": "Blocked users", - "id": "navigation_bar.blocks" - }, - { - "defaultMessage": "Blocked domains", - "id": "navigation_bar.domain_blocks" - }, - { - "defaultMessage": "Muted users", - "id": "navigation_bar.mutes" - }, - { - "defaultMessage": "Pinned posts", - "id": "navigation_bar.pins" - }, - { - "defaultMessage": "Lists", - "id": "navigation_bar.lists" - }, - { - "defaultMessage": "Discover", - "id": "navigation_bar.discover" - }, - { - "defaultMessage": "Personal", - "id": "navigation_bar.personal" - }, - { - "defaultMessage": "Security", - "id": "navigation_bar.security" - }, - { - "defaultMessage": "Getting started", - "id": "getting_started.heading" - } - ], - "path": "app/javascript/mastodon/features/getting_started/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Enter hashtags…", - "id": "hashtag.column_settings.select.placeholder" - }, - { - "defaultMessage": "No suggestions found", - "id": "hashtag.column_settings.select.no_options_message" - }, - { - "defaultMessage": "Any of these", - "id": "hashtag.column_settings.tag_mode.any" - }, - { - "defaultMessage": "All of these", - "id": "hashtag.column_settings.tag_mode.all" - }, - { - "defaultMessage": "None of these", - "id": "hashtag.column_settings.tag_mode.none" - }, - { - "defaultMessage": "Include additional tags in this column", - "id": "hashtag.column_settings.tag_toggle" - }, - { - "defaultMessage": "Local only", - "id": "community.column_settings.local_only" - } - ], - "path": "app/javascript/mastodon/features/hashtag_timeline/components/column_settings.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Follow hashtag", - "id": "hashtag.follow" - }, - { - "defaultMessage": "Unfollow hashtag", - "id": "hashtag.unfollow" - }, - { - "defaultMessage": "or {additional}", - "id": "hashtag.column_header.tag_mode.any" - }, - { - "defaultMessage": "and {additional}", - "id": "hashtag.column_header.tag_mode.all" - }, - { - "defaultMessage": "without {additional}", - "id": "hashtag.column_header.tag_mode.none" - }, - { - "defaultMessage": "There is nothing in this hashtag yet.", - "id": "empty_column.hashtag" - } - ], - "path": "app/javascript/mastodon/features/hashtag_timeline/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Basic", - "id": "home.column_settings.basic" - }, - { - "defaultMessage": "Show boosts", - "id": "home.column_settings.show_reblogs" - }, - { - "defaultMessage": "Show replies", - "id": "home.column_settings.show_replies" - } - ], - "path": "app/javascript/mastodon/features/home_timeline/components/column_settings.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Home", - "id": "column.home" - }, - { - "defaultMessage": "Show announcements", - "id": "home.show_announcements" - }, - { - "defaultMessage": "Hide announcements", - "id": "home.hide_announcements" - }, - { - "defaultMessage": "Your home timeline is empty! Follow more people to fill it up. {suggestions}", - "id": "empty_column.home" - }, - { - "defaultMessage": "See some suggestions", - "id": "empty_column.home.suggestions" - } - ], - "path": "app/javascript/mastodon/features/home_timeline/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Copied", - "id": "copypaste.copied" - }, - { - "defaultMessage": "Copy", - "id": "copypaste.copy" - }, - { - "defaultMessage": "Reply to {name}'s post", - "id": "interaction_modal.title.reply" - }, - { - "defaultMessage": "With an account on Mastodon, you can respond to this post.", - "id": "interaction_modal.description.reply" - }, - { - "defaultMessage": "Boost {name}'s post", - "id": "interaction_modal.title.reblog" - }, - { - "defaultMessage": "With an account on Mastodon, you can boost this post to share it with your own followers.", - "id": "interaction_modal.description.reblog" - }, - { - "defaultMessage": "Favourite {name}'s post", - "id": "interaction_modal.title.favourite" - }, - { - "defaultMessage": "With an account on Mastodon, you can favourite this post to let the author know you appreciate it and save it for later.", - "id": "interaction_modal.description.favourite" - }, - { - "defaultMessage": "Follow {name}", - "id": "interaction_modal.title.follow" - }, - { - "defaultMessage": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.", - "id": "interaction_modal.description.follow" - }, - { - "defaultMessage": "Create account", - "id": "sign_in_banner.create_account" - }, - { - "defaultMessage": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.", - "id": "interaction_modal.preamble" - }, - { - "defaultMessage": "On this server", - "id": "interaction_modal.on_this_server" - }, - { - "defaultMessage": "Login", - "id": "sign_in_banner.sign_in" - }, - { - "defaultMessage": "On a different server", - "id": "interaction_modal.on_another_server" - }, - { - "defaultMessage": "Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.", - "id": "interaction_modal.other_server_instructions" - } - ], - "path": "app/javascript/mastodon/features/interaction_modal/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Keyboard Shortcuts", - "id": "keyboard_shortcuts.heading" - }, - { - "defaultMessage": "Hotkey", - "id": "keyboard_shortcuts.hotkey" - }, - { - "defaultMessage": "Description", - "id": "keyboard_shortcuts.description" - }, - { - "defaultMessage": "to reply", - "id": "keyboard_shortcuts.reply" - }, - { - "defaultMessage": "to mention author", - "id": "keyboard_shortcuts.mention" - }, - { - "defaultMessage": "to open author's profile", - "id": "keyboard_shortcuts.profile" - }, - { - "defaultMessage": "to favourite", - "id": "keyboard_shortcuts.favourite" - }, - { - "defaultMessage": "to boost", - "id": "keyboard_shortcuts.boost" - }, - { - "defaultMessage": "to open status", - "id": "keyboard_shortcuts.enter" - }, - { - "defaultMessage": "to open media", - "id": "keyboard_shortcuts.open_media" - }, - { - "defaultMessage": "to show/hide text behind CW", - "id": "keyboard_shortcuts.toggle_hidden" - }, - { - "defaultMessage": "to show/hide media", - "id": "keyboard_shortcuts.toggle_sensitivity" - }, - { - "defaultMessage": "to move up in the list", - "id": "keyboard_shortcuts.up" - }, - { - "defaultMessage": "to move down in the list", - "id": "keyboard_shortcuts.down" - }, - { - "defaultMessage": "to focus a status in one of the columns", - "id": "keyboard_shortcuts.column" - }, - { - "defaultMessage": "to focus the compose textarea", - "id": "keyboard_shortcuts.compose" - }, - { - "defaultMessage": "to start a brand new post", - "id": "keyboard_shortcuts.toot" - }, - { - "defaultMessage": "to show/hide CW field", - "id": "keyboard_shortcuts.spoilers" - }, - { - "defaultMessage": "to navigate back", - "id": "keyboard_shortcuts.back" - }, - { - "defaultMessage": "to focus search", - "id": "keyboard_shortcuts.search" - }, - { - "defaultMessage": "to un-focus compose textarea/search", - "id": "keyboard_shortcuts.unfocus" - }, - { - "defaultMessage": "to open home timeline", - "id": "keyboard_shortcuts.home" - }, - { - "defaultMessage": "to open notifications column", - "id": "keyboard_shortcuts.notifications" - }, - { - "defaultMessage": "to open local timeline", - "id": "keyboard_shortcuts.local" - }, - { - "defaultMessage": "to open federated timeline", - "id": "keyboard_shortcuts.federated" - }, - { - "defaultMessage": "to open direct messages column", - "id": "keyboard_shortcuts.direct" - }, - { - "defaultMessage": "to open \"get started\" column", - "id": "keyboard_shortcuts.start" - }, - { - "defaultMessage": "to open favourites list", - "id": "keyboard_shortcuts.favourites" - }, - { - "defaultMessage": "to open pinned posts list", - "id": "keyboard_shortcuts.pinned" - }, - { - "defaultMessage": "to open your profile", - "id": "keyboard_shortcuts.my_profile" - }, - { - "defaultMessage": "to open blocked users list", - "id": "keyboard_shortcuts.blocked" - }, - { - "defaultMessage": "to open muted users list", - "id": "keyboard_shortcuts.muted" - }, - { - "defaultMessage": "to open follow requests list", - "id": "keyboard_shortcuts.requests" - }, - { - "defaultMessage": "to display this legend", - "id": "keyboard_shortcuts.legend" - } - ], - "path": "app/javascript/mastodon/features/keyboard_shortcuts/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Remove from list", - "id": "lists.account.remove" - }, - { - "defaultMessage": "Add to list", - "id": "lists.account.add" - } - ], - "path": "app/javascript/mastodon/features/list_adder/components/list.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Remove from list", - "id": "lists.account.remove" - }, - { - "defaultMessage": "Add to list", - "id": "lists.account.add" - } - ], - "path": "app/javascript/mastodon/features/list_editor/components/account.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Change title", - "id": "lists.edit.submit" - } - ], - "path": "app/javascript/mastodon/features/list_editor/components/edit_list_form.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Search among people you follow", - "id": "lists.search" - } - ], - "path": "app/javascript/mastodon/features/list_editor/components/search.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Are you sure you want to permanently delete this list?", - "id": "confirmations.delete_list.message" - }, - { - "defaultMessage": "Delete", - "id": "confirmations.delete_list.confirm" - }, - { - "defaultMessage": "Any followed user", - "id": "lists.replies_policy.followed" - }, - { - "defaultMessage": "No one", - "id": "lists.replies_policy.none" - }, - { - "defaultMessage": "Members of the list", - "id": "lists.replies_policy.list" - }, - { - "defaultMessage": "Edit list", - "id": "lists.edit" - }, - { - "defaultMessage": "Delete list", - "id": "lists.delete" - }, - { - "defaultMessage": "Show replies to:", - "id": "lists.replies_policy.title" - }, - { - "defaultMessage": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", - "id": "empty_column.list" - } - ], - "path": "app/javascript/mastodon/features/list_timeline/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "New list title", - "id": "lists.new.title_placeholder" - }, - { - "defaultMessage": "Add list", - "id": "lists.new.create" - } - ], - "path": "app/javascript/mastodon/features/lists/components/new_list_form.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Lists", - "id": "column.lists" - }, - { - "defaultMessage": "Your lists", - "id": "lists.subheading" - }, - { - "defaultMessage": "You don't have any lists yet. When you create one, it will show up here.", - "id": "empty_column.lists" - } - ], - "path": "app/javascript/mastodon/features/lists/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Muted users", - "id": "column.mutes" - }, - { - "defaultMessage": "You haven't muted any users yet.", - "id": "empty_column.mutes" - } - ], - "path": "app/javascript/mastodon/features/mutes/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Clear notifications", - "id": "notifications.clear" - } - ], - "path": "app/javascript/mastodon/features/notifications/components/clear_column_button.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Highlight unread notifications", - "id": "notifications.column_settings.unread_notifications.highlight" - }, - { - "defaultMessage": "Show filter bar", - "id": "notifications.column_settings.filter_bar.show_bar" - }, - { - "defaultMessage": "Display all categories", - "id": "notifications.column_settings.filter_bar.advanced" - }, - { - "defaultMessage": "Desktop notifications", - "id": "notifications.column_settings.alert" - }, - { - "defaultMessage": "Show in column", - "id": "notifications.column_settings.show" - }, - { - "defaultMessage": "Play sound", - "id": "notifications.column_settings.sound" - }, - { - "defaultMessage": "Push notifications", - "id": "notifications.column_settings.push" - }, - { - "defaultMessage": "Desktop notifications are unavailable due to previously denied browser permissions request", - "id": "notifications.permission_denied" - }, - { - "defaultMessage": "Desktop notifications are unavailable because the required permission has not been granted.", - "id": "notifications.permission_required" - }, - { - "defaultMessage": "Unread notifications", - "id": "notifications.column_settings.unread_notifications.category" - }, - { - "defaultMessage": "Quick filter bar", - "id": "notifications.column_settings.filter_bar.category" - }, - { - "defaultMessage": "New followers:", - "id": "notifications.column_settings.follow" - }, - { - "defaultMessage": "New follow requests:", - "id": "notifications.column_settings.follow_request" - }, - { - "defaultMessage": "Favourites:", - "id": "notifications.column_settings.favourite" - }, - { - "defaultMessage": "Mentions:", - "id": "notifications.column_settings.mention" - }, - { - "defaultMessage": "Boosts:", - "id": "notifications.column_settings.reblog" - }, - { - "defaultMessage": "Poll results:", - "id": "notifications.column_settings.poll" - }, - { - "defaultMessage": "New posts:", - "id": "notifications.column_settings.status" - }, - { - "defaultMessage": "Edits:", - "id": "notifications.column_settings.update" - }, - { - "defaultMessage": "New sign-ups:", - "id": "notifications.column_settings.admin.sign_up" - }, - { - "defaultMessage": "New reports:", - "id": "notifications.column_settings.admin.report" - } - ], - "path": "app/javascript/mastodon/features/notifications/components/column_settings.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Mentions", - "id": "notifications.filter.mentions" - }, - { - "defaultMessage": "Favourites", - "id": "notifications.filter.favourites" - }, - { - "defaultMessage": "Boosts", - "id": "notifications.filter.boosts" - }, - { - "defaultMessage": "Poll results", - "id": "notifications.filter.polls" - }, - { - "defaultMessage": "Follows", - "id": "notifications.filter.follows" - }, - { - "defaultMessage": "Updates from people you follow", - "id": "notifications.filter.statuses" - }, - { - "defaultMessage": "All", - "id": "notifications.filter.all" - } - ], - "path": "app/javascript/mastodon/features/notifications/components/filter_bar.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Authorize", - "id": "follow_request.authorize" - }, - { - "defaultMessage": "Reject", - "id": "follow_request.reject" - } - ], - "path": "app/javascript/mastodon/features/notifications/components/follow_request.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Grant permission.", - "id": "notifications.grant_permission" - } - ], - "path": "app/javascript/mastodon/features/notifications/components/grant_permission_button.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{name} favourited your status", - "id": "notification.favourite" - }, - { - "defaultMessage": "{name} followed you", - "id": "notification.follow" - }, - { - "defaultMessage": "Your poll has ended", - "id": "notification.own_poll" - }, - { - "defaultMessage": "A poll you have voted in has ended", - "id": "notification.poll" - }, - { - "defaultMessage": "{name} boosted your status", - "id": "notification.reblog" - }, - { - "defaultMessage": "{name} just posted", - "id": "notification.status" - }, - { - "defaultMessage": "{name} edited a post", - "id": "notification.update" - }, - { - "defaultMessage": "{name} signed up", - "id": "notification.admin.sign_up" - }, - { - "defaultMessage": "{name} reported {target}", - "id": "notification.admin.report" - }, - { - "defaultMessage": "{name} has requested to follow you", - "id": "notification.follow_request" - } - ], - "path": "app/javascript/mastodon/features/notifications/components/notification.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Never miss a thing", - "id": "notifications_permission_banner.title" - }, - { - "defaultMessage": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.", - "id": "notifications_permission_banner.how_to_control" - }, - { - "defaultMessage": "Enable desktop notifications", - "id": "notifications_permission_banner.enable" - } - ], - "path": "app/javascript/mastodon/features/notifications/components/notifications_permission_banner.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Open report", - "id": "report_notification.open" - }, - { - "defaultMessage": "Other", - "id": "report_notification.categories.other" - }, - { - "defaultMessage": "Spam", - "id": "report_notification.categories.spam" - }, - { - "defaultMessage": "Rule violation", - "id": "report_notification.categories.violation" - }, - { - "defaultMessage": "{count, plural, one {# post} other {# posts}} attached", - "id": "report_notification.attached_statuses" - } - ], - "path": "app/javascript/mastodon/features/notifications/components/report.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Are you sure you want to permanently clear all your notifications?", - "id": "notifications.clear_confirmation" - }, - { - "defaultMessage": "Clear notifications", - "id": "notifications.clear" - }, - { - "defaultMessage": "Desktop notifications can't be enabled, as browser permission has been denied before", - "id": "notifications.permission_denied_alert" - } - ], - "path": "app/javascript/mastodon/features/notifications/containers/column_settings_container.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Notifications", - "id": "column.notifications" - }, - { - "defaultMessage": "Mark every notification as read", - "id": "notifications.mark_as_read" - }, - { - "defaultMessage": "You don't have any notifications yet. When other people interact with you, you will see it here.", - "id": "empty_column.notifications" - } - ], - "path": "app/javascript/mastodon/features/notifications/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.", - "id": "onboarding.follows.empty" - }, - { - "defaultMessage": "Popular on Mastodon", - "id": "onboarding.follows.title" - }, - { - "defaultMessage": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", - "id": "onboarding.follows.lead" - }, - { - "defaultMessage": "Did you know? Since Mastodon is decentralized, some profiles you come across will be hosted on servers other than yours. And yet you can interact with them seamlessly! Their server is in the second half of their username!", - "id": "onboarding.tips.accounts_from_other_servers" - }, - { - "defaultMessage": "Take me back", - "id": "onboarding.actions.back" - } - ], - "path": "app/javascript/mastodon/features/onboarding/follows.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Hello #Mastodon!", - "id": "onboarding.compose.template" - }, - { - "defaultMessage": "You've made it!", - "id": "onboarding.start.title" - }, - { - "defaultMessage": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", - "id": "onboarding.start.lead" - }, - { - "defaultMessage": "Customize your profile", - "id": "onboarding.steps.setup_profile.title" - }, - { - "defaultMessage": "Others are more likely to interact with you with a filled out profile.", - "id": "onboarding.steps.setup_profile.body" - }, - { - "defaultMessage": "Follow {count, plural, one {one person} other {# people}}", - "id": "onboarding.steps.follow_people.title" - }, - { - "defaultMessage": "You curate your own feed. Let's fill it with interesting people.", - "id": "onboarding.steps.follow_people.body" - }, - { - "defaultMessage": "Make your first post", - "id": "onboarding.steps.publish_status.title" - }, - { - "defaultMessage": "Say hello to the world.", - "id": "onboarding.steps.publish_status.body" - }, - { - "defaultMessage": "Share your profile", - "id": "onboarding.steps.share_profile.title" - }, - { - "defaultMessage": "Let your friends know how to find you on Mastodon!", - "id": "onboarding.steps.share_profile.body" - }, - { - "defaultMessage": "Want to skip right ahead?", - "id": "onboarding.start.skip" - }, - { - "defaultMessage": "See what's trending", - "id": "onboarding.actions.go_to_explore" - }, - { - "defaultMessage": "Don't show this screen again", - "id": "onboarding.actions.close" - } - ], - "path": "app/javascript/mastodon/features/onboarding/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "I'm {username} on #Mastodon! Come follow me at {url}", - "id": "onboarding.share.message" - }, - { - "defaultMessage": "Copied", - "id": "copypaste.copied" - }, - { - "defaultMessage": "Copy to clipboard", - "id": "copypaste.copy_to_clipboard" - }, - { - "defaultMessage": "Share your profile", - "id": "onboarding.share.title" - }, - { - "defaultMessage": "Let people know how they can find you on Mastodon!", - "id": "onboarding.share.lead" - }, - { - "defaultMessage": "Did you know? You can verify your account by putting a link to your Mastodon profile on your own website and adding the website to your profile. No fees or documents necessary!", - "id": "onboarding.tips.verification" - }, - { - "defaultMessage": "Did you know? If you feel like {domain} is not a great server choice for you in the future, you can move to another Mastodon server without losing your followers. You can even host your own server!", - "id": "onboarding.tips.migration" - }, - { - "defaultMessage": "Did you know? You can secure your account by setting up two-factor authentication in your account settings. It works with any TOTP app of your choice, no phone number necessary!", - "id": "onboarding.tips.2fa" - }, - { - "defaultMessage": "Possible next steps:", - "id": "onboarding.share.next_steps" - }, - { - "defaultMessage": "Go to your home feed", - "id": "onboarding.actions.go_to_home" - }, - { - "defaultMessage": "See what's trending", - "id": "onboarding.actions.go_to_explore" - }, - { - "defaultMessage": "Take me back", - "id": "onboarding.action.back" - } - ], - "path": "app/javascript/mastodon/features/onboarding/share.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Reply", - "id": "status.reply" - }, - { - "defaultMessage": "Reply to thread", - "id": "status.replyAll" - }, - { - "defaultMessage": "Boost", - "id": "status.reblog" - }, - { - "defaultMessage": "Boost with original visibility", - "id": "status.reblog_private" - }, - { - "defaultMessage": "Unboost", - "id": "status.cancel_reblog_private" - }, - { - "defaultMessage": "This post cannot be boosted", - "id": "status.cannot_reblog" - }, - { - "defaultMessage": "Favourite", - "id": "status.favourite" - }, - { - "defaultMessage": "Reply", - "id": "confirmations.reply.confirm" - }, - { - "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", - "id": "confirmations.reply.message" - }, - { - "defaultMessage": "Expand this status", - "id": "status.open" - } - ], - "path": "app/javascript/mastodon/features/picture_in_picture/components/footer.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - } - ], - "path": "app/javascript/mastodon/features/picture_in_picture/components/header.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Pinned post", - "id": "column.pins" - } - ], - "path": "app/javascript/mastodon/features/pinned_statuses/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Privacy Policy", - "id": "privacy_policy.title" - }, - { - "defaultMessage": "Last updated {date}", - "id": "privacy_policy.last_updated" - } - ], - "path": "app/javascript/mastodon/features/privacy_policy/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Media only", - "id": "community.column_settings.media_only" - }, - { - "defaultMessage": "Remote only", - "id": "community.column_settings.remote_only" - } - ], - "path": "app/javascript/mastodon/features/public_timeline/components/column_settings.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Federated timeline", - "id": "column.public" - }, - { - "defaultMessage": "These are the most recent public posts from people on this and other servers of the decentralized network that this server knows about.", - "id": "dismissable_banner.public_timeline" - }, - { - "defaultMessage": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up", - "id": "empty_column.public" - } - ], - "path": "app/javascript/mastodon/features/public_timeline/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Refresh", - "id": "refresh" - }, - { - "defaultMessage": "No one has boosted this post yet. When someone does, they will show up here.", - "id": "status.reblogs.empty" - } - ], - "path": "app/javascript/mastodon/features/reblogs/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "I don't like it", - "id": "report.reasons.dislike" - }, - { - "defaultMessage": "It is not something you want to see", - "id": "report.reasons.dislike_description" - }, - { - "defaultMessage": "It's spam", - "id": "report.reasons.spam" - }, - { - "defaultMessage": "Malicious links, fake engagement, or repetitive replies", - "id": "report.reasons.spam_description" - }, - { - "defaultMessage": "It violates server rules", - "id": "report.reasons.violation" - }, - { - "defaultMessage": "You are aware that it breaks specific rules", - "id": "report.reasons.violation_description" - }, - { - "defaultMessage": "It's something else", - "id": "report.reasons.other" - }, - { - "defaultMessage": "The issue does not fit into other categories", - "id": "report.reasons.other_description" - }, - { - "defaultMessage": "post", - "id": "report.category.title_status" - }, - { - "defaultMessage": "profile", - "id": "report.category.title_account" - }, - { - "defaultMessage": "Tell us what's going on with this {type}", - "id": "report.category.title" - }, - { - "defaultMessage": "Choose the best match", - "id": "report.category.subtitle" - }, - { - "defaultMessage": "Next", - "id": "report.next" - } - ], - "path": "app/javascript/mastodon/features/report/category.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Type or paste additional comments", - "id": "report.placeholder" - }, - { - "defaultMessage": "Is there anything else you think we should know?", - "id": "report.comment.title" - }, - { - "defaultMessage": "The account is from another server. Send an anonymized copy of the report there as well?", - "id": "report.forward_hint" - }, - { - "defaultMessage": "Forward to {target}", - "id": "report.forward" - }, - { - "defaultMessage": "Submit report", - "id": "report.submit" - } - ], - "path": "app/javascript/mastodon/features/report/comment.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Public", - "id": "privacy.public.short" - }, - { - "defaultMessage": "Unlisted", - "id": "privacy.unlisted.short" - }, - { - "defaultMessage": "Followers only", - "id": "privacy.private.short" - }, - { - "defaultMessage": "Mentioned people only", - "id": "privacy.direct.short" - } - ], - "path": "app/javascript/mastodon/features/report/components/status_check_box.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Which rules are being violated?", - "id": "report.rules.title" - }, - { - "defaultMessage": "Select all that apply", - "id": "report.rules.subtitle" - }, - { - "defaultMessage": "Next", - "id": "report.next" - } - ], - "path": "app/javascript/mastodon/features/report/rules.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Are there any posts that back up this report?", - "id": "report.statuses.title" - }, - { - "defaultMessage": "Select all that apply", - "id": "report.statuses.subtitle" - }, - { - "defaultMessage": "Next", - "id": "report.next" - } - ], - "path": "app/javascript/mastodon/features/report/statuses.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Thanks for reporting, we'll look into this.", - "id": "report.thanks.title_actionable" - }, - { - "defaultMessage": "Don't want to see this?", - "id": "report.thanks.title" - }, - { - "defaultMessage": "While we review this, you can take action against @{name}:", - "id": "report.thanks.take_action_actionable" - }, - { - "defaultMessage": "Here are your options for controlling what you see on Mastodon:", - "id": "report.thanks.take_action" - }, - { - "defaultMessage": "Unfollow @{name}", - "id": "report.unfollow" - }, - { - "defaultMessage": "You are following this account. To not see their posts in your home feed anymore, unfollow them.", - "id": "report.unfollow_explanation" - }, - { - "defaultMessage": "Unfollow", - "id": "account.unfollow" - }, - { - "defaultMessage": "Mute @{name}", - "id": "account.mute" - }, - { - "defaultMessage": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.", - "id": "report.mute_explanation" - }, - { - "defaultMessage": "Mute", - "id": "report.mute" - }, - { - "defaultMessage": "Muted", - "id": "account.muted" - }, - { - "defaultMessage": "Block @{name}", - "id": "account.block" - }, - { - "defaultMessage": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.", - "id": "report.block_explanation" - }, - { - "defaultMessage": "Block", - "id": "report.block" - }, - { - "defaultMessage": "Blocked", - "id": "account.blocked" - }, - { - "defaultMessage": "Done", - "id": "report.close" - } - ], - "path": "app/javascript/mastodon/features/report/thanks.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Delete", - "id": "status.delete" - }, - { - "defaultMessage": "Delete & re-draft", - "id": "status.redraft" - }, - { - "defaultMessage": "Edit", - "id": "status.edit" - }, - { - "defaultMessage": "Privately mention @{name}", - "id": "status.direct" - }, - { - "defaultMessage": "Mention @{name}", - "id": "status.mention" - }, - { - "defaultMessage": "Reply", - "id": "status.reply" - }, - { - "defaultMessage": "Boost", - "id": "status.reblog" - }, - { - "defaultMessage": "Boost with original visibility", - "id": "status.reblog_private" - }, - { - "defaultMessage": "Unboost", - "id": "status.cancel_reblog_private" - }, - { - "defaultMessage": "This post cannot be boosted", - "id": "status.cannot_reblog" - }, - { - "defaultMessage": "Favourite", - "id": "status.favourite" - }, - { - "defaultMessage": "Bookmark", - "id": "status.bookmark" - }, - { - "defaultMessage": "More", - "id": "status.more" - }, - { - "defaultMessage": "Mute @{name}", - "id": "status.mute" - }, - { - "defaultMessage": "Mute conversation", - "id": "status.mute_conversation" - }, - { - "defaultMessage": "Unmute conversation", - "id": "status.unmute_conversation" - }, - { - "defaultMessage": "Block @{name}", - "id": "status.block" - }, - { - "defaultMessage": "Report @{name}", - "id": "status.report" - }, - { - "defaultMessage": "Share", - "id": "status.share" - }, - { - "defaultMessage": "Pin on profile", - "id": "status.pin" - }, - { - "defaultMessage": "Unpin from profile", - "id": "status.unpin" - }, - { - "defaultMessage": "Embed", - "id": "status.embed" - }, - { - "defaultMessage": "Open moderation interface for @{name}", - "id": "status.admin_account" - }, - { - "defaultMessage": "Open this post in the moderation interface", - "id": "status.admin_status" - }, - { - "defaultMessage": "Open moderation interface for {domain}", - "id": "status.admin_domain" - }, - { - "defaultMessage": "Copy link to post", - "id": "status.copy" - }, - { - "defaultMessage": "Block domain {domain}", - "id": "account.block_domain" - }, - { - "defaultMessage": "Unblock domain {domain}", - "id": "account.unblock_domain" - }, - { - "defaultMessage": "Unmute @{name}", - "id": "account.unmute" - }, - { - "defaultMessage": "Unblock @{name}", - "id": "account.unblock" - }, - { - "defaultMessage": "Open original page", - "id": "account.open_original_page" - } - ], - "path": "app/javascript/mastodon/features/status/components/action_bar.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Sensitive content", - "id": "status.sensitive_warning" - } - ], - "path": "app/javascript/mastodon/features/status/components/card.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Public", - "id": "privacy.public.short" - }, - { - "defaultMessage": "Unlisted", - "id": "privacy.unlisted.short" - }, - { - "defaultMessage": "Followers only", - "id": "privacy.private.short" - }, - { - "defaultMessage": "Mentioned people only", - "id": "privacy.direct.short" - }, - { - "defaultMessage": "Private mention", - "id": "status.direct_indicator" - } - ], - "path": "app/javascript/mastodon/features/status/components/detailed_status.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Delete", - "id": "confirmations.delete.confirm" - }, - { - "defaultMessage": "Are you sure you want to delete this status?", - "id": "confirmations.delete.message" - }, - { - "defaultMessage": "Delete & redraft", - "id": "confirmations.redraft.confirm" - }, - { - "defaultMessage": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", - "id": "confirmations.redraft.message" - }, - { - "defaultMessage": "Show more for all", - "id": "status.show_more_all" - }, - { - "defaultMessage": "Show less for all", - "id": "status.show_less_all" - }, - { - "defaultMessage": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}", - "id": "status.title.with_attachments" - }, - { - "defaultMessage": "Detailed conversation view", - "id": "status.detailed_status" - }, - { - "defaultMessage": "Reply", - "id": "confirmations.reply.confirm" - }, - { - "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", - "id": "confirmations.reply.message" - }, - { - "defaultMessage": "Block entire domain", - "id": "confirmations.domain_block.confirm" - }, - { - "defaultMessage": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", - "id": "confirmations.domain_block.message" - } - ], - "path": "app/javascript/mastodon/features/status/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Change subscribed languages for {target}", - "id": "subscribed_languages.target" - }, - { - "defaultMessage": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.", - "id": "subscribed_languages.lead" - }, - { - "defaultMessage": "Save changes", - "id": "subscribed_languages.save" - } - ], - "path": "app/javascript/mastodon/features/subscribed_languages_modal/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Are you sure you want to block {name}?", - "id": "confirmations.block.message" - }, - { - "defaultMessage": "Cancel", - "id": "confirmation_modal.cancel" - }, - { - "defaultMessage": "Block & Report", - "id": "confirmations.block.block_and_report" - }, - { - "defaultMessage": "Block", - "id": "confirmations.block.confirm" - } - ], - "path": "app/javascript/mastodon/features/ui/components/block_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Unboost", - "id": "status.cancel_reblog_private" - }, - { - "defaultMessage": "Boost", - "id": "status.reblog" - }, - { - "defaultMessage": "Public", - "id": "privacy.public.short" - }, - { - "defaultMessage": "Unlisted", - "id": "privacy.unlisted.short" - }, - { - "defaultMessage": "Followers only", - "id": "privacy.private.short" - }, - { - "defaultMessage": "Mentioned people only", - "id": "privacy.direct.short" - }, - { - "defaultMessage": "You can press {combo} to skip this next time", - "id": "boost_modal.combo" - } - ], - "path": "app/javascript/mastodon/features/ui/components/boost_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Copied", - "id": "copypaste.copied" - }, - { - "defaultMessage": "404", - "id": "bundle_column_error.routing.title" - }, - { - "defaultMessage": "The requested page could not be found. Are you sure the URL in the address bar is correct?", - "id": "bundle_column_error.routing.body" - }, - { - "defaultMessage": "Network error", - "id": "bundle_column_error.network.title" - }, - { - "defaultMessage": "There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.", - "id": "bundle_column_error.network.body" - }, - { - "defaultMessage": "Oh, no!", - "id": "bundle_column_error.error.title" - }, - { - "defaultMessage": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.", - "id": "bundle_column_error.error.body" - }, - { - "defaultMessage": "Try again", - "id": "bundle_column_error.retry" - }, - { - "defaultMessage": "Copy error report", - "id": "bundle_column_error.copy_stacktrace" - }, - { - "defaultMessage": "Go back home", - "id": "bundle_column_error.return" - } - ], - "path": "app/javascript/mastodon/features/ui/components/bundle_column_error.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Something went wrong while loading this component.", - "id": "bundle_modal_error.message" - }, - { - "defaultMessage": "Try again", - "id": "bundle_modal_error.retry" - }, - { - "defaultMessage": "Close", - "id": "bundle_modal_error.close" - } - ], - "path": "app/javascript/mastodon/features/ui/components/bundle_modal_error.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{name} created {date}", - "id": "status.history.created" - }, - { - "defaultMessage": "{name} edited {date}", - "id": "status.history.edited" - } - ], - "path": "app/javascript/mastodon/features/ui/components/compare_history_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Cancel", - "id": "confirmation_modal.cancel" - } - ], - "path": "app/javascript/mastodon/features/ui/components/confirmation_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Are you sure you want to log out?", - "id": "confirmations.logout.message" - }, - { - "defaultMessage": "Log out", - "id": "confirmations.logout.confirm" - }, - { - "defaultMessage": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.", - "id": "moved_to_account_banner.text" - }, - { - "defaultMessage": "Your account {disabledAccount} is currently disabled.", - "id": "disabled_account_banner.text" - }, - { - "defaultMessage": "Account settings", - "id": "disabled_account_banner.account_settings" - } - ], - "path": "app/javascript/mastodon/features/ui/components/disabled_account_banner.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Embed", - "id": "status.embed" - }, - { - "defaultMessage": "Embed this status on your website by copying the code below.", - "id": "embed.instructions" - }, - { - "defaultMessage": "Here is what it will look like:", - "id": "embed.preview" - } - ], - "path": "app/javascript/mastodon/features/ui/components/embed_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Filter a post", - "id": "filter_modal.title.status" - } - ], - "path": "app/javascript/mastodon/features/ui/components/filter_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Apply", - "id": "upload_modal.apply" - }, - { - "defaultMessage": "Applying…", - "id": "upload_modal.applying" - }, - { - "defaultMessage": "A quick brown fox jumps over the lazy dog", - "id": "upload_modal.description_placeholder" - }, - { - "defaultMessage": "Choose image", - "id": "upload_modal.choose_image" - }, - { - "defaultMessage": "You have unsaved changes to the media description or preview, discard them anyway?", - "id": "confirmations.discard_edit_media.message" - }, - { - "defaultMessage": "Discard", - "id": "confirmations.discard_edit_media.confirm" - }, - { - "defaultMessage": "Describe for people who are hard of hearing", - "id": "upload_form.audio_description" - }, - { - "defaultMessage": "Describe for people who are deaf, hard of hearing, blind or have low vision", - "id": "upload_form.video_description" - }, - { - "defaultMessage": "Describe for people who are blind or have low vision", - "id": "upload_form.description" - }, - { - "defaultMessage": "Analyzing picture…", - "id": "upload_modal.analyzing_picture" - }, - { - "defaultMessage": "Preparing OCR…", - "id": "upload_modal.preparing_ocr" - }, - { - "defaultMessage": "Edit media", - "id": "upload_modal.edit_media" - }, - { - "defaultMessage": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", - "id": "upload_modal.hint" - }, - { - "defaultMessage": "Change thumbnail", - "id": "upload_form.thumbnail" - }, - { - "defaultMessage": "Detect text from picture", - "id": "upload_modal.detect_text" - }, - { - "defaultMessage": "Preview ({ratio})", - "id": "upload_modal.preview_label" - } - ], - "path": "app/javascript/mastodon/features/ui/components/focal_point_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Follow requests", - "id": "navigation_bar.follow_requests" - } - ], - "path": "app/javascript/mastodon/features/ui/components/follow_requests_column_link.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Publish", - "id": "compose_form.publish_form" - }, - { - "defaultMessage": "Create account", - "id": "sign_in_banner.create_account" - }, - { - "defaultMessage": "Login", - "id": "sign_in_banner.sign_in" - } - ], - "path": "app/javascript/mastodon/features/ui/components/header.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - } - ], - "path": "app/javascript/mastodon/features/ui/components/image_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Are you sure you want to log out?", - "id": "confirmations.logout.message" - }, - { - "defaultMessage": "Log out", - "id": "confirmations.logout.confirm" - }, - { - "defaultMessage": "About", - "id": "footer.about" - }, - { - "defaultMessage": "Status", - "id": "footer.status" - }, - { - "defaultMessage": "Invite people", - "id": "footer.invite" - }, - { - "defaultMessage": "Profiles directory", - "id": "footer.directory" - }, - { - "defaultMessage": "Privacy policy", - "id": "footer.privacy_policy" - }, - { - "defaultMessage": "Get the app", - "id": "footer.get_app" - }, - { - "defaultMessage": "Keyboard shortcuts", - "id": "footer.keyboard_shortcuts" - }, - { - "defaultMessage": "View source code", - "id": "footer.source_code" - } - ], - "path": "app/javascript/mastodon/features/ui/components/link_footer.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Previous", - "id": "lightbox.previous" - }, - { - "defaultMessage": "Next", - "id": "lightbox.next" - } - ], - "path": "app/javascript/mastodon/features/ui/components/media_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "{number, plural, one {# minute} other {# minutes}}", - "id": "intervals.full.minutes" - }, - { - "defaultMessage": "{number, plural, one {# hour} other {# hours}}", - "id": "intervals.full.hours" - }, - { - "defaultMessage": "{number, plural, one {# day} other {# days}}", - "id": "intervals.full.days" - }, - { - "defaultMessage": "Indefinite", - "id": "mute_modal.indefinite" - }, - { - "defaultMessage": "Are you sure you want to mute {name}?", - "id": "confirmations.mute.message" - }, - { - "defaultMessage": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", - "id": "confirmations.mute.explanation" - }, - { - "defaultMessage": "Hide notifications from this user?", - "id": "mute_modal.hide_notifications" - }, - { - "defaultMessage": "Duration", - "id": "mute_modal.duration" - }, - { - "defaultMessage": "Cancel", - "id": "confirmation_modal.cancel" - }, - { - "defaultMessage": "Mute", - "id": "confirmations.mute.confirm" - } - ], - "path": "app/javascript/mastodon/features/ui/components/mute_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Home", - "id": "tabs_bar.home" - }, - { - "defaultMessage": "Notifications", - "id": "tabs_bar.notifications" - }, - { - "defaultMessage": "Explore", - "id": "explore.title" - }, - { - "defaultMessage": "Local", - "id": "tabs_bar.local_timeline" - }, - { - "defaultMessage": "Federated", - "id": "tabs_bar.federated_timeline" - }, - { - "defaultMessage": "Private mentions", - "id": "navigation_bar.direct" - }, - { - "defaultMessage": "Favourites", - "id": "navigation_bar.favourites" - }, - { - "defaultMessage": "Bookmarks", - "id": "navigation_bar.bookmarks" - }, - { - "defaultMessage": "Lists", - "id": "navigation_bar.lists" - }, - { - "defaultMessage": "Preferences", - "id": "navigation_bar.preferences" - }, - { - "defaultMessage": "Follows and followers", - "id": "navigation_bar.follows_and_followers" - }, - { - "defaultMessage": "About", - "id": "navigation_bar.about" - }, - { - "defaultMessage": "Search", - "id": "navigation_bar.search" - } - ], - "path": "app/javascript/mastodon/features/ui/components/navigation_panel.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Close", - "id": "lightbox.close" - }, - { - "defaultMessage": "Report {target}", - "id": "report.target" - } - ], - "path": "app/javascript/mastodon/features/ui/components/report_modal.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Create account", - "id": "sign_in_banner.create_account" - }, - { - "defaultMessage": "Login to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.", - "id": "sign_in_banner.text" - }, - { - "defaultMessage": "Login", - "id": "sign_in_banner.sign_in" - } - ], - "path": "app/javascript/mastodon/features/ui/components/sign_in_banner.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Drag & drop to upload", - "id": "upload_area.title" - } - ], - "path": "app/javascript/mastodon/features/ui/components/upload_area.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Compress image view box", - "id": "lightbox.compress" - }, - { - "defaultMessage": "Expand image view box", - "id": "lightbox.expand" - } - ], - "path": "app/javascript/mastodon/features/ui/components/zoomable_image.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Your draft will be lost if you leave Mastodon.", - "id": "ui.beforeunload" - } - ], - "path": "app/javascript/mastodon/features/ui/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Play", - "id": "video.play" - }, - { - "defaultMessage": "Pause", - "id": "video.pause" - }, - { - "defaultMessage": "Mute sound", - "id": "video.mute" - }, - { - "defaultMessage": "Unmute sound", - "id": "video.unmute" - }, - { - "defaultMessage": "Hide video", - "id": "video.hide" - }, - { - "defaultMessage": "Expand video", - "id": "video.expand" - }, - { - "defaultMessage": "Close video", - "id": "video.close" - }, - { - "defaultMessage": "Full screen", - "id": "video.fullscreen" - }, - { - "defaultMessage": "Exit full screen", - "id": "video.exit_fullscreen" - }, - { - "defaultMessage": "Sensitive content", - "id": "status.sensitive_warning" - }, - { - "defaultMessage": "Media hidden", - "id": "status.media_hidden" - } - ], - "path": "app/javascript/mastodon/features/video/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "That username is taken. Try another", - "id": "username.taken" - }, - { - "defaultMessage": "Password confirmation exceeds the maximum password length", - "id": "password_confirmation.exceeds_maxlength" - }, - { - "defaultMessage": "Password confirmation does not match", - "id": "password_confirmation.mismatching" - } - ], - "path": "app/javascript/packs/public.json" - } -] \ No newline at end of file diff --git a/app/javascript/mastodon/locales/index.js b/app/javascript/mastodon/locales/index.js index 421cb7fab0..6e57e3ddc4 100644 --- a/app/javascript/mastodon/locales/index.js +++ b/app/javascript/mastodon/locales/index.js @@ -7,3 +7,16 @@ export function setLocale(locale) { export function getLocale() { return theLocale; } + +export function onProviderError(error) { + // Silent the error, like upstream does + if(process.env.NODE_ENV === 'production') return; + + // This browser does not advertise Intl support for this locale, we only print a warning + // As-per the spec, the browser should select the best matching locale + if(typeof error === "object" && error.message.match("MISSING_DATA")) { + console.warn(error.message); + } + + console.error(error); +} diff --git a/app/javascript/mastodon/locales/whitelist_af.json b/app/javascript/mastodon/locales/whitelist_af.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_af.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_an.json b/app/javascript/mastodon/locales/whitelist_an.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_an.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ar.json b/app/javascript/mastodon/locales/whitelist_ar.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ar.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ast.json b/app/javascript/mastodon/locales/whitelist_ast.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ast.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_be.json b/app/javascript/mastodon/locales/whitelist_be.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_be.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_bg.json b/app/javascript/mastodon/locales/whitelist_bg.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_bg.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_bn.json b/app/javascript/mastodon/locales/whitelist_bn.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_bn.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_br.json b/app/javascript/mastodon/locales/whitelist_br.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_br.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_bs.json b/app/javascript/mastodon/locales/whitelist_bs.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_bs.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ca.json b/app/javascript/mastodon/locales/whitelist_ca.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ca.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ckb.json b/app/javascript/mastodon/locales/whitelist_ckb.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ckb.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_co.json b/app/javascript/mastodon/locales/whitelist_co.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_co.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_cs.json b/app/javascript/mastodon/locales/whitelist_cs.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_cs.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_csb.json b/app/javascript/mastodon/locales/whitelist_csb.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_csb.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_cy.json b/app/javascript/mastodon/locales/whitelist_cy.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_cy.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_da.json b/app/javascript/mastodon/locales/whitelist_da.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_da.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_de.json b/app/javascript/mastodon/locales/whitelist_de.json deleted file mode 100644 index c311ad0489..0000000000 --- a/app/javascript/mastodon/locales/whitelist_de.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "account.badges.bot", - "compose_form.publish_loud", - "search_results.hashtags" -] diff --git a/app/javascript/mastodon/locales/whitelist_el.json b/app/javascript/mastodon/locales/whitelist_el.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_el.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_en-GB.json b/app/javascript/mastodon/locales/whitelist_en-GB.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_en-GB.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_en.json b/app/javascript/mastodon/locales/whitelist_en.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_en.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_eo.json b/app/javascript/mastodon/locales/whitelist_eo.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_eo.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_es-AR.json b/app/javascript/mastodon/locales/whitelist_es-AR.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_es-AR.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_es-MX.json b/app/javascript/mastodon/locales/whitelist_es-MX.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_es-MX.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_es.json b/app/javascript/mastodon/locales/whitelist_es.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_es.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_et.json b/app/javascript/mastodon/locales/whitelist_et.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_et.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_eu.json b/app/javascript/mastodon/locales/whitelist_eu.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_eu.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_fa.json b/app/javascript/mastodon/locales/whitelist_fa.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_fa.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_fi.json b/app/javascript/mastodon/locales/whitelist_fi.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_fi.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_fo.json b/app/javascript/mastodon/locales/whitelist_fo.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_fo.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_fr-QC.json b/app/javascript/mastodon/locales/whitelist_fr-QC.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_fr-QC.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_fr.json b/app/javascript/mastodon/locales/whitelist_fr.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_fr.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_fy.json b/app/javascript/mastodon/locales/whitelist_fy.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_fy.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ga.json b/app/javascript/mastodon/locales/whitelist_ga.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ga.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_gd.json b/app/javascript/mastodon/locales/whitelist_gd.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_gd.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_gl.json b/app/javascript/mastodon/locales/whitelist_gl.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_gl.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_he.json b/app/javascript/mastodon/locales/whitelist_he.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_he.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_hi.json b/app/javascript/mastodon/locales/whitelist_hi.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_hi.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_hr.json b/app/javascript/mastodon/locales/whitelist_hr.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_hr.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_hu.json b/app/javascript/mastodon/locales/whitelist_hu.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_hu.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_hy.json b/app/javascript/mastodon/locales/whitelist_hy.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_hy.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_id.json b/app/javascript/mastodon/locales/whitelist_id.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_id.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ig.json b/app/javascript/mastodon/locales/whitelist_ig.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ig.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_io.json b/app/javascript/mastodon/locales/whitelist_io.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_io.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_is.json b/app/javascript/mastodon/locales/whitelist_is.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_is.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_it.json b/app/javascript/mastodon/locales/whitelist_it.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_it.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ja.json b/app/javascript/mastodon/locales/whitelist_ja.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ja.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ka.json b/app/javascript/mastodon/locales/whitelist_ka.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ka.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_kab.json b/app/javascript/mastodon/locales/whitelist_kab.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_kab.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_kk.json b/app/javascript/mastodon/locales/whitelist_kk.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_kk.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_kn.json b/app/javascript/mastodon/locales/whitelist_kn.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_kn.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ko.json b/app/javascript/mastodon/locales/whitelist_ko.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ko.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ku.json b/app/javascript/mastodon/locales/whitelist_ku.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ku.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_kw.json b/app/javascript/mastodon/locales/whitelist_kw.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_kw.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_la.json b/app/javascript/mastodon/locales/whitelist_la.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_la.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_lt.json b/app/javascript/mastodon/locales/whitelist_lt.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_lt.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_lv.json b/app/javascript/mastodon/locales/whitelist_lv.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_lv.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_mk.json b/app/javascript/mastodon/locales/whitelist_mk.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_mk.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ml.json b/app/javascript/mastodon/locales/whitelist_ml.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ml.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_mr.json b/app/javascript/mastodon/locales/whitelist_mr.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_mr.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ms.json b/app/javascript/mastodon/locales/whitelist_ms.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ms.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_my.json b/app/javascript/mastodon/locales/whitelist_my.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_my.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_nl.json b/app/javascript/mastodon/locales/whitelist_nl.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_nl.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_nn.json b/app/javascript/mastodon/locales/whitelist_nn.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_nn.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_no.json b/app/javascript/mastodon/locales/whitelist_no.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_no.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_oc.json b/app/javascript/mastodon/locales/whitelist_oc.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_oc.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_pa.json b/app/javascript/mastodon/locales/whitelist_pa.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_pa.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_pl.json b/app/javascript/mastodon/locales/whitelist_pl.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_pl.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_pt-BR.json b/app/javascript/mastodon/locales/whitelist_pt-BR.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_pt-BR.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_pt-PT.json b/app/javascript/mastodon/locales/whitelist_pt-PT.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_pt-PT.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ro.json b/app/javascript/mastodon/locales/whitelist_ro.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ro.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ru.json b/app/javascript/mastodon/locales/whitelist_ru.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ru.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_sa.json b/app/javascript/mastodon/locales/whitelist_sa.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_sa.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_sc.json b/app/javascript/mastodon/locales/whitelist_sc.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_sc.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_sco.json b/app/javascript/mastodon/locales/whitelist_sco.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_sco.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_si.json b/app/javascript/mastodon/locales/whitelist_si.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_si.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_sk.json b/app/javascript/mastodon/locales/whitelist_sk.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_sk.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_sl.json b/app/javascript/mastodon/locales/whitelist_sl.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_sl.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_sq.json b/app/javascript/mastodon/locales/whitelist_sq.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_sq.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_sr-Latn.json b/app/javascript/mastodon/locales/whitelist_sr-Latn.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_sr-Latn.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_sr.json b/app/javascript/mastodon/locales/whitelist_sr.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_sr.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_sv.json b/app/javascript/mastodon/locales/whitelist_sv.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_sv.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_szl.json b/app/javascript/mastodon/locales/whitelist_szl.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_szl.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ta.json b/app/javascript/mastodon/locales/whitelist_ta.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ta.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_tai.json b/app/javascript/mastodon/locales/whitelist_tai.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_tai.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_te.json b/app/javascript/mastodon/locales/whitelist_te.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_te.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_th.json b/app/javascript/mastodon/locales/whitelist_th.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_th.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_tr.json b/app/javascript/mastodon/locales/whitelist_tr.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_tr.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_tt.json b/app/javascript/mastodon/locales/whitelist_tt.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_tt.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ug.json b/app/javascript/mastodon/locales/whitelist_ug.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ug.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_uk.json b/app/javascript/mastodon/locales/whitelist_uk.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_uk.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_ur.json b/app/javascript/mastodon/locales/whitelist_ur.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_ur.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_uz.json b/app/javascript/mastodon/locales/whitelist_uz.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_uz.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_vi.json b/app/javascript/mastodon/locales/whitelist_vi.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_vi.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_zgh.json b/app/javascript/mastodon/locales/whitelist_zgh.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_zgh.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_zh-CN.json b/app/javascript/mastodon/locales/whitelist_zh-CN.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_zh-CN.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_zh-HK.json b/app/javascript/mastodon/locales/whitelist_zh-HK.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_zh-HK.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/locales/whitelist_zh-TW.json b/app/javascript/mastodon/locales/whitelist_zh-TW.json deleted file mode 100644 index 0d4f101c7a..0000000000 --- a/app/javascript/mastodon/locales/whitelist_zh-TW.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/app/javascript/mastodon/polyfills/base_polyfills.ts b/app/javascript/mastodon/polyfills/base_polyfills.ts index e008d8f025..3cde1b1ede 100644 --- a/app/javascript/mastodon/polyfills/base_polyfills.ts +++ b/app/javascript/mastodon/polyfills/base_polyfills.ts @@ -1,5 +1,3 @@ -import 'intl'; -import 'intl/locale-data/jsonp/en'; import 'core-js/features/object/assign'; import 'core-js/features/object/values'; import 'core-js/features/symbol'; diff --git a/app/javascript/mastodon/polyfills/index.ts b/app/javascript/mastodon/polyfills/index.ts index 6d2e5426e4..b2dbfdac0a 100644 --- a/app/javascript/mastodon/polyfills/index.ts +++ b/app/javascript/mastodon/polyfills/index.ts @@ -2,6 +2,8 @@ // If there are no polyfills, then this is just Promise.resolve() which means // it will execute in the same tick of the event loop (i.e. near-instant). +import { loadIntlPolyfills } from './intl'; + function importBasePolyfills() { return import(/* webpackChunkName: "base_polyfills" */ './base_polyfills'); } @@ -13,7 +15,6 @@ function importExtraPolyfills() { export function loadPolyfills() { const needsBasePolyfills = !( 'toBlob' in HTMLCanvasElement.prototype && - 'Intl' in window && 'assign' in Object && 'values' in Object && 'Symbol' in window && @@ -32,6 +33,7 @@ export function loadPolyfills() { ); return Promise.all([ + loadIntlPolyfills(), needsBasePolyfills && importBasePolyfills(), needsExtraPolyfills && importExtraPolyfills(), ]); diff --git a/app/javascript/mastodon/polyfills/intl.ts b/app/javascript/mastodon/polyfills/intl.ts new file mode 100644 index 0000000000..4d5ee3ccf9 --- /dev/null +++ b/app/javascript/mastodon/polyfills/intl.ts @@ -0,0 +1,105 @@ +// import { shouldPolyfill as shouldPolyfillCanonicalLocales } from '@formatjs/intl-getcanonicallocales/should-polyfill'; +// import { shouldPolyfill as shouldPolyfillLocale } from '@formatjs/intl-locale/should-polyfill'; +import { shouldPolyfill as shoudPolyfillPluralRules } from '@formatjs/intl-pluralrules/should-polyfill'; +// import { shouldPolyfill as shouldPolyfillNumberFormat } from '@formatjs/intl-numberformat/should-polyfill'; +// import { shouldPolyfill as shouldPolyfillIntlDateTimeFormat } from '@formatjs/intl-datetimeformat/should-polyfill'; +// import { shouldPolyfill as shouldPolyfillIntlRelativeTimeFormat } from '@formatjs/intl-relativetimeformat/should-polyfill'; + +// async function loadGetCanonicalLocalesPolyfill() { +// // This platform already supports Intl.getCanonicalLocales +// if (shouldPolyfillCanonicalLocales()) { +// await import('@formatjs/intl-getcanonicallocales/polyfill'); +// } +// } + +// async function loadLocalePolyfill() { +// // This platform already supports Intl.Locale +// if (shouldPolyfillLocale()) { +// await import('@formatjs/intl-locale/polyfill'); +// } +// } + +// async function loadIntlNumberFormatPolyfill(locale: string) { +// const unsupportedLocale = shouldPolyfillNumberFormat(locale); +// // This locale is supported +// if (!unsupportedLocale) { +// return; +// } +// // Load the polyfill 1st BEFORE loading data +// await import('@formatjs/intl-numberformat/polyfill-force'); +// await import(`@formatjs/intl-numberformat/locale-data/${unsupportedLocale}`); +// } + +// async function loadIntlDateTimeFormatPolyfill(locale: string) { +// const unsupportedLocale = shouldPolyfillIntlDateTimeFormat(locale); +// // This locale is supported +// if (!unsupportedLocale) { +// return; +// } +// // Load the polyfill 1st BEFORE loading data +// await import('@formatjs/intl-datetimeformat/polyfill-force'); + +// // Parallelize CLDR data loading +// const dataPolyfills = [ +// import('@formatjs/intl-datetimeformat/add-all-tz'), +// import(`@formatjs/intl-datetimeformat/locale-data/${unsupportedLocale}`), +// ]; +// await Promise.all(dataPolyfills); +// } + +async function loadIntlPluralRulesPolyfills(locale: string) { + const unsupportedLocale = shoudPolyfillPluralRules(locale); + // This locale is supported + if (!unsupportedLocale) { + return; + } + // Load the polyfill 1st BEFORE loading data + await import( + /* webpackChunkName: "i18n-pluralrules-polyfill" */ '@formatjs/intl-pluralrules/polyfill-force' + ); + await import( + /* webpackChunkName: "i18n-pluralrules-polyfill-[request]" */ `@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}` + ); +} + +// async function loadIntlRelativeTimeFormatPolyfill(locale: string) { +// const unsupportedLocale = shouldPolyfillIntlRelativeTimeFormat(locale); +// // This locale is supported +// if (!unsupportedLocale) { +// return; +// } +// // Load the polyfill 1st BEFORE loading data +// await import( +// /* webpackChunkName: "i18n-relativetimeformat-polyfill" */ +// '@formatjs/intl-relativetimeformat/polyfill-force' +// ); +// await import( +// /* webpackChunkName: "i18n-relativetimeformat-polyfill-[request]" */ +// `@formatjs/intl-relativetimeformat/locale-data/${unsupportedLocale}` +// ); +// } + +export async function loadIntlPolyfills() { + const locale = document.querySelector('html')?.lang || 'en'; + + // order is important here + + // Supported in IE11 and most other browsers, not useful + // await loadGetCanonicalLocalesPolyfill() + + // Supported in IE11 and most other browsers, not useful + // await loadLocalePolyfill() + + // Supported in IE11 and most other browsers, not useful + // await loadIntlNumberFormatPolyfill(locale) + + // Supported in IE11 and most other browsers, not useful + // await loadIntlDateTimeFormatPolyfill(locale) + + // Supported from Safari 13+, may still be useful + await loadIntlPluralRulesPolyfills(locale); + + // This is not used yet in the codebase yet + // Supported from Safari 14+ + // await loadIntlRelativeTimeFormatPolyfill(locale); +} diff --git a/app/javascript/mastodon/service_worker/web_push_locales.js b/app/javascript/mastodon/service_worker/web_push_locales.js index 3912f75c7d..89ae20007b 100644 --- a/app/javascript/mastodon/service_worker/web_push_locales.js +++ b/app/javascript/mastodon/service_worker/web_push_locales.js @@ -10,7 +10,7 @@ const filtered = {}; const filenames = fs.readdirSync(path.resolve(__dirname, '../locales')); filenames.forEach(filename => { - if (!filename.match(/\.json$/) || filename.match(/defaultMessages|whitelist/)) return; + if (!filename.match(/\.json$/)) return; const content = fs.readFileSync(path.resolve(__dirname, `../locales/${filename}`), 'utf-8'); const full = JSON.parse(content); diff --git a/app/javascript/mastodon/service_worker/web_push_notifications.js b/app/javascript/mastodon/service_worker/web_push_notifications.js index 54247e6f66..77187a59ed 100644 --- a/app/javascript/mastodon/service_worker/web_push_notifications.js +++ b/app/javascript/mastodon/service_worker/web_push_notifications.js @@ -1,4 +1,4 @@ -import IntlMessageFormat from 'intl-messageformat'; +import { IntlMessageFormat } from 'intl-messageformat'; import { unescape } from 'lodash'; diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 29fd5cde9b..01ab8f8f4b 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -1,10 +1,11 @@ import './public-path'; import { start } from '../mastodon/common'; +import { loadLocale } from '../mastodon/load_locale'; import { loadPolyfills } from '../mastodon/polyfills'; start(); -loadPolyfills().then(async () => { +loadPolyfills().then(loadLocale).then(async () => { const { default: main } = await import('mastodon/main'); return main(); diff --git a/app/javascript/packs/public.jsx b/app/javascript/packs/public.jsx index 72c7acbb4e..22e6b01a1f 100644 --- a/app/javascript/packs/public.jsx +++ b/app/javascript/packs/public.jsx @@ -2,7 +2,7 @@ import { createRoot } from 'react-dom/client'; import './public-path'; -import * as IntlMessageFormat from 'intl-messageformat'; +import { IntlMessageFormat } from 'intl-messageformat'; import { defineMessages } from 'react-intl'; import { delegate } from '@rails/ujs'; @@ -15,6 +15,7 @@ import { start } from '../mastodon/common'; import { timeAgoString } from '../mastodon/components/relative_timestamp'; import emojify from '../mastodon/features/emoji/emoji'; import loadKeyboardExtensions from '../mastodon/load_keyboard_extensions'; +import { loadLocale } from '../mastodon/load_locale'; import { getLocale } from '../mastodon/locales'; import { loadPolyfills } from '../mastodon/polyfills'; import ready from '../mastodon/ready'; @@ -46,7 +47,7 @@ window.addEventListener('message', e => { }); function loaded() { - const { localeData } = getLocale(); + const { messages: localeData } = getLocale(); const scrollToDetailedStatus = () => { const history = createBrowserHistory(); @@ -352,6 +353,7 @@ function main() { } loadPolyfills() + .then(loadLocale) .then(main) .then(loadKeyboardExtensions) .catch(error => { diff --git a/app/javascript/packs/share.jsx b/app/javascript/packs/share.jsx index 3bec37d1e4..f9fc785618 100644 --- a/app/javascript/packs/share.jsx +++ b/app/javascript/packs/share.jsx @@ -1,9 +1,9 @@ import './public-path'; -import React from 'react'; import { createRoot } from 'react-dom/client'; import { start } from '../mastodon/common'; import ComposeContainer from '../mastodon/containers/compose_container'; +import { loadLocale } from '../mastodon/load_locale'; import { loadPolyfills } from '../mastodon/polyfills'; import ready from '../mastodon/ready'; @@ -26,6 +26,6 @@ function main() { ready(loaded); } -loadPolyfills().then(main).catch(error => { +loadPolyfills().then(loadLocale).then(main).catch(error => { console.error(error); }); diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 3fa5fef09b..4fe2f18bfb 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -29,7 +29,7 @@ = stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous' = stylesheet_pack_tag current_theme, media: 'all', crossorigin: 'anonymous' = javascript_pack_tag 'common', crossorigin: 'anonymous' - = javascript_pack_tag "locale_#{I18n.locale}", crossorigin: 'anonymous' + = preload_pack_asset "locale/#{I18n.locale}-json.js" = csrf_meta_tags unless skip_csrf_meta_tags? %meta{ name: 'style-nonce', content: request.content_security_policy_nonce } diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml index e74bff9cc1..d8aa522d80 100644 --- a/app/views/layouts/embedded.html.haml +++ b/app/views/layouts/embedded.html.haml @@ -14,7 +14,7 @@ = stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous' = stylesheet_pack_tag Setting.default_settings['theme'], media: 'all', crossorigin: 'anonymous' = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' - = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' + = preload_pack_asset "locale/#{I18n.locale}-json.js" = render_initial_state = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' %body.embed diff --git a/babel.config.js b/babel.config.js index 986d605495..0eb877cc32 100644 --- a/babel.config.js +++ b/babel.config.js @@ -22,7 +22,7 @@ module.exports = (api) => { ['@babel/env', envOptions], ], plugins: [ - ['react-intl', { messagesDir: './build/messages' }], + ['formatjs'], 'preval', '@babel/plugin-transform-optional-chaining', '@babel/plugin-transform-nullish-coalescing-operator', diff --git a/config/formatjs-formatter.js b/config/formatjs-formatter.js new file mode 100644 index 0000000000..adb5e82ef7 --- /dev/null +++ b/config/formatjs-formatter.js @@ -0,0 +1,11 @@ +const path = require('path'); + +const currentTranslations = require(path.join(__dirname, "../app/javascript/mastodon/locales/en.json")); + +exports.format = (msgs) => { + const results = {}; + for (const [id, msg] of Object.entries(msgs)) { + results[id] = currentTranslations[id] || msg.defaultMessage; + } + return results; +}; diff --git a/config/webpack/generateLocalePacks.js b/config/webpack/generateLocalePacks.js deleted file mode 100644 index b8d5d82c6d..0000000000 --- a/config/webpack/generateLocalePacks.js +++ /dev/null @@ -1,51 +0,0 @@ -// To avoid adding a lot of boilerplate, locale packs are -// automatically generated here. These are written into the tmp/ -// directory and then used to generate locale_en.js, locale_fr.js, etc. - -const fs = require('fs'); -const path = require('path'); - -const { mkdirp } = require('mkdirp'); -const rimraf = require('rimraf'); - -const localesJsonPath = path.join(__dirname, '../../app/javascript/mastodon/locales'); -const locales = fs.readdirSync(localesJsonPath).filter(filename => { - return /\.json$/.test(filename) && - !/defaultMessages/.test(filename) && - !/whitelist/.test(filename); -}).map(filename => filename.replace(/\.json$/, '')); - -const outPath = path.join(__dirname, '../../tmp/packs'); - -rimraf.sync(outPath); -mkdirp.sync(outPath); - -const outPaths = []; - -locales.forEach(locale => { - const localePath = path.join(outPath, `locale_${locale}.js`); - const baseLocale = locale.split('-')[0]; // e.g. 'zh-TW' -> 'zh' - const localeDataPath = [ - // first try react-intl - `../../node_modules/react-intl/locale-data/${baseLocale}.js`, - // then check locales/locale-data - `../../app/javascript/mastodon/locales/locale-data/${baseLocale}.js`, - // fall back to English (this is what react-intl does anyway) - '../../node_modules/react-intl/locale-data/en.js', - ].filter(filename => fs.existsSync(path.join(outPath, filename))) - .map(filename => filename.replace(/..\/..\/node_modules\//, ''))[0]; - - const localeContent = `// -// locale_${locale}.js -// automatically generated by generateLocalePacks.js -// -import messages from '../../app/javascript/mastodon/locales/${locale}.json'; -import localeData from ${JSON.stringify(localeDataPath)}; -import { setLocale } from '../../app/javascript/mastodon/locales'; -setLocale({messages, localeData}); -`; - fs.writeFileSync(localePath, localeContent, 'utf8'); - outPaths.push(localePath); -}); - -module.exports = outPaths; diff --git a/config/webpack/shared.js b/config/webpack/shared.js index f2f182c566..bb6ae74c33 100644 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@ -9,7 +9,6 @@ const webpack = require('webpack'); const AssetsManifestPlugin = require('webpack-assets-manifest'); const { env, settings, themes, output } = require('./configuration'); -const localePackPaths = require('./generateLocalePacks'); const rules = require('./rules'); const extensionGlob = `**/*{${settings.extensions.join(',')}}*`; @@ -24,11 +23,6 @@ module.exports = { localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry); return localMap; }, {}), - localePackPaths.reduce((map, entry) => { - const localMap = map; - localMap[basename(entry, extname(entry, extname(entry)))] = resolve(entry); - return localMap; - }, {}), Object.keys(themes).reduce((themePaths, name) => { themePaths[name] = resolve(join(settings.source_path, themes[name])); return themePaths; diff --git a/config/webpack/translationRunner.js b/config/webpack/translationRunner.js index 9c684c277f..77534c9de3 100644 --- a/config/webpack/translationRunner.js +++ b/config/webpack/translationRunner.js @@ -1,101 +1,3 @@ -const fs = require('fs'); -const path = require('path'); +console.error("The localisation functionality has been refactored, please see the Localisation section in the development documentation (https://docs.joinmastodon.org/dev/code/#localizations)"); -// eslint-disable-next-line import/order -const { default: manageTranslations } = require('react-intl-translations-manager'); - -const RFC5646_REGEXP = /^[a-z]{2,3}(?:-(?:x|[A-Za-z]{2,4}))*$/; - -const rootDirectory = path.resolve(__dirname, '..', '..'); -const translationsDirectory = path.resolve(rootDirectory, 'app', 'javascript', 'mastodon', 'locales'); -const messagesDirectory = path.resolve(rootDirectory, 'build', 'messages'); -const availableLanguages = fs.readdirSync(translationsDirectory).reduce((languages, filename) => { - const basename = path.basename(filename, '.json'); - if (RFC5646_REGEXP.test(basename)) { - languages.push(basename); - } - return languages; -}, []); - -const testRFC5646 = language => { - if (!RFC5646_REGEXP.test(language)) { - throw new Error('Not RFC5646 name'); - } -}; - -const testAvailability = language => { - if (!availableLanguages.includes(language)) { - throw new Error('Not an available language'); - } -}; - -const validateLanguages = (languages, validators) => { - const invalidLanguages = languages.reduce((acc, language) => { - try { - validators.forEach(validator => validator(language)); - } catch (error) { - acc.push({ language, error }); - } - return acc; - }, []); - - if (invalidLanguages.length > 0) { - console.error(` -Error: Specified invalid LANGUAGES: -${invalidLanguages.map(({ language, error }) => `* ${language}: ${error.message}`).join('\n')} - -Use yarn "manage:translations -- --help" for usage information -`); - process.exit(1); - } -}; - -const usage = `Usage: yarn manage:translations [OPTIONS] [LANGUAGES] - -Manage JavaScript translation files in Mastodon. Generates and update translations in translationsDirectory: ${translationsDirectory} - -LANGUAGES -The RFC5646 language tag for the language you want to test or fix. If you want to input multiple languages, separate them with space. - -Available languages: -${availableLanguages.join(', ')} -`; - -const { argv } = require('yargs') - .usage(usage) - .option('f', { - alias: 'force', - default: false, - describe: 'force using the provided languages. create files if not exists.', - type: 'boolean', - }); - -// check if message directory exists -if (!fs.existsSync(messagesDirectory)) { - console.error(` -Error: messagesDirectory not exists -(${messagesDirectory}) -Try to run "yarn build:development" first`); - process.exit(1); -} - -// determine the languages list -const languages = (argv._.length > 0) ? argv._ : availableLanguages; - -// validate languages -validateLanguages(languages, [ - testRFC5646, - !argv.force && testAvailability, -].filter(Boolean)); - -// manage translations -manageTranslations({ - messagesDirectory, - translationsDirectory, - detectDuplicateIds: false, - singleMessagesFile: true, - languages, - jsonOptions: { - trailingNewline: true, - }, -}); +process.exit(1); diff --git a/package.json b/package.json index b8265a549a..c299bd509b 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build:development": "cross-env RAILS_ENV=development NODE_ENV=development ./bin/webpack", "build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack", "manage:translations": "node ./config/webpack/translationRunner.js", + "i18n:extract": "formatjs extract 'app/javascript/**/*.{js,jsx,ts,tsx}' '--ignore=**/*.d.ts' --out-file app/javascript/mastodon/locales/en.json --format config/formatjs-formatter.js", "start": "node ./streaming/index.js", "test": "${npm_execpath} run test:lint:js && ${npm_execpath} run test:typecheck && ${npm_execpath} run test:jest", "test:lint": "${npm_execpath} run test:lint:js && ${npm_execpath} run test:lint:sass", @@ -34,6 +35,7 @@ "@babel/preset-react": "^7.22.3", "@babel/preset-typescript": "^7.21.5", "@babel/runtime": "^7.22.3", + "@formatjs/intl-pluralrules": "^5.2.2", "@gamestdio/websocket": "^0.3.2", "@github/webauthn-json": "^2.1.1", "@rails/ujs": "^6.1.7", @@ -43,9 +45,9 @@ "autoprefixer": "^10.4.14", "axios": "^1.4.0", "babel-loader": "^8.3.0", + "babel-plugin-formatjs": "^10.5.1", "babel-plugin-lodash": "^3.3.4", "babel-plugin-preval": "^5.1.0", - "babel-plugin-react-intl": "^6.2.0", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "blurhash": "^2.0.5", "classnames": "^2.3.2", @@ -69,9 +71,7 @@ "http-link-header": "^1.1.1", "immutable": "^4.3.0", "imports-loader": "^1.2.0", - "intl": "^1.2.5", - "intl-messageformat": "^2.2.0", - "intl-relativeformat": "^6.4.3", + "intl-messageformat": "^10.3.5", "js-yaml": "^4.1.0", "jsdom": "^22.1.0", "lodash": "^4.17.21", @@ -93,7 +93,7 @@ "react-hotkeys": "^1.1.4", "react-immutable-proptypes": "^2.2.0", "react-immutable-pure-component": "^2.2.2", - "react-intl": "^2.9.0", + "react-intl": "^6.4.2", "react-motion": "^0.5.2", "react-notification": "^6.8.5", "react-overlays": "^5.2.1", @@ -139,6 +139,7 @@ "ws": "^8.12.1" }, "devDependencies": { + "@formatjs/cli": "^6.1.1", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", "@types/babel__core": "^7.20.1", @@ -159,7 +160,6 @@ "@types/react-dom": "^18.2.4", "@types/react-helmet": "^6.1.6", "@types/react-immutable-proptypes": "^2.1.0", - "@types/react-intl": "2.3.18", "@types/react-motion": "^0.0.34", "@types/react-overlays": "^3.1.0", "@types/react-router-dom": "^5.3.3", @@ -193,7 +193,6 @@ "jest-environment-jsdom": "^29.5.0", "lint-staged": "^13.2.2", "prettier": "^2.8.8", - "react-intl-translations-manager": "^5.0.3", "react-test-renderer": "^18.2.0", "stylelint": "^15.6.2", "stylelint-config-standard-scss": "^9.0.0", diff --git a/yarn.lock b/yarn.lock index 2611f068b9..d89d488546 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24,7 +24,7 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.21.4": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== @@ -36,7 +36,28 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.3.tgz#cd502a6a0b6e37d7ad72ce7e71a7160a3ae36f7e" integrity sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ== -"@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.22.1", "@babel/core@^7.7.2": +"@babel/core@^7.10.4", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" + integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.5" + "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helpers" "^7.21.5" + "@babel/parser" "^7.21.8" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/core@^7.22.1": version "7.22.1" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.1.tgz#5de51c5206f4c6f5533562838337a603c1033cfd" integrity sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA== @@ -57,7 +78,7 @@ json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.22.0", "@babel/generator@^7.22.3", "@babel/generator@^7.7.2": +"@babel/generator@^7.21.5", "@babel/generator@^7.22.0", "@babel/generator@^7.22.3", "@babel/generator@^7.7.2": version "7.22.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e" integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A== @@ -90,7 +111,7 @@ "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/types" "^7.19.0" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.1": +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5", "@babel/helper-compilation-targets@^7.22.1": version "7.22.1" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz#bfcd6b7321ffebe33290d68550e2c9d7eb7c7a58" integrity sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ== @@ -164,7 +185,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-environment-visitor@^7.22.1": +"@babel/helper-environment-visitor@^7.21.5", "@babel/helper-environment-visitor@^7.22.1": version "7.22.1" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8" integrity sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA== @@ -333,7 +354,7 @@ "@babel/traverse" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/helpers@^7.22.0": +"@babel/helpers@^7.21.5", "@babel/helpers@^7.22.0": version "7.22.3" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.3.tgz#53b74351da9684ea2f694bf0877998da26dd830e" integrity sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w== @@ -351,7 +372,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.9", "@babel/parser@^7.22.0", "@babel/parser@^7.22.4": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8", "@babel/parser@^7.21.9", "@babel/parser@^7.22.0", "@babel/parser@^7.22.4": version "7.22.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== @@ -460,7 +481,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2": +"@babel/plugin-syntax-jsx@7", "@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== @@ -1135,7 +1156,16 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.21.9", "@babel/template@^7.3.3": +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/template@^7.21.9": version "7.21.9" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== @@ -1144,7 +1174,23 @@ "@babel/parser" "^7.21.9" "@babel/types" "^7.21.5" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.22.1", "@babel/traverse@^7.7.2": +"@babel/traverse@7": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" + integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.5" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.5" + "@babel/types" "^7.21.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@^7.18.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.5", "@babel/traverse@^7.22.1", "@babel/traverse@^7.7.2": version "7.22.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.4.tgz#c3cf96c5c290bd13b55e29d025274057727664c0" integrity sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ== @@ -1160,7 +1206,16 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.12.11", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" + integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== + dependencies: + "@babel/helper-string-parser" "^7.21.5" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4": version "7.22.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071" integrity sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA== @@ -1335,6 +1390,11 @@ dependencies: "@floating-ui/core" "^1.0.1" +"@formatjs/cli@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@formatjs/cli/-/cli-6.1.1.tgz#089d6d25fe96490f8d1401a53705b3cdfefd7afb" + integrity sha512-prUblUQRJwFQqfmBtRWXZFKX+QmhXQkBKRl54hWTCwenskorK6+LTlm9TFbUDhfib2Xt3iDsjk7o9LpeU/AQCw== + "@formatjs/ecma402-abstract@1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.15.0.tgz#0a285a5dc69889e15d53803bd5036272e23e5a18" @@ -1343,6 +1403,13 @@ "@formatjs/intl-localematcher" "0.2.32" tslib "^2.4.0" +"@formatjs/fast-memoize@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.0.1.tgz#f15aaa73caad5562899c69bdcad8db82adcd3b0b" + integrity sha512-M2GgV+qJn5WJQAYewz7q2Cdl6fobQa69S1AzSM2y0P68ZDbK5cWrJIcPCO395Of1ksftGZoOt4LYCO/j9BKBSA== + dependencies: + tslib "^2.4.0" + "@formatjs/icu-messageformat-parser@2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.4.0.tgz#e165f3594c68416ce15f63793768251de2a85f88" @@ -1360,6 +1427,24 @@ "@formatjs/ecma402-abstract" "1.15.0" tslib "^2.4.0" +"@formatjs/intl-displaynames@6.3.2": + version "6.3.2" + resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-6.3.2.tgz#be169393a132eed9ca9c10ccb9d22ab150e24c90" + integrity sha512-kBOh0O7QYKLUqaZujLSEF2+au017plPp63R6Hrokl+oDtLyTt9y9pEuCTbOKh/P8CC9THnDLKRKgeVWZw5Ek8A== + dependencies: + "@formatjs/ecma402-abstract" "1.15.0" + "@formatjs/intl-localematcher" "0.2.32" + tslib "^2.4.0" + +"@formatjs/intl-listformat@7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-7.2.2.tgz#d787932b5d6f1f936c73c5fec531692ab7069c7a" + integrity sha512-YIruRGwUrmgVOXjWi6VbwPcRNBkEfgK2DFjyyqopCmpfJ+39vnl46oLpVchErnuXs6kkARy5GcGaGV7xRsH4lw== + dependencies: + "@formatjs/ecma402-abstract" "1.15.0" + "@formatjs/intl-localematcher" "0.2.32" + tslib "^2.4.0" + "@formatjs/intl-localematcher@0.2.32": version "0.2.32" resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.32.tgz#00d4d307cd7d514b298e15a11a369b86c8933ec1" @@ -1367,17 +1452,27 @@ dependencies: tslib "^2.4.0" -"@formatjs/intl-unified-numberformat@^3.3.3": - version "3.3.6" - resolved "https://registry.yarnpkg.com/@formatjs/intl-unified-numberformat/-/intl-unified-numberformat-3.3.6.tgz#ab69818f7568894023cb31fdb5b5c7eed62c6537" - integrity sha512-VQYswh9Pxf4kN6FQvKprAQwSJrF93eJstCDPM1HIt3c3O6NqPFWNWhZ91PLTppOV11rLYsFK11ZxiGbnLNiPTg== +"@formatjs/intl-pluralrules@^5.2.2": + version "5.2.2" + resolved "https://registry.yarnpkg.com/@formatjs/intl-pluralrules/-/intl-pluralrules-5.2.2.tgz#6322d20a6d0172459e4faf4b0f06603c931673aa" + integrity sha512-mEbnbRzsSCIYqaBmrmUlOsPu5MG6KfMcnzekPzUrUucX2dNiI1KWBGHK6IoXl5c8zx60L1NXJ6cSQ7akoc15SQ== dependencies: - "@formatjs/intl-utils" "^2.2.5" + "@formatjs/ecma402-abstract" "1.15.0" + "@formatjs/intl-localematcher" "0.2.32" + tslib "^2.4.0" -"@formatjs/intl-utils@^2.2.5": - version "2.2.5" - resolved "https://registry.yarnpkg.com/@formatjs/intl-utils/-/intl-utils-2.2.5.tgz#eaafd94df3d102ee13e54e80f992a33868a6b1e8" - integrity sha512-p7gcmazKROteL4IECCp03Qrs790fZ8tbemUAjQu0+K0AaAlK49rI1SIFFq3LzDUAqXIshV95JJhRe/yXxkal5g== +"@formatjs/intl@2.7.2": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.7.2.tgz#83dc77080a984d4883195bed39eedd947ebfd3d7" + integrity sha512-ziiQfnXwY0/rXhtohSAmYMqDjRsihoMKdl8H2aA+FvxG9638E0XrvfBFCb+1HhimNiuqRz5fTY7F/bZtsJxsjA== + dependencies: + "@formatjs/ecma402-abstract" "1.15.0" + "@formatjs/fast-memoize" "2.0.1" + "@formatjs/icu-messageformat-parser" "2.4.0" + "@formatjs/intl-displaynames" "6.3.2" + "@formatjs/intl-listformat" "7.2.2" + intl-messageformat "10.3.5" + tslib "^2.4.0" "@formatjs/ts-transformer@3.13.1": version "3.13.1" @@ -1933,7 +2028,29 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q== -"@types/babel__core@^7.1.12", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.3", "@types/babel__core@^7.20.1": +"@types/babel__core@*", "@types/babel__core@^7.1.7": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" + integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__core@^7.1.12", "@types/babel__core@^7.1.14": + version "7.1.18" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" + integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__core@^7.20.1": version "7.20.1" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b" integrity sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw== @@ -1951,6 +2068,13 @@ dependencies: "@babel/types" "^7.0.0" +"@types/babel__helper-plugin-utils@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@types/babel__helper-plugin-utils/-/babel__helper-plugin-utils-7.10.0.tgz#dcd2416f9c189d5837ab2a276368cf67134efe78" + integrity sha512-60YtHzhQ9HAkToHVV+TB4VLzBn9lrfgrsOjiJMtbv/c1jPdekBxaByd6DMsGBzROXWoIL6U3lEFvvbu69RkUoA== + dependencies: + "@types/babel__core" "*" + "@types/babel__template@*": version "7.0.2" resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" @@ -1966,6 +2090,13 @@ dependencies: "@babel/types" "^7.3.0" +"@types/babel__traverse@^7.1.7": + version "7.18.5" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.5.tgz#c107216842905afafd3b6e774f6f935da6f5db80" + integrity sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q== + dependencies: + "@babel/types" "^7.3.0" + "@types/body-parser@*": version "1.19.2" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" @@ -2253,11 +2384,6 @@ "@types/prop-types" "*" immutable "^3.8.2" -"@types/react-intl@2.3.18": - version "2.3.18" - resolved "https://registry.yarnpkg.com/@types/react-intl/-/react-intl-2.3.18.tgz#fd2d8b7f4d0a1dd05b5f1784ab0d7fe1786a690d" - integrity sha512-DVNJs49zUxKRZng8VuILE886Yihdsf3yLr5vHk9zJrmF8SyRSK3sxNSvikAKxNkv9hX55XBTJShz6CkJnbNjgg== - "@types/react-motion@^0.0.34": version "0.0.34" resolved "https://registry.yarnpkg.com/@types/react-motion/-/react-motion-0.0.34.tgz#789ff2063e2f7fbb6085d291135c442e8b35291a" @@ -2338,7 +2464,16 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@>=16.9.11", "@types/react@^18.0.26", "@types/react@^18.2.7": +"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@>=16.9.11", "@types/react@^18.0.26": + version "18.2.6" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.6.tgz#5cd53ee0d30ffc193b159d3516c8c8ad2f19d571" + integrity sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/react@^18.2.7": version "18.2.7" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.7.tgz#dfb4518042a3117a045b8c222316f83414a783b3" integrity sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw== @@ -2372,11 +2507,6 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== -"@types/schema-utils@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/schema-utils/-/schema-utils-1.0.0.tgz#295d36f01e2cb8bc3207ca1d9a68e210db6b40cb" - integrity sha512-YesPanU1+WCigC/Aj1Mga8UCOjHIfMNHZ3zzDsUY7lI8GlKnh/Kv2QwJOQ+jNQ36Ru7IfzSedlG14hppYaN13A== - "@types/semver@^7.3.12": version "7.3.13" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" @@ -3214,6 +3344,23 @@ babel-loader@^8.3.0: make-dir "^3.1.0" schema-utils "^2.6.5" +babel-plugin-formatjs@^10.5.1: + version "10.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-formatjs/-/babel-plugin-formatjs-10.5.1.tgz#9baeccb590538fb1915ef85fb7dfd13aedd8b1fa" + integrity sha512-IkwrKjl2Zg6br2wuayPIsaPF92RzGgh5WdQj+A/9zokpYeIF7sscZGwwHmeTSoPnIAAENvjRMm/escMQkp+eKg== + dependencies: + "@babel/core" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "7" + "@babel/traverse" "7" + "@babel/types" "^7.12.11" + "@formatjs/icu-messageformat-parser" "2.4.0" + "@formatjs/ts-transformer" "3.13.1" + "@types/babel__core" "^7.1.7" + "@types/babel__helper-plugin-utils" "^7.10.0" + "@types/babel__traverse" "^7.1.7" + tslib "^2.4.0" + babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -3298,19 +3445,6 @@ babel-plugin-preval@^5.1.0: babel-plugin-macros "^3.0.1" require-from-string "^2.0.2" -babel-plugin-react-intl@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-react-intl/-/babel-plugin-react-intl-6.2.0.tgz#ac51ca757f318938792fc91e1747515e9225386a" - integrity sha512-ajGpa14mLzyDgdOS75DRlQ0aEL+q7iSCB77613YUPOZbxnAvfB0wg+gLngbd/43eKRw7a4y+IzO3P8kDHl40nA== - dependencies: - "@babel/core" "^7.7.2" - "@babel/helper-plugin-utils" "^7.0.0" - "@types/babel__core" "^7.1.3" - "@types/schema-utils" "^1.0.0" - fs-extra "^8.1.0" - intl-messageformat-parser "^4.1.1" - schema-utils "^2.2.0" - babel-plugin-transform-react-remove-prop-types@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" @@ -3751,7 +3885,7 @@ chalk@5.2.0: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== -chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -5741,15 +5875,6 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^9.0.1: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -5921,7 +6046,7 @@ glob@^10.2.5, glob@^10.2.6: minipass "^5.0.0 || ^6.0.2" path-scurry "^1.7.0" -glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -6506,48 +6631,17 @@ intersection-observer@^0.12.0: resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.12.2.tgz#4a45349cc0cd91916682b1f44c28d7ec737dc375" integrity sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg== -intl-format-cache@^2.0.5: - version "2.2.9" - resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.2.9.tgz#fb560de20c549cda20b569cf1ffb6dc62b5b93b4" - integrity sha512-Zv/u8wRpekckv0cLkwpVdABYST4hZNTDaX7reFetrYTJwxExR2VyTqQm+l0WmL0Qo8Mjb9Tf33qnfj0T7pjxdQ== - -intl-messageformat-parser@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz#b43d45a97468cadbe44331d74bb1e8dea44fc075" - integrity sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU= - -intl-messageformat-parser@^4.1.1: - version "4.1.4" - resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-4.1.4.tgz#98f3415e6990d44bebf2e0ad8e4cfbacf3ef5ed3" - integrity sha512-zV4kBUD1yhxSyaXm6bGhmP4HFH9Gh4pRQwNn+xq5P+B1dT8mpaAfU75nfUn4HgddIB6pyFnzM5MQjO55UpJwkQ== +intl-messageformat@10.3.5, intl-messageformat@^10.3.5: + version "10.3.5" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.3.5.tgz#f55684fc663e62616ad59d3a504ea0cac3f267b7" + integrity sha512-6kPkftF8Jg3XJCkGKa5OD+nYQ+qcSxF4ZkuDdXZ6KGG0VXn+iblJqRFyDdm9VvKcMyC0Km2+JlVQffFM52D0YA== dependencies: - "@formatjs/intl-unified-numberformat" "^3.3.3" + "@formatjs/ecma402-abstract" "1.15.0" + "@formatjs/fast-memoize" "2.0.1" + "@formatjs/icu-messageformat-parser" "2.4.0" + tslib "^2.4.0" -intl-messageformat@^2.0.0, intl-messageformat@^2.1.0, intl-messageformat@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-2.2.0.tgz#345bcd46de630b7683330c2e52177ff5eab484fc" - integrity sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw= - dependencies: - intl-messageformat-parser "1.4.0" - -intl-relativeformat@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-2.2.0.tgz#6aca95d019ec8d30b6c5653b6629f9983ea5b6c5" - integrity sha512-4bV/7kSKaPEmu6ArxXf9xjv1ny74Zkwuey8Pm01NH4zggPP7JHwg2STk8Y3JdspCKRDriwIyLRfEXnj2ZLr4Bw== - dependencies: - intl-messageformat "^2.0.0" - -intl-relativeformat@^6.4.3: - version "6.4.3" - resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-6.4.3.tgz#cb5559e1e257cc2e763583502012a354bb777efe" - integrity sha512-VxZXZfhuX/zBVfxzE/J6kPUpsyWKYjqtZ3jVGZwr6wzK5BOLVpe1vSlwCQX56w5UjlpL63fS8Nxq0kgTyf1gJA== - -intl@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/intl/-/intl-1.2.5.tgz#82244a2190c4e419f8371f5aa34daa3420e2abde" - integrity sha1-giRKIZDE5Bn4Nx9ao02qNCDiq94= - -invariant@^2.1.1, invariant@^2.2.2, invariant@^2.2.4: +invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -7603,13 +7697,6 @@ json5@^2.1.2, json5@^2.2.0, json5@^2.2.2: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -9587,26 +9674,21 @@ react-immutable-pure-component@^2.2.2: resolved "https://registry.yarnpkg.com/react-immutable-pure-component/-/react-immutable-pure-component-2.2.2.tgz#3014d3e20cd5a7a4db73b81f1f1464f4d351684b" integrity sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A== -react-intl-translations-manager@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/react-intl-translations-manager/-/react-intl-translations-manager-5.0.3.tgz#aee010ecf35975673e033ca5d7d3f4147894324d" - integrity sha512-EfBeugnOGFcdUbQyY9TqBMbuauQ8wm73ZqFr0UqCljhbXl7YDHQcVzclWFRkVmlUffzxitLQFhAZEVVeRNQSwA== +react-intl@^6.4.2: + version "6.4.2" + resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-6.4.2.tgz#cf4f49f5f89e66e0975927783d0d270e708314fd" + integrity sha512-q8QyLZfbyqV3Ifa7vtjRrgfSQPGTR6Fi+u9tP/CuzhUPl9DJEPIrvUFhlBryKtRW2qNASqchaP/79Obip+h6oA== dependencies: - chalk "^2.3.2" - glob "^7.1.2" - json-stable-stringify "^1.0.1" - mkdirp "^0.5.1" - -react-intl@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.9.0.tgz#c97c5d17d4718f1575fdbd5a769f96018a3b1843" - integrity sha512-27jnDlb/d2A7mSJwrbOBnUgD+rPep+abmoJE511Tf8BnoONIAUehy/U1zZCHGO17mnOwMWxqN4qC0nW11cD6rA== - dependencies: - hoist-non-react-statics "^3.3.0" - intl-format-cache "^2.0.5" - intl-messageformat "^2.1.0" - intl-relativeformat "^2.1.0" - invariant "^2.1.1" + "@formatjs/ecma402-abstract" "1.15.0" + "@formatjs/icu-messageformat-parser" "2.4.0" + "@formatjs/intl" "2.7.2" + "@formatjs/intl-displaynames" "6.3.2" + "@formatjs/intl-listformat" "7.2.2" + "@types/hoist-non-react-statics" "^3.3.1" + "@types/react" "16 || 17 || 18" + hoist-non-react-statics "^3.3.2" + intl-messageformat "10.3.5" + tslib "^2.4.0" "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.2.0: version "18.2.0" @@ -10318,7 +10400,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.2.0, schema-utils@^2.6.5: +schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== @@ -11644,11 +11726,6 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - universalify@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" From 69057467cba138d2c9e459f565e88ea5979f61b0 Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Thu, 1 Jun 2023 00:10:21 +0200 Subject: [PATCH 19/69] Translate CW, poll options and media descriptions (#24175) Co-authored-by: Claire --- .../mastodon/actions/importer/normalizer.js | 38 ++- app/javascript/mastodon/actions/statuses.js | 3 +- .../mastodon/components/media_attachments.jsx | 15 +- .../mastodon/components/media_gallery.jsx | 12 +- app/javascript/mastodon/components/poll.jsx | 12 +- app/javascript/mastodon/components/status.jsx | 30 ++- .../mastodon/components/status_content.jsx | 20 +- .../mastodon/containers/status_container.jsx | 2 +- .../status/components/detailed_status.jsx | 14 +- .../mastodon/features/status/index.jsx | 2 +- .../features/ui/components/audio_modal.jsx | 12 +- .../features/ui/components/media_modal.jsx | 7 +- .../features/ui/components/video_modal.jsx | 12 +- app/javascript/mastodon/reducers/polls.js | 29 +++ app/javascript/mastodon/reducers/statuses.js | 26 +- app/lib/emoji_formatter.rb | 11 +- app/lib/translation_service/deepl.rb | 19 +- .../translation_service/libre_translate.rb | 19 +- app/models/translation.rb | 14 ++ .../rest/translation_serializer.rb | 35 ++- app/services/translate_status_service.rb | 83 ++++++- .../statuses/translations_controller_spec.rb | 2 +- spec/lib/translation_service/deepl_spec.rb | 26 +- .../libre_translate_spec.rb | 34 ++- .../services/translate_status_service_spec.rb | 226 ++++++++++++++++++ 25 files changed, 603 insertions(+), 100 deletions(-) create mode 100644 app/models/translation.rb create mode 100644 spec/services/translate_status_service_spec.rb diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index 61062fd2c8..3232e12a2b 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -6,7 +6,7 @@ import { unescapeHTML } from '../../utils/html'; const domParser = new DOMParser(); -const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => { +const makeEmojiMap = emojis => emojis.reduce((obj, emoji) => { obj[`:${emoji.shortcode}:`] = emoji; return obj; }, {}); @@ -20,7 +20,7 @@ export function searchTextFromRawStatus (status) { export function normalizeAccount(account) { account = { ...account }; - const emojiMap = makeEmojiMap(account); + const emojiMap = makeEmojiMap(account.emojis); const displayName = account.display_name.trim().length === 0 ? account.username : account.display_name; account.display_name_html = emojify(escapeTextContentForBrowser(displayName), emojiMap); @@ -86,7 +86,7 @@ export function normalizeStatus(status, normalOldStatus) { const spoilerText = normalStatus.spoiler_text || ''; const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(//g, '\n').replace(/<\/p>

/g, '\n\n'); - const emojiMap = makeEmojiMap(normalStatus); + const emojiMap = makeEmojiMap(normalStatus.emojis); normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent; normalStatus.contentHtml = emojify(normalStatus.content, emojiMap); @@ -97,22 +97,48 @@ export function normalizeStatus(status, normalOldStatus) { return normalStatus; } +export function normalizeStatusTranslation(translation, status) { + const emojiMap = makeEmojiMap(status.get('emojis').toJS()); + + const normalTranslation = { + detected_source_language: translation.detected_source_language, + language: translation.language, + provider: translation.provider, + contentHtml: emojify(translation.content, emojiMap), + spoilerHtml: emojify(escapeTextContentForBrowser(translation.spoiler_text), emojiMap), + spoiler_text: translation.spoiler_text, + }; + + return normalTranslation; +} + export function normalizePoll(poll) { const normalPoll = { ...poll }; - const emojiMap = makeEmojiMap(normalPoll); + const emojiMap = makeEmojiMap(poll.emojis); normalPoll.options = poll.options.map((option, index) => ({ ...option, voted: poll.own_votes && poll.own_votes.includes(index), - title_emojified: emojify(escapeTextContentForBrowser(option.title), emojiMap), + titleHtml: emojify(escapeTextContentForBrowser(option.title), emojiMap), })); return normalPoll; } +export function normalizePollOptionTranslation(translation, poll) { + const emojiMap = makeEmojiMap(poll.get('emojis').toJS()); + + const normalTranslation = { + ...translation, + titleHtml: emojify(escapeTextContentForBrowser(translation.title), emojiMap), + }; + + return normalTranslation; +} + export function normalizeAnnouncement(announcement) { const normalAnnouncement = { ...announcement }; - const emojiMap = makeEmojiMap(normalAnnouncement); + const emojiMap = makeEmojiMap.emojis(normalAnnouncement); normalAnnouncement.contentHtml = emojify(normalAnnouncement.content, emojiMap); diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index 84a1271b8b..3aed807358 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -343,7 +343,8 @@ export const translateStatusFail = (id, error) => ({ error, }); -export const undoStatusTranslation = id => ({ +export const undoStatusTranslation = (id, pollId) => ({ type: STATUS_TRANSLATE_UNDO, id, + pollId, }); diff --git a/app/javascript/mastodon/components/media_attachments.jsx b/app/javascript/mastodon/components/media_attachments.jsx index d2f1712437..7b945a0ea2 100644 --- a/app/javascript/mastodon/components/media_attachments.jsx +++ b/app/javascript/mastodon/components/media_attachments.jsx @@ -51,8 +51,9 @@ export default class MediaAttachments extends ImmutablePureComponent { }; render () { - const { status, lang, width, height } = this.props; + const { status, width, height } = this.props; const mediaAttachments = status.get('media_attachments'); + const language = status.getIn(['language', 'translation']) || status.get('language') || this.props.lang; if (mediaAttachments.size === 0) { return null; @@ -60,14 +61,15 @@ export default class MediaAttachments extends ImmutablePureComponent { if (mediaAttachments.getIn([0, 'type']) === 'audio') { const audio = mediaAttachments.get(0); + const description = audio.getIn(['translation', 'description']) || audio.get('description'); return ( {Component => ( @@ -90,8 +93,8 @@ export default class MediaAttachments extends ImmutablePureComponent { frameRate={video.getIn(['meta', 'original', 'frame_rate'])} blurhash={video.get('blurhash')} src={video.get('url')} - alt={video.get('description')} - lang={lang || status.get('language')} + alt={description} + lang={language} width={width} height={height} inline @@ -107,7 +110,7 @@ export default class MediaAttachments extends ImmutablePureComponent { {Component => ( ALT); } + const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); + if (attachment.get('type') === 'unknown') { return (

- +
{mentionsPlaceholder} -
+
{!hidden && poll} - {!hidden && translateButton} + {translateButton}
); } else if (this.props.onClick) { return ( <>
-
+
{poll} {translateButton} @@ -303,7 +303,7 @@ class StatusContent extends PureComponent { } else { return (
-
+
{poll} {translateButton} diff --git a/app/javascript/mastodon/containers/status_container.jsx b/app/javascript/mastodon/containers/status_container.jsx index 3026dde0a8..6167b404f0 100644 --- a/app/javascript/mastodon/containers/status_container.jsx +++ b/app/javascript/mastodon/containers/status_container.jsx @@ -180,7 +180,7 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({ onTranslate (status) { if (status.get('translation')) { - dispatch(undoStatusTranslation(status.get('id'))); + dispatch(undoStatusTranslation(status.get('id'), status.get('poll'))); } else { dispatch(translateStatus(status.get('id'))); } diff --git a/app/javascript/mastodon/features/status/components/detailed_status.jsx b/app/javascript/mastodon/features/status/components/detailed_status.jsx index 187e04ad17..83a566710d 100644 --- a/app/javascript/mastodon/features/status/components/detailed_status.jsx +++ b/app/javascript/mastodon/features/status/components/detailed_status.jsx @@ -133,17 +133,20 @@ class DetailedStatus extends ImmutablePureComponent { outerStyle.height = `${this.state.height}px`; } + const language = status.getIn(['translation', 'language']) || status.get('language'); + if (pictureInPicture.get('inUse')) { media = ; } else if (status.get('media_attachments').size > 0) { if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { const attachment = status.getIn(['media_attachments', 0]); + const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); media = (
-
+
- {!minimal && <> {verification} {muteTimeRemaining}} + {!minimal && ( +
+ {verification} {muteTimeRemaining} +
+ )}
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 45e7f7e7b0..6c76ddd4dd 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -7814,13 +7814,28 @@ noscript { } } +.account__contents { + overflow: hidden; +} + +.account__details { + display: flex; + flex-wrap: wrap; + column-gap: 1em; +} + .verified-badge { display: inline-flex; align-items: center; color: $valid-value-color; gap: 4px; overflow: hidden; - text-overflow: ellipsis; + white-space: nowrap; + + > span { + overflow: hidden; + text-overflow: ellipsis; + } a { color: inherit; From b0780cfeeda641645ea65da257a72ec507e71647 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Fri, 2 Jun 2023 15:00:27 +0200 Subject: [PATCH 43/69] Fix `/share` and cleanup and reorganize frontend locale loading (#25240) --- .prettierignore | 2 +- app/helpers/react_component_helper.rb | 2 +- app/javascript/mastodon/actions/streaming.js | 9 +- .../mastodon/containers/admin_component.jsx | 11 +- .../mastodon/containers/compose_container.jsx | 16 +- .../mastodon/containers/mastodon.jsx | 14 +- .../mastodon/containers/media_container.jsx | 11 +- app/javascript/mastodon/load_locale.js | 14 -- .../mastodon/locales/global_locale.ts | 22 ++ app/javascript/mastodon/locales/index.js | 22 -- app/javascript/mastodon/locales/index.ts | 5 + .../mastodon/locales/intl_provider.tsx | 56 +++++ .../mastodon/locales/load_locale.ts | 29 +++ .../mastodon/locales/locale-data/README.md | 221 ------------------ .../mastodon/locales/locale-data/co.js | 110 --------- .../mastodon/locales/locale-data/oc.js | 110 --------- .../mastodon/locales/locale-data/sa.js | 98 -------- app/javascript/packs/admin.jsx | 4 +- app/javascript/packs/application.js | 17 +- app/javascript/packs/public.jsx | 3 +- app/javascript/packs/share.jsx | 3 +- jest.config.js | 1 - package.json | 1 + spec/helpers/react_component_helper_spec.rb | 2 +- yarn.lock | 7 + 25 files changed, 152 insertions(+), 638 deletions(-) delete mode 100644 app/javascript/mastodon/load_locale.js create mode 100644 app/javascript/mastodon/locales/global_locale.ts delete mode 100644 app/javascript/mastodon/locales/index.js create mode 100644 app/javascript/mastodon/locales/index.ts create mode 100644 app/javascript/mastodon/locales/intl_provider.tsx create mode 100644 app/javascript/mastodon/locales/load_locale.ts delete mode 100644 app/javascript/mastodon/locales/locale-data/README.md delete mode 100644 app/javascript/mastodon/locales/locale-data/co.js delete mode 100644 app/javascript/mastodon/locales/locale-data/oc.js delete mode 100644 app/javascript/mastodon/locales/locale-data/sa.js diff --git a/.prettierignore b/.prettierignore index 2ea4075333..91029f665d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -61,7 +61,7 @@ docker-compose.override.yml /app/javascript/mastodon/features/emoji/emoji_map.json # Ignore locale files -/app/javascript/mastodon/locales +/app/javascript/mastodon/locales/*.json /config/locales # Ignore vendored CSS reset diff --git a/app/helpers/react_component_helper.rb b/app/helpers/react_component_helper.rb index fc08de13dd..ce616e8306 100644 --- a/app/helpers/react_component_helper.rb +++ b/app/helpers/react_component_helper.rb @@ -11,7 +11,7 @@ module ReactComponentHelper end def react_admin_component(name, props = {}) - data = { 'admin-component': name.to_s.camelcase, props: Oj.dump({ locale: I18n.locale }.merge(props)) } + data = { 'admin-component': name.to_s.camelcase, props: Oj.dump(props) } div_tag_with_data(data) end diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js index 562e72655c..9daeb3c60f 100644 --- a/app/javascript/mastodon/actions/streaming.js +++ b/app/javascript/mastodon/actions/streaming.js @@ -24,8 +24,6 @@ import { fillListTimelineGaps, } from './timelines'; -const { messages } = getLocale(); - /** * @param {number} max * @returns {number} @@ -43,8 +41,10 @@ const randomUpTo = max => * @param {function(object): boolean} [options.accept] * @returns {function(): void} */ -export const connectTimelineStream = (timelineId, channelName, params = {}, options = {}) => - connectStream(channelName, params, (dispatch, getState) => { +export const connectTimelineStream = (timelineId, channelName, params = {}, options = {}) => { + const { messages } = getLocale(); + + return connectStream(channelName, params, (dispatch, getState) => { const locale = getState().getIn(['meta', 'locale']); // @ts-expect-error @@ -121,6 +121,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti }, }; }); +}; /** * @param {Function} dispatch diff --git a/app/javascript/mastodon/containers/admin_component.jsx b/app/javascript/mastodon/containers/admin_component.jsx index 562151fe24..7400111293 100644 --- a/app/javascript/mastodon/containers/admin_component.jsx +++ b/app/javascript/mastodon/containers/admin_component.jsx @@ -1,24 +1,19 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { IntlProvider } from 'react-intl'; - -import { getLocale, onProviderError } from '../locales'; - -const { messages } = getLocale(); +import { IntlProvider } from 'mastodon/locales'; export default class AdminComponent extends PureComponent { static propTypes = { - locale: PropTypes.string.isRequired, children: PropTypes.node.isRequired, }; render () { - const { locale, children } = this.props; + const { children } = this.props; return ( - + {children} ); diff --git a/app/javascript/mastodon/containers/compose_container.jsx b/app/javascript/mastodon/containers/compose_container.jsx index 751015d18d..f76550678e 100644 --- a/app/javascript/mastodon/containers/compose_container.jsx +++ b/app/javascript/mastodon/containers/compose_container.jsx @@ -1,18 +1,14 @@ -import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { IntlProvider } from 'react-intl'; - import { Provider } from 'react-redux'; import { fetchCustomEmojis } from '../actions/custom_emojis'; import { hydrateStore } from '../actions/store'; import Compose from '../features/standalone/compose'; import initialState from '../initial_state'; -import { getLocale, onProviderError } from '../locales'; +import { IntlProvider } from '../locales'; import { store } from '../store'; -const { messages } = getLocale(); if (initialState) { store.dispatch(hydrateStore(initialState)); @@ -20,17 +16,11 @@ if (initialState) { store.dispatch(fetchCustomEmojis()); -export default class TimelineContainer extends PureComponent { - - static propTypes = { - locale: PropTypes.string.isRequired, - }; +export default class ComposeContainer extends PureComponent { render () { - const { locale } = this.props; - return ( - + diff --git a/app/javascript/mastodon/containers/mastodon.jsx b/app/javascript/mastodon/containers/mastodon.jsx index c4d4611a2d..4538db050d 100644 --- a/app/javascript/mastodon/containers/mastodon.jsx +++ b/app/javascript/mastodon/containers/mastodon.jsx @@ -1,8 +1,6 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { IntlProvider } from 'react-intl'; - import { Helmet } from 'react-helmet'; import { BrowserRouter, Route } from 'react-router-dom'; @@ -16,11 +14,9 @@ import { connectUserStream } from 'mastodon/actions/streaming'; import ErrorBoundary from 'mastodon/components/error_boundary'; import UI from 'mastodon/features/ui'; import initialState, { title as siteTitle } from 'mastodon/initial_state'; -import { getLocale, onProviderError } from 'mastodon/locales'; +import { IntlProvider } from 'mastodon/locales'; import { store } from 'mastodon/store'; -const { messages } = getLocale(); - const title = process.env.NODE_ENV === 'production' ? siteTitle : `${siteTitle} (Dev)`; const hydrateAction = hydrateStore(initialState); @@ -40,10 +36,6 @@ const createIdentityContext = state => ({ export default class Mastodon extends PureComponent { - static propTypes = { - locale: PropTypes.string.isRequired, - }; - static childContextTypes = { identity: PropTypes.shape({ signedIn: PropTypes.bool.isRequired, @@ -79,10 +71,8 @@ export default class Mastodon extends PureComponent { } render () { - const { locale } = this.props; - return ( - + diff --git a/app/javascript/mastodon/containers/media_container.jsx b/app/javascript/mastodon/containers/media_container.jsx index 84eab1cae1..fba3c5df78 100644 --- a/app/javascript/mastodon/containers/media_container.jsx +++ b/app/javascript/mastodon/containers/media_container.jsx @@ -2,8 +2,6 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; import { createPortal } from 'react-dom'; -import { IntlProvider } from 'react-intl'; - import { fromJS } from 'immutable'; import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag'; @@ -14,17 +12,14 @@ import Audio from 'mastodon/features/audio'; import Card from 'mastodon/features/status/components/card'; import MediaModal from 'mastodon/features/ui/components/media_modal'; import Video from 'mastodon/features/video'; -import { getLocale, onProviderError } from 'mastodon/locales'; +import { IntlProvider } from 'mastodon/locales'; import { getScrollbarWidth } from 'mastodon/utils/scrollbar'; -const { messages } = getLocale(); - const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll, Hashtag, Audio }; export default class MediaContainer extends PureComponent { static propTypes = { - locale: PropTypes.string.isRequired, components: PropTypes.object.isRequired, }; @@ -73,7 +68,7 @@ export default class MediaContainer extends PureComponent { }; render () { - const { locale, components } = this.props; + const { components } = this.props; let handleOpenVideo; @@ -83,7 +78,7 @@ export default class MediaContainer extends PureComponent { } return ( - + <> {[].map.call(components, (component, i) => { const componentName = component.getAttribute('data-component'); diff --git a/app/javascript/mastodon/load_locale.js b/app/javascript/mastodon/load_locale.js deleted file mode 100644 index cb14acd622..0000000000 --- a/app/javascript/mastodon/load_locale.js +++ /dev/null @@ -1,14 +0,0 @@ -import { setLocale } from "./locales"; - -export async function loadLocale() { - const locale = document.querySelector('html').lang || 'en'; - - const localeData = await import( - /* webpackMode: "lazy" */ - /* webpackChunkName: "locale/[request]" */ - /* webpackInclude: /\.json$/ */ - /* webpackPreload: true */ - `mastodon/locales/${locale}.json`); - - setLocale({ messages: localeData }); -} diff --git a/app/javascript/mastodon/locales/global_locale.ts b/app/javascript/mastodon/locales/global_locale.ts new file mode 100644 index 0000000000..01133ca239 --- /dev/null +++ b/app/javascript/mastodon/locales/global_locale.ts @@ -0,0 +1,22 @@ +export interface LocaleData { + locale: string; + messages: Record; +} + +let loadedLocale: LocaleData; + +export function setLocale(locale: LocaleData) { + loadedLocale = locale; +} + +export function getLocale() { + if (!loadedLocale && process.env.NODE_ENV === 'development') { + throw new Error('getLocale() called before any locale has been set'); + } + + return loadedLocale; +} + +export function isLocaleLoaded() { + return !!loadedLocale; +} diff --git a/app/javascript/mastodon/locales/index.js b/app/javascript/mastodon/locales/index.js deleted file mode 100644 index 6e57e3ddc4..0000000000 --- a/app/javascript/mastodon/locales/index.js +++ /dev/null @@ -1,22 +0,0 @@ -let theLocale; - -export function setLocale(locale) { - theLocale = locale; -} - -export function getLocale() { - return theLocale; -} - -export function onProviderError(error) { - // Silent the error, like upstream does - if(process.env.NODE_ENV === 'production') return; - - // This browser does not advertise Intl support for this locale, we only print a warning - // As-per the spec, the browser should select the best matching locale - if(typeof error === "object" && error.message.match("MISSING_DATA")) { - console.warn(error.message); - } - - console.error(error); -} diff --git a/app/javascript/mastodon/locales/index.ts b/app/javascript/mastodon/locales/index.ts new file mode 100644 index 0000000000..63f45c3047 --- /dev/null +++ b/app/javascript/mastodon/locales/index.ts @@ -0,0 +1,5 @@ +export type { LocaleData } from './global_locale'; +export { setLocale, getLocale, isLocaleLoaded } from './global_locale'; +export { loadLocale } from './load_locale'; + +export { IntlProvider } from './intl_provider'; diff --git a/app/javascript/mastodon/locales/intl_provider.tsx b/app/javascript/mastodon/locales/intl_provider.tsx new file mode 100644 index 0000000000..1ea77c798e --- /dev/null +++ b/app/javascript/mastodon/locales/intl_provider.tsx @@ -0,0 +1,56 @@ +import { useEffect, useState } from 'react'; + +import { IntlProvider as BaseIntlProvider } from 'react-intl'; + +import { getLocale, isLocaleLoaded } from './global_locale'; +import { loadLocale } from './load_locale'; + +function onProviderError(error: unknown) { + // Silent the error, like upstream does + if (process.env.NODE_ENV === 'production') return; + + // This browser does not advertise Intl support for this locale, we only print a warning + // As-per the spec, the browser should select the best matching locale + if ( + error && + typeof error === 'object' && + error instanceof Error && + error.message.match('MISSING_DATA') + ) { + console.warn(error.message); + } + + console.error(error); +} + +export const IntlProvider: React.FC< + Omit, 'locale' | 'messages'> +> = ({ children, ...props }) => { + const [localeLoaded, setLocaleLoaded] = useState(false); + + useEffect(() => { + async function loadLocaleData() { + if (!isLocaleLoaded()) { + await loadLocale(); + } + + setLocaleLoaded(true); + } + void loadLocaleData(); + }, []); + + if (!localeLoaded) return null; + + const { locale, messages } = getLocale(); + + return ( + + {children} + + ); +}; diff --git a/app/javascript/mastodon/locales/load_locale.ts b/app/javascript/mastodon/locales/load_locale.ts new file mode 100644 index 0000000000..8a69123174 --- /dev/null +++ b/app/javascript/mastodon/locales/load_locale.ts @@ -0,0 +1,29 @@ +import { Semaphore } from 'async-mutex'; + +import type { LocaleData } from './global_locale'; +import { isLocaleLoaded, setLocale } from './global_locale'; + +const localeLoadingSemaphore = new Semaphore(1); + +export async function loadLocale() { + const locale = document.querySelector('html')?.lang || 'en'; + + // We use a Semaphore here so only one thing can try to load the locales at + // the same time. If one tries to do it while its in progress, it will wait + // for the initial load to finish before it is resumed (and will see that locale + // data is already loaded) + await localeLoadingSemaphore.runExclusive(async () => { + // if the locale is already set, then do nothing + if (isLocaleLoaded()) return; + + const localeData = (await import( + /* webpackMode: "lazy" */ + /* webpackChunkName: "locale/[request]" */ + /* webpackInclude: /\.json$/ */ + /* webpackPreload: true */ + `mastodon/locales/${locale}.json` + )) as LocaleData['messages']; + + setLocale({ messages: localeData, locale }); + }); +} diff --git a/app/javascript/mastodon/locales/locale-data/README.md b/app/javascript/mastodon/locales/locale-data/README.md deleted file mode 100644 index 83368fae7d..0000000000 --- a/app/javascript/mastodon/locales/locale-data/README.md +++ /dev/null @@ -1,221 +0,0 @@ -# Custom Locale Data - -This folder is used to store custom locale data. These custom locale data are -not yet provided by [Unicode Common Locale Data Repository](http://cldr.unicode.org/development/new-cldr-developers) -and hence not provided in [react-intl/locale-data/*](https://github.com/yahoo/react-intl). - -The locale data should support [Locale Data APIs](https://github.com/yahoo/react-intl/wiki/API#locale-data-apis) -of the react-intl library. - -It is recommended to start your custom locale data from this sample English -locale data ([*](#plural-rules)): - -```javascript -/*eslint eqeqeq: "off"*/ -/*eslint no-nested-ternary: "off"*/ - -export default [ - { - locale: "en", - pluralRuleFunction: function(e, a) { - var n = String(e).split("."), - l = !n[1], - o = Number(n[0]) == e, - t = o && n[0].slice(-1), - r = o && n[0].slice(-2); - return a ? 1 == t && 11 != r ? "one" : 2 == t && 12 != r ? "two" : 3 == t && 13 != r ? "few" : "other" : 1 == e && l ? "one" : "other" - }, - fields: { - year: { - displayName: "year", - relative: { - 0: "this year", - 1: "next year", - "-1": "last year" - }, - relativeTime: { - future: { - one: "in {0} year", - other: "in {0} years" - }, - past: { - one: "{0} year ago", - other: "{0} years ago" - } - } - }, - month: { - displayName: "month", - relative: { - 0: "this month", - 1: "next month", - "-1": "last month" - }, - relativeTime: { - future: { - one: "in {0} month", - other: "in {0} months" - }, - past: { - one: "{0} month ago", - other: "{0} months ago" - } - } - }, - day: { - displayName: "day", - relative: { - 0: "today", - 1: "tomorrow", - "-1": "yesterday" - }, - relativeTime: { - future: { - one: "in {0} day", - other: "in {0} days" - }, - past: { - one: "{0} day ago", - other: "{0} days ago" - } - } - }, - hour: { - displayName: "hour", - relativeTime: { - future: { - one: "in {0} hour", - other: "in {0} hours" - }, - past: { - one: "{0} hour ago", - other: "{0} hours ago" - } - } - }, - minute: { - displayName: "minute", - relativeTime: { - future: { - one: "in {0} minute", - other: "in {0} minutes" - }, - past: { - one: "{0} minute ago", - other: "{0} minutes ago" - } - } - }, - second: { - displayName: "second", - relative: { - 0: "now" - }, - relativeTime: { - future: { - one: "in {0} second", - other: "in {0} seconds" - }, - past: { - one: "{0} second ago", - other: "{0} seconds ago" - } - } - } - } - } -] - -``` - -## Notes - -### Plural Rules - -The function `pluralRuleFunction()` should return the key to proper string of -a plural form(s). The purpose of the function is to provide key of translate -strings of correct plural form according. The different forms are described in -[CLDR's Plural Rules][cldr-plural-rules], - -[cldr-plural-rules]: http://cldr.unicode.org/index/cldr-spec/plural-rules - -#### Quick Overview on CLDR Rules - -Let's take English as an example. - -When you describe a number, you can be either describe it as: -* Cardinals: 1st, 2nd, 3rd ... 11th, 12th ... 21st, 22nd, 23nd .... -* Ordinals: 1, 2, 3 ... - -In any of these cases, the nouns will reflect the number with singular or plural -form. For example: -* in 0 days -* in 1 day -* in 2 days - -The `pluralRuleFunction` receives 2 parameters: -* `e`: a string representation of the number. Such as, "`1`", "`2`", "`2.1`". -* `a`: `true` if this is "cardinal" type of description. `false` for ordinal and other case. - -#### How you should write `pluralRuleFunction` - -The first rule to write pluralRuleFunction is never translate the output string -into your language. [Plural Rules][cldr-plural-rules] specified you should use -these as the return values: - - * "`zero`" - * "`one`" (singular) - * "`two`" (dual) - * "`few`" (paucal) - * "`many`" (also used for fractions if they have a separate class) - * "`other`" (required—general plural form—also used if the language only has a single form) - -Again, we'll use English as the example here. - -Let's read the `return` statement in the pluralRuleFunction above: -```javascript - return a ? 1 == t && 11 != r ? "one" : 2 == t && 12 != r ? "two" : 3 == t && 13 != r ? "few" : "other" : 1 == e && l ? "one" : "other" -``` - -This nested ternary is hard to read. It basically means: -```javascript -// e: the number variable to examine -// a: "true" if cardinals -// l: "true" if the variable e has nothin after decimal mark (e.g. "1.0" would be false) -// o: "true" if the variable e is an integer -// t: the "ones" of the number. e.g. "3" for number "9123" -// r: the "ones" and "tens" of the number. e.g. "23" for number "9123" -if (a == true) { - if (t == 1 && r != 11) { - return "one"; // i.e. 1st, 21st, 101st, 121st ... - } else if (t == 2 && r != 12) { - return "two"; // i.e. 2nd, 22nd, 102nd, 122nd ... - } else if (t == 3 && r != 13) { - return "few"; // i.e. 3rd, 23rd, 103rd, 123rd ... - } else { - return "other"; // i.e. 4th, 11th, 12th, 24th ... - } -} else { - if (e == 1 && l) { - return "one"; // i.e. 1 day - } else { - return "other"; // i.e. 0 days, 2 days, 3 days - } -} -``` - -If your language, like French, do not have complicated cardinal rules, you may -use the French's version of it: -```javascript -function (e, a) { - return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other"; -} -``` - -If your language, like Chinese, do not have any pluralization rule at all you -may use the Chinese's version of it: -```javascript -function (e, a) { - return "other"; -} -``` diff --git a/app/javascript/mastodon/locales/locale-data/co.js b/app/javascript/mastodon/locales/locale-data/co.js deleted file mode 100644 index dff8a54dac..0000000000 --- a/app/javascript/mastodon/locales/locale-data/co.js +++ /dev/null @@ -1,110 +0,0 @@ -/*eslint eqeqeq: "off"*/ -/*eslint no-nested-ternary: "off"*/ -/*eslint quotes: "off"*/ - -const rules = [{ - locale: "co", - pluralRuleFunction: function (e, a) { - return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other"; - }, - fields: { - year: { - displayName: "annu", - relative: { - 0: "quist'annu", - 1: "l'annu chì vene", - "-1": "l'annu passatu", - }, - relativeTime: { - future: { - one: "in {0} annu", - other: "in {0} anni", - }, - past: { - one: "{0} annu fà", - other: "{0} anni fà", - }, - }, - }, - month: { - displayName: "mese", - relative: { - 0: "Questu mese", - 1: "u mese chì vene", - "-1": "u mese passatu", - }, - relativeTime: { - future: { - one: "in {0} mese", - other: "in {0} mesi", - }, - past: { - one: "{0} mese fà", - other: "{0} mesi fà", - }, - }, - }, - day: { - displayName: "ghjornu", - relative: { - 0: "oghje", - 1: "dumane", - "-1": "eri", - }, - relativeTime: { - future: { - one: "in {0} ghjornu", - other: "in {0} ghjornu", - }, - past: { - one: "{0} ghjornu fà", - other: "{0} ghjorni fà", - }, - }, - }, - hour: { - displayName: "ora", - relativeTime: { - future: { - one: "in {0} ora", - other: "in {0} ore", - }, - past: { - one: "{0} ora fà", - other: "{0} ore fà", - }, - }, - }, - minute: { - displayName: "minuta", - relativeTime: { - future: { - one: "in {0} minuta", - other: "in {0} minute", - }, - past: { - one: "{0} minuta fà", - other: "{0} minute fà", - }, - }, - }, - second: { - displayName: "siconda", - relative: { - 0: "avà", - }, - relativeTime: { - future: { - one: "in {0} siconda", - other: "in {0} siconde", - }, - past: { - one: "{0} siconda fà", - other: "{0} siconde fà", - }, - }, - }, - }, -}]; - -export default rules; diff --git a/app/javascript/mastodon/locales/locale-data/oc.js b/app/javascript/mastodon/locales/locale-data/oc.js deleted file mode 100644 index 6ab306b8cf..0000000000 --- a/app/javascript/mastodon/locales/locale-data/oc.js +++ /dev/null @@ -1,110 +0,0 @@ -/*eslint eqeqeq: "off"*/ -/*eslint no-nested-ternary: "off"*/ -/*eslint quotes: "off"*/ - -const rules = [{ - locale: "oc", - pluralRuleFunction: function (e, a) { - return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other"; - }, - fields: { - year: { - displayName: "an", - relative: { - 0: "ongan", - 1: "l'an que ven", - "-1": "l'an passat", - }, - relativeTime: { - future: { - one: "d’aquí {0} an", - other: "d’aquí {0} ans", - }, - past: { - one: "fa {0} an", - other: "fa {0} ans", - }, - }, - }, - month: { - displayName: "mes", - relative: { - 0: "aqueste mes", - 1: "lo mes que ven", - "-1": "lo mes passat", - }, - relativeTime: { - future: { - one: "d’aquí {0} mes", - other: "d’aquí {0} meses", - }, - past: { - one: "fa {0} mes", - other: "fa {0} meses", - }, - }, - }, - day: { - displayName: "jorn", - relative: { - 0: "uèi", - 1: "deman", - "-1": "ièr", - }, - relativeTime: { - future: { - one: "d’aquí {0} jorn", - other: "d’aquí {0} jorns", - }, - past: { - one: "fa {0} jorn", - other: "fa {0} jorns", - }, - }, - }, - hour: { - displayName: "ora", - relativeTime: { - future: { - one: "d’aquí {0} ora", - other: "d’aquí {0} oras", - }, - past: { - one: "fa {0} ora", - other: "fa {0} oras", - }, - }, - }, - minute: { - displayName: "minuta", - relativeTime: { - future: { - one: "d’aquí {0} minuta", - other: "d’aquí {0} minutas", - }, - past: { - one: "fa {0} minuta", - other: "fa {0} minutas", - }, - }, - }, - second: { - displayName: "segonda", - relative: { - 0: "ara", - }, - relativeTime: { - future: { - one: "d’aquí {0} segonda", - other: "d’aquí {0} segondas", - }, - past: { - one: "fa {0} segonda", - other: "fa {0} segondas", - }, - }, - }, - }, -}]; - -export default rules; diff --git a/app/javascript/mastodon/locales/locale-data/sa.js b/app/javascript/mastodon/locales/locale-data/sa.js deleted file mode 100644 index 65e09e97f2..0000000000 --- a/app/javascript/mastodon/locales/locale-data/sa.js +++ /dev/null @@ -1,98 +0,0 @@ -/*eslint eqeqeq: "off"*/ -/*eslint no-nested-ternary: "off"*/ -/*eslint quotes: "off"*/ -/*eslint comma-dangle: "off"*/ - -const rules = [ - { - locale: "sa", - fields: { - year: { - displayName: "year", - relative: { - 0: "this year", - 1: "next year", - "-1": "last year" - }, - relativeTime: { - future: { - other: "+{0} y" - }, - past: { - other: "-{0} y" - } - } - }, - month: { - displayName: "month", - relative: { - 0: "this month", - 1: "next month", - "-1": "last month" - }, - relativeTime: { - future: { - other: "+{0} m" - }, - past: { - other: "-{0} m" - } - } - }, - day: { - displayName: "day", - relative: { - 0: "अद्य", - 1: "श्वः", - "-1": "गतदिनम्" - }, - relativeTime: { - future: { - other: "+{0} d" - }, - past: { - other: "-{0} d" - } - } - }, - hour: { - displayName: "hour", - relativeTime: { - future: { - other: "+{0} h" - }, - past: { - other: "-{0} h" - } - } - }, - minute: { - displayName: "minute", - relativeTime: { - future: { - other: "+{0} min" - }, - past: { - other: "-{0} min" - } - } - }, - second: { - displayName: "second", - relative: { - 0: "now" - }, - relativeTime: { - future: { - other: "+{0} s" - }, - past: { - other: "-{0} s" - } - } - } - } - } -]; - -export default rules; diff --git a/app/javascript/packs/admin.jsx b/app/javascript/packs/admin.jsx index 9bb4d4dbf3..ebcc6903f8 100644 --- a/app/javascript/packs/admin.jsx +++ b/app/javascript/packs/admin.jsx @@ -229,14 +229,14 @@ ready(() => { [].forEach.call(document.querySelectorAll('[data-admin-component]'), element => { const componentName = element.getAttribute('data-admin-component'); - const { locale, ...componentProps } = JSON.parse(element.getAttribute('data-props')); + const componentProps = JSON.parse(element.getAttribute('data-props')); import('../mastodon/containers/admin_component').then(({ default: AdminComponent }) => { return import('../mastodon/components/admin/' + componentName).then(({ default: Component }) => { const root = createRoot(element); root.render ( - + , ); diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 01ab8f8f4b..f26321c41a 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -1,14 +1,15 @@ import './public-path'; +import main from "mastodon/main" + import { start } from '../mastodon/common'; -import { loadLocale } from '../mastodon/load_locale'; +import { loadLocale } from '../mastodon/locales'; import { loadPolyfills } from '../mastodon/polyfills'; start(); -loadPolyfills().then(loadLocale).then(async () => { - const { default: main } = await import('mastodon/main'); - - return main(); -}).catch(e => { - console.error(e); -}); +loadPolyfills() + .then(loadLocale) + .then(main) + .catch(e => { + console.error(e); + }); diff --git a/app/javascript/packs/public.jsx b/app/javascript/packs/public.jsx index 22e6b01a1f..da43bba7d6 100644 --- a/app/javascript/packs/public.jsx +++ b/app/javascript/packs/public.jsx @@ -15,8 +15,7 @@ import { start } from '../mastodon/common'; import { timeAgoString } from '../mastodon/components/relative_timestamp'; import emojify from '../mastodon/features/emoji/emoji'; import loadKeyboardExtensions from '../mastodon/load_keyboard_extensions'; -import { loadLocale } from '../mastodon/load_locale'; -import { getLocale } from '../mastodon/locales'; +import { loadLocale, getLocale } from '../mastodon/locales'; import { loadPolyfills } from '../mastodon/polyfills'; import ready from '../mastodon/ready'; diff --git a/app/javascript/packs/share.jsx b/app/javascript/packs/share.jsx index f9fc785618..0f3b84549d 100644 --- a/app/javascript/packs/share.jsx +++ b/app/javascript/packs/share.jsx @@ -3,7 +3,6 @@ import { createRoot } from 'react-dom/client'; import { start } from '../mastodon/common'; import ComposeContainer from '../mastodon/containers/compose_container'; -import { loadLocale } from '../mastodon/load_locale'; import { loadPolyfills } from '../mastodon/polyfills'; import ready from '../mastodon/ready'; @@ -26,6 +25,6 @@ function main() { ready(loaded); } -loadPolyfills().then(loadLocale).then(main).catch(error => { +loadPolyfills().then(main).catch(error => { console.error(error); }); diff --git a/jest.config.js b/jest.config.js index 42c2b41522..f611812ef6 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,7 +13,6 @@ const config = { collectCoverageFrom: [ 'app/javascript/mastodon/**/*.{js,jsx,ts,tsx}', '!app/javascript/mastodon/features/emoji/emoji_compressed.js', - '!app/javascript/mastodon/locales/locale-data/*.js', '!app/javascript/mastodon/service_worker/entry.js', '!app/javascript/mastodon/test_setup.js', ], diff --git a/package.json b/package.json index 31f2454fee..a08b485fd7 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@reduxjs/toolkit": "^1.9.5", "abortcontroller-polyfill": "^1.7.5", "arrow-key-navigation": "^1.2.0", + "async-mutex": "^0.4.0", "autoprefixer": "^10.4.14", "axios": "^1.4.0", "babel-loader": "^8.3.0", diff --git a/spec/helpers/react_component_helper_spec.rb b/spec/helpers/react_component_helper_spec.rb index 3f133bff9a..28208b619b 100644 --- a/spec/helpers/react_component_helper_spec.rb +++ b/spec/helpers/react_component_helper_spec.rb @@ -33,7 +33,7 @@ describe ReactComponentHelper do it 'returns a tag with data attributes' do expect(parsed_html.div['data-admin-component']).to eq('Name') - expect(parsed_html.div['data-props']).to eq('{"locale":"en","one":"two"}') + expect(parsed_html.div['data-props']).to eq('{"one":"two"}') end end diff --git a/yarn.lock b/yarn.lock index 3ae6343635..fd751c0419 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3273,6 +3273,13 @@ async-limiter@~1.0.0: resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== +async-mutex@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f" + integrity sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA== + dependencies: + tslib "^2.4.0" + async@^2.6.2: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" From acc419b81be647336059ed8048dd88c1c1c1e95a Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Fri, 2 Jun 2023 09:40:23 -0400 Subject: [PATCH 44/69] Fix spacing of middle dots in the detailed status meta section (#25247) --- .../mastodon/features/status/components/detailed_status.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/status/components/detailed_status.jsx b/app/javascript/mastodon/features/status/components/detailed_status.jsx index 83a566710d..ddda6eaac6 100644 --- a/app/javascript/mastodon/features/status/components/detailed_status.jsx +++ b/app/javascript/mastodon/features/status/components/detailed_status.jsx @@ -217,7 +217,7 @@ class DetailedStatus extends ImmutablePureComponent { } else if (this.context.router) { reblogLink = ( <> - · + {' · '} @@ -229,7 +229,7 @@ class DetailedStatus extends ImmutablePureComponent { } else { reblogLink = ( <> - · + {' · '} @@ -263,7 +263,7 @@ class DetailedStatus extends ImmutablePureComponent { if (status.get('edited_at')) { edited = ( <> - · + {' · '} ); From 94329f28e1d6edace2667daeaf0097f895e4940c Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 2 Jun 2023 18:09:08 +0200 Subject: [PATCH 45/69] =?UTF-8?q?Change=20wording=20of=20=E2=80=9CContent?= =?UTF-8?q?=20cache=20retention=20period=E2=80=9D=20setting=20to=20highlig?= =?UTF-8?q?ht=20destructive=20implications=20(#23261)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/settings/content_retention/show.html.haml | 2 +- config/initializers/simple_form.rb | 10 ++++++++++ config/locales/simple_form.en.yml | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/views/admin/settings/content_retention/show.html.haml b/app/views/admin/settings/content_retention/show.html.haml index 36856127f2..de34b5ee3c 100644 --- a/app/views/admin/settings/content_retention/show.html.haml +++ b/app/views/admin/settings/content_retention/show.html.haml @@ -15,7 +15,7 @@ .fields-group = f.input :media_cache_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' } - = f.input :content_cache_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' } + = f.input :content_cache_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }, hint: false, warning_hint: t('simple_form.hints.form_admin_settings.content_cache_retention_period') = f.input :backups_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' } .actions diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index 92cffc5a2a..74034f36fd 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -19,8 +19,17 @@ module RecommendedComponent end end +module WarningHintComponent + def warning_hint(_wrapper_options = nil) + @warning_hint ||= begin + options[:warning_hint].to_s.html_safe if options[:warning_hint].present? + end + end +end + SimpleForm.include_component(AppendComponent) SimpleForm.include_component(RecommendedComponent) +SimpleForm.include_component(WarningHintComponent) SimpleForm.setup do |config| # Wrappers are used by the form builder to generate a @@ -101,6 +110,7 @@ SimpleForm.setup do |config| b.use :html5 b.use :label b.use :hint, wrap_with: { tag: :span, class: :hint } + b.use :warning_hint, wrap_with: { tag: :span, class: [:hint, 'warning-hint'] } b.use :input, wrap_with: { tag: :div, class: :label_input } b.use :error, wrap_with: { tag: :span, class: :error } end diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index b646a15e26..9c747e595d 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -78,7 +78,7 @@ en: backups_retention_period: Keep generated user archives for the specified number of days. bootstrap_timeline_accounts: These accounts will be pinned to the top of new users' follow recommendations. closed_registrations_message: Displayed when sign-ups are closed - content_cache_retention_period: Posts from other servers will be deleted after the specified number of days when set to a positive value. This may be irreversible. + content_cache_retention_period: All posts and boosts from other servers will be deleted after the specified number of days. Some posts may not be recoverable. All related bookmarks, favourites and boosts will also be lost and impossible to undo. custom_css: You can apply custom styles on the web version of Mastodon. mascot: Overrides the illustration in the advanced web interface. media_cache_retention_period: Downloaded media files will be deleted after the specified number of days when set to a positive value, and re-downloaded on demand. From 0766c9a631e45ff66603ff10fa69808b8452d0b3 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 2 Jun 2023 18:35:37 +0200 Subject: [PATCH 46/69] Add card with who invited you to join when displaying rules on sign-up (#23475) --- app/javascript/styles/mastodon/accounts.scss | 14 ++------------ app/javascript/styles/mastodon/forms.scss | 4 ++++ app/views/application/_card.html.haml | 6 ++++-- app/views/auth/registrations/rules.html.haml | 10 ++++++++-- config/locales/en.yml | 3 +++ 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index 8b7b634071..b50306deda 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -3,11 +3,8 @@ display: block; text-decoration: none; color: inherit; - box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); - - @media screen and (max-width: $no-gap-breakpoint) { - box-shadow: none; - } + overflow: hidden; + border-radius: 4px; &:hover, &:active, @@ -22,7 +19,6 @@ height: 130px; position: relative; background: darken($ui-base-color, 12%); - border-radius: 4px 4px 0 0; img { display: block; @@ -30,7 +26,6 @@ height: 100%; margin: 0; object-fit: cover; - border-radius: 4px 4px 0 0; } @media screen and (width <= 600px) { @@ -45,11 +40,6 @@ justify-content: flex-start; align-items: center; background: lighten($ui-base-color, 4%); - border-radius: 0 0 4px 4px; - - @media screen and (max-width: $no-gap-breakpoint) { - border-radius: 0; - } .avatar { flex: 0 0 auto; diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 57f077c4e8..d63a42557f 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -137,6 +137,10 @@ code { color: $secondary-text-color; margin-bottom: 30px; + &.invited-by { + margin-bottom: 15px; + } + a { color: $highlight-text-color; } diff --git a/app/views/application/_card.html.haml b/app/views/application/_card.html.haml index 719856d495..1b3dd889c1 100644 --- a/app/views/application/_card.html.haml +++ b/app/views/application/_card.html.haml @@ -1,9 +1,11 @@ - account_url = local_assigns[:admin] ? admin_account_path(account.id) : ActivityPub::TagManager.instance.url_for(account) +- compact ||= false .card.h-card = link_to account_url, target: '_blank', rel: 'noopener noreferrer' do - .card__img - = image_tag account.header.url, alt: '' + - unless compact + .card__img + = image_tag account.header.url, alt: '' .card__bar .avatar = image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo' diff --git a/app/views/auth/registrations/rules.html.haml b/app/views/auth/registrations/rules.html.haml index ab3fa864ab..234f4a601d 100644 --- a/app/views/auth/registrations/rules.html.haml +++ b/app/views/auth/registrations/rules.html.haml @@ -7,8 +7,14 @@ .simple_form = render 'auth/shared/progress', stage: 'rules' - %h1.title= t('auth.rules.title') - %p.lead= t('auth.rules.preamble', domain: site_hostname) + - if @invite.present? && @invite.autofollow? + %h1.title= t('auth.rules.title_invited') + %p.lead.invited-by= t('auth.rules.invited_by', domain: site_hostname) + = render 'application/card', account: @invite.user.account, compact: true + %p.lead= t('auth.rules.preamble_invited', domain: site_hostname) + - else + %h1.title= t('auth.rules.title') + %p.lead= t('auth.rules.preamble', domain: site_hostname) %ol.rules-list - @rules.each do |rule| diff --git a/config/locales/en.yml b/config/locales/en.yml index 6a8da6e60d..2c292c42d4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1031,8 +1031,11 @@ en: rules: accept: Accept back: Back + invited_by: 'You can join %{domain} thanks to the invitation you have received from:' preamble: These are set and enforced by the %{domain} moderators. + preamble_invited: Before you proceed, please consider the ground rules set by the moderators of %{domain}. title: Some ground rules. + title_invited: You've been invited. security: Security set_new_password: Set new password setup: From 768b00c4d0c05c35c2c6c9bc8b4a821f1bde119d Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Fri, 2 Jun 2023 13:58:18 -0400 Subject: [PATCH 47/69] =?UTF-8?q?Consistently=20use=20middle=20dot=20(?= =?UTF-8?q?=C2=B7)=20instead=20of=20bullet=20(=E2=80=A2)=20to=20separate?= =?UTF-8?q?=20items=20(#25248)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 9 ++++++ .haml-lint.yml | 5 +++ .rubocop.yml | 4 +++ .../_email_domain_block.html.haml | 2 +- .../_domain_block.html.haml | 6 ++-- app/views/admin/instances/_instance.html.haml | 2 +- app/views/admin/instances/show.html.haml | 2 +- app/views/admin/ip_blocks/_ip_block.html.haml | 2 +- app/views/admin/roles/_role.html.haml | 2 +- .../trends/links/_preview_card.html.haml | 10 +++--- .../admin/trends/statuses/_status.html.haml | 10 +++--- app/views/admin/trends/tags/_tag.html.haml | 6 ++-- app/views/admin/webhooks/_webhook.html.haml | 2 +- .../admin_mailer/_new_trending_links.text.erb | 4 +-- .../_new_trending_statuses.text.erb | 2 +- .../admin_mailer/_new_trending_tags.text.erb | 2 +- .../authorized_applications/index.html.haml | 2 +- lib/linter/haml_middle_dot.rb | 26 ++++++++++++++++ lib/linter/rubocop_middle_dot.rb | 31 +++++++++++++++++++ 19 files changed, 102 insertions(+), 27 deletions(-) create mode 100644 lib/linter/haml_middle_dot.rb create mode 100644 lib/linter/rubocop_middle_dot.rb diff --git a/.eslintrc.js b/.eslintrc.js index 24961cdd9d..91dcd8e60c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -81,6 +81,15 @@ module.exports = { { property: 'substring', message: 'Use .slice instead of .substring.' }, { property: 'substr', message: 'Use .slice instead of .substr.' }, ], + 'no-restricted-syntax': [ + 'error', + { + // eslint-disable-next-line no-restricted-syntax + selector: 'Literal[value=/•/], JSXText[value=/•/]', + // eslint-disable-next-line no-restricted-syntax + message: "Use '·' (middle dot) instead of '•' (bullet)", + }, + ], 'no-self-assign': 'off', 'no-unused-expressions': 'error', 'no-unused-vars': 'off', diff --git a/.haml-lint.yml b/.haml-lint.yml index 12ca463422..d1ed30b260 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -4,6 +4,11 @@ exclude: - 'vendor/**/*' - lib/templates/haml/scaffold/_form.html.haml +require: + - ./lib/linter/haml_middle_dot.rb + linters: AltText: enabled: true + MiddleDot: + enabled: true diff --git a/.rubocop.yml b/.rubocop.yml index bd561df1d2..eff89bdaee 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -11,6 +11,7 @@ require: - rubocop-rspec - rubocop-performance - rubocop-capybara + - ./lib/linter/rubocop_middle_dot AllCops: TargetRubyVersion: 3.0 # Set to minimum supported version of CI @@ -205,3 +206,6 @@ Style/TrailingCommaInArrayLiteral: # https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainhashliteral Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: 'comma' + +Style/MiddleDot: + Enabled: true diff --git a/app/views/admin/email_domain_blocks/_email_domain_block.html.haml b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml index c5a55bc27c..7cb973c4b4 100644 --- a/app/views/admin/email_domain_blocks/_email_domain_block.html.haml +++ b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml @@ -9,6 +9,6 @@ - if email_domain_block.parent.present? = t('admin.email_domain_blocks.resolved_through_html', domain: content_tag(:samp, email_domain_block.parent.domain)) - • + · = t('admin.email_domain_blocks.attempts_over_week', count: email_domain_block.history.reduce(0) { |sum, day| sum + day.accounts }) diff --git a/app/views/admin/export_domain_blocks/_domain_block.html.haml b/app/views/admin/export_domain_blocks/_domain_block.html.haml index 5d4b6c4d0d..cdce4fd28a 100644 --- a/app/views/admin/export_domain_blocks/_domain_block.html.haml +++ b/app/views/admin/export_domain_blocks/_domain_block.html.haml @@ -17,11 +17,11 @@ %br/ - = f.object.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ') + = f.object.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ') - if f.object.public_comment.present? - • + · = f.object.public_comment - if existing_relationships - • + · = fa_icon 'warning fw' = t('admin.export_domain_blocks.import.existing_relationships_warning') diff --git a/app/views/admin/instances/_instance.html.haml b/app/views/admin/instances/_instance.html.haml index 93f9bd4181..65cf789ce3 100644 --- a/app/views/admin/instances/_instance.html.haml +++ b/app/views/admin/instances/_instance.html.haml @@ -6,7 +6,7 @@ %small - if instance.domain_block - = instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ') + = instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ') - elsif instance.domain_allow = t('admin.accounts.whitelisted') - else diff --git a/app/views/admin/instances/show.html.haml b/app/views/admin/instances/show.html.haml index ab290912e2..6d67d389d2 100644 --- a/app/views/admin/instances/show.html.haml +++ b/app/views/admin/instances/show.html.haml @@ -58,7 +58,7 @@ %td= @instance.domain_block.public_comment %tr %th= t('admin.instances.content_policies.policy') - %td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ') + %td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ') = link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button' = link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete } diff --git a/app/views/admin/ip_blocks/_ip_block.html.haml b/app/views/admin/ip_blocks/_ip_block.html.haml index b8d3ac0e86..3dc6f8f8e5 100644 --- a/app/views/admin/ip_blocks/_ip_block.html.haml +++ b/app/views/admin/ip_blocks/_ip_block.html.haml @@ -5,7 +5,7 @@ .pending-account__header %samp= link_to "#{ip_block.ip}/#{ip_block.ip.prefix}", admin_accounts_path(ip: "#{ip_block.ip}/#{ip_block.ip.prefix}") - if ip_block.comment.present? - • + · = ip_block.comment %br/ = t("simple_form.labels.ip_block.severities.#{ip_block.severity}") diff --git a/app/views/admin/roles/_role.html.haml b/app/views/admin/roles/_role.html.haml index 798d8d8b4f..d6c6b62c81 100644 --- a/app/views/admin/roles/_role.html.haml +++ b/app/views/admin/roles/_role.html.haml @@ -24,7 +24,7 @@ = t('admin.roles.everyone_full_description_html') - else = link_to t('admin.roles.assigned_users', count: role.users.count), admin_accounts_path(role_ids: role.id) - • + · %abbr{ title: role.permissions_as_keys.map { |privilege| I18n.t("admin.roles.privileges.#{privilege}") }.join(', ') }= t('admin.roles.permissions_count', count: role.permissions_as_keys.size) %div = table_link_to 'pencil', t('admin.accounts.edit'), edit_admin_role_path(role) if can?(:update, role) diff --git a/app/views/admin/trends/links/_preview_card.html.haml b/app/views/admin/trends/links/_preview_card.html.haml index 8812feb316..1ca3483715 100644 --- a/app/views/admin/trends/links/_preview_card.html.haml +++ b/app/views/admin/trends/links/_preview_card.html.haml @@ -10,21 +10,21 @@ - if preview_card.provider_name.present? = preview_card.provider_name - • + · - if preview_card.language.present? = standard_locale_name(preview_card.language) - • + · = t('admin.trends.links.shared_by_over_week', count: preview_card.history.reduce(0) { |sum, day| sum + day.accounts }) - if preview_card.trend.allowed? - • + · %abbr{ title: t('admin.trends.tags.current_score', score: preview_card.trend.score) }= t('admin.trends.tags.trending_rank', rank: preview_card.trend.rank) - if preview_card.decaying? - • + · = t('admin.trends.tags.peaked_on_and_decaying', date: l(preview_card.max_score_at.to_date, format: :short)) - elsif preview_card.requires_review? - • + · = t('admin.trends.pending_review') diff --git a/app/views/admin/trends/statuses/_status.html.haml b/app/views/admin/trends/statuses/_status.html.haml index f35e13d128..98f2e77090 100644 --- a/app/views/admin/trends/statuses/_status.html.haml +++ b/app/views/admin/trends/statuses/_status.html.haml @@ -17,17 +17,17 @@ = t('admin.trends.statuses.shared_by', count: status.reblogs_count + status.favourites_count, friendly_count: friendly_number_to_human(status.reblogs_count + status.favourites_count)) - if status.account.domain.present? - • + · = status.account.domain - if status.language.present? - • + · = standard_locale_name(status.language) - if status.trendable? && !status.account.discoverable? - • + · = t('admin.trends.statuses.not_discoverable') - if status.trend.allowed? - • + · %abbr{ title: t('admin.trends.tags.current_score', score: status.trend.score) }= t('admin.trends.tags.trending_rank', rank: status.trend.rank) - elsif status.requires_review? - • + · = t('admin.trends.pending_review') diff --git a/app/views/admin/trends/tags/_tag.html.haml b/app/views/admin/trends/tags/_tag.html.haml index a30666a08b..3bbdd08db8 100644 --- a/app/views/admin/trends/tags/_tag.html.haml +++ b/app/views/admin/trends/tags/_tag.html.haml @@ -13,12 +13,12 @@ = t('admin.trends.tags.used_by_over_week', count: tag.history.reduce(0) { |sum, day| sum + day.accounts }) - if tag.trendable? && (rank = Trends.tags.rank(tag.id)) - • + · %abbr{ title: t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id)) }= t('admin.trends.tags.trending_rank', rank: rank + 1) - if tag.decaying? - • + · = t('admin.trends.tags.peaked_on_and_decaying', date: l(tag.max_score_at.to_date, format: :short)) - elsif tag.requires_review? - • + · = t('admin.trends.pending_review') diff --git a/app/views/admin/webhooks/_webhook.html.haml b/app/views/admin/webhooks/_webhook.html.haml index d94a41eb3d..6b3e49eba0 100644 --- a/app/views/admin/webhooks/_webhook.html.haml +++ b/app/views/admin/webhooks/_webhook.html.haml @@ -10,7 +10,7 @@ - else %span.negative-hint= t('admin.webhooks.disabled') - • + · %abbr{ title: webhook.events.join(', ') }= t('admin.webhooks.enabled_events', count: webhook.events.size) diff --git a/app/views/admin_mailer/_new_trending_links.text.erb b/app/views/admin_mailer/_new_trending_links.text.erb index 602e12793e..85f3f8039d 100644 --- a/app/views/admin_mailer/_new_trending_links.text.erb +++ b/app/views/admin_mailer/_new_trending_links.text.erb @@ -1,8 +1,8 @@ <%= raw t('admin_mailer.new_trends.new_trending_links.title') %> <% @links.each do |link| %> -- <%= link.title %> • <%= link.url %> - <%= standard_locale_name(link.language) %> • <%= raw t('admin.trends.links.usage_comparison', today: link.history.get(Time.now.utc).accounts, yesterday: link.history.get(Time.now.utc - 1.day).accounts) %> • <%= t('admin.trends.tags.current_score', score: link.trend.score.round(2)) %> +- <%= link.title %> · <%= link.url %> + <%= standard_locale_name(link.language) %> · <%= raw t('admin.trends.links.usage_comparison', today: link.history.get(Time.now.utc).accounts, yesterday: link.history.get(Time.now.utc - 1.day).accounts) %> · <%= t('admin.trends.tags.current_score', score: link.trend.score.round(2)) %> <% end %> <%= raw t('application_mailer.view')%> <%= admin_trends_links_url %> diff --git a/app/views/admin_mailer/_new_trending_statuses.text.erb b/app/views/admin_mailer/_new_trending_statuses.text.erb index 1ed3ae8573..eedbfff9d9 100644 --- a/app/views/admin_mailer/_new_trending_statuses.text.erb +++ b/app/views/admin_mailer/_new_trending_statuses.text.erb @@ -2,7 +2,7 @@ <% @statuses.each do |status| %> - <%= ActivityPub::TagManager.instance.url_for(status) %> - <%= standard_locale_name(status.language) %> • <%= raw t('admin.trends.tags.current_score', score: status.trend.score.round(2)) %> + <%= standard_locale_name(status.language) %> · <%= raw t('admin.trends.tags.current_score', score: status.trend.score.round(2)) %> <% end %> <%= raw t('application_mailer.view')%> <%= admin_trends_statuses_url %> diff --git a/app/views/admin_mailer/_new_trending_tags.text.erb b/app/views/admin_mailer/_new_trending_tags.text.erb index 363df369d5..d528ab8eb7 100644 --- a/app/views/admin_mailer/_new_trending_tags.text.erb +++ b/app/views/admin_mailer/_new_trending_tags.text.erb @@ -2,7 +2,7 @@ <% @tags.each do |tag| %> - #<%= tag.display_name %> - <%= raw t('admin.trends.tags.usage_comparison', today: tag.history.get(Time.now.utc).accounts, yesterday: tag.history.get(Time.now.utc - 1.day).accounts) %> • <%= t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id).round(2)) %> + <%= raw t('admin.trends.tags.usage_comparison', today: tag.history.get(Time.now.utc).accounts, yesterday: tag.history.get(Time.now.utc - 1.day).accounts) %> · <%= t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id).round(2)) %> <% end %> <% if @lowest_trending_tag %> diff --git a/app/views/oauth/authorized_applications/index.html.haml b/app/views/oauth/authorized_applications/index.html.haml index 55d8524dbe..689f051029 100644 --- a/app/views/oauth/authorized_applications/index.html.haml +++ b/app/views/oauth/authorized_applications/index.html.haml @@ -23,7 +23,7 @@ - else = t('doorkeeper.authorized_applications.index.never_used') - • + · = t('doorkeeper.authorized_applications.index.authorized_at', date: l(application.created_at.to_date)) diff --git a/lib/linter/haml_middle_dot.rb b/lib/linter/haml_middle_dot.rb new file mode 100644 index 0000000000..3b27711521 --- /dev/null +++ b/lib/linter/haml_middle_dot.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module HamlLint + # Bans the usage of “•” (bullet) in HTML/HAML in favor of “·” (middle dot) in anything that will end up as a text node. (including string literals in Ruby code) + class Linter::MiddleDot < Linter + include LinterRegistry + + # rubocop:disable Style/MiddleDot + BULLET = '•' + # rubocop:enable Style/MiddleDot + MIDDLE_DOT = '·' + MESSAGE = "Use '#{MIDDLE_DOT}' (middle dot) instead of '#{BULLET}' (bullet)".freeze + + def visit_plain(node) + return unless node.text.include?(BULLET) + + record_lint(node, MESSAGE) + end + + def visit_script(node) + return unless node.script.include?(BULLET) + + record_lint(node, MESSAGE) + end + end +end diff --git a/lib/linter/rubocop_middle_dot.rb b/lib/linter/rubocop_middle_dot.rb new file mode 100644 index 0000000000..3a1d97c0c9 --- /dev/null +++ b/lib/linter/rubocop_middle_dot.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Style + # Bans the usage of “•” (bullet) in HTML/HAML in favor of “·” (middle dot) in string literals + class MiddleDot < Base + extend AutoCorrector + extend Util + + # rubocop:disable Style/MiddleDot + BULLET = '•' + # rubocop:enable Style/MiddleDot + MIDDLE_DOT = '·' + MESSAGE = "Use '#{MIDDLE_DOT}' (middle dot) instead of '#{BULLET}' (bullet)".freeze + + def on_str(node) + # Constants like __FILE__ are handled as strings, + # but don't respond to begin. + return unless node.loc.respond_to?(:begin) && node.loc.begin + + return unless node.value.include?(BULLET) + + add_offense(node, message: MESSAGE) do |corrector| + corrector.replace(node, node.source.gsub(BULLET, MIDDLE_DOT)) + end + end + end + end + end +end From aea67d448bc7974aa4c342f5c1987ec4ee2681ea Mon Sep 17 00:00:00 2001 From: Nick Schonning Date: Fri, 2 Jun 2023 14:01:36 -0400 Subject: [PATCH 48/69] Cleanup old translationRunner (#25241) --- config/webpack/translationRunner.js | 3 --- package.json | 1 - 2 files changed, 4 deletions(-) delete mode 100644 config/webpack/translationRunner.js diff --git a/config/webpack/translationRunner.js b/config/webpack/translationRunner.js deleted file mode 100644 index 77534c9de3..0000000000 --- a/config/webpack/translationRunner.js +++ /dev/null @@ -1,3 +0,0 @@ -console.error("The localisation functionality has been refactored, please see the Localisation section in the development documentation (https://docs.joinmastodon.org/dev/code/#localizations)"); - -process.exit(1); diff --git a/package.json b/package.json index a08b485fd7..49e9c7f743 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "lint:sass": "stylelint \"**/*.{css,scss}\" && prettier --check \"**/*.{css,scss}\"", "lint:yml": "prettier --check \"**/*.{yaml,yml}\"", "lint": "yarn lint:js && yarn lint:json && yarn lint:sass && yarn lint:yml", - "manage:translations": "node ./config/webpack/translationRunner.js", "postversion": "git push --tags", "prepare": "husky install", "start": "node ./streaming/index.js", From 4a5464f36051ea9cbc0c7afa8c885f4f2bdd9245 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 5 Jun 2023 01:42:17 +0200 Subject: [PATCH 49/69] Change "Follow 7 people" to "Find at least 7 people to follow" in web UI (#24954) --- app/javascript/mastodon/features/onboarding/index.jsx | 2 +- app/javascript/mastodon/locales/en.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/onboarding/index.jsx b/app/javascript/mastodon/features/onboarding/index.jsx index ecebdb6965..79291b3d08 100644 --- a/app/javascript/mastodon/features/onboarding/index.jsx +++ b/app/javascript/mastodon/features/onboarding/index.jsx @@ -120,7 +120,7 @@ class Onboarding extends ImmutablePureComponent {
0 && account.get('note').length > 0)} icon='address-book-o' label={} description={} /> - = 7} icon='user-plus' label={} description={} /> + = 7} icon='user-plus' label={} description={} /> = 1} icon='pencil-square-o' label={} description={} /> } description={} />
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 5ed793cdba..f6d6daa3e5 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -460,8 +460,8 @@ "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", "onboarding.start.skip": "Want to skip right ahead?", "onboarding.start.title": "You've made it!", - "onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.", - "onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}", + "onboarding.steps.follow_people.body": "You curate your own home feed. Let's fill it with interesting people.", + "onboarding.steps.follow_people.title": "Find at least {count, plural, one {one person} other {# people}} to follow", "onboarding.steps.publish_status.body": "Say hello to the world.", "onboarding.steps.publish_status.title": "Make your first post", "onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.", From e49819142f98a94ba44e24aa093815bf74afab05 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Sun, 4 Jun 2023 19:57:05 -0400 Subject: [PATCH 50/69] Remove unmaintained `nsa` gem (#25265) --- Gemfile | 1 - Gemfile.lock | 7 ------- config/initializers/statsd.rb | 15 --------------- 3 files changed, 23 deletions(-) delete mode 100644 config/initializers/statsd.rb diff --git a/Gemfile b/Gemfile index 62e45a5f30..cff8cb1f88 100644 --- a/Gemfile +++ b/Gemfile @@ -60,7 +60,6 @@ gem 'kaminari', '~> 1.2' gem 'link_header', '~> 0.0' gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar' gem 'nokogiri', '~> 1.15' -gem 'nsa', '~> 0.2' gem 'oj', '~> 3.14' gem 'ox', '~> 2.14' gem 'parslet' diff --git a/Gemfile.lock b/Gemfile.lock index 7d04d875c5..bb209b3841 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -442,11 +442,6 @@ GEM nokogiri (1.15.2) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nsa (0.2.8) - activesupport (>= 4.2, < 7) - concurrent-ruby (~> 1.0, >= 1.0.2) - sidekiq (>= 3.5) - statsd-ruby (~> 1.4, >= 1.4.0) oj (3.14.3) omniauth (1.9.2) hashie (>= 3.4.6) @@ -682,7 +677,6 @@ GEM net-scp (>= 1.1.2) net-ssh (>= 2.8.0) stackprof (0.2.25) - statsd-ruby (1.5.0) stoplight (3.0.1) redlock (~> 1.0) strong_migrations (0.8.0) @@ -831,7 +825,6 @@ DEPENDENCIES net-http (~> 0.3.2) net-ldap (~> 0.18) nokogiri (~> 1.15) - nsa (~> 0.2) oj (~> 3.14) omniauth (~> 1.9) omniauth-cas (~> 2.0) diff --git a/config/initializers/statsd.rb b/config/initializers/statsd.rb deleted file mode 100644 index 93ea1d1e4a..0000000000 --- a/config/initializers/statsd.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -if ENV['STATSD_ADDR'].present? - host, port = ENV['STATSD_ADDR'].split(':') - - $statsd = ::Statsd.new(host, port) - $statsd.namespace = ENV.fetch('STATSD_NAMESPACE') { ['Mastodon', Rails.env].join('.') } - - ::NSA.inform_statsd($statsd) do |informant| - informant.collect(:action_controller, :web) - informant.collect(:active_record, :db) - informant.collect(:active_support_cache, :cache) - informant.collect(:sidekiq, :sidekiq) - end -end From 03a707f6a6c8bbefec630c04d183fc9f22b23d9d Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Mon, 5 Jun 2023 03:16:12 -0300 Subject: [PATCH 51/69] Add test coverage for `Mastodon::CLI::Accounts#merge` (#25199) --- spec/lib/mastodon/cli/accounts_spec.rb | 111 +++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/spec/lib/mastodon/cli/accounts_spec.rb b/spec/lib/mastodon/cli/accounts_spec.rb index ba49e480ad..50066572c6 100644 --- a/spec/lib/mastodon/cli/accounts_spec.rb +++ b/spec/lib/mastodon/cli/accounts_spec.rb @@ -998,4 +998,115 @@ describe Mastodon::CLI::Accounts do end end end + + describe '#merge' do + shared_examples 'an account not found' do |acct| + it 'exits with an error message indicating that there is no such account' do + expect { cli.invoke(:merge, arguments) }.to output( + a_string_including("No such account (#{acct})") + ).to_stdout + .and raise_error(SystemExit) + end + end + + context 'when "from_account" is not found' do + let(:to_account) { Fabricate(:account, domain: 'example.com') } + let(:arguments) { ['non_existent_username@domain.com', "#{to_account.username}@#{to_account.domain}"] } + + it_behaves_like 'an account not found', 'non_existent_username@domain.com' + end + + context 'when "from_account" is a local account' do + let(:from_account) { Fabricate(:account, domain: nil, username: 'bob') } + let(:to_account) { Fabricate(:account, domain: 'example.com') } + let(:arguments) { [from_account.username, "#{to_account.username}@#{to_account.domain}"] } + + it_behaves_like 'an account not found', 'bob' + end + + context 'when "to_account" is not found' do + let(:from_account) { Fabricate(:account, domain: 'example.com') } + let(:arguments) { ["#{from_account.username}@#{from_account.domain}", 'non_existent_username'] } + + it_behaves_like 'an account not found', 'non_existent_username' + end + + context 'when "to_account" is local' do + let(:from_account) { Fabricate(:account, domain: 'example.com') } + let(:to_account) { Fabricate(:account, domain: nil, username: 'bob') } + let(:arguments) do + ["#{from_account.username}@#{from_account.domain}", "#{to_account.username}@#{to_account.domain}"] + end + + it_behaves_like 'an account not found', 'bob@' + end + + context 'when "from_account" and "to_account" public keys do not match' do + let(:from_account) { instance_double(Account, username: 'bob', domain: 'example1.com', local?: false, public_key: 'from_account') } + let(:to_account) { instance_double(Account, username: 'bob', domain: 'example2.com', local?: false, public_key: 'to_account') } + let(:arguments) do + ["#{from_account.username}@#{from_account.domain}", "#{to_account.username}@#{to_account.domain}"] + end + + before do + allow(Account).to receive(:find_remote).with(from_account.username, from_account.domain).and_return(from_account) + allow(Account).to receive(:find_remote).with(to_account.username, to_account.domain).and_return(to_account) + end + + it 'exits with an error message indicating that the accounts do not have the same pub key' do + expect { cli.invoke(:merge, arguments) }.to output( + a_string_including("Accounts don't have the same public key, might not be duplicates!\nOverride with --force") + ).to_stdout + .and raise_error(SystemExit) + end + + context 'with --force option' do + let(:options) { { force: true } } + + before do + allow(to_account).to receive(:merge_with!) + allow(from_account).to receive(:destroy) + end + + it 'merges "from_account" into "to_account"' do + cli.invoke(:merge, arguments, options) + + expect(to_account).to have_received(:merge_with!).with(from_account).once + end + + it 'deletes "from_account"' do + cli.invoke(:merge, arguments, options) + + expect(from_account).to have_received(:destroy).once + end + end + end + + context 'when "from_account" and "to_account" public keys match' do + let(:from_account) { instance_double(Account, username: 'bob', domain: 'example1.com', local?: false, public_key: 'pub_key') } + let(:to_account) { instance_double(Account, username: 'bob', domain: 'example2.com', local?: false, public_key: 'pub_key') } + let(:arguments) do + ["#{from_account.username}@#{from_account.domain}", "#{to_account.username}@#{to_account.domain}"] + end + + before do + allow(Account).to receive(:find_remote).with(from_account.username, from_account.domain).and_return(from_account) + allow(Account).to receive(:find_remote).with(to_account.username, to_account.domain).and_return(to_account) + allow(to_account).to receive(:merge_with!) + allow(from_account).to receive(:destroy) + end + + it 'merges "from_account" into "to_account"' do + cli.invoke(:merge, arguments) + + expect(to_account).to have_received(:merge_with!).with(from_account).once + end + + it 'deletes "from_account"' do + cli.invoke(:merge, arguments) + + expect(from_account).to have_received(:destroy) + end + end + end end From 5c87c6650424c0ec759257b0c8e71fa2d51eedc9 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 5 Jun 2023 02:20:18 -0400 Subject: [PATCH 52/69] Add coverage for CLI::CanonicalEmailBlocks command (#25239) --- .../cli/canonical_email_blocks_spec.rb | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb b/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb index fb481e8a82..eb57a3cd15 100644 --- a/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb +++ b/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb @@ -4,9 +4,57 @@ require 'rails_helper' require 'mastodon/cli/canonical_email_blocks' describe Mastodon::CLI::CanonicalEmailBlocks do + let(:cli) { described_class.new } + describe '.exit_on_failure?' do it 'returns true' do expect(described_class.exit_on_failure?).to be true end end + + describe '#find' do + let(:arguments) { ['user@example.com'] } + + context 'when a block is present' do + before { Fabricate(:canonical_email_block, email: 'user@example.com') } + + it 'announces the presence of the block' do + expect { cli.invoke(:find, arguments) }.to output( + a_string_including('user@example.com is blocked') + ).to_stdout + end + end + + context 'when a block is not present' do + it 'announces the absence of the block' do + expect { cli.invoke(:find, arguments) }.to output( + a_string_including('user@example.com is not blocked') + ).to_stdout + end + end + end + + describe '#remove' do + let(:arguments) { ['user@example.com'] } + + context 'when a block is present' do + before { Fabricate(:canonical_email_block, email: 'user@example.com') } + + it 'removes the block' do + expect { cli.invoke(:remove, arguments) }.to output( + a_string_including('Unblocked user@example.com') + ).to_stdout + + expect(CanonicalEmailBlock.matching_email('user@example.com')).to be_empty + end + end + + context 'when a block is not present' do + it 'announces the absence of the block' do + expect { cli.invoke(:remove, arguments) }.to output( + a_string_including('user@example.com is not blocked') + ).to_stdout + end + end + end end From 0daf78f903ba81a2fd1566fd9845cea623e8324c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 5 Jun 2023 02:22:03 -0400 Subject: [PATCH 53/69] Add `allow_other_host: true` to backups controller (#25266) --- app/controllers/backups_controller.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/backups_controller.rb b/app/controllers/backups_controller.rb index 5891da6f6d..205df48d44 100644 --- a/app/controllers/backups_controller.rb +++ b/app/controllers/backups_controller.rb @@ -11,15 +11,15 @@ class BackupsController < ApplicationController def download case Paperclip::Attachment.default_options[:storage] when :s3 - redirect_to @backup.dump.expiring_url(10) + redirect_to @backup.dump.expiring_url(10), allow_other_host: true when :fog if Paperclip::Attachment.default_options.dig(:fog_credentials, :openstack_temp_url_key).present? - redirect_to @backup.dump.expiring_url(Time.now.utc + 10) + redirect_to @backup.dump.expiring_url(Time.now.utc + 10), allow_other_host: true else - redirect_to full_asset_url(@backup.dump.url) + redirect_to full_asset_url(@backup.dump.url), allow_other_host: true end when :filesystem - redirect_to full_asset_url(@backup.dump.url) + redirect_to full_asset_url(@backup.dump.url), allow_other_host: true end end From bacb6749217767de83120ad1dea6a59f69ee66d9 Mon Sep 17 00:00:00 2001 From: Darius Kazemi Date: Mon, 5 Jun 2023 00:37:02 -0700 Subject: [PATCH 54/69] Add exclusive lists (#22048) Co-authored-by: Liam Cooke Co-authored-by: John Holdun Co-authored-by: Effy Elden Co-authored-by: Lina Reyne Co-authored-by: Lina <20880695+necropolina@users.noreply.github.com> Co-authored-by: Claire --- app/controllers/api/v1/lists_controller.rb | 2 +- app/javascript/mastodon/actions/lists.js | 4 +- .../mastodon/features/list_timeline/index.jsx | 18 ++++++++- app/javascript/mastodon/locales/en.json | 1 + .../mastodon/reducers/list_editor.js | 2 + app/lib/feed_manager.rb | 26 +++++++------ app/models/list.rb | 1 + app/serializers/rest/list_serializer.rb | 2 +- .../20230605085710_add_exclusive_to_lists.rb | 7 ++++ db/schema.rb | 3 +- spec/lib/feed_manager_spec.rb | 37 +++++++++++++++++++ 11 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 db/migrate/20230605085710_add_exclusive_to_lists.rb diff --git a/app/controllers/api/v1/lists_controller.rb b/app/controllers/api/v1/lists_controller.rb index 843ca2ec2b..4bbbed2673 100644 --- a/app/controllers/api/v1/lists_controller.rb +++ b/app/controllers/api/v1/lists_controller.rb @@ -42,6 +42,6 @@ class Api::V1::ListsController < Api::BaseController end def list_params - params.permit(:title, :replies_policy) + params.permit(:title, :replies_policy, :exclusive) end end diff --git a/app/javascript/mastodon/actions/lists.js b/app/javascript/mastodon/actions/lists.js index 2faa54b955..b0789cd426 100644 --- a/app/javascript/mastodon/actions/lists.js +++ b/app/javascript/mastodon/actions/lists.js @@ -151,10 +151,10 @@ export const createListFail = error => ({ error, }); -export const updateList = (id, title, shouldReset, replies_policy) => (dispatch, getState) => { +export const updateList = (id, title, shouldReset, isExclusive, replies_policy) => (dispatch, getState) => { dispatch(updateListRequest(id)); - api(getState).put(`/api/v1/lists/${id}`, { title, replies_policy }).then(({ data }) => { + api(getState).put(`/api/v1/lists/${id}`, { title, replies_policy, exclusive: typeof isExclusive === 'undefined' ? undefined : !!isExclusive }).then(({ data }) => { dispatch(updateListSuccess(data)); if (shouldReset) { diff --git a/app/javascript/mastodon/features/list_timeline/index.jsx b/app/javascript/mastodon/features/list_timeline/index.jsx index f41e8e6f23..f9f3a7c315 100644 --- a/app/javascript/mastodon/features/list_timeline/index.jsx +++ b/app/javascript/mastodon/features/list_timeline/index.jsx @@ -8,6 +8,8 @@ import { Helmet } from 'react-helmet'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; +import Toggle from 'react-toggle'; + import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; import { fetchList, deleteList, updateList } from 'mastodon/actions/lists'; import { openModal } from 'mastodon/actions/modal'; @@ -145,7 +147,13 @@ class ListTimeline extends PureComponent { handleRepliesPolicyChange = ({ target }) => { const { dispatch } = this.props; const { id } = this.props.params; - dispatch(updateList(id, undefined, false, target.value)); + dispatch(updateList(id, undefined, false, undefined, target.value)); + }; + + onExclusiveToggle = ({ target }) => { + const { dispatch } = this.props; + const { id } = this.props.params; + dispatch(updateList(id, undefined, false, target.checked, undefined)); }; render () { @@ -154,6 +162,7 @@ class ListTimeline extends PureComponent { const pinned = !!columnId; const title = list ? list.get('title') : id; const replies_policy = list ? list.get('replies_policy') : undefined; + const isExclusive = list ? list.get('exclusive') : undefined; if (typeof list === 'undefined') { return ( @@ -191,6 +200,13 @@ class ListTimeline extends PureComponent {
+
+ + +
+ { replies_policy !== undefined && (
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index f6d6daa3e5..09282de7c8 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -356,6 +356,7 @@ "lists.delete": "Delete list", "lists.edit": "Edit list", "lists.edit.submit": "Change title", + "lists.exclusive": "Hide these posts from home", "lists.new.create": "Add list", "lists.new.title_placeholder": "New list title", "lists.replies_policy.followed": "Any followed user", diff --git a/app/javascript/mastodon/reducers/list_editor.js b/app/javascript/mastodon/reducers/list_editor.js index ceceb27c7a..d3fd62adec 100644 --- a/app/javascript/mastodon/reducers/list_editor.js +++ b/app/javascript/mastodon/reducers/list_editor.js @@ -25,6 +25,7 @@ const initialState = ImmutableMap({ isSubmitting: false, isChanged: false, title: '', + isExclusive: false, accounts: ImmutableMap({ items: ImmutableList(), @@ -46,6 +47,7 @@ export default function listEditorReducer(state = initialState, action) { return state.withMutations(map => { map.set('listId', action.list.get('id')); map.set('title', action.list.get('title')); + map.set('isExclusive', action.list.get('is_exclusive')); map.set('isSubmitting', false); }); case LIST_EDITOR_TITLE_CHANGE: diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 643e6828d2..7423d2d092 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -40,9 +40,9 @@ class FeedManager def filter?(timeline_type, status, receiver) case timeline_type when :home - filter_from_home?(status, receiver.id, build_crutches(receiver.id, [status])) + filter_from_home?(status, receiver.id, build_crutches(receiver.id, [status]), :home) when :list - filter_from_list?(status, receiver) || filter_from_home?(status, receiver.account_id, build_crutches(receiver.account_id, [status])) + filter_from_list?(status, receiver) || filter_from_home?(status, receiver.account_id, build_crutches(receiver.account_id, [status]), :list) when :mentions filter_from_mentions?(status, receiver.id) when :tags @@ -351,10 +351,11 @@ class FeedManager # @param [Integer] receiver_id # @param [Hash] crutches # @return [Boolean] - def filter_from_home?(status, receiver_id, crutches) + def filter_from_home?(status, receiver_id, crutches, timeline_type = :home) return false if receiver_id == status.account_id return true if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?) - return true if crutches[:languages][status.account_id].present? && status.language.present? && !crutches[:languages][status.account_id].include?(status.language) + return true if timeline_type != :list && crutches[:exclusive_list_users][status.account_id].present? + return true if crutches[:languages][status.account_id].present? && status.language.present? && !crutches[:languages][status.account_id].include?(status.language) check_for_blocks = crutches[:active_mentions][status.id] || [] check_for_blocks.push(status.account_id) @@ -543,13 +544,16 @@ class FeedManager arr end - crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map(&:in_reply_to_account_id)).pluck(:target_account_id).index_with(true) - crutches[:languages] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:account_id)).pluck(:target_account_id, :languages).to_h - crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map { |s| s.account_id if s.reblog? }, show_reblogs: false).pluck(:target_account_id).index_with(true) - crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true) - crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true) - crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.flat_map { |s| [s.account.domain, s.reblog&.account&.domain] }.compact).pluck(:domain).index_with(true) - crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| [s.account_id, s.reblog&.account_id] }.flatten.compact).pluck(:account_id).index_with(true) + lists = List.where(account_id: receiver_id, exclusive: true) + + crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map(&:in_reply_to_account_id)).pluck(:target_account_id).index_with(true) + crutches[:languages] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:account_id)).pluck(:target_account_id, :languages).to_h + crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map { |s| s.account_id if s.reblog? }, show_reblogs: false).pluck(:target_account_id).index_with(true) + crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true) + crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true) + crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.flat_map { |s| [s.account.domain, s.reblog&.account&.domain] }.compact).pluck(:domain).index_with(true) + crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| [s.account_id, s.reblog&.account_id] }.flatten.compact).pluck(:account_id).index_with(true) + crutches[:exclusive_list_users] = ListAccount.where(list: lists, account_id: statuses.map(&:account_id)).pluck(:account_id).index_with(true) crutches end diff --git a/app/models/list.rb b/app/models/list.rb index bd1bdbd24d..7dc96f01b3 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -10,6 +10,7 @@ # created_at :datetime not null # updated_at :datetime not null # replies_policy :integer default("list"), not null +# exclusive :boolean default(FALSE) # class List < ApplicationRecord diff --git a/app/serializers/rest/list_serializer.rb b/app/serializers/rest/list_serializer.rb index 3e87f71196..6a1b6ea3eb 100644 --- a/app/serializers/rest/list_serializer.rb +++ b/app/serializers/rest/list_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class REST::ListSerializer < ActiveModel::Serializer - attributes :id, :title, :replies_policy + attributes :id, :title, :replies_policy, :exclusive def id object.id.to_s diff --git a/db/migrate/20230605085710_add_exclusive_to_lists.rb b/db/migrate/20230605085710_add_exclusive_to_lists.rb new file mode 100644 index 0000000000..cc21a3e315 --- /dev/null +++ b/db/migrate/20230605085710_add_exclusive_to_lists.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddExclusiveToLists < ActiveRecord::Migration[6.1] + def change + add_column :lists, :exclusive, :boolean, null: false, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 98fa5d6004..35fbb8d2ef 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_05_31_154811) do +ActiveRecord::Schema.define(version: 2023_06_05_085710) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -567,6 +567,7 @@ ActiveRecord::Schema.define(version: 2023_05_31_154811) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "replies_policy", default: 0, null: false + t.boolean "exclusive", default: false t.index ["account_id"], name: "index_lists_on_account_id" end diff --git a/spec/lib/feed_manager_spec.rb b/spec/lib/feed_manager_spec.rb index 79d1f5249e..31b53fd879 100644 --- a/spec/lib/feed_manager_spec.rb +++ b/spec/lib/feed_manager_spec.rb @@ -26,6 +26,7 @@ RSpec.describe FeedManager do let(:alice) { Fabricate(:account, username: 'alice') } let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com') } let(:jeff) { Fabricate(:account, username: 'jeff') } + let(:list) { Fabricate(:list, account: alice) } context 'with home feed' do it 'returns false for followee\'s status' do @@ -153,6 +154,42 @@ RSpec.describe FeedManager do status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de') expect(FeedManager.instance.filter?(:home, status, alice)).to be false end + + it 'returns true for post from followee on exclusive list' do + list.exclusive = true + alice.follow!(bob) + list.accounts << bob + allow(List).to receive(:where).and_return(list) + status = Fabricate(:status, text: 'I post a lot', account: bob) + expect(FeedManager.instance.filter?(:home, status, alice)).to be true + end + + it 'returns true for reblog from followee on exclusive list' do + list.exclusive = true + alice.follow!(jeff) + list.accounts << jeff + allow(List).to receive(:where).and_return(list) + status = Fabricate(:status, text: 'I post a lot', account: bob) + reblog = Fabricate(:status, reblog: status, account: jeff) + expect(FeedManager.instance.filter?(:home, reblog, alice)).to be true + end + + it 'returns false for post from followee on non-exclusive list' do + list.exclusive = false + alice.follow!(bob) + list.accounts << bob + status = Fabricate(:status, text: 'I post a lot', account: bob) + expect(FeedManager.instance.filter?(:home, status, alice)).to be false + end + + it 'returns false for reblog from followee on non-exclusive list' do + list.exclusive = false + alice.follow!(jeff) + list.accounts << jeff + status = Fabricate(:status, text: 'I post a lot', account: bob) + reblog = Fabricate(:status, reblog: status, account: jeff) + expect(FeedManager.instance.filter?(:home, reblog, alice)).to be false + end end context 'with mentions feed' do From c2c396157fa76160b2bb28fd224b1eac40c9c220 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 Jun 2023 09:52:36 +0200 Subject: [PATCH 55/69] Fix design issues with recent react-intl upgrade (#25272) --- app/javascript/mastodon/locales/intl_provider.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/mastodon/locales/intl_provider.tsx b/app/javascript/mastodon/locales/intl_provider.tsx index 1ea77c798e..4fa8b2247c 100644 --- a/app/javascript/mastodon/locales/intl_provider.tsx +++ b/app/javascript/mastodon/locales/intl_provider.tsx @@ -48,6 +48,7 @@ export const IntlProvider: React.FC< locale={locale} messages={messages} onError={onProviderError} + textComponent='span' {...props} > {children} From a8310b15ed95dc262084940d79edb2f456f96742 Mon Sep 17 00:00:00 2001 From: Nick Schonning Date: Mon, 5 Jun 2023 08:40:35 -0400 Subject: [PATCH 56/69] Update kt-paperclip 7.2 from sha (#25274) --- Gemfile | 2 +- Gemfile.lock | 20 +++++++------------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/Gemfile b/Gemfile index cff8cb1f88..ad164af1e4 100644 --- a/Gemfile +++ b/Gemfile @@ -20,7 +20,7 @@ gem 'dotenv-rails', '~> 2.8' gem 'aws-sdk-s3', '~> 1.123', require: false gem 'fog-core', '<= 2.4.0' gem 'fog-openstack', '~> 0.3', require: false -gem 'kt-paperclip', '~> 7.1', github: 'kreeti/kt-paperclip', ref: '11abf222dc31bff71160a1d138b445214f434b2b' +gem 'kt-paperclip', '~> 7.2' gem 'blurhash', '~> 0.1' gem 'active_model_serializers', '~> 0.10' diff --git a/Gemfile.lock b/Gemfile.lock index bb209b3841..a9919bd3a2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,18 +7,6 @@ GIT hkdf (~> 0.2) jwt (~> 2.0) -GIT - remote: https://github.com/kreeti/kt-paperclip.git - revision: 11abf222dc31bff71160a1d138b445214f434b2b - ref: 11abf222dc31bff71160a1d138b445214f434b2b - specs: - kt-paperclip (7.1.1) - activemodel (>= 4.2.0) - activesupport (>= 4.2.0) - marcel (~> 1.0.1) - mime-types - terrapin (~> 0.6.0) - GIT remote: https://github.com/mastodon/rails-settings-cached.git revision: 86328ef0bd04ce21cc0504ff5e334591e8c2ccab @@ -380,6 +368,12 @@ GEM activerecord kaminari-core (= 1.2.2) kaminari-core (1.2.2) + kt-paperclip (7.2.0) + activemodel (>= 4.2.0) + activesupport (>= 4.2.0) + marcel (~> 1.0.1) + mime-types + terrapin (~> 0.6.0) launchy (2.5.2) addressable (~> 2.8) letter_opener (1.8.1) @@ -813,7 +807,7 @@ DEPENDENCIES json-ld-preloaded (~> 3.2) json-schema (~> 4.0) kaminari (~> 1.2) - kt-paperclip (~> 7.1)! + kt-paperclip (~> 7.2) letter_opener (~> 1.8) letter_opener_web (~> 2.0) link_header (~> 0.0) From a6c898f50160c322eb53252a9a2fff8b7b93a0fb Mon Sep 17 00:00:00 2001 From: "S.H" Date: Mon, 5 Jun 2023 21:49:51 +0900 Subject: [PATCH 57/69] Fix not shown announcements in hometimeline. (#25251) --- app/javascript/mastodon/actions/importer/normalizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index 3232e12a2b..9ed6b583b5 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -138,7 +138,7 @@ export function normalizePollOptionTranslation(translation, poll) { export function normalizeAnnouncement(announcement) { const normalAnnouncement = { ...announcement }; - const emojiMap = makeEmojiMap.emojis(normalAnnouncement); + const emojiMap = makeEmojiMap(normalAnnouncement.emojis); normalAnnouncement.contentHtml = emojify(normalAnnouncement.content, emojiMap); From 70cd2d600029bb43e98994886b4e528c565fafd6 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Mon, 5 Jun 2023 09:51:25 -0300 Subject: [PATCH 58/69] Add test coverage for `Mastodon::CLI::Accounts#cull` (#25250) --- spec/lib/mastodon/cli/accounts_spec.rb | 139 +++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/spec/lib/mastodon/cli/accounts_spec.rb b/spec/lib/mastodon/cli/accounts_spec.rb index 50066572c6..cf1d612f3d 100644 --- a/spec/lib/mastodon/cli/accounts_spec.rb +++ b/spec/lib/mastodon/cli/accounts_spec.rb @@ -1109,4 +1109,143 @@ describe Mastodon::CLI::Accounts do end end end + + describe '#cull' do + let(:delete_account_service) { instance_double(DeleteAccountService, call: nil) } + let!(:tom) { Fabricate(:account, updated_at: 30.days.ago, username: 'tom', uri: 'https://example.com/users/tom', domain: 'example.com') } + let!(:bob) { Fabricate(:account, updated_at: 30.days.ago, last_webfingered_at: nil, username: 'bob', uri: 'https://example.org/users/bob', domain: 'example.org') } + let!(:gon) { Fabricate(:account, updated_at: 15.days.ago, last_webfingered_at: 15.days.ago, username: 'gon', uri: 'https://example.net/users/gon', domain: 'example.net') } + let!(:ana) { Fabricate(:account, username: 'ana', uri: 'https://example.com/users/ana', domain: 'example.com') } + let!(:tales) { Fabricate(:account, updated_at: 10.days.ago, last_webfingered_at: nil, username: 'tales', uri: 'https://example.net/users/tales', domain: 'example.net') } + + before do + allow(DeleteAccountService).to receive(:new).and_return(delete_account_service) + end + + context 'when no domain is specified' do + let(:scope) { Account.remote.where(protocol: :activitypub).partitioned } + + before do + allow(cli).to receive(:parallelize_with_progress).and_yield(tom) + .and_yield(bob) + .and_yield(gon) + .and_yield(ana) + .and_yield(tales) + .and_return([5, 3]) + stub_request(:head, 'https://example.org/users/bob').to_return(status: 404) + stub_request(:head, 'https://example.net/users/gon').to_return(status: 410) + stub_request(:head, 'https://example.net/users/tales').to_return(status: 200) + end + + it 'deletes all inactive remote accounts that longer exist in the origin server' do + cli.cull + + expect(cli).to have_received(:parallelize_with_progress).with(scope).once + expect(delete_account_service).to have_received(:call).with(bob, reserve_username: false).once + expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once + end + + it 'does not delete any active remote account that still exists in the origin server' do + cli.cull + + expect(cli).to have_received(:parallelize_with_progress).with(scope).once + expect(delete_account_service).to_not have_received(:call).with(tom, reserve_username: false) + expect(delete_account_service).to_not have_received(:call).with(ana, reserve_username: false) + expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false) + end + + it 'touches inactive remote accounts that have not been deleted' do + allow(tales).to receive(:touch) + + cli.cull + + expect(tales).to have_received(:touch).once + end + + it 'displays the summary correctly' do + expect { cli.cull }.to output( + a_string_including('Visited 5 accounts, removed 3') + ).to_stdout + end + end + + context 'when a domain is specified' do + let(:domain) { 'example.net' } + let(:scope) { Account.remote.where(protocol: :activitypub, domain: domain).partitioned } + + before do + allow(cli).to receive(:parallelize_with_progress).and_yield(gon) + .and_yield(tales) + .and_return([2, 2]) + stub_request(:head, 'https://example.net/users/gon').to_return(status: 410) + stub_request(:head, 'https://example.net/users/tales').to_return(status: 404) + end + + it 'deletes inactive remote accounts that longer exist in the specified domain' do + cli.cull(domain) + + expect(cli).to have_received(:parallelize_with_progress).with(scope).once + expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once + expect(delete_account_service).to have_received(:call).with(tales, reserve_username: false).once + end + + it 'displays the summary correctly' do + expect { cli.cull }.to output( + a_string_including('Visited 2 accounts, removed 2') + ).to_stdout + end + end + + context 'when a domain is unavailable' do + shared_examples 'an unavailable domain' do + before do + allow(cli).to receive(:parallelize_with_progress).and_yield(tales).and_return([1, 0]) + end + + it 'skips accounts from the unavailable domain' do + cli.cull + + expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false) + end + + it 'displays the summary correctly' do + expect { cli.cull }.to output( + a_string_including("Visited 1 accounts, removed 0\nThe following domains were not available during the check:\n example.net") + ).to_stdout + end + end + + context 'when a connection timeout occurs' do + before do + stub_request(:head, 'https://example.net/users/tales').to_timeout + end + + it_behaves_like 'an unavailable domain' + end + + context 'when a connection error occurs' do + before do + stub_request(:head, 'https://example.net/users/tales').to_raise(HTTP::ConnectionError) + end + + it_behaves_like 'an unavailable domain' + end + + context 'when an ssl error occurs' do + before do + stub_request(:head, 'https://example.net/users/tales').to_raise(OpenSSL::SSL::SSLError) + end + + it_behaves_like 'an unavailable domain' + end + + context 'when a private network address error occurs' do + before do + stub_request(:head, 'https://example.net/users/tales').to_raise(Mastodon::PrivateNetworkAddressError) + end + + it_behaves_like 'an unavailable domain' + end + end + end end From 3b21c13dcc80bad4d8d1ec7c7c52470c5d3942aa Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 5 Jun 2023 10:52:33 -0400 Subject: [PATCH 59/69] Rails 7 compatibility fix for `Admin::Metrics::Dimension` classes (#25277) --- .../dimension/instance_accounts_dimension.rb | 19 +++++++----- .../dimension/instance_languages_dimension.rb | 25 ++++++++++++---- .../metrics/dimension/languages_dimension.rb | 19 +++++++----- .../admin/metrics/dimension/query_helper.rb | 13 ++++++++ .../metrics/dimension/servers_dimension.rb | 24 +++++++++++---- .../metrics/dimension/sources_dimension.rb | 20 ++++++++----- .../dimension/tag_languages_dimension.rb | 29 ++++++++++++++---- .../dimension/tag_servers_dimension.rb | 30 +++++++++++++++---- .../instance_accounts_dimension_spec.rb | 18 +++++++++++ .../instance_languages_dimension_spec.rb | 18 +++++++++++ .../dimension/languages_dimension_spec.rb | 18 +++++++++++ .../dimension/servers_dimension_spec.rb | 18 +++++++++++ .../software_versions_dimension_spec.rb | 18 +++++++++++ .../dimension/sources_dimension_spec.rb | 18 +++++++++++ .../dimension/space_usage_dimension_spec.rb | 18 +++++++++++ .../dimension/tag_languages_dimension_spec.rb | 18 +++++++++++ .../dimension/tag_servers_dimension_spec.rb | 18 +++++++++++ 17 files changed, 297 insertions(+), 44 deletions(-) create mode 100644 app/lib/admin/metrics/dimension/query_helper.rb create mode 100644 spec/lib/admin/metrics/dimension/instance_accounts_dimension_spec.rb create mode 100644 spec/lib/admin/metrics/dimension/instance_languages_dimension_spec.rb create mode 100644 spec/lib/admin/metrics/dimension/languages_dimension_spec.rb create mode 100644 spec/lib/admin/metrics/dimension/servers_dimension_spec.rb create mode 100644 spec/lib/admin/metrics/dimension/software_versions_dimension_spec.rb create mode 100644 spec/lib/admin/metrics/dimension/sources_dimension_spec.rb create mode 100644 spec/lib/admin/metrics/dimension/space_usage_dimension_spec.rb create mode 100644 spec/lib/admin/metrics/dimension/tag_languages_dimension_spec.rb create mode 100644 spec/lib/admin/metrics/dimension/tag_servers_dimension_spec.rb diff --git a/app/lib/admin/metrics/dimension/instance_accounts_dimension.rb b/app/lib/admin/metrics/dimension/instance_accounts_dimension.rb index 4eac8e611e..f8eb9d7bfb 100644 --- a/app/lib/admin/metrics/dimension/instance_accounts_dimension.rb +++ b/app/lib/admin/metrics/dimension/instance_accounts_dimension.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Admin::Metrics::Dimension::InstanceAccountsDimension < Admin::Metrics::Dimension::BaseDimension + include Admin::Metrics::Dimension::QueryHelper include LanguagesHelper def self.with_params? @@ -14,19 +15,23 @@ class Admin::Metrics::Dimension::InstanceAccountsDimension < Admin::Metrics::Dim protected def perform_query - sql = <<-SQL.squish + dimension_data_rows.map { |row| { key: row['username'], human_key: row['username'], value: row['value'].to_s } } + end + + def sql_array + [sql_query_string, { domain: params[:domain], limit: @limit }] + end + + def sql_query_string + <<~SQL.squish SELECT accounts.username, count(follows.*) AS value FROM accounts LEFT JOIN follows ON follows.target_account_id = accounts.id - WHERE accounts.domain = $1 + WHERE accounts.domain = :domain GROUP BY accounts.id, follows.target_account_id ORDER BY value DESC - LIMIT $2 + LIMIT :limit SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:domain]], [nil, @limit]]) - - rows.map { |row| { key: row['username'], human_key: row['username'], value: row['value'].to_s } } end def params diff --git a/app/lib/admin/metrics/dimension/instance_languages_dimension.rb b/app/lib/admin/metrics/dimension/instance_languages_dimension.rb index 1ede1a56e4..b478213808 100644 --- a/app/lib/admin/metrics/dimension/instance_languages_dimension.rb +++ b/app/lib/admin/metrics/dimension/instance_languages_dimension.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Admin::Metrics::Dimension::InstanceLanguagesDimension < Admin::Metrics::Dimension::BaseDimension + include Admin::Metrics::Dimension::QueryHelper include LanguagesHelper def self.with_params? @@ -14,21 +15,33 @@ class Admin::Metrics::Dimension::InstanceLanguagesDimension < Admin::Metrics::Di protected def perform_query - sql = <<-SQL.squish + dimension_data_rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } } + end + + def sql_array + [sql_query_string, { domain: params[:domain], earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }] + end + + def sql_query_string + <<~SQL.squish SELECT COALESCE(statuses.language, 'und') AS language, count(*) AS value FROM statuses INNER JOIN accounts ON accounts.id = statuses.account_id - WHERE accounts.domain = $1 - AND statuses.id BETWEEN $2 AND $3 + WHERE accounts.domain = :domain + AND statuses.id BETWEEN :earliest_status_id AND :latest_status_id AND statuses.reblog_of_id IS NULL GROUP BY COALESCE(statuses.language, 'und') ORDER BY count(*) DESC - LIMIT $4 + LIMIT :limit SQL + end - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:domain]], [nil, Mastodon::Snowflake.id_at(@start_at, with_random: false)], [nil, Mastodon::Snowflake.id_at(@end_at, with_random: false)], [nil, @limit]]) + def earliest_status_id + Mastodon::Snowflake.id_at(@start_at, with_random: false) + end - rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } } + def latest_status_id + Mastodon::Snowflake.id_at(@end_at, with_random: false) end def params diff --git a/app/lib/admin/metrics/dimension/languages_dimension.rb b/app/lib/admin/metrics/dimension/languages_dimension.rb index f1cf82cf27..100692a17b 100644 --- a/app/lib/admin/metrics/dimension/languages_dimension.rb +++ b/app/lib/admin/metrics/dimension/languages_dimension.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Admin::Metrics::Dimension::LanguagesDimension < Admin::Metrics::Dimension::BaseDimension + include Admin::Metrics::Dimension::QueryHelper include LanguagesHelper def key @@ -10,18 +11,22 @@ class Admin::Metrics::Dimension::LanguagesDimension < Admin::Metrics::Dimension: protected def perform_query - sql = <<-SQL.squish + dimension_data_rows.map { |row| { key: row['locale'], human_key: standard_locale_name(row['locale']), value: row['value'].to_s } } + end + + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at, limit: @limit }] + end + + def sql_query_string + <<~SQL.squish SELECT locale, count(*) AS value FROM users - WHERE current_sign_in_at BETWEEN $1 AND $2 + WHERE current_sign_in_at BETWEEN :start_at AND :end_at AND locale IS NOT NULL GROUP BY locale ORDER BY count(*) DESC - LIMIT $3 + LIMIT :limit SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, @limit]]) - - rows.map { |row| { key: row['locale'], human_key: standard_locale_name(row['locale']), value: row['value'].to_s } } end end diff --git a/app/lib/admin/metrics/dimension/query_helper.rb b/app/lib/admin/metrics/dimension/query_helper.rb new file mode 100644 index 0000000000..9fc953cb3e --- /dev/null +++ b/app/lib/admin/metrics/dimension/query_helper.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Admin::Metrics::Dimension::QueryHelper + protected + + def dimension_data_rows + ActiveRecord::Base.connection.select_all(sanitized_sql_string) + end + + def sanitized_sql_string + ActiveRecord::Base.sanitize_sql_array(sql_array) + end +end diff --git a/app/lib/admin/metrics/dimension/servers_dimension.rb b/app/lib/admin/metrics/dimension/servers_dimension.rb index 91bcce6551..42aba8e213 100644 --- a/app/lib/admin/metrics/dimension/servers_dimension.rb +++ b/app/lib/admin/metrics/dimension/servers_dimension.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Dimension::ServersDimension < Admin::Metrics::Dimension::BaseDimension + include Admin::Metrics::Dimension::QueryHelper + def key 'servers' end @@ -8,18 +10,30 @@ class Admin::Metrics::Dimension::ServersDimension < Admin::Metrics::Dimension::B protected def perform_query - sql = <<-SQL.squish + dimension_data_rows.map { |row| { key: row['domain'] || Rails.configuration.x.local_domain, human_key: row['domain'] || Rails.configuration.x.local_domain, value: row['value'].to_s } } + end + + def sql_array + [sql_query_string, { earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }] + end + + def sql_query_string + <<~SQL.squish SELECT accounts.domain, count(*) AS value FROM statuses INNER JOIN accounts ON accounts.id = statuses.account_id - WHERE statuses.id BETWEEN $1 AND $2 + WHERE statuses.id BETWEEN :earliest_status_id AND :latest_status_id GROUP BY accounts.domain ORDER BY count(*) DESC - LIMIT $3 + LIMIT :limit SQL + end - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, Mastodon::Snowflake.id_at(@start_at)], [nil, Mastodon::Snowflake.id_at(@end_at)], [nil, @limit]]) + def earliest_status_id + Mastodon::Snowflake.id_at(@start_at) + end - rows.map { |row| { key: row['domain'] || Rails.configuration.x.local_domain, human_key: row['domain'] || Rails.configuration.x.local_domain, value: row['value'].to_s } } + def latest_status_id + Mastodon::Snowflake.id_at(@end_at) end end diff --git a/app/lib/admin/metrics/dimension/sources_dimension.rb b/app/lib/admin/metrics/dimension/sources_dimension.rb index 122807cdcd..a14c3e7c16 100644 --- a/app/lib/admin/metrics/dimension/sources_dimension.rb +++ b/app/lib/admin/metrics/dimension/sources_dimension.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Dimension::SourcesDimension < Admin::Metrics::Dimension::BaseDimension + include Admin::Metrics::Dimension::QueryHelper + def key 'sources' end @@ -8,18 +10,22 @@ class Admin::Metrics::Dimension::SourcesDimension < Admin::Metrics::Dimension::B protected def perform_query - sql = <<-SQL.squish + dimension_data_rows.map { |row| { key: row['name'] || 'web', human_key: row['name'] || I18n.t('admin.dashboard.website'), value: row['value'].to_s } } + end + + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at, limit: @limit }] + end + + def sql_query_string + <<~SQL.squish SELECT oauth_applications.name, count(*) AS value FROM users LEFT JOIN oauth_applications ON oauth_applications.id = users.created_by_application_id - WHERE users.created_at BETWEEN $1 AND $2 + WHERE users.created_at BETWEEN :start_at AND :end_at GROUP BY oauth_applications.name ORDER BY count(*) DESC - LIMIT $3 + LIMIT :limit SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, @limit]]) - - rows.map { |row| { key: row['name'] || 'web', human_key: row['name'] || I18n.t('admin.dashboard.website'), value: row['value'].to_s } } end end diff --git a/app/lib/admin/metrics/dimension/tag_languages_dimension.rb b/app/lib/admin/metrics/dimension/tag_languages_dimension.rb index e1349c2294..cd077ff863 100644 --- a/app/lib/admin/metrics/dimension/tag_languages_dimension.rb +++ b/app/lib/admin/metrics/dimension/tag_languages_dimension.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Admin::Metrics::Dimension::TagLanguagesDimension < Admin::Metrics::Dimension::BaseDimension + include Admin::Metrics::Dimension::QueryHelper include LanguagesHelper def self.with_params? @@ -14,20 +15,36 @@ class Admin::Metrics::Dimension::TagLanguagesDimension < Admin::Metrics::Dimensi protected def perform_query - sql = <<-SQL.squish + dimension_data_rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } } + end + + def sql_array + [sql_query_string, { tag_id: tag_id, earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }] + end + + def sql_query_string + <<~SQL.squish SELECT COALESCE(statuses.language, 'und') AS language, count(*) AS value FROM statuses INNER JOIN statuses_tags ON statuses_tags.status_id = statuses.id - WHERE statuses_tags.tag_id = $1 - AND statuses.id BETWEEN $2 AND $3 + WHERE statuses_tags.tag_id = :tag_id + AND statuses.id BETWEEN :earliest_status_id AND :latest_status_id GROUP BY COALESCE(statuses.language, 'und') ORDER BY count(*) DESC - LIMIT $4 + LIMIT :limit SQL + end - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:id]], [nil, Mastodon::Snowflake.id_at(@start_at, with_random: false)], [nil, Mastodon::Snowflake.id_at(@end_at, with_random: false)], [nil, @limit]]) + def tag_id + params[:id] + end - rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } } + def earliest_status_id + Mastodon::Snowflake.id_at(@start_at, with_random: false) + end + + def latest_status_id + Mastodon::Snowflake.id_at(@end_at, with_random: false) end def params diff --git a/app/lib/admin/metrics/dimension/tag_servers_dimension.rb b/app/lib/admin/metrics/dimension/tag_servers_dimension.rb index 7ddf3378cd..fc5e49a966 100644 --- a/app/lib/admin/metrics/dimension/tag_servers_dimension.rb +++ b/app/lib/admin/metrics/dimension/tag_servers_dimension.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Dimension::TagServersDimension < Admin::Metrics::Dimension::BaseDimension + include Admin::Metrics::Dimension::QueryHelper + def self.with_params? true end @@ -12,21 +14,37 @@ class Admin::Metrics::Dimension::TagServersDimension < Admin::Metrics::Dimension protected def perform_query - sql = <<-SQL.squish + dimension_data_rows.map { |row| { key: row['domain'] || Rails.configuration.x.local_domain, human_key: row['domain'] || Rails.configuration.x.local_domain, value: row['value'].to_s } } + end + + def sql_array + [sql_query_string, { tag_id: tag_id, earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }] + end + + def sql_query_string + <<-SQL.squish SELECT accounts.domain, count(*) AS value FROM statuses INNER JOIN accounts ON accounts.id = statuses.account_id INNER JOIN statuses_tags ON statuses_tags.status_id = statuses.id - WHERE statuses_tags.tag_id = $1 - AND statuses.id BETWEEN $2 AND $3 + WHERE statuses_tags.tag_id = :tag_id + AND statuses.id BETWEEN :earliest_status_id AND :latest_status_id GROUP BY accounts.domain ORDER BY count(*) DESC - LIMIT $4 + LIMIT :limit SQL + end - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:id]], [nil, Mastodon::Snowflake.id_at(@start_at, with_random: false)], [nil, Mastodon::Snowflake.id_at(@end_at, with_random: false)], [nil, @limit]]) + def tag_id + params[:id] + end - rows.map { |row| { key: row['domain'] || Rails.configuration.x.local_domain, human_key: row['domain'] || Rails.configuration.x.local_domain, value: row['value'].to_s } } + def earliest_status_id + Mastodon::Snowflake.id_at(@start_at, with_random: false) + end + + def latest_status_id + Mastodon::Snowflake.id_at(@end_at, with_random: false) end def params diff --git a/spec/lib/admin/metrics/dimension/instance_accounts_dimension_spec.rb b/spec/lib/admin/metrics/dimension/instance_accounts_dimension_spec.rb new file mode 100644 index 0000000000..106717f97b --- /dev/null +++ b/spec/lib/admin/metrics/dimension/instance_accounts_dimension_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Dimension::InstanceAccountsDimension do + subject(:dimension) { described_class.new(start_at, end_at, limit, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:limit) { 10 } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { dimension.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/dimension/instance_languages_dimension_spec.rb b/spec/lib/admin/metrics/dimension/instance_languages_dimension_spec.rb new file mode 100644 index 0000000000..f9f6430ca0 --- /dev/null +++ b/spec/lib/admin/metrics/dimension/instance_languages_dimension_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Dimension::InstanceLanguagesDimension do + subject(:dimension) { described_class.new(start_at, end_at, limit, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:limit) { 10 } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { dimension.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/dimension/languages_dimension_spec.rb b/spec/lib/admin/metrics/dimension/languages_dimension_spec.rb new file mode 100644 index 0000000000..1722c4c616 --- /dev/null +++ b/spec/lib/admin/metrics/dimension/languages_dimension_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Dimension::LanguagesDimension do + subject(:dimension) { described_class.new(start_at, end_at, limit, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:limit) { 10 } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { dimension.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/dimension/servers_dimension_spec.rb b/spec/lib/admin/metrics/dimension/servers_dimension_spec.rb new file mode 100644 index 0000000000..7e2bb9ac0b --- /dev/null +++ b/spec/lib/admin/metrics/dimension/servers_dimension_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Dimension::ServersDimension do + subject(:dimension) { described_class.new(start_at, end_at, limit, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:limit) { 10 } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { dimension.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/dimension/software_versions_dimension_spec.rb b/spec/lib/admin/metrics/dimension/software_versions_dimension_spec.rb new file mode 100644 index 0000000000..ee14917330 --- /dev/null +++ b/spec/lib/admin/metrics/dimension/software_versions_dimension_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Dimension::SoftwareVersionsDimension do + subject(:dimension) { described_class.new(start_at, end_at, limit, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:limit) { 10 } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { dimension.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/dimension/sources_dimension_spec.rb b/spec/lib/admin/metrics/dimension/sources_dimension_spec.rb new file mode 100644 index 0000000000..d6b581a9bb --- /dev/null +++ b/spec/lib/admin/metrics/dimension/sources_dimension_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Dimension::SourcesDimension do + subject(:dimension) { described_class.new(start_at, end_at, limit, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:limit) { 10 } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { dimension.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/dimension/space_usage_dimension_spec.rb b/spec/lib/admin/metrics/dimension/space_usage_dimension_spec.rb new file mode 100644 index 0000000000..65d04cfedd --- /dev/null +++ b/spec/lib/admin/metrics/dimension/space_usage_dimension_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Dimension::SpaceUsageDimension do + subject(:dimension) { described_class.new(start_at, end_at, limit, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:limit) { 10 } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { dimension.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/dimension/tag_languages_dimension_spec.rb b/spec/lib/admin/metrics/dimension/tag_languages_dimension_spec.rb new file mode 100644 index 0000000000..721d24fa18 --- /dev/null +++ b/spec/lib/admin/metrics/dimension/tag_languages_dimension_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Dimension::TagLanguagesDimension do + subject(:dimension) { described_class.new(start_at, end_at, limit, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:limit) { 10 } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { dimension.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/dimension/tag_servers_dimension_spec.rb b/spec/lib/admin/metrics/dimension/tag_servers_dimension_spec.rb new file mode 100644 index 0000000000..3054716816 --- /dev/null +++ b/spec/lib/admin/metrics/dimension/tag_servers_dimension_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Dimension::TagServersDimension do + subject(:dimension) { described_class.new(start_at, end_at, limit, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:limit) { 10 } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { dimension.data }.to_not raise_error + end + end +end From 1483a3ddfe74e4fb81d87447a1781943eab86c60 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 Jun 2023 17:32:24 +0200 Subject: [PATCH 60/69] Add data-nosnippet so Google doesn't use trending posts in snippets for / (#25279) --- app/javascript/mastodon/features/explore/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/explore/index.jsx b/app/javascript/mastodon/features/explore/index.jsx index dbc0400e8e..185db0732a 100644 --- a/app/javascript/mastodon/features/explore/index.jsx +++ b/app/javascript/mastodon/features/explore/index.jsx @@ -67,7 +67,7 @@ class Explore extends PureComponent {
-
+
{isSearching ? ( ) : ( From e428670e614b59048431c79e849d18315da98d72 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 Jun 2023 17:35:05 +0200 Subject: [PATCH 61/69] Fix CSP headers when S3_ALIAS_HOST includes a path component (#25273) --- config/initializers/content_security_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index f4f9177996..a05b67440c 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -3,7 +3,7 @@ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy def host_to_url(str) - "http#{Rails.configuration.x.use_https ? 's' : ''}://#{str}" if str.present? + "http#{Rails.configuration.x.use_https ? 's' : ''}://#{str}".split('/').first if str.present? end base_host = Rails.configuration.x.web_domain From af135bddd0a17be9a1345216db7986b593d51151 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 5 Jun 2023 14:46:04 -0400 Subject: [PATCH 62/69] Update `Admin::Metrics::Measure` classes for Rails 7 (#25236) --- .../measure/instance_accounts_measure.rb | 24 ++++++--------- .../measure/instance_followers_measure.rb | 24 ++++++--------- .../measure/instance_follows_measure.rb | 24 ++++++--------- .../instance_media_attachments_measure.rb | 23 +++++--------- .../measure/instance_reports_measure.rb | 24 ++++++--------- .../measure/instance_statuses_measure.rb | 30 ++++++++++--------- .../metrics/measure/new_users_measure.rb | 16 +++++----- .../metrics/measure/opened_reports_measure.rb | 16 +++++----- app/lib/admin/metrics/measure/query_helper.rb | 25 ++++++++++++++++ .../measure/resolved_reports_measure.rb | 16 +++++----- .../metrics/measure/tag_servers_measure.rb | 24 ++++++++++----- .../measure/active_users_measure_spec.rb | 17 +++++++++++ .../measure/instance_accounts_measure_spec.rb | 6 ++++ .../instance_followers_measure_spec.rb | 6 ++++ .../measure/instance_follows_measure_spec.rb | 6 ++++ ...instance_media_attachments_measure_spec.rb | 6 ++++ .../measure/instance_reports_measure_spec.rb | 6 ++++ .../measure/instance_statuses_measure_spec.rb | 6 ++++ .../measure/interactions_measure_spec.rb | 17 +++++++++++ .../metrics/measure/new_users_measure_spec.rb | 17 +++++++++++ .../measure/opened_reports_measure_spec.rb | 17 +++++++++++ .../measure/resolved_reports_measure_spec.rb | 17 +++++++++++ .../measure/tag_accounts_measure_spec.rb | 19 ++++++++++++ .../measure/tag_servers_measure_spec.rb | 19 ++++++++++++ .../metrics/measure/tag_uses_measure_spec.rb | 19 ++++++++++++ 25 files changed, 307 insertions(+), 117 deletions(-) create mode 100644 app/lib/admin/metrics/measure/query_helper.rb create mode 100644 spec/lib/admin/metrics/measure/active_users_measure_spec.rb create mode 100644 spec/lib/admin/metrics/measure/interactions_measure_spec.rb create mode 100644 spec/lib/admin/metrics/measure/new_users_measure_spec.rb create mode 100644 spec/lib/admin/metrics/measure/opened_reports_measure_spec.rb create mode 100644 spec/lib/admin/metrics/measure/resolved_reports_measure_spec.rb create mode 100644 spec/lib/admin/metrics/measure/tag_accounts_measure_spec.rb create mode 100644 spec/lib/admin/metrics/measure/tag_servers_measure_spec.rb create mode 100644 spec/lib/admin/metrics/measure/tag_uses_measure_spec.rb diff --git a/app/lib/admin/metrics/measure/instance_accounts_measure.rb b/app/lib/admin/metrics/measure/instance_accounts_measure.rb index 14a61de88c..3d081fdd90 100644 --- a/app/lib/admin/metrics/measure/instance_accounts_measure.rb +++ b/app/lib/admin/metrics/measure/instance_accounts_measure.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Measure::InstanceAccountsMeasure < Admin::Metrics::Measure::BaseMeasure + include Admin::Metrics::Measure::QueryHelper + def self.with_params? true end @@ -25,33 +27,25 @@ class Admin::Metrics::Measure::InstanceAccountsMeasure < Admin::Metrics::Measure nil end - def perform_data_query - account_matching_sql = begin - if params[:include_subdomains] - "accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $3::text))" - else - 'accounts.domain = $3::text' - end - end + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at, domain: params[:domain] }] + end - sql = <<-SQL.squish + def sql_query_string + <<~SQL.squish SELECT axis.*, ( WITH new_accounts AS ( SELECT accounts.id FROM accounts WHERE date_trunc('day', accounts.created_at)::date = axis.period - AND #{account_matching_sql} + AND #{account_domain_sql(params[:include_subdomains])} ) SELECT count(*) FROM new_accounts ) AS value FROM ( - SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period ) AS axis SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, params[:domain]]]) - - rows.map { |row| { date: row['period'], value: row['value'].to_s } } end def time_period diff --git a/app/lib/admin/metrics/measure/instance_followers_measure.rb b/app/lib/admin/metrics/measure/instance_followers_measure.rb index dc0f5492c9..378c6754d9 100644 --- a/app/lib/admin/metrics/measure/instance_followers_measure.rb +++ b/app/lib/admin/metrics/measure/instance_followers_measure.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Measure::InstanceFollowersMeasure < Admin::Metrics::Measure::BaseMeasure + include Admin::Metrics::Measure::QueryHelper + def self.with_params? true end @@ -25,34 +27,26 @@ class Admin::Metrics::Measure::InstanceFollowersMeasure < Admin::Metrics::Measur nil end - def perform_data_query - account_matching_sql = begin - if params[:include_subdomains] - "accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $3::text))" - else - 'accounts.domain = $3::text' - end - end + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at, domain: params[:domain] }] + end - sql = <<-SQL.squish + def sql_query_string + <<~SQL.squish SELECT axis.*, ( WITH new_followers AS ( SELECT follows.id FROM follows INNER JOIN accounts ON follows.account_id = accounts.id WHERE date_trunc('day', follows.created_at)::date = axis.period - AND #{account_matching_sql} + AND #{account_domain_sql(params[:include_subdomains])} ) SELECT count(*) FROM new_followers ) AS value FROM ( - SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period ) AS axis SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, params[:domain]]]) - - rows.map { |row| { date: row['period'], value: row['value'].to_s } } end def time_period diff --git a/app/lib/admin/metrics/measure/instance_follows_measure.rb b/app/lib/admin/metrics/measure/instance_follows_measure.rb index f2088ffb30..e213348fbc 100644 --- a/app/lib/admin/metrics/measure/instance_follows_measure.rb +++ b/app/lib/admin/metrics/measure/instance_follows_measure.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Measure::InstanceFollowsMeasure < Admin::Metrics::Measure::BaseMeasure + include Admin::Metrics::Measure::QueryHelper + def self.with_params? true end @@ -25,34 +27,26 @@ class Admin::Metrics::Measure::InstanceFollowsMeasure < Admin::Metrics::Measure: nil end - def perform_data_query - account_matching_sql = begin - if params[:include_subdomains] - "accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $3::text))" - else - 'accounts.domain = $3::text' - end - end + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at, domain: params[:domain] }] + end - sql = <<-SQL.squish + def sql_query_string + <<~SQL.squish SELECT axis.*, ( WITH new_follows AS ( SELECT follows.id FROM follows INNER JOIN accounts ON follows.target_account_id = accounts.id WHERE date_trunc('day', follows.created_at)::date = axis.period - AND #{account_matching_sql} + AND #{account_domain_sql(params[:include_subdomains])} ) SELECT count(*) FROM new_follows ) AS value FROM ( - SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period ) AS axis SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, params[:domain]]]) - - rows.map { |row| { date: row['period'], value: row['value'].to_s } } end def time_period diff --git a/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb b/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb index 779883e031..2d4b5f56b0 100644 --- a/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb +++ b/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure < Admin::Metrics::Measure::BaseMeasure + include Admin::Metrics::Measure::QueryHelper include ActionView::Helpers::NumberHelper def self.with_params? @@ -35,34 +36,26 @@ class Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure < Admin::Metrics: nil end - def perform_data_query - account_matching_sql = begin - if params[:include_subdomains] - "accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $3::text))" - else - 'accounts.domain = $3::text' - end - end + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at, domain: params[:domain] }] + end - sql = <<-SQL.squish + def sql_query_string + <<~SQL.squish SELECT axis.*, ( WITH new_media_attachments AS ( SELECT COALESCE(media_attachments.file_file_size, 0) + COALESCE(media_attachments.thumbnail_file_size, 0) AS size FROM media_attachments INNER JOIN accounts ON accounts.id = media_attachments.account_id WHERE date_trunc('day', media_attachments.created_at)::date = axis.period - AND #{account_matching_sql} + AND #{account_domain_sql(params[:include_subdomains])} ) SELECT SUM(size) FROM new_media_attachments ) AS value FROM ( - SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period ) AS axis SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, params[:domain]]]) - - rows.map { |row| { date: row['period'], value: row['value'].to_s } } end def time_period diff --git a/app/lib/admin/metrics/measure/instance_reports_measure.rb b/app/lib/admin/metrics/measure/instance_reports_measure.rb index c1f7189bfe..9da3d53e34 100644 --- a/app/lib/admin/metrics/measure/instance_reports_measure.rb +++ b/app/lib/admin/metrics/measure/instance_reports_measure.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Measure::InstanceReportsMeasure < Admin::Metrics::Measure::BaseMeasure + include Admin::Metrics::Measure::QueryHelper + def self.with_params? true end @@ -25,34 +27,26 @@ class Admin::Metrics::Measure::InstanceReportsMeasure < Admin::Metrics::Measure: nil end - def perform_data_query - account_matching_sql = begin - if params[:include_subdomains] - "accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $3::text))" - else - 'accounts.domain = $3::text' - end - end + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at, domain: params[:domain] }] + end - sql = <<-SQL.squish + def sql_query_string + <<~SQL.squish SELECT axis.*, ( WITH new_reports AS ( SELECT reports.id FROM reports INNER JOIN accounts ON accounts.id = reports.target_account_id WHERE date_trunc('day', reports.created_at)::date = axis.period - AND #{account_matching_sql} + AND #{account_domain_sql(params[:include_subdomains])} ) SELECT count(*) FROM new_reports ) AS value FROM ( - SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period ) AS axis SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, params[:domain]]]) - - rows.map { |row| { date: row['period'], value: row['value'].to_s } } end def time_period diff --git a/app/lib/admin/metrics/measure/instance_statuses_measure.rb b/app/lib/admin/metrics/measure/instance_statuses_measure.rb index 1b38b40c55..8c71c66145 100644 --- a/app/lib/admin/metrics/measure/instance_statuses_measure.rb +++ b/app/lib/admin/metrics/measure/instance_statuses_measure.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Measure::InstanceStatusesMeasure < Admin::Metrics::Measure::BaseMeasure + include Admin::Metrics::Measure::QueryHelper + def self.with_params? true end @@ -25,35 +27,35 @@ class Admin::Metrics::Measure::InstanceStatusesMeasure < Admin::Metrics::Measure nil end - def perform_data_query - account_matching_sql = begin - if params[:include_subdomains] - "accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || $5::text))" - else - 'accounts.domain = $5::text' - end - end + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at, domain: params[:domain], earliest_status_id: earliest_status_id, latest_status_id: latest_status_id }] + end - sql = <<-SQL.squish + def sql_query_string + <<~SQL.squish SELECT axis.*, ( WITH new_statuses AS ( SELECT statuses.id FROM statuses INNER JOIN accounts ON accounts.id = statuses.account_id - WHERE statuses.id BETWEEN $3 AND $4 - AND #{account_matching_sql} + WHERE statuses.id BETWEEN :earliest_status_id AND :latest_status_id + AND #{account_domain_sql(params[:include_subdomains])} AND date_trunc('day', statuses.created_at)::date = axis.period ) SELECT count(*) FROM new_statuses ) AS value FROM ( - SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period ) AS axis SQL + end - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, Mastodon::Snowflake.id_at(@start_at, with_random: false)], [nil, Mastodon::Snowflake.id_at(@end_at, with_random: false)], [nil, params[:domain]]]) + def earliest_status_id + Mastodon::Snowflake.id_at(@start_at, with_random: false) + end - rows.map { |row| { date: row['period'], value: row['value'].to_s } } + def latest_status_id + Mastodon::Snowflake.id_at(@end_at, with_random: false) end def time_period diff --git a/app/lib/admin/metrics/measure/new_users_measure.rb b/app/lib/admin/metrics/measure/new_users_measure.rb index 71191f1a22..6837c14c82 100644 --- a/app/lib/admin/metrics/measure/new_users_measure.rb +++ b/app/lib/admin/metrics/measure/new_users_measure.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Measure::NewUsersMeasure < Admin::Metrics::Measure::BaseMeasure + include Admin::Metrics::Measure::QueryHelper + def key 'new_users' end @@ -15,8 +17,12 @@ class Admin::Metrics::Measure::NewUsersMeasure < Admin::Metrics::Measure::BaseMe User.where(created_at: previous_time_period).count end - def perform_data_query - sql = <<-SQL.squish + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at }] + end + + def sql_query_string + <<~SQL.squish SELECT axis.*, ( WITH new_users AS ( SELECT users.id @@ -26,12 +32,8 @@ class Admin::Metrics::Measure::NewUsersMeasure < Admin::Metrics::Measure::BaseMe SELECT count(*) FROM new_users ) AS value FROM ( - SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period ) AS axis SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at]]) - - rows.map { |row| { date: row['period'], value: row['value'].to_s } } end end diff --git a/app/lib/admin/metrics/measure/opened_reports_measure.rb b/app/lib/admin/metrics/measure/opened_reports_measure.rb index 4b80a0c8c3..c395c46341 100644 --- a/app/lib/admin/metrics/measure/opened_reports_measure.rb +++ b/app/lib/admin/metrics/measure/opened_reports_measure.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Measure::OpenedReportsMeasure < Admin::Metrics::Measure::BaseMeasure + include Admin::Metrics::Measure::QueryHelper + def key 'opened_reports' end @@ -15,8 +17,12 @@ class Admin::Metrics::Measure::OpenedReportsMeasure < Admin::Metrics::Measure::B Report.where(created_at: previous_time_period).count end - def perform_data_query - sql = <<-SQL.squish + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at }] + end + + def sql_query_string + <<~SQL.squish SELECT axis.*, ( WITH new_reports AS ( SELECT reports.id @@ -26,12 +32,8 @@ class Admin::Metrics::Measure::OpenedReportsMeasure < Admin::Metrics::Measure::B SELECT count(*) FROM new_reports ) AS value FROM ( - SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period ) AS axis SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at]]) - - rows.map { |row| { date: row['period'], value: row['value'].to_s } } end end diff --git a/app/lib/admin/metrics/measure/query_helper.rb b/app/lib/admin/metrics/measure/query_helper.rb new file mode 100644 index 0000000000..969065f73f --- /dev/null +++ b/app/lib/admin/metrics/measure/query_helper.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Admin::Metrics::Measure::QueryHelper + protected + + def perform_data_query + measurement_data_rows.map { |row| { date: row['period'], value: row['value'].to_s } } + end + + def measurement_data_rows + ActiveRecord::Base.connection.select_all(sanitized_sql_string) + end + + def sanitized_sql_string + ActiveRecord::Base.sanitize_sql_array(sql_array) + end + + def account_domain_sql(include_subdomains) + if include_subdomains + "accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || :domain::text))" + else + 'accounts.domain = :domain::text' + end + end +end diff --git a/app/lib/admin/metrics/measure/resolved_reports_measure.rb b/app/lib/admin/metrics/measure/resolved_reports_measure.rb index 4ab746c8fa..780db75a10 100644 --- a/app/lib/admin/metrics/measure/resolved_reports_measure.rb +++ b/app/lib/admin/metrics/measure/resolved_reports_measure.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Measure::ResolvedReportsMeasure < Admin::Metrics::Measure::BaseMeasure + include Admin::Metrics::Measure::QueryHelper + def key 'resolved_reports' end @@ -15,8 +17,12 @@ class Admin::Metrics::Measure::ResolvedReportsMeasure < Admin::Metrics::Measure: Report.resolved.where(action_taken_at: previous_time_period).count end - def perform_data_query - sql = <<-SQL.squish + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at }] + end + + def sql_query_string + <<~SQL.squish SELECT axis.*, ( WITH resolved_reports AS ( SELECT reports.id @@ -26,12 +32,8 @@ class Admin::Metrics::Measure::ResolvedReportsMeasure < Admin::Metrics::Measure: SELECT count(*) FROM resolved_reports ) AS value FROM ( - SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period ) AS axis SQL - - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at]]) - - rows.map { |row| { date: row['period'], value: row['value'].to_s } } end end diff --git a/app/lib/admin/metrics/measure/tag_servers_measure.rb b/app/lib/admin/metrics/measure/tag_servers_measure.rb index 11f229602e..e6378b8021 100644 --- a/app/lib/admin/metrics/measure/tag_servers_measure.rb +++ b/app/lib/admin/metrics/measure/tag_servers_measure.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Metrics::Measure::TagServersMeasure < Admin::Metrics::Measure::BaseMeasure + include Admin::Metrics::Measure::QueryHelper + def self.with_params? true end @@ -19,25 +21,33 @@ class Admin::Metrics::Measure::TagServersMeasure < Admin::Metrics::Measure::Base tag.statuses.where('statuses.id BETWEEN ? AND ?', Mastodon::Snowflake.id_at(@start_at - length_of_period, with_random: false), Mastodon::Snowflake.id_at(@end_at - length_of_period, with_random: false)).joins(:account).count('distinct accounts.domain') end - def perform_data_query - sql = <<-SQL.squish + def sql_array + [sql_query_string, { start_at: @start_at, end_at: @end_at, tag_id: tag.id, earliest_status_id: earliest_status_id, latest_status_id: latest_status_id }] + end + + def sql_query_string + <<~SQL.squish SELECT axis.*, ( SELECT count(distinct accounts.domain) AS value FROM statuses INNER JOIN statuses_tags ON statuses.id = statuses_tags.status_id INNER JOIN accounts ON statuses.account_id = accounts.id - WHERE statuses_tags.tag_id = $1 - AND statuses.id BETWEEN $2 AND $3 + WHERE statuses_tags.tag_id = :tag_id + AND statuses.id BETWEEN :earliest_status_id AND :latest_status_id AND date_trunc('day', statuses.created_at)::date = axis.day ) FROM ( - SELECT generate_series(date_trunc('day', $4::timestamp)::date, date_trunc('day', $5::timestamp)::date, ('1 day')::interval) AS day + SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, ('1 day')::interval) AS day ) as axis SQL + end - rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:id].to_i], [nil, Mastodon::Snowflake.id_at(@start_at, with_random: false)], [nil, Mastodon::Snowflake.id_at(@end_at, with_random: false)], [nil, @start_at], [nil, @end_at]]) + def earliest_status_id + Mastodon::Snowflake.id_at(@start_at, with_random: false) + end - rows.map { |row| { date: row['day'], value: row['value'].to_s } } + def latest_status_id + Mastodon::Snowflake.id_at(@end_at, with_random: false) end def tag diff --git a/spec/lib/admin/metrics/measure/active_users_measure_spec.rb b/spec/lib/admin/metrics/measure/active_users_measure_spec.rb new file mode 100644 index 0000000000..55164ed88a --- /dev/null +++ b/spec/lib/admin/metrics/measure/active_users_measure_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Measure::ActiveUsersMeasure do + subject(:measure) { described_class.new(start_at, end_at, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/measure/instance_accounts_measure_spec.rb b/spec/lib/admin/metrics/measure/instance_accounts_measure_spec.rb index 29a157491e..8e414963f3 100644 --- a/spec/lib/admin/metrics/measure/instance_accounts_measure_spec.rb +++ b/spec/lib/admin/metrics/measure/instance_accounts_measure_spec.rb @@ -37,4 +37,10 @@ describe Admin::Metrics::Measure::InstanceAccountsMeasure do end end end + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end end diff --git a/spec/lib/admin/metrics/measure/instance_followers_measure_spec.rb b/spec/lib/admin/metrics/measure/instance_followers_measure_spec.rb index ebf789c1b3..c627e6cede 100644 --- a/spec/lib/admin/metrics/measure/instance_followers_measure_spec.rb +++ b/spec/lib/admin/metrics/measure/instance_followers_measure_spec.rb @@ -39,4 +39,10 @@ describe Admin::Metrics::Measure::InstanceFollowersMeasure do end end end + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end end diff --git a/spec/lib/admin/metrics/measure/instance_follows_measure_spec.rb b/spec/lib/admin/metrics/measure/instance_follows_measure_spec.rb index 335e3c7321..42f33dfc35 100644 --- a/spec/lib/admin/metrics/measure/instance_follows_measure_spec.rb +++ b/spec/lib/admin/metrics/measure/instance_follows_measure_spec.rb @@ -39,4 +39,10 @@ describe Admin::Metrics::Measure::InstanceFollowsMeasure do end end end + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end end diff --git a/spec/lib/admin/metrics/measure/instance_media_attachments_measure_spec.rb b/spec/lib/admin/metrics/measure/instance_media_attachments_measure_spec.rb index 711a2aff05..c103307f97 100644 --- a/spec/lib/admin/metrics/measure/instance_media_attachments_measure_spec.rb +++ b/spec/lib/admin/metrics/measure/instance_media_attachments_measure_spec.rb @@ -40,4 +40,10 @@ describe Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure do end end end + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end end diff --git a/spec/lib/admin/metrics/measure/instance_reports_measure_spec.rb b/spec/lib/admin/metrics/measure/instance_reports_measure_spec.rb index f0ffd39cfb..62fcf84ac1 100644 --- a/spec/lib/admin/metrics/measure/instance_reports_measure_spec.rb +++ b/spec/lib/admin/metrics/measure/instance_reports_measure_spec.rb @@ -36,4 +36,10 @@ describe Admin::Metrics::Measure::InstanceReportsMeasure do end end end + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end end diff --git a/spec/lib/admin/metrics/measure/instance_statuses_measure_spec.rb b/spec/lib/admin/metrics/measure/instance_statuses_measure_spec.rb index c1425ecdb9..df4cfe207b 100644 --- a/spec/lib/admin/metrics/measure/instance_statuses_measure_spec.rb +++ b/spec/lib/admin/metrics/measure/instance_statuses_measure_spec.rb @@ -36,4 +36,10 @@ describe Admin::Metrics::Measure::InstanceStatusesMeasure do end end end + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end end diff --git a/spec/lib/admin/metrics/measure/interactions_measure_spec.rb b/spec/lib/admin/metrics/measure/interactions_measure_spec.rb new file mode 100644 index 0000000000..e98c830598 --- /dev/null +++ b/spec/lib/admin/metrics/measure/interactions_measure_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Measure::InteractionsMeasure do + subject(:measure) { described_class.new(start_at, end_at, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/measure/new_users_measure_spec.rb b/spec/lib/admin/metrics/measure/new_users_measure_spec.rb new file mode 100644 index 0000000000..fe82f8219b --- /dev/null +++ b/spec/lib/admin/metrics/measure/new_users_measure_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Measure::NewUsersMeasure do + subject(:measure) { described_class.new(start_at, end_at, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/measure/opened_reports_measure_spec.rb b/spec/lib/admin/metrics/measure/opened_reports_measure_spec.rb new file mode 100644 index 0000000000..deed64ae88 --- /dev/null +++ b/spec/lib/admin/metrics/measure/opened_reports_measure_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Measure::OpenedReportsMeasure do + subject(:measure) { described_class.new(start_at, end_at, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/measure/resolved_reports_measure_spec.rb b/spec/lib/admin/metrics/measure/resolved_reports_measure_spec.rb new file mode 100644 index 0000000000..cb98df2dc2 --- /dev/null +++ b/spec/lib/admin/metrics/measure/resolved_reports_measure_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Measure::ResolvedReportsMeasure do + subject(:measure) { described_class.new(start_at, end_at, params) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:params) { ActionController::Parameters.new } + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/measure/tag_accounts_measure_spec.rb b/spec/lib/admin/metrics/measure/tag_accounts_measure_spec.rb new file mode 100644 index 0000000000..938b67afa3 --- /dev/null +++ b/spec/lib/admin/metrics/measure/tag_accounts_measure_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Measure::TagAccountsMeasure do + subject(:measure) { described_class.new(start_at, end_at, params) } + + let!(:tag) { Fabricate(:tag) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:params) { ActionController::Parameters.new(id: tag.id) } + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/measure/tag_servers_measure_spec.rb b/spec/lib/admin/metrics/measure/tag_servers_measure_spec.rb new file mode 100644 index 0000000000..e09a2b04e5 --- /dev/null +++ b/spec/lib/admin/metrics/measure/tag_servers_measure_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Measure::TagServersMeasure do + subject(:measure) { described_class.new(start_at, end_at, params) } + + let!(:tag) { Fabricate(:tag) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:params) { ActionController::Parameters.new(id: tag.id) } + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end +end diff --git a/spec/lib/admin/metrics/measure/tag_uses_measure_spec.rb b/spec/lib/admin/metrics/measure/tag_uses_measure_spec.rb new file mode 100644 index 0000000000..869e937445 --- /dev/null +++ b/spec/lib/admin/metrics/measure/tag_uses_measure_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::Metrics::Measure::TagUsesMeasure do + subject(:measure) { described_class.new(start_at, end_at, params) } + + let!(:tag) { Fabricate(:tag) } + + let(:start_at) { 2.days.ago } + let(:end_at) { Time.now.utc } + let(:params) { ActionController::Parameters.new(id: tag.id) } + + describe '#data' do + it 'runs data query without error' do + expect { measure.data }.to_not raise_error + end + end +end From 0ddc89528213859bf4caeefd475a93ed973ab0e0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 6 Jun 2023 04:14:28 +0200 Subject: [PATCH 63/69] Change follow button in account row to be more obvious in web UI (#24956) --- .../mastodon/components/account.jsx | 41 ++++++++++--------- app/javascript/mastodon/locales/en.json | 8 ++-- .../styles/mastodon/components.scss | 6 ++- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/app/javascript/mastodon/components/account.jsx b/app/javascript/mastodon/components/account.jsx index ea863f5d18..0f3b85388c 100644 --- a/app/javascript/mastodon/components/account.jsx +++ b/app/javascript/mastodon/components/account.jsx @@ -16,6 +16,7 @@ import { VerifiedBadge } from 'mastodon/components/verified_badge'; import { me } from '../initial_state'; import { Avatar } from './avatar'; +import Button from './button'; import { DisplayName } from './display_name'; import { IconButton } from './icon_button'; import { RelativeTimestamp } from './relative_timestamp'; @@ -23,13 +24,13 @@ import { RelativeTimestamp } from './relative_timestamp'; const messages = defineMessages({ follow: { id: 'account.follow', defaultMessage: 'Follow' }, unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, - requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' }, - unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, - unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }, - mute_notifications: { id: 'account.mute_notifications', defaultMessage: 'Mute notifications from @{name}' }, - unmute_notifications: { id: 'account.unmute_notifications', defaultMessage: 'Unmute notifications from @{name}' }, - mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' }, - block: { id: 'account.block', defaultMessage: 'Block @{name}' }, + cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Withdraw follow request' }, + unblock: { id: 'account.unblock_short', defaultMessage: 'Unblock' }, + unmute: { id: 'account.unmute_short', defaultMessage: 'Unmute' }, + mute_notifications: { id: 'account.mute_notifications_short', defaultMessage: 'Mute notifications' }, + unmute_notifications: { id: 'account.unmute_notifications_short', defaultMessage: 'Unmute notifications' }, + mute: { id: 'account.mute_short', defaultMessage: 'Mute' }, + block: { id: 'account.block_short', defaultMessage: 'Block' }, }); class Account extends ImmutablePureComponent { @@ -96,39 +97,39 @@ class Account extends ImmutablePureComponent { let buttons; - if (actionIcon) { - if (onActionClick) { - buttons = ; - } - } else if (account.get('id') !== me && account.get('relationship', null) !== null) { + if (actionIcon && onActionClick) { + buttons = ; + } else if (!actionIcon && account.get('id') !== me && account.get('relationship', null) !== null) { const following = account.getIn(['relationship', 'following']); const requested = account.getIn(['relationship', 'requested']); const blocking = account.getIn(['relationship', 'blocking']); const muting = account.getIn(['relationship', 'muting']); if (requested) { - buttons = ; + buttons =
)} + + { antennas.length > 0 && ( +
+ + + +
    + { antennas.map(antenna => ( +
  • + +
  • + ))} +
+
+ )}