diff --git a/src/components/contacts/screens/ViewMatches.tsx b/src/components/contacts/screens/ViewMatches.tsx index dab3a79c6b..0ef4711919 100644 --- a/src/components/contacts/screens/ViewMatches.tsx +++ b/src/components/contacts/screens/ViewMatches.tsx @@ -21,7 +21,7 @@ import { optimisticRemoveMatch, useMatchesPassthroughQuery, } from '#/state/queries/find-contacts' -import {useAgent, useAppviewClient, useSession} from '#/state/session' +import {useAppviewClient, usePdsClient, useSession} from '#/state/session' import {List, type ListMethods} from '#/view/com/util/List' import {UserAvatar} from '#/view/com/util/UserAvatar' import {OnboardingPosition} from '#/screens/Onboarding/Layout' @@ -91,7 +91,7 @@ export function ViewMatches({ const gutter = useGutters([0, 'wide']) const moderationOpts = useModerationOpts() const queryClient = useQueryClient() - const agent = useAgent() + const pdsClient = usePdsClient() const client = useAppviewClient() const insets = useSafeAreaInsets() const listRef = useRef(null) @@ -127,7 +127,10 @@ export function ViewMatches({ }) } - const uris = await wait(500, bulkWriteFollows(agent, followableDids)) + const uris = await wait( + 500, + bulkWriteFollows(pdsClient, client, followableDids), + ) for (const did of followableDids) { const uri = uris.get(did) diff --git a/src/components/dialogs/lists/CreateListFromStarterPackDialog.tsx b/src/components/dialogs/lists/CreateListFromStarterPackDialog.tsx index 3aabbec9ff..ceb07fb0e0 100644 --- a/src/components/dialogs/lists/CreateListFromStarterPackDialog.tsx +++ b/src/components/dialogs/lists/CreateListFromStarterPackDialog.tsx @@ -1,13 +1,14 @@ import {View} from 'react-native' -import { - type $Typed, - type AppBskyGraphDefs, - type AppBskyGraphListitem, - type AppBskyGraphStarterpack, - AtUri, - type ComAtprotoRepoApplyWrites, -} from '@atproto/api' +import {type AppBskyGraphDefs, type AppBskyGraphStarterpack} from '@atproto/api' import {TID} from '@atproto/common-web' +import {type $Typed} from '@atproto/lex' +import { + type AtIdentifierString, + AtUri, + type AtUriString, + type DidString, + toDatetimeString, +} from '@atproto/syntax' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' @@ -20,7 +21,7 @@ import {wait} from '#/lib/async/wait' import {type NavigationProp} from '#/lib/routes/types' import {logger} from '#/logger' import {getAllListMembers} from '#/state/queries/list-members' -import {useAgent, useAppviewClient, useSession} from '#/state/session' +import {useAppviewClient, usePdsClient, useSession} from '#/state/session' import {atoms as a, platform, useTheme, web} from '#/alf' import {Admonition} from '#/components/Admonition' import {Button, ButtonText} from '#/components/Button' @@ -29,6 +30,7 @@ import {Loader} from '#/components/Loader' import * as Toast from '#/components/Toast' import {Text} from '#/components/Typography' import {useAnalytics} from '#/analytics' +import {app, com} from '#/lexicons' import {CreateOrEditListDialog} from './CreateOrEditListDialog' export function CreateListFromStarterPackDialog({ @@ -40,8 +42,8 @@ export function CreateListFromStarterPackDialog({ }) { const {_} = useLingui() const t = useTheme() - const agent = useAgent() const appviewClient = useAppviewClient() + const pdsClient = usePdsClient() const ax = useAnalytics() const {currentAccount} = useSession() const navigation = useNavigation() @@ -80,13 +82,14 @@ export function CreateListFromStarterPackDialog({ ) if (items.length > 0) { - const listitemWrites: $Typed[] = + const listitemWrites: $Typed[] = items.map(item => { - const listitemRecord: $Typed = { + const listitemRecord: $Typed = { $type: 'app.bsky.graph.listitem', - subject: item.subject.did, - list: listUri, - createdAt: new Date().toISOString(), + // the list view is still legacy-typed, so its strings are unbranded + subject: item.subject.did as DidString, + list: listUri as AtUriString, + createdAt: toDatetimeString(new Date()), } return { $type: 'com.atproto.repo.applyWrites#create', @@ -98,8 +101,8 @@ export function CreateListFromStarterPackDialog({ const chunks = chunk(listitemWrites, 50) for (const c of chunks) { - await agent.com.atproto.repo.applyWrites({ - repo: currentAccount.did, + await pdsClient.call(com.atproto.repo.applyWrites, { + repo: currentAccount.did as AtIdentifierString, writes: c, }) } @@ -107,10 +110,10 @@ export function CreateListFromStarterPackDialog({ await until( 5, 1e3, - (res: {data: {items: unknown[]}}) => res.data.items.length > 0, + (res: {items: unknown[]}) => res.items.length > 0, () => - agent.app.bsky.graph.getList({ - list: listUri, + appviewClient.call(app.bsky.graph.getList, { + list: listUri as AtUriString, limit: 1, }), ) diff --git a/src/lib/generate-starterpack.ts b/src/lib/generate-starterpack.ts index a53e95a6a3..a786fb47ad 100644 --- a/src/lib/generate-starterpack.ts +++ b/src/lib/generate-starterpack.ts @@ -1,11 +1,5 @@ -import { - type $Typed, - type AppBskyActorDefs, - type AppBskyGraphGetStarterPack, - type AtpAgent, - type ComAtprotoRepoApplyWrites, - type Facet, -} from '@atproto/api' +import {type Client} from '@atproto/lex' +import {type AtUriString, toDatetimeString} from '@atproto/syntax' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {useMutation} from '@tanstack/react-query' @@ -14,7 +8,8 @@ import {until} from '#/lib/async/until' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {enforceLen} from '#/lib/strings/helpers' -import {useAgent} from '#/state/session' +import {useAppviewClient, usePdsClient} from '#/state/session' +import {app, com} from '#/lexicons' import type * as bsky from '#/types/bsky' export const createStarterPackList = async ({ @@ -22,30 +17,27 @@ export const createStarterPackList = async ({ description, descriptionFacets, profiles, - agent, + client, }: { name: string description?: string - descriptionFacets?: Facet[] + descriptionFacets?: app.bsky.richtext.facet.Main[] profiles: bsky.profile.AnyProfileView[] - agent: AtpAgent + client: Client }): Promise<{uri: string; cid: string}> => { if (profiles.length === 0) throw new Error('No profiles given') - const list = await agent.app.bsky.graph.list.create( - {repo: agent.session!.did}, - { - name, - description, - descriptionFacets, - avatar: undefined, - createdAt: new Date().toISOString(), - purpose: 'app.bsky.graph.defs#referencelist', - }, - ) + const list = await client.create(app.bsky.graph.list, { + name, + description, + descriptionFacets, + avatar: undefined, + createdAt: toDatetimeString(new Date()), + purpose: 'app.bsky.graph.defs#referencelist', + }) if (!list) throw new Error('List creation failed') - await agent.com.atproto.repo.applyWrites({ - repo: agent.session!.did, + await client.call(com.atproto.repo.applyWrites, { + repo: client.assertDid, writes: profiles.map(p => createListItem({did: p.did, listUri: list.uri})), }) @@ -60,28 +52,27 @@ export function useGenerateStarterPackMutation({ onError: (e: Error) => void }) { const {_} = useLingui() - const agent = useAgent() + const appviewClient = useAppviewClient() + const pdsClient = usePdsClient() return useMutation<{uri: string; cid: string}, Error, void>({ mutationFn: async () => { - let profile: AppBskyActorDefs.ProfileViewDetailed | undefined - let profiles: AppBskyActorDefs.ProfileView[] | undefined + let profile: app.bsky.actor.defs.ProfileViewDetailed | undefined + let profiles: app.bsky.actor.defs.ProfileView[] | undefined await Promise.all([ (async () => { - profile = ( - await agent.app.bsky.actor.getProfile({ - actor: agent.session!.did, - }) - ).data + profile = await appviewClient.call(app.bsky.actor.getProfile, { + actor: pdsClient.assertDid, + }) })(), (async () => { profiles = ( - await agent.app.bsky.actor.searchActors({ + await appviewClient.call(app.bsky.actor.searchActors, { q: encodeURIComponent('*'), limit: 49, }) - ).data.actors.filter(p => p.viewer?.following) + ).actors.filter(p => p.viewer?.following) })(), ]) @@ -106,23 +97,19 @@ export function useGenerateStarterPackMutation({ const list = await createStarterPackList({ name: starterPackName, profiles, - agent, + client: pdsClient, }) - return await agent.app.bsky.graph.starterpack.create( - { - repo: agent.session!.did, - }, - { - name: starterPackName, - list: list.uri, - createdAt: new Date().toISOString(), - }, - ) + return await pdsClient.create(app.bsky.graph.starterpack, { + name: starterPackName, + // `create` returns a plain string uri + list: list.uri as AtUriString, + createdAt: toDatetimeString(new Date()), + }) }, onSuccess: async data => { - await whenAppViewReady(agent, data.uri, v => { - return typeof v?.data.starterPack.uri === 'string' + await whenAppViewReady(appviewClient, data.uri, v => { + return typeof v?.starterPack.uri === 'string' }) onSuccess(data) }, @@ -138,7 +125,7 @@ function createListItem({ }: { did: string listUri: string -}): $Typed { +}): com.atproto.repo.applyWrites.$InputBody['writes'][number] { return { $type: 'com.atproto.repo.applyWrites#create', collection: 'app.bsky.graph.listitem', @@ -152,14 +139,17 @@ function createListItem({ } async function whenAppViewReady( - agent: AtpAgent, + client: Client, uri: string, - fn: (res?: AppBskyGraphGetStarterPack.Response) => boolean, + fn: (res?: app.bsky.graph.getStarterPack.$OutputBody) => boolean, ) { await until( 5, // 5 tries 1e3, // 1s delay between tries fn, - () => agent.app.bsky.graph.getStarterPack({starterPack: uri}), + () => + client.call(app.bsky.graph.getStarterPack, { + starterPack: uri as AtUriString, + }), ) } diff --git a/src/screens/Onboarding/StepFinished/index.tsx b/src/screens/Onboarding/StepFinished/index.tsx index 05f4ef8626..6f1dea3a4f 100644 --- a/src/screens/Onboarding/StepFinished/index.tsx +++ b/src/screens/Onboarding/StepFinished/index.tsx @@ -8,6 +8,7 @@ import { type Un$Typed, } from '@atproto/api' import {TID} from '@atproto/common-web' +import {type AtUriString} from '@atproto/syntax' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' @@ -26,7 +27,7 @@ import {useSetHasCheckedForStarterPack} from '#/state/preferences/used-starter-p import {getAllListMembers} from '#/state/queries/list-members' import {preferencesQueryKey} from '#/state/queries/preferences' import {RQKEY as profileRQKey} from '#/state/queries/profile' -import {useAgent, useAppviewClient} from '#/state/session' +import {useAgent, useAppviewClient, usePdsClient} from '#/state/session' import {useOnboardingDispatch} from '#/state/shell' import { useActiveStarterPack, @@ -59,6 +60,7 @@ export function StepFinished() { const queryClient = useQueryClient() const agent = useAgent() const appviewClient = useAppviewClient() + const pdsClient = usePdsClient() const requestNotificationsPermission = useRequestNotificationsPermission() const activeStarterPack = useActiveStarterPack() const setActiveStarterPack = useSetActiveStarterPack() @@ -102,10 +104,15 @@ export function StepFinished() { await Promise.all([ bulkWriteFollows( - agent, + pdsClient, + appviewClient, [BSKY_APP_ACCOUNT_DID, ...(listItems?.map(i => i.subject.did) ?? [])], starterPack - ? {uri: starterPack.uri, cid: starterPack.cid} + ? // the starter pack view is still legacy-typed + { + uri: starterPack.uri as AtUriString, + cid: starterPack.cid, + } : undefined, ), (async () => { @@ -236,6 +243,7 @@ export function StepFinished() { queryClient, agent, appviewClient, + pdsClient, dispatch, onboardDispatch, activeStarterPack, diff --git a/src/screens/Onboarding/StepSuggestedAccounts/index.tsx b/src/screens/Onboarding/StepSuggestedAccounts/index.tsx index 514e892895..f43d06cdd4 100644 --- a/src/screens/Onboarding/StepSuggestedAccounts/index.tsx +++ b/src/screens/Onboarding/StepSuggestedAccounts/index.tsx @@ -14,7 +14,7 @@ import {logger} from '#/logger' import {updateProfileShadow} from '#/state/cache/profile-shadow' import {useLanguagePrefs} from '#/state/preferences' import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {useAgent, useSession} from '#/state/session' +import {useAppviewClient, usePdsClient, useSession} from '#/state/session' import { OnboardingControls, OnboardingPosition, @@ -42,7 +42,8 @@ export function StepSuggestedAccounts() { const t = useTheme() const {gtMobile} = useBreakpoints() const moderationOpts = useModerationOpts() - const agent = useAgent() + const appviewClient = useAppviewClient() + const pdsClient = usePdsClient() const {currentAccount} = useSession() const queryClient = useQueryClient() @@ -119,7 +120,10 @@ export function StepSuggestedAccounts() { followingUri: 'pending', }) } - const uris = await wait(1e3, bulkWriteFollows(agent, followableDids)) + const uris = await wait( + 1e3, + bulkWriteFollows(pdsClient, appviewClient, followableDids), + ) for (const did of followableDids) { const uri = uris.get(did) updateProfileShadow(queryClient, did, { diff --git a/src/screens/Onboarding/StepSuggestedStarterpacks/StarterPackCard.tsx b/src/screens/Onboarding/StepSuggestedStarterpacks/StarterPackCard.tsx index fa296dfbd2..dc208917f5 100644 --- a/src/screens/Onboarding/StepSuggestedStarterpacks/StarterPackCard.tsx +++ b/src/screens/Onboarding/StepSuggestedStarterpacks/StarterPackCard.tsx @@ -1,6 +1,7 @@ import {useState} from 'react' import {View} from 'react-native' import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api' +import {type AtUriString} from '@atproto/syntax' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' @@ -11,7 +12,7 @@ import {isBlockedOrBlocking, isMuted} from '#/lib/moderation/blocked-and-muted' import {logger} from '#/logger' import {updateProfileShadow} from '#/state/cache/profile-shadow' import {getAllListMembers} from '#/state/queries/list-members' -import {useAgent, useAppviewClient, useSession} from '#/state/session' +import {useAppviewClient, usePdsClient, useSession} from '#/state/session' import {bulkWriteFollows} from '#/screens/Onboarding/util' import {AvatarStack} from '#/screens/Search/components/StarterPackCard' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' @@ -35,8 +36,8 @@ export function StarterPackCard({ const ax = useAnalytics() const {currentAccount} = useSession() const {gtPhone} = useBreakpoints() - const agent = useAgent() const appviewClient = useAppviewClient() + const pdsClient = usePdsClient() const queryClient = useQueryClient() const record = view.record const [isProcessing, setIsProcessing] = useState(false) @@ -74,8 +75,9 @@ export function StarterPackCard({ let followUris: Map try { - followUris = await bulkWriteFollows(agent, dids, { - uri: view.uri, + followUris = await bulkWriteFollows(pdsClient, appviewClient, dids, { + // the starter pack view is still legacy-typed + uri: view.uri as AtUriString, cid: view.cid, }) } catch (e) { diff --git a/src/screens/Onboarding/util.ts b/src/screens/Onboarding/util.ts index f4ecde3c5d..af8a40f092 100644 --- a/src/screens/Onboarding/util.ts +++ b/src/screens/Onboarding/util.ts @@ -1,37 +1,34 @@ -import { - type $Typed, - type AppBskyGraphFollow, - type AppBskyGraphGetFollows, - type AtpAgent, - type ComAtprotoRepoApplyWrites, - type ComAtprotoRepoStrongRef, -} from '@atproto/api' import {TID} from '@atproto/common-web' +import {type $Typed, type Client} from '@atproto/lex' +import { + type AtIdentifierString, + type DidString, + toDatetimeString, +} from '@atproto/syntax' import chunk from 'lodash.chunk' import {until} from '#/lib/async/until' +import {app, com} from '#/lexicons' export async function bulkWriteFollows( - agent: AtpAgent, + pdsClient: Client, + appviewClient: Client, dids: string[], - via?: ComAtprotoRepoStrongRef.Main, + via?: com.atproto.repo.strongRef.Main, ) { - const session = agent.session + const did = pdsClient.assertDid - if (!session) { - throw new Error(`bulkWriteFollows failed: no session`) - } - - const followRecords: $Typed[] = dids.map(did => { + const followRecords: $Typed[] = dids.map(did => { return { $type: 'app.bsky.graph.follow', - subject: did, - createdAt: new Date().toISOString(), + // callers hold plain dids read off legacy-typed views + subject: did as DidString, + createdAt: toDatetimeString(new Date()), via, } }) - const followWrites: $Typed[] = + const followWrites: $Typed[] = followRecords.map(r => ({ $type: 'com.atproto.repo.applyWrites#create', collection: 'app.bsky.graph.follow', @@ -41,35 +38,35 @@ export async function bulkWriteFollows( const chunks = chunk(followWrites, 50) for (const chunk of chunks) { - await agent.com.atproto.repo.applyWrites({ - repo: session.did, + await pdsClient.call(com.atproto.repo.applyWrites, { + repo: did, writes: chunk, }) } - await whenFollowsIndexed(agent, session.did, res => !!res.data.follows.length) + await whenFollowsIndexed(appviewClient, did, res => !!res.follows.length) const followUris = new Map() for (const r of followWrites) { followUris.set( r.value.subject as string, - `at://${session.did}/app.bsky.graph.follow/${r.rkey}`, + `at://${did}/app.bsky.graph.follow/${r.rkey}`, ) } return followUris } async function whenFollowsIndexed( - agent: AtpAgent, + appviewClient: Client, actor: string, - fn: (res: AppBskyGraphGetFollows.Response) => boolean, + fn: (res: app.bsky.graph.getFollows.$OutputBody) => boolean, ) { await until( 5, // 5 tries 1e3, // 1s delay between tries fn, () => - agent.app.bsky.graph.getFollows({ - actor, + appviewClient.call(app.bsky.graph.getFollows, { + actor: actor as AtIdentifierString, limit: 1, }), ) diff --git a/src/screens/Settings/FindContactsSettings.tsx b/src/screens/Settings/FindContactsSettings.tsx index 6104fd12b7..c8ecdf4102 100644 --- a/src/screens/Settings/FindContactsSettings.tsx +++ b/src/screens/Settings/FindContactsSettings.tsx @@ -29,7 +29,7 @@ import { useContactsMatchesQuery, useContactsSyncStatusQuery, } from '#/state/queries/find-contacts' -import {useAgent, useAppviewClient, useSession} from '#/state/session' +import {useAppviewClient, usePdsClient, useSession} from '#/state/session' import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' import {List} from '#/view/com/util/List' import {atoms as a, tokens, useGutters, useTheme} from '#/alf' @@ -370,7 +370,7 @@ function StatusHeader({ }) { const {_} = useLingui() const ax = useAnalytics() - const agent = useAgent() + const pdsClient = usePdsClient() const client = useAppviewClient() const queryClient = useQueryClient() const {currentAccount} = useSession() @@ -406,7 +406,10 @@ function StatusHeader({ followCount: didsToFollow.length, }) - const uris = await wait(500, bulkWriteFollows(agent, didsToFollow)) + const uris = await wait( + 500, + bulkWriteFollows(pdsClient, client, didsToFollow), + ) for (const did of didsToFollow) { const uri = uris.get(did) diff --git a/src/screens/StarterPack/StarterPackScreen.tsx b/src/screens/StarterPack/StarterPackScreen.tsx index e7674c00b4..c79e39c190 100644 --- a/src/screens/StarterPack/StarterPackScreen.tsx +++ b/src/screens/StarterPack/StarterPackScreen.tsx @@ -7,6 +7,7 @@ import { AtUri, type ModerationOpts, } from '@atproto/api' +import {type AtUriString} from '@atproto/syntax' import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' @@ -37,7 +38,7 @@ import { useDeleteStarterPackMutation, useStarterPackQuery, } from '#/state/queries/starter-packs' -import {useAgent, useAppviewClient, useSession} from '#/state/session' +import {useAppviewClient, usePdsClient, useSession} from '#/state/session' import {useSetActiveStarterPack} from '#/state/shell/landing' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import { @@ -311,8 +312,8 @@ function Header({ const {_} = useLingui() const t = useTheme() const {currentAccount, hasSession} = useSession() - const agent = useAgent() const appviewClient = useAppviewClient() + const pdsClient = usePdsClient() const queryClient = useQueryClient() const setActiveStarterPack = useSetActiveStarterPack() const {requestSwitchToAccount} = useLoggedOutViewControls() @@ -379,8 +380,9 @@ function Header({ let followUris: Map try { - followUris = await bulkWriteFollows(agent, dids, { - uri: starterPack.uri, + followUris = await bulkWriteFollows(pdsClient, appviewClient, dids, { + // the starter pack view is still legacy-typed + uri: starterPack.uri as AtUriString, cid: starterPack.cid, }) } catch (e) { diff --git a/src/state/queries/starter-packs.ts b/src/state/queries/starter-packs.ts index 6a1aecef0e..0c781bcfea 100644 --- a/src/state/queries/starter-packs.ts +++ b/src/state/queries/starter-packs.ts @@ -1,11 +1,10 @@ import { AppBskyFeedDefs, AppBskyGraphDefs, - type AppBskyGraphGetStarterPack, AppBskyGraphStarterpack, - type AtpAgent, - AtUri, } from '@atproto/api' +import {type Client, type LexValue} from '@atproto/lex' +import {AtUri, type AtUriString, toDatetimeString} from '@atproto/syntax' import {RichText} from '@bsky.app/sdk/richtext' import { type QueryClient, @@ -25,8 +24,8 @@ import { import {invalidateActorStarterPacksQuery} from '#/state/queries/actor-starter-packs' import {STALE} from '#/state/queries/index' import {invalidateListMembersQuery} from '#/state/queries/list-members' -import {useAgent, useAppviewClient} from '#/state/session' -import {type app} from '#/lexicons' +import {useAppviewClient, usePdsClient} from '#/state/session' +import {app, com} from '#/lexicons' import * as bsky from '#/types/bsky' const RQKEY_ROOT = 'starter-pack' @@ -56,7 +55,7 @@ export function useStarterPackQuery({ did?: string rkey?: string }) { - const agent = useAgent() + const client = useAppviewClient() return useQuery({ queryKey: RQKEY(uri ? {uri} : {did, rkey}), @@ -67,10 +66,10 @@ export function useStarterPackQuery({ uri = httpStarterPackUriToAtUri(uri) as string } - const res = await agent.app.bsky.graph.getStarterPack({ - starterPack: uri, + const res = await client.call(app.bsky.graph.getStarterPack, { + starterPack: uri as AtUriString, }) - return res.data.starterPack + return res.starterPack }, enabled: Boolean(uri) || Boolean(did && rkey), staleTime: STALE.MINUTES.FIVE, @@ -104,12 +103,12 @@ export function useCreateStarterPackMutation({ onError: (e: Error) => void }) { const queryClient = useQueryClient() - const agent = useAgent() /* * Facet/mention resolution is an appview job - it resolves handles through * the appview, and the public fallback keeps it working when logged out. */ const appviewClient = useAppviewClient() + const pdsClient = usePdsClient() return useMutation< {uri: string; cid: string}, @@ -130,30 +129,26 @@ export function useCreateStarterPackMutation({ description, profiles, descriptionFacets, - agent, + client: pdsClient, }) - return await agent.app.bsky.graph.starterpack.create( - { - repo: agent.assertDid, - }, - { - name, - description, - descriptionFacets, - list: listRes?.uri, - feeds: feeds?.map(f => ({uri: f.uri})), - createdAt: new Date().toISOString(), - }, - ) + return await pdsClient.create(app.bsky.graph.starterpack, { + name, + description, + descriptionFacets, + // `createStarterPackList` returns a plain string uri + list: listRes?.uri as AtUriString, + feeds: feeds?.map(f => ({uri: f.uri as AtUriString})), + createdAt: toDatetimeString(new Date()), + }) }, onSuccess: async data => { - await whenAppViewReady(agent, data.uri, v => { - return typeof v?.data.starterPack.uri === 'string' + await whenAppViewReady(appviewClient, data.uri, v => { + return typeof v?.starterPack.uri === 'string' }) await invalidateActorStarterPacksQuery({ queryClient, - did: agent.session!.did, + did: pdsClient.assertDid, }) onSuccess(data) }, @@ -171,8 +166,8 @@ export function useEditStarterPackMutation({ onError: (error: Error) => void }) { const queryClient = useQueryClient() - const agent = useAgent() const appviewClient = useAppviewClient() + const pdsClient = usePdsClient() return useMutation< void, @@ -197,25 +192,29 @@ export function useEditStarterPackMutation({ descriptionFacets = rt.facets } - if (!AppBskyGraphStarterpack.isRecord(currentStarterPack.record)) { + if (!bsky.isType(app.bsky.graph.starterpack, currentStarterPack.record)) { throw new Error('Invalid starter pack') } const removedItems = currentListItems.filter( i => - i.subject.did !== agent.session?.did && + i.subject.did !== pdsClient.did && !profiles.find(p => p.did === i.subject.did && p.did), ) if (removedItems.length !== 0) { const chunks = chunk(removedItems, 50) for (const chunk of chunks) { - await agent.com.atproto.repo.applyWrites({ - repo: agent.session!.did, - writes: chunk.map(i => ({ - $type: 'com.atproto.repo.applyWrites#delete', - collection: 'app.bsky.graph.listitem', - rkey: new AtUri(i.uri).rkey, - })), + await pdsClient.call(com.atproto.repo.applyWrites, { + repo: pdsClient.assertDid, + writes: chunk.map( + ( + i, + ): com.atproto.repo.applyWrites.$InputBody['writes'][number] => ({ + $type: 'com.atproto.repo.applyWrites#delete', + collection: 'app.bsky.graph.listitem', + rkey: new AtUri(i.uri).rkeySafe, + }), + ), }) } } @@ -226,33 +225,44 @@ export function useEditStarterPackMutation({ if (addedProfiles.length > 0) { const chunks = chunk(addedProfiles, 50) for (const chunk of chunks) { - await agent.com.atproto.repo.applyWrites({ - repo: agent.session!.did, - writes: chunk.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(), - }, - })), + await pdsClient.call(com.atproto.repo.applyWrites, { + repo: pdsClient.assertDid, + writes: chunk.map( + ( + p, + ): com.atproto.repo.applyWrites.$InputBody['writes'][number] => ({ + $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, + await pdsClient.call(com.atproto.repo.putRecord, { + repo: pdsClient.assertDid, collection: 'app.bsky.graph.starterpack', rkey, record: { + $type: 'app.bsky.graph.starterpack', name, description, descriptionFacets, list: currentStarterPack.list?.uri, - feeds, + /* + * Pre-existing quirk preserved verbatim: the edit path writes whole + * `GeneratorView`s where the lexicon declares `feedItem` refs. lex + * types the raw `putRecord` body as a `LexValue`, which the legacy + * view interface does not structurally satisfy, hence the cast. + */ + feeds: feeds as unknown as LexValue, createdAt: currentStarterPack.record.createdAt, updatedAt: new Date().toISOString(), }, @@ -260,12 +270,12 @@ export function useEditStarterPackMutation({ }, onSuccess: async (_, {currentStarterPack}) => { const parsed = parseStarterPackUri(currentStarterPack.uri) - await whenAppViewReady(agent, currentStarterPack.uri, v => { - return currentStarterPack.cid !== v?.data.starterPack.cid + await whenAppViewReady(appviewClient, currentStarterPack.uri, v => { + return currentStarterPack.cid !== v?.starterPack.cid }) await invalidateActorStarterPacksQuery({ queryClient, - did: agent.session!.did, + did: pdsClient.assertDid, }) if (currentStarterPack.list) { await invalidateListMembersQuery({ @@ -275,7 +285,7 @@ export function useEditStarterPackMutation({ } await invalidateStarterPack({ queryClient, - did: agent.session!.did, + did: pdsClient.assertDid, rkey: parsed!.rkey, }) onSuccess() @@ -293,35 +303,34 @@ export function useDeleteStarterPackMutation({ onSuccess: () => void onError: (error: Error) => void }) { - const agent = useAgent() + const appviewClient = useAppviewClient() + const pdsClient = usePdsClient() const queryClient = useQueryClient() return useMutation({ mutationFn: async ({listUri, rkey}: {listUri?: string; rkey: string}) => { - if (!agent.session) { - throw new Error(`Requires signed in user`) - } + const did = pdsClient.assertDid if (listUri) { - await agent.app.bsky.graph.list.delete({ - repo: agent.session.did, - rkey: new AtUri(listUri).rkey, + await pdsClient.delete(app.bsky.graph.list, { + repo: did, + rkey: new AtUri(listUri).rkeySafe, }) } - await agent.app.bsky.graph.starterpack.delete({ - repo: agent.session.did, + await pdsClient.delete(app.bsky.graph.starterpack, { + repo: did, rkey, }) }, onSuccess: async (_, {listUri, rkey}) => { const uri = createStarterPackUri({ - did: agent.session!.did, + did: pdsClient.assertDid, rkey, }) if (uri) { - await whenAppViewReady(agent, uri, v => { - return Boolean(v?.data?.starterPack) === false + await whenAppViewReady(appviewClient, uri, v => { + return Boolean(v?.starterPack) === false }) } @@ -330,11 +339,11 @@ export function useDeleteStarterPackMutation({ } await invalidateActorStarterPacksQuery({ queryClient, - did: agent.session!.did, + did: pdsClient.assertDid, }) await invalidateStarterPack({ queryClient, - did: agent.session!.did, + did: pdsClient.assertDid, rkey, }) onSuccess() @@ -346,15 +355,18 @@ export function useDeleteStarterPackMutation({ } async function whenAppViewReady( - agent: AtpAgent, + client: Client, uri: string, - fn: (res?: AppBskyGraphGetStarterPack.Response) => boolean, + fn: (res?: app.bsky.graph.getStarterPack.$OutputBody) => boolean, ) { await until( 5, // 5 tries 1e3, // 1s delay between tries fn, - () => agent.app.bsky.graph.getStarterPack({starterPack: uri}), + () => + client.call(app.bsky.graph.getStarterPack, { + starterPack: uri as AtUriString, + }), ) }