import {useCallback, useMemo} from 'react'
import {Platform, View} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro'
import {HITSLOP_30} from '#/lib/constants'
import {useGoogleTranslate} from '#/lib/hooks/useGoogleTranslate'
import {useTranslate} from '#/lib/translation'
import {type TranslationFunction} from '#/lib/translation'
import {
codeToLanguageName,
isPostInLanguage,
languageName,
} from '#/locale/helpers'
import {LANGUAGES} from '#/locale/languages'
import {useLanguagePrefs} from '#/state/preferences'
import {atoms as a, native, useTheme, web} from '#/alf'
import {Button} from '#/components/Button'
import {ArrowRight_Stroke2_Corner0_Rounded as ArrowRightIcon} from '#/components/icons/Arrow'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
import {createStaticClick, Link} from '#/components/Link'
import {Loader} from '#/components/Loader'
import * as Select from '#/components/Select'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_WEB} from '#/env'
export function TranslatedPost({
hideTranslateLink = false,
post,
postText,
}: {
hideTranslateLink?: boolean
post: AppBskyFeedDefs.PostView
postText: string
}) {
const langPrefs = useLanguagePrefs()
const {clearTranslation, translate, translationState} = useTranslate({
key: post.uri,
})
const needsTranslation = useMemo(() => {
if (hideTranslateLink) return false
return !isPostInLanguage(post, [langPrefs.primaryLanguage])
}, [hideTranslateLink, post, langPrefs.primaryLanguage])
switch (translationState.status) {
case 'loading':
return
case 'success':
return (
)
case 'error':
return (
)
default:
return (
needsTranslation && (
)
)
}
}
function TranslationLoading() {
const t = useTheme()
return (
Translating…
)
}
function TranslationLink({
postText,
primaryLanguage,
translate,
}: {
postText: string
primaryLanguage: string
translate: TranslationFunction
}) {
const t = useTheme()
const {t: l} = useLingui()
const ax = useAnalytics()
const handleTranslate = useCallback(() => {
void translate({
text: postText,
targetLangCode: primaryLanguage,
})
ax.metric('translate', {
sourceLanguages: [], // todo: get from post maybe?
targetLanguage: primaryLanguage,
textLength: postText.length,
})
}, [ax, postText, primaryLanguage, translate])
return (
{
handleTranslate()
})}
label={l`Translate`}
hoverStyle={[
native({opacity: 0.5}),
web([a.underline, {textDecorationColor: t.palette.primary_500}]),
]}
hitSlop={HITSLOP_30}>
Translate
)
}
function TranslationError({
clearTranslation,
message,
postText,
primaryLanguage,
}: {
clearTranslation: () => void
message: string
postText: string
primaryLanguage: string
}) {
const t = useTheme()
const {t: l} = useLingui()
const translate = useGoogleTranslate()
const handleFallback = () => {
void translate(postText, primaryLanguage)
}
return (
{message}
{
handleFallback()
})}
label={l`Try Google Translate`}
hoverStyle={[
native({opacity: 0.5}),
web([a.underline, {textDecorationColor: t.palette.primary_500}]),
]}
hitSlop={HITSLOP_30}>
Try Google Translate
)
}
function TranslationResult({
clearTranslation,
translate,
postText,
sourceLanguage,
translatedText,
}: {
clearTranslation: () => void
translate: TranslationFunction
postText: string
sourceLanguage: string | null
translatedText: string
}) {
const t = useTheme()
const langPrefs = useLanguagePrefs()
const {i18n, t: l} = useLingui()
const langName = sourceLanguage
? codeToLanguageName(sourceLanguage, i18n.locale)
: undefined
return (
{langName ? (
{langName}{' '}
{' '}
{codeToLanguageName(
langPrefs.primaryLanguage,
langPrefs.appLanguage,
)}
) : (
Translated
)}
{sourceLanguage != null && (
<>
{' '}
·{' '}
>
)}
{translatedText}
)
}
function TranslationLanguageSelect({
translate,
postText,
sourceLanguage,
}: {
translate: TranslationFunction
postText: string
sourceLanguage: string
}) {
const t = useTheme()
const ax = useAnalytics()
const {t: l} = useLingui()
const langPrefs = useLanguagePrefs()
const items = useMemo(
() =>
LANGUAGES.filter(
(lang, index, self) =>
!langPrefs.primaryLanguage.startsWith(lang.code2) && // Don't show the current language as it would be redundant
index === self.findIndex(t => t.code2 === lang.code2), // Remove dupes (which will happen due to multiple code3 values mapping to the same code2)
)
.sort((a, b) => {
// Prioritize sourceLanguage at the top
if (a.code2 === sourceLanguage) return -1
if (b.code2 === sourceLanguage) return 1
// Localized sort
return languageName(a, langPrefs.appLanguage).localeCompare(
languageName(b, langPrefs.appLanguage),
langPrefs.appLanguage,
)
})
.map(l => ({
label: languageName(l, langPrefs.appLanguage), // The viewer may not be familiar with the source language, so localize the name
value: l.code2,
})),
[langPrefs, sourceLanguage],
)
const handleChangeTranslationLanguage = (sourceLangCode: string) => {
ax.metric('translate:override', {
os: Platform.OS,
sourceLanguage: sourceLangCode,
targetLanguage: langPrefs.primaryLanguage,
})
void translate({
text: postText,
targetLangCode: langPrefs.primaryLanguage,
sourceLangCode,
})
}
return (
{({props}) => {
return (
)
}}
(
{label}
)}
items={items}
/>
)
}