about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2023-11-15 01:55:54 +0000
committerGitHub <noreply@github.com>2023-11-14 17:55:54 -0800
commite699df21c66f2f55d34af4d2a14c03d02274b43e (patch)
tree3112b234b0870ababc3eb8c0834d90bb61bf8fee /src/state
parentd1cb74febea9725b028dca372e1b7d7e157ad24d (diff)
downloadvoidsky-e699df21c66f2f55d34af4d2a14c03d02274b43e.tar.zst
Port Profile Followers/Follows to RQ (#1893)
* Port user followers to RQ

* Port user follows to RQ

* Start porting FollowButton to RQ

* Fix RQ key

* Check pending

* Fix shadow and pending states

* Rm unused

* Remove last usage of useFollowProfile
Diffstat (limited to 'src/state')
-rw-r--r--src/state/models/lists/user-followers.ts121
-rw-r--r--src/state/models/lists/user-follows.ts121
-rw-r--r--src/state/queries/profile-followers.ts32
-rw-r--r--src/state/queries/profile-follows.ts32
-rw-r--r--src/state/queries/suggested-follows.ts28
5 files changed, 90 insertions, 244 deletions
diff --git a/src/state/models/lists/user-followers.ts b/src/state/models/lists/user-followers.ts
deleted file mode 100644
index 159308b9b..000000000
--- a/src/state/models/lists/user-followers.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import {makeAutoObservable} from 'mobx'
-import {
-  AppBskyGraphGetFollowers as GetFollowers,
-  AppBskyActorDefs as ActorDefs,
-} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {cleanError} from 'lib/strings/errors'
-import {bundleAsync} from 'lib/async/bundle'
-import {logger} from '#/logger'
-
-const PAGE_SIZE = 30
-
-export type FollowerItem = ActorDefs.ProfileViewBasic
-
-export class UserFollowersModel {
-  // state
-  isLoading = false
-  isRefreshing = false
-  hasLoaded = false
-  error = ''
-  params: GetFollowers.QueryParams
-  hasMore = true
-  loadMoreCursor?: string
-
-  // data
-  subject: ActorDefs.ProfileViewBasic = {
-    did: '',
-    handle: '',
-  }
-  followers: FollowerItem[] = []
-
-  constructor(
-    public rootStore: RootStoreModel,
-    params: GetFollowers.QueryParams,
-  ) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-        params: false,
-      },
-      {autoBind: true},
-    )
-    this.params = params
-  }
-
-  get hasContent() {
-    return this.subject.did !== ''
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  // public api
-  // =
-
-  async refresh() {
-    return this.loadMore(true)
-  }
-
-  loadMore = bundleAsync(async (replace: boolean = false) => {
-    if (!replace && !this.hasMore) {
-      return
-    }
-    this._xLoading(replace)
-    try {
-      const params = Object.assign({}, this.params, {
-        limit: PAGE_SIZE,
-        cursor: replace ? undefined : this.loadMoreCursor,
-      })
-      const res = await this.rootStore.agent.getFollowers(params)
-      if (replace) {
-        this._replaceAll(res)
-      } else {
-        this._appendAll(res)
-      }
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(e)
-    }
-  })
-
-  // state transitions
-  // =
-
-  _xLoading(isRefreshing = false) {
-    this.isLoading = true
-    this.isRefreshing = isRefreshing
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      logger.error('Failed to fetch user followers', {error: err})
-    }
-  }
-
-  // helper functions
-  // =
-
-  _replaceAll(res: GetFollowers.Response) {
-    this.followers = []
-    this._appendAll(res)
-  }
-
-  _appendAll(res: GetFollowers.Response) {
-    this.loadMoreCursor = res.data.cursor
-    this.hasMore = !!this.loadMoreCursor
-    this.followers = this.followers.concat(res.data.followers)
-    this.rootStore.me.follows.hydrateMany(res.data.followers)
-  }
-}
diff --git a/src/state/models/lists/user-follows.ts b/src/state/models/lists/user-follows.ts
deleted file mode 100644
index 3abbbaf95..000000000
--- a/src/state/models/lists/user-follows.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import {makeAutoObservable} from 'mobx'
-import {
-  AppBskyGraphGetFollows as GetFollows,
-  AppBskyActorDefs as ActorDefs,
-} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {cleanError} from 'lib/strings/errors'
-import {bundleAsync} from 'lib/async/bundle'
-import {logger} from '#/logger'
-
-const PAGE_SIZE = 30
-
-export type FollowItem = ActorDefs.ProfileViewBasic
-
-export class UserFollowsModel {
-  // state
-  isLoading = false
-  isRefreshing = false
-  hasLoaded = false
-  error = ''
-  params: GetFollows.QueryParams
-  hasMore = true
-  loadMoreCursor?: string
-
-  // data
-  subject: ActorDefs.ProfileViewBasic = {
-    did: '',
-    handle: '',
-  }
-  follows: FollowItem[] = []
-
-  constructor(
-    public rootStore: RootStoreModel,
-    params: GetFollows.QueryParams,
-  ) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-        params: false,
-      },
-      {autoBind: true},
-    )
-    this.params = params
-  }
-
-  get hasContent() {
-    return this.subject.did !== ''
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  // public api
-  // =
-
-  async refresh() {
-    return this.loadMore(true)
-  }
-
-  loadMore = bundleAsync(async (replace: boolean = false) => {
-    if (!replace && !this.hasMore) {
-      return
-    }
-    this._xLoading(replace)
-    try {
-      const params = Object.assign({}, this.params, {
-        limit: PAGE_SIZE,
-        cursor: replace ? undefined : this.loadMoreCursor,
-      })
-      const res = await this.rootStore.agent.getFollows(params)
-      if (replace) {
-        this._replaceAll(res)
-      } else {
-        this._appendAll(res)
-      }
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(e)
-    }
-  })
-
-  // state transitions
-  // =
-
-  _xLoading(isRefreshing = false) {
-    this.isLoading = true
-    this.isRefreshing = isRefreshing
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.isRefreshing = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      logger.error('Failed to fetch user follows', err)
-    }
-  }
-
-  // helper functions
-  // =
-
-  _replaceAll(res: GetFollows.Response) {
-    this.follows = []
-    this._appendAll(res)
-  }
-
-  _appendAll(res: GetFollows.Response) {
-    this.loadMoreCursor = res.data.cursor
-    this.hasMore = !!this.loadMoreCursor
-    this.follows = this.follows.concat(res.data.follows)
-    this.rootStore.me.follows.hydrateMany(res.data.follows)
-  }
-}
diff --git a/src/state/queries/profile-followers.ts b/src/state/queries/profile-followers.ts
new file mode 100644
index 000000000..8e76a20a0
--- /dev/null
+++ b/src/state/queries/profile-followers.ts
@@ -0,0 +1,32 @@
+import {AppBskyGraphGetFollowers} from '@atproto/api'
+import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
+import {useSession} from '../session'
+
+const PAGE_SIZE = 30
+type RQPageParam = string | undefined
+
+export const RQKEY = (did: string) => ['profile-followers', did]
+
+export function useProfileFollowersQuery(did: string | undefined) {
+  const {agent} = useSession()
+  return useInfiniteQuery<
+    AppBskyGraphGetFollowers.OutputSchema,
+    Error,
+    InfiniteData<AppBskyGraphGetFollowers.OutputSchema>,
+    QueryKey,
+    RQPageParam
+  >({
+    queryKey: RQKEY(did || ''),
+    async queryFn({pageParam}: {pageParam: RQPageParam}) {
+      const res = await agent.app.bsky.graph.getFollowers({
+        actor: did || '',
+        limit: PAGE_SIZE,
+        cursor: pageParam,
+      })
+      return res.data
+    },
+    initialPageParam: undefined,
+    getNextPageParam: lastPage => lastPage.cursor,
+    enabled: !!did,
+  })
+}
diff --git a/src/state/queries/profile-follows.ts b/src/state/queries/profile-follows.ts
new file mode 100644
index 000000000..f96cfc107
--- /dev/null
+++ b/src/state/queries/profile-follows.ts
@@ -0,0 +1,32 @@
+import {AppBskyGraphGetFollows} from '@atproto/api'
+import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
+import {useSession} from '../session'
+
+const PAGE_SIZE = 30
+type RQPageParam = string | undefined
+
+export const RQKEY = (did: string) => ['profile-follows', did]
+
+export function useProfileFollowsQuery(did: string | undefined) {
+  const {agent} = useSession()
+  return useInfiniteQuery<
+    AppBskyGraphGetFollows.OutputSchema,
+    Error,
+    InfiniteData<AppBskyGraphGetFollows.OutputSchema>,
+    QueryKey,
+    RQPageParam
+  >({
+    queryKey: RQKEY(did || ''),
+    async queryFn({pageParam}: {pageParam: RQPageParam}) {
+      const res = await agent.app.bsky.graph.getFollows({
+        actor: did || '',
+        limit: PAGE_SIZE,
+        cursor: pageParam,
+      })
+      return res.data
+    },
+    initialPageParam: undefined,
+    getNextPageParam: lastPage => lastPage.cursor,
+    enabled: !!did,
+  })
+}
diff --git a/src/state/queries/suggested-follows.ts b/src/state/queries/suggested-follows.ts
index 805668bcb..5b5e142ca 100644
--- a/src/state/queries/suggested-follows.ts
+++ b/src/state/queries/suggested-follows.ts
@@ -1,7 +1,12 @@
-import {AppBskyActorGetSuggestions, moderateProfile} from '@atproto/api'
+import {
+  AppBskyActorGetSuggestions,
+  AppBskyGraphGetSuggestedFollowsByActor,
+  moderateProfile,
+} from '@atproto/api'
 import {
   useInfiniteQuery,
   useMutation,
+  useQuery,
   InfiniteData,
   QueryKey,
 } from '@tanstack/react-query'
@@ -9,7 +14,11 @@ import {
 import {useSession} from '#/state/session'
 import {useModerationOpts} from '#/state/queries/preferences'
 
-export const suggestedFollowsQueryKey = ['suggested-follows']
+const suggestedFollowsQueryKey = ['suggested-follows']
+const suggestedFollowsByActorQuery = (did: string) => [
+  'suggested-follows-by-actor',
+  did,
+]
 
 export function useSuggestedFollowsQuery() {
   const {agent, currentAccount} = useSession()
@@ -60,6 +69,21 @@ export function useSuggestedFollowsQuery() {
   })
 }
 
+export function useSuggestedFollowsByActorQuery({did}: {did: string}) {
+  const {agent} = useSession()
+
+  return useQuery<AppBskyGraphGetSuggestedFollowsByActor.OutputSchema, Error>({
+    queryKey: suggestedFollowsByActorQuery(did),
+    queryFn: async () => {
+      const res = await agent.app.bsky.graph.getSuggestedFollowsByActor({
+        actor: did,
+      })
+      return res.data
+    },
+  })
+}
+
+// TODO: Delete and replace usages with the one above.
 export function useGetSuggestedFollowersByActor() {
   const {agent} = useSession()