From 498d321bb1ec00085ca2e52f879cb67bf765fec5 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 31 Mar 2026 09:35:31 -0700 Subject: [PATCH 01/12] 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 02/12] 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 03/12] 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 04/12] 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 05/12] 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 06/12] 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 07/12] 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 08/12] 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 09/12] 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 10/12] 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 11/12] 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 12/12] 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(), }), }) }