about summary refs log tree commit diff
path: root/src/lib/api/feed/merge.ts
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-11-10 15:34:25 -0800
committerGitHub <noreply@github.com>2023-11-10 15:34:25 -0800
commitc8c308e31e63607280648e3e9f1f56a371adcd05 (patch)
tree09cea4c603968a1a0b4cab299af9a417880c8115 /src/lib/api/feed/merge.ts
parent51f04b96200e38d95e486628d3cbc43398c47980 (diff)
downloadvoidsky-c8c308e31e63607280648e3e9f1f56a371adcd05.tar.zst
Refactor feeds to use react-query (#1862)
* Update to react-query v5

* Introduce post-feed react query

* Add feed refresh behaviors

* Only fetch feeds of visible pages

* Implement polling for latest on feeds

* Add moderation filtering to slices

* Handle block errors

* Update feed error messages

* Remove old models

* Replace simple-feed option with disable-tuner option

* Add missing useMemo

* Implement the mergefeed and fixes to polling

* Correctly handle failed load more state

* Improve error and empty state behaviors

* Clearer naming
Diffstat (limited to 'src/lib/api/feed/merge.ts')
-rw-r--r--src/lib/api/feed/merge.ts88
1 files changed, 49 insertions, 39 deletions
diff --git a/src/lib/api/feed/merge.ts b/src/lib/api/feed/merge.ts
index e0fbcecd8..7a0f02887 100644
--- a/src/lib/api/feed/merge.ts
+++ b/src/lib/api/feed/merge.ts
@@ -1,11 +1,12 @@
-import {AppBskyFeedDefs, AppBskyFeedGetTimeline} from '@atproto/api'
+import {AppBskyFeedDefs, AppBskyFeedGetTimeline, BskyAgent} from '@atproto/api'
 import shuffle from 'lodash.shuffle'
-import {RootStoreModel} from 'state/index'
 import {timeout} from 'lib/async/timeout'
 import {bundleAsync} from 'lib/async/bundle'
 import {feedUriToHref} from 'lib/strings/url-helpers'
 import {FeedTuner} from '../feed-manip'
-import {FeedAPI, FeedAPIResponse, FeedSourceInfo} from './types'
+import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types'
+import {FeedParams} from '#/state/queries/post-feed'
+import {FeedTunerFn} from '../feed-manip'
 
 const REQUEST_WAIT_MS = 500 // 500ms
 const POST_AGE_CUTOFF = 60e3 * 60 * 24 // 24hours
@@ -17,28 +18,49 @@ export class MergeFeedAPI implements FeedAPI {
   itemCursor = 0
   sampleCursor = 0
 
-  constructor(public rootStore: RootStoreModel) {
-    this.following = new MergeFeedSource_Following(this.rootStore)
+  constructor(
+    public agent: BskyAgent,
+    public params: FeedParams,
+    public feedTuners: FeedTunerFn[],
+  ) {
+    this.following = new MergeFeedSource_Following(this.agent, this.feedTuners)
   }
 
   reset() {
-    this.following = new MergeFeedSource_Following(this.rootStore)
+    this.following = new MergeFeedSource_Following(this.agent, this.feedTuners)
     this.customFeeds = [] // just empty the array, they will be captured in _fetchNext()
     this.feedCursor = 0
     this.itemCursor = 0
     this.sampleCursor = 0
+    if (this.params.mergeFeedEnabled && this.params.mergeFeedSources) {
+      this.customFeeds = shuffle(
+        this.params.mergeFeedSources.map(
+          feedUri =>
+            new MergeFeedSource_Custom(this.agent, feedUri, this.feedTuners),
+        ),
+      )
+    } else {
+      this.customFeeds = []
+    }
   }
 
   async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
-    const res = await this.rootStore.agent.getTimeline({
+    const res = await this.agent.getTimeline({
       limit: 1,
     })
     return res.data.feed[0]
   }
 
-  async fetchNext({limit}: {limit: number}): Promise<FeedAPIResponse> {
-    // we capture here to ensure the data has loaded
-    this._captureFeedsIfNeeded()
+  async fetch({
+    cursor,
+    limit,
+  }: {
+    cursor: string | undefined
+    limit: number
+  }): Promise<FeedAPIResponse> {
+    if (!cursor) {
+      this.reset()
+    }
 
     const promises = []
 
@@ -76,7 +98,7 @@ export class MergeFeedAPI implements FeedAPI {
     }
 
     return {
-      cursor: posts.length ? 'fake' : undefined,
+      cursor: posts.length ? String(this.itemCursor) : undefined,
       feed: posts,
     }
   }
@@ -107,28 +129,15 @@ export class MergeFeedAPI implements FeedAPI {
     // provide follow
     return this.following.take(1)
   }
-
-  _captureFeedsIfNeeded() {
-    if (!this.rootStore.preferences.homeFeed.lab_mergeFeedEnabled) {
-      return
-    }
-    if (this.customFeeds.length === 0) {
-      this.customFeeds = shuffle(
-        this.rootStore.preferences.savedFeeds.map(
-          feedUri => new MergeFeedSource_Custom(this.rootStore, feedUri),
-        ),
-      )
-    }
-  }
 }
 
 class MergeFeedSource {
-  sourceInfo: FeedSourceInfo | undefined
+  sourceInfo: ReasonFeedSource | undefined
   cursor: string | undefined = undefined
   queue: AppBskyFeedDefs.FeedViewPost[] = []
   hasMore = true
 
-  constructor(public rootStore: RootStoreModel) {}
+  constructor(public agent: BskyAgent, public feedTuners: FeedTunerFn[]) {}
 
   get numReady() {
     return this.queue.length
@@ -190,16 +199,12 @@ class MergeFeedSource_Following extends MergeFeedSource {
     cursor: string | undefined,
     limit: number,
   ): Promise<AppBskyFeedGetTimeline.Response> {
-    const res = await this.rootStore.agent.getTimeline({cursor, limit})
+    const res = await this.agent.getTimeline({cursor, limit})
     // run the tuner pre-emptively to ensure better mixing
-    const slices = this.tuner.tune(
-      res.data.feed,
-      this.rootStore.preferences.getFeedTuners('home'),
-      {
-        dryRun: false,
-        maintainOrder: true,
-      },
-    )
+    const slices = this.tuner.tune(res.data.feed, this.feedTuners, {
+      dryRun: false,
+      maintainOrder: true,
+    })
     res.data.feed = slices.map(slice => slice.rootItem)
     return res
   }
@@ -208,14 +213,19 @@ class MergeFeedSource_Following extends MergeFeedSource {
 class MergeFeedSource_Custom extends MergeFeedSource {
   minDate: Date
 
-  constructor(public rootStore: RootStoreModel, public feedUri: string) {
-    super(rootStore)
+  constructor(
+    public agent: BskyAgent,
+    public feedUri: string,
+    public feedTuners: FeedTunerFn[],
+  ) {
+    super(agent, feedTuners)
     this.sourceInfo = {
+      $type: 'reasonFeedSource',
       displayName: feedUri.split('/').pop() || '',
       uri: feedUriToHref(feedUri),
     }
     this.minDate = new Date(Date.now() - POST_AGE_CUTOFF)
-    this.rootStore.agent.app.bsky.feed
+    this.agent.app.bsky.feed
       .getFeedGenerator({
         feed: feedUri,
       })
@@ -234,7 +244,7 @@ class MergeFeedSource_Custom extends MergeFeedSource {
     limit: number,
   ): Promise<AppBskyFeedGetTimeline.Response> {
     try {
-      const res = await this.rootStore.agent.app.bsky.feed.getFeed({
+      const res = await this.agent.app.bsky.feed.getFeed({
         cursor,
         limit,
         feed: this.feedUri,