Add error handling with Google Translate link

This commit is contained in:
DS Boyce
2026-03-03 15:49:13 -08:00
parent c47c1e7aaf
commit ffce872adb
5 changed files with 100 additions and 12 deletions
+1 -1
View File
@@ -85,7 +85,7 @@
"@braintree/sanitize-url": "^6.0.2",
"@bsky.app/alf": "^0.1.7",
"@bsky.app/expo-image-crop-tool": "^0.5.0",
"@bsky.app/expo-translate-text": "^0.2.4",
"@bsky.app/expo-translate-text": "^0.2.5",
"@bsky.app/react-native-mmkv": "2.12.5",
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
"@emoji-mart/react": "^1.1.1",
+78 -2
View File
@@ -18,6 +18,7 @@ import {atoms as a, native, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {ArrowRight_Stroke2_Corner0_Rounded as ArrowRight} from '#/components/icons/Arrow'
import {TimesLarge_Stroke2_Corner0_Rounded as Times} from '#/components/icons/Times'
import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/Warning'
import {InlineLinkText} from '#/components/Link'
import {Loader} from '#/components/Loader'
import * as Select from '#/components/Select'
@@ -45,6 +46,17 @@ export function TranslatedPost({
[post, langPrefs.primaryLanguage],
)
if (translationState.status === 'error') {
return (
<TranslationError
clearTranslation={clearTranslation}
message={translationState.message}
postText={postText}
primaryLanguage={langPrefs.primaryLanguage}
/>
)
}
if (translationState.status === 'loading') {
return <TranslationLoading />
}
@@ -137,6 +149,63 @@ function TranslationLink({
)
}
function TranslationError({
clearTranslation,
message,
postText,
primaryLanguage,
}: {
clearTranslation: () => void
message: string
postText: string
primaryLanguage: string
}) {
const t = useTheme()
const {t: l} = useLingui()
return (
<View
style={[
a.px_lg,
a.py_md,
a.mt_sm,
a.border,
a.rounded_lg,
t.atoms.border_contrast_high,
]}>
<View
style={[a.flex_row, a.flex_wrap, a.align_center, a.justify_between]}>
<View style={[a.flex_row, a.align_center, a.mb_sm]}>
<Warning size="sm" fill={t.atoms.text_contrast_medium.color} />
<Text style={[a.text_xs, a.font_medium, t.atoms.text_contrast_high]}>
{' '}
{message}
</Text>
</View>
<View style={[a.flex_row, a.align_center, a.mb_xs]}>
<Button
label={l`Hide translation`}
hitSlop={HITSLOP_30}
hoverStyle={native({opacity: 0.5})}
onPress={clearTranslation}>
<Times size="sm" fill={t.atoms.text_contrast_medium.color} />
</Button>
</View>
</View>
<View style={[a.flex_row, a.align_center]}>
<Text>
<InlineLinkText
to={getTranslatorLink(postText, primaryLanguage)}
label={l`Try Google Translate`}
style={[a.text_xs, a.font_medium]}>
<Trans>Try Google Translate</Trans>
</InlineLinkText>
</Text>
</View>
</View>
)
}
function TranslationResult({
clearTranslation,
translate,
@@ -213,7 +282,12 @@ function TranslationResult({
)}
{sourceLanguage != null && (
<>
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
<Text
style={[
a.text_xs,
a.font_medium,
t.atoms.text_contrast_medium,
]}>
{' '}
&middot;{' '}
</Text>
@@ -250,6 +324,7 @@ function TranslationLanguageSelect({
postText: string
sourceLanguage: string
}) {
const t = useTheme()
const ax = useAnalytics()
const {t: l} = useLingui()
const langPrefs = useLanguagePrefs()
@@ -300,7 +375,8 @@ function TranslationLanguageSelect({
{...props}
hitSlop={HITSLOP_30}
hoverStyle={native({opacity: 0.5})}>
<Text style={[a.text_xs, a.font_medium]}>
<Text
style={[a.text_xs, a.font_medium, t.atoms.text_contrast_high]}>
<Trans>Change</Trans>
</Text>
</Button>
+13 -5
View File
@@ -2,6 +2,7 @@ import {useCallback, useContext, useEffect, useMemo, useState} from 'react'
import {LayoutAnimation, Platform} from 'react-native'
import {getLocales} from 'expo-localization'
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'
@@ -61,9 +62,15 @@ async function attemptTranslation(
})
// Since `input` is always a string, the result should always be a string.
const translatedText =
typeof result.translatedTexts === 'string' ? result.translatedTexts : ''
if (translatedText === input) {
throw new Error('Translation result is the same as the source text.')
}
return {
translatedText:
typeof result.translatedTexts === 'string' ? result.translatedTexts : '',
translatedText,
targetLanguage: result.targetLanguage,
sourceLanguage: result.sourceLanguage ?? sourceLangCode ?? null, // iOS doesn't return the source language
}
@@ -125,6 +132,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
>({})
const [refCounts, setRefCounts] = useState<Record<string, number>>({})
const ax = useAnalytics()
const {t: l} = useLingui()
const googleTranslate = useGoogleTranslate()
useEffect(() => {
@@ -235,12 +243,12 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
sourceLanguage: sourceLangCode ?? null,
targetLanguage: targetLangCode,
})
let errorMessage = l`Device failed to translate. :(`
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
setTranslationState(prev => ({
...prev,
[key]: {status: 'idle'},
[key]: {status: 'error', message: errorMessage},
}))
await googleTranslate(text, targetLangCode, sourceLangCode)
}
}
@@ -254,7 +262,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
[key]: {status: 'loading'},
}))
},
[ax, googleTranslate],
[ax, googleTranslate, l],
)
const ctx = useMemo(
+4
View File
@@ -9,6 +9,10 @@ export type TranslationState =
sourceLanguage: TranslationTaskResult['sourceLanguage']
targetLanguage: TranslationTaskResult['targetLanguage']
}
| {
status: 'error'
message: string
}
export type TranslationFunctionParams = {
/**
+4 -4
View File
@@ -3731,10 +3731,10 @@
resolved "https://registry.yarnpkg.com/@bsky.app/expo-image-crop-tool/-/expo-image-crop-tool-0.5.0.tgz#4308fbde5c15e6be9122601797bc3d9549c95e31"
integrity sha512-gmhQr2HWTRFyPO00fn5OmtiEVtikXusHMrN5Zoq26pu1VZX3zVE+aoc668etTqrvsQcm2Qu8fo96k5F3Wu+6wg==
"@bsky.app/expo-translate-text@^0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@bsky.app/expo-translate-text/-/expo-translate-text-0.2.4.tgz#6e7f20f286111ee4d550c0c84f57393fc215a675"
integrity sha512-7mvFggNfkJEufI5A3WnjfjdN3H9P6Dpx7CpDkA9npWqA8Cb2icXq3k3nz3MaXGrVKTYiJnytAffYtos7mPoeOg==
"@bsky.app/expo-translate-text@^0.2.5":
version "0.2.5"
resolved "https://registry.yarnpkg.com/@bsky.app/expo-translate-text/-/expo-translate-text-0.2.5.tgz#eb0bf41522b710b11e64d14a06d4ec770a72ad20"
integrity sha512-nWGxK8bwKN6G7AtN7bvCIyO3Ynbzeg+NDzfGcWh0nEfuP4gKTrFO6QnBMMcZ9r+SzLP3EryqEs0AzeZ56n6Y+A==
"@bsky.app/react-native-mmkv@2.12.5":
version "2.12.5"