migrate the bookmark and draft queries to the appview client

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-08-03 17:49:19 +03:00
parent c0aafe4ffd
commit 8a87a5fdea
3 changed files with 62 additions and 49 deletions
+29 -16
View File
@@ -1,4 +1,4 @@
import {AppBskyDraftCreateDraft, AppBskyDraftDefs} from '@atproto/api'
import {AppBskyDraftDefs} from '@atproto/api'
import {
useInfiniteQuery,
useMutation,
@@ -6,10 +6,12 @@ import {
} from '@tanstack/react-query'
import {isNetworkError} from '#/lib/strings/errors'
import {useAgent} from '#/state/session'
import {matchXrpcError} from '#/lib/xrpc-error'
import {useAppviewClient} from '#/state/session'
import {type ComposerState} from '#/view/com/composer/state/composer'
import {useAnalytics} from '#/analytics'
import {getDeviceId} from '#/analytics/identifiers'
import {app} from '#/lexicons'
import {composerStateToDraft, draftViewToSummary} from './api'
import {logger} from './logger'
import * as storage from './storage'
@@ -20,7 +22,7 @@ const DRAFTS_QUERY_KEY = ['drafts']
* Hook to list all drafts for the current account
*/
export function useDraftsQuery() {
const agent = useAgent()
const client = useAppviewClient()
const ax = useAnalytics()
return useInfiniteQuery({
@@ -28,10 +30,12 @@ export function useDraftsQuery() {
queryFn: async ({pageParam}) => {
// Ensure media cache is populated before checking which media exists
await storage.ensureMediaCachePopulated()
const res = await agent.app.bsky.draft.getDrafts({cursor: pageParam})
const data = await client.call(app.bsky.draft.getDrafts, {
cursor: pageParam,
})
return {
cursor: res.data.cursor,
drafts: res.data.drafts.map(view =>
cursor: data.cursor,
drafts: data.drafts.map(view =>
draftViewToSummary({
view,
analytics: ax,
@@ -116,7 +120,7 @@ export async function loadDraftMedia(draft: AppBskyDraftDefs.Draft): Promise<{
* This ensures we don't lose data if the network request fails.
*/
export function useSaveDraftMutation() {
const agent = useAgent()
const client = useAppviewClient()
const queryClient = useQueryClient()
return useMutation({
@@ -132,7 +136,14 @@ export function useSaveDraftMutation() {
originalLocalRefs: Set<string> | undefined
}> => {
// Convert composer state to server draft format
const {draft, localRefPaths} = await composerStateToDraft(composerState)
const {draft: apiDraft, localRefPaths} =
await composerStateToDraft(composerState)
/*
* `composerStateToDraft` builds the draft against the `@atproto/api`
* types, whose string fields are unbranded, so it is asserted once here
* to the vendored input type.
*/
const draft = apiDraft as unknown as app.bsky.draft.defs.Draft
logger.debug('saving draft', {
existingDraftId,
@@ -147,7 +158,7 @@ export function useSaveDraftMutation() {
logger.debug('updating existing draft on server', {
draftId: existingDraftId,
})
await agent.app.bsky.draft.updateDraft({
await client.call(app.bsky.draft.updateDraft, {
draft: {
id: existingDraftId,
draft,
@@ -157,8 +168,8 @@ export function useSaveDraftMutation() {
} else {
// Create new draft
logger.debug('creating new draft on server')
const res = await agent.app.bsky.draft.createDraft({draft})
draftId = res.data.id
const data = await client.call(app.bsky.draft.createDraft, {draft})
draftId = data.id
logger.debug('created new draft', {draftId})
}
@@ -203,7 +214,7 @@ export function useSaveDraftMutation() {
},
onError: error => {
// Check for draft limit error
if (error instanceof AppBskyDraftCreateDraft.DraftLimitReachedError) {
if (matchXrpcError(error, app.bsky.draft.createDraft)) {
logger.error('Draft limit reached', {safeMessage: error.message})
// Error will be handled by caller
} else if (!isNetworkError(error)) {
@@ -220,7 +231,7 @@ export function useSaveDraftMutation() {
* Takes the full draft data to avoid re-fetching for media cleanup.
*/
export function useDeleteDraftMutation() {
const agent = useAgent()
const client = useAppviewClient()
const queryClient = useQueryClient()
return useMutation({
@@ -231,7 +242,7 @@ export function useDeleteDraftMutation() {
draft: AppBskyDraftDefs.Draft
}) => {
// Delete from server first - if this fails, we keep local media for retry
await agent.app.bsky.draft.deleteDraft({id: draftId})
await client.call(app.bsky.draft.deleteDraft, {id: draftId})
},
onSuccess: async (_, {draft}) => {
// Only delete local media after server deletion succeeds
@@ -264,7 +275,7 @@ export function useDeleteDraftMutation() {
* Takes draftId and originalLocalRefs from composer state.
*/
export function useCleanupPublishedDraftMutation() {
const agent = useAgent()
const client = useAppviewClient()
const queryClient = useQueryClient()
return useMutation({
@@ -280,7 +291,9 @@ export function useCleanupPublishedDraftMutation() {
mediaFileCount: originalLocalRefs.size,
})
// Delete from server first
await agent.app.bsky.draft.deleteDraft({id: draftId})
await client.call(app.bsky.draft.deleteDraft, {
id: draftId,
})
logger.debug('deleted draft from server', {draftId})
},
onSuccess: async (_, {originalLocalRefs}) => {