Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[Session] Drill `getAgent` into feed APIs (#3701)

* Update to desired post-feed usage

* Drill agent into feed apis

* Thread getAgent instead

---------

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>

authored by

Eric Bailey
Dan Abramov
and committed by
GitHub
ec376960 282ad4b1

+202 -65
+17 -4
src/lib/api/feed/author.ts
··· 1 1 import { 2 2 AppBskyFeedDefs, 3 3 AppBskyFeedGetAuthorFeed as GetAuthorFeed, 4 + BskyAgent, 4 5 } from '@atproto/api' 6 + 5 7 import {FeedAPI, FeedAPIResponse} from './types' 6 - import {getAgent} from '#/state/session' 7 8 8 9 export class AuthorFeedAPI implements FeedAPI { 9 - constructor(public params: GetAuthorFeed.QueryParams) {} 10 + getAgent: () => BskyAgent 11 + params: GetAuthorFeed.QueryParams 12 + 13 + constructor({ 14 + getAgent, 15 + feedParams, 16 + }: { 17 + getAgent: () => BskyAgent 18 + feedParams: GetAuthorFeed.QueryParams 19 + }) { 20 + this.getAgent = getAgent 21 + this.params = feedParams 22 + } 10 23 11 24 async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> { 12 - const res = await getAgent().getAuthorFeed({ 25 + const res = await this.getAgent().getAuthorFeed({ 13 26 ...this.params, 14 27 limit: 1, 15 28 }) ··· 23 36 cursor: string | undefined 24 37 limit: number 25 38 }): Promise<FeedAPIResponse> { 26 - const res = await getAgent().getAuthorFeed({ 39 + const res = await this.getAgent().getAuthorFeed({ 27 40 ...this.params, 28 41 cursor, 29 42 limit,
+22 -6
src/lib/api/feed/custom.ts
··· 2 2 AppBskyFeedDefs, 3 3 AppBskyFeedGetFeed as GetCustomFeed, 4 4 AtpAgent, 5 + BskyAgent, 5 6 } from '@atproto/api' 6 7 7 8 import {getContentLanguages} from '#/state/preferences/languages' 8 - import {getAgent} from '#/state/session' 9 9 import {FeedAPI, FeedAPIResponse} from './types' 10 10 11 11 export class CustomFeedAPI implements FeedAPI { 12 - constructor(public params: GetCustomFeed.QueryParams) {} 12 + getAgent: () => BskyAgent 13 + params: GetCustomFeed.QueryParams 14 + 15 + constructor({ 16 + getAgent, 17 + feedParams, 18 + }: { 19 + getAgent: () => BskyAgent 20 + feedParams: GetCustomFeed.QueryParams 21 + }) { 22 + this.getAgent = getAgent 23 + this.params = feedParams 24 + } 13 25 14 26 async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> { 15 27 const contentLangs = getContentLanguages().join(',') 16 - const res = await getAgent().app.bsky.feed.getFeed( 28 + const res = await this.getAgent().app.bsky.feed.getFeed( 17 29 { 18 30 ...this.params, 19 31 limit: 1, ··· 31 43 limit: number 32 44 }): Promise<FeedAPIResponse> { 33 45 const contentLangs = getContentLanguages().join(',') 34 - const agent = getAgent() 46 + const agent = this.getAgent() 35 47 const res = agent.session 36 - ? await getAgent().app.bsky.feed.getFeed( 48 + ? await this.getAgent().app.bsky.feed.getFeed( 37 49 { 38 50 ...this.params, 39 51 cursor, 40 52 limit, 41 53 }, 42 - {headers: {'Accept-Language': contentLangs}}, 54 + { 55 + headers: { 56 + 'Accept-Language': contentLangs, 57 + }, 58 + }, 43 59 ) 44 60 : await loggedOutFetch({...this.params, cursor, limit}) 45 61 if (res.success) {
+9 -5
src/lib/api/feed/following.ts
··· 1 - import {AppBskyFeedDefs} from '@atproto/api' 1 + import {AppBskyFeedDefs, BskyAgent} from '@atproto/api' 2 + 2 3 import {FeedAPI, FeedAPIResponse} from './types' 3 - import {getAgent} from '#/state/session' 4 4 5 5 export class FollowingFeedAPI implements FeedAPI { 6 - constructor() {} 6 + getAgent: () => BskyAgent 7 + 8 + constructor({getAgent}: {getAgent: () => BskyAgent}) { 9 + this.getAgent = getAgent 10 + } 7 11 8 12 async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> { 9 - const res = await getAgent().getTimeline({ 13 + const res = await this.getAgent().getTimeline({ 10 14 limit: 1, 11 15 }) 12 16 return res.data.feed[0] ··· 19 23 cursor: string | undefined 20 24 limit: number 21 25 }): Promise<FeedAPIResponse> { 22 - const res = await getAgent().getTimeline({ 26 + const res = await this.getAgent().getTimeline({ 23 27 cursor, 24 28 limit, 25 29 })
+18 -9
src/lib/api/feed/home.ts
··· 1 - import {AppBskyFeedDefs} from '@atproto/api' 1 + import {AppBskyFeedDefs, BskyAgent} from '@atproto/api' 2 + 3 + import {PROD_DEFAULT_FEED} from '#/lib/constants' 4 + import {CustomFeedAPI} from './custom' 5 + import {FollowingFeedAPI} from './following' 2 6 import {FeedAPI, FeedAPIResponse} from './types' 3 - import {FollowingFeedAPI} from './following' 4 - import {CustomFeedAPI} from './custom' 5 - import {PROD_DEFAULT_FEED} from '#/lib/constants' 6 7 7 8 // HACK 8 9 // the feed API does not include any facilities for passing down ··· 26 27 } 27 28 28 29 export class HomeFeedAPI implements FeedAPI { 30 + getAgent: () => BskyAgent 29 31 following: FollowingFeedAPI 30 32 discover: CustomFeedAPI 31 33 usingDiscover = false 32 34 itemCursor = 0 33 35 34 - constructor() { 35 - this.following = new FollowingFeedAPI() 36 - this.discover = new CustomFeedAPI({feed: PROD_DEFAULT_FEED('whats-hot')}) 36 + constructor({getAgent}: {getAgent: () => BskyAgent}) { 37 + this.getAgent = getAgent 38 + this.following = new FollowingFeedAPI({getAgent}) 39 + this.discover = new CustomFeedAPI({ 40 + getAgent, 41 + feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')}, 42 + }) 37 43 } 38 44 39 45 reset() { 40 - this.following = new FollowingFeedAPI() 41 - this.discover = new CustomFeedAPI({feed: PROD_DEFAULT_FEED('whats-hot')}) 46 + this.following = new FollowingFeedAPI({getAgent: this.getAgent}) 47 + this.discover = new CustomFeedAPI({ 48 + getAgent: this.getAgent, 49 + feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')}, 50 + }) 42 51 this.usingDiscover = false 43 52 this.itemCursor = 0 44 53 }
+17 -4
src/lib/api/feed/likes.ts
··· 1 1 import { 2 2 AppBskyFeedDefs, 3 3 AppBskyFeedGetActorLikes as GetActorLikes, 4 + BskyAgent, 4 5 } from '@atproto/api' 6 + 5 7 import {FeedAPI, FeedAPIResponse} from './types' 6 - import {getAgent} from '#/state/session' 7 8 8 9 export class LikesFeedAPI implements FeedAPI { 9 - constructor(public params: GetActorLikes.QueryParams) {} 10 + getAgent: () => BskyAgent 11 + params: GetActorLikes.QueryParams 12 + 13 + constructor({ 14 + getAgent, 15 + feedParams, 16 + }: { 17 + getAgent: () => BskyAgent 18 + feedParams: GetActorLikes.QueryParams 19 + }) { 20 + this.getAgent = getAgent 21 + this.params = feedParams 22 + } 10 23 11 24 async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> { 12 - const res = await getAgent().getActorLikes({ 25 + const res = await this.getAgent().getActorLikes({ 13 26 ...this.params, 14 27 limit: 1, 15 28 }) ··· 23 36 cursor: string | undefined 24 37 limit: number 25 38 }): Promise<FeedAPIResponse> { 26 - const res = await getAgent().getActorLikes({ 39 + const res = await this.getAgent().getActorLikes({ 27 40 ...this.params, 28 41 cursor, 29 42 limit,
+17 -4
src/lib/api/feed/list.ts
··· 1 1 import { 2 2 AppBskyFeedDefs, 3 3 AppBskyFeedGetListFeed as GetListFeed, 4 + BskyAgent, 4 5 } from '@atproto/api' 6 + 5 7 import {FeedAPI, FeedAPIResponse} from './types' 6 - import {getAgent} from '#/state/session' 7 8 8 9 export class ListFeedAPI implements FeedAPI { 9 - constructor(public params: GetListFeed.QueryParams) {} 10 + getAgent: () => BskyAgent 11 + params: GetListFeed.QueryParams 12 + 13 + constructor({ 14 + getAgent, 15 + feedParams, 16 + }: { 17 + getAgent: () => BskyAgent 18 + feedParams: GetListFeed.QueryParams 19 + }) { 20 + this.getAgent = getAgent 21 + this.params = feedParams 22 + } 10 23 11 24 async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> { 12 - const res = await getAgent().app.bsky.feed.getListFeed({ 25 + const res = await this.getAgent().app.bsky.feed.getListFeed({ 13 26 ...this.params, 14 27 limit: 1, 15 28 }) ··· 23 36 cursor: string | undefined 24 37 limit: number 25 38 }): Promise<FeedAPIResponse> { 26 - const res = await getAgent().app.bsky.feed.getListFeed({ 39 + const res = await this.getAgent().app.bsky.feed.getListFeed({ 27 40 ...this.params, 28 41 cursor, 29 42 limit,
+72 -17
src/lib/api/feed/merge.ts
··· 1 - import {AppBskyFeedDefs, AppBskyFeedGetTimeline} from '@atproto/api' 1 + import {AppBskyFeedDefs, AppBskyFeedGetTimeline, BskyAgent} from '@atproto/api' 2 2 import shuffle from 'lodash.shuffle' 3 - import {timeout} from 'lib/async/timeout' 3 + 4 + import {getContentLanguages} from '#/state/preferences/languages' 5 + import {FeedParams} from '#/state/queries/post-feed' 4 6 import {bundleAsync} from 'lib/async/bundle' 7 + import {timeout} from 'lib/async/timeout' 5 8 import {feedUriToHref} from 'lib/strings/url-helpers' 6 9 import {FeedTuner} from '../feed-manip' 7 - import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types' 8 - import {FeedParams} from '#/state/queries/post-feed' 9 10 import {FeedTunerFn} from '../feed-manip' 10 - import {getAgent} from '#/state/session' 11 - import {getContentLanguages} from '#/state/preferences/languages' 11 + import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types' 12 12 13 13 const REQUEST_WAIT_MS = 500 // 500ms 14 14 const POST_AGE_CUTOFF = 60e3 * 60 * 24 // 24hours 15 15 16 16 export class MergeFeedAPI implements FeedAPI { 17 + getAgent: () => BskyAgent 18 + params: FeedParams 19 + feedTuners: FeedTunerFn[] 17 20 following: MergeFeedSource_Following 18 21 customFeeds: MergeFeedSource_Custom[] = [] 19 22 feedCursor = 0 20 23 itemCursor = 0 21 24 sampleCursor = 0 22 25 23 - constructor(public params: FeedParams, public feedTuners: FeedTunerFn[]) { 24 - this.following = new MergeFeedSource_Following(this.feedTuners) 26 + constructor({ 27 + getAgent, 28 + feedParams, 29 + feedTuners, 30 + }: { 31 + getAgent: () => BskyAgent 32 + feedParams: FeedParams 33 + feedTuners: FeedTunerFn[] 34 + }) { 35 + this.getAgent = getAgent 36 + this.params = feedParams 37 + this.feedTuners = feedTuners 38 + this.following = new MergeFeedSource_Following({ 39 + getAgent: this.getAgent, 40 + feedTuners: this.feedTuners, 41 + }) 25 42 } 26 43 27 44 reset() { 28 - this.following = new MergeFeedSource_Following(this.feedTuners) 45 + this.following = new MergeFeedSource_Following({ 46 + getAgent: this.getAgent, 47 + feedTuners: this.feedTuners, 48 + }) 29 49 this.customFeeds = [] 30 50 this.feedCursor = 0 31 51 this.itemCursor = 0 ··· 33 53 if (this.params.mergeFeedSources) { 34 54 this.customFeeds = shuffle( 35 55 this.params.mergeFeedSources.map( 36 - feedUri => new MergeFeedSource_Custom(feedUri, this.feedTuners), 56 + feedUri => 57 + new MergeFeedSource_Custom({ 58 + getAgent: this.getAgent, 59 + feedUri, 60 + feedTuners: this.feedTuners, 61 + }), 37 62 ), 38 63 ) 39 64 } else { ··· 42 67 } 43 68 44 69 async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> { 45 - const res = await getAgent().getTimeline({ 70 + const res = await this.getAgent().getTimeline({ 46 71 limit: 1, 47 72 }) 48 73 return res.data.feed[0] ··· 136 161 } 137 162 138 163 class MergeFeedSource { 164 + getAgent: () => BskyAgent 165 + feedTuners: FeedTunerFn[] 139 166 sourceInfo: ReasonFeedSource | undefined 140 167 cursor: string | undefined = undefined 141 168 queue: AppBskyFeedDefs.FeedViewPost[] = [] 142 169 hasMore = true 143 170 144 - constructor(public feedTuners: FeedTunerFn[]) {} 171 + constructor({ 172 + getAgent, 173 + feedTuners, 174 + }: { 175 + getAgent: () => BskyAgent 176 + feedTuners: FeedTunerFn[] 177 + }) { 178 + this.getAgent = getAgent 179 + this.feedTuners = feedTuners 180 + } 145 181 146 182 get numReady() { 147 183 return this.queue.length ··· 203 239 cursor: string | undefined, 204 240 limit: number, 205 241 ): Promise<AppBskyFeedGetTimeline.Response> { 206 - const res = await getAgent().getTimeline({cursor, limit}) 242 + const res = await this.getAgent().getTimeline({cursor, limit}) 207 243 // run the tuner pre-emptively to ensure better mixing 208 244 const slices = this.tuner.tune(res.data.feed, { 209 245 dryRun: false, ··· 215 251 } 216 252 217 253 class MergeFeedSource_Custom extends MergeFeedSource { 254 + getAgent: () => BskyAgent 218 255 minDate: Date 256 + feedUri: string 219 257 220 - constructor(public feedUri: string, public feedTuners: FeedTunerFn[]) { 221 - super(feedTuners) 258 + constructor({ 259 + getAgent, 260 + feedUri, 261 + feedTuners, 262 + }: { 263 + getAgent: () => BskyAgent 264 + feedUri: string 265 + feedTuners: FeedTunerFn[] 266 + }) { 267 + super({ 268 + getAgent, 269 + feedTuners, 270 + }) 271 + this.getAgent = getAgent 272 + this.feedUri = feedUri 222 273 this.sourceInfo = { 223 274 $type: 'reasonFeedSource', 224 275 uri: feedUri, ··· 233 284 ): Promise<AppBskyFeedGetTimeline.Response> { 234 285 try { 235 286 const contentLangs = getContentLanguages().join(',') 236 - const res = await getAgent().app.bsky.feed.getFeed( 287 + const res = await this.getAgent().app.bsky.feed.getFeed( 237 288 { 238 289 cursor, 239 290 limit, 240 291 feed: this.feedUri, 241 292 }, 242 - {headers: {'Accept-Language': contentLangs}}, 293 + { 294 + headers: { 295 + 'Accept-Language': contentLangs, 296 + }, 297 + }, 243 298 ) 244 299 // NOTE 245 300 // some custom feeds fail to enforce the pagination limit
+30 -16
src/state/queries/post-feed.ts
··· 135 135 queryKey: RQKEY(feedDesc, params), 136 136 async queryFn({pageParam}: {pageParam: RQPageParam}) { 137 137 logger.debug('usePostFeedQuery', {feedDesc, cursor: pageParam?.cursor}) 138 - 139 138 const {api, cursor} = pageParam 140 139 ? pageParam 141 140 : { 142 - api: createApi(feedDesc, params || {}, feedTuners), 141 + api: createApi({ 142 + feedDesc, 143 + feedParams: params || {}, 144 + feedTuners, 145 + }), 143 146 cursor: undefined, 144 147 } 145 148 ··· 365 368 return false 366 369 } 367 370 368 - function createApi( 369 - feedDesc: FeedDescriptor, 370 - params: FeedParams, 371 - feedTuners: FeedTunerFn[], 372 - ) { 371 + function createApi({ 372 + feedDesc, 373 + feedParams, 374 + feedTuners, 375 + }: { 376 + feedDesc: FeedDescriptor 377 + feedParams: FeedParams 378 + feedTuners: FeedTunerFn[] 379 + }) { 373 380 if (feedDesc === 'home') { 374 - if (params.mergeFeedEnabled) { 375 - return new MergeFeedAPI(params, feedTuners) 381 + if (feedParams.mergeFeedEnabled) { 382 + return new MergeFeedAPI({ 383 + getAgent, 384 + feedParams, 385 + feedTuners, 386 + }) 376 387 } else { 377 - return new HomeFeedAPI() 388 + return new HomeFeedAPI({getAgent}) 378 389 } 379 390 } else if (feedDesc === 'following') { 380 - return new FollowingFeedAPI() 391 + return new FollowingFeedAPI({getAgent}) 381 392 } else if (feedDesc.startsWith('author')) { 382 393 const [_, actor, filter] = feedDesc.split('|') 383 - return new AuthorFeedAPI({actor, filter}) 394 + return new AuthorFeedAPI({getAgent, feedParams: {actor, filter}}) 384 395 } else if (feedDesc.startsWith('likes')) { 385 396 const [_, actor] = feedDesc.split('|') 386 - return new LikesFeedAPI({actor}) 397 + return new LikesFeedAPI({getAgent, feedParams: {actor}}) 387 398 } else if (feedDesc.startsWith('feedgen')) { 388 399 const [_, feed] = feedDesc.split('|') 389 - return new CustomFeedAPI({feed}) 400 + return new CustomFeedAPI({ 401 + getAgent, 402 + feedParams: {feed}, 403 + }) 390 404 } else if (feedDesc.startsWith('list')) { 391 405 const [_, list] = feedDesc.split('|') 392 - return new ListFeedAPI({list}) 406 + return new ListFeedAPI({getAgent, feedParams: {list}}) 393 407 } else { 394 408 // shouldnt happen 395 - return new FollowingFeedAPI() 409 + return new FollowingFeedAPI({getAgent}) 396 410 } 397 411 } 398 412