From 6fe2b52f6860916a62bf9a4d680a0a3b91b50d91 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Wed, 29 Nov 2023 10:10:04 -0800 Subject: [PATCH] Get more rigorous about getAgent() consistency (#2026) * Get more rigorous about getAgent() consistency * Update the feed wrapper API to use getAgent() directly --- src/lib/api/feed/author.ts | 11 +++--- src/lib/api/feed/custom.ts | 11 +++--- src/lib/api/feed/following.ts | 9 ++--- src/lib/api/feed/likes.ts | 11 +++--- src/lib/api/feed/list.ts | 11 +++--- src/lib/api/feed/merge.ts | 36 ++++++++----------- src/state/queries/list.ts | 40 +++++++--------------- src/state/queries/notifications/feed.ts | 8 ++--- src/state/queries/notifications/unread.tsx | 6 ++-- src/state/queries/post-feed.ts | 18 +++++----- src/state/queries/profile.ts | 6 ++-- src/state/session/index.tsx | 6 ++++ 12 files changed, 70 insertions(+), 103 deletions(-) diff --git a/src/lib/api/feed/author.ts b/src/lib/api/feed/author.ts index 77c16786..92df84f8 100644 --- a/src/lib/api/feed/author.ts +++ b/src/lib/api/feed/author.ts @@ -1,18 +1,15 @@ import { AppBskyFeedDefs, AppBskyFeedGetAuthorFeed as GetAuthorFeed, - BskyAgent, } from '@atproto/api' import {FeedAPI, FeedAPIResponse} from './types' +import {getAgent} from '#/state/session' export class AuthorFeedAPI implements FeedAPI { - constructor( - public agent: BskyAgent, - public params: GetAuthorFeed.QueryParams, - ) {} + constructor(public params: GetAuthorFeed.QueryParams) {} async peekLatest(): Promise { - const res = await this.agent.getAuthorFeed({ + const res = await getAgent().getAuthorFeed({ ...this.params, limit: 1, }) @@ -26,7 +23,7 @@ export class AuthorFeedAPI implements FeedAPI { cursor: string | undefined limit: number }): Promise { - const res = await this.agent.getAuthorFeed({ + const res = await getAgent().getAuthorFeed({ ...this.params, cursor, limit, diff --git a/src/lib/api/feed/custom.ts b/src/lib/api/feed/custom.ts index 0be98fb4..47ffc65e 100644 --- a/src/lib/api/feed/custom.ts +++ b/src/lib/api/feed/custom.ts @@ -1,18 +1,15 @@ import { AppBskyFeedDefs, AppBskyFeedGetFeed as GetCustomFeed, - BskyAgent, } from '@atproto/api' import {FeedAPI, FeedAPIResponse} from './types' +import {getAgent} from '#/state/session' export class CustomFeedAPI implements FeedAPI { - constructor( - public agent: BskyAgent, - public params: GetCustomFeed.QueryParams, - ) {} + constructor(public params: GetCustomFeed.QueryParams) {} async peekLatest(): Promise { - const res = await this.agent.app.bsky.feed.getFeed({ + const res = await getAgent().app.bsky.feed.getFeed({ ...this.params, limit: 1, }) @@ -26,7 +23,7 @@ export class CustomFeedAPI implements FeedAPI { cursor: string | undefined limit: number }): Promise { - const res = await this.agent.app.bsky.feed.getFeed({ + const res = await getAgent().app.bsky.feed.getFeed({ ...this.params, cursor, limit, diff --git a/src/lib/api/feed/following.ts b/src/lib/api/feed/following.ts index 13f06c7a..24389b5e 100644 --- a/src/lib/api/feed/following.ts +++ b/src/lib/api/feed/following.ts @@ -1,11 +1,12 @@ -import {AppBskyFeedDefs, BskyAgent} from '@atproto/api' +import {AppBskyFeedDefs} from '@atproto/api' import {FeedAPI, FeedAPIResponse} from './types' +import {getAgent} from '#/state/session' export class FollowingFeedAPI implements FeedAPI { - constructor(public agent: BskyAgent) {} + constructor() {} async peekLatest(): Promise { - const res = await this.agent.getTimeline({ + const res = await getAgent().getTimeline({ limit: 1, }) return res.data.feed[0] @@ -18,7 +19,7 @@ export class FollowingFeedAPI implements FeedAPI { cursor: string | undefined limit: number }): Promise { - const res = await this.agent.getTimeline({ + const res = await getAgent().getTimeline({ cursor, limit, }) diff --git a/src/lib/api/feed/likes.ts b/src/lib/api/feed/likes.ts index 434ed771..2b0afdf1 100644 --- a/src/lib/api/feed/likes.ts +++ b/src/lib/api/feed/likes.ts @@ -1,18 +1,15 @@ import { AppBskyFeedDefs, AppBskyFeedGetActorLikes as GetActorLikes, - BskyAgent, } from '@atproto/api' import {FeedAPI, FeedAPIResponse} from './types' +import {getAgent} from '#/state/session' export class LikesFeedAPI implements FeedAPI { - constructor( - public agent: BskyAgent, - public params: GetActorLikes.QueryParams, - ) {} + constructor(public params: GetActorLikes.QueryParams) {} async peekLatest(): Promise { - const res = await this.agent.getActorLikes({ + const res = await getAgent().getActorLikes({ ...this.params, limit: 1, }) @@ -26,7 +23,7 @@ export class LikesFeedAPI implements FeedAPI { cursor: string | undefined limit: number }): Promise { - const res = await this.agent.getActorLikes({ + const res = await getAgent().getActorLikes({ ...this.params, cursor, limit, diff --git a/src/lib/api/feed/list.ts b/src/lib/api/feed/list.ts index 6cb0730e..19f2ff17 100644 --- a/src/lib/api/feed/list.ts +++ b/src/lib/api/feed/list.ts @@ -1,18 +1,15 @@ import { AppBskyFeedDefs, AppBskyFeedGetListFeed as GetListFeed, - BskyAgent, } from '@atproto/api' import {FeedAPI, FeedAPIResponse} from './types' +import {getAgent} from '#/state/session' export class ListFeedAPI implements FeedAPI { - constructor( - public agent: BskyAgent, - public params: GetListFeed.QueryParams, - ) {} + constructor(public params: GetListFeed.QueryParams) {} async peekLatest(): Promise { - const res = await this.agent.app.bsky.feed.getListFeed({ + const res = await getAgent().app.bsky.feed.getListFeed({ ...this.params, limit: 1, }) @@ -26,7 +23,7 @@ export class ListFeedAPI implements FeedAPI { cursor: string | undefined limit: number }): Promise { - const res = await this.agent.app.bsky.feed.getListFeed({ + const res = await getAgent().app.bsky.feed.getListFeed({ ...this.params, cursor, limit, diff --git a/src/lib/api/feed/merge.ts b/src/lib/api/feed/merge.ts index 7a0f0288..bc1b0883 100644 --- a/src/lib/api/feed/merge.ts +++ b/src/lib/api/feed/merge.ts @@ -1,4 +1,4 @@ -import {AppBskyFeedDefs, AppBskyFeedGetTimeline, BskyAgent} from '@atproto/api' +import {AppBskyFeedDefs, AppBskyFeedGetTimeline} from '@atproto/api' import shuffle from 'lodash.shuffle' import {timeout} from 'lib/async/timeout' import {bundleAsync} from 'lib/async/bundle' @@ -7,6 +7,7 @@ import {FeedTuner} from '../feed-manip' import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types' import {FeedParams} from '#/state/queries/post-feed' import {FeedTunerFn} from '../feed-manip' +import {getAgent} from '#/state/session' const REQUEST_WAIT_MS = 500 // 500ms const POST_AGE_CUTOFF = 60e3 * 60 * 24 // 24hours @@ -18,16 +19,12 @@ export class MergeFeedAPI implements FeedAPI { itemCursor = 0 sampleCursor = 0 - constructor( - public agent: BskyAgent, - public params: FeedParams, - public feedTuners: FeedTunerFn[], - ) { - this.following = new MergeFeedSource_Following(this.agent, this.feedTuners) + constructor(public params: FeedParams, public feedTuners: FeedTunerFn[]) { + this.following = new MergeFeedSource_Following(this.feedTuners) } reset() { - this.following = new MergeFeedSource_Following(this.agent, this.feedTuners) + this.following = new MergeFeedSource_Following(this.feedTuners) this.customFeeds = [] // just empty the array, they will be captured in _fetchNext() this.feedCursor = 0 this.itemCursor = 0 @@ -35,8 +32,7 @@ export class MergeFeedAPI implements FeedAPI { if (this.params.mergeFeedEnabled && this.params.mergeFeedSources) { this.customFeeds = shuffle( this.params.mergeFeedSources.map( - feedUri => - new MergeFeedSource_Custom(this.agent, feedUri, this.feedTuners), + feedUri => new MergeFeedSource_Custom(feedUri, this.feedTuners), ), ) } else { @@ -45,7 +41,7 @@ export class MergeFeedAPI implements FeedAPI { } async peekLatest(): Promise { - const res = await this.agent.getTimeline({ + const res = await getAgent().getTimeline({ limit: 1, }) return res.data.feed[0] @@ -137,7 +133,7 @@ class MergeFeedSource { queue: AppBskyFeedDefs.FeedViewPost[] = [] hasMore = true - constructor(public agent: BskyAgent, public feedTuners: FeedTunerFn[]) {} + constructor(public feedTuners: FeedTunerFn[]) {} get numReady() { return this.queue.length @@ -199,7 +195,7 @@ class MergeFeedSource_Following extends MergeFeedSource { cursor: string | undefined, limit: number, ): Promise { - const res = await this.agent.getTimeline({cursor, limit}) + const res = await getAgent().getTimeline({cursor, limit}) // run the tuner pre-emptively to ensure better mixing const slices = this.tuner.tune(res.data.feed, this.feedTuners, { dryRun: false, @@ -213,20 +209,16 @@ class MergeFeedSource_Following extends MergeFeedSource { class MergeFeedSource_Custom extends MergeFeedSource { minDate: Date - constructor( - public agent: BskyAgent, - public feedUri: string, - public feedTuners: FeedTunerFn[], - ) { - super(agent, feedTuners) + constructor(public feedUri: string, public feedTuners: FeedTunerFn[]) { + super(feedTuners) this.sourceInfo = { $type: 'reasonFeedSource', displayName: feedUri.split('/').pop() || '', uri: feedUriToHref(feedUri), } this.minDate = new Date(Date.now() - POST_AGE_CUTOFF) - this.agent.app.bsky.feed - .getFeedGenerator({ + getAgent() + .app.bsky.feed.getFeedGenerator({ feed: feedUri, }) .then( @@ -244,7 +236,7 @@ class MergeFeedSource_Custom extends MergeFeedSource { limit: number, ): Promise { try { - const res = await this.agent.app.bsky.feed.getFeed({ + const res = await getAgent().app.bsky.feed.getFeed({ cursor, limit, feed: this.feedUri, diff --git a/src/state/queries/list.ts b/src/state/queries/list.ts index ef05009d..550baecb 100644 --- a/src/state/queries/list.ts +++ b/src/state/queries/list.ts @@ -3,7 +3,6 @@ import { AppBskyGraphGetList, AppBskyGraphList, AppBskyGraphDefs, - BskyAgent, } from '@atproto/api' import {Image as RNImage} from 'react-native-image-crop-picker' import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query' @@ -75,13 +74,9 @@ export function useListCreateMutation() { ) // wait for the appview to update - await whenAppViewReady( - getAgent(), - res.uri, - (v: AppBskyGraphGetList.Response) => { - return typeof v?.data?.list.uri === 'string' - }, - ) + await whenAppViewReady(res.uri, (v: AppBskyGraphGetList.Response) => { + return typeof v?.data?.list.uri === 'string' + }) return res }, onSuccess() { @@ -142,16 +137,12 @@ export function useListMetadataMutation() { ).data // wait for the appview to update - await whenAppViewReady( - getAgent(), - res.uri, - (v: AppBskyGraphGetList.Response) => { - const list = v.data.list - return ( - list.name === record.name && list.description === record.description - ) - }, - ) + await whenAppViewReady(res.uri, (v: AppBskyGraphGetList.Response) => { + const list = v.data.list + return ( + list.name === record.name && list.description === record.description + ) + }) return res }, onSuccess(data, variables) { @@ -216,13 +207,9 @@ export function useListDeleteMutation() { } // wait for the appview to update - await whenAppViewReady( - getAgent(), - uri, - (v: AppBskyGraphGetList.Response) => { - return !v?.success - }, - ) + await whenAppViewReady(uri, (v: AppBskyGraphGetList.Response) => { + return !v?.success + }) }, onSuccess() { invalidateMyLists(queryClient) @@ -271,7 +258,6 @@ export function useListBlockMutation() { } async function whenAppViewReady( - agent: BskyAgent, uri: string, fn: (res: AppBskyGraphGetList.Response) => boolean, ) { @@ -280,7 +266,7 @@ async function whenAppViewReady( 1e3, // 1s delay between tries fn, () => - agent.app.bsky.graph.getList({ + getAgent().app.bsky.graph.getList({ list: uri, limit: 1, }), diff --git a/src/state/queries/notifications/feed.ts b/src/state/queries/notifications/feed.ts index 54bd8754..68396143 100644 --- a/src/state/queries/notifications/feed.ts +++ b/src/state/queries/notifications/feed.ts @@ -4,7 +4,6 @@ import { AppBskyFeedRepost, AppBskyFeedLike, AppBskyNotificationListNotifications, - BskyAgent, } from '@atproto/api' import chunk from 'lodash.chunk' import { @@ -84,7 +83,7 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) { // we fetch subjects of notifications (usually posts) now instead of lazily // in the UI to avoid relayouts - const subjects = await fetchSubjects(getAgent(), notifsGrouped) + const subjects = await fetchSubjects(notifsGrouped) for (const notif of notifsGrouped) { if (notif.subjectUri) { notif.subject = subjects.get(notif.subjectUri) @@ -173,7 +172,6 @@ function groupNotifications( } async function fetchSubjects( - agent: BskyAgent, groupedNotifs: FeedNotification[], ): Promise> { const uris = new Set() @@ -185,7 +183,9 @@ async function fetchSubjects( const uriChunks = chunk(Array.from(uris), 25) const postsChunks = await Promise.all( uriChunks.map(uris => - agent.app.bsky.feed.getPosts({uris}).then(res => res.data.posts), + getAgent() + .app.bsky.feed.getPosts({uris}) + .then(res => res.data.posts), ), ) const map = new Map() diff --git a/src/state/queries/notifications/unread.tsx b/src/state/queries/notifications/unread.tsx index 36bc6528..b93e1dc8 100644 --- a/src/state/queries/notifications/unread.tsx +++ b/src/state/queries/notifications/unread.tsx @@ -70,12 +70,10 @@ export function Provider({children}: React.PropsWithChildren<{}>) { }, async checkUnread() { - const agent = getAgent() - - if (!agent.session) return + if (!getAgent().session) return // count - const res = await agent.listNotifications({limit: 40}) + const res = await getAgent().listNotifications({limit: 40}) const filtered = res.data.notifications.filter( notif => !notif.isRead && !shouldFilterNotif(notif, moderationOpts), ) diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts index 113e6f2f..c3f0c758 100644 --- a/src/state/queries/post-feed.ts +++ b/src/state/queries/post-feed.ts @@ -7,7 +7,6 @@ import { QueryClient, useQueryClient, } from '@tanstack/react-query' -import {getAgent} from '../session' import {useFeedTuners} from '../preferences/feed-tuners' import {FeedTuner, NoopFeedTuner} from 'lib/api/feed-manip' import {FeedAPI, ReasonFeedSource} from 'lib/api/feed/types' @@ -77,30 +76,29 @@ export function usePostFeedQuery( const feedTuners = useFeedTuners(feedDesc) const enabled = opts?.enabled !== false const moderationOpts = useModerationOpts() - const agent = getAgent() const api: FeedAPI = useMemo(() => { if (feedDesc === 'home') { - return new MergeFeedAPI(agent, params || {}, feedTuners) + return new MergeFeedAPI(params || {}, feedTuners) } else if (feedDesc === 'following') { - return new FollowingFeedAPI(agent) + return new FollowingFeedAPI() } else if (feedDesc.startsWith('author')) { const [_, actor, filter] = feedDesc.split('|') - return new AuthorFeedAPI(agent, {actor, filter}) + return new AuthorFeedAPI({actor, filter}) } else if (feedDesc.startsWith('likes')) { const [_, actor] = feedDesc.split('|') - return new LikesFeedAPI(agent, {actor}) + return new LikesFeedAPI({actor}) } else if (feedDesc.startsWith('feedgen')) { const [_, feed] = feedDesc.split('|') - return new CustomFeedAPI(agent, {feed}) + return new CustomFeedAPI({feed}) } else if (feedDesc.startsWith('list')) { const [_, list] = feedDesc.split('|') - return new ListFeedAPI(agent, {list}) + return new ListFeedAPI({list}) } else { // shouldnt happen - return new FollowingFeedAPI(agent) + return new FollowingFeedAPI() } - }, [feedDesc, params, feedTuners, agent]) + }, [feedDesc, params, feedTuners]) const disableTuner = !!params?.disableTuner const tuner = useMemo( diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts index e27bac9a..9467a255 100644 --- a/src/state/queries/profile.ts +++ b/src/state/queries/profile.ts @@ -4,7 +4,6 @@ import { AppBskyActorDefs, AppBskyActorProfile, AppBskyActorGetProfile, - BskyAgent, } from '@atproto/api' import {useQuery, useQueryClient, useMutation} from '@tanstack/react-query' import {Image as RNImage} from 'react-native-image-crop-picker' @@ -68,7 +67,7 @@ export function useProfileUpdateMutation() { } return existing }) - await whenAppViewReady(getAgent(), profile.did, res => { + await whenAppViewReady(profile.did, res => { if (typeof newUserAvatar !== 'undefined') { if (newUserAvatar === null && res.data.avatar) { // url hasnt cleared yet @@ -464,7 +463,6 @@ function useProfileUnblockMutation() { } async function whenAppViewReady( - agent: BskyAgent, actor: string, fn: (res: AppBskyActorGetProfile.Response) => boolean, ) { @@ -472,6 +470,6 @@ async function whenAppViewReady( 5, // 5 tries 1e3, // 1s delay between tries fn, - () => agent.app.bsky.actor.getProfile({actor}), + () => getAgent().app.bsky.actor.getProfile({actor}), ) } diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx index 946c742a..e6def1fa 100644 --- a/src/state/session/index.tsx +++ b/src/state/session/index.tsx @@ -13,6 +13,12 @@ import {useCloseAllActiveElements} from '#/state/util' let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT +/** + * NOTE + * Never hold on to the object returned by this function. + * Call `getAgent()` at the time of invocation to ensure + * that you never have a stale agent. + */ export function getAgent() { return __globalAgent }