fix: prevent duplicate vault folder pickers

This commit is contained in:
lucaronin
2026-05-14 11:59:20 +02:00
parent ff4939fb29
commit 7cd346bde0
2 changed files with 40 additions and 13 deletions

View File

@@ -85,6 +85,25 @@ describe('pickFolder', () => {
})
})
it('ignores overlapping native folder picker requests while one is open', async () => {
vi.mocked(isTauri).mockReturnValue(true)
vi.mocked(isRestartRequiredAfterUpdate).mockReturnValue(false)
let resolveOpen: ((path: string) => void) | null = null
openMock.mockReturnValueOnce(new Promise((resolve) => {
resolveOpen = resolve
}))
const firstRequest = pickFolder('Open vault folder')
const secondRequest = pickFolder('Open vault folder')
await expect(secondRequest).resolves.toBeNull()
expect(openMock).toHaveBeenCalledTimes(1)
resolveOpen?.('/Users/test/restored-vault')
await expect(firstRequest).resolves.toBe('/Users/test/restored-vault')
})
it('normalizes native file URLs to filesystem paths', async () => {
vi.mocked(isTauri).mockReturnValue(true)
vi.mocked(isRestartRequiredAfterUpdate).mockReturnValue(false)

View File

@@ -75,24 +75,32 @@ function normalizePickedFolderPath(selected: string | string[] | null): string |
}
}
let folderPickerRequestInFlight = false
/**
* Opens a native folder picker dialog (Tauri) or falls back to prompt (browser).
* Returns the selected folder path, or null if the user cancelled.
*/
export async function pickFolder(title?: string): Promise<string | null> {
if (isTauri()) {
if (isRestartRequiredAfterUpdate()) {
throw new NativeFolderPickerBlockedError()
}
if (folderPickerRequestInFlight) return null
const { open } = await import('@tauri-apps/plugin-dialog')
const selected = await open({
directory: true,
multiple: false,
title: title ?? 'Select folder',
})
return normalizePickedFolderPath(selected)
folderPickerRequestInFlight = true
try {
if (isTauri()) {
if (isRestartRequiredAfterUpdate()) {
throw new NativeFolderPickerBlockedError()
}
const { open } = await import('@tauri-apps/plugin-dialog')
const selected = await open({
directory: true,
multiple: false,
title: title ?? 'Select folder',
})
return normalizePickedFolderPath(selected)
}
return normalizePickedFolderPath(prompt(title ?? 'Enter folder path:'))
} finally {
folderPickerRequestInFlight = false
}
// Browser fallback: prompt for path
return normalizePickedFolderPath(prompt(title ?? 'Enter folder path:'))
}