about summary refs log tree commit diff
path: root/src/lib/api/feed
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/api/feed')
-rw-r--r--src/lib/api/feed/custom.ts27
-rw-r--r--src/lib/api/feed/home.ts89
-rw-r--r--src/lib/api/feed/merge.ts19
3 files changed, 119 insertions, 16 deletions
diff --git a/src/lib/api/feed/custom.ts b/src/lib/api/feed/custom.ts
index 94cbff130..41c5367e5 100644
--- a/src/lib/api/feed/custom.ts
+++ b/src/lib/api/feed/custom.ts
@@ -4,15 +4,20 @@ import {
 } from '@atproto/api'
 import {FeedAPI, FeedAPIResponse} from './types'
 import {getAgent} from '#/state/session'
+import {getContentLanguages} from '#/state/preferences/languages'
 
 export class CustomFeedAPI implements FeedAPI {
   constructor(public params: GetCustomFeed.QueryParams) {}
 
   async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
-    const res = await getAgent().app.bsky.feed.getFeed({
-      ...this.params,
-      limit: 1,
-    })
+    const contentLangs = getContentLanguages().join(',')
+    const res = await getAgent().app.bsky.feed.getFeed(
+      {
+        ...this.params,
+        limit: 1,
+      },
+      {headers: {'Accept-Language': contentLangs}},
+    )
     return res.data.feed[0]
   }
 
@@ -23,11 +28,15 @@ export class CustomFeedAPI implements FeedAPI {
     cursor: string | undefined
     limit: number
   }): Promise<FeedAPIResponse> {
-    const res = await getAgent().app.bsky.feed.getFeed({
-      ...this.params,
-      cursor,
-      limit,
-    })
+    const contentLangs = getContentLanguages().join(',')
+    const res = await getAgent().app.bsky.feed.getFeed(
+      {
+        ...this.params,
+        cursor,
+        limit,
+      },
+      {headers: {'Accept-Language': contentLangs}},
+    )
     if (res.success) {
       // NOTE
       // some custom feeds fail to enforce the pagination limit
diff --git a/src/lib/api/feed/home.ts b/src/lib/api/feed/home.ts
new file mode 100644
index 000000000..436a66d07
--- /dev/null
+++ b/src/lib/api/feed/home.ts
@@ -0,0 +1,89 @@
+import {AppBskyFeedDefs} from '@atproto/api'
+import {FeedAPI, FeedAPIResponse} from './types'
+import {FollowingFeedAPI} from './following'
+import {CustomFeedAPI} from './custom'
+import {PROD_DEFAULT_FEED} from '#/lib/constants'
+
+// HACK
+// the feed API does not include any facilities for passing down
+// non-post elements. adding that is a bit of a heavy lift, and we
+// have just one temporary usecase for it: flagging when the home feed
+// falls back to discover.
+// we use this fallback marker post to drive this instead. see Feed.tsx
+// for the usage.
+// -prf
+export const FALLBACK_MARKER_POST: AppBskyFeedDefs.FeedViewPost = {
+  post: {
+    uri: 'fallback-marker-post',
+    cid: 'fake',
+    record: {},
+    author: {
+      did: 'did:fake',
+      handle: 'fake.com',
+    },
+    indexedAt: new Date().toISOString(),
+  },
+}
+
+export class HomeFeedAPI implements FeedAPI {
+  following: FollowingFeedAPI
+  discover: CustomFeedAPI
+  usingDiscover = false
+  itemCursor = 0
+
+  constructor() {
+    this.following = new FollowingFeedAPI()
+    this.discover = new CustomFeedAPI({feed: PROD_DEFAULT_FEED('whats-hot')})
+  }
+
+  reset() {
+    this.following = new FollowingFeedAPI()
+    this.discover = new CustomFeedAPI({feed: PROD_DEFAULT_FEED('whats-hot')})
+    this.usingDiscover = false
+    this.itemCursor = 0
+  }
+
+  async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
+    if (this.usingDiscover) {
+      return this.discover.peekLatest()
+    }
+    return this.following.peekLatest()
+  }
+
+  async fetch({
+    cursor,
+    limit,
+  }: {
+    cursor: string | undefined
+    limit: number
+  }): Promise<FeedAPIResponse> {
+    if (!cursor) {
+      this.reset()
+    }
+
+    let returnCursor
+    let posts: AppBskyFeedDefs.FeedViewPost[] = []
+
+    if (!this.usingDiscover) {
+      const res = await this.following.fetch({cursor, limit})
+      returnCursor = res.cursor
+      posts = posts.concat(res.feed)
+      if (!returnCursor) {
+        cursor = ''
+        posts.push(FALLBACK_MARKER_POST)
+        this.usingDiscover = true
+      }
+    }
+
+    if (this.usingDiscover) {
+      const res = await this.discover.fetch({cursor, limit})
+      returnCursor = res.cursor
+      posts = posts.concat(res.feed)
+    }
+
+    return {
+      cursor: returnCursor,
+      feed: posts,
+    }
+  }
+}
diff --git a/src/lib/api/feed/merge.ts b/src/lib/api/feed/merge.ts
index a4391afb2..28bf143cb 100644
--- a/src/lib/api/feed/merge.ts
+++ b/src/lib/api/feed/merge.ts
@@ -8,6 +8,7 @@ import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types'
 import {FeedParams} from '#/state/queries/post-feed'
 import {FeedTunerFn} from '../feed-manip'
 import {getAgent} from '#/state/session'
+import {getContentLanguages} from '#/state/preferences/languages'
 
 const REQUEST_WAIT_MS = 500 // 500ms
 const POST_AGE_CUTOFF = 60e3 * 60 * 24 // 24hours
@@ -25,7 +26,7 @@ export class MergeFeedAPI implements FeedAPI {
 
   reset() {
     this.following = new MergeFeedSource_Following(this.feedTuners)
-    this.customFeeds = [] // just empty the array, they will be captured in _fetchNext()
+    this.customFeeds = []
     this.feedCursor = 0
     this.itemCursor = 0
     this.sampleCursor = 0
@@ -98,7 +99,7 @@ export class MergeFeedAPI implements FeedAPI {
     }
 
     return {
-      cursor: posts.length ? String(this.itemCursor) : undefined,
+      cursor: String(this.itemCursor),
       feed: posts,
     }
   }
@@ -231,11 +232,15 @@ class MergeFeedSource_Custom extends MergeFeedSource {
     limit: number,
   ): Promise<AppBskyFeedGetTimeline.Response> {
     try {
-      const res = await getAgent().app.bsky.feed.getFeed({
-        cursor,
-        limit,
-        feed: this.feedUri,
-      })
+      const contentLangs = getContentLanguages().join(',')
+      const res = await getAgent().app.bsky.feed.getFeed(
+        {
+          cursor,
+          limit,
+          feed: this.feedUri,
+        },
+        {headers: {'Accept-Language': contentLangs}},
+      )
       // NOTE
       // some custom feeds fail to enforce the pagination limit
       // so we manually truncate here