mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {
2 AppBskyFeedDefs,
3 AppBskyFeedGetFeed as GetCustomFeed,
4 AtpAgent,
5 BskyAgent,
6} from '@atproto/api'
7
8import {getContentLanguages} from '#/state/preferences/languages'
9import {FeedAPI, FeedAPIResponse} from './types'
10import {createBskyTopicsHeader, isBlueskyOwnedFeed} from './utils'
11
12export class CustomFeedAPI implements FeedAPI {
13 agent: BskyAgent
14 params: GetCustomFeed.QueryParams
15 userInterests?: string
16
17 constructor({
18 agent,
19 feedParams,
20 userInterests,
21 }: {
22 agent: BskyAgent
23 feedParams: GetCustomFeed.QueryParams
24 userInterests?: string
25 }) {
26 this.agent = agent
27 this.params = feedParams
28 this.userInterests = userInterests
29 }
30
31 async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
32 const contentLangs = getContentLanguages().join(',')
33 const res = await this.agent.app.bsky.feed.getFeed(
34 {
35 ...this.params,
36 limit: 1,
37 },
38 {headers: {'Accept-Language': contentLangs}},
39 )
40 return res.data.feed[0]
41 }
42
43 async fetch({
44 cursor,
45 limit,
46 }: {
47 cursor: string | undefined
48 limit: number
49 }): Promise<FeedAPIResponse> {
50 const contentLangs = getContentLanguages().join(',')
51 const agent = this.agent
52 const isBlueskyOwned = isBlueskyOwnedFeed(this.params.feed)
53
54 const res = agent.session
55 ? await this.agent.app.bsky.feed.getFeed(
56 {
57 ...this.params,
58 cursor,
59 limit,
60 },
61 {
62 headers: {
63 ...(isBlueskyOwned
64 ? createBskyTopicsHeader(this.userInterests)
65 : {}),
66 'Accept-Language': contentLangs,
67 },
68 },
69 )
70 : await loggedOutFetch({...this.params, cursor, limit})
71 if (res.success) {
72 // NOTE
73 // some custom feeds fail to enforce the pagination limit
74 // so we manually truncate here
75 // -prf
76 if (res.data.feed.length > limit) {
77 res.data.feed = res.data.feed.slice(0, limit)
78 }
79 return {
80 cursor: res.data.feed.length ? res.data.cursor : undefined,
81 feed: res.data.feed,
82 }
83 }
84 return {
85 feed: [],
86 }
87 }
88}
89
90// HACK
91// we want feeds to give language-specific results immediately when a
92// logged-out user changes their language. this comes with two problems:
93// 1. not all languages have content, and
94// 2. our public caching layer isnt correctly busting against the accept-language header
95// for now we handle both of these with a manual workaround
96// -prf
97async function loggedOutFetch({
98 feed,
99 limit,
100 cursor,
101}: {
102 feed: string
103 limit: number
104 cursor?: string
105}) {
106 let contentLangs = getContentLanguages().join(',')
107
108 // manually construct fetch call so we can add the `lang` cache-busting param
109 let res = await AtpAgent.fetch!(
110 `https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=${feed}${
111 cursor ? `&cursor=${cursor}` : ''
112 }&limit=${limit}&lang=${contentLangs}`,
113 'GET',
114 {'Accept-Language': contentLangs},
115 undefined,
116 )
117 if (res.body?.feed?.length) {
118 return {
119 success: true,
120 data: res.body,
121 }
122 }
123
124 // no data, try again with language headers removed
125 res = await AtpAgent.fetch!(
126 `https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=${feed}${
127 cursor ? `&cursor=${cursor}` : ''
128 }&limit=${limit}`,
129 'GET',
130 {'Accept-Language': ''},
131 undefined,
132 )
133 if (res.body?.feed?.length) {
134 return {
135 success: true,
136 data: res.body,
137 }
138 }
139
140 return {
141 success: false,
142 data: {feed: []},
143 }
144}