mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Fixed #476
This commit is contained in:
@ -5,131 +5,119 @@ import { useTranslateQuery } from '@utils/queryHooks/translate'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import * as Localization from 'expo-localization'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Pressable } from 'react-native'
|
||||
import { Circle } from 'react-native-animated-spinkit'
|
||||
import detectLanguage from 'react-native-language-detection'
|
||||
import StatusContext from './Context'
|
||||
|
||||
export interface Props {
|
||||
highlighted: boolean
|
||||
status: Pick<Mastodon.Status, 'language' | 'spoiler_text' | 'content' | 'emojis'>
|
||||
}
|
||||
const TimelineTranslate = () => {
|
||||
const { status, highlighted } = useContext(StatusContext)
|
||||
if (!status || !highlighted) return null
|
||||
|
||||
const TimelineTranslate = React.memo(
|
||||
({ highlighted, status }: Props) => {
|
||||
if (!highlighted) {
|
||||
return null
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const { colors } = useTheme()
|
||||
|
||||
const text = status.spoiler_text ? [status.spoiler_text, status.content] : [status.content]
|
||||
|
||||
for (const i in text) {
|
||||
for (const emoji of status.emojis) {
|
||||
text[i] = text[i].replaceAll(`:${emoji.shortcode}:`, ' ')
|
||||
}
|
||||
text[i] = text[i]
|
||||
.replace(/(<([^>]+)>)/gi, ' ')
|
||||
.replace(/@.*? /gi, ' ')
|
||||
.replace(/#.*? /gi, ' ')
|
||||
.replace(/http(s):\/\/.*? /gi, ' ')
|
||||
}
|
||||
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const { colors } = useTheme()
|
||||
|
||||
const text = status.spoiler_text ? [status.spoiler_text, status.content] : [status.content]
|
||||
|
||||
for (const i in text) {
|
||||
for (const emoji of status.emojis) {
|
||||
text[i] = text[i].replaceAll(`:${emoji.shortcode}:`, ' ')
|
||||
}
|
||||
text[i] = text[i]
|
||||
.replace(/(<([^>]+)>)/gi, ' ')
|
||||
.replace(/@.*? /gi, ' ')
|
||||
.replace(/#.*? /gi, ' ')
|
||||
.replace(/http(s):\/\/.*? /gi, ' ')
|
||||
const [detectedLanguage, setDetectedLanguage] = useState<string>('')
|
||||
useEffect(() => {
|
||||
const detect = async () => {
|
||||
const result = await detectLanguage(text.join(`\n\n`)).catch(() => {
|
||||
// No need to log language detection failure
|
||||
})
|
||||
result?.detected && setDetectedLanguage(result.detected.slice(0, 2))
|
||||
}
|
||||
detect()
|
||||
}, [])
|
||||
|
||||
const [detectedLanguage, setDetectedLanguage] = useState<string>('')
|
||||
useEffect(() => {
|
||||
const detect = async () => {
|
||||
const result = await detectLanguage(text.join(`\n\n`)).catch(() => {
|
||||
// No need to log language detection failure
|
||||
})
|
||||
result?.detected && setDetectedLanguage(result.detected.slice(0, 2))
|
||||
}
|
||||
detect()
|
||||
}, [])
|
||||
const settingsLanguage = getLanguage()
|
||||
const targetLanguage = settingsLanguage?.startsWith('en')
|
||||
? Localization.locale || settingsLanguage || 'en'
|
||||
: settingsLanguage || Localization.locale || 'en'
|
||||
|
||||
const settingsLanguage = getLanguage()
|
||||
const targetLanguage = settingsLanguage?.startsWith('en')
|
||||
? Localization.locale || settingsLanguage || 'en'
|
||||
: settingsLanguage || Localization.locale || 'en'
|
||||
const [enabled, setEnabled] = useState(false)
|
||||
const { refetch, data, isLoading, isSuccess, isError } = useTranslateQuery({
|
||||
source: detectedLanguage,
|
||||
target: targetLanguage,
|
||||
text,
|
||||
options: { enabled }
|
||||
})
|
||||
|
||||
const [enabled, setEnabled] = useState(false)
|
||||
const { refetch, data, isLoading, isSuccess, isError } = useTranslateQuery({
|
||||
source: detectedLanguage,
|
||||
target: targetLanguage,
|
||||
text,
|
||||
options: { enabled }
|
||||
})
|
||||
if (!detectedLanguage) {
|
||||
return null
|
||||
}
|
||||
if (Localization.locale.slice(0, 2).includes(detectedLanguage)) {
|
||||
return null
|
||||
}
|
||||
if (settingsLanguage?.slice(0, 2).includes(detectedLanguage)) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!detectedLanguage) {
|
||||
return null
|
||||
}
|
||||
if (Localization.locale.slice(0, 2).includes(detectedLanguage)) {
|
||||
return null
|
||||
}
|
||||
if (settingsLanguage?.slice(0, 2).includes(detectedLanguage)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Pressable
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: StyleConstants.Spacing.S,
|
||||
paddingBottom: isSuccess ? 0 : undefined
|
||||
}}
|
||||
onPress={() => {
|
||||
if (enabled) {
|
||||
if (!isSuccess) {
|
||||
refetch()
|
||||
}
|
||||
} else {
|
||||
setEnabled(true)
|
||||
return (
|
||||
<>
|
||||
<Pressable
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: StyleConstants.Spacing.S,
|
||||
paddingBottom: isSuccess ? 0 : undefined
|
||||
}}
|
||||
onPress={() => {
|
||||
if (enabled) {
|
||||
if (!isSuccess) {
|
||||
refetch()
|
||||
}
|
||||
} else {
|
||||
setEnabled(true)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CustomText
|
||||
fontStyle='M'
|
||||
style={{
|
||||
color: isLoading || isSuccess ? colors.secondary : isError ? colors.red : colors.blue
|
||||
}}
|
||||
>
|
||||
<CustomText
|
||||
fontStyle='M'
|
||||
style={{
|
||||
color: isLoading || isSuccess ? colors.secondary : isError ? colors.red : colors.blue
|
||||
}}
|
||||
>
|
||||
{isError
|
||||
? t('shared.translate.failed')
|
||||
: isSuccess
|
||||
? typeof data?.error === 'string'
|
||||
? t(`shared.translate.${data.error}`)
|
||||
: t('shared.translate.succeed', {
|
||||
provider: data?.provider,
|
||||
source: data?.sourceLanguage
|
||||
})
|
||||
: t('shared.translate.default')}
|
||||
</CustomText>
|
||||
<CustomText>
|
||||
{__DEV__ ? ` Source: ${detectedLanguage}; Target: ${targetLanguage}` : undefined}
|
||||
</CustomText>
|
||||
{isLoading ? (
|
||||
<Circle
|
||||
size={StyleConstants.Font.Size.M}
|
||||
color={colors.disabled}
|
||||
style={{ marginLeft: StyleConstants.Spacing.S }}
|
||||
/>
|
||||
) : null}
|
||||
</Pressable>
|
||||
{data && data.error === undefined
|
||||
? data.text.map((d, i) => (
|
||||
<ParseHTML key={i} content={d} size={'M'} numberOfLines={999} />
|
||||
))
|
||||
: null}
|
||||
</>
|
||||
)
|
||||
},
|
||||
(prev, next) =>
|
||||
prev.status.content === next.status.content &&
|
||||
prev.status.spoiler_text === next.status.spoiler_text
|
||||
)
|
||||
{isError
|
||||
? t('shared.translate.failed')
|
||||
: isSuccess
|
||||
? typeof data?.error === 'string'
|
||||
? t(`shared.translate.${data.error}`)
|
||||
: t('shared.translate.succeed', {
|
||||
provider: data?.provider,
|
||||
source: data?.sourceLanguage
|
||||
})
|
||||
: t('shared.translate.default')}
|
||||
</CustomText>
|
||||
<CustomText>
|
||||
{__DEV__ ? ` Source: ${detectedLanguage}; Target: ${targetLanguage}` : undefined}
|
||||
</CustomText>
|
||||
{isLoading ? (
|
||||
<Circle
|
||||
size={StyleConstants.Font.Size.M}
|
||||
color={colors.disabled}
|
||||
style={{ marginLeft: StyleConstants.Spacing.S }}
|
||||
/>
|
||||
) : null}
|
||||
</Pressable>
|
||||
{data && data.error === undefined
|
||||
? data.text.map((d, i) => <ParseHTML key={i} content={d} size={'M'} numberOfLines={999} />)
|
||||
: null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TimelineTranslate
|
||||
|
Reference in New Issue
Block a user