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 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 00:06:18 +02:00
parent e124809ad5
commit a52f171946

View File

@@ -43,7 +43,13 @@
type Section, type Section,
type TagCategory type TagCategory
} from '$lib/stores/filters.svelte'; } 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 { openMove } from '$lib/stores/moveDialog.svelte';
import { indexer } from '$lib/stores/indexer.svelte'; import { indexer } from '$lib/stores/indexer.svelte';
import FolderTree, { buildTree } from './FolderTree.svelte'; import FolderTree, { buildTree } from './FolderTree.svelte';
@@ -285,10 +291,15 @@
} }
const createFolderMut = createMutation(() => ({ 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) => { onSuccess: (r) => {
qc.invalidateQueries({ queryKey: ['folders'] }); qc.invalidateQueries({ queryKey: ['folders'] });
toast.success(`Folder created: ${r.path}`); toast.success(`Folder created: ${toUserPath(r.path)}`);
}, },
onError: (err) => onError: (err) =>
toast.error(err instanceof Error ? err.message : 'Could not create folder') toast.error(err instanceof Error ? err.message : 'Could not create folder')
@@ -296,31 +307,36 @@
const renameFolderMut = createMutation(() => ({ const renameFolderMut = createMutation(() => ({
mutationFn: (args: { rel: string; newName: string }) => mutationFn: (args: { rel: string; newName: string }) =>
renameFolder(args.rel, args.newName), renameFolder(toOriginalsPath(args.rel), args.newName),
onSuccess: (r) => { onSuccess: (r) => {
qc.invalidateQueries({ queryKey: ['folders'] }); qc.invalidateQueries({ queryKey: ['folders'] });
qc.invalidateQueries({ queryKey: ['photos'] }); 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 the active folder filter was on this folder, follow the rename.
if (filters.folderPath === r.oldPath) { if (filters.folderPath === oldUi) {
setFolderPath(r.newPath); setFolderPath(newUi);
const params = new URLSearchParams({ folder: r.newPath }); const params = new URLSearchParams({ folder: newUi });
void goto(`/?${params.toString()}`, { keepFocus: true, noScroll: true }); void goto(`/?${params.toString()}`, { keepFocus: true, noScroll: true });
} }
toast.success(`Renamed: ${r.oldPath}${r.newPath}`); toast.success(`Renamed: ${oldUi}${newUi}`);
}, },
onError: (err) => onError: (err) =>
toast.error(err instanceof Error ? err.message : 'Rename failed') toast.error(err instanceof Error ? err.message : 'Rename failed')
})); }));
const deleteFolderMut = createMutation(() => ({ const deleteFolderMut = createMutation(() => ({
mutationFn: (rel: string) => deleteFolder(rel), mutationFn: (rel: string) => deleteFolder(toOriginalsPath(rel)),
onSuccess: (r) => { onSuccess: (r) => {
qc.invalidateQueries({ queryKey: ['folders'] }); 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); setFolderPath(null);
void goto('/', { keepFocus: true, noScroll: true }); void goto('/', { keepFocus: true, noScroll: true });
} }
toast.success(`Folder deleted: ${r.path}`); toast.success(`Folder deleted: ${ui}`);
}, },
onError: (err) => onError: (err) =>
toast.error(err instanceof Error ? err.message : 'Delete failed') toast.error(err instanceof Error ? err.message : 'Delete failed')