add on-device translation to post feed item
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
import {useMemo} from 'react'
|
import {useMemo} from 'react'
|
||||||
import {Platform, View} from 'react-native'
|
import {Platform, View} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {msg} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
import {Trans} from '@lingui/react/macro'
|
|
||||||
|
|
||||||
|
import {HITSLOP_30} from '#/lib/constants'
|
||||||
import {codeToLanguageName, languageName} from '#/locale/helpers'
|
import {codeToLanguageName, languageName} from '#/locale/helpers'
|
||||||
import {LANGUAGES} from '#/locale/languages'
|
import {LANGUAGES} from '#/locale/languages'
|
||||||
import {useLanguagePrefs} from '#/state/preferences'
|
import {useLanguagePrefs} from '#/state/preferences'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, native, useTheme} from '#/alf'
|
||||||
|
import {Button} from '#/components/Button'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import * as Select from '#/components/Select'
|
import * as Select from '#/components/Select'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
@@ -16,24 +17,43 @@ import {useTranslateOnDevice} from '#/translation'
|
|||||||
|
|
||||||
export function TranslatedPost({
|
export function TranslatedPost({
|
||||||
postText,
|
postText,
|
||||||
hideLoading = false,
|
standalone = false,
|
||||||
}: {
|
}: {
|
||||||
postText: string
|
postText: string
|
||||||
hideLoading: boolean
|
/**
|
||||||
|
* Hides the loading state, and adds its own "hide translation button"
|
||||||
|
*/
|
||||||
|
standalone?: boolean
|
||||||
}) {
|
}) {
|
||||||
const {translationState} = useTranslateOnDevice()
|
const {translationState, clearTranslation} = useTranslateOnDevice()
|
||||||
|
const {t: l} = useLingui()
|
||||||
|
const t = useTheme()
|
||||||
|
|
||||||
if (translationState.status === 'loading' && !hideLoading) {
|
if (translationState.status === 'loading' && standalone) {
|
||||||
return <TranslationLoading />
|
return <TranslationLoading />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (translationState.status === 'success') {
|
if (translationState.status === 'success') {
|
||||||
return (
|
return (
|
||||||
<TranslationResult
|
<>
|
||||||
postText={postText}
|
<TranslationResult
|
||||||
sourceLanguage={translationState.sourceLanguage}
|
postText={postText}
|
||||||
translatedText={translationState.translatedText}
|
sourceLanguage={translationState.sourceLanguage}
|
||||||
/>
|
translatedText={translationState.translatedText}
|
||||||
|
/>
|
||||||
|
{standalone && (
|
||||||
|
<Button
|
||||||
|
label={l`Hide translation`}
|
||||||
|
onPress={clearTranslation}
|
||||||
|
style={[a.self_start, a.mt_sm]}
|
||||||
|
hoverStyle={native({opacity: 0.5})}
|
||||||
|
hitSlop={HITSLOP_30}>
|
||||||
|
<Text style={[a.text_sm, {color: t.palette.primary_500}]}>
|
||||||
|
<Trans>Hide translation</Trans>
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +65,7 @@ function TranslationLoading() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_sm, a.py_xs]}>
|
<View style={[a.flex_row, a.align_center, a.gap_sm, a.py_xs]}>
|
||||||
<Loader size="sm" />
|
<Loader size="xs" />
|
||||||
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
|
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
|
||||||
<Trans>Translating…</Trans>
|
<Trans>Translating…</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -420,7 +420,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
|
|||||||
shouldProxyLinks={true}
|
shouldProxyLinks={true}
|
||||||
/>
|
/>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
<TranslatedPost postText={record.text} hideLoading />
|
<TranslatedPost postText={record.text} />
|
||||||
<TranslateLink post={item.value.post} />
|
<TranslateLink post={item.value.post} />
|
||||||
{post.embed && (
|
{post.embed && (
|
||||||
<View style={[a.py_xs]}>
|
<View style={[a.py_xs]}>
|
||||||
@@ -617,7 +617,7 @@ function TranslateLink({
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
needsTranslation && (
|
(needsTranslation || translationState.status !== 'idle') && (
|
||||||
<View style={[a.gap_md, a.pt_md, a.align_start]}>
|
<View style={[a.gap_md, a.pt_md, a.align_start]}>
|
||||||
{translationState.status === 'loading' ? (
|
{translationState.status === 'loading' ? (
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_xs]}>
|
<View style={[a.flex_row, a.align_center, a.gap_xs]}>
|
||||||
|
|||||||
@@ -42,12 +42,14 @@ import {Embed} from '#/components/Post/Embed'
|
|||||||
import {PostEmbedViewContext} from '#/components/Post/Embed/types'
|
import {PostEmbedViewContext} from '#/components/Post/Embed/types'
|
||||||
import {PostRepliedTo} from '#/components/Post/PostRepliedTo'
|
import {PostRepliedTo} from '#/components/Post/PostRepliedTo'
|
||||||
import {ShowMoreTextButton} from '#/components/Post/ShowMoreTextButton'
|
import {ShowMoreTextButton} from '#/components/Post/ShowMoreTextButton'
|
||||||
|
import {TranslatedPost} from '#/components/Post/Translated'
|
||||||
import {PostControls} from '#/components/PostControls'
|
import {PostControls} from '#/components/PostControls'
|
||||||
import {DiscoverDebug} from '#/components/PostControls/DiscoverDebug'
|
import {DiscoverDebug} from '#/components/PostControls/DiscoverDebug'
|
||||||
import {RichText} from '#/components/RichText'
|
import {RichText} from '#/components/RichText'
|
||||||
import {SubtleHover} from '#/components/SubtleHover'
|
import {SubtleHover} from '#/components/SubtleHover'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
import {useActorStatus} from '#/features/liveNow'
|
import {useActorStatus} from '#/features/liveNow'
|
||||||
|
import {Provider as OnDeviceTranslationProvider} from '#/translation'
|
||||||
import * as bsky from '#/types/bsky'
|
import * as bsky from '#/types/bsky'
|
||||||
import {PostFeedReason} from './PostFeedReason'
|
import {PostFeedReason} from './PostFeedReason'
|
||||||
|
|
||||||
@@ -108,27 +110,29 @@ export function PostFeedItem({
|
|||||||
}
|
}
|
||||||
if (richText && moderation) {
|
if (richText && moderation) {
|
||||||
return (
|
return (
|
||||||
<FeedItemInner
|
<OnDeviceTranslationProvider>
|
||||||
// Safeguard from clobbering per-post state below:
|
<FeedItemInner
|
||||||
key={postShadowed.uri}
|
// Safeguard from clobbering per-post state below:
|
||||||
post={postShadowed}
|
key={postShadowed.uri}
|
||||||
record={record}
|
post={postShadowed}
|
||||||
reason={reason}
|
record={record}
|
||||||
feedContext={feedContext}
|
reason={reason}
|
||||||
reqId={reqId}
|
feedContext={feedContext}
|
||||||
richText={richText}
|
reqId={reqId}
|
||||||
parentAuthor={parentAuthor}
|
richText={richText}
|
||||||
showReplyTo={showReplyTo}
|
parentAuthor={parentAuthor}
|
||||||
moderation={moderation}
|
showReplyTo={showReplyTo}
|
||||||
isThreadChild={isThreadChild}
|
moderation={moderation}
|
||||||
isThreadLastChild={isThreadLastChild}
|
isThreadChild={isThreadChild}
|
||||||
isThreadParent={isThreadParent}
|
isThreadLastChild={isThreadLastChild}
|
||||||
hideTopBorder={hideTopBorder}
|
isThreadParent={isThreadParent}
|
||||||
isParentBlocked={isParentBlocked}
|
hideTopBorder={hideTopBorder}
|
||||||
isParentNotFound={isParentNotFound}
|
isParentBlocked={isParentBlocked}
|
||||||
rootPost={rootPost}
|
isParentNotFound={isParentNotFound}
|
||||||
onShowLess={onShowLess}
|
rootPost={rootPost}
|
||||||
/>
|
onShowLess={onShowLess}
|
||||||
|
/>
|
||||||
|
</OnDeviceTranslationProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -479,6 +483,7 @@ let PostContent = ({
|
|||||||
{limitLines && (
|
{limitLines && (
|
||||||
<ShowMoreTextButton style={[a.text_md]} onPress={onPressShowMore} />
|
<ShowMoreTextButton style={[a.text_md]} onPress={onPressShowMore} />
|
||||||
)}
|
)}
|
||||||
|
<TranslatedPost postText={richText.text} standalone />
|
||||||
</View>
|
</View>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
{postEmbed ? (
|
{postEmbed ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user