mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at utm-source 4.1 kB view raw
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}