about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2024-08-05 20:51:41 +0100
committerGitHub <noreply@github.com>2024-08-05 20:51:41 +0100
commit74b0318d89b5ec4746cd4861f8573ea24c6ccea1 (patch)
tree953e092d042a77bef90cf24dfcd0ba778ea9db71 /src/state
parent18b423396b75d8b4348a434412d0da1f38230717 (diff)
downloadvoidsky-74b0318d89b5ec4746cd4861f8573ea24c6ccea1.tar.zst
Show replies in context of their threads (#4871)
* Don't reconstruct threads from separate posts

* Remove post-level dedupe for now

* Change repost dedupe condition to look just at length

* Delete unused isThread

* Delete another isThread field

It is now meaningless because there's nothing special about author threads.

* Narrow down slice item shape so it does not need reply

* Consolidate slice validation criteria in one place

* Show replies in context

* Make fallback marker work

* Remove misleading and now-unused property

It was called rootUri but it was actually the leaf URI. Regardless, it's not used anymore.

* Add by-thread dedupe to non-author feeds

* Add post-level dedupe

* Always count from the start

This is easier to think about.

* Only tuner state need to be untouched on dry run

* Account for threads in reply filtering

* Remove repost deduping

This is already being taken care of by item-level deduping. It's also now wrong and removing too much (since it wasn't filtering for reposts directly).

* Calculate rootUri correctly

* Apply Following settings to all lists

* Don't dedupe intentional reposts by thread

* Show reply parent when ambiguous

* Explicitly remove orphaned replies from following/lists

* Fix thread dedupe to work across pages

* Mark grandparent-blocked as orphaned

* Guard tuner state change by dryRun

* Remove dead code

* Don't dedupe feedgen threads

* Revert "Apply Following settings to all lists"

This reverts commit aff86be6d37b60cc5d0ac38f22c31a4808342cf4.

Let's not do this yet and have a bit more discussion. This is a chunky change already.

* Reason belongs to a slice, not item

* Logically feedContext belongs to the slice

* Update comment to reflect latest behavior
Diffstat (limited to 'src/state')
-rw-r--r--src/state/feed-feedback.tsx2
-rw-r--r--src/state/preferences/feed-tuners.tsx19
-rw-r--r--src/state/queries/post-feed.ts78
3 files changed, 30 insertions, 69 deletions
diff --git a/src/state/feed-feedback.tsx b/src/state/feed-feedback.tsx
index 59b4bf78a..aab2737e5 100644
--- a/src/state/feed-feedback.tsx
+++ b/src/state/feed-feedback.tsx
@@ -123,7 +123,7 @@ export function useFeedFeedback(feed: FeedDescriptor, hasSession: boolean) {
             toString({
               item: postItem.uri,
               event: 'app.bsky.feed.defs#interactionSeen',
-              feedContext: postItem.feedContext,
+              feedContext: slice.feedContext,
             }),
           )
           sendToFeed()
diff --git a/src/state/preferences/feed-tuners.tsx b/src/state/preferences/feed-tuners.tsx
index d816bde64..b6f14fae7 100644
--- a/src/state/preferences/feed-tuners.tsx
+++ b/src/state/preferences/feed-tuners.tsx
@@ -19,20 +19,15 @@ export function useFeedTuners(feedDesc: FeedDescriptor) {
       }
     }
     if (feedDesc.startsWith('feedgen')) {
-      return [
-        FeedTuner.dedupReposts,
-        FeedTuner.preferredLangOnly(langPrefs.contentLanguages),
-      ]
+      return [FeedTuner.preferredLangOnly(langPrefs.contentLanguages)]
     }
     if (feedDesc.startsWith('list')) {
-      const feedTuners = []
-
+      let feedTuners = []
       if (feedDesc.endsWith('|as_following')) {
         // Same as Following tuners below, copypaste for now.
+        feedTuners.push(FeedTuner.removeOrphans)
         if (preferences?.feedViewPrefs.hideReposts) {
           feedTuners.push(FeedTuner.removeReposts)
-        } else {
-          feedTuners.push(FeedTuner.dedupReposts)
         }
         if (preferences?.feedViewPrefs.hideReplies) {
           feedTuners.push(FeedTuner.removeReplies)
@@ -46,18 +41,15 @@ export function useFeedTuners(feedDesc: FeedDescriptor) {
         if (preferences?.feedViewPrefs.hideQuotePosts) {
           feedTuners.push(FeedTuner.removeQuotePosts)
         }
-      } else {
-        feedTuners.push(FeedTuner.dedupReposts)
+        feedTuners.push(FeedTuner.dedupThreads)
       }
       return feedTuners
     }
     if (feedDesc === 'following') {
-      const feedTuners = []
+      const feedTuners = [FeedTuner.removeOrphans]
 
       if (preferences?.feedViewPrefs.hideReposts) {
         feedTuners.push(FeedTuner.removeReposts)
-      } else {
-        feedTuners.push(FeedTuner.dedupReposts)
       }
       if (preferences?.feedViewPrefs.hideReplies) {
         feedTuners.push(FeedTuner.removeReplies)
@@ -71,6 +63,7 @@ export function useFeedTuners(feedDesc: FeedDescriptor) {
       if (preferences?.feedViewPrefs.hideQuotePosts) {
         feedTuners.push(FeedTuner.removeQuotePosts)
       }
+      feedTuners.push(FeedTuner.dedupThreads)
 
       return feedTuners
     }
diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts
index 65467e802..724043e58 100644
--- a/src/state/queries/post-feed.ts
+++ b/src/state/queries/post-feed.ts
@@ -77,11 +77,6 @@ export interface FeedPostSliceItem {
   uri: string
   post: AppBskyFeedDefs.PostView
   record: AppBskyFeedPost.Record
-  reason?:
-    | AppBskyFeedDefs.ReasonRepost
-    | ReasonFeedSource
-    | {[k: string]: unknown; $type: string}
-  feedContext: string | undefined
   moderation: ModerationDecision
   parentAuthor?: AppBskyActorDefs.ProfileViewBasic
   isParentBlocked?: boolean
@@ -90,9 +85,14 @@ export interface FeedPostSliceItem {
 export interface FeedPostSlice {
   _isFeedPostSlice: boolean
   _reactKey: string
-  rootUri: string
-  isThread: boolean
   items: FeedPostSliceItem[]
+  isIncompleteThread: boolean
+  isFallbackMarker: boolean
+  feedContext: string | undefined
+  reason?:
+    | AppBskyFeedDefs.ReasonRepost
+    | ReasonFeedSource
+    | {[k: string]: unknown; $type: string}
 }
 
 export interface FeedPageUnselected {
@@ -313,53 +313,22 @@ export function usePostFeedQuery(
                   const feedPostSlice: FeedPostSlice = {
                     _reactKey: slice._reactKey,
                     _isFeedPostSlice: true,
-                    rootUri: slice.uri,
-                    isThread:
-                      slice.items.length > 1 &&
-                      slice.items.every(
-                        item =>
-                          item.post.author.did ===
-                          slice.items[0].post.author.did,
-                      ),
-                    items: slice.items
-                      .map((item, i) => {
-                        if (
-                          AppBskyFeedPost.isRecord(item.post.record) &&
-                          AppBskyFeedPost.validateRecord(item.post.record)
-                            .success
-                        ) {
-                          const parent = item.reply?.parent
-                          let parentAuthor:
-                            | AppBskyActorDefs.ProfileViewBasic
-                            | undefined
-                          if (AppBskyFeedDefs.isPostView(parent)) {
-                            parentAuthor = parent.author
-                          }
-                          if (!parentAuthor) {
-                            parentAuthor =
-                              slice.items[i + 1]?.reply?.grandparentAuthor
-                          }
-                          const replyRef = item.reply
-                          const isParentBlocked = AppBskyFeedDefs.isBlockedPost(
-                            replyRef?.parent,
-                          )
-
-                          const feedPostSliceItem: FeedPostSliceItem = {
-                            _reactKey: `${slice._reactKey}-${i}-${item.post.uri}`,
-                            uri: item.post.uri,
-                            post: item.post,
-                            record: item.post.record,
-                            reason: slice.reason,
-                            feedContext: slice.feedContext,
-                            moderation: moderations[i],
-                            parentAuthor,
-                            isParentBlocked,
-                          }
-                          return feedPostSliceItem
-                        }
-                        return undefined
-                      })
-                      .filter(n => !!n),
+                    isIncompleteThread: slice.isIncompleteThread,
+                    isFallbackMarker: slice.isFallbackMarker,
+                    feedContext: slice.feedContext,
+                    reason: slice.reason,
+                    items: slice.items.map((item, i) => {
+                      const feedPostSliceItem: FeedPostSliceItem = {
+                        _reactKey: `${slice._reactKey}-${i}-${item.post.uri}`,
+                        uri: item.post.uri,
+                        post: item.post,
+                        record: item.record,
+                        moderation: moderations[i],
+                        parentAuthor: item.parentAuthor,
+                        isParentBlocked: item.isParentBlocked,
+                      }
+                      return feedPostSliceItem
+                    }),
                   }
                   return feedPostSlice
                 })
@@ -442,7 +411,6 @@ export async function pollLatest(page: FeedPage | undefined) {
   if (post) {
     const slices = page.tuner.tune([post], {
       dryRun: true,
-      maintainOrder: true,
     })
     if (slices[0]) {
       return true