From a52f171946d4459a7026f1131b41e74089a20374 Mon Sep 17 00:00:00 2001 From: dtoro Date: Mon, 22 Jun 2026 00:06:18 +0200 Subject: [PATCH] fix(folders): translate BasePath for create/rename/delete to fix "invalid path" The sidebar shows user-relative paths (BasePath stripped) but the sidecar operates on originals-relative paths. Folder create/rename/delete passed the stripped path straight through, so a BasePath user's ops resolved to the wrong directory and the sidecar returned "invalid path". Wrap outgoing paths with toOriginalsPath and map returned paths back with toUserPath, matching the move flow. Identity for admin accounts (empty BasePath). Co-Authored-By: Claude Opus 4.8 --- .../lib/components/layout/LeftSidebar.svelte | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/web/src/lib/components/layout/LeftSidebar.svelte b/web/src/lib/components/layout/LeftSidebar.svelte index c53eae3..41801f1 100644 --- a/web/src/lib/components/layout/LeftSidebar.svelte +++ b/web/src/lib/components/layout/LeftSidebar.svelte @@ -43,7 +43,13 @@ type Section, type TagCategory } from '$lib/stores/filters.svelte'; - import { isAuthenticated, session, userBasePath } from '$lib/stores/session.svelte'; + import { + isAuthenticated, + session, + userBasePath, + toOriginalsPath, + toUserPath + } from '$lib/stores/session.svelte'; import { openMove } from '$lib/stores/moveDialog.svelte'; import { indexer } from '$lib/stores/indexer.svelte'; import FolderTree, { buildTree } from './FolderTree.svelte'; @@ -285,10 +291,15 @@ } const createFolderMut = createMutation(() => ({ - mutationFn: (relPath: string) => createFolder(relPath), + // The sidebar deals in user-relative paths (BasePath stripped); the + // sidecar operates on originals-relative paths. Translate on the way + // out (toOriginalsPath) and back for display (toUserPath), exactly like + // the move flow — otherwise a BasePath user's folder ops resolve to the + // wrong directory and the sidecar returns "invalid path". + mutationFn: (relPath: string) => createFolder(toOriginalsPath(relPath)), onSuccess: (r) => { qc.invalidateQueries({ queryKey: ['folders'] }); - toast.success(`Folder created: ${r.path}`); + toast.success(`Folder created: ${toUserPath(r.path)}`); }, onError: (err) => toast.error(err instanceof Error ? err.message : 'Could not create folder') @@ -296,31 +307,36 @@ const renameFolderMut = createMutation(() => ({ mutationFn: (args: { rel: string; newName: string }) => - renameFolder(args.rel, args.newName), + renameFolder(toOriginalsPath(args.rel), args.newName), onSuccess: (r) => { qc.invalidateQueries({ queryKey: ['folders'] }); qc.invalidateQueries({ queryKey: ['photos'] }); + // Handler returns originals-relative paths; map back to the UI's + // user-relative space before comparing/navigating. + const oldUi = toUserPath(r.oldPath); + const newUi = toUserPath(r.newPath); // If the active folder filter was on this folder, follow the rename. - if (filters.folderPath === r.oldPath) { - setFolderPath(r.newPath); - const params = new URLSearchParams({ folder: r.newPath }); + if (filters.folderPath === oldUi) { + setFolderPath(newUi); + const params = new URLSearchParams({ folder: newUi }); void goto(`/?${params.toString()}`, { keepFocus: true, noScroll: true }); } - toast.success(`Renamed: ${r.oldPath} → ${r.newPath}`); + toast.success(`Renamed: ${oldUi} → ${newUi}`); }, onError: (err) => toast.error(err instanceof Error ? err.message : 'Rename failed') })); const deleteFolderMut = createMutation(() => ({ - mutationFn: (rel: string) => deleteFolder(rel), + mutationFn: (rel: string) => deleteFolder(toOriginalsPath(rel)), onSuccess: (r) => { qc.invalidateQueries({ queryKey: ['folders'] }); - if (filters.folderPath && filters.folderPath.startsWith(r.path)) { + const ui = toUserPath(r.path); + if (filters.folderPath && filters.folderPath.startsWith(ui)) { setFolderPath(null); void goto('/', { keepFocus: true, noScroll: true }); } - toast.success(`Folder deleted: ${r.path}`); + toast.success(`Folder deleted: ${ui}`); }, onError: (err) => toast.error(err instanceof Error ? err.message : 'Delete failed')