fix(file-explorer): report the saved filename in the download toast (#12959)
* fix(file-explorer): report the saved filename in the download toast The success toast named the remote node, so renaming a file in the native save dialog left the label disagreeing with its own Open action, which opens the real destination. Folder downloads had the same gap whenever sanitizeLocalDownloadFilename rewrote the remote basename. * fix(file-explorer): respect local download path semantics --------- Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com>
This commit is contained in:
parent
a77002c42b
commit
6382b8a053
|
|
@ -293,6 +293,14 @@ function makeToolbar(overrides: Partial<Parameters<typeof FileExplorerToolbar>[0
|
|||
})
|
||||
}
|
||||
|
||||
function setDownloadPlatform(platform: NodeJS.Platform): void {
|
||||
;(
|
||||
window as unknown as {
|
||||
api: { platform: { get: () => { platform: NodeJS.Platform } } }
|
||||
}
|
||||
).api.platform = { get: () => ({ platform }) }
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
toastErrorMock.mockReset()
|
||||
toastSuccessMock.mockReset()
|
||||
|
|
@ -677,7 +685,10 @@ describe('FileExplorerRow collapse folder action', () => {
|
|||
it('calls the preload download API and shows success only when not canceled', async () => {
|
||||
const downloadFile = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ canceled: false, destinationPath: '/downloads/index.ts' })
|
||||
.mockResolvedValueOnce({
|
||||
canceled: false,
|
||||
destinationPath: '/downloads/renamed\\entry.ts'
|
||||
})
|
||||
.mockResolvedValueOnce({ canceled: true })
|
||||
const openPath = vi.fn().mockResolvedValue(undefined)
|
||||
;(
|
||||
|
|
@ -690,6 +701,7 @@ describe('FileExplorerRow collapse folder action', () => {
|
|||
}
|
||||
}
|
||||
).window = { api: { fs: { downloadFile }, shell: { openPath } } }
|
||||
setDownloadPlatform('linux')
|
||||
|
||||
await downloadRemoteFile(fileNode, 'ssh-1')
|
||||
await downloadRemoteFile(fileNode, 'ssh-1')
|
||||
|
|
@ -699,7 +711,7 @@ describe('FileExplorerRow collapse folder action', () => {
|
|||
connectionId: 'ssh-1'
|
||||
})
|
||||
expect(toastSuccessMock).toHaveBeenCalledTimes(1)
|
||||
expect(toastSuccessMock).toHaveBeenCalledWith("Downloaded 'index.ts'", {
|
||||
expect(toastSuccessMock).toHaveBeenCalledWith("Downloaded 'renamed\\entry.ts'", {
|
||||
action: {
|
||||
label: 'Open',
|
||||
onClick: expect.any(Function)
|
||||
|
|
@ -709,10 +721,30 @@ describe('FileExplorerRow collapse folder action', () => {
|
|||
| { onClick: () => void }
|
||||
| undefined
|
||||
action?.onClick()
|
||||
expect(openPath).toHaveBeenCalledWith('/downloads/index.ts')
|
||||
expect(openPath).toHaveBeenCalledWith('/downloads/renamed\\entry.ts')
|
||||
expect(toastErrorMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('reports the saved folder name from a Windows destination path', async () => {
|
||||
const downloadFolder = vi.fn().mockResolvedValue({
|
||||
canceled: false,
|
||||
destinationPath: 'C:\\Users\\dev\\Downloads\\src-copy'
|
||||
})
|
||||
;(
|
||||
globalThis as unknown as {
|
||||
window: { api: { fs: { downloadFolder: typeof downloadFolder } } }
|
||||
}
|
||||
).window = { api: { fs: { downloadFolder } } }
|
||||
setDownloadPlatform('win32')
|
||||
|
||||
await downloadRemoteFile(directoryNode, 'ssh-1')
|
||||
|
||||
expect(toastSuccessMock).toHaveBeenCalledWith(
|
||||
"Downloaded folder 'src-copy'",
|
||||
expect.objectContaining({ action: expect.anything() })
|
||||
)
|
||||
})
|
||||
|
||||
it('calls the preload folder download API for SSH directory rows', async () => {
|
||||
const downloadFolder = vi.fn().mockResolvedValue({
|
||||
canceled: false,
|
||||
|
|
@ -729,6 +761,7 @@ describe('FileExplorerRow collapse folder action', () => {
|
|||
}
|
||||
}
|
||||
).window = { api: { fs: { downloadFolder }, shell: { openPath } } }
|
||||
setDownloadPlatform('linux')
|
||||
|
||||
await downloadRemoteFile(directoryNode, 'ssh-1')
|
||||
|
||||
|
|
@ -765,6 +798,7 @@ describe('FileExplorerRow collapse folder action', () => {
|
|||
}
|
||||
}
|
||||
).window = { api: { shell: { openPath } } }
|
||||
setDownloadPlatform('linux')
|
||||
|
||||
await downloadRemoteFile(fileNode, runtimeContext)
|
||||
|
||||
|
|
|
|||
|
|
@ -342,6 +342,14 @@ export function shouldShowCopyFileAction(
|
|||
)
|
||||
}
|
||||
|
||||
function getLocalDownloadName(destinationPath: string, platform: NodeJS.Platform): string {
|
||||
const lastSeparatorIndex =
|
||||
platform === 'win32'
|
||||
? Math.max(destinationPath.lastIndexOf('/'), destinationPath.lastIndexOf('\\'))
|
||||
: destinationPath.lastIndexOf('/')
|
||||
return destinationPath.slice(lastSeparatorIndex + 1)
|
||||
}
|
||||
|
||||
export async function downloadRemoteFile(
|
||||
node: TreeNode,
|
||||
connectionIdOrRuntimeContext: string | RuntimeFileOperationArgs
|
||||
|
|
@ -363,17 +371,22 @@ export async function downloadRemoteFile(
|
|||
if (result.canceled) {
|
||||
return
|
||||
}
|
||||
// Why: POSIX permits backslashes in saved names; only Windows treats them as separators.
|
||||
const savedName = getLocalDownloadName(
|
||||
result.destinationPath,
|
||||
window.api.platform.get().platform
|
||||
)
|
||||
toast.success(
|
||||
node.isDirectory
|
||||
? translate(
|
||||
'auto.components.right.sidebar.FileExplorerRow.a4029c996b',
|
||||
"Downloaded folder '{{value0}}'",
|
||||
{ value0: node.name }
|
||||
{ value0: savedName }
|
||||
)
|
||||
: translate(
|
||||
'auto.components.right.sidebar.FileExplorerRow.bce4d4e44f',
|
||||
"Downloaded '{{value0}}'",
|
||||
{ value0: node.name }
|
||||
{ value0: savedName }
|
||||
),
|
||||
{
|
||||
action: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue