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