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