From 7315f5dface4caf2249bb40c13a47d220af375c0 Mon Sep 17 00:00:00 2001 From: Hailey Date: Wed, 19 Jun 2024 17:06:55 -0700 Subject: [PATCH] merge in delete mutation --- src/screens/Signup/index.tsx | 2 +- .../StarterPack/StarterPackLandingScreen.tsx | 2 +- src/screens/StarterPack/StarterPackScreen.tsx | 2 +- src/screens/StarterPack/Wizard/index.tsx | 2 +- src/state/queries/starter-pack.ts | 260 ----------------- src/state/queries/starter-packs.ts | 261 +++++++++++++++++- 6 files changed, 261 insertions(+), 268 deletions(-) delete mode 100644 src/state/queries/starter-pack.ts diff --git a/src/screens/Signup/index.tsx b/src/screens/Signup/index.tsx index 5185adcc85..3203d443cc 100644 --- a/src/screens/Signup/index.tsx +++ b/src/screens/Signup/index.tsx @@ -16,7 +16,7 @@ import {createFullHandle} from '#/lib/strings/handles' import {logger} from '#/logger' import {useServiceQuery} from '#/state/queries/service' import {useAgent} from '#/state/session' -import {useStarterPackQuery} from 'state/queries/starter-pack' +import {useStarterPackQuery} from 'state/queries/starter-packs' import {useActiveStarterPack} from 'state/shell/starter-pack' import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout' import { diff --git a/src/screens/StarterPack/StarterPackLandingScreen.tsx b/src/screens/StarterPack/StarterPackLandingScreen.tsx index 16b4461077..f8cb2616f1 100644 --- a/src/screens/StarterPack/StarterPackLandingScreen.tsx +++ b/src/screens/StarterPack/StarterPackLandingScreen.tsx @@ -16,7 +16,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {createStarterPackGooglePlayUri} from 'lib/strings/starter-pack' import {isWeb} from 'platform/detection' import {useModerationOpts} from 'state/preferences/moderation-opts' -import {useStarterPackQuery} from 'state/queries/starter-pack' +import {useStarterPackQuery} from 'state/queries/starter-packs' import { useActiveStarterPack, useSetActiveStarterPack, diff --git a/src/screens/StarterPack/StarterPackScreen.tsx b/src/screens/StarterPack/StarterPackScreen.tsx index 58cbaf3226..39ee3167e2 100644 --- a/src/screens/StarterPack/StarterPackScreen.tsx +++ b/src/screens/StarterPack/StarterPackScreen.tsx @@ -17,7 +17,7 @@ import {logEvent} from 'lib/statsig/statsig' import {isWeb} from 'platform/detection' import {RQKEY} from 'state/queries/list-members' import {useResolveDidQuery} from 'state/queries/resolve-uri' -import {useStarterPackQuery} from 'state/queries/starter-pack' +import {useStarterPackQuery} from 'state/queries/starter-packs' import {useAgent, useSession} from 'state/session' import * as Toast from '#/view/com/util/Toast' import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx index ca82fdfc7a..1f25b425ad 100644 --- a/src/screens/StarterPack/Wizard/index.tsx +++ b/src/screens/StarterPack/Wizard/index.tsx @@ -34,7 +34,7 @@ import { useCreateStarterPackMutation, useEditStarterPackMutation, useStarterPackQuery, -} from 'state/queries/starter-pack' +} from 'state/queries/starter-packs' import {useAgent, useSession} from 'state/session' import {useSetMinimalShellMode} from 'state/shell' import * as Toast from '#/view/com/util/Toast' diff --git a/src/state/queries/starter-pack.ts b/src/state/queries/starter-pack.ts deleted file mode 100644 index 70d3754fc9..0000000000 --- a/src/state/queries/starter-pack.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { - AppBskyActorDefs, - AppBskyFeedDefs, - AppBskyGraphDefs, - AppBskyGraphGetStarterPack, - AppBskyGraphStarterpack, - AtUri, - BskyAgent, -} from '@atproto/api' -import {StarterPackView} from '@atproto/api/dist/client/types/app/bsky/graph/defs' -import { - QueryClient, - useMutation, - useQuery, - useQueryClient, -} from '@tanstack/react-query' - -import {until} from 'lib/async/until' -import {createStarterPackList} from 'lib/generate-starterpack' -import { - httpStarterPackUriToAtUri, - parseStarterPackUri, -} from 'lib/strings/starter-pack' -import {invalidateActorStarterPacksQuery} from 'state/queries/actor-starter-packs' -import {invalidateListMembersQuery} from 'state/queries/list-members' -import {useAgent} from 'state/session' - -const RQKEY_ROOT = 'starter-pack' -const RQKEY = (did?: string, rkey?: string) => { - if (did?.startsWith('https://') || did?.startsWith('at://')) { - const parsed = parseStarterPackUri(did) - return [RQKEY_ROOT, parsed?.name, parsed?.rkey] - } else { - return [RQKEY_ROOT, did, rkey] - } -} - -export function useStarterPackQuery({ - uri, - did, - rkey, -}: { - uri?: string - did?: string - rkey?: string -}) { - const agent = useAgent() - - return useQuery({ - queryKey: RQKEY(did, rkey), - queryFn: async () => { - if (!uri) { - uri = `at://${did}/app.bsky.graph.starterpack/${rkey}` - } else if (uri && !uri.startsWith('at://')) { - // TODO remove this assertion - uri = httpStarterPackUriToAtUri(uri) as string - } - const res = await agent.app.bsky.graph.getStarterPack({ - starterPack: uri, - }) - return res.data.starterPack - }, - enabled: Boolean(uri) || Boolean(did && rkey), - }) -} - -export async function invalidateStarterPack({ - queryClient, - did, - rkey, -}: { - queryClient: QueryClient - did: string - rkey: string -}) { - await queryClient.invalidateQueries({queryKey: RQKEY(did, rkey)}) -} - -interface UseCreateStarterPackMutationParams { - name: string - description?: string - descriptionFacets: [] - profiles: AppBskyActorDefs.ProfileViewBasic[] - feeds?: AppBskyFeedDefs.GeneratorView[] -} - -export function useCreateStarterPackMutation({ - onSuccess, - onError, -}: { - onSuccess: (data: {uri: string; cid: string}) => void - onError: (e: Error) => void -}) { - const queryClient = useQueryClient() - const agent = useAgent() - - return useMutation< - {uri: string; cid: string}, - Error, - UseCreateStarterPackMutationParams - >({ - mutationFn: async params => { - let listRes - listRes = await createStarterPackList({...params, agent}) - return await agent.app.bsky.graph.starterpack.create( - { - repo: agent.session?.did, - validate: false, // TODO remove - }, - { - ...params, - list: listRes?.uri, - createdAt: new Date().toISOString(), - }, - ) - }, - onSuccess: async data => { - await whenAppViewReady(agent, data.uri, v => { - return typeof v?.data.starterPack.uri === 'string' - }) - await invalidateActorStarterPacksQuery({ - queryClient, - 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?.data.starterPack.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() - }, - onError: async error => { - onError(error) - }, - }) -} - -async function whenAppViewReady( - agent: BskyAgent, - uri: string, - fn: (res?: AppBskyGraphGetStarterPack.Response) => boolean, -) { - await until( - 5, // 5 tries - 1e3, // 1s delay between tries - fn, - () => agent.app.bsky.graph.getStarterPack({starterPack: uri}), - ) -} diff --git a/src/state/queries/starter-packs.ts b/src/state/queries/starter-packs.ts index 02fa94099b..c40d9d94ed 100644 --- a/src/state/queries/starter-packs.ts +++ b/src/state/queries/starter-packs.ts @@ -1,10 +1,250 @@ -import {AtUri} from '@atproto/api' -import {useMutation, useQueryClient} from '@tanstack/react-query' +import { + AppBskyActorDefs, + AppBskyFeedDefs, + AppBskyGraphDefs, + AppBskyGraphGetStarterPack, + AppBskyGraphStarterpack, + AtUri, + BskyAgent, +} from '@atproto/api' +import {StarterPackView} from '@atproto/api/dist/client/types/app/bsky/graph/defs' +import { + QueryClient, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query' -import {useAgent} from '#/state/session' +import {until} from 'lib/async/until' +import {createStarterPackList} from 'lib/generate-starterpack' +import { + httpStarterPackUriToAtUri, + parseStarterPackUri, +} from 'lib/strings/starter-pack' import {invalidateActorStarterPacksQuery} from 'state/queries/actor-starter-packs' import {invalidateListMembersQuery} from 'state/queries/list-members' -import {invalidateStarterPack} from 'state/queries/useStarterPackQuery' +import {useAgent} from 'state/session' + +const RQKEY_ROOT = 'starter-pack' +const RQKEY = (did?: string, rkey?: string) => { + if (did?.startsWith('https://') || did?.startsWith('at://')) { + const parsed = parseStarterPackUri(did) + return [RQKEY_ROOT, parsed?.name, parsed?.rkey] + } else { + return [RQKEY_ROOT, did, rkey] + } +} + +export function useStarterPackQuery({ + uri, + did, + rkey, +}: { + uri?: string + did?: string + rkey?: string +}) { + const agent = useAgent() + + return useQuery({ + queryKey: RQKEY(did, rkey), + queryFn: async () => { + if (!uri) { + uri = `at://${did}/app.bsky.graph.starterpack/${rkey}` + } else if (uri && !uri.startsWith('at://')) { + // TODO remove this assertion + uri = httpStarterPackUriToAtUri(uri) as string + } + const res = await agent.app.bsky.graph.getStarterPack({ + starterPack: uri, + }) + return res.data.starterPack + }, + enabled: Boolean(uri) || Boolean(did && rkey), + }) +} + +export async function invalidateStarterPack({ + queryClient, + did, + rkey, +}: { + queryClient: QueryClient + did: string + rkey: string +}) { + await queryClient.invalidateQueries({queryKey: RQKEY(did, rkey)}) +} + +interface UseCreateStarterPackMutationParams { + name: string + description?: string + descriptionFacets: [] + profiles: AppBskyActorDefs.ProfileViewBasic[] + feeds?: AppBskyFeedDefs.GeneratorView[] +} + +export function useCreateStarterPackMutation({ + onSuccess, + onError, +}: { + onSuccess: (data: {uri: string; cid: string}) => void + onError: (e: Error) => void +}) { + const queryClient = useQueryClient() + const agent = useAgent() + + return useMutation< + {uri: string; cid: string}, + Error, + UseCreateStarterPackMutationParams + >({ + mutationFn: async params => { + let listRes + listRes = await createStarterPackList({...params, agent}) + return await agent.app.bsky.graph.starterpack.create( + { + repo: agent.session?.did, + validate: false, // TODO remove + }, + { + ...params, + list: listRes?.uri, + createdAt: new Date().toISOString(), + }, + ) + }, + onSuccess: async data => { + await whenAppViewReady(agent, data.uri, v => { + return typeof v?.data.starterPack.uri === 'string' + }) + await invalidateActorStarterPacksQuery({ + queryClient, + 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?.data.starterPack.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() + }, + onError: async error => { + onError(error) + }, + }) +} export function useDeleteStarterPackMutation() { const agent = useAgent() @@ -40,3 +280,16 @@ export function useDeleteStarterPackMutation() { }, }) } + +async function whenAppViewReady( + agent: BskyAgent, + uri: string, + fn: (res?: AppBskyGraphGetStarterPack.Response) => boolean, +) { + await until( + 5, // 5 tries + 1e3, // 1s delay between tries + fn, + () => agent.app.bsky.graph.getStarterPack({starterPack: uri}), + ) +}