refactor: use query data for selected file download, keep mutation for tree downloads

The toolbar download button now uses the already-fetched download URL
from useQuery (zero extra requests), while tree node downloads keep
using useMutation with React Query-managed isPending state instead of
a hand-rolled useState wrapper.
This commit is contained in:
yyh
2026-01-29 20:48:58 +08:00
parent 9b62be2eb1
commit ff71816373
3 changed files with 31 additions and 31 deletions

View File

@ -26,7 +26,7 @@ const ArtifactsSection = ({ className }: ArtifactsSectionProps) => {
const { data: treeData, hasFiles, isLoading } = useSandboxFilesTree(sandboxId)
const downloadMutation = useDownloadSandboxFile(sandboxId)
const { mutateAsync: fetchDownloadUrl, isPending: isDownloading } = useDownloadSandboxFile(sandboxId)
const storeApi = useWorkflowStore()
const selectedArtifactPath = useStore(s => s.selectedArtifactPath)
@ -40,13 +40,13 @@ const ArtifactsSection = ({ className }: ArtifactsSectionProps) => {
const handleDownload = useCallback(async (node: SandboxFileTreeNode) => {
try {
const ticket = await downloadMutation.mutateAsync(node.path)
const ticket = await fetchDownloadUrl(node.path)
downloadUrl({ url: ticket.download_url, fileName: node.name })
}
catch (error) {
console.error('Download failed:', error)
}
}, [downloadMutation])
}, [fetchDownloadUrl])
const showBlueDot = !isExpanded && hasFiles
const showSpinner = isLoading
@ -100,7 +100,7 @@ const ArtifactsSection = ({ className }: ArtifactsSectionProps) => {
onDownload={handleDownload}
onSelect={handleSelect}
selectedPath={selectedArtifactPath ?? undefined}
isDownloading={downloadMutation.isPending}
isDownloading={isDownloading}
/>
)
: (

View File

@ -61,7 +61,7 @@ const ArtifactsTab = (headerProps: InspectHeaderProps) => {
const { data: treeData, hasFiles, isLoading } = useSandboxFilesTree(sandboxId, {
enabled: !!sandboxId,
})
const downloadMutation = useDownloadSandboxFile(sandboxId)
const { mutateAsync: fetchDownloadUrl, isPending: isDownloading } = useDownloadSandboxFile(sandboxId)
const [selectedFile, setSelectedFile] = useState<SandboxFileTreeNode | null>(null)
const { data: downloadUrlData, isLoading: isDownloadUrlLoading } = useSandboxFileDownloadUrl(
sandboxId,
@ -73,16 +73,20 @@ const ArtifactsTab = (headerProps: InspectHeaderProps) => {
setSelectedFile(node)
}, [])
const { mutateAsync: downloadFile } = downloadMutation
const handleDownload = useCallback(async (node: SandboxFileTreeNode) => {
const handleTreeDownload = useCallback(async (node: SandboxFileTreeNode) => {
try {
const ticket = await downloadFile(node.path)
const ticket = await fetchDownloadUrl(node.path)
downloadUrl({ url: ticket.download_url, fileName: node.name })
}
catch (error) {
console.error('Download failed:', error)
}
}, [downloadFile])
}, [fetchDownloadUrl])
const handleSelectedFileDownload = useCallback(() => {
if (downloadUrlData?.download_url && selectedFile)
downloadUrl({ url: downloadUrlData.download_url, fileName: selectedFile.name })
}, [downloadUrlData, selectedFile])
if (isLoading) {
return (
@ -119,10 +123,10 @@ const ArtifactsTab = (headerProps: InspectHeaderProps) => {
<div className="h-full overflow-y-auto py-1">
<ArtifactsTree
data={treeData}
onDownload={handleDownload}
onDownload={handleTreeDownload}
onSelect={handleFileSelect}
selectedPath={selectedFile?.path}
isDownloading={downloadMutation.isPending}
isDownloading={isDownloading}
/>
</div>
)}
@ -160,8 +164,8 @@ const ArtifactsTab = (headerProps: InspectHeaderProps) => {
</div>
<div className="flex shrink-0 items-center gap-1">
<ActionButton
onClick={() => handleDownload(file)}
disabled={downloadMutation.isPending}
onClick={handleSelectedFileDownload}
disabled={!downloadUrlData?.download_url}
aria-label={`Download ${file.name}`}
>
<FileDownload01 className="h-4 w-4" />

View File

@ -3,10 +3,7 @@ import type {
SandboxFileNode,
SandboxFileTreeNode,
} from '@/types/sandbox-file'
import {
useMutation,
useQuery,
} from '@tanstack/react-query'
import { useMutation, useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { consoleClient, consoleQuery } from '@/service/client'
@ -39,20 +36,6 @@ export function useGetSandboxFiles(
})
}
export function useDownloadSandboxFile(sandboxId: string | undefined) {
return useMutation({
mutationKey: consoleQuery.sandboxFile.downloadFile.mutationKey(),
mutationFn: (path: string) => {
if (!sandboxId)
throw new Error('sandboxId is required')
return consoleClient.sandboxFile.downloadFile({
params: { sandboxId },
body: { path },
})
},
})
}
export function useSandboxFileDownloadUrl(
sandboxId: string | undefined,
path: string | undefined,
@ -67,6 +50,19 @@ export function useSandboxFileDownloadUrl(
})
}
export function useDownloadSandboxFile(sandboxId: string | undefined) {
return useMutation({
mutationFn: (path: string) => {
if (!sandboxId)
throw new Error('sandboxId is required')
return consoleClient.sandboxFile.downloadFile({
params: { sandboxId },
body: { path },
})
},
})
}
function buildTreeFromFlatList(nodes: SandboxFileNode[]): SandboxFileTreeNode[] {
const nodeMap = new Map<string, SandboxFileTreeNode>()
const roots: SandboxFileTreeNode[] = []