about summary refs log tree commit diff
path: root/src/state/models/feed-view.ts
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2022-12-18 18:54:05 -0600
committerPaul Frazee <pfrazee@gmail.com>2022-12-18 18:54:05 -0600
commitae3099dfca13f6651762f6ea9a3d2a14ebc99df4 (patch)
treec78136d8fb2f15da0c1be3bef8d0e732287d050f /src/state/models/feed-view.ts
parent69b86255c6c2275b3403ce6654b17e0a2c56ced6 (diff)
downloadvoidsky-ae3099dfca13f6651762f6ea9a3d2a14ebc99df4.tar.zst
Improve thread rendering
Diffstat (limited to 'src/state/models/feed-view.ts')
-rw-r--r--src/state/models/feed-view.ts117
1 files changed, 76 insertions, 41 deletions
diff --git a/src/state/models/feed-view.ts b/src/state/models/feed-view.ts
index f2832887a..f8080d4b5 100644
--- a/src/state/models/feed-view.ts
+++ b/src/state/models/feed-view.ts
@@ -17,6 +17,7 @@ let _idCounter = 0
 type FeedItem = GetTimeline.FeedItem | GetAuthorFeed.FeedItem
 type FeedItemWithThreadMeta = FeedItem & {
   _isThreadParent?: boolean
+  _isThreadChildElided?: boolean
   _isThreadChild?: boolean
 }
 
@@ -34,6 +35,7 @@ export class FeedItemModel implements GetTimeline.FeedItem {
   // ui state
   _reactKey: string = ''
   _isThreadParent: boolean = false
+  _isThreadChildElided: boolean = false
   _isThreadChild: boolean = false
 
   // data
@@ -70,6 +72,7 @@ export class FeedItemModel implements GetTimeline.FeedItem {
     this.copy(v)
     this._isThreadParent = v._isThreadParent || false
     this._isThreadChild = v._isThreadChild || false
+    this._isThreadChildElided = v._isThreadChildElided || false
   }
 
   copy(v: GetTimeline.FeedItem | GetAuthorFeed.FeedItem) {
@@ -469,15 +472,7 @@ export class FeedModel {
     this.loadMoreCursor = res.data.cursor
     this.hasMore = !!this.loadMoreCursor
 
-    // HACK 1
-    // rearrange the posts to represent threads
-    // (should be done on the server)
-    // -prf
-    // HACK 2
-    // deduplicate posts on the home feed
-    // (should be done on the server)
-    // -prf
-    const reorgedFeed = preprocessFeed(res.data.feed, this.feedType === 'home')
+    const reorgedFeed = preprocessFeed(res.data.feed)
 
     const promises = []
     const toAppend: FeedItemModel[] = []
@@ -569,38 +564,78 @@ export class FeedModel {
   }
 }
 
-function preprocessFeed(
-  feed: FeedItem[],
-  dedup: boolean,
-): FeedItemWithThreadMeta[] {
-  // DEBUG
-  // this has been temporarily disabled to see if it's the cause of some bugs
-  // if the issues go away, we know this was the cause
-  // -prf
-  return feed
-  // const reorg: FeedItemWithThreadMeta[] = []
-  // for (let i = feed.length - 1; i >= 0; i--) {
-  //   const item = feed[i] as FeedItemWithThreadMeta
-
-  //   if (dedup) {
-  //     if (reorg.find(item2 => item2.uri === item.uri)) {
-  //       continue
-  //     }
-  //   }
-
-  //   const selfReplyUri = getSelfReplyUri(item)
-  //   if (selfReplyUri) {
-  //     const parentIndex = reorg.findIndex(item2 => item2.uri === selfReplyUri)
-  //     if (parentIndex !== -1 && !reorg[parentIndex]._isThreadParent) {
-  //       reorg[parentIndex]._isThreadParent = true
-  //       item._isThreadChild = true
-  //       reorg.splice(parentIndex + 1, 0, item)
-  //       continue
-  //     }
-  //   }
-  //   reorg.unshift(item)
-  // }
-  // return reorg
+interface Slice {
+  index: number
+  length: number
+}
+function preprocessFeed(feed: FeedItem[]): FeedItemWithThreadMeta[] {
+  const reorg: FeedItemWithThreadMeta[] = []
+
+  // phase one: identify threads and reorganize them into the feed so
+  // that they are in order and marked as part of a thread
+  for (let i = feed.length - 1; i >= 0; i--) {
+    const item = feed[i] as FeedItemWithThreadMeta
+
+    const selfReplyUri = getSelfReplyUri(item)
+    if (selfReplyUri) {
+      const parentIndex = reorg.findIndex(item2 => item2.uri === selfReplyUri)
+      if (parentIndex !== -1 && !reorg[parentIndex]._isThreadParent) {
+        reorg[parentIndex]._isThreadParent = true
+        item._isThreadChild = true
+        reorg.splice(parentIndex + 1, 0, item)
+        continue
+      }
+    }
+    reorg.unshift(item)
+  }
+
+  // phase two: identify the positions of the threads
+  let activeSlice = -1
+  let threadSlices: Slice[] = []
+  for (let i = 0; i < reorg.length; i++) {
+    const item = reorg[i] as FeedItemWithThreadMeta
+    if (activeSlice === -1) {
+      if (item._isThreadParent) {
+        activeSlice = i
+      }
+    } else {
+      if (!item._isThreadChild) {
+        threadSlices.push({index: activeSlice, length: i - activeSlice})
+        activeSlice = -1
+      }
+    }
+  }
+  if (activeSlice !== -1) {
+    threadSlices.push({index: activeSlice, length: reorg.length - activeSlice})
+  }
+
+  // phase three: reorder the feed so that the timestamp of the
+  // last post in a thread establishes its ordering
+  for (const slice of threadSlices) {
+    const removed: FeedItemWithThreadMeta[] = reorg.splice(
+      slice.index,
+      slice.length,
+    )
+    const targetDate = new Date(removed[removed.length - 1].indexedAt)
+    const newIndex = reorg.findIndex(
+      item => new Date(item.indexedAt) < targetDate,
+    )
+    reorg.splice(newIndex, 0, ...removed)
+    slice.index = newIndex
+  }
+
+  // phase four: compress any threads that are longer than 3 posts
+  let removedCount = 0
+  for (const slice of threadSlices) {
+    if (slice.length > 3) {
+      reorg.splice(slice.index - removedCount + 1, slice.length - 3)
+      reorg[slice.index - removedCount]._isThreadChildElided = true
+      console.log(reorg[slice.index - removedCount])
+      removedCount += slice.length - 3
+    }
+  }
+
+  return reorg
 }
 
 function getSelfReplyUri(