about summary refs log tree commit diff
path: root/src/view/com/post-thread
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/post-thread')
-rw-r--r--src/view/com/post-thread/PostThread.tsx95
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx23
2 files changed, 87 insertions, 31 deletions
diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx
index 6cd1f3551..072ef7e33 100644
--- a/src/view/com/post-thread/PostThread.tsx
+++ b/src/view/com/post-thread/PostThread.tsx
@@ -36,11 +36,13 @@ import {Trans, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {
   UsePreferencesQueryResponse,
+  useModerationOpts,
   usePreferencesQuery,
 } from '#/state/queries/preferences'
 import {useSession} from '#/state/session'
-import {isNative} from '#/platform/detection'
+import {isAndroid, isNative} from '#/platform/detection'
 import {logger} from '#/logger'
+import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
 
 const MAINTAIN_VISIBLE_CONTENT_POSITION = {minIndexForVisible: 2}
 
@@ -79,14 +81,30 @@ export function PostThread({
     data: thread,
   } = usePostThreadQuery(uri)
   const {data: preferences} = usePreferencesQuery()
+
   const rootPost = thread?.type === 'post' ? thread.post : undefined
   const rootPostRecord = thread?.type === 'post' ? thread.record : undefined
 
+  const moderationOpts = useModerationOpts()
+  const isNoPwi = React.useMemo(() => {
+    const mod =
+      rootPost && moderationOpts
+        ? moderatePost(rootPost, moderationOpts)
+        : undefined
+
+    const cause = mod?.content.cause
+
+    return cause
+      ? cause.type === 'label' && cause.labelDef.id === '!no-unauthenticated'
+      : false
+  }, [rootPost, moderationOpts])
+
   useSetTitle(
-    rootPost &&
-      `${sanitizeDisplayName(
-        rootPost.author.displayName || `@${rootPost.author.handle}`,
-      )}: "${rootPostRecord?.text}"`,
+    rootPost && !isNoPwi
+      ? `${sanitizeDisplayName(
+          rootPost.author.displayName || `@${rootPost.author.handle}`,
+        )}: "${rootPostRecord!.text}"`
+      : '',
   )
   useEffect(() => {
     if (rootPost) {
@@ -139,7 +157,7 @@ function PostThreadLoaded({
   const {hasSession} = useSession()
   const {_} = useLingui()
   const pal = usePalette('default')
-  const {isTablet, isDesktop} = useWebMediaQueries()
+  const {isTablet, isDesktop, isTabletOrMobile} = useWebMediaQueries()
   const ref = useRef<ListMethods>(null)
   const highlightedPostRef = useRef<View | null>(null)
   const needsScrollAdjustment = useRef<boolean>(
@@ -157,7 +175,11 @@ function PostThreadLoaded({
   const posts = React.useMemo(() => {
     let arr = [TOP_COMPONENT].concat(
       Array.from(
-        flattenThreadSkeleton(sortThread(thread, threadViewPrefs), hasSession),
+        flattenThreadSkeleton(
+          sortThread(thread, threadViewPrefs),
+          hasSession,
+          treeView,
+        ),
       ),
     )
     if (arr.length > maxVisible) {
@@ -167,7 +189,7 @@ function PostThreadLoaded({
       arr.push(BOTTOM_COMPONENT)
     }
     return arr
-  }, [thread, maxVisible, threadViewPrefs, hasSession])
+  }, [thread, treeView, maxVisible, threadViewPrefs, hasSession])
 
   /**
    * NOTE
@@ -197,17 +219,35 @@ function PostThreadLoaded({
 
     // wait for loading to finish
     if (thread.type === 'post' && !!thread.parent) {
-      highlightedPostRef.current?.measure(
-        (_x, _y, _width, _height, _pageX, pageY) => {
-          ref.current?.scrollToOffset({
-            animated: false,
-            offset: pageY - (isDesktop ? 0 : 50),
-          })
-        },
-      )
+      function onMeasure(pageY: number) {
+        let spinnerHeight = 0
+        if (isDesktop) {
+          spinnerHeight = 40
+        } else if (isTabletOrMobile) {
+          spinnerHeight = 82
+        }
+        ref.current?.scrollToOffset({
+          animated: false,
+          offset: pageY - spinnerHeight,
+        })
+      }
+      if (isNative) {
+        highlightedPostRef.current?.measure(
+          (_x, _y, _width, _height, _pageX, pageY) => {
+            onMeasure(pageY)
+          },
+        )
+      } else {
+        // Measure synchronously to avoid a layout jump.
+        const domNode = highlightedPostRef.current
+        if (domNode) {
+          const pageY = (domNode as any as Element).getBoundingClientRect().top
+          onMeasure(pageY)
+        }
+      }
       needsScrollAdjustment.current = false
     }
-  }, [thread, isDesktop])
+  }, [thread, isDesktop, isTabletOrMobile])
 
   const onPTR = React.useCallback(async () => {
     setIsPTRing(true)
@@ -222,7 +262,11 @@ function PostThreadLoaded({
   const renderItem = React.useCallback(
     ({item, index}: {item: YieldedItem; index: number}) => {
       if (item === TOP_COMPONENT) {
-        return isTablet ? <ViewHeader title={_(msg`Post`)} /> : null
+        return isTablet ? (
+          <ViewHeader
+            title={_(msg({message: `Post`, context: 'description'}))}
+          />
+        ) : null
       } else if (item === PARENT_SPINNER) {
         return (
           <View style={styles.parentSpinner}>
@@ -276,8 +320,10 @@ function PostThreadLoaded({
         // -prf
         return (
           <View
+            // @ts-ignore web-only
             style={{
-              height: 400,
+              // Leave enough space below that the scroll doesn't jump
+              height: isNative ? 400 : '100vh',
               borderTopWidth: 1,
               borderColor: pal.colors.border,
             }}
@@ -354,6 +400,7 @@ function PostThreadLoaded({
       style={s.hContentRegion}
       // @ts-ignore our .web version only -prf
       desktopFixedHeight
+      removeClippedSubviews={isAndroid ? false : undefined}
     />
   )
 }
@@ -393,7 +440,7 @@ function PostThreadBlocked() {
               style={[pal.link as FontAwesomeIconStyle, s.mr5]}
               size={14}
             />
-            Back
+            <Trans context="action">Back</Trans>
           </Text>
         </TouchableOpacity>
       </View>
@@ -464,10 +511,11 @@ function isThreadPost(v: unknown): v is ThreadPost {
 function* flattenThreadSkeleton(
   node: ThreadNode,
   hasSession: boolean,
+  treeView: boolean,
 ): Generator<YieldedItem, void> {
   if (node.type === 'post') {
     if (node.parent) {
-      yield* flattenThreadSkeleton(node.parent, hasSession)
+      yield* flattenThreadSkeleton(node.parent, hasSession, treeView)
     } else if (node.ctx.isParentLoading) {
       yield PARENT_SPINNER
     }
@@ -480,7 +528,10 @@ function* flattenThreadSkeleton(
     }
     if (node.replies?.length) {
       for (const reply of node.replies) {
-        yield* flattenThreadSkeleton(reply, hasSession)
+        yield* flattenThreadSkeleton(reply, hasSession, treeView)
+        if (!treeView && !node.ctx.isHighlightedPost) {
+          break
+        }
       }
     } else if (node.ctx.isChildLoading) {
       yield CHILD_SPINNER
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 986fd70b2..a27ee0a58 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -158,6 +158,7 @@ let PostThreadItemLoaded = ({
   onPostReply: () => void
 }): React.ReactNode => {
   const pal = usePalette('default')
+  const {_} = useLingui()
   const langPrefs = useLanguagePrefs()
   const {openComposer} = useComposerControls()
   const {currentAccount} = useSession()
@@ -172,7 +173,7 @@ let PostThreadItemLoaded = ({
     const urip = new AtUri(post.uri)
     return makeProfileLink(post.author, 'post', urip.rkey)
   }, [post.uri, post.author])
-  const itemTitle = `Post by ${post.author.handle}`
+  const itemTitle = _(msg`Post by ${post.author.handle}`)
   const authorHref = makeProfileLink(post.author)
   const authorTitle = post.author.handle
   const isAuthorMuted = post.author.viewer?.muted
@@ -180,12 +181,12 @@ let PostThreadItemLoaded = ({
     const urip = new AtUri(post.uri)
     return makeProfileLink(post.author, 'post', urip.rkey, 'liked-by')
   }, [post.uri, post.author])
-  const likesTitle = 'Likes on this post'
+  const likesTitle = _(msg`Likes on this post`)
   const repostsHref = React.useMemo(() => {
     const urip = new AtUri(post.uri)
     return makeProfileLink(post.author, 'post', urip.rkey, 'reposted-by')
   }, [post.uri, post.author])
-  const repostsTitle = 'Reposts of this post'
+  const repostsTitle = _(msg`Reposts of this post`)
   const isModeratedPost =
     moderation.decisions.post.cause?.type === 'label' &&
     moderation.decisions.post.cause.label.src !== currentAccount?.did
@@ -214,6 +215,7 @@ let PostThreadItemLoaded = ({
           displayName: post.author.displayName,
           avatar: post.author.avatar,
         },
+        embed: post.embed,
       },
       onPost: onPostReply,
     })
@@ -224,7 +226,7 @@ let PostThreadItemLoaded = ({
   }, [setLimitLines])
 
   if (!record) {
-    return <ErrorMessage message="Invalid or unsupported post record" />
+    return <ErrorMessage message={_(msg`Invalid or unsupported post record`)} />
   }
 
   if (isHighlightedPost) {
@@ -246,10 +248,9 @@ let PostThreadItemLoaded = ({
           </View>
         )}
 
-        <Link
+        <View
           testID={`postThreadItem-by-${post.author.handle}`}
           style={[styles.outer, styles.outerHighlighted, pal.border, pal.view]}
-          noFeedback
           accessible={false}>
           <PostSandboxWarning />
           <View style={styles.layout}>
@@ -334,6 +335,7 @@ let PostThreadItemLoaded = ({
               postCid={post.cid}
               postUri={post.uri}
               record={record}
+              richText={richText}
               showAppealLabelItem={
                 post.author.did === currentAccount?.did && isModeratedPost
               }
@@ -367,6 +369,7 @@ let PostThreadItemLoaded = ({
                     richText={richText}
                     lineHeight={1.3}
                     style={s.flex1}
+                    selectable
                   />
                 </View>
               ) : undefined}
@@ -437,11 +440,12 @@ let PostThreadItemLoaded = ({
                 big
                 post={post}
                 record={record}
+                richText={richText}
                 onPressReply={onPressReply}
               />
             </View>
           </View>
-        </Link>
+        </View>
         <WhoCanReply post={post} />
       </>
     )
@@ -562,7 +566,7 @@ let PostThreadItemLoaded = ({
                 ) : undefined}
                 {limitLines ? (
                   <TextLink
-                    text="Show More"
+                    text={_(msg`Show More`)}
                     style={pal.link}
                     onPress={onPressShowMore}
                     href="#"
@@ -585,6 +589,7 @@ let PostThreadItemLoaded = ({
                 <PostCtrls
                   post={post}
                   record={record}
+                  richText={richText}
                   onPressReply={onPressReply}
                 />
               </View>
@@ -701,7 +706,7 @@ function ExpandedPostDetails({
       <Text style={pal.textLight}>{niceDate(post.indexedAt)}</Text>
       {needsTranslation && (
         <>
-          <Text style={[pal.textLight, s.ml5, s.mr5]}>•</Text>
+          <Text style={pal.textLight}> &middot; </Text>
           <Link href={translatorUrl} title={_(msg`Translate`)}>
             <Text style={pal.link}>
               <Trans>Translate</Trans>