finish editing

This commit is contained in:
Hailey
2024-06-09 19:11:14 -07:00
parent 2496a1589c
commit d53f253ae9
5 changed files with 247 additions and 65 deletions
+3 -3
View File
@@ -116,11 +116,11 @@ function reducer(state: State, action: Action): State {
// TODO supply the initial state to this component // TODO supply the initial state to this component
export function Provider({ export function Provider({
starterPack, starterPack,
profiles, listItems,
children, children,
}: { }: {
starterPack?: AppBskyGraphDefs.StarterPackView starterPack?: AppBskyGraphDefs.StarterPackView
profiles?: AppBskyActorDefs.ProfileViewBasic[] listItems?: AppBskyGraphDefs.ListItemView[]
children: React.ReactNode children: React.ReactNode
}) { }) {
const createInitialState = (): State => { const createInitialState = (): State => {
@@ -130,7 +130,7 @@ export function Provider({
currentStep: 'Details', currentStep: 'Details',
name: starterPack.record.name, name: starterPack.record.name,
description: starterPack.record.description, description: starterPack.record.description,
profiles: profiles ?? [], profiles: listItems?.map(i => i.subject) ?? [],
feeds: starterPack.feeds ?? [], feeds: starterPack.feeds ?? [],
processing: false, processing: false,
} }
+193 -53
View File
@@ -1,22 +1,35 @@
import React from 'react' import React from 'react'
import {Keyboard, TouchableOpacity, View} from 'react-native' import {Keyboard, TouchableOpacity, View} from 'react-native'
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller' import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
import {AppBskyActorDefs, AtUri} from '@atproto/api' import {
AppBskyActorDefs,
AppBskyGraphDefs,
AppBskyGraphStarterpack,
AtUri,
} from '@atproto/api'
import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs' import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Plural, Trans} from '@lingui/macro' 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 {HITSLOP_10} from 'lib/constants' import {HITSLOP_10} from 'lib/constants'
import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types' import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
import {enforceLen} from 'lib/strings/helpers' import {enforceLen} from 'lib/strings/helpers'
import {isAndroid, isNative, isWeb} from 'platform/detection' import {isAndroid, isNative, isWeb} from 'platform/detection'
import {useListMembersQuery} from 'state/queries/list-members' import {invalidateActorStarterPacksQuery} from 'state/queries/actor-starter-packs'
import {
invalidateListMembersQuery,
useListMembersQuery,
} from 'state/queries/list-members'
import {useProfileQuery} from 'state/queries/profile' import {useProfileQuery} from 'state/queries/profile'
import {useResolveDidQuery} from 'state/queries/resolve-uri' import {useResolveDidQuery} from 'state/queries/resolve-uri'
import {useStarterPackQuery} from 'state/queries/useStarterPackQuery' import {
invalidateStarterPack,
useStarterPackQuery,
} from 'state/queries/useStarterPackQuery'
import {useAgent, useSession} from 'state/session' import {useAgent, useSession} from 'state/session'
import {useSetMinimalShellMode} from 'state/shell' import {useSetMinimalShellMode} from 'state/shell'
import {UserAvatar} from 'view/com/util/UserAvatar' import {UserAvatar} from 'view/com/util/UserAvatar'
@@ -61,9 +74,13 @@ export function Wizard({
isLoading: isLoadingProfiles, isLoading: isLoadingProfiles,
isError: isErrorProfiles, isError: isErrorProfiles,
} = useListMembersQuery(listUri, 51) // 51 because we also include the current user } = useListMembersQuery(listUri, 51) // 51 because we also include the current user
const profiles = profilesData?.pages.flatMap(p => p.items.map(i => i.subject)) const listItems = profilesData?.pages.flatMap(p => p.items)
if (name && rkey && (!starterPack || (starterPack && listUri && !profiles))) { if (
name &&
rkey &&
(!starterPack || (starterPack && listUri && !listItems))
) {
return ( return (
<ListMaybePlaceholder <ListMaybePlaceholder
isLoading={isLoadingDid || isLoadingStarterPack || isLoadingProfiles} isLoading={isLoadingDid || isLoadingStarterPack || isLoadingProfiles}
@@ -74,16 +91,39 @@ export function Wizard({
} }
return ( return (
<Provider starterPack={starterPack} profiles={profiles}> <Provider starterPack={starterPack} listItems={listItems}>
<WizardInner /> <WizardInner
did={did}
rkey={rkey}
createdAt={
AppBskyGraphStarterpack.isRecord(starterPack?.record)
? starterPack.record.createdAt
: undefined
}
listItems={listItems}
listUri={listUri}
/>
</Provider> </Provider>
) )
} }
function WizardInner() { function WizardInner({
did,
rkey,
createdAt: initialCreatedAt,
listUri: initialListUri,
listItems: initialListItems,
}: {
did?: string
rkey?: string
createdAt?: string
listUri?: string
listItems?: AppBskyGraphDefs.ListItemView[]
}) {
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const t = useTheme()
const queryClient = useQueryClient()
const [state, dispatch] = useWizardState() const [state, dispatch] = useWizardState()
const agent = useAgent() const agent = useAgent()
const {currentAccount} = useSession() const {currentAccount} = useSession()
@@ -131,63 +171,163 @@ function WizardInner() {
const uiStrings = wizardUiStrings[state.currentStep] const uiStrings = wizardUiStrings[state.currentStep]
const createList = async (): Promise<
{uri: string; cid: string} | undefined
> => {
if (state.profiles.length === 0) return
const list = await agent.app.bsky.graph.list.create(
{repo: currentAccount?.did},
{
name: state.name ?? '',
description: state.description ?? '',
descriptionFacets: [],
avatar: undefined,
createdAt: new Date().toISOString(),
purpose: 'app.bsky.graph.defs#referencelist',
},
)
if (!list) throw new Error('List creation failed')
await agent.com.atproto.repo.applyWrites({
repo: currentAccount!.did,
writes: state.profiles.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(),
},
})),
})
return list
}
const submit = async () => { const submit = async () => {
dispatch({type: 'SetProcessing', processing: true}) dispatch({type: 'SetProcessing', processing: true})
try { try {
const list = await agent.app.bsky.graph.list.create( if (did && rkey) {
{repo: currentAccount?.did}, // Editing an existing starter pack
{ let list: {uri: string; cid: string} | undefined = initialListUri
name: state.name ?? '', ? {uri: initialListUri, cid: ''}
description: state.description ?? '', : undefined
descriptionFacets: [], if (initialListUri) {
avatar: undefined, const removedItems = initialListItems?.filter(
createdAt: new Date().toISOString(), i => !state.profiles.find(p => p.did === i.subject.did),
purpose: 'app.bsky.graph.defs#referencelist', )
}, if (removedItems && removedItems.length > 0) {
) await agent.com.atproto.repo.applyWrites({
repo: currentAccount!.did,
writes: removedItems.map(i => ({
$type: 'com.atproto.repo.applyWrites#delete',
collection: 'app.bsky.graph.listitem',
rkey: new AtUri(i.uri).rkey,
})),
})
}
await agent.com.atproto.repo.applyWrites({ const addedProfiles = state.profiles.filter(
repo: currentAccount!.did, p => !initialListItems?.find(i => i.subject.did === p.did),
writes: state.profiles.map(p => ({ )
$type: 'com.atproto.repo.applyWrites#create',
collection: 'app.bsky.graph.listitem', if (addedProfiles.length > 0) {
value: { await agent.com.atproto.repo.applyWrites({
$type: 'app.bsky.graph.listitem', repo: currentAccount!.did,
subject: p.did, writes: addedProfiles.map(p => ({
list: list.uri, $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 createList()
}
await agent.com.atproto.repo.putRecord({
repo: currentAccount!.did,
collection: 'app.bsky.graph.starterpack',
rkey,
record: {
name: state.name ?? '',
description: state.description ?? '',
descriptionFacets: [],
list: list?.uri,
feeds: state.feeds.map(f => ({
uri: f.uri,
})),
createdAt: initialCreatedAt,
updatedAt: new Date().toISOString(),
},
})
if (initialListUri) {
await invalidateListMembersQuery({queryClient, uri: initialListUri})
}
await invalidateActorStarterPacksQuery({
queryClient,
did,
})
await invalidateStarterPack({
queryClient,
did,
rkey,
})
setTimeout(() => {
if (navigation.canGoBack()) {
navigation.goBack()
} else {
navigation.replace('StarterPack', {
name: currentAccount!.handle,
rkey,
})
}
dispatch({type: 'SetProcessing', processing: false})
}, 1000)
} else {
// Creating a new starter pack
const list = await createList()
const res = await agent.app.bsky.graph.starterpack.create(
{
repo: currentAccount!.did,
validate: false,
},
{
name: state.name ?? '',
description: state.description ?? '',
descriptionFacets: [],
list: list?.uri,
feeds: state.feeds.map(f => ({
uri: f.uri,
})),
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
}, },
})), )
})
const res = await agent.app.bsky.graph.starterpack.create( const newRkey = new AtUri(res.uri).rkey
{
repo: currentAccount!.did,
validate: false,
},
{
name: state.name ?? '',
description: state.description ?? '',
descriptionFacets: [],
list: list.uri,
feeds: state.feeds.map(f => ({
uri: f.uri,
})),
createdAt: new Date().toISOString(),
},
)
const rkey = new AtUri(res.uri).rkey // TODO hack?
setTimeout(() => {
// TODO hack? navigation.replace('StarterPack', {
setTimeout(() => { name: currentAccount!.handle,
navigation.replace('StarterPack', {name: currentAccount!.handle, rkey}) rkey: newRkey,
dispatch({type: 'SetProcessing', processing: false}) })
}, 1000) dispatch({type: 'SetProcessing', processing: false})
}, 1000)
}
} catch (e) { } catch (e) {
// TODO handle the error here // TODO handle the error here
dispatch({type: 'SetProcessing', processing: false}) dispatch({type: 'SetProcessing', processing: false})
return
} }
} }
+18 -4
View File
@@ -1,10 +1,15 @@
import {AppBskyGraphGetActorStarterPacks} from '@atproto/api' import {AppBskyGraphGetActorStarterPacks} from '@atproto/api'
import {InfiniteData, QueryKey, useInfiniteQuery} from '@tanstack/react-query' import {
InfiniteData,
QueryClient,
QueryKey,
useInfiniteQuery,
} from '@tanstack/react-query'
import {STALE} from 'state/queries/index'
import {useAgent} from 'state/session' import {useAgent} from 'state/session'
const RQKEY_ROOT = 'actor-starter-packs' const RQKEY_ROOT = 'actor-starter-packs'
const RQKEY = (did?: string) => [RQKEY_ROOT, did]
export function useActorStarterPacksQuery({did}: {did?: string}) { export function useActorStarterPacksQuery({did}: {did?: string}) {
const agent = useAgent() const agent = useAgent()
@@ -16,7 +21,7 @@ export function useActorStarterPacksQuery({did}: {did?: string}) {
QueryKey, QueryKey,
string | undefined string | undefined
>({ >({
queryKey: [RQKEY_ROOT, did], queryKey: RQKEY(did),
queryFn: async ({pageParam}: {pageParam?: string}) => { queryFn: async ({pageParam}: {pageParam?: string}) => {
const res = await agent.app.bsky.graph.getActorStarterPacks({ const res = await agent.app.bsky.graph.getActorStarterPacks({
actor: did!, actor: did!,
@@ -28,6 +33,15 @@ export function useActorStarterPacksQuery({did}: {did?: string}) {
enabled: Boolean(did), enabled: Boolean(did),
initialPageParam: undefined, initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
staleTime: STALE.MINUTES.ONE,
}) })
} }
export async function invalidateActorStarterPacksQuery({
queryClient,
did,
}: {
queryClient: QueryClient
did: string
}) {
await queryClient.invalidateQueries({queryKey: RQKEY(did)})
}
+10
View File
@@ -40,6 +40,16 @@ export function useListMembersQuery(uri?: string, limit: number = PAGE_SIZE) {
}) })
} }
export async function invalidateListMembersQuery({
queryClient,
uri,
}: {
queryClient: QueryClient
uri: string
}) {
await queryClient.invalidateQueries({queryKey: RQKEY(uri)})
}
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
+23 -5
View File
@@ -1,10 +1,10 @@
import {StarterPackView} from '@atproto/api/dist/client/types/app/bsky/graph/defs' import {StarterPackView} from '@atproto/api/dist/client/types/app/bsky/graph/defs'
import {useMutation, useQuery} from '@tanstack/react-query' import {QueryClient, useMutation, useQuery} from '@tanstack/react-query'
import {STALE} from 'state/queries/index'
import {useAgent, useSession} from 'state/session' import {useAgent, useSession} from 'state/session'
const RQKEY_ROOT = 'starter-pack' const RQKEY_ROOT = 'starter-pack'
const RQKEY = (did?: string, rkey?: string) => [RQKEY_ROOT, did, rkey]
export function useStarterPackQuery({ export function useStarterPackQuery({
did, did,
@@ -17,7 +17,7 @@ export function useStarterPackQuery({
const uri = `at://${did}/app.bsky.graph.starterpack/${rkey}` const uri = `at://${did}/app.bsky.graph.starterpack/${rkey}`
return useQuery<StarterPackView>({ return useQuery<StarterPackView>({
queryKey: [RQKEY_ROOT, did, rkey], queryKey: RQKEY(did, rkey),
queryFn: async () => { queryFn: async () => {
const res = await agent.app.bsky.graph.getStarterPack({ const res = await agent.app.bsky.graph.getStarterPack({
starterPack: uri, starterPack: uri,
@@ -25,7 +25,6 @@ export function useStarterPackQuery({
return res.data.starterPack return res.data.starterPack
}, },
enabled: Boolean(did) && Boolean(rkey), enabled: Boolean(did) && Boolean(rkey),
staleTime: STALE.MINUTES.FIVE,
}) })
} }
@@ -40,13 +39,32 @@ export function useDeleteStarterPackMutation({
const {currentAccount} = useSession() const {currentAccount} = useSession()
return useMutation({ return useMutation({
mutationFn: async (rkey: string) => { mutationFn: async (rkey: string, listRkey?: string) => {
await agent.app.bsky.graph.starterpack.delete({ await agent.app.bsky.graph.starterpack.delete({
repo: currentAccount!.did, repo: currentAccount!.did,
rkey, rkey,
}) })
if (listRkey) {
await agent.app.bsky.graph.list.delete({
repo: currentAccount!.did,
rkey: listRkey,
})
}
}, },
onError, onError,
onSuccess, onSuccess,
}) })
} }
export async function invalidateStarterPack({
queryClient,
did,
rkey,
}: {
queryClient: QueryClient
did: string
rkey: string
}) {
await queryClient.invalidateQueries({queryKey: RQKEY(did, rkey)})
}