start using mutations

This commit is contained in:
Hailey
2024-06-19 16:50:32 -07:00
parent a1880ac1ba
commit dca966da52
5 changed files with 207 additions and 200 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ import {createFullHandle} from '#/lib/strings/handles'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useServiceQuery} from '#/state/queries/service' import {useServiceQuery} from '#/state/queries/service'
import {useAgent} from '#/state/session' import {useAgent} from '#/state/session'
import {useStarterPackQuery} from 'state/queries/useStarterPackQuery' import {useStarterPackQuery} from 'state/queries/starter-pack'
import {useActiveStarterPack} from 'state/shell/starter-pack' import {useActiveStarterPack} from 'state/shell/starter-pack'
import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout' import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout'
import { import {
@@ -16,7 +16,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {createStarterPackGooglePlayUri} from 'lib/strings/starter-pack' import {createStarterPackGooglePlayUri} from 'lib/strings/starter-pack'
import {isWeb} from 'platform/detection' import {isWeb} from 'platform/detection'
import {useModerationOpts} from 'state/preferences/moderation-opts' import {useModerationOpts} from 'state/preferences/moderation-opts'
import {useStarterPackQuery} from 'state/queries/useStarterPackQuery' import {useStarterPackQuery} from 'state/queries/starter-pack'
import { import {
useActiveStarterPack, useActiveStarterPack,
useSetActiveStarterPack, useSetActiveStarterPack,
@@ -17,7 +17,7 @@ import {logEvent} from 'lib/statsig/statsig'
import {isWeb} from 'platform/detection' import {isWeb} from 'platform/detection'
import {RQKEY} from 'state/queries/list-members' import {RQKEY} from 'state/queries/list-members'
import {useResolveDidQuery} from 'state/queries/resolve-uri' import {useResolveDidQuery} from 'state/queries/resolve-uri'
import {useStarterPackQuery} from 'state/queries/useStarterPackQuery' import {useStarterPackQuery} from 'state/queries/starter-pack'
import {useAgent, useSession} from 'state/session' import {useAgent, useSession} from 'state/session'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
+73 -178
View File
@@ -8,7 +8,6 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
import { import {
AppBskyActorDefs, AppBskyActorDefs,
AppBskyGraphDefs, AppBskyGraphDefs,
AppBskyGraphStarterpack,
AtUri, AtUri,
ModerationOpts, ModerationOpts,
} from '@atproto/api' } from '@atproto/api'
@@ -18,28 +17,24 @@ import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useFocusEffect, useNavigation} from '@react-navigation/native' import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {NativeStackScreenProps} from '@react-navigation/native-stack' import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/logger'
import {HITSLOP_10} from 'lib/constants' import {HITSLOP_10} from 'lib/constants'
import {createStarterPackList} from 'lib/generate-starterpack'
import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types' import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
import {logEvent} from 'lib/statsig/statsig' import {logEvent} from 'lib/statsig/statsig'
import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeDisplayName} from 'lib/strings/display-names'
import {sanitizeHandle} from 'lib/strings/handles' import {sanitizeHandle} from 'lib/strings/handles'
import {enforceLen} from 'lib/strings/helpers' import {enforceLen} from 'lib/strings/helpers'
import {parseStarterPackUri} from 'lib/strings/starter-pack'
import {isAndroid, isNative, isWeb} from 'platform/detection' import {isAndroid, isNative, isWeb} from 'platform/detection'
import {useModerationOpts} from 'state/preferences/moderation-opts' import {useModerationOpts} from 'state/preferences/moderation-opts'
import {invalidateActorStarterPacksQuery} from 'state/queries/actor-starter-packs' import {useListMembersQuery} from 'state/queries/list-members'
import {
invalidateListMembersQuery,
useListMembersQuery,
} from 'state/queries/list-members'
import {useProfileQuery} from 'state/queries/profile' import {useProfileQuery} from 'state/queries/profile'
import { import {
invalidateStarterPack, useCreateStarterPackMutation,
useEditStarterPackMutation,
useStarterPackQuery, useStarterPackQuery,
} from 'state/queries/useStarterPackQuery' } from 'state/queries/starter-pack'
import {useAgent, useSession} from 'state/session' import {useAgent, useSession} from 'state/session'
import {useSetMinimalShellMode} from 'state/shell' import {useSetMinimalShellMode} from 'state/shell'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
@@ -120,14 +115,8 @@ export function Wizard({
return ( return (
<Provider starterPack={starterPack} listItems={listItems}> <Provider starterPack={starterPack} listItems={listItems}>
<WizardInner <WizardInner
rkey={rkey} currentStarterPack={starterPack}
createdAt={ currentListItems={listItems}
AppBskyGraphStarterpack.isRecord(starterPack?.record)
? starterPack.record.createdAt
: undefined
}
listItems={listItems}
listUri={listUri}
profile={profile} profile={profile}
moderationOpts={moderationOpts} moderationOpts={moderationOpts}
/> />
@@ -136,17 +125,13 @@ export function Wizard({
} }
function WizardInner({ function WizardInner({
rkey, currentStarterPack,
createdAt: initialCreatedAt, currentListItems,
listUri: initialListUri,
listItems: initialListItems,
profile, profile,
moderationOpts, moderationOpts,
}: { }: {
rkey?: string currentStarterPack?: AppBskyGraphDefs.StarterPackView
createdAt?: string currentListItems?: AppBskyGraphDefs.ListItemView[]
listUri?: string
listItems?: AppBskyGraphDefs.ListItemView[]
profile: AppBskyActorDefs.ProfileViewBasic profile: AppBskyActorDefs.ProfileViewBasic
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
}) { }) {
@@ -155,7 +140,6 @@ function WizardInner({
const t = useTheme() const t = useTheme()
const setMinimalShellMode = useSetMinimalShellMode() const setMinimalShellMode = useSetMinimalShellMode()
const {setEnabled} = useKeyboardController() const {setEnabled} = useKeyboardController()
const queryClient = useQueryClient()
const [state, dispatch] = useWizardState() const [state, dispatch] = useWizardState()
const agent = useAgent() const agent = useAgent()
const {currentAccount} = useSession() const {currentAccount} = useSession()
@@ -163,6 +147,7 @@ function WizardInner({
did: currentAccount?.did, did: currentAccount?.did,
staleTime: 0, staleTime: 0,
}) })
const parsed = parseStarterPackUri(currentStarterPack?.uri)
React.useEffect(() => { React.useEffect(() => {
navigation.setOptions({ navigation.setOptions({
@@ -218,178 +203,88 @@ function WizardInner({
} }
const currUiStrings = wizardUiStrings[state.currentStep] const currUiStrings = wizardUiStrings[state.currentStep]
const invalidateQueries = async () => { const onSuccessCreate = (data: {uri: string; cid: string}) => {
if (!rkey) return const rkey = new AtUri(data.uri).rkey
logEvent('starterPack:create', {
if (initialListUri) { setName: state.name != null,
await invalidateListMembersQuery({queryClient, uri: initialListUri}) setDescription: state.description != null,
} profilesCount: state.profiles.length,
await invalidateActorStarterPacksQuery({ feedsCount: state.feeds.length,
queryClient,
did: currentAccount!.did,
}) })
await invalidateStarterPack({ dispatch({type: 'SetProcessing', processing: false})
queryClient, navigation.replace('StarterPack', {
did: currentAccount!.did, name: currentAccount!.handle,
rkey, rkey,
}) })
} }
const onSuccessEdit = () => {
if (navigation.canGoBack()) {
navigation.goBack()
} else {
navigation.replace('StarterPack', {
name: currentAccount!.handle,
rkey: parsed!.rkey,
})
}
}
const onError = (e: Error) => {
logger.error('Failed to create starter pack', {safeMessage: e})
dispatch({type: 'SetProcessing', processing: false})
Toast.show(_(msg`Failed to create starter pack`))
}
const {mutate: createStarterPack} = useCreateStarterPackMutation({
onSuccess: onSuccessCreate,
onError,
})
const {mutate: editStarterPack} = useEditStarterPackMutation({
onSuccess: onSuccessEdit,
onError,
})
const submit = async () => { const submit = async () => {
dispatch({type: 'SetProcessing', processing: true}) dispatch({type: 'SetProcessing', processing: true})
if (currentStarterPack && currentListItems) {
try { editStarterPack({
if (rkey) { name: state.name ?? getDefaultName(),
// Editing an existing starter pack description: state.description,
let list: {uri: string; cid: string} | undefined = initialListUri descriptionFacets: [],
? {uri: initialListUri, cid: ''} profiles: state.profiles,
: undefined feeds: state.feeds,
if (initialListUri) { currentStarterPack: currentStarterPack,
const removedItems = initialListItems?.filter( currentListItems: currentListItems,
i => !state.profiles.find(p => p.did === i.subject.did), })
) } else {
if (removedItems && removedItems.length > 0) { createStarterPack({
await agent.com.atproto.repo.applyWrites({ name: state.name ?? getDefaultName(),
repo: currentAccount!.did, description: state.description,
writes: removedItems.map(i => ({ descriptionFacets: [],
$type: 'com.atproto.repo.applyWrites#delete', profiles: state.profiles,
collection: 'app.bsky.graph.listitem', feeds: state.feeds,
rkey: new AtUri(i.uri).rkey, })
})),
})
}
const addedProfiles = state.profiles.filter(
p => !initialListItems?.find(i => i.subject.did === p.did),
)
if (addedProfiles.length > 0) {
await agent.com.atproto.repo.applyWrites({
repo: currentAccount!.did,
writes: addedProfiles.map(p => ({
$type: 'com.atproto.repo.applyWrites#create',
collection: 'app.bsky.graph.listitem',
value: {
$type: 'app.bsky.graph.listitem',
subject: p.did,
list: list?.uri,
createdAt: new Date().toISOString(),
},
})),
})
}
} else {
list = await createStarterPackList({
name: state.name ?? getDefaultName(),
description: state.description,
descriptionFacets: [],
profiles: state.profiles,
agent,
})
}
await agent.com.atproto.repo.putRecord({
repo: currentAccount!.did,
collection: 'app.bsky.graph.starterpack',
rkey,
record: {
name: state.name ?? getDefaultName(),
description: state.description,
descriptionFacets: [],
list: list?.uri,
feeds: state.feeds.map(f => ({
uri: f.uri,
})),
createdAt: initialCreatedAt,
updatedAt: new Date().toISOString(),
},
validate: false, // TODO remove!
})
setTimeout(async () => {
await invalidateQueries()
if (navigation.canGoBack()) {
navigation.goBack()
} else {
navigation.replace('StarterPack', {
name: currentAccount!.handle,
rkey,
})
}
dispatch({type: 'SetProcessing', processing: false})
}, 2000)
} else {
// Creating a new starter pack
const list = await createStarterPackList({
name: state.name ?? getDefaultName(),
description: state.description,
descriptionFacets: [],
profiles: state.profiles,
agent,
})
const res = await agent.app.bsky.graph.starterpack.create(
{
repo: currentAccount!.did,
validate: false,
},
{
name: state.name ?? getDefaultName(),
description: state.description,
descriptionFacets: [],
list: list.uri,
feeds: state.feeds.map(f => ({
uri: f.uri,
})),
createdAt: new Date().toISOString(),
},
)
logEvent('starterPack:create', {
setName: state.name != null,
setDescription: state.description != null,
profilesCount: state.profiles.length,
feedsCount: state.feeds.length,
})
const newRkey = new AtUri(res.uri).rkey
setTimeout(async () => {
await invalidateQueries()
navigation.replace('StarterPack', {
name: currentAccount!.handle,
rkey: newRkey,
})
dispatch({type: 'SetProcessing', processing: false})
}, 2000)
}
} catch (e: unknown) {
logger.error('Failed to create starter pack', {safeMessage: e})
Toast.show(_(msg`Failed to create starter pack`))
dispatch({type: 'SetProcessing', processing: false})
return
} }
} }
const deleteStarterPack = async () => { const deleteStarterPack = async () => {
if (!rkey || !initialListUri) return if (!currentStarterPack || !currentListItems) return
dispatch({type: 'SetProcessing', processing: true}) dispatch({type: 'SetProcessing', processing: true})
try { try {
await agent.app.bsky.graph.list.delete({ await agent.app.bsky.graph.list.delete({
repo: currentAccount!.did, repo: currentAccount!.did,
rkey: new AtUri(initialListUri).rkey, // TODO list is required, so shouldn't need this assertion
rkey: new AtUri(currentStarterPack.list!.uri).rkey,
}) })
await agent.app.bsky.graph.starterpack.delete({ await agent.app.bsky.graph.starterpack.delete({
repo: currentAccount!.did, repo: currentAccount!.did,
rkey, rkey: new AtUri(currentStarterPack.uri).rkey,
}) })
setTimeout(async () => { logEvent('starterPack:delete', {})
await invalidateQueries() navigation.popToTop()
logEvent('starterPack:delete', {})
navigation.popToTop()
}, 2000)
} catch (e) { } catch (e) {
Toast.show(_(msg`Failed to delete starter pack`)) Toast.show(_(msg`Failed to delete starter pack`))
} finally { } finally {
@@ -456,7 +351,7 @@ function WizardInner({
</View> </View>
<Container <Container
showDeleteBtn={Boolean(rkey)} showDeleteBtn={Boolean(currentStarterPack)}
deleteStarterPack={deleteStarterPack}> deleteStarterPack={deleteStarterPack}>
{state.currentStep === 'Details' ? ( {state.currentStep === 'Details' ? (
<StepDetails /> <StepDetails />
+131 -19
View File
@@ -2,7 +2,8 @@ import {
AppBskyActorDefs, AppBskyActorDefs,
AppBskyFeedDefs, AppBskyFeedDefs,
AppBskyGraphDefs, AppBskyGraphDefs,
AppBskyGraphGetList, AppBskyGraphStarterpack,
AtUri,
BskyAgent, BskyAgent,
} from '@atproto/api' } from '@atproto/api'
import {StarterPackView} from '@atproto/api/dist/client/types/app/bsky/graph/defs' import {StarterPackView} from '@atproto/api/dist/client/types/app/bsky/graph/defs'
@@ -19,10 +20,8 @@ import {
httpStarterPackUriToAtUri, httpStarterPackUriToAtUri,
parseStarterPackUri, parseStarterPackUri,
} from 'lib/strings/starter-pack' } from 'lib/strings/starter-pack'
import { import {invalidateActorStarterPacksQuery} from 'state/queries/actor-starter-packs'
invalidateActorStarterPacksQuery, import {invalidateListMembersQuery} from 'state/queries/list-members'
RQKEY,
} from 'state/queries/actor-starter-packs'
import {useAgent} from 'state/session' import {useAgent} from 'state/session'
const RQKEY_ROOT = 'starter-pack' const RQKEY_ROOT = 'starter-pack'
@@ -86,8 +85,9 @@ interface UseCreateStarterPackMutationParams {
export function useCreateStarterPackMutation({ export function useCreateStarterPackMutation({
onSuccess, onSuccess,
onError,
}: { }: {
onSuccess: () => void onSuccess: (data: {uri: string; cid: string}) => void
onError: (e: Error) => void onError: (e: Error) => void
}) { }) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
@@ -100,7 +100,7 @@ export function useCreateStarterPackMutation({
>({ >({
mutationFn: async params => { mutationFn: async params => {
let listRes let listRes
const listRes = await createStarterPackList({...params, agent}) listRes = await createStarterPackList({...params, agent})
return await agent.app.bsky.graph.starterpack.create( return await agent.app.bsky.graph.starterpack.create(
{ {
repo: agent.session?.did, repo: agent.session?.did,
@@ -108,22 +108,134 @@ export function useCreateStarterPackMutation({
}, },
{ {
...params, ...params,
list: listRes.uri, list: listRes?.uri,
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
}, },
) )
}, },
onSuccess: async data => { onSuccess: async data => {
await whenAppViewReady( await whenAppViewReady(agent, data.uri, v => {
agent, return typeof v?.uri === 'string'
data.uri, })
(v?: AppBskyGraphDefs.StarterPackView) => {
return typeof v.uri === 'string'
},
)
await invalidateActorStarterPacksQuery({ await invalidateActorStarterPacksQuery({
queryClient, queryClient,
did: agent.session?.did, did: agent.session!.did,
})
onSuccess(data)
},
onError: async error => {
onError(error)
},
})
}
export function useEditStarterPackMutation({
onSuccess,
onError,
}: {
onSuccess: () => void
onError: (error: Error) => void
}) {
const queryClient = useQueryClient()
const agent = useAgent()
return useMutation<
void,
Error,
UseCreateStarterPackMutationParams & {
currentStarterPack: AppBskyGraphDefs.StarterPackView
currentListItems: AppBskyGraphDefs.ListItemView[]
}
>({
mutationFn: async params => {
const {
name,
description,
descriptionFacets,
feeds,
profiles,
currentStarterPack,
currentListItems,
} = params
if (!AppBskyGraphStarterpack.isRecord(currentStarterPack.record)) {
throw new Error('Invalid starter pack')
}
const removedItems = currentListItems.filter(
i =>
!profiles.find(
p => p.did === i.subject.did && p.did !== agent.session!.did,
),
)
if (removedItems.length !== 0) {
await agent.com.atproto.repo.applyWrites({
repo: agent.session!.did,
writes: removedItems.map(i => ({
$type: 'com.atproto.repo.applyWrites#delete',
collection: 'app.bsky.graph.listitem',
rkey: new AtUri(i.uri).rkey,
})),
})
}
const addedProfiles = profiles.filter(
p => !currentListItems.find(i => i.subject.did === p.did),
)
if (addedProfiles.length > 0) {
await agent.com.atproto.repo.applyWrites({
repo: agent.session!.did,
writes: addedProfiles.map(p => ({
$type: 'com.atproto.repo.applyWrites#create',
collection: 'app.bsky.graph.listitem',
value: {
$type: 'app.bsky.graph.listitem',
subject: p.did,
list: currentStarterPack.list?.uri,
createdAt: new Date().toISOString(),
},
})),
})
}
const rkey = parseStarterPackUri(currentStarterPack.uri)!.rkey
await agent.com.atproto.repo.putRecord({
repo: agent.session!.did,
collection: 'app.bsky.graph.starterpack',
rkey,
record: {
name,
description,
descriptionFacets,
list: currentStarterPack.list?.uri,
feeds,
createdAt: currentStarterPack.record.createdAt,
updatedAt: new Date().toISOString(),
},
validate: false, // TODO remove!
})
},
onSuccess: async (_, {currentStarterPack}) => {
const parsed = parseStarterPackUri(currentStarterPack.uri)
await whenAppViewReady(agent, currentStarterPack.uri, v => {
return currentStarterPack.cid !== v?.cid
})
await invalidateActorStarterPacksQuery({
queryClient,
did: agent.session!.did,
})
if (currentStarterPack.list) {
await invalidateListMembersQuery({
queryClient,
uri: currentStarterPack.list.uri,
})
}
await invalidateStarterPack({
queryClient,
did: agent.session!.did,
rkey: parsed!.rkey,
}) })
onSuccess() onSuccess()
}, },
@@ -136,11 +248,11 @@ export function useCreateStarterPackMutation({
async function whenAppViewReady( async function whenAppViewReady(
agent: BskyAgent, agent: BskyAgent,
uri: string, uri: string,
fn: (res?: AppBskyGraphGetList.Response) => boolean, fn: (res?: AppBskyGraphDefs.StarterPackView) => boolean,
) { ) {
await until( await until(
5, // 5 tries 10, // 5 tries
1e3, // 1s delay between tries 1e5, // 1s delay between tries
fn, fn,
() => () =>
agent.app.bsky.graph.getStarterPack({ agent.app.bsky.graph.getStarterPack({