about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/lib/api/feed-manip.ts23
-rw-r--r--src/state/models/feed-view.ts3
-rw-r--r--src/view/com/posts/FeedItem.tsx37
-rw-r--r--src/view/com/posts/FeedSlice.tsx83
4 files changed, 103 insertions, 43 deletions
diff --git a/src/lib/api/feed-manip.ts b/src/lib/api/feed-manip.ts
index 67ca8f952..f6b54a175 100644
--- a/src/lib/api/feed-manip.ts
+++ b/src/lib/api/feed-manip.ts
@@ -28,6 +28,7 @@ export class FeedViewPostsSlice {
   get isThread() {
     return (
       this.items.length > 1 &&
+      !this.items[0].reply &&
       this.items.every(
         item => item.post.author.did === this.items[0].post.author.did,
       )
@@ -35,7 +36,7 @@ export class FeedViewPostsSlice {
   }
 
   get isReply() {
-    return this.items.length === 2 && !this.isThread
+    return this.items.length > 1 && !this.isThread
   }
 
   get rootItem() {
@@ -49,6 +50,10 @@ export class FeedViewPostsSlice {
     return !!this.items.find(item => item.post.uri === uri)
   }
 
+  isNextInThread(uri: string) {
+    return this.items[this.items.length - 1].post.uri === uri
+  }
+
   insert(item: FeedViewPost) {
     const selfReplyUri = getSelfReplyUri(item)
     const i = this.items.findIndex(item2 => item2.post.uri === selfReplyUri)
@@ -102,7 +107,7 @@ export class FeedTuner {
 
       const selfReplyUri = getSelfReplyUri(item)
       if (selfReplyUri) {
-        const parent = slices.find(item2 => item2.containsUri(selfReplyUri))
+        const parent = slices.find(item2 => item2.isNextInThread(selfReplyUri))
         if (parent) {
           parent.insert(item)
           continue
@@ -112,9 +117,14 @@ export class FeedTuner {
     }
 
     // remove any items already "seen"
+    const soonToBeSeenUris: Set<string> = new Set()
     for (let i = slices.length - 1; i >= 0; i--) {
       if (this.seenUris.has(slices[i].uri)) {
         slices.splice(i, 1)
+      } else {
+        for (const item of slices[i].items) {
+          soonToBeSeenUris.add(item.post.uri)
+        }
       }
     }
 
@@ -124,9 +134,12 @@ export class FeedTuner {
         !slice.isThread &&
         !slice.items[0].reason &&
         slice.items[0].reply?.parent &&
-        !this.seenUris.has(slice.items[0].reply?.parent.uri)
+        !this.seenUris.has(slice.items[0].reply?.parent.uri) &&
+        !soonToBeSeenUris.has(slice.items[0].reply?.parent.uri)
       ) {
+        const uri = slice.items[0].reply?.parent.uri
         slice.flattenReplyParent()
+        soonToBeSeenUris.add(uri)
       }
     }
 
@@ -168,14 +181,14 @@ export class FeedTuner {
   }
 
   static likedRepliesOnly(tuner: FeedTuner, slices: FeedViewPostsSlice[]) {
-    // remove any replies without any likes
+    // remove any replies without at least 2 likes
     for (let i = slices.length - 1; i >= 0; i--) {
       if (slices[i].isThread) {
         continue
       }
       const item = slices[i].rootItem
       const isRepost = Boolean(item.reason)
-      if (item.reply && !isRepost && item.post.upvoteCount === 0) {
+      if (item.reply && !isRepost && item.post.upvoteCount < 2) {
         slices.splice(i, 1)
       }
     }
diff --git a/src/state/models/feed-view.ts b/src/state/models/feed-view.ts
index c412065dd..0fbfa515a 100644
--- a/src/state/models/feed-view.ts
+++ b/src/state/models/feed-view.ts
@@ -200,6 +200,7 @@ export class FeedSliceModel {
   get isThread() {
     return (
       this.items.length > 1 &&
+      !this.items[0].reply &&
       this.items.every(
         item => item.post.author.did === this.items[0].post.author.did,
       )
@@ -207,7 +208,7 @@ export class FeedSliceModel {
   }
 
   get isReply() {
-    return this.items.length === 2 && !this.isThread
+    return this.items.length > 1 && !this.isThread
   }
 
   get rootItem() {
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index 35a917591..573b92fd3 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -2,7 +2,6 @@ import React, {useMemo, useState} from 'react'
 import {observer} from 'mobx-react-lite'
 import {Linking, StyleSheet, View} from 'react-native'
 import Clipboard from '@react-native-clipboard/clipboard'
-import Svg, {Circle, Line} from 'react-native-svg'
 import {AtUri} from '../../../third-party/uri'
 import {
   FontAwesomeIcon,
@@ -253,32 +252,6 @@ export const FeedItem = observer(function ({
           </View>
         </View>
       </Link>
-      {false /*isThreadChildElided*/ ? (
-        <Link
-          style={[pal.view, styles.viewFullThread]}
-          href={itemHref}
-          title={itemTitle}
-          noFeedback>
-          <View style={styles.viewFullThreadDots}>
-            <Svg width="4" height="30">
-              <Line
-                x1="2"
-                y1="0"
-                x2="2"
-                y2="8"
-                stroke={pal.colors.replyLine}
-                strokeWidth="2"
-              />
-              <Circle x="2" y="14" r="1.5" fill={pal.colors.replyLineDot} />
-              <Circle x="2" y="20" r="1.5" fill={pal.colors.replyLineDot} />
-              <Circle x="2" y="26" r="1.5" fill={pal.colors.replyLineDot} />
-            </Svg>
-          </View>
-          <Text type="md" style={pal.link}>
-            View full thread
-          </Text>
-        </Link>
-      ) : undefined}
     </PostMutedWrapper>
   )
 })
@@ -348,14 +321,4 @@ const styles = StyleSheet.create({
   ctrls: {
     marginTop: 4,
   },
-  viewFullThread: {
-    paddingTop: 12,
-    paddingBottom: 2,
-    paddingLeft: 80,
-  },
-  viewFullThreadDots: {
-    position: 'absolute',
-    left: 41,
-    top: 0,
-  },
 })
diff --git a/src/view/com/posts/FeedSlice.tsx b/src/view/com/posts/FeedSlice.tsx
index 1dba8ac93..4df6d4f54 100644
--- a/src/view/com/posts/FeedSlice.tsx
+++ b/src/view/com/posts/FeedSlice.tsx
@@ -1,6 +1,12 @@
 import React from 'react'
+import {StyleSheet, View} from 'react-native'
 import {FeedSliceModel} from 'state/models/feed-view'
+import {AtUri} from '../../../third-party/uri'
+import {Link} from '../util/Link'
+import {Text} from '../util/text/Text'
+import Svg, {Circle, Line} from 'react-native-svg'
 import {FeedItem} from './FeedItem'
+import {usePalette} from 'lib/hooks/usePalette'
 
 export function FeedSlice({
   slice,
@@ -11,6 +17,39 @@ export function FeedSlice({
   showFollowBtn?: boolean
   ignoreMuteFor?: string
 }) {
+  if (slice.isThread && slice.items.length > 3) {
+    const last = slice.items.length - 1
+    return (
+      <>
+        <FeedItem
+          key={slice.items[0]._reactKey}
+          item={slice.items[0]}
+          isThreadParent={slice.isThreadParentAt(0)}
+          isThreadChild={slice.isThreadChildAt(0)}
+          showFollowBtn={showFollowBtn}
+          ignoreMuteFor={ignoreMuteFor}
+        />
+        <FeedItem
+          key={slice.items[1]._reactKey}
+          item={slice.items[1]}
+          isThreadParent={slice.isThreadParentAt(1)}
+          isThreadChild={slice.isThreadChildAt(1)}
+          showFollowBtn={showFollowBtn}
+          ignoreMuteFor={ignoreMuteFor}
+        />
+        <ViewFullThread slice={slice} />
+        <FeedItem
+          key={slice.items[last]._reactKey}
+          item={slice.items[last]}
+          isThreadParent={slice.isThreadParentAt(last)}
+          isThreadChild={slice.isThreadChildAt(last)}
+          showFollowBtn={showFollowBtn}
+          ignoreMuteFor={ignoreMuteFor}
+        />
+      </>
+    )
+  }
+
   return (
     <>
       {slice.items.map((item, i) => (
@@ -26,3 +65,47 @@ export function FeedSlice({
     </>
   )
 }
+
+function ViewFullThread({slice}: {slice: FeedSliceModel}) {
+  const pal = usePalette('default')
+  const itemHref = React.useMemo(() => {
+    const urip = new AtUri(slice.rootItem.post.uri)
+    return `/profile/${slice.rootItem.post.author.handle}/post/${urip.rkey}`
+  }, [slice.rootItem.post.uri, slice.rootItem.post.author.handle])
+
+  return (
+    <Link style={[pal.view, styles.viewFullThread]} href={itemHref} noFeedback>
+      <View style={styles.viewFullThreadDots}>
+        <Svg width="4" height="30">
+          <Line
+            x1="2"
+            y1="0"
+            x2="2"
+            y2="8"
+            stroke={pal.colors.replyLine}
+            strokeWidth="2"
+          />
+          <Circle x="2" y="16" r="1.5" fill={pal.colors.replyLineDot} />
+          <Circle x="2" y="22" r="1.5" fill={pal.colors.replyLineDot} />
+          <Circle x="2" y="28" r="1.5" fill={pal.colors.replyLineDot} />
+        </Svg>
+      </View>
+      <Text type="md" style={pal.link}>
+        View full thread
+      </Text>
+    </Link>
+  )
+}
+
+const styles = StyleSheet.create({
+  viewFullThread: {
+    paddingTop: 14,
+    paddingBottom: 6,
+    paddingLeft: 80,
+  },
+  viewFullThreadDots: {
+    position: 'absolute',
+    left: 41,
+    top: 0,
+  },
+})