From 8ac63d780d38c14f0963859dec5d123836adb913 Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Mon, 30 Mar 2026 05:30:33 -0700 Subject: [PATCH 01/32] Update emoji regex to detect variations and sequences (#10138) --- src/alf/typography.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/alf/typography.tsx b/src/alf/typography.tsx index d79ad81ead..9b750f1537 100644 --- a/src/alf/typography.tsx +++ b/src/alf/typography.tsx @@ -1,12 +1,14 @@ import {Children} from 'react' -import {type TextProps as RNTextProps} from 'react-native' -import {type StyleProp, type TextStyle} from 'react-native' +import { + type StyleProp, + type TextProps as RNTextProps, + type TextStyle, +} from 'react-native' import {UITextView} from 'react-native-uitextview' import createEmojiRegex from 'emoji-regex' import {type Alf, applyFonts, atoms, flatten} from '#/alf' -import {IS_NATIVE} from '#/env' -import {IS_IOS} from '#/env' +import {IS_IOS, IS_NATIVE} from '#/env' /** * Ensures that `lineHeight` defaults to a relative value of `1`, or applies @@ -107,7 +109,8 @@ export function renderChildrenWithEmoji( }) } -const SINGLE_EMOJI_RE = /^[\p{Emoji_Presentation}\p{Extended_Pictographic}]+$/u +const SINGLE_EMOJI_RE = + /^[\p{Emoji_Presentation}\p{Extended_Pictographic}\uFE0F\u200D]+$/u export function isOnlyEmoji(text: string) { return text.length <= 15 && SINGLE_EMOJI_RE.test(text) } From a697841a21299b271acd74909512bef1ab299b06 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 31 Mar 2026 06:00:56 -0700 Subject: [PATCH 02/32] Update `typescript-eslint`, fix up eslint setup (#10150) --- eslint.config.mjs | 2 +- eslint/avoid-unwrapped-text.js | 1 + eslint/use-exact-imports.js | 1 + eslint/use-prefixed-imports.js | 1 + package.json | 7 +- yarn.lock | 788 ++++++--------------------------- 6 files changed, 134 insertions(+), 666 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 21cdd850a5..fc4474642d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -47,7 +47,6 @@ export default defineConfig( js.configs.recommended, tseslint.configs.recommendedTypeChecked, reactHooks.configs.flat.recommended, - // @ts-expect-error https://github.com/un-ts/eslint-plugin-import-x/issues/439 importX.flatConfigs.recommended, importX.flatConfigs.typescript, importX.flatConfigs['react-native'], @@ -62,6 +61,7 @@ export default defineConfig( 'react-native': reactNative, 'react-native-a11y': reactNativeA11y, 'simple-import-sort': simpleImportSort, + // @ts-expect-error - not sure why lingui, 'react-compiler': reactCompiler, 'bsky-internal': bskyInternal, diff --git a/eslint/avoid-unwrapped-text.js b/eslint/avoid-unwrapped-text.js index a963a94923..206617f191 100644 --- a/eslint/avoid-unwrapped-text.js +++ b/eslint/avoid-unwrapped-text.js @@ -29,6 +29,7 @@ function getTagName(node) { return reversedIdentifiers.reverse().join('.') } +/** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { type: 'problem', diff --git a/eslint/use-exact-imports.js b/eslint/use-exact-imports.js index 07f6c11e55..6ba2df5d6c 100644 --- a/eslint/use-exact-imports.js +++ b/eslint/use-exact-imports.js @@ -3,6 +3,7 @@ const BANNED_IMPORTS = [ '@fortawesome/free-solid-svg-icons', ] +/** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { type: 'suggestion', diff --git a/eslint/use-prefixed-imports.js b/eslint/use-prefixed-imports.js index 64a7b12f25..8c04803849 100644 --- a/eslint/use-prefixed-imports.js +++ b/eslint/use-prefixed-imports.js @@ -10,6 +10,7 @@ const BANNED_IMPORT_PREFIXES = [ 'view/', ] +/** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { type: 'suggestion', diff --git a/package.json b/package.json index a2b220df2c..e815bea407 100644 --- a/package.json +++ b/package.json @@ -246,7 +246,6 @@ "@lingui/cli": "^5.9.2", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", "@react-native/babel-preset": "0.81.5", - "@react-native/eslint-config": "^0.81.5", "@react-native/typescript-config": "^0.81.5", "@sentry/webpack-plugin": "^3.2.2", "@testing-library/react-native": "^13.2.0", @@ -264,8 +263,8 @@ "eslint": "^9.39.2", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-bsky-internal": "link:./eslint", - "eslint-plugin-import-x": "^4.16.1", - "eslint-plugin-lingui": "^0.11.0", + "eslint-plugin-import-x": "^4.16.2", + "eslint-plugin-lingui": "^0.12.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-compiler": "^19.1.0-rc.2", "eslint-plugin-react-hooks": "^7.0.1", @@ -287,7 +286,7 @@ "svgo": "^3.3.2", "ts-plugin-sort-import-suggestions": "^1.0.4", "typescript": "^6.0.2", - "typescript-eslint": "^8.57.2", + "typescript-eslint": "^8.58.0", "webpack-bundle-analyzer": "^4.10.1" }, "resolutions": { diff --git a/yarn.lock b/yarn.lock index 2f083a7ebf..a920bc60a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -297,15 +297,6 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/eslint-parser@^7.25.1": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.25.9.tgz#603c68a63078796527bc9d0833f5e52dd5f9224c" - integrity sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ== - dependencies: - "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" - eslint-visitor-keys "^2.1.0" - semver "^6.3.1" - "@babel/generator@^7.20.5": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" @@ -2196,7 +2187,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/traverse@^7.22.10", "@babel/traverse@^7.7.4": +"@babel/traverse@^7.22.10": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig== @@ -2605,20 +2596,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5" integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA== -"@eslint-community/eslint-utils@^4.2.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" - integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== - dependencies: - eslint-visitor-keys "^3.3.0" - -"@eslint-community/eslint-utils@^4.4.0": - version "4.4.1" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" - integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== - dependencies: - eslint-visitor-keys "^3.4.3" - "@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": version "4.9.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" @@ -2626,11 +2603,6 @@ dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.10.0": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" - integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== - "@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.12.2": version "4.12.2" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" @@ -3957,13 +3929,6 @@ "@emnapi/runtime" "^1.4.3" "@tybys/wasm-util" "^0.10.0" -"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": - version "5.1.1-v1" - resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" - integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg== - dependencies: - eslint-scope "5.1.1" - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -3985,6 +3950,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@package-json/types@^0.0.12": + version "0.0.12" + resolved "https://registry.yarnpkg.com/@package-json/types/-/types-0.0.12.tgz#4629e833ba128ed9880b6b7a947633ee22952462" + integrity sha512-uu43FGU34B5VM9mCNjXCwLaGHYjXdNincqKLaraaCW+7S2+SmiBg1Nv8bPnmschrIfZmfKNY9f3fC376MRrObw== + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -4797,29 +4767,6 @@ serve-static "^1.16.2" ws "^6.2.3" -"@react-native/eslint-config@^0.81.5": - version "0.81.5" - resolved "https://registry.yarnpkg.com/@react-native/eslint-config/-/eslint-config-0.81.5.tgz#c3624e546b0ade02c9576dc5cf9988e03da4a590" - integrity sha512-6MAn0ZjWQrWMqW09pEWTQAhLZ3WWB+zDRAZ/D1xj1Wyaz2qQH5KYfZMgnanhYIYuX7sxTS50ACMr/IOptMS1Og== - dependencies: - "@babel/core" "^7.25.2" - "@babel/eslint-parser" "^7.25.1" - "@react-native/eslint-plugin" "0.81.5" - "@typescript-eslint/eslint-plugin" "^7.1.1" - "@typescript-eslint/parser" "^7.1.1" - eslint-config-prettier "^8.5.0" - eslint-plugin-eslint-comments "^3.2.0" - eslint-plugin-ft-flow "^2.0.1" - eslint-plugin-jest "^27.9.0" - eslint-plugin-react "^7.30.1" - eslint-plugin-react-hooks "^5.2.0" - eslint-plugin-react-native "^4.0.0" - -"@react-native/eslint-plugin@0.81.5": - version "0.81.5" - resolved "https://registry.yarnpkg.com/@react-native/eslint-plugin/-/eslint-plugin-0.81.5.tgz#f3de8893c4490eb0f611a9e45a04c6da2a43cf9b" - integrity sha512-PyI+Xal1gBGKmcM595nxxXdCK12nXpEMwkg67POurC2t1J3jT9v8Dq3wiNsoBLXnRo8VdOME+BLwQQBeGedoTA== - "@react-native/gradle-plugin@0.81.5": version "0.81.5" resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.81.5.tgz#a58830f38789f6254b64449a17fe57455b589d00" @@ -5606,11 +5553,6 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== -"@types/semver@^7.3.12": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" - integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== - "@types/send@*": version "0.17.1" resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301" @@ -5676,289 +5618,100 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@8.57.2": - version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz#ad0dcefeca9c2ecbe09f730d478063666aee010b" - integrity sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w== +"@typescript-eslint/eslint-plugin@8.58.0": + version "8.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz#ad40e492f1931f46da1bd888e52b9e56df9063aa" + integrity sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.57.2" - "@typescript-eslint/type-utils" "8.57.2" - "@typescript-eslint/utils" "8.57.2" - "@typescript-eslint/visitor-keys" "8.57.2" + "@typescript-eslint/scope-manager" "8.58.0" + "@typescript-eslint/type-utils" "8.58.0" + "@typescript-eslint/utils" "8.58.0" + "@typescript-eslint/visitor-keys" "8.58.0" ignore "^7.0.5" natural-compare "^1.4.0" - ts-api-utils "^2.4.0" + ts-api-utils "^2.5.0" -"@typescript-eslint/eslint-plugin@^7.1.1": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz#b16d3cf3ee76bf572fdf511e79c248bdec619ea3" - integrity sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw== +"@typescript-eslint/parser@8.58.0": + version "8.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.58.0.tgz#da04ece1967b6c2fe8f10c3473dabf3825795ef7" + integrity sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA== dependencies: - "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "7.18.0" - "@typescript-eslint/type-utils" "7.18.0" - "@typescript-eslint/utils" "7.18.0" - "@typescript-eslint/visitor-keys" "7.18.0" - graphemer "^1.4.0" - ignore "^5.3.1" - natural-compare "^1.4.0" - ts-api-utils "^1.3.0" - -"@typescript-eslint/parser@8.57.2": - version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.57.2.tgz#b819955e39f976c0d4f95b5ed67fe22f85cd6898" - integrity sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA== - dependencies: - "@typescript-eslint/scope-manager" "8.57.2" - "@typescript-eslint/types" "8.57.2" - "@typescript-eslint/typescript-estree" "8.57.2" - "@typescript-eslint/visitor-keys" "8.57.2" + "@typescript-eslint/scope-manager" "8.58.0" + "@typescript-eslint/types" "8.58.0" + "@typescript-eslint/typescript-estree" "8.58.0" + "@typescript-eslint/visitor-keys" "8.58.0" debug "^4.4.3" -"@typescript-eslint/parser@^7.1.1": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.18.0.tgz#83928d0f1b7f4afa974098c64b5ce6f9051f96a0" - integrity sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg== +"@typescript-eslint/project-service@8.58.0": + version "8.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.58.0.tgz#66ceda0aabf7427aec3e2713fa43eb278dead2aa" + integrity sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg== dependencies: - "@typescript-eslint/scope-manager" "7.18.0" - "@typescript-eslint/types" "7.18.0" - "@typescript-eslint/typescript-estree" "7.18.0" - "@typescript-eslint/visitor-keys" "7.18.0" - debug "^4.3.4" - -"@typescript-eslint/project-service@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.53.0.tgz#327c67c61c16a1c8b12a440b0779b41eb77cc7df" - integrity sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg== - dependencies: - "@typescript-eslint/tsconfig-utils" "^8.53.0" - "@typescript-eslint/types" "^8.53.0" + "@typescript-eslint/tsconfig-utils" "^8.58.0" + "@typescript-eslint/types" "^8.58.0" debug "^4.4.3" -"@typescript-eslint/project-service@8.57.2": - version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.57.2.tgz#dfbc7777f9f633f2b06b558cda3836e76f856e3c" - integrity sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw== +"@typescript-eslint/scope-manager@8.58.0": + version "8.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz#e304142775e49a1b7ac3c8bf2536714447c72cab" + integrity sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.57.2" - "@typescript-eslint/types" "^8.57.2" + "@typescript-eslint/types" "8.58.0" + "@typescript-eslint/visitor-keys" "8.58.0" + +"@typescript-eslint/tsconfig-utils@8.58.0", "@typescript-eslint/tsconfig-utils@^8.58.0": + version "8.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz#c5a8edb21f31e0fdee565724e1b984171c559482" + integrity sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A== + +"@typescript-eslint/type-utils@8.58.0": + version "8.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz#ce0e72cd967ffbbe8de322db6089bd4374be352f" + integrity sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg== + dependencies: + "@typescript-eslint/types" "8.58.0" + "@typescript-eslint/typescript-estree" "8.58.0" + "@typescript-eslint/utils" "8.58.0" debug "^4.4.3" + ts-api-utils "^2.5.0" -"@typescript-eslint/scope-manager@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" - integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== +"@typescript-eslint/types@8.58.0", "@typescript-eslint/types@^8.56.0", "@typescript-eslint/types@^8.58.0": + version "8.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.58.0.tgz#e94ae7abdc1c6530e71183c1007b61fa93112a5a" + integrity sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww== + +"@typescript-eslint/typescript-estree@8.58.0": + version "8.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz#ed233faa8e2f2a2e1357c3e7d553d6465a0ee59a" + integrity sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA== dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - -"@typescript-eslint/scope-manager@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz#c928e7a9fc2c0b3ed92ab3112c614d6bd9951c83" - integrity sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA== - dependencies: - "@typescript-eslint/types" "7.18.0" - "@typescript-eslint/visitor-keys" "7.18.0" - -"@typescript-eslint/scope-manager@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.53.0.tgz#f922fcbf0d42e72f065297af31779ccf19de9a97" - integrity sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g== - dependencies: - "@typescript-eslint/types" "8.53.0" - "@typescript-eslint/visitor-keys" "8.53.0" - -"@typescript-eslint/scope-manager@8.57.2": - version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz#734dcde40677f430b5d963108337295bdbc09dae" - integrity sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw== - dependencies: - "@typescript-eslint/types" "8.57.2" - "@typescript-eslint/visitor-keys" "8.57.2" - -"@typescript-eslint/tsconfig-utils@8.53.0", "@typescript-eslint/tsconfig-utils@^8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.0.tgz#105279d7969a7abdc8345cc9c57cff83cf910f8f" - integrity sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA== - -"@typescript-eslint/tsconfig-utils@8.57.2", "@typescript-eslint/tsconfig-utils@^8.57.2": - version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz#cf82dc11e884103ec13188a7352591efaa1a887e" - integrity sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw== - -"@typescript-eslint/type-utils@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz#2165ffaee00b1fbbdd2d40aa85232dab6998f53b" - integrity sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA== - dependencies: - "@typescript-eslint/typescript-estree" "7.18.0" - "@typescript-eslint/utils" "7.18.0" - debug "^4.3.4" - ts-api-utils "^1.3.0" - -"@typescript-eslint/type-utils@8.57.2": - version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz#3ec65a94e73776252991a3cf0a15d220734c28f5" - integrity sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg== - dependencies: - "@typescript-eslint/types" "8.57.2" - "@typescript-eslint/typescript-estree" "8.57.2" - "@typescript-eslint/utils" "8.57.2" - debug "^4.4.3" - ts-api-utils "^2.4.0" - -"@typescript-eslint/types@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" - integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== - -"@typescript-eslint/types@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.18.0.tgz#b90a57ccdea71797ffffa0321e744f379ec838c9" - integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ== - -"@typescript-eslint/types@8.53.0", "@typescript-eslint/types@^8.35.0", "@typescript-eslint/types@^8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.53.0.tgz#1adcad3fa32bc2c4cbf3785ba07a5e3151819efb" - integrity sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ== - -"@typescript-eslint/types@8.57.2", "@typescript-eslint/types@^8.57.2": - version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.57.2.tgz#efe0da4c28b505ed458f113aa960dce2c5c671f4" - integrity sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA== - -"@typescript-eslint/typescript-estree@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" - integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/typescript-estree@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz#b5868d486c51ce8f312309ba79bdb9f331b37931" - integrity sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA== - dependencies: - "@typescript-eslint/types" "7.18.0" - "@typescript-eslint/visitor-keys" "7.18.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - minimatch "^9.0.4" - semver "^7.6.0" - ts-api-utils "^1.3.0" - -"@typescript-eslint/typescript-estree@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.0.tgz#7805b46b7a8ce97e91b7bb56fc8b1ba26ca8ef52" - integrity sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw== - dependencies: - "@typescript-eslint/project-service" "8.53.0" - "@typescript-eslint/tsconfig-utils" "8.53.0" - "@typescript-eslint/types" "8.53.0" - "@typescript-eslint/visitor-keys" "8.53.0" - debug "^4.4.3" - minimatch "^9.0.5" - semver "^7.7.3" - tinyglobby "^0.2.15" - ts-api-utils "^2.4.0" - -"@typescript-eslint/typescript-estree@8.57.2": - version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz#432e61a6cf2ab565837da387e5262c159672abea" - integrity sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA== - dependencies: - "@typescript-eslint/project-service" "8.57.2" - "@typescript-eslint/tsconfig-utils" "8.57.2" - "@typescript-eslint/types" "8.57.2" - "@typescript-eslint/visitor-keys" "8.57.2" + "@typescript-eslint/project-service" "8.58.0" + "@typescript-eslint/tsconfig-utils" "8.58.0" + "@typescript-eslint/types" "8.58.0" + "@typescript-eslint/visitor-keys" "8.58.0" debug "^4.4.3" minimatch "^10.2.2" semver "^7.7.3" tinyglobby "^0.2.15" - ts-api-utils "^2.4.0" + ts-api-utils "^2.5.0" -"@typescript-eslint/utils@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.18.0.tgz#bca01cde77f95fc6a8d5b0dbcbfb3d6ca4be451f" - integrity sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw== - dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "7.18.0" - "@typescript-eslint/types" "7.18.0" - "@typescript-eslint/typescript-estree" "7.18.0" - -"@typescript-eslint/utils@8.57.2": - version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.57.2.tgz#46a8974c24326fb8899486728428a0f1a3115014" - integrity sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg== +"@typescript-eslint/utils@8.58.0", "@typescript-eslint/utils@^8.57.2": + version "8.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.58.0.tgz#21a74a7963b0d288b719a4121c7dd555adaab3c3" + integrity sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.57.2" - "@typescript-eslint/types" "8.57.2" - "@typescript-eslint/typescript-estree" "8.57.2" + "@typescript-eslint/scope-manager" "8.58.0" + "@typescript-eslint/types" "8.58.0" + "@typescript-eslint/typescript-estree" "8.58.0" -"@typescript-eslint/utils@^5.10.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" - integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== +"@typescript-eslint/visitor-keys@8.58.0": + version "8.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz#2abd55a4be70fd55967aceaba4330b9ba9f45189" + integrity sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ== dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - eslint-scope "^5.1.1" - semver "^7.3.7" - -"@typescript-eslint/utils@^8.0.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.53.0.tgz#bf0a4e2edaf1afc9abce209fc02f8cab0b74af13" - integrity sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA== - dependencies: - "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.53.0" - "@typescript-eslint/types" "8.53.0" - "@typescript-eslint/typescript-estree" "8.53.0" - -"@typescript-eslint/visitor-keys@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" - integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== - dependencies: - "@typescript-eslint/types" "5.62.0" - eslint-visitor-keys "^3.3.0" - -"@typescript-eslint/visitor-keys@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz#0564629b6124d67607378d0f0332a0495b25e7d7" - integrity sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg== - dependencies: - "@typescript-eslint/types" "7.18.0" - eslint-visitor-keys "^3.4.3" - -"@typescript-eslint/visitor-keys@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.0.tgz#9a785664ddae7e3f7e570ad8166e48dbc9c6cf02" - integrity sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw== - dependencies: - "@typescript-eslint/types" "8.53.0" - eslint-visitor-keys "^4.2.1" - -"@typescript-eslint/visitor-keys@8.57.2": - version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz#a5c9605774247336c0412beb7dc288ab2a07c11e" - integrity sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw== - dependencies: - "@typescript-eslint/types" "8.57.2" + "@typescript-eslint/types" "8.58.0" eslint-visitor-keys "^5.0.0" "@ungap/structured-clone@^1.3.0": @@ -6537,11 +6290,6 @@ array-union@^1.0.1: dependencies: array-uniq "^1.0.1" -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - array-union@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/array-union/-/array-union-3.0.1.tgz#da52630d327f8b88cfbfb57728e2af5cd9b6b975" @@ -6585,16 +6333,6 @@ array.prototype.flat@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" - integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-shim-unscopables "^1.0.0" - array.prototype.flatmap@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" @@ -6605,17 +6343,6 @@ array.prototype.flatmap@^1.3.3: es-abstract "^1.23.5" es-shim-unscopables "^1.0.2" -array.prototype.tosorted@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" - integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-shim-unscopables "^1.0.0" - get-intrinsic "^1.1.3" - array.prototype.tosorted@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" @@ -6690,13 +6417,6 @@ 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== -asynciterator.prototype@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62" - integrity sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg== - dependencies: - has-symbols "^1.0.3" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -7059,6 +6779,13 @@ brace-expansion@^5.0.2: dependencies: balanced-match "^4.0.2" +brace-expansion@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" + integrity sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ== + dependencies: + balanced-match "^4.0.2" + braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -8486,7 +8213,7 @@ es-abstract@^1.17.5, es-abstract@^1.23.3, es-abstract@^1.23.6, es-abstract@^1.24 unbox-primitive "^1.1.0" which-typed-array "^1.1.19" -es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.3: +es-abstract@^1.19.0, es-abstract@^1.20.4: version "1.22.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== @@ -8643,26 +8370,6 @@ es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-iterator-helpers@^1.0.12: - version "1.0.13" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.13.tgz#72101046ffc19baf9996adc70e6177a26e6e8084" - integrity sha512-LK3VGwzvaPWobO8xzXXGRUOGw8Dcjyfk62CsY/wfHN75CwsJPbuypOYJxK6g5RyEL8YDjIWcl6jgd8foO6mmrA== - dependencies: - asynciterator.prototype "^1.0.0" - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.21.3" - es-set-tostringtag "^2.0.1" - function-bind "^1.1.1" - get-intrinsic "^1.2.1" - globalthis "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.5" - iterator.prototype "^1.1.0" - safe-array-concat "^1.0.0" - es-iterator-helpers@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz#d979a9f686e2b0b72f88dbead7229924544720bc" @@ -8831,11 +8538,6 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^8.5.0: - version "8.10.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" - integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== - eslint-import-context@^0.1.8, eslint-import-context@^0.1.9: version "0.1.9" resolved "https://registry.yarnpkg.com/eslint-import-context/-/eslint-import-context-0.1.9.tgz#967b0b2f0a90ef4b689125e088f790f0b7756dbe" @@ -8861,51 +8563,29 @@ eslint-import-resolver-typescript@^4.4.4: version "0.0.0" uid "" -eslint-plugin-eslint-comments@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz#9e1cd7b4413526abb313933071d7aba05ca12ffa" - integrity sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ== +eslint-plugin-import-x@^4.16.2: + version "4.16.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.2.tgz#95d1f798795566712c87897317ef8433d101db29" + integrity sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw== dependencies: - escape-string-regexp "^1.0.5" - ignore "^5.0.5" - -eslint-plugin-ft-flow@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-ft-flow/-/eslint-plugin-ft-flow-2.0.3.tgz#3b3c113c41902bcbacf0e22b536debcfc3c819e8" - integrity sha512-Vbsd/b+LYA99jUbsL6viEUWShFaYQt2YQs3QN3f+aeszOhh2sgdcU0mjzDyD4yyBvMc8qy2uwvBBWfMzEX06tg== - dependencies: - lodash "^4.17.21" - string-natural-compare "^3.0.1" - -eslint-plugin-import-x@^4.16.1: - version "4.16.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.1.tgz#a96ee1ad5ba6816f9a5573a9617935011a24c4df" - integrity sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ== - dependencies: - "@typescript-eslint/types" "^8.35.0" + "@package-json/types" "^0.0.12" + "@typescript-eslint/types" "^8.56.0" comment-parser "^1.4.1" debug "^4.4.1" eslint-import-context "^0.1.9" is-glob "^4.0.3" - minimatch "^9.0.3 || ^10.0.1" + minimatch "^9.0.3 || ^10.1.2" semver "^7.7.2" stable-hash-x "^0.2.0" unrs-resolver "^1.9.2" -eslint-plugin-jest@^27.9.0: - version "27.9.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz#7c98a33605e1d8b8442ace092b60e9919730000b" - integrity sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug== +eslint-plugin-lingui@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-lingui/-/eslint-plugin-lingui-0.12.0.tgz#fca175ad3ed40538483bfcd50c091851fefc3e8b" + integrity sha512-2+9P3thudGIBI10sDWYUIrGs3HIP09gv0XF98RDzZs34GAsAsGTUoSgrttKS1knAU5dwbrFKhNKu2LMrjRECag== dependencies: - "@typescript-eslint/utils" "^5.10.0" - -eslint-plugin-lingui@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-lingui/-/eslint-plugin-lingui-0.11.0.tgz#e33a4fe83698bb4cdfbfa816391fb79b68e6c026" - integrity sha512-O2Ixoapt5fa4VKZJgXhVwb6BHnzByIUDNMfZOhHWGMYk40GfGCho4MUfspLVrHAFLimgBPKXtCcJ8GC4YNZmfg== - dependencies: - "@typescript-eslint/utils" "^8.0.0" - micromatch "^4.0.0" + "@typescript-eslint/utils" "^8.57.2" + micromatch "^4.0.8" eslint-plugin-react-compiler@^19.1.0-rc.2: version "19.1.0-rc.2" @@ -8919,11 +8599,6 @@ eslint-plugin-react-compiler@^19.1.0-rc.2: zod "^3.22.4" zod-validation-error "^3.0.3" -eslint-plugin-react-hooks@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz#1be0080901e6ac31ce7971beed3d3ec0a423d9e3" - integrity sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg== - eslint-plugin-react-hooks@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz#66e258db58ece50723ef20cc159f8aa908219169" @@ -8949,14 +8624,6 @@ eslint-plugin-react-native-globals@^0.1.1: resolved "https://registry.yarnpkg.com/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz#ee1348bc2ceb912303ce6bdbd22e2f045ea86ea2" integrity sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g== -eslint-plugin-react-native@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-4.0.0.tgz#eec41984abe4970bdd7c6082dff7a98a5e34d0bb" - integrity sha512-kMmdxrSY7A1WgdqaGC+rY/28rh7kBGNBRsk48ovqkQmdg5j4K+DaFmegENDzMrdLkoufKGRNkKX6bgSwQTCAxQ== - dependencies: - "@babel/traverse" "^7.7.4" - eslint-plugin-react-native-globals "^0.1.1" - eslint-plugin-react-native@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-5.0.0.tgz#2ee990ba4967c557183b31121578547fb5c02d5d" @@ -8964,28 +8631,6 @@ eslint-plugin-react-native@^5.0.0: dependencies: eslint-plugin-react-native-globals "^0.1.1" -eslint-plugin-react@^7.30.1: - version "7.33.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608" - integrity sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw== - dependencies: - array-includes "^3.1.6" - array.prototype.flatmap "^1.3.1" - array.prototype.tosorted "^1.1.1" - doctrine "^2.1.0" - es-iterator-helpers "^1.0.12" - estraverse "^5.3.0" - jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.1.2" - object.entries "^1.1.6" - object.fromentries "^2.0.6" - object.hasown "^1.1.2" - object.values "^1.1.6" - prop-types "^15.8.1" - resolve "^2.0.0-next.4" - semver "^6.3.1" - string.prototype.matchall "^4.0.8" - eslint-plugin-react@^7.37.5: version "7.37.5" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz#2975511472bdda1b272b34d779335c9b0e877065" @@ -9015,7 +8660,7 @@ eslint-plugin-simple-import-sort@^12.1.1: resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz#e64bfdaf91c5b98a298619aa634a9f7aa43b709e" integrity sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA== -eslint-scope@5.1.1, eslint-scope@^5.1.1: +eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -9031,12 +8676,7 @@ eslint-scope@^8.4.0: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== @@ -9615,7 +9255,7 @@ fast-deep-equal@^3, fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.7, fast-glob@^3.2.9: +fast-glob@^3.2.7: version "3.3.1" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== @@ -10178,18 +9818,6 @@ globalthis@^1.0.4: define-properties "^1.2.1" gopd "^1.0.1" -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - globby@^12.0.2: version "12.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-12.2.0.tgz#2ab8046b4fba4ff6eede835b29f678f90e3d3c22" @@ -10230,11 +9858,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -10585,7 +10208,7 @@ ieee754@^1.1.13: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.0.5, ignore@^5.1.9, ignore@^5.2.0: +ignore@^5.1.9, ignore@^5.2.0: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -10663,7 +10286,7 @@ inline-style-prefixer@^7.0.1: dependencies: css-in-js-utils "^3.1.0" -internal-slot@^1.0.3, internal-slot@^1.0.5: +internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== @@ -10810,7 +10433,7 @@ is-ci@^3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.13.0, is-core-module@^2.9.0: +is-core-module@^2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== @@ -10856,13 +10479,6 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-finalizationregistry@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" - integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== - dependencies: - call-bind "^1.0.2" - is-finalizationregistry@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" @@ -10904,11 +10520,6 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-map@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" - integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== - is-map@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" @@ -11014,11 +10625,6 @@ is-regex@^1.2.1: has-tostringtag "^1.0.2" hasown "^2.0.2" -is-set@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" - integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== - is-set@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" @@ -11098,11 +10704,6 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== - is-weakmap@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" @@ -11122,14 +10723,6 @@ is-weakref@^1.1.0, is-weakref@^1.1.1: dependencies: call-bound "^1.0.3" -is-weakset@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" - integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - is-weakset@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" @@ -11218,17 +10811,6 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -iterator.prototype@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.0.tgz#690c88b043d821f783843aaf725d7ac3b62e3b46" - integrity sha512-rjuhAk1AJ1fssphHD0IFV6TWL40CwRZ53FrztKx43yk2v6rguBYsY4Bj1VU4HmoMmKwZUlx7mfnhDf9cOp4YTw== - dependencies: - define-properties "^1.1.4" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - has-tostringtag "^1.0.0" - reflect.getprototypeof "^1.0.3" - iterator.prototype@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" @@ -12635,7 +12217,7 @@ micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" -micromatch@^4.0.0, micromatch@^4.0.7: +micromatch@^4.0.7, micromatch@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -12692,7 +12274,7 @@ minimalistic-assert@^1.0.0: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@^10.1.1, "minimatch@^9.0.3 || ^10.0.1": +minimatch@^10.1.1: version "10.1.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.1.1.tgz#e6e61b9b0c1dcab116b5a7d1458e8b6ae9e73a55" integrity sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ== @@ -12720,13 +12302,20 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.0, minimatch@^9.0.5: +minimatch@^9.0.0: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" +"minimatch@^9.0.3 || ^10.1.2": + version "10.2.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1" + integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== + dependencies: + brace-expansion "^5.0.5" + minimatch@^9.0.4: version "9.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" @@ -13041,15 +12630,6 @@ object.assign@^4.1.7: has-symbols "^1.1.0" object-keys "^1.1.1" -object.entries@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" - integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - object.entries@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.9.tgz#e4770a6a1444afb61bd39f984018b5bede25f8b3" @@ -13060,15 +12640,6 @@ object.entries@^1.1.9: define-properties "^1.2.1" es-object-atoms "^1.1.1" -object.fromentries@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" - integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - object.fromentries@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" @@ -13079,14 +12650,6 @@ object.fromentries@^2.0.8: es-abstract "^1.23.2" es-object-atoms "^1.0.0" -object.hasown@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" - integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== - dependencies: - define-properties "^1.1.4" - es-abstract "^1.20.4" - object.values@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" @@ -14627,18 +14190,6 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -reflect.getprototypeof@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.3.tgz#2738fd896fcc3477ffbd4190b40c2458026b6928" - integrity sha512-TTAOZpkJ2YLxl7mVHWrNo3iDMEkYlva/kgFcXndqMgbo/AZUmmavEkdXV+hXtE4P8xdyEKRzalaFqZVuwIk/Nw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.1" - globalthis "^1.0.3" - which-builtin-type "^1.1.3" - reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" @@ -14689,7 +14240,7 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: +regexp.prototype.flags@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== @@ -14882,15 +14433,6 @@ resolve@^1.22.8: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.4: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - resolve@^2.0.0-next.5: version "2.0.0-next.5" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" @@ -15116,7 +14658,7 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@~7.5.4: +semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@~7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -15676,11 +15218,6 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -string-natural-compare@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" - integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== - "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -15727,20 +15264,6 @@ string.prototype.matchall@^4.0.12: set-function-name "^2.0.2" side-channel "^1.1.0" -string.prototype.matchall@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" - integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.3" - side-channel "^1.0.4" - string.prototype.repeat@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" @@ -16205,15 +15728,10 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -ts-api-utils@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.0.tgz#709c6f2076e511a81557f3d07a0cbd566ae8195c" - integrity sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ== - -ts-api-utils@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.4.0.tgz#2690579f96d2790253bdcf1ca35d569ad78f9ad8" - integrity sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA== +ts-api-utils@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1" + integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== ts-interface-checker@^0.1.9: version "0.1.13" @@ -16225,11 +15743,6 @@ ts-plugin-sort-import-suggestions@^1.0.4: resolved "https://registry.yarnpkg.com/ts-plugin-sort-import-suggestions/-/ts-plugin-sort-import-suggestions-1.0.4.tgz#d1ed6c235feb8c8bb8b34c625ea75b46e3e62925" integrity sha512-85n5lm2OQQ+b7aRNK9omU1gmjMNXRsgeLwojm5u4OSY5sVBkAHTcgMQPEeHMNlyyfFW0uXnwgqAU0pNfhD96Bw== -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" @@ -16240,13 +15753,6 @@ tslib@^2.8.0, tslib@^2.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -16371,15 +15877,15 @@ typed-array-length@^1.0.7: possible-typed-array-names "^1.0.0" reflect.getprototypeof "^1.0.6" -typescript-eslint@^8.57.2: - version "8.57.2" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.57.2.tgz#d64c6648dda5b15176708701537ab0b55ba3c83d" - integrity sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A== +typescript-eslint@^8.58.0: + version "8.58.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.58.0.tgz#5758b1b68ae7ec05d756b98c63a1f6953a01172b" + integrity sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA== dependencies: - "@typescript-eslint/eslint-plugin" "8.57.2" - "@typescript-eslint/parser" "8.57.2" - "@typescript-eslint/typescript-estree" "8.57.2" - "@typescript-eslint/utils" "8.57.2" + "@typescript-eslint/eslint-plugin" "8.58.0" + "@typescript-eslint/parser" "8.58.0" + "@typescript-eslint/typescript-estree" "8.58.0" + "@typescript-eslint/utils" "8.58.0" typescript@^6.0.2: version "6.0.2" @@ -16953,24 +16459,6 @@ which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: is-string "^1.1.1" is-symbol "^1.1.1" -which-builtin-type@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" - integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== - dependencies: - function.prototype.name "^1.1.5" - has-tostringtag "^1.0.0" - is-async-function "^2.0.0" - is-date-object "^1.0.5" - is-finalizationregistry "^1.0.2" - is-generator-function "^1.0.10" - is-regex "^1.1.4" - is-weakref "^1.0.2" - isarray "^2.0.5" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.9" - which-builtin-type@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" @@ -16990,16 +16478,6 @@ which-builtin-type@^1.2.1: which-collection "^1.0.2" which-typed-array "^1.1.16" -which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== - dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" - which-collection@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" @@ -17052,18 +16530,6 @@ which-typed-array@^1.1.19: gopd "^1.2.0" has-tostringtag "^1.0.2" -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" - which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" From 498d321bb1ec00085ca2e52f879cb67bf765fec5 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 31 Mar 2026 09:35:31 -0700 Subject: [PATCH 03/32] Change other borderWidths from 2 to 1 (#10156) --- src/components/Select/index.web.tsx | 5 ++--- src/components/forms/DateField/index.shared.tsx | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/Select/index.web.tsx b/src/components/Select/index.web.tsx index 67c86f7dc5..1aa4b39ea1 100644 --- a/src/components/Select/index.web.tsx +++ b/src/components/Select/index.web.tsx @@ -3,8 +3,7 @@ import {View} from 'react-native' import {Select as RadixSelect} from 'radix-ui' import {useA11y} from '#/state/a11y' -import {flatten, useTheme, web} from '#/alf' -import {atoms as a} from '#/alf' +import {atoms as a, flatten, useTheme, web} from '#/alf' import {useInteractionState} from '#/components/hooks/useInteractionState' import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check' import { @@ -109,7 +108,7 @@ export function Trigger({children, label}: TriggerProps) { borderRadius: 10, maxWidth: 400, outline: 0, - borderWidth: 2, + borderWidth: 1, borderStyle: 'solid', borderColor: focused ? t.palette.primary_500 diff --git a/src/components/forms/DateField/index.shared.tsx b/src/components/forms/DateField/index.shared.tsx index 24344b4417..325805ed69 100644 --- a/src/components/forms/DateField/index.shared.tsx +++ b/src/components/forms/DateField/index.shared.tsx @@ -63,7 +63,7 @@ export function DateFieldButton({ paddingLeft: 14, paddingRight: 14, borderColor: 'transparent', - borderWidth: 2, + borderWidth: 1, }, native({ paddingTop: 10, From 22620407970465f32d801aae1f78cb6c441b4685 Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:54:51 -0700 Subject: [PATCH 04/32] Display see more link for suggested users in profile header (#10134) --- src/components/FeedInterstitials.tsx | 68 +++++++++++++--------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx index 645b90a4f6..ff23e3998f 100644 --- a/src/components/FeedInterstitials.tsx +++ b/src/components/FeedInterstitials.tsx @@ -560,32 +560,30 @@ export function ProfileGrid({ Suggested for you - {!isProfileHeaderContext && ( - - )} + @@ -605,16 +603,14 @@ export function ProfileGrid({ decelerationRate="fast"> {content} - {!isProfileHeaderContext && ( - { - followDialogControl.open() - ax.metric('suggestedUser:seeMore', { - logContext, - }) - }} - /> - )} + { + followDialogControl.open() + ax.metric('suggestedUser:seeMore', { + logContext, + }) + }} + /> )} From 68971166251664f14894df7752914ac7fc49f001 Mon Sep 17 00:00:00 2001 From: pfrazee <1270099+pfrazee@users.noreply.github.com> Date: Wed, 1 Apr 2026 03:13:49 +0000 Subject: [PATCH 05/32] Nightly source-language update --- src/locale/locales/en/messages.po | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po index 0a969bad5a..18a8a62229 100644 --- a/src/locale/locales/en/messages.po +++ b/src/locale/locales/en/messages.po @@ -1827,20 +1827,20 @@ msgstr "" msgid "Browse custom feeds" msgstr "" -#: src/components/FeedInterstitials.tsx:631 +#: src/components/FeedInterstitials.tsx:627 msgid "Browse more accounts" msgstr "" -#: src/components/FeedInterstitials.tsx:761 +#: src/components/FeedInterstitials.tsx:757 msgid "Browse more feeds on the Explore page" msgstr "" -#: src/components/FeedInterstitials.tsx:742 -#: src/components/FeedInterstitials.tsx:745 +#: src/components/FeedInterstitials.tsx:738 +#: src/components/FeedInterstitials.tsx:741 msgid "Browse more suggestions" msgstr "" -#: src/components/FeedInterstitials.tsx:770 +#: src/components/FeedInterstitials.tsx:766 msgid "Browse more suggestions on the Explore page" msgstr "" @@ -8864,12 +8864,12 @@ msgstr "" msgid "See jobs at Bluesky" msgstr "" -#: src/components/FeedInterstitials.tsx:584 -#: src/components/FeedInterstitials.tsx:645 +#: src/components/FeedInterstitials.tsx:583 +#: src/components/FeedInterstitials.tsx:641 msgid "See more" msgstr "" -#: src/components/FeedInterstitials.tsx:565 +#: src/components/FeedInterstitials.tsx:564 msgid "See more suggested profiles" msgstr "" @@ -9565,7 +9565,7 @@ msgstr "" msgid "Some of your verifications are invalid." msgstr "" -#: src/components/FeedInterstitials.tsx:724 +#: src/components/FeedInterstitials.tsx:720 msgid "Some other feeds you might like" msgstr "" From e8ee30398ad26b8c9f1e587f3be0bd58c5719b8e Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Wed, 1 Apr 2026 09:11:27 -0700 Subject: [PATCH 06/32] Update Search to use ALF atoms (#10146) Co-authored-by: Samuel Newman --- src/view/shell/desktop/Search.tsx | 128 ++++++++++++++---------------- 1 file changed, 59 insertions(+), 69 deletions(-) diff --git a/src/view/shell/desktop/Search.tsx b/src/view/shell/desktop/Search.tsx index ede77640dc..cb69eb0dfd 100644 --- a/src/view/shell/desktop/Search.tsx +++ b/src/view/shell/desktop/Search.tsx @@ -1,7 +1,6 @@ import {memo, useCallback, useState} from 'react' import { - ActivityIndicator, - StyleSheet, + type StyleProp, TouchableOpacity, View, type ViewStyle, @@ -9,15 +8,17 @@ import { import {useLingui} from '@lingui/react/macro' import {StackActions, useNavigation} from '@react-navigation/native' -import {usePalette} from '#/lib/hooks/usePalette' import {type NavigationProp} from '#/lib/routes/types' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' -import {Link} from '#/view/com/util/Link' -import {Text} from '#/view/com/util/text/Text' import {SearchProfileCard} from '#/screens/Search/components/SearchProfileCard' -import {atoms as a} from '#/alf' +import {atoms as a, useTheme} from '#/alf' import {SearchInput} from '#/components/forms/SearchInput' +import {Link} from '#/components/Link' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' + +const WHITESPACE_RE = /\s+/gu let SearchLinkCard = ({ label, @@ -28,20 +29,17 @@ let SearchLinkCard = ({ label: string to?: string onPress?: () => void - style?: ViewStyle + style?: StyleProp }): React.ReactNode => { - const pal = usePalette('default') + const t = useTheme() const inner = ( - - - {label} - + + {label} ) - if (onPress) { + if (onPress || !to) { return ( - - - {label} - - + + {label} ) } @@ -71,8 +64,8 @@ SearchLinkCard = memo(SearchLinkCard) export {SearchLinkCard} export function DesktopSearch() { + const t = useTheme() const {t: l} = useLingui() - const pal = usePalette('default') const navigation = useNavigation() const [isActive, setIsActive] = useState(false) const [query, setQuery] = useState('') @@ -80,6 +73,7 @@ export function DesktopSearch() { query, true, ) + const tQuery = query.replace(WHITESPACE_RE, ' ').trim() const moderationOpts = useModerationOpts() @@ -95,9 +89,9 @@ export function DesktopSearch() { const onSubmit = useCallback(() => { setIsActive(false) - if (!query.length) return - navigation.dispatch(StackActions.push('Search', {q: query})) - }, [query, navigation]) + if (!tQuery.length) return + navigation.dispatch(StackActions.push('Search', {q: tQuery})) + }, [tQuery, navigation]) const onSearchProfileCardPress = useCallback(() => { setQuery('') @@ -105,62 +99,58 @@ export function DesktopSearch() { }, []) return ( - + - {query !== '' && isActive && moderationOpts && ( + {tQuery !== '' && isActive && moderationOpts && ( + 0 ? a.border_b : undefined} + /> {isFetching && !autocompleteData?.length ? ( - - + + ) : ( - <> - 0 - ? {borderBottomWidth: 1} - : undefined - } + autocompleteData?.map(item => ( + - {autocompleteData?.map(item => ( - - ))} - + )) )} )} ) } - -const styles = StyleSheet.create({ - container: { - position: 'relative', - width: '100%', - }, - resultsContainer: { - marginTop: 10, - flexDirection: 'column', - width: '100%', - borderWidth: 1, - borderRadius: 6, - }, -}) From 9b20023c8653ca67e9b54b8720c6ae7471e469d6 Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Wed, 1 Apr 2026 09:12:11 -0700 Subject: [PATCH 07/32] Fix Safari layout shift issue with SubtleHover (#10135) --- src/components/SubtleHover.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/SubtleHover.tsx b/src/components/SubtleHover.tsx index 22a26f0989..c3d1610a9b 100644 --- a/src/components/SubtleHover.tsx +++ b/src/components/SubtleHover.tsx @@ -1,6 +1,6 @@ import {View} from 'react-native' -import {atoms as a, useTheme, type ViewStyleProp} from '#/alf' +import {atoms as a, useTheme, type ViewStyleProp, web as webOnly} from '#/alf' import {IS_NATIVE, IS_WEB, IS_WEB_TOUCH_DEVICE} from '#/env' export function SubtleHover({ @@ -33,6 +33,10 @@ export function SubtleHover({ a.transition_opacity, t.atoms.bg_contrast_50, style, + // Force Safari to composite the overlay on its own GPU layer. + // This fixes a layout shift that happens due to different subpixel + // rounding when the overlay is composited on hover. + webOnly({willChange: 'opacity'}), {opacity: hover ? opacity : 0}, ]} /> From f30acadc73b672d97948a9a566f96017a519626c Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 1 Apr 2026 11:00:11 -0700 Subject: [PATCH 08/32] Hide alt text note after alt text is added (#10132) --- src/view/com/composer/GifAltText.tsx | 2 +- src/view/com/composer/photos/Gallery.tsx | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/view/com/composer/GifAltText.tsx b/src/view/com/composer/GifAltText.tsx index 8afa7d0570..e32df485c3 100644 --- a/src/view/com/composer/GifAltText.tsx +++ b/src/view/com/composer/GifAltText.tsx @@ -101,7 +101,7 @@ export function GifAltTextDialogLoaded({ - + Alt text describes images for blind and low-vision users, and helps give context to everyone. diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx index 2ad91fa594..0df31da625 100644 --- a/src/view/com/composer/photos/Gallery.tsx +++ b/src/view/com/composer/photos/Gallery.tsx @@ -120,12 +120,14 @@ const GalleryInner = ({images, containerInfo, dispatch}: GalleryInnerProps) => { ) })} - - - Alt text describes images for blind and low-vision users, and helps - give context to everyone. - - + {images.some(image => !image.alt) && ( + + + Alt text describes images for blind and low-vision users, and helps + give context to everyone. + + + )} ) : null } From 69164c640fef9c8315355391e0e6cb038440c431 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 1 Apr 2026 11:07:02 -0700 Subject: [PATCH 09/32] Keep screen awake when videos are playing (#10155) --- .../Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx index 209ba10e72..946df787e7 100644 --- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx @@ -13,6 +13,7 @@ import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' import {Pause_Filled_Corner0_Rounded as PauseIcon} from '#/components/icons/Pause' import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play' import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' +import {KeepAwake} from '#/components/KeepAwake' import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {useVideoMuteState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' import {GifPresentationControls} from '../GifPresentationControls' @@ -112,6 +113,7 @@ export function VideoEmbedInnerNative({ /> )} + ) } From 5a2135733f6d2ece6b6b2d72d635d26ceab894e0 Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:39:05 -0700 Subject: [PATCH 10/32] Handle more translation errors (#10160) --- src/env/index.ts | 8 +++++++ src/env/index.web.ts | 1 + src/lib/translation/index.tsx | 41 ++++++++++++++++++++++++++--------- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/env/index.ts b/src/env/index.ts index 14abba55a4..13ae3dcc7d 100644 --- a/src/env/index.ts +++ b/src/env/index.ts @@ -10,6 +10,10 @@ const iOSMajorVersion = Platform.OS === 'ios' && typeof Platform.Version === 'string' ? parseInt(Platform.Version.split('.')[0], 10) : 0 +const androidPlatformVersion = + Platform.OS === 'android' && typeof Platform.Version === 'number' + ? Platform.Version + : 0 /** * The semver version of the app, specified in our `package.json`.file. On @@ -49,3 +53,7 @@ export const IS_WEB_FIREFOX: boolean = false export const IS_HIGH_DPI: boolean = true // ideally we'd use isLiquidGlassAvailable() from expo-glass-effect but checking iOS version is good enough for now export const IS_LIQUID_GLASS: boolean = iOSMajorVersion >= 26 +// So we can avoid attempting on-device translation when we know it's unsupported. +export const IS_TRANSLATION_SUPPORTED: boolean = + (IS_IOS && iOSMajorVersion >= 18) || + (IS_ANDROID && androidPlatformVersion > 22) diff --git a/src/env/index.web.ts b/src/env/index.web.ts index 0a078fdeb0..68604a2e5b 100644 --- a/src/env/index.web.ts +++ b/src/env/index.web.ts @@ -48,3 +48,4 @@ export const IS_HIGH_DPI: boolean = window.matchMedia( '(min-resolution: 2dppx)', ).matches export const IS_LIQUID_GLASS: boolean = false +export const IS_TRANSLATION_SUPPORTED: boolean = false diff --git a/src/lib/translation/index.tsx b/src/lib/translation/index.tsx index 710c9993d1..0e7b182a62 100644 --- a/src/lib/translation/index.tsx +++ b/src/lib/translation/index.tsx @@ -1,18 +1,17 @@ import {useCallback, useContext, useEffect, useMemo, useState} from 'react' import {LayoutAnimation, Platform} from 'react-native' import {getLocales} from 'expo-localization' -import { - isTranslationSupported, - onTranslateTask, -} from '@bsky.app/expo-translate-text' +import {onTranslateTask} from '@bsky.app/expo-translate-text' import {type TranslationTaskResult} from '@bsky.app/expo-translate-text/build/ExpoTranslateText.types' import {useLingui} from '@lingui/react/macro' import {useFocusEffect} from '@react-navigation/native' import {useGoogleTranslate} from '#/lib/hooks/useGoogleTranslate' +import {codeToLanguageName} from '#/locale/helpers' import {logger} from '#/logger' +import {useLanguagePrefs} from '#/state/preferences' import {useAnalytics} from '#/analytics' -import {IS_ANDROID, IS_IOS} from '#/env' +import {IS_ANDROID, IS_IOS, IS_TRANSLATION_SUPPORTED} from '#/env' import {Context} from './context' import { type ContextType, @@ -25,6 +24,11 @@ import {guessLanguage} from './utils' export * from './types' export * from './utils' +const E_SAME_AS_SOURCE_LANGUAGE = + 'Translation result is the same as the source text.' +const E_EMPTY_RESULT = 'Translation result is empty.' +const E_INVALID_SOURCE_LANGUAGE = 'Invalid source language' + /** * Attempts on-device translation via @bsky.app/expo-translate-text. * Uses a lazy import to avoid crashing if the native module isn't linked into @@ -80,11 +84,11 @@ async function attemptTranslation( typeof result.translatedTexts === 'string' ? result.translatedTexts : '' if (translatedText === input) { - throw new Error('Translation result is the same as the source text.') + throw new Error(E_SAME_AS_SOURCE_LANGUAGE) } if (translatedText === '') { - throw new Error('Translation result is empty.') + throw new Error(E_EMPTY_RESULT) } return { @@ -159,6 +163,7 @@ export function Provider({children}: React.PropsWithChildren) { >({}) const [refCounts, setRefCounts] = useState>({}) const ax = useAnalytics() + const langPrefs = useLanguagePrefs() const {t: l} = useLingui() const googleTranslate = useGoogleTranslate() @@ -235,7 +240,7 @@ export function Provider({children}: React.PropsWithChildren) { googleTranslate: shouldForceGoogleTranslate, }) - if (shouldForceGoogleTranslate || !isTranslationSupported()) { + if (shouldForceGoogleTranslate || !IS_TRANSLATION_SUPPORTED) { await googleTranslate( text, expectedTargetLanguage, @@ -280,7 +285,8 @@ export function Provider({children}: React.PropsWithChildren) { postLanguages: possibleSourceLanguages, }, })) - } catch (e) { + } catch (err) { + const e = err as Error logger.error('Failed to translate text on device', {safeMessage: e}) // On-device translation failed (language pack missing or user // dismissed the download prompt). @@ -295,6 +301,21 @@ export function Provider({children}: React.PropsWithChildren) { textLength: text.length, }) let errorMessage = l`Device failed to translate :(` + if (e.message === E_SAME_AS_SOURCE_LANGUAGE) { + errorMessage = l`Translation to the same language is unavailable on your device.` + } + if (e.message === E_EMPTY_RESULT) { + errorMessage = l`No translation received from your device.` + } + if ( + expectedSourceLanguage && + e.message.includes(E_INVALID_SOURCE_LANGUAGE) + ) { + errorMessage = l`${codeToLanguageName( + expectedSourceLanguage, + langPrefs.appLanguage, + )} is not supported by your device.` + } if (!IS_ANDROID) { LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) } @@ -304,7 +325,7 @@ export function Provider({children}: React.PropsWithChildren) { })) } }, - [ax, googleTranslate, l], + [ax, googleTranslate, l, langPrefs.appLanguage], ) const ctx = useMemo( From b196ddef733fd36163eef194efc6230a40c5bf1e Mon Sep 17 00:00:00 2001 From: Arunabh Verma Date: Thu, 2 Apr 2026 02:55:16 +0530 Subject: [PATCH 11/32] Replace react-native-paste-input with expo-paste-input (#10158) Co-authored-by: Eric Bailey --- package.json | 2 +- ...rmost+react-native-paste-input+0.8.1.patch | 13 - ...ct-native-paste-input+0.8.1.patch.disabled | 264 ------------------ .../com/composer/text-input/TextInput.tsx | 104 +++---- yarn.lock | 21 +- 5 files changed, 63 insertions(+), 341 deletions(-) delete mode 100644 patches/@mattermost+react-native-paste-input+0.8.1.patch delete mode 100644 patches/@mattermost+react-native-paste-input+0.8.1.patch.disabled diff --git a/package.json b/package.json index e815bea407..2f2bad9932 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,6 @@ "@ipld/dag-cbor": "^9.2.0", "@lingui/core": "^5.9.2", "@lingui/react": "^5.9.2", - "@mattermost/react-native-paste-input": "mattermost/react-native-paste-input", "@miblanchard/react-native-slider": "^2.6.0", "@mozzius/expo-dynamic-app-icon": "^1.8.0", "@react-native-async-storage/async-storage": "2.2.0", @@ -167,6 +166,7 @@ "expo-location": "~19.0.8", "expo-media-library": "~18.2.1", "expo-notifications": "~0.32.16", + "expo-paste-input": "^0.1.10", "expo-privacy-sensitive": "^0.1.0", "expo-screen-orientation": "~9.0.8", "expo-sharing": "~14.0.8", diff --git a/patches/@mattermost+react-native-paste-input+0.8.1.patch b/patches/@mattermost+react-native-paste-input+0.8.1.patch deleted file mode 100644 index c0a895715e..0000000000 --- a/patches/@mattermost+react-native-paste-input+0.8.1.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/@mattermost/react-native-paste-input/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt b/node_modules/@mattermost/react-native-paste-input/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt -index 4ed2307..ede1181 100644 ---- a/node_modules/@mattermost/react-native-paste-input/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt -+++ b/node_modules/@mattermost/react-native-paste-input/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt -@@ -54,7 +54,7 @@ class PasteTextInputManager(context: ReactApplicationContext) : ReactTextInputMa - } - - override fun getExportedCustomBubblingEventTypeConstants(): MutableMap { -- val map = super.getExportedCustomBubblingEventTypeConstants()!! -+ val map = super.getExportedCustomBubblingEventTypeConstants().toMutableMap() - map["onPaste"] = MapBuilder.of( - "phasedRegistrationNames", - MapBuilder.of("bubbled", "onPaste") diff --git a/patches/@mattermost+react-native-paste-input+0.8.1.patch.disabled b/patches/@mattermost+react-native-paste-input+0.8.1.patch.disabled deleted file mode 100644 index a7f1461432..0000000000 --- a/patches/@mattermost+react-native-paste-input+0.8.1.patch.disabled +++ /dev/null @@ -1,264 +0,0 @@ -diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m b/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m -index e916023..5049c33 100644 ---- a/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m -+++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m -@@ -4,6 +4,7 @@ - // - // Created by Elias Nahum on 04-11-20. - // Copyright © 2020 Facebook. All rights reserved. -+// Updated to remove parent’s default text view - // - - #import "PasteInputView.h" -@@ -12,49 +13,78 @@ - - @implementation PasteInputView - { -- PasteInputTextView *_backedTextInputView; -+ // We'll store the custom text view in this ivar -+ PasteInputTextView *_customBackedTextView; - } - - - (instancetype)initWithBridge:(RCTBridge *)bridge - { -+ // Must call the super’s designated initializer - if (self = [super initWithBridge:bridge]) { -- _backedTextInputView = [[PasteInputTextView alloc] initWithFrame:self.bounds]; -- _backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; -- _backedTextInputView.textInputDelegate = self; -+ // 1. The parent (RCTMultilineTextInputView) has already created -+ // its own _backedTextInputView = [RCTUITextView new] in super init. -+ // We can remove that subview: - -- [self addSubview:_backedTextInputView]; -- } -+ id parentInputView = super.backedTextInputView; -+ if ([parentInputView isKindOfClass:[UIView class]]) { -+ UIView *parentSubview = (UIView *)parentInputView; -+ if (parentSubview.superview == self) { -+ [parentSubview removeFromSuperview]; -+ } -+ } - -+ // 2. Now create our custom PasteInputTextView -+ _customBackedTextView = [[PasteInputTextView alloc] initWithFrame:self.bounds]; -+ _customBackedTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; -+ _customBackedTextView.textInputDelegate = self; -+ -+ // Optional: disable inline predictions for iOS 17+ -+ if (@available(iOS 17.0, *)) { -+ _customBackedTextView.inlinePredictionType = UITextInlinePredictionTypeNo; -+ } -+ -+ // 3. Add your custom text view as the only subview -+ [self addSubview:_customBackedTextView]; -+ } - return self; - } - -+/** -+ * Override the parent's accessor so that anywhere in RN that calls -+ * `self.backedTextInputView` will get the custom PasteInputTextView. -+ */ - - (id)backedTextInputView - { -- return _backedTextInputView; -+ return _customBackedTextView; - } - --- (void)setDisableCopyPaste:(BOOL)disableCopyPaste { -- _backedTextInputView.disableCopyPaste = disableCopyPaste; -+#pragma mark - Setters for React Props -+ -+- (void)setDisableCopyPaste:(BOOL)disableCopyPaste -+{ -+ _customBackedTextView.disableCopyPaste = disableCopyPaste; - } - --- (void)setOnPaste:(RCTDirectEventBlock)onPaste { -- _backedTextInputView.onPaste = onPaste; -+- (void)setOnPaste:(RCTDirectEventBlock)onPaste -+{ -+ _customBackedTextView.onPaste = onPaste; - } - --- (void)setSmartPunctuation:(NSString *)smartPunctuation { -- if ([smartPunctuation isEqualToString:@"enable"]) { -- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeYes]; -- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeYes]; -- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeYes]; -- } else if ([smartPunctuation isEqualToString:@"disable"]) { -- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeNo]; -- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeNo]; -- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeNo]; -- } else { -- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeDefault]; -- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeDefault]; -- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeDefault]; -- } -+- (void)setSmartPunctuation:(NSString *)smartPunctuation -+{ -+ if ([smartPunctuation isEqualToString:@"enable"]) { -+ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeYes]; -+ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeYes]; -+ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeYes]; -+ } else if ([smartPunctuation isEqualToString:@"disable"]) { -+ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeNo]; -+ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeNo]; -+ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeNo]; -+ } else { -+ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeDefault]; -+ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeDefault]; -+ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeDefault]; -+ } - } - - #pragma mark - UIScrollViewDelegate -@@ -62,7 +92,6 @@ - (void)setSmartPunctuation:(NSString *)smartPunctuation { - - (void)scrollViewDidScroll:(UIScrollView *)scrollView - { - RCTDirectEventBlock onScroll = self.onScroll; -- - if (onScroll) { - CGPoint contentOffset = scrollView.contentOffset; - CGSize contentSize = scrollView.contentSize; -@@ -71,22 +100,22 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView - - onScroll(@{ - @"contentOffset": @{ -- @"x": @(contentOffset.x), -- @"y": @(contentOffset.y) -+ @"x": @(contentOffset.x), -+ @"y": @(contentOffset.y) - }, - @"contentInset": @{ -- @"top": @(contentInset.top), -- @"left": @(contentInset.left), -- @"bottom": @(contentInset.bottom), -- @"right": @(contentInset.right) -+ @"top": @(contentInset.top), -+ @"left": @(contentInset.left), -+ @"bottom": @(contentInset.bottom), -+ @"right": @(contentInset.right) - }, - @"contentSize": @{ -- @"width": @(contentSize.width), -- @"height": @(contentSize.height) -+ @"width": @(contentSize.width), -+ @"height": @(contentSize.height) - }, - @"layoutMeasurement": @{ -- @"width": @(size.width), -- @"height": @(size.height) -+ @"width": @(size.width), -+ @"height": @(size.height) - }, - @"zoomScale": @(scrollView.zoomScale ?: 1), - }); -diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInput.mm b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInput.mm -index dd50053..2ed7017 100644 ---- a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInput.mm -+++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInput.mm -@@ -122,8 +122,8 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & - const auto &newTextInputProps = static_cast(*props); - - // Traits: -- if (newTextInputProps.traits.multiline != oldTextInputProps.traits.multiline) { -- [self _setMultiline:newTextInputProps.traits.multiline]; -+ if (newTextInputProps.multiline != oldTextInputProps.multiline) { -+ [self _setMultiline:newTextInputProps.multiline]; - } - - if (newTextInputProps.traits.autocapitalizationType != oldTextInputProps.traits.autocapitalizationType) { -@@ -421,7 +421,7 @@ - (void)textInputDidChangeSelection - return; - } - const auto &props = static_cast(*_props); -- if (props.traits.multiline && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) { -+ if (props.multiline && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) { - [self textInputDidChange]; - _ignoreNextTextInputCall = YES; - } -@@ -708,11 +708,11 @@ - (BOOL)_textOf:(NSAttributedString *)newText equals:(NSAttributedString *)oldTe - - (SubmitBehavior)getSubmitBehavior - { - const auto &props = static_cast(*_props); -- const SubmitBehavior submitBehaviorDefaultable = props.traits.submitBehavior; -+ const SubmitBehavior submitBehaviorDefaultable = props.submitBehavior; - - // We should always have a non-default `submitBehavior`, but in case we don't, set it based on multiline. - if (submitBehaviorDefaultable == SubmitBehavior::Default) { -- return props.traits.multiline ? SubmitBehavior::Newline : SubmitBehavior::BlurAndSubmit; -+ return props.multiline ? SubmitBehavior::Newline : SubmitBehavior::BlurAndSubmit; - } - - return submitBehaviorDefaultable; -diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.cpp b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.cpp -index 29e094f..7ef519a 100644 ---- a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.cpp -+++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.cpp -@@ -22,8 +22,7 @@ PasteTextInputProps::PasteTextInputProps( - const PropsParserContext &context, - const PasteTextInputProps &sourceProps, - const RawProps& rawProps) -- : ViewProps(context, sourceProps, rawProps), -- BaseTextProps(context, sourceProps, rawProps), -+ : BaseTextInputProps(context, sourceProps, rawProps), - traits(convertRawProp(context, rawProps, sourceProps.traits, {})), - smartPunctuation(convertRawProp(context, rawProps, "smartPunctuation", sourceProps.smartPunctuation, {})), - disableCopyPaste(convertRawProp(context, rawProps, "disableCopyPaste", sourceProps.disableCopyPaste, {false})), -@@ -133,7 +132,7 @@ TextAttributes PasteTextInputProps::getEffectiveTextAttributes(Float fontSizeMul - ParagraphAttributes PasteTextInputProps::getEffectiveParagraphAttributes() const { - auto result = paragraphAttributes; - -- if (!traits.multiline) { -+ if (!multiline) { - result.maximumNumberOfLines = 1; - } - -diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.h b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.h -index 723d00c..31cfe66 100644 ---- a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.h -+++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/Props.h -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -25,7 +26,7 @@ - - namespace facebook::react { - --class PasteTextInputProps final : public ViewProps, public BaseTextProps { -+class PasteTextInputProps final : public BaseTextInputProps { - public: - PasteTextInputProps() = default; - PasteTextInputProps(const PropsParserContext& context, const PasteTextInputProps& sourceProps, const RawProps& rawProps); -diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/ShadowNodes.cpp b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/ShadowNodes.cpp -index 31e07e3..7f0ebfb 100644 ---- a/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/ShadowNodes.cpp -+++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteTextInputSpecs/ShadowNodes.cpp -@@ -91,20 +91,11 @@ void PasteTextInputShadowNode::updateStateIfNeeded( - const auto& state = getStateData(); - - react_native_assert(textLayoutManager_); -- react_native_assert( -- (!state.layoutManager || state.layoutManager == textLayoutManager_) && -- "`StateData` refers to a different `TextLayoutManager`"); -- -- if (state.reactTreeAttributedString == reactTreeAttributedString && -- state.layoutManager == textLayoutManager_) { -- return; -- } - - auto newState = TextInputState{}; - newState.attributedStringBox = AttributedStringBox{reactTreeAttributedString}; - newState.paragraphAttributes = getConcreteProps().paragraphAttributes; - newState.reactTreeAttributedString = reactTreeAttributedString; -- newState.layoutManager = textLayoutManager_; - newState.mostRecentEventCount = getConcreteProps().mostRecentEventCount; - setStateData(std::move(newState)); - } diff --git a/src/view/com/composer/text-input/TextInput.tsx b/src/view/com/composer/text-input/TextInput.tsx index 0ba2e958a6..6806f037bf 100644 --- a/src/view/com/composer/text-input/TextInput.tsx +++ b/src/view/com/composer/text-input/TextInput.tsx @@ -8,21 +8,17 @@ import { import { type NativeSyntheticEvent, Text as RNText, + TextInput as RNTextInput, type TextInputSelectionChangeEventData, View, } from 'react-native' +import {type PasteEventPayload, TextInputWrapper} from 'expo-paste-input' import {AppBskyRichtextFacet, RichText} from '@atproto/api' -import PasteInput, { - type PastedFile, - type PasteInputRef, - // @ts-expect-error no types when installing from github - // eslint-disable-next-line import-x/no-unresolved -} from '@mattermost/react-native-paste-input' +import {useLingui} from '@lingui/react/macro' import {POST_IMG_MAX} from '#/lib/constants' import {downloadAndResize} from '#/lib/media/manip' import {isUriImage} from '#/lib/media/util' -import {cleanError} from '#/lib/strings/errors' import {getMentionAt, insertMentionAt} from '#/lib/strings/mention-manip' import {useTheme} from '#/lib/ThemeContext' import { @@ -51,8 +47,9 @@ export function TextInput({ onError, ...props }: TextInputProps) { + const {t: l} = useLingui() const {theme: t, fonts} = useAlf() - const textInput = useRef(null) + const textInput = useRef(null) const textInputSelection = useRef({start: 0, end: 0}) const theme = useTheme() const [autocompletePrefix, setAutocompletePrefix] = useState('') @@ -129,19 +126,21 @@ export function TextInput({ ) const onPaste = useCallback( - async (err: string | undefined, files: PastedFile[]) => { - if (err) { - return onError(cleanError(err)) + (payload: PasteEventPayload) => { + if (payload.type === 'unsupported') { + onError(l`Unsupported clipboard content`) + return } - const uris = files.map(f => f.uri) - const uri = uris.find(isUriImage) - - if (uri) { - onPhotoPasted(uri) + if (payload.type === 'images') { + for (const uri of payload.uris) { + if (isUriImage(uri)) { + onPhotoPasted(uri) + } + } } }, - [onError, onPhotoPasted], + [l, onError, onPhotoPasted], ) const onSelectionChange = useCallback( @@ -217,41 +216,42 @@ export function TextInput({ return ( - - {textDecorated} - + + + {textDecorated} + + Date: Wed, 1 Apr 2026 15:39:26 -0700 Subject: [PATCH 12/32] Update React Query to latest (#10126) --- package.json | 6 ++-- src/state/queries/resolve-uri.ts | 58 ++++++++++++++++++-------------- yarn.lock | 49 ++++++++++++++------------- 3 files changed, 60 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index 2f2bad9932..36f7ec6152 100644 --- a/package.json +++ b/package.json @@ -116,9 +116,9 @@ "@react-navigation/native": "^7.1.33", "@react-navigation/native-stack": "^7.14.4", "@sentry/react-native": "~6.20.0", - "@tanstack/query-async-storage-persister": "^5.25.0", - "@tanstack/react-query": "5.25.0", - "@tanstack/react-query-persist-client": "^5.25.0", + "@tanstack/query-async-storage-persister": "^5.95.2", + "@tanstack/react-query": "^5.95.2", + "@tanstack/react-query-persist-client": "^5.95.2", "@tiptap/core": "^2.9.1", "@tiptap/extension-document": "^2.9.1", "@tiptap/extension-hard-break": "^2.9.1", diff --git a/src/state/queries/resolve-uri.ts b/src/state/queries/resolve-uri.ts index b40e380724..a4667c9228 100644 --- a/src/state/queries/resolve-uri.ts +++ b/src/state/queries/resolve-uri.ts @@ -1,9 +1,5 @@ -import {AtUri} from '@atproto/api' -import { - type QueryClient, - useQuery, - type UseQueryResult, -} from '@tanstack/react-query' +import {AtUri, type BskyAgent} from '@atproto/api' +import {type QueryClient, queryOptions, useQuery} from '@tanstack/react-query' import {STALE} from '#/state/queries' import {useAgent} from '#/state/session' @@ -12,26 +8,12 @@ import {useUnstableProfileViewCache} from './profile' const RQKEY_ROOT = 'resolved-did' export const RQKEY = (didOrHandle: string) => [RQKEY_ROOT, didOrHandle] -type UriUseQueryResult = UseQueryResult<{did: string; uri: string}, Error> -export function useResolveUriQuery(uri: string | undefined): UriUseQueryResult { - const urip = new AtUri(uri || '') - const res = useResolveDidQuery(urip.host) - if (res.data) { - // @ts-expect-error TODO new-sdk-migration - urip.host = res.data - return { - ...res, - data: {did: urip.host, uri: urip.toString()}, - } as UriUseQueryResult - } - return res as UriUseQueryResult -} - -export function useResolveDidQuery(didOrHandle: string | undefined) { - const agent = useAgent() - const {getUnstableProfile} = useUnstableProfileViewCache() - - return useQuery({ +const resolvedDidQueryOptions = ( + agent: BskyAgent, + getUnstableProfile: (did: string) => {did: string} | undefined, + didOrHandle: string | undefined, +) => + queryOptions({ staleTime: STALE.HOURS.ONE, queryKey: RQKEY(didOrHandle ?? ''), queryFn: async () => { @@ -50,6 +32,30 @@ export function useResolveDidQuery(didOrHandle: string | undefined) { }, enabled: !!didOrHandle, }) + +export function useResolveUriQuery(uri: string | undefined) { + const urip = new AtUri(uri || '') + const host = urip.host + + const agent = useAgent() + const {getUnstableProfile} = useUnstableProfileViewCache() + + return useQuery({ + ...resolvedDidQueryOptions(agent, getUnstableProfile, host), + select: did => ({ + did, + uri: urip.toString(), + }), + }) +} + +export function useResolveDidQuery(didOrHandle: string | undefined) { + const agent = useAgent() + const {getUnstableProfile} = useUnstableProfileViewCache() + + return useQuery( + resolvedDidQueryOptions(agent, getUnstableProfile, didOrHandle), + ) } export function precacheResolvedUri( diff --git a/yarn.lock b/yarn.lock index 63b5b62524..4a5d8dc52e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5102,38 +5102,39 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@tanstack/query-async-storage-persister@^5.25.0": - version "5.25.0" - resolved "https://registry.yarnpkg.com/@tanstack/query-async-storage-persister/-/query-async-storage-persister-5.25.0.tgz#0e8a2a781b8e32a81a5d02a688d6fcdfd055235b" - integrity sha512-58UTp1CuLr2mehsJRMOd8IZPtYGHFeL+uHnHyRd1kmbwo7wDaa8HXstiBdTRq5KokxIXy9FiFbA06LtKuOiwMQ== +"@tanstack/query-async-storage-persister@^5.95.2": + version "5.95.2" + resolved "https://registry.yarnpkg.com/@tanstack/query-async-storage-persister/-/query-async-storage-persister-5.95.2.tgz#0c7ed1c8013823e2d5abbb8d55bc0e9305abf7e0" + integrity sha512-ZhPIHH8J833OVZhEWwwdOk0uhY94d9Wgdnq97JoQx4Ui4xx4Dh6e7WPUrjlUWo88Yqi4Ij+T1o/VR7Vlbnkbjw== dependencies: - "@tanstack/query-persist-client-core" "5.25.0" + "@tanstack/query-core" "5.95.2" + "@tanstack/query-persist-client-core" "5.95.2" -"@tanstack/query-core@5.25.0": - version "5.25.0" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.25.0.tgz#e08ed0a9fad34c8005d1a282e57280031ac50cdc" - integrity sha512-vlobHP64HTuSE68lWF1mEhwSRC5Q7gaT+a/m9S+ItuN+ruSOxe1rFnR9j0ACWQ314BPhBEVKfBQ6mHL0OWfdbQ== +"@tanstack/query-core@5.95.2": + version "5.95.2" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.95.2.tgz#9e3299d0c1c8785dd9e3d0cac1993e45f35113f2" + integrity sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ== -"@tanstack/query-persist-client-core@5.25.0": - version "5.25.0" - resolved "https://registry.yarnpkg.com/@tanstack/query-persist-client-core/-/query-persist-client-core-5.25.0.tgz#52fa634a8067d7b965854a532a33077fd4df0eff" - integrity sha512-sEUsEZ/XWkOosO45CDBI5nj5woCS+DUd9Dk8pGpU8MkeH0EVd3p4N5CdbjNhrreyy5Krf3rpNaiRN9ygLX/rWA== +"@tanstack/query-persist-client-core@5.95.2": + version "5.95.2" + resolved "https://registry.yarnpkg.com/@tanstack/query-persist-client-core/-/query-persist-client-core-5.95.2.tgz#1c94a87c9886a8e1c6a0a3ebbb325afdaf486f81" + integrity sha512-Opfj34WZ594YXpEcZEs8WBiyPGrjrKlGILfk/Ss283uwWQ36C5nX3tRY/bBiXmM82KWauUuNvahwGwiyco/8cQ== dependencies: - "@tanstack/query-core" "5.25.0" + "@tanstack/query-core" "5.95.2" -"@tanstack/react-query-persist-client@^5.25.0": - version "5.25.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query-persist-client/-/react-query-persist-client-5.25.0.tgz#ecbd1362cd6fd94e723d54f5af477d0812852dab" - integrity sha512-j1+GyFj4UQGWuiFZoDUVJZS+wxqKd9SGvPlyHG619zzYNN+QQu4B5uvvHc6U8MroM377EOBOuLKK3W6qsAdahQ== +"@tanstack/react-query-persist-client@^5.95.2": + version "5.95.2" + resolved "https://registry.yarnpkg.com/@tanstack/react-query-persist-client/-/react-query-persist-client-5.95.2.tgz#4d6fe899513725978e86c13c1727ee7e393eaca5" + integrity sha512-i3fvzD8gaLgQyFvRc/+iSUr60aL31tMN+5QM11zdPRg0K9CirIQjHD7WgXFBnD29KJDvcjcv7OrIBaPwZ+H9xw== dependencies: - "@tanstack/query-persist-client-core" "5.25.0" + "@tanstack/query-persist-client-core" "5.95.2" -"@tanstack/react-query@5.25.0": - version "5.25.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.25.0.tgz#f4dac794cf10dd956aa56dbbdf67049a5ba2669d" - integrity sha512-u+n5R7mLO7RmeiIonpaCRVXNRWtZEef/aVZ/XGWRPa7trBIvGtzlfo0Ah7ZtnTYfrKEVwnZ/tzRCBcoiqJ/tFw== +"@tanstack/react-query@^5.95.2": + version "5.95.2" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.95.2.tgz#7daf77342a4e374c22fad88bb98ea13fc19ae086" + integrity sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA== dependencies: - "@tanstack/query-core" "5.25.0" + "@tanstack/query-core" "5.95.2" "@testing-library/react-native@^13.2.0": version "13.2.0" From 512e550c2e87a305e5fd89a40af07f25f5f6754e Mon Sep 17 00:00:00 2001 From: pfrazee <1270099+pfrazee@users.noreply.github.com> Date: Thu, 2 Apr 2026 03:13:26 +0000 Subject: [PATCH 13/32] Nightly source-language update --- src/locale/locales/en/messages.po | 59 ++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po index 18a8a62229..bae0b9ec75 100644 --- a/src/locale/locales/en/messages.po +++ b/src/locale/locales/en/messages.po @@ -182,6 +182,11 @@ msgstr "" msgid "{0} is not available" msgstr "" +#. placeholder {0}: codeToLanguageName( expectedSourceLanguage, langPrefs.appLanguage, ) +#: src/lib/translation/index.tsx:314 +msgid "{0} is not supported by your device." +msgstr "{0} is not supported by your device." + #. placeholder {0}: formatCount(i18n, JOINED_THIS_WEEK) #: src/screens/StarterPack/StarterPackLandingScreen.tsx:232 msgid "{0} joined this week" @@ -852,8 +857,8 @@ msgstr "" #: src/view/com/composer/GifAltText.tsx:76 #: src/view/com/composer/GifAltText.tsx:150 #: src/view/com/composer/GifAltText.tsx:217 -#: src/view/com/composer/photos/Gallery.tsx:194 -#: src/view/com/composer/photos/Gallery.tsx:241 +#: src/view/com/composer/photos/Gallery.tsx:196 +#: src/view/com/composer/photos/Gallery.tsx:243 #: src/view/com/composer/photos/ImageAltTextDialog.tsx:95 #: src/view/com/composer/photos/ImageAltTextDialog.tsx:103 msgid "Add alt text" @@ -1157,7 +1162,7 @@ msgstr "" #: src/components/images/Gallery.tsx:120 #: src/components/Post/Embed/VideoEmbed/GifPresentationControls.tsx:94 #: src/view/com/composer/GifAltText.tsx:100 -#: src/view/com/composer/photos/Gallery.tsx:212 +#: src/view/com/composer/photos/Gallery.tsx:214 msgid "ALT" msgstr "" @@ -1176,7 +1181,7 @@ msgid "Alt Text" msgstr "" #: src/view/com/composer/GifAltText.tsx:105 -#: src/view/com/composer/photos/Gallery.tsx:124 +#: src/view/com/composer/photos/Gallery.tsx:125 msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone." msgstr "" @@ -3159,7 +3164,7 @@ msgstr "" msgid "Developer options" msgstr "" -#: src/lib/translation/index.tsx:297 +#: src/lib/translation/index.tsx:303 msgid "Device failed to translate :(" msgstr "Device failed to translate :(" @@ -3489,7 +3494,7 @@ msgstr "" #: src/view/com/composer/photos/EditImageDialog.web.tsx:86 #: src/view/com/composer/photos/EditImageDialog.web.tsx:90 -#: src/view/com/composer/photos/Gallery.tsx:219 +#: src/view/com/composer/photos/Gallery.tsx:221 msgid "Edit image" msgstr "" @@ -3760,7 +3765,7 @@ msgstr "" msgid "Enter your username and password" msgstr "" -#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:148 +#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:150 msgid "Enters full screen" msgstr "" @@ -6236,7 +6241,7 @@ msgstr "" msgid "Music" msgstr "" -#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:169 +#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:171 #: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx:97 msgctxt "video" msgid "Mute" @@ -6712,6 +6717,10 @@ msgstr "" msgid "No thanks" msgstr "" +#: src/lib/translation/index.tsx:308 +msgid "No translation received from your device." +msgstr "No translation received from your device." + #: src/view/screens/Profile.tsx:500 msgid "No video posts yet" msgstr "" @@ -7262,7 +7271,7 @@ msgstr "" msgid "Password updated!" msgstr "" -#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:153 +#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:155 #: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx:395 msgid "Pause" msgstr "" @@ -7377,7 +7386,7 @@ msgstr "" msgid "Pinned to your feeds" msgstr "" -#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:153 +#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:155 #: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx:396 msgid "Play" msgstr "" @@ -7404,7 +7413,7 @@ msgstr "" msgid "Plays or pauses the GIF" msgstr "" -#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:154 +#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:156 msgid "Plays or pauses the video" msgstr "" @@ -8121,7 +8130,7 @@ msgstr "" msgid "Remove from your feeds?" msgstr "" -#: src/view/com/composer/photos/Gallery.tsx:228 +#: src/view/com/composer/photos/Gallery.tsx:230 msgid "Remove image" msgstr "" @@ -8759,14 +8768,14 @@ msgstr "" msgid "Search for \"{interestsDisplayName}\" (active)" msgstr "" -#: src/view/shell/desktop/Search.tsx:130 -msgid "Search for \"{query}\"" -msgstr "" - #: src/screens/Search/components/AutocompleteResults.tsx:47 msgid "Search for \"{searchText}\"" msgstr "" +#: src/view/shell/desktop/Search.tsx:128 +msgid "Search for “{tQuery}”" +msgstr "Search for “{tQuery}”" + #: src/screens/StarterPack/Wizard/index.tsx:552 msgid "Search for feeds that you want to suggest to others." msgstr "" @@ -10609,7 +10618,7 @@ msgstr "" msgid "Toggle to enable or disable adult content" msgstr "" -#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:171 +#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:173 msgid "Toggles the sound" msgstr "" @@ -10664,6 +10673,10 @@ msgstr "Translating" msgid "Translating…" msgstr "Translating…" +#: src/lib/translation/index.tsx:305 +msgid "Translation to the same language is unavailable on your device." +msgstr "Translation to the same language is unavailable on your device." + #: src/screens/Settings/ThreadPreferences.tsx:87 #: src/screens/Settings/ThreadPreferences.tsx:92 msgid "Tree view" @@ -10882,7 +10895,7 @@ msgstr "" msgid "Unmute" msgstr "" -#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:168 +#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:170 #: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx:96 msgctxt "video" msgid "Unmute" @@ -10981,6 +10994,10 @@ msgstr "" msgid "Unsubscribed from list" msgstr "" +#: src/view/com/composer/text-input/TextInput.tsx:131 +msgid "Unsupported clipboard content" +msgstr "Unsupported clipboard content" + #: src/view/com/composer/Composer.tsx:1369 msgid "Unsupported video type: {mimeType}" msgstr "" @@ -11295,8 +11312,8 @@ msgstr "" msgid "Version {0}" msgstr "" -#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:86 -#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:147 +#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:87 +#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:149 msgid "Video" msgstr "" @@ -11344,7 +11361,7 @@ msgid "Video uploaded" msgstr "" #. placeholder {0}: embed.alt -#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:86 +#: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx:87 msgid "Video: {0}" msgstr "" From eb566c5fcca040f65825c30e89f5f079114c84e7 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 2 Apr 2026 06:39:40 -0700 Subject: [PATCH 14/32] Fix `useResolveUriQuery` (#10163) --- src/state/queries/resolve-uri.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state/queries/resolve-uri.ts b/src/state/queries/resolve-uri.ts index a4667c9228..a6ca192be3 100644 --- a/src/state/queries/resolve-uri.ts +++ b/src/state/queries/resolve-uri.ts @@ -44,7 +44,7 @@ export function useResolveUriQuery(uri: string | undefined) { ...resolvedDidQueryOptions(agent, getUnstableProfile, host), select: did => ({ did, - uri: urip.toString(), + uri: AtUri.make(did, urip.collection, urip.rkey).toString(), }), }) } From 68a4d73d61c46bd0fc205ebaa016e2e38912b50c Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 2 Apr 2026 08:34:12 -0700 Subject: [PATCH 15/32] Add `no-extraneous-dependencies` and `no-nodejs-modules` eslint rules (#10151) --- eslint.config.mjs | 12 +++++ jest/jestSetup.js | 1 + jest/test-utils.tsx | 23 -------- package.json | 2 +- .../ageAssurance/AgeAssuranceInitDialog.tsx | 2 +- src/components/dialogs/EmailDialog/events.ts | 2 +- .../moderation/LabelsOnMeDialog.tsx | 2 +- src/geolocation/service.ts | 2 +- src/lib/hooks/useNavigationDeduped.ts | 2 +- src/lib/hooks/useWebScrollRestoration.ts | 2 +- src/lib/media/manip.ts | 8 ++- src/lib/strings/errors.ts | 2 +- src/state/cache/post-shadow.ts | 2 +- src/state/cache/profile-shadow.ts | 2 +- src/state/events.ts | 2 +- src/state/global-gesture-events/index.tsx | 2 +- src/state/messages/convo/agent.ts | 4 +- src/state/messages/events/agent.ts | 2 +- src/state/persisted/index.web.ts | 2 +- src/state/queries/notifications/unread.tsx | 2 +- .../text-input/textInputWebEmitter.ts | 2 +- src/view/com/util/MainScrollProvider.tsx | 2 +- src/view/shell/desktop/RightNav.tsx | 2 +- src/view/shell/index.web.tsx | 2 +- yarn.lock | 52 +++++++++---------- 25 files changed, 66 insertions(+), 72 deletions(-) delete mode 100644 jest/test-utils.tsx diff --git a/eslint.config.mjs b/eslint.config.mjs index fc4474642d..abfdd4a013 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -189,6 +189,18 @@ export default defineConfig( */ ignore: ['^#\/locale\/locales\/.+\/messages'], }], + 'import-x/no-extraneous-dependencies': ['error', { + 'whitelist': [ + // test files only + '@jest/globals', + // we only use a really simple util from this, and we know it will be present + 'expo-modules-core', + // this is a dep for @atproto/api, but we absolutely need them in sync, so just + // rely on the transient version + '@atproto/common-web', + ] + }], + 'import-x/no-nodejs-modules': 'error', /** * TypeScript-specific rules diff --git a/jest/jestSetup.js b/jest/jestSetup.js index f9bc36f6bf..6a6987c79d 100644 --- a/jest/jestSetup.js +++ b/jest/jestSetup.js @@ -9,6 +9,7 @@ jest.mock('@react-native-async-storage/async-storage', () => require('@react-native-async-storage/async-storage/jest/async-storage-mock'), ) jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter', () => { + // eslint-disable-next-line import-x/no-nodejs-modules const {EventEmitter} = require('events') return { __esModule: true, diff --git a/jest/test-utils.tsx b/jest/test-utils.tsx deleted file mode 100644 index 264b31fae5..0000000000 --- a/jest/test-utils.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import {GestureHandlerRootView} from 'react-native-gesture-handler' -import {SafeAreaProvider} from 'react-native-safe-area-context' -import {render} from '@testing-library/react-native' - -import {ThemeProvider} from '../src/lib/ThemeContext' -import {type RootStoreModel, RootStoreProvider} from '../src/state' - -const customRender = (ui: any, rootStore: RootStoreModel) => - render( - - - - {ui} - - - , - ) - -// re-export everything -export * from '@testing-library/react-native' - -// override render method -export {customRender as render} diff --git a/package.json b/package.json index 36f7ec6152..1871a8e0af 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "icons:optimize": "svgo -f ./assets/icons" }, "dependencies": { - "@atproto/api": "^0.19.3", + "@atproto/api": "^0.19.5", "@bitdrift/react-native": "^0.6.8", "@braintree/sanitize-url": "^6.0.2", "@bsky.app/alf": "^0.1.7", diff --git a/src/components/ageAssurance/AgeAssuranceInitDialog.tsx b/src/components/ageAssurance/AgeAssuranceInitDialog.tsx index bf79f5dfc4..15e3a2b472 100644 --- a/src/components/ageAssurance/AgeAssuranceInitDialog.tsx +++ b/src/components/ageAssurance/AgeAssuranceInitDialog.tsx @@ -1,6 +1,6 @@ import {useState} from 'react' import {View} from 'react-native' -import {XRPCError} from '@atproto/xrpc' +import {XRPCError} from '@atproto/api' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' diff --git a/src/components/dialogs/EmailDialog/events.ts b/src/components/dialogs/EmailDialog/events.ts index 4fa171cad5..d2c3810208 100644 --- a/src/components/dialogs/EmailDialog/events.ts +++ b/src/components/dialogs/EmailDialog/events.ts @@ -1,5 +1,5 @@ import {useEffect} from 'react' -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' const events = new EventEmitter<{ emailVerified: void diff --git a/src/components/moderation/LabelsOnMeDialog.tsx b/src/components/moderation/LabelsOnMeDialog.tsx index c750079d1e..83fe2e4019 100644 --- a/src/components/moderation/LabelsOnMeDialog.tsx +++ b/src/components/moderation/LabelsOnMeDialog.tsx @@ -1,7 +1,7 @@ import {useCallback, useMemo, useState} from 'react' import {View} from 'react-native' import {type ComAtprotoLabelDefs, ToolsOzoneReportDefs} from '@atproto/api' -import {XRPCError} from '@atproto/xrpc' +import {XRPCError} from '@atproto/api' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' diff --git a/src/geolocation/service.ts b/src/geolocation/service.ts index 2d9285b676..ec34747284 100644 --- a/src/geolocation/service.ts +++ b/src/geolocation/service.ts @@ -1,5 +1,5 @@ import {useEffect, useState} from 'react' -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' import {networkRetry} from '#/lib/async/retry' import { diff --git a/src/lib/hooks/useNavigationDeduped.ts b/src/lib/hooks/useNavigationDeduped.ts index 2448787bd3..19d0c1c34f 100644 --- a/src/lib/hooks/useNavigationDeduped.ts +++ b/src/lib/hooks/useNavigationDeduped.ts @@ -1,5 +1,5 @@ import {useMemo} from 'react' -import {useNavigation} from '@react-navigation/core' +import {useNavigation} from '@react-navigation/native' import {useDedupe} from '#/lib/hooks/useDedupe' import {type NavigationProp} from '#/lib/routes/types' diff --git a/src/lib/hooks/useWebScrollRestoration.ts b/src/lib/hooks/useWebScrollRestoration.ts index 5f60d5a592..bc79bc534e 100644 --- a/src/lib/hooks/useWebScrollRestoration.ts +++ b/src/lib/hooks/useWebScrollRestoration.ts @@ -1,5 +1,5 @@ import {useEffect, useMemo, useState} from 'react' -import {type EventArg, useNavigation} from '@react-navigation/core' +import {type EventArg, useNavigation} from '@react-navigation/native' if ('scrollRestoration' in history) { // Tell the brower not to mess with the scroll. diff --git a/src/lib/media/manip.ts b/src/lib/media/manip.ts index 6d66fea489..2be799e261 100644 --- a/src/lib/media/manip.ts +++ b/src/lib/media/manip.ts @@ -15,7 +15,6 @@ import { import {manipulateAsync, SaveFormat} from 'expo-image-manipulator' import * as MediaLibrary from 'expo-media-library' import * as Sharing from 'expo-sharing' -import {Buffer} from 'buffer' import {POST_IMG_MAX} from '#/lib/constants' import {logger} from '#/logger' @@ -322,7 +321,12 @@ export async function saveBytesToDisk( bytes: Uint8Array, type: string, ) { - const encoded = Buffer.from(bytes).toString('base64') + // ideally we'd use `bytes.toBase64()`, but that's only baseline newly available + let binary = '' + for (const byte of bytes) { + binary += String.fromCharCode(byte) + } + const encoded = btoa(binary) return await saveToDevice(filename, encoded, type) } diff --git a/src/lib/strings/errors.ts b/src/lib/strings/errors.ts index f3a91e58dd..5f56cccc4d 100644 --- a/src/lib/strings/errors.ts +++ b/src/lib/strings/errors.ts @@ -1,4 +1,4 @@ -import {XRPCError} from '@atproto/xrpc' +import {XRPCError} from '@atproto/api' import {t} from '@lingui/core/macro' export function cleanError(str: any): string { diff --git a/src/state/cache/post-shadow.ts b/src/state/cache/post-shadow.ts index 0afa272c53..f01a7a081d 100644 --- a/src/state/cache/post-shadow.ts +++ b/src/state/cache/post-shadow.ts @@ -5,7 +5,7 @@ import { type AppBskyFeedDefs, } from '@atproto/api' import {type QueryClient} from '@tanstack/react-query' -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' import {batchedUpdates} from '#/lib/batchedUpdates' import {findAllPostsInQueryData as findAllPostsInBookmarksQueryData} from '#/state/queries/bookmarks/useBookmarksQuery' diff --git a/src/state/cache/profile-shadow.ts b/src/state/cache/profile-shadow.ts index 34422dc0c0..b8a6c58c35 100644 --- a/src/state/cache/profile-shadow.ts +++ b/src/state/cache/profile-shadow.ts @@ -1,7 +1,7 @@ import {useEffect, useMemo, useState} from 'react' import {type AppBskyActorDefs, type AppBskyNotificationDefs} from '@atproto/api' import {type QueryClient} from '@tanstack/react-query' -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' import {batchedUpdates} from '#/lib/batchedUpdates' import {findAllProfilesInQueryData as findAllProfilesInActivitySubscriptionsQueryData} from '#/state/queries/activity-subscriptions' diff --git a/src/state/events.ts b/src/state/events.ts index dcd36464ec..2ab72465de 100644 --- a/src/state/events.ts +++ b/src/state/events.ts @@ -1,4 +1,4 @@ -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' type UnlistenFn = () => void diff --git a/src/state/global-gesture-events/index.tsx b/src/state/global-gesture-events/index.tsx index 2f0d652210..4d3e9795dc 100644 --- a/src/state/global-gesture-events/index.tsx +++ b/src/state/global-gesture-events/index.tsx @@ -7,7 +7,7 @@ import { type GestureUpdateEvent, type PanGestureHandlerEventPayload, } from 'react-native-gesture-handler' -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' export type GlobalGestureEvents = { begin: GestureStateChangeEvent diff --git a/src/state/messages/convo/agent.ts b/src/state/messages/convo/agent.ts index d29049c872..b6c8ee2f16 100644 --- a/src/state/messages/convo/agent.ts +++ b/src/state/messages/convo/agent.ts @@ -5,8 +5,8 @@ import { type ChatBskyConvoGetLog, type ChatBskyConvoSendMessage, } from '@atproto/api' -import {XRPCError} from '@atproto/xrpc' -import EventEmitter from 'eventemitter3' +import {XRPCError} from '@atproto/api' +import {EventEmitter} from 'eventemitter3' import {nanoid} from 'nanoid/non-secure' import {networkRetry} from '#/lib/async/retry' diff --git a/src/state/messages/events/agent.ts b/src/state/messages/events/agent.ts index e8404fd000..ce9518212b 100644 --- a/src/state/messages/events/agent.ts +++ b/src/state/messages/events/agent.ts @@ -1,5 +1,5 @@ import {type BskyAgent, type ChatBskyConvoGetLog} from '@atproto/api' -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' import {nanoid} from 'nanoid/non-secure' import {networkRetry} from '#/lib/async/retry' diff --git a/src/state/persisted/index.web.ts b/src/state/persisted/index.web.ts index ff278b74be..35e796810d 100644 --- a/src/state/persisted/index.web.ts +++ b/src/state/persisted/index.web.ts @@ -1,4 +1,4 @@ -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' import BroadcastChannel from '#/lib/broadcast' import {logger} from '#/logger' diff --git a/src/state/queries/notifications/unread.tsx b/src/state/queries/notifications/unread.tsx index ce6e209386..bf7505f91b 100644 --- a/src/state/queries/notifications/unread.tsx +++ b/src/state/queries/notifications/unread.tsx @@ -12,7 +12,7 @@ import { } from 'react' import {AppState} from 'react-native' import {useQueryClient} from '@tanstack/react-query' -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' import BroadcastChannel from '#/lib/broadcast' import {resetBadgeCount} from '#/lib/notifications/notifications' diff --git a/src/view/com/composer/text-input/textInputWebEmitter.ts b/src/view/com/composer/text-input/textInputWebEmitter.ts index fb037cac2e..c889e12f57 100644 --- a/src/view/com/composer/text-input/textInputWebEmitter.ts +++ b/src/view/com/composer/text-input/textInputWebEmitter.ts @@ -1,3 +1,3 @@ -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' export const textInputWebEmitter = new EventEmitter() diff --git a/src/view/com/util/MainScrollProvider.tsx b/src/view/com/util/MainScrollProvider.tsx index a49a798983..3038ad21e0 100644 --- a/src/view/com/util/MainScrollProvider.tsx +++ b/src/view/com/util/MainScrollProvider.tsx @@ -7,7 +7,7 @@ import { withSpring, } from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import EventEmitter from 'eventemitter3' +import {EventEmitter} from 'eventemitter3' import {ScrollProvider} from '#/lib/ScrollContext' import {useMinimalShellMode} from '#/state/shell' diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx index d50a4bb679..07ea25632a 100644 --- a/src/view/shell/desktop/RightNav.tsx +++ b/src/view/shell/desktop/RightNav.tsx @@ -3,7 +3,7 @@ import {View} from 'react-native' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' -import {useNavigation} from '@react-navigation/core' +import {useNavigation} from '@react-navigation/native' import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants' import {useKawaiiMode} from '#/state/preferences/kawaii' diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index a7f255570e..ce13986524 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -36,7 +36,7 @@ import {NoAccessScreen} from '#/ageAssurance/components/NoAccessScreen' import {RedirectOverlay} from '#/ageAssurance/components/RedirectOverlay' import {PassiveAnalytics} from '#/analytics/PassiveAnalytics' import {FlatNavigator, RoutesContainer} from '#/Navigation' -import {Composer} from './Composer.web' +import {Composer} from './Composer' import {DrawerContent} from './Drawer' function ShellInner() { diff --git a/yarn.lock b/yarn.lock index 4a5d8dc52e..ca998282a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,46 +20,46 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@atproto/api@^0.19.3": - version "0.19.3" - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.19.3.tgz#61de8d2e31abe9eb2b4c8f4ad124ed79d4a77e89" - integrity sha512-G8YpBpRouHdTAIagi/QQIUZOhGd1jfBQWkJy9QfxAzjjEpPvaVOSk4e1S85QzGLm/xbzVONzGkmdtiOSfP6wVg== +"@atproto/api@^0.19.5": + version "0.19.5" + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.19.5.tgz#6388e5d6d3a1693fe04b5f37c705682bac8601d3" + integrity sha512-u6R5TecYJDO8l8QFN09AMuJASYnUkJ4HhYE5hg4/dha/z14a+OAil2/dli/208uM5AHPFLtlnB8kIK9XU5GgQQ== dependencies: - "@atproto/common-web" "^0.4.18" + "@atproto/common-web" "^0.4.19" "@atproto/lexicon" "^0.6.2" - "@atproto/syntax" "^0.5.0" + "@atproto/syntax" "^0.5.2" "@atproto/xrpc" "^0.7.7" await-lock "^2.2.2" multiformats "^9.9.0" tlds "^1.234.0" zod "^3.23.8" -"@atproto/common-web@^0.4.18": - version "0.4.18" - resolved "https://registry.yarnpkg.com/@atproto/common-web/-/common-web-0.4.18.tgz#832976340457afd3d29345ad2c6f0ac0bff087dd" - integrity sha512-ilImzP+9N/mtse440kN60pGrEzG7wi4xsV13nGeLrS+Zocybc/ISOpKlbZM13o+twPJ+Q7veGLw9CtGg0GAFoQ== +"@atproto/common-web@^0.4.18", "@atproto/common-web@^0.4.19": + version "0.4.19" + resolved "https://registry.yarnpkg.com/@atproto/common-web/-/common-web-0.4.19.tgz#bbd7f84f545ebe73ca3bc00314ccf4ee66e7069e" + integrity sha512-3BTi58p5WpT+9/zb6UZrdsXcfPo5P45UJm0E4iwHLILr+jc37CuBj9JReDSZ4U0i9RTrI3ZkfySyZ9bd+LnMsw== dependencies: - "@atproto/lex-data" "^0.0.13" - "@atproto/lex-json" "^0.0.13" - "@atproto/syntax" "^0.5.0" + "@atproto/lex-data" "^0.0.14" + "@atproto/lex-json" "^0.0.14" + "@atproto/syntax" "^0.5.1" zod "^3.23.8" -"@atproto/lex-data@^0.0.13": - version "0.0.13" - resolved "https://registry.yarnpkg.com/@atproto/lex-data/-/lex-data-0.0.13.tgz#db1bcfa12d5056210f6eb7f3b8bac909909d6b9c" - integrity sha512-7Z7RwZ1Y/JzBF/Tcn/I4UJ/vIGfh5zn1zjv0KX+flke2JtgFkSE8uh2hOtqgBQMNqE3zdJFM+dcSWln86hR3MQ== +"@atproto/lex-data@^0.0.14": + version "0.0.14" + resolved "https://registry.yarnpkg.com/@atproto/lex-data/-/lex-data-0.0.14.tgz#2f2f3c64699925a0d4785e5afd0e7731ba1d46c0" + integrity sha512-53DUa9664SS76nGAMYopWsO10OH0AAdf7P/HSKB6Wzx3iqe6lk/K61QZnKxOG1LreYl5CfvIJU6eNf4txI6GlQ== dependencies: multiformats "^9.9.0" tslib "^2.8.1" uint8arrays "3.0.0" unicode-segmenter "^0.14.0" -"@atproto/lex-json@^0.0.13": - version "0.0.13" - resolved "https://registry.yarnpkg.com/@atproto/lex-json/-/lex-json-0.0.13.tgz#b0081f786aeeb1707087318fb03c928e75c19059" - integrity sha512-hwLhkKaIHulGJpt0EfXAEWdrxqM2L1tV/tvilzhMp3QxPqYgXchFnrfVmLsyFDx6P6qkH1GsX/XC2V36U0UlPQ== +"@atproto/lex-json@^0.0.14": + version "0.0.14" + resolved "https://registry.yarnpkg.com/@atproto/lex-json/-/lex-json-0.0.14.tgz#717e533ab583aa5f580acb2a77d9aa3e7eddaa17" + integrity sha512-6lPkDKqe7teEu4WrN5q7400cvZKgYS3uwUMvzG3F9XkgVYhOwSDCtouV/nSLBbpvo3l9OP0kiigtclcNcyekww== dependencies: - "@atproto/lex-data" "^0.0.13" + "@atproto/lex-data" "^0.0.14" tslib "^2.8.1" "@atproto/lexicon@^0.6.0", "@atproto/lexicon@^0.6.2": @@ -73,10 +73,10 @@ multiformats "^9.9.0" zod "^3.23.8" -"@atproto/syntax@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.5.0.tgz#061ef538aee784f8e5fa1ea50a7f5beb4c276c9b" - integrity sha512-UA2DSpGdOQzUQ4gi5SH+NEJz/YR3a3Fg3y2oh+xETDSiTRmA4VhHRCojhXAVsBxUT6EnItw190C/KN+DWW90kw== +"@atproto/syntax@^0.5.0", "@atproto/syntax@^0.5.1", "@atproto/syntax@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.5.2.tgz#d4b32c9feb421ceeb5ade1fa80bc42764d51e52e" + integrity sha512-W41szOnkppoHr0iCUrzL8gy3OD6qmDyp1UvUgmTx2oFQfgbudpz51T/gznesiCcqiUT5obfHdx4PJ+WdlEOE7Q== dependencies: tslib "^2.8.1" From b9f3d04d65b0c639a4ad5d59c38cb5435ffe74e3 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 2 Apr 2026 11:51:38 -0700 Subject: [PATCH 16/32] Scale profile badge icons with font size (#10161) --- src/components/ProfileBadges.tsx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/components/ProfileBadges.tsx b/src/components/ProfileBadges.tsx index dd0cedfe8f..22c682bbba 100644 --- a/src/components/ProfileBadges.tsx +++ b/src/components/ProfileBadges.tsx @@ -1,7 +1,7 @@ -import {View} from 'react-native' +import {useWindowDimensions, View} from 'react-native' import {useProfileShadow} from '#/state/cache/profile-shadow' -import {atoms as a, type ViewStyleProp} from '#/alf' +import {atoms as a, useAlf, type ViewStyleProp} from '#/alf' import {BotBadge, BotBadgeButton, isBotAccount} from '#/components/BotBadge' import {useSimpleVerificationState} from '#/components/verification' import {VerificationCheck} from '#/components/verification/VerificationCheck' @@ -38,12 +38,21 @@ export function ProfileBadges({ }) { const shadowed = useProfileShadow(profile) const verification = useSimpleVerificationState({profile}) + const {fontScale: nativeScaleMultiplier} = useWindowDimensions() + const { + fonts: {scaleMultiplier: alfScaleMultiplier}, + } = useAlf() // if nothing to show, don't render the container at all if (!verification.showBadge && !isBotAccount(shadowed)) return null const isOnTheSmallSide = size === 'xs' || size === 'sm' + const verificationIconWidth = + verificationIconSizes[size] * nativeScaleMultiplier * alfScaleMultiplier + const botIconWidth = + botIconSizes[size] * nativeScaleMultiplier * alfScaleMultiplier + return ( - + ) : ( <> {verification.showBadge && ( )} - + )} From 9fe808f8a8962b2bdc9ee3638ad231c5b0fbce7b Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 2 Apr 2026 11:51:54 -0700 Subject: [PATCH 17/32] Fix bottom bar badge text padding (#10162) --- src/view/shell/bottom-bar/BottomBar.tsx | 10 ++++++++-- src/view/shell/bottom-bar/BottomBarStyles.tsx | 12 ++++++------ src/view/shell/bottom-bar/BottomBarWeb.tsx | 4 +++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index a1386c644c..8c1fb3c52b 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -400,10 +400,16 @@ function Btn({ a.rounded_full, {backgroundColor: t.palette.primary_500}, ]}> - {notificationCount} + + {notificationCount} + ) : hasNew ? ( - + ) : null} ) diff --git a/src/view/shell/bottom-bar/BottomBarStyles.tsx b/src/view/shell/bottom-bar/BottomBarStyles.tsx index 3c99eaf6f6..2602862dcb 100644 --- a/src/view/shell/bottom-bar/BottomBarStyles.tsx +++ b/src/view/shell/bottom-bar/BottomBarStyles.tsx @@ -1,6 +1,5 @@ import {StyleSheet} from 'react-native' -import {colors} from '#/lib/styles' import {atoms as a} from '#/alf' export const styles = StyleSheet.create({ @@ -24,8 +23,9 @@ export const styles = StyleSheet.create({ position: 'absolute', left: '52%', top: 8, - paddingHorizontal: 4, - paddingBottom: 1, + paddingHorizontal: 5, + paddingTop: 1, + paddingBottom: 2, borderRadius: 6, zIndex: 1, }, @@ -37,8 +37,9 @@ export const styles = StyleSheet.create({ notificationCountLabel: { fontSize: 12, fontWeight: '600', - color: colors.white, + color: 'white', fontVariant: ['tabular-nums'], + includeFontPadding: false, }, hasNewBadge: { position: 'absolute', @@ -47,8 +48,7 @@ export const styles = StyleSheet.create({ top: 10, width: 8, height: 8, - backgroundColor: colors.blue3, - borderRadius: 6, + borderRadius: 4, zIndex: 1, }, ctrlIcon: { diff --git a/src/view/shell/bottom-bar/BottomBarWeb.tsx b/src/view/shell/bottom-bar/BottomBarWeb.tsx index e0d1edd66a..2bf9807e43 100644 --- a/src/view/shell/bottom-bar/BottomBarWeb.tsx +++ b/src/view/shell/bottom-bar/BottomBarWeb.tsx @@ -313,7 +313,9 @@ const NavItem: React.FC<{ {notificationCount} ) : hasNew ? ( - + ) : null} ) From e0ea778e585807b24beb07bd8ab75a61131522ce Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Thu, 2 Apr 2026 15:47:56 -0700 Subject: [PATCH 18/32] Warn when useState get/set names mismatch (#10166) --- eslint.config.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index abfdd4a013..a0f0db9140 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -127,6 +127,7 @@ export default defineConfig( */ ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, + 'react/hook-use-state': 'warn', 'react/no-unescaped-entities': 'off', 'react/prop-types': 'off', 'react-native/no-inline-styles': 'off', From cca3326b21540ca2973793517ba05c00f43f734b Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:06:51 -0700 Subject: [PATCH 19/32] Create global keyboard shortcut handler (#10145) --- package.json | 1 + src/App.native.tsx | 27 ++-- src/App.web.tsx | 32 ++-- src/components/forms/SearchInput.tsx | 141 ++++++++++-------- src/lib/hotkeys/index.tsx | 76 ++++++++++ src/screens/Search/Shell.tsx | 1 + src/state/dialogs/index.tsx | 26 +++- src/state/events.ts | 8 + src/state/lightbox.tsx | 12 +- src/state/modals/index.tsx | 12 +- .../composer/useComposerKeyboardShortcut.tsx | 77 ---------- src/state/shell/drawer-open.tsx | 16 +- src/view/shell/desktop/LeftNav.tsx | 2 +- src/view/shell/desktop/Search.tsx | 1 + src/view/shell/index.web.tsx | 2 - yarn.lock | 5 + 16 files changed, 257 insertions(+), 182 deletions(-) create mode 100644 src/lib/hotkeys/index.tsx delete mode 100644 src/state/shell/composer/useComposerKeyboardShortcut.tsx diff --git a/package.json b/package.json index 1871a8e0af..8e290f4e5c 100644 --- a/package.json +++ b/package.json @@ -199,6 +199,7 @@ "react": "19.1.0", "react-compiler-runtime": "^19.1.0-rc.1", "react-dom": "19.1.0", + "react-hotkeys-hook": "5.2.4", "react-image-crop": "^11.0.7", "react-is": "19", "react-keyed-flatten-children": "^5.0.0", diff --git a/src/App.native.tsx b/src/App.native.tsx index 6f6a2220c9..026f6079b1 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -11,8 +11,7 @@ import { import * as ScreenOrientation from 'expo-screen-orientation' import * as SplashScreen from 'expo-splash-screen' import * as SystemUI from 'expo-system-ui' -import {msg} from '@lingui/core/macro' -import {useLingui} from '@lingui/react' +import {useLingui} from '@lingui/react/macro' import * as Sentry from '@sentry/react-native' import {Provider as HideBottomBarBorderProvider} from '#/lib/hooks/useHideBottomBarBorder' @@ -89,9 +88,9 @@ import {Splash} from '#/Splash' import {BottomSheetProvider} from '../modules/bottom-sheet' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' -SplashScreen.preventAutoHideAsync() +void SplashScreen.preventAutoHideAsync() if (IS_IOS) { - SystemUI.setBackgroundColorAsync('black') + void SystemUI.setBackgroundColorAsync('black') } if (IS_ANDROID) { // iOS is handled by the config plugin -sfn @@ -105,17 +104,17 @@ if (IS_ANDROID) { /** * Begin geolocation ASAP */ -Geo.resolve() -prefetchAgeAssuranceConfig() -prefetchLiveEvents() -prefetchAppConfig() +void Geo.resolve() +void prefetchAgeAssuranceConfig() +void prefetchLiveEvents() +void prefetchAppConfig() function InnerApp() { const [isReady, setIsReady] = useState(false) const {currentAccount} = useSession() const {resumeSession} = useSessionApi() const theme = useColorModeTheme() - const {_} = useLingui() + const {t: l} = useLingui() const hasCheckedReferrer = useStarterPackEntry() // init @@ -134,16 +133,16 @@ function InnerApp() { } } const account = readLastActiveAccount() - onLaunch(account) + void onLaunch(account) }, [resumeSession]) useEffect(() => { return listenSessionDropped(() => { - Toast.show(_(msg`Sorry! Your session expired. Please sign in again.`), { + Toast.show(l`Sorry! Your session expired. Please sign in again.`, { type: 'info', }) }) - }, [_]) + }, [l]) return ( @@ -220,8 +219,8 @@ function App() { const [isReady, setReady] = useState(false) useEffect(() => { - Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(() => - setReady(true), + void Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then( + () => setReady(true), ) }, []) diff --git a/src/App.web.tsx b/src/App.web.tsx index fc2a5b1650..c391d69925 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -5,10 +5,10 @@ import './style.css' import {Fragment, useEffect, useState} from 'react' import {KeyboardProvider as KeyboardControllerProvider} from 'react-native-keyboard-controller' import {SafeAreaProvider} from 'react-native-safe-area-context' -import {msg} from '@lingui/core/macro' -import {useLingui} from '@lingui/react' +import {useLingui} from '@lingui/react/macro' import * as Sentry from '@sentry/react-native' +import {Provider as HotkeysProvider} from '#/lib/hotkeys' import {QueryProvider} from '#/lib/react-query' import {ThemeProvider} from '#/lib/ThemeContext' import {Provider as TranslateOnDeviceProvider} from '#/lib/translation' @@ -82,17 +82,17 @@ import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottom /** * Begin geolocation ASAP */ -Geo.resolve() -prefetchAgeAssuranceConfig() -prefetchLiveEvents() -prefetchAppConfig() +void Geo.resolve() +void prefetchAgeAssuranceConfig() +void prefetchLiveEvents() +void prefetchAppConfig() function InnerApp() { const [isReady, setIsReady] = useState(false) const {currentAccount} = useSession() const {resumeSession} = useSessionApi() const theme = useColorModeTheme() - const {_} = useLingui() + const {t: l} = useLingui() const hasCheckedReferrer = useStarterPackEntry() // init @@ -105,22 +105,22 @@ function InnerApp() { await features.init } } catch (e) { - logger.error(`session: resumeSession failed`, {message: e}) + logger.error('session: resumeSession failed', {message: e}) } finally { setIsReady(true) } } const account = readLastActiveAccount() - onLaunch(account) + void onLaunch(account) }, [resumeSession]) useEffect(() => { return listenSessionDropped(() => { - Toast.show(_(msg`Sorry! Your session expired. Please sign in again.`), { + Toast.show(l`Sorry! Your session expired. Please sign in again.`, { type: 'info', }) }) - }, [_]) + }, [l]) return ( @@ -156,8 +156,10 @@ function InnerApp() { - - + + + + @@ -195,8 +197,8 @@ function App() { const [isReady, setReady] = useState(false) useEffect(() => { - Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then(() => - setReady(true), + void Promise.all([initPersistedState(), Geo.resolve(), setupDeviceId]).then( + () => setReady(true), ) }, []) diff --git a/src/components/forms/SearchInput.tsx b/src/components/forms/SearchInput.tsx index 47829101ec..8b54b44246 100644 --- a/src/components/forms/SearchInput.tsx +++ b/src/components/forms/SearchInput.tsx @@ -1,8 +1,9 @@ -import {forwardRef} from 'react' +import {useEffect, useRef} from 'react' import {type TextInput, View} from 'react-native' import {useLingui} from '@lingui/react/macro' import {HITSLOP_10} from '#/lib/constants' +import {listenFocusSearch} from '#/state/events' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' import * as TextField from '#/components/forms/TextField' @@ -10,73 +11,89 @@ import {MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlassIcon} from '#/ import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {IS_NATIVE} from '#/env' -type SearchInputProps = Omit & { +type Props = Omit & { label?: TextField.InputProps['label'] /** * Called when the user presses the (X) button */ onClearText?: () => void + hotkey?: boolean + ref?: React.RefObject } -export const SearchInput = forwardRef( - function SearchInput({value, label, onClearText, ...rest}, ref) { - const t = useTheme() - const {t: l} = useLingui() - const showClear = value && value.length > 0 +export function SearchInput({ + value, + label, + onClearText, + hotkey, + ref, + ...rest +}: Props) { + const t = useTheme() + const {t: l} = useLingui() + const showClear = value && value.length > 0 + const internalRef = useRef(null) + const inputRef = ref ?? internalRef - return ( - - - - - + useEffect(() => { + if (!hotkey) return + return listenFocusSearch(() => { + inputRef.current?.focus() + }) + }, [hotkey, inputRef]) - {showClear && ( - - - - )} - - ) - }, -) + return ( + + + + + + + {showClear && ( + + + + )} + + ) +} diff --git a/src/lib/hotkeys/index.tsx b/src/lib/hotkeys/index.tsx new file mode 100644 index 0000000000..6cddf3b13b --- /dev/null +++ b/src/lib/hotkeys/index.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import {useLingui} from '@lingui/react/macro' +import { + HotkeysProvider, + useHotkeys, + useHotkeysContext, +} from 'react-hotkeys-hook' + +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' +import {emitFocusSearch} from '#/state/events' +import {useSession} from '#/state/session' + +enum Hotkeys { + OPEN_COMPOSER = 'n', + FOCUS_SEARCH = 'slash', +} + +export function Provider({children}: React.PropsWithChildren) { + return ( + + {children} + + ) +} + +export {useHotkeysContext} + +function KeyboardShortcuts({children}: React.PropsWithChildren) { + useKeyboardShortcuts() + return children +} + +function useKeyboardShortcuts() { + const {openComposer} = useOpenComposer() + const {hasSession} = useSession() + const {t: l} = useLingui() + + const shouldIgnore = (requiresSession: boolean = false) => { + if (requiresSession && !hasSession) { + return true + } + + return false + } + + const handleKey = ( + callback: () => void, + options?: {requiresSession?: boolean}, + ) => { + if (shouldIgnore(options?.requiresSession)) { + return + } + callback() + } + + useHotkeys( + Hotkeys.OPEN_COMPOSER, + () => + handleKey( + () => { + openComposer({logContext: 'Other'}) + }, + { + requiresSession: true, + }, + ), + {scopes: ['global'], description: l`Compose new post`}, + [openComposer], + ) + + useHotkeys(Hotkeys.FOCUS_SEARCH, () => handleKey(emitFocusSearch), { + scopes: ['global'], + preventDefault: true, + description: l`Focus the search field`, + }) +} diff --git a/src/screens/Search/Shell.tsx b/src/screens/Search/Shell.tsx index f46812285a..ac0ad75483 100644 --- a/src/screens/Search/Shell.tsx +++ b/src/screens/Search/Shell.tsx @@ -380,6 +380,7 @@ export function SearchScreenShell({ inputPlaceholder ?? l`Search for posts, users, or feeds` } hitSlop={{...HITSLOP_20, top: 0}} + hotkey={true} /> {showAutocomplete && ( diff --git a/src/state/dialogs/index.tsx b/src/state/dialogs/index.tsx index 93170f6275..f9711bfa3a 100644 --- a/src/state/dialogs/index.tsx +++ b/src/state/dialogs/index.tsx @@ -7,6 +7,7 @@ import { useState, } from 'react' +import {useHotkeysContext} from '#/lib/hotkeys' import {type DialogControlRefProps} from '#/components/Dialog' import {Provider as GlobalDialogsProvider} from '#/components/dialogs/Context' import {IS_WEB} from '#/env' @@ -62,6 +63,7 @@ export function useDialogFullyExpandedCountContext() { export function Provider({children}: React.PropsWithChildren<{}>) { const [fullyExpandedCount, setFullyExpandedCount] = useState(0) + const {disableScope, enableScope} = useHotkeysContext() const activeDialogs = useRef< Map> @@ -77,18 +79,26 @@ export function Provider({children}: React.PropsWithChildren<{}>) { return openDialogs.current.size > 0 } else { - BottomSheetNativeComponent.dismissAll() + void BottomSheetNativeComponent.dismissAll() return false } }, []) - const setDialogIsOpen = useCallback((id: string, isOpen: boolean) => { - if (isOpen) { - openDialogs.current.add(id) - } else { - openDialogs.current.delete(id) - } - }, []) + const setDialogIsOpen = useCallback( + (id: string, isOpen: boolean) => { + if (isOpen) { + openDialogs.current.add(id) + } else { + openDialogs.current.delete(id) + } + if (openDialogs.current.size > 0) { + disableScope('global') + } else { + enableScope('global') + } + }, + [disableScope, enableScope], + ) const context = useMemo( () => ({ diff --git a/src/state/events.ts b/src/state/events.ts index 2ab72465de..87a1fab705 100644 --- a/src/state/events.ts +++ b/src/state/events.ts @@ -45,3 +45,11 @@ export function listenPostCreated(fn: () => void): UnlistenFn { emitter.on('post-created', fn) return () => emitter.off('post-created', fn) } + +export function emitFocusSearch() { + emitter.emit('focus-search') +} +export function listenFocusSearch(fn: () => void): UnlistenFn { + emitter.on('focus-search', fn) + return () => emitter.off('focus-search', fn) +} diff --git a/src/state/lightbox.tsx b/src/state/lightbox.tsx index 52c74278cb..1e22cc98a4 100644 --- a/src/state/lightbox.tsx +++ b/src/state/lightbox.tsx @@ -1,7 +1,8 @@ -import {createContext, useContext, useMemo, useState} from 'react' +import {createContext, useContext, useEffect, useMemo, useState} from 'react' import {nanoid} from 'nanoid/non-secure' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' +import {useHotkeysContext} from '#/lib/hotkeys' import {type ImageSource} from '#/view/com/lightbox/ImageViewing/@types' export type Lightbox = { @@ -28,6 +29,15 @@ LightboxControlContext.displayName = 'LightboxControlContext' export function Provider({children}: React.PropsWithChildren<{}>) { const [activeLightbox, setActiveLightbox] = useState(null) + const {disableScope, enableScope} = useHotkeysContext() + + useEffect(() => { + if (activeLightbox) { + disableScope('global') + } else { + enableScope('global') + } + }, [activeLightbox, disableScope, enableScope]) const openLightbox = useNonReactiveCallback( (lightbox: Omit) => { diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx index 484890ba15..3c545362b8 100644 --- a/src/state/modals/index.tsx +++ b/src/state/modals/index.tsx @@ -1,6 +1,7 @@ -import {createContext, useContext, useMemo, useState} from 'react' +import {createContext, useContext, useEffect, useMemo, useState} from 'react' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' +import {useHotkeysContext} from '#/lib/hotkeys' export interface UserAddRemoveListsModal { name: 'user-add-remove-lists' @@ -47,6 +48,15 @@ ModalControlContext.displayName = 'ModalControlContext' export function Provider({children}: React.PropsWithChildren<{}>) { const [activeModals, setActiveModals] = useState([]) + const {disableScope, enableScope} = useHotkeysContext() + + useEffect(() => { + if (activeModals.length > 0) { + disableScope('global') + } else { + enableScope('global') + } + }, [activeModals.length, disableScope, enableScope]) const openModal = useNonReactiveCallback((modal: Modal) => { setActiveModals(modals => [...modals, modal]) diff --git a/src/state/shell/composer/useComposerKeyboardShortcut.tsx b/src/state/shell/composer/useComposerKeyboardShortcut.tsx deleted file mode 100644 index a1e76fdfd9..0000000000 --- a/src/state/shell/composer/useComposerKeyboardShortcut.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import {useEffect} from 'react' - -import {useOpenComposer} from '#/lib/hooks/useOpenComposer' -import {useDialogStateContext} from '#/state/dialogs' -import {useLightbox} from '#/state/lightbox' -import {useModals} from '#/state/modals' -import {useSession} from '#/state/session' -import {useIsDrawerOpen} from '#/state/shell/drawer-open' - -/** - * Based on {@link https://github.com/jaywcjlove/hotkeys-js/blob/b0038773f3b902574f22af747f3bb003a850f1da/src/index.js#L51C1-L64C2} - */ -function shouldIgnore(event: KeyboardEvent) { - const target: any = event.target || event.srcElement - if (!target) return false - const {tagName} = target - if (!tagName) return false - const isInput = - tagName === 'INPUT' && - ![ - 'checkbox', - 'radio', - 'range', - 'button', - 'file', - 'reset', - 'submit', - 'color', - ].includes(target.type) - // ignore: isContentEditable === 'true', and