about summary refs log tree commit diff
path: root/src/view/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com')
-rw-r--r--src/view/com/lists/ListCard.tsx2
-rw-r--r--src/view/com/lists/ListItems.tsx1
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx70
-rw-r--r--src/view/com/post/Post.tsx13
-rw-r--r--src/view/com/posts/FeedItem.tsx32
-rw-r--r--src/view/com/profile/ProfileHeader.tsx1
6 files changed, 85 insertions, 34 deletions
diff --git a/src/view/com/lists/ListCard.tsx b/src/view/com/lists/ListCard.tsx
index 2293dbeca..b70fa3773 100644
--- a/src/view/com/lists/ListCard.tsx
+++ b/src/view/com/lists/ListCard.tsx
@@ -96,7 +96,7 @@ export const ListCard = ({
       {descriptionRichText ? (
         <View style={styles.details}>
           <RichTextCom
-            style={pal.text}
+            style={[pal.text, s.flex1]}
             numberOfLines={20}
             richText={descriptionRichText}
           />
diff --git a/src/view/com/lists/ListItems.tsx b/src/view/com/lists/ListItems.tsx
index 47fa4a943..289ba000b 100644
--- a/src/view/com/lists/ListItems.tsx
+++ b/src/view/com/lists/ListItems.tsx
@@ -347,6 +347,7 @@ const styles = StyleSheet.create({
     borderTopWidth: 1,
   },
   headerDescription: {
+    flex: 1,
     marginTop: 8,
   },
   headerBtns: {
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 002795d77..692fac9e9 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -2,7 +2,7 @@ import React, {useCallback, useMemo} from 'react'
 import {observer} from 'mobx-react-lite'
 import {AccessibilityActionEvent, Linking, StyleSheet, View} from 'react-native'
 import Clipboard from '@react-native-clipboard/clipboard'
-import {AtUri} from '@atproto/api'
+import {AtUri, AppBskyFeedDefs} from '@atproto/api'
 import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
@@ -18,6 +18,7 @@ import {s} from 'lib/styles'
 import {ago, niceDate} from 'lib/strings/time'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
 import {pluralize} from 'lib/strings/helpers'
+import {getTranslatorLink, isPostInLanguage} from '../../../locale/helpers'
 import {useStores} from 'state/index'
 import {PostMeta} from '../util/PostMeta'
 import {PostEmbeds} from '../util/post-embeds'
@@ -65,6 +66,13 @@ export const PostThreadItem = observer(function PostThreadItem({
   }, [item.post.uri, item.post.author.handle])
   const repostsTitle = 'Reposts of this post'
 
+  const primaryLanguage = store.preferences.contentLanguages[0] || 'en'
+  const translatorUrl = getTranslatorLink(primaryLanguage, record?.text || '')
+  const needsTranslation = useMemo(
+    () => !isPostInLanguage(item.post, store.preferences.contentLanguages),
+    [item.post, store.preferences.contentLanguages],
+  )
+
   const onPressReply = React.useCallback(() => {
     store.shell.openComposer({
       replyTo: {
@@ -98,17 +106,9 @@ export const PostThreadItem = observer(function PostThreadItem({
     Toast.show('Copied to clipboard')
   }, [record])
 
-  const primaryLanguage = store.preferences.contentLanguages[0] || 'en'
-
   const onOpenTranslate = React.useCallback(() => {
-    Linking.openURL(
-      encodeURI(
-        `https://translate.google.com/?sl=auto&tl=${primaryLanguage}&text=${
-          record?.text || ''
-        }`,
-      ),
-    )
-  }, [record, primaryLanguage])
+    Linking.openURL(translatorUrl)
+  }, [translatorUrl])
 
   const onToggleThreadMute = React.useCallback(async () => {
     try {
@@ -276,6 +276,7 @@ export const PostThreadItem = observer(function PostThreadItem({
                   type="post-text-lg"
                   richText={item.richText}
                   lineHeight={1.3}
+                  style={s.flex1}
                 />
               </View>
             ) : undefined}
@@ -283,9 +284,11 @@ export const PostThreadItem = observer(function PostThreadItem({
               <PostEmbeds embed={item.post.embed} style={s.mb10} />
             </ImageHider>
           </ContentHider>
-          <View style={[s.mt2, s.mb10]}>
-            <Text style={pal.textLight}>{niceDate(item.post.indexedAt)}</Text>
-          </View>
+          <ExpandedPostDetails
+            post={item.post}
+            translatorUrl={translatorUrl}
+            needsTranslation={needsTranslation}
+          />
           {hasEngagement ? (
             <View style={[styles.expandedInfo, pal.border]}>
               {item.post.repostCount ? (
@@ -411,7 +414,7 @@ export const PostThreadItem = observer(function PostThreadItem({
                     <RichText
                       type="post-text"
                       richText={item.richText}
-                      style={pal.text}
+                      style={[pal.text, s.flex1]}
                       lineHeight={1.3}
                     />
                   </View>
@@ -419,6 +422,15 @@ export const PostThreadItem = observer(function PostThreadItem({
                 <ImageHider style={s.mb10} moderation={item.moderation.thread}>
                   <PostEmbeds embed={item.post.embed} style={s.mb10} />
                 </ImageHider>
+                {needsTranslation && (
+                  <View style={[pal.borderDark, styles.translateLink]}>
+                    <Link href={translatorUrl} title="Translate">
+                      <Text type="sm" style={pal.link}>
+                        Translate this post
+                      </Text>
+                    </Link>
+                  </View>
+                )}
               </ContentHider>
               <PostCtrls
                 itemUri={itemUri}
@@ -473,6 +485,31 @@ export const PostThreadItem = observer(function PostThreadItem({
   }
 })
 
+function ExpandedPostDetails({
+  post,
+  needsTranslation,
+  translatorUrl,
+}: {
+  post: AppBskyFeedDefs.PostView
+  needsTranslation: boolean
+  translatorUrl: string
+}) {
+  const pal = usePalette('default')
+  return (
+    <View style={[s.flexRow, s.mt2, s.mb10]}>
+      <Text style={pal.textLight}>{niceDate(post.indexedAt)}</Text>
+      {needsTranslation && (
+        <>
+          <Text style={pal.textLight}> • </Text>
+          <Link href={translatorUrl} title="Translate">
+            <Text style={pal.link}>Translate</Text>
+          </Link>
+        </>
+      )}
+    </View>
+  )
+}
+
 const styles = StyleSheet.create({
   outer: {
     borderTopWidth: 1,
@@ -540,6 +577,9 @@ const styles = StyleSheet.create({
     paddingHorizontal: 0,
     paddingBottom: 10,
   },
+  translateLink: {
+    marginBottom: 6,
+  },
   contentHider: {
     marginTop: 4,
   },
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index 3eac7ee7b..fac27b842 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -30,6 +30,7 @@ import {UserAvatar} from '../util/UserAvatar'
 import {useStores} from 'state/index'
 import {s, colors} from 'lib/styles'
 import {usePalette} from 'lib/hooks/usePalette'
+import {getTranslatorLink} from '../../../locale/helpers'
 
 export const Post = observer(function Post({
   uri,
@@ -167,16 +168,11 @@ const PostLoaded = observer(
     }, [record])
 
     const primaryLanguage = store.preferences.contentLanguages[0] || 'en'
+    const translatorUrl = getTranslatorLink(primaryLanguage, record?.text || '')
 
     const onOpenTranslate = React.useCallback(() => {
-      Linking.openURL(
-        encodeURI(
-          `https://translate.google.com/?sl=auto&tl=${primaryLanguage}&text=${
-            record?.text || ''
-          }`,
-        ),
-      )
-    }, [record, primaryLanguage])
+      Linking.openURL(translatorUrl)
+    }, [translatorUrl])
 
     const onToggleThreadMute = React.useCallback(async () => {
       try {
@@ -299,6 +295,7 @@ const PostLoaded = observer(
                     type="post-text"
                     richText={item.richText}
                     lineHeight={1.3}
+                    style={s.flex1}
                   />
                 </View>
               ) : undefined}
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index d83e64073..7354c8a67 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -27,6 +27,7 @@ import {useStores} from 'state/index'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useAnalytics} from 'lib/analytics/analytics'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
+import {getTranslatorLink, isPostInLanguage} from '../../../locale/helpers'
 
 export const FeedItem = observer(function ({
   item,
@@ -62,6 +63,12 @@ export const FeedItem = observer(function ({
     const urip = new AtUri(record.reply.parent?.uri || record.reply.root.uri)
     return urip.hostname
   }, [record?.reply])
+  const primaryLanguage = store.preferences.contentLanguages[0] || 'en'
+  const translatorUrl = getTranslatorLink(primaryLanguage, record?.text || '')
+  const needsTranslation = useMemo(
+    () => !isPostInLanguage(item.post, store.preferences.contentLanguages),
+    [item.post, store.preferences.contentLanguages],
+  )
 
   const onPressReply = React.useCallback(() => {
     track('FeedItem:PostReply')
@@ -98,17 +105,9 @@ export const FeedItem = observer(function ({
     Toast.show('Copied to clipboard')
   }, [record])
 
-  const primaryLanguage = store.preferences.contentLanguages[0] || 'en'
-
   const onOpenTranslate = React.useCallback(() => {
-    Linking.openURL(
-      encodeURI(
-        `https://translate.google.com/?sl=auto&tl=${primaryLanguage}&text=${
-          record?.text || ''
-        }`,
-      ),
-    )
-  }, [record, primaryLanguage])
+    Linking.openURL(translatorUrl)
+  }, [translatorUrl])
 
   const onToggleThreadMute = React.useCallback(async () => {
     track('FeedItem:ThreadMute')
@@ -301,12 +300,22 @@ export const FeedItem = observer(function ({
                   type="post-text"
                   richText={item.richText}
                   lineHeight={1.3}
+                  style={s.flex1}
                 />
               </View>
             ) : undefined}
             <ImageHider moderation={item.moderation.list} style={styles.embed}>
               <PostEmbeds embed={item.post.embed} style={styles.embed} />
             </ImageHider>
+            {needsTranslation && (
+              <View style={[pal.borderDark, styles.translateLink]}>
+                <Link href={translatorUrl} title="Translate">
+                  <Text type="sm" style={pal.link}>
+                    Translate this post
+                  </Text>
+                </Link>
+              </View>
+            )}
           </ContentHider>
           <PostCtrls
             style={styles.ctrls}
@@ -402,6 +411,9 @@ const styles = StyleSheet.create({
   embed: {
     marginBottom: 6,
   },
+  translateLink: {
+    marginBottom: 6,
+  },
   ctrls: {
     marginTop: 4,
   },
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index 0ad6b2eb7..b142e7616 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -609,6 +609,7 @@ const styles = StyleSheet.create({
   },
 
   description: {
+    flex: 1,
     marginBottom: 8,
   },