Update Muted and Blocked accounts screens (react-query refactor) (#1892)
* Add my-blocked-accounts and my-muted-accounts queries * Update ProfileCard to use the profile shadow cache and useModerationOpts * Update blocked accounts and muted accounts screens
This commit is contained in:
parent
0501c2be77
commit
a81c4b68fa
9 changed files with 212 additions and 289 deletions
|
@ -1,107 +0,0 @@
|
|||
import {makeAutoObservable} from 'mobx'
|
||||
import {
|
||||
AppBskyGraphGetBlocks as GetBlocks,
|
||||
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 class BlockedAccountsModel {
|
||||
// state
|
||||
isLoading = false
|
||||
isRefreshing = false
|
||||
hasLoaded = false
|
||||
error = ''
|
||||
hasMore = true
|
||||
loadMoreCursor?: string
|
||||
|
||||
// data
|
||||
blocks: ActorDefs.ProfileView[] = []
|
||||
|
||||
constructor(public rootStore: RootStoreModel) {
|
||||
makeAutoObservable(
|
||||
this,
|
||||
{
|
||||
rootStore: false,
|
||||
},
|
||||
{autoBind: true},
|
||||
)
|
||||
}
|
||||
|
||||
get hasContent() {
|
||||
return this.blocks.length > 0
|
||||
}
|
||||
|
||||
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 res = await this.rootStore.agent.app.bsky.graph.getBlocks({
|
||||
limit: PAGE_SIZE,
|
||||
cursor: replace ? undefined : this.loadMoreCursor,
|
||||
})
|
||||
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: GetBlocks.Response) {
|
||||
this.blocks = []
|
||||
this._appendAll(res)
|
||||
}
|
||||
|
||||
_appendAll(res: GetBlocks.Response) {
|
||||
this.loadMoreCursor = res.data.cursor
|
||||
this.hasMore = !!this.loadMoreCursor
|
||||
this.blocks = this.blocks.concat(res.data.blocks)
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
import {makeAutoObservable} from 'mobx'
|
||||
import {
|
||||
AppBskyGraphGetMutes as GetMutes,
|
||||
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 class MutedAccountsModel {
|
||||
// state
|
||||
isLoading = false
|
||||
isRefreshing = false
|
||||
hasLoaded = false
|
||||
error = ''
|
||||
hasMore = true
|
||||
loadMoreCursor?: string
|
||||
|
||||
// data
|
||||
mutes: ActorDefs.ProfileView[] = []
|
||||
|
||||
constructor(public rootStore: RootStoreModel) {
|
||||
makeAutoObservable(
|
||||
this,
|
||||
{
|
||||
rootStore: false,
|
||||
},
|
||||
{autoBind: true},
|
||||
)
|
||||
}
|
||||
|
||||
get hasContent() {
|
||||
return this.mutes.length > 0
|
||||
}
|
||||
|
||||
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 res = await this.rootStore.agent.app.bsky.graph.getMutes({
|
||||
limit: PAGE_SIZE,
|
||||
cursor: replace ? undefined : this.loadMoreCursor,
|
||||
})
|
||||
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: GetMutes.Response) {
|
||||
this.mutes = []
|
||||
this._appendAll(res)
|
||||
}
|
||||
|
||||
_appendAll(res: GetMutes.Response) {
|
||||
this.loadMoreCursor = res.data.cursor
|
||||
this.hasMore = !!this.loadMoreCursor
|
||||
this.mutes = this.mutes.concat(res.data.mutes)
|
||||
}
|
||||
}
|
28
src/state/queries/my-blocked-accounts.ts
Normal file
28
src/state/queries/my-blocked-accounts.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import {AppBskyGraphGetBlocks} from '@atproto/api'
|
||||
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||
import {useSession} from '../session'
|
||||
|
||||
export const RQKEY = () => ['my-blocked-accounts']
|
||||
type RQPageParam = string | undefined
|
||||
|
||||
export function useMyBlockedAccountsQuery() {
|
||||
const {agent} = useSession()
|
||||
return useInfiniteQuery<
|
||||
AppBskyGraphGetBlocks.OutputSchema,
|
||||
Error,
|
||||
InfiniteData<AppBskyGraphGetBlocks.OutputSchema>,
|
||||
QueryKey,
|
||||
RQPageParam
|
||||
>({
|
||||
queryKey: RQKEY(),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
const res = await agent.app.bsky.graph.getBlocks({
|
||||
limit: 30,
|
||||
cursor: pageParam,
|
||||
})
|
||||
return res.data
|
||||
},
|
||||
initialPageParam: undefined,
|
||||
getNextPageParam: lastPage => lastPage.cursor,
|
||||
})
|
||||
}
|
28
src/state/queries/my-muted-accounts.ts
Normal file
28
src/state/queries/my-muted-accounts.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import {AppBskyGraphGetMutes} from '@atproto/api'
|
||||
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
|
||||
import {useSession} from '../session'
|
||||
|
||||
export const RQKEY = () => ['my-muted-accounts']
|
||||
type RQPageParam = string | undefined
|
||||
|
||||
export function useMyMutedAccountsQuery() {
|
||||
const {agent} = useSession()
|
||||
return useInfiniteQuery<
|
||||
AppBskyGraphGetMutes.OutputSchema,
|
||||
Error,
|
||||
InfiniteData<AppBskyGraphGetMutes.OutputSchema>,
|
||||
QueryKey,
|
||||
RQPageParam
|
||||
>({
|
||||
queryKey: RQKEY(),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
const res = await agent.app.bsky.graph.getMutes({
|
||||
limit: 30,
|
||||
cursor: pageParam,
|
||||
})
|
||||
return res.data
|
||||
},
|
||||
initialPageParam: undefined,
|
||||
getNextPageParam: lastPage => lastPage.cursor,
|
||||
})
|
||||
}
|
|
@ -11,6 +11,8 @@ import {useSession} from '../session'
|
|||
import {updateProfileShadow} from '../cache/profile-shadow'
|
||||
import {uploadBlob} from '#/lib/api'
|
||||
import {until} from '#/lib/async/until'
|
||||
import {RQKEY as RQKEY_MY_MUTED} from './my-muted-accounts'
|
||||
import {RQKEY as RQKEY_MY_BLOCKED} from './my-blocked-accounts'
|
||||
|
||||
export const RQKEY = (did: string) => ['profile', did]
|
||||
|
||||
|
@ -147,6 +149,7 @@ export function useProfileUnfollowMutation() {
|
|||
|
||||
export function useProfileMuteMutation() {
|
||||
const {agent} = useSession()
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation<void, Error, {did: string}>({
|
||||
mutationFn: async ({did}) => {
|
||||
await agent.mute(did)
|
||||
|
@ -157,6 +160,9 @@ export function useProfileMuteMutation() {
|
|||
muted: true,
|
||||
})
|
||||
},
|
||||
onSuccess() {
|
||||
queryClient.invalidateQueries({queryKey: RQKEY_MY_MUTED()})
|
||||
},
|
||||
onError(error, variables) {
|
||||
// revert the optimistic update
|
||||
updateProfileShadow(variables.did, {
|
||||
|
@ -189,6 +195,7 @@ export function useProfileUnmuteMutation() {
|
|||
|
||||
export function useProfileBlockMutation() {
|
||||
const {agent, currentAccount} = useSession()
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation<{uri: string; cid: string}, Error, {did: string}>({
|
||||
mutationFn: async ({did}) => {
|
||||
if (!currentAccount) {
|
||||
|
@ -210,6 +217,7 @@ export function useProfileBlockMutation() {
|
|||
updateProfileShadow(variables.did, {
|
||||
blockingUri: data.uri,
|
||||
})
|
||||
queryClient.invalidateQueries({queryKey: RQKEY_MY_BLOCKED()})
|
||||
},
|
||||
onError(error, variables) {
|
||||
// revert the optimistic update
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue