diff --git a/src/components/dialogs/StarterPackDialog.tsx b/src/components/dialogs/StarterPackDialog.tsx
index ac3861e2b6..0b7759673b 100644
--- a/src/components/dialogs/StarterPackDialog.tsx
+++ b/src/components/dialogs/StarterPackDialog.tsx
@@ -1,4 +1,4 @@
-import {useCallback, useState} from 'react'
+import {useCallback} from 'react'
import {View} from 'react-native'
import {
type AppBskyGraphGetStarterPacksWithMembership,
@@ -7,14 +7,12 @@ import {
import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
-import {useQueryClient} from '@tanstack/react-query'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import {type NavigationProp} from '#/lib/routes/types'
-import {
- invalidateActorStarterPacksWithMembershipQuery,
- useActorStarterPacksWithMembershipsQuery,
-} from '#/state/queries/actor-starter-packs'
+import {isNetworkError} from '#/lib/strings/errors'
+import {logger} from '#/logger'
+import {useActorStarterPacksWithMembershipsQuery} from '#/state/queries/actor-starter-packs'
import {
useListMembershipAddMutation,
useListMembershipRemoveMutation,
@@ -253,50 +251,37 @@ function StarterPackItem({
const t = useTheme()
const ax = useAnalytics()
const {_} = useLingui()
- const queryClient = useQueryClient()
const starterPack = starterPackWithMembership.starterPack
const isInPack = !!starterPackWithMembership.listItem
- const [isPendingRefresh, setIsPendingRefresh] = useState(false)
+ const {mutate: addMembership, isPending: isPendingAdd} =
+ useListMembershipAddMutation({
+ onSuccess: () => {
+ Toast.show(_(msg`Added to starter pack`))
+ },
+ onError: err => {
+ if (!isNetworkError(err)) {
+ logger.error('Failed to remove from starter pack', {safeMessage: err})
+ }
+ Toast.show(_(msg`Failed to add to starter pack`), {type: 'error'})
+ },
+ })
- const {mutate: addMembership} = useListMembershipAddMutation({
- onSuccess: () => {
- Toast.show(_(msg`Added to starter pack`))
- // Use a timeout to wait for the appview to update, matching the pattern
- // in list-memberships.ts
- setTimeout(() => {
- invalidateActorStarterPacksWithMembershipQuery({
- queryClient,
- did: targetDid,
- })
- setIsPendingRefresh(false)
- }, 1e3)
- },
- onError: () => {
- Toast.show(_(msg`Failed to add to starter pack`), 'xmark')
- setIsPendingRefresh(false)
- },
- })
+ const {mutate: removeMembership, isPending: isPendingRemove} =
+ useListMembershipRemoveMutation({
+ onSuccess: () => {
+ Toast.show(_(msg`Removed from starter pack`))
+ },
+ onError: err => {
+ if (!isNetworkError(err)) {
+ logger.error('Failed to remove from starter pack', {safeMessage: err})
+ }
+ Toast.show(_(msg`Failed to remove from starter pack`), {type: 'error'})
+ },
+ })
- const {mutate: removeMembership} = useListMembershipRemoveMutation({
- onSuccess: () => {
- Toast.show(_(msg`Removed from starter pack`))
- // Use a timeout to wait for the appview to update, matching the pattern
- // in list-memberships.ts
- setTimeout(() => {
- invalidateActorStarterPacksWithMembershipQuery({
- queryClient,
- did: targetDid,
- })
- setIsPendingRefresh(false)
- }, 1e3)
- },
- onError: () => {
- Toast.show(_(msg`Failed to remove from starter pack`), 'xmark')
- setIsPendingRefresh(false)
- },
- })
+ const isPending = isPendingAdd || isPendingRemove
const handleToggleMembership = () => {
if (!starterPack.list?.uri || isPending) return
@@ -313,7 +298,6 @@ function StarterPackItem({
} else {
if (!starterPackWithMembership.listItem?.uri) {
console.error('Cannot remove: missing membership URI')
- setIsPendingRefresh(false)
return
}
removeMembership({
@@ -379,8 +363,9 @@ function StarterPackItem({
label={isInPack ? _(msg`Remove`) : _(msg`Add`)}
color={isInPack ? 'secondary' : 'primary_subtle'}
size="tiny"
- disabled={isPendingRefresh}
+ disabled={isPending}
onPress={handleToggleMembership}>
+ {isPending && }
{isInPack ? Remove : Add}
diff --git a/src/state/queries/actor-starter-packs.ts b/src/state/queries/actor-starter-packs.ts
index d40e054535..97cc3b5e05 100644
--- a/src/state/queries/actor-starter-packs.ts
+++ b/src/state/queries/actor-starter-packs.ts
@@ -1,13 +1,4 @@
-import {
- type AppBskyGraphGetActorStarterPacks,
- type AppBskyGraphGetStarterPacksWithMembership,
-} from '@atproto/api'
-import {
- type InfiniteData,
- type QueryClient,
- type QueryKey,
- useInfiniteQuery,
-} from '@tanstack/react-query'
+import {type QueryClient, useInfiniteQuery} from '@tanstack/react-query'
import {useAgent} from '#/state/session'
@@ -28,13 +19,7 @@ export function useActorStarterPacksQuery({
}) {
const agent = useAgent()
- return useInfiniteQuery<
- AppBskyGraphGetActorStarterPacks.OutputSchema,
- Error,
- InfiniteData,
- QueryKey,
- string | undefined
- >({
+ return useInfiniteQuery({
queryKey: RQKEY(did),
queryFn: async ({pageParam}: {pageParam?: string}) => {
const res = await agent.app.bsky.graph.getActorStarterPacks({
@@ -59,13 +44,7 @@ export function useActorStarterPacksWithMembershipsQuery({
}) {
const agent = useAgent()
- return useInfiniteQuery<
- AppBskyGraphGetStarterPacksWithMembership.OutputSchema,
- Error,
- InfiniteData,
- QueryKey,
- string | undefined
- >({
+ return useInfiniteQuery({
queryKey: RQKEY_WITH_MEMBERSHIP(did),
queryFn: async ({pageParam}: {pageParam?: string}) => {
const res = await agent.app.bsky.graph.getStarterPacksWithMembership({
diff --git a/src/state/queries/list-memberships.ts b/src/state/queries/list-memberships.ts
index 410a613ada..a7657bb572 100644
--- a/src/state/queries/list-memberships.ts
+++ b/src/state/queries/list-memberships.ts
@@ -14,12 +14,22 @@
* -prf
*/
-import {AtUri} from '@atproto/api'
-import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
+import {
+ type AppBskyActorDefs,
+ type AppBskyGraphGetStarterPacksWithMembership,
+ AtUri,
+} from '@atproto/api'
+import {
+ type InfiniteData,
+ useMutation,
+ useQuery,
+ useQueryClient,
+} from '@tanstack/react-query'
import {STALE} from '#/state/queries'
import {RQKEY as LIST_MEMBERS_RQKEY} from '#/state/queries/list-members'
import {useAgent, useSession} from '#/state/session'
+import {RQKEY_WITH_MEMBERSHIP as STARTER_PACKS_WITH_MEMBERSHIPS_RKEY} from './actor-starter-packs'
// sanity limit is SANITY_PAGE_LIMIT*PAGE_SIZE total records
const SANITY_PAGE_LIMIT = 1000
@@ -123,7 +133,7 @@ export function useListMembershipAddMutation({
// -prf
return res
},
- onSuccess: (data, variables) => {
+ onSuccess: async (data, variables) => {
// manually update the cache; a refetch is too expensive
let memberships = queryClient.getQueryData(RQKEY())
if (memberships) {
@@ -151,6 +161,42 @@ export function useListMembershipAddMutation({
queryKey: LIST_MEMBERS_RQKEY(variables.listUri),
})
}, 1e3)
+
+ // update WITH_MEMBERSHIPS query
+ const subject = await agent
+ .getProfile({actor: variables.actorDid})
+ .then(res => res.data)
+
+ if (subject) {
+ queryClient.setQueryData<
+ InfiniteData
+ >(STARTER_PACKS_WITH_MEMBERSHIPS_RKEY(variables.actorDid), old => {
+ if (!old) return old
+
+ return {
+ ...old,
+ pages: old.pages.map(page => ({
+ ...page,
+ starterPacksWithMembership: page.starterPacksWithMembership.map(
+ list => {
+ if (list.starterPack.list?.uri === variables.listUri) {
+ return {
+ ...list,
+ listItem: {
+ uri: data.uri,
+ subject: subject as AppBskyActorDefs.ProfileView,
+ },
+ }
+ }
+
+ return list
+ },
+ ),
+ })),
+ }
+ })
+ }
+
onSuccess?.(data)
},
onError,
@@ -206,6 +252,32 @@ export function useListMembershipRemoveMutation({
queryKey: LIST_MEMBERS_RQKEY(variables.listUri),
})
}, 1e3)
+
+ queryClient.setQueryData<
+ InfiniteData
+ >(STARTER_PACKS_WITH_MEMBERSHIPS_RKEY(variables.actorDid), old => {
+ if (!old) return old
+
+ return {
+ ...old,
+ pages: old.pages.map(page => ({
+ ...page,
+ starterPacksWithMembership: page.starterPacksWithMembership.map(
+ list => {
+ if (list.starterPack.list?.uri === variables.listUri) {
+ return {
+ ...list,
+ listItem: undefined,
+ }
+ }
+
+ return list
+ },
+ ),
+ })),
+ }
+ })
+
onSuccess?.(data)
},
onError,