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
This commit is contained in:
parent
d1cb74febe
commit
e699df21c6
11 changed files with 370 additions and 485 deletions
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
32
src/state/queries/profile-followers.ts
Normal file
32
src/state/queries/profile-followers.ts
Normal file
|
@ -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,
|
||||
})
|
||||
}
|
32
src/state/queries/profile-follows.ts
Normal file
32
src/state/queries/profile-follows.ts
Normal file
|
@ -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,
|
||||
})
|
||||
}
|
|
@ -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()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue