about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2024-02-13 22:12:33 +0000
committerGitHub <noreply@github.com>2024-02-13 22:12:33 +0000
commit08525b52c3b5ea7128ad87d0231c5a852b5495a6 (patch)
treeb6ffc99fdcc83e2c47fe6fa179abf351e8ea4a46 /src
parent2f6d7606b3ca23eb27cd5ccc7b9ecb60abb45706 (diff)
downloadvoidsky-08525b52c3b5ea7128ad87d0231c5a852b5495a6.tar.zst
Move visual display logic out of PostThread generators (#2862)
* Split skeleton gen into replies and parents

Co-authored-by: Hailey <me@haileyok.com>

* Move REPLY_PROMPT out of the generator

* Move the rest of visual logic out of gen

---------

Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'src')
-rw-r--r--src/view/com/post-thread/PostThread.tsx83
1 files changed, 55 insertions, 28 deletions
diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx
index 50e2c69da..6c026d6cb 100644
--- a/src/view/com/post-thread/PostThread.tsx
+++ b/src/view/com/post-thread/PostThread.tsx
@@ -55,7 +55,15 @@ const CHILD_SPINNER = {_reactKey: '__child_spinner__'}
 const LOAD_MORE = {_reactKey: '__load_more__'}
 const BOTTOM_COMPONENT = {_reactKey: '__bottom_component__'}
 
-type YieldedItem = ThreadPost | typeof TOP_COMPONENT | typeof REPLY_PROMPT
+type YieldedItem = ThreadPost | ThreadBlocked | ThreadNotFound
+type RowItem =
+  | YieldedItem
+  // TODO: TS doesn't actually enforce it's one of these, it only enforces matching shape.
+  | typeof TOP_COMPONENT
+  | typeof REPLY_PROMPT
+  | typeof CHILD_SPINNER
+  | typeof LOAD_MORE
+  | typeof BOTTOM_COMPONENT
 
 export function PostThread({
   uri,
@@ -160,19 +168,31 @@ function PostThreadLoaded({
 
   // construct content
   const posts = React.useMemo(() => {
-    let arr = Array.from(
-      flattenThreadSkeleton(
-        sortThread(thread, threadViewPrefs),
-        hasSession,
-        treeView,
-      ),
-    )
+    const root = sortThread(thread, threadViewPrefs)
+    let arr: RowItem[] = []
+    if (root.type === 'post') {
+      if (!root.ctx.isParentLoading) {
+        arr.push(TOP_COMPONENT)
+        for (const parent of flattenThreadParents(root, hasSession)) {
+          arr.push(parent)
+        }
+      }
+      arr.push(root)
+      if (!root.post.viewer?.replyDisabled) {
+        arr.push(REPLY_PROMPT)
+      }
+      if (root.ctx.isChildLoading) {
+        arr.push(CHILD_SPINNER)
+      } else {
+        for (const reply of flattenThreadReplies(root, hasSession, treeView)) {
+          arr.push(reply)
+        }
+        arr.push(BOTTOM_COMPONENT)
+      }
+    }
     if (arr.length > maxVisible) {
       arr = arr.slice(0, maxVisible).concat([LOAD_MORE])
     }
-    if (arr.indexOf(CHILD_SPINNER) === -1) {
-      arr.push(BOTTOM_COMPONENT)
-    }
     return arr
   }, [thread, treeView, maxVisible, threadViewPrefs, hasSession])
 
@@ -239,7 +259,7 @@ function PostThreadLoaded({
   }, [setIsPTRing, onRefresh])
 
   const renderItem = React.useCallback(
-    ({item, index}: {item: YieldedItem; index: number}) => {
+    ({item, index}: {item: RowItem; index: number}) => {
       if (item === TOP_COMPONENT) {
         return isTabletOrMobile ? (
           <ViewHeader
@@ -489,36 +509,43 @@ function isThreadBlocked(v: unknown): v is ThreadBlocked {
   return !!v && typeof v === 'object' && 'type' in v && v.type === 'blocked'
 }
 
-function* flattenThreadSkeleton(
+function* flattenThreadParents(
   node: ThreadNode,
   hasSession: boolean,
-  treeView: boolean,
-  isTraversingReplies: boolean = false,
 ): Generator<YieldedItem, void> {
   if (node.type === 'post') {
-    if (!node.ctx.isParentLoading) {
-      if (node.parent) {
-        yield* flattenThreadSkeleton(node.parent, hasSession, treeView, false)
-      } else if (!isTraversingReplies) {
-        yield TOP_COMPONENT
-      }
+    if (node.parent) {
+      yield* flattenThreadParents(node.parent, hasSession)
     }
-    if (!hasSession && node.ctx.depth > 0 && hasPwiOptOut(node)) {
-      return
+    if (!node.ctx.isHighlightedPost) {
+      yield node
     }
+  } else if (node.type === 'not-found') {
     yield node
-    if (node.ctx.isHighlightedPost && !node.post.viewer?.replyDisabled) {
-      yield REPLY_PROMPT
+  } else if (node.type === 'blocked') {
+    yield node
+  }
+}
+
+function* flattenThreadReplies(
+  node: ThreadNode,
+  hasSession: boolean,
+  treeView: boolean,
+): Generator<YieldedItem, void> {
+  if (node.type === 'post') {
+    if (!hasSession && hasPwiOptOut(node)) {
+      return
+    }
+    if (!node.ctx.isHighlightedPost) {
+      yield node
     }
     if (node.replies?.length) {
       for (const reply of node.replies) {
-        yield* flattenThreadSkeleton(reply, hasSession, treeView, true)
+        yield* flattenThreadReplies(reply, hasSession, treeView)
         if (!treeView && !node.ctx.isHighlightedPost) {
           break
         }
       }
-    } else if (node.ctx.isChildLoading) {
-      yield CHILD_SPINNER
     }
   } else if (node.type === 'not-found') {
     yield node