From ad6e73362213a1a3a9931d1fb624854b1e9efaea Mon Sep 17 00:00:00 2001 From: dtoro Date: Mon, 22 Jun 2026 00:06:18 +0200 Subject: [PATCH] fix(move): close the dialog when a move starts so header progress shows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The move dialog held its full-screen overlay open for the whole operation, hiding exactly the header reindex/status pill the user waits on. Snapshot the draft state, closeMove() up front, and run the move in the background with a toast.loading→success/error — mirrors the archive flow. Co-Authored-By: Claude Opus 4.8 --- .../layout/MoveToFolderDialog.svelte | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/web/src/lib/components/layout/MoveToFolderDialog.svelte b/web/src/lib/components/layout/MoveToFolderDialog.svelte index f13e4c4..d1f9ae2 100644 --- a/web/src/lib/components/layout/MoveToFolderDialog.svelte +++ b/web/src/lib/components/layout/MoveToFolderDialog.svelte @@ -120,19 +120,38 @@ // (nothing picked) so a falsy check doesn't wrongly block root. if (!s || pickedPath === null || submitting) return; submitting = true; + + // Snapshot the draft before closing — closeMove() nulls the subject, + // which the reset effect uses to wipe pickedPath/mode/subfolder. + const dest = pickedPath; + const opMode = mode; + const sub = subfolder.trim() || null; + const delHeap = mode === 'move' && deleteHeap; + const labelName = folderName; + + // Close the dialog immediately and run the move in the background. The + // move can be slow (a folder/heap with many files triggers a real + // disk move + reindex) and its progress surfaces in the header pill; + // keeping the modal + overlay up would hide exactly the feedback the + // user is waiting on. Mirrors the archive flow (toast + header pill). + closeMove(); + + const verbing = opMode === 'copy' ? 'Copying' : 'Moving'; + const tid = toast.loading(`${verbing}…`); try { if (s.kind === 'heap') { const r = await convertHeap(s.heap.UID, { - targetFolder: toOriginalsPath(pickedPath), - mode, - subfolder: subfolder.trim() || null, - deleteHeap: mode === 'move' && deleteHeap + targetFolder: toOriginalsPath(dest), + mode: opMode, + subfolder: sub, + deleteHeap: delHeap }); qc.invalidateQueries({ queryKey: ['photos'] }); qc.invalidateQueries({ queryKey: ['folders'] }); qc.invalidateQueries({ queryKey: ['heaps'] }); toast.success( - moveSummary(mode === 'copy' ? 'Copied' : 'Moved', mode === 'copy' ? r.copied : r.moved, r.errors.length) + moveSummary(opMode === 'copy' ? 'Copied' : 'Moved', opMode === 'copy' ? r.copied : r.moved, r.errors.length), + { id: tid } ); if (r.heap_deleted && filters.section === 'heap' && filters.heapUid === s.heap.UID) { setSection('all-photos'); @@ -141,32 +160,30 @@ } else if (s.kind === 'photos') { const r = await movePhotosToFolder({ uids: s.uids, - targetFolder: toOriginalsPath(pickedPath), - mode, - subfolder: subfolder.trim() || null + targetFolder: toOriginalsPath(dest), + mode: opMode, + subfolder: sub }); qc.invalidateQueries({ queryKey: ['photos'] }); qc.invalidateQueries({ queryKey: ['folders'] }); toast.success( - moveSummary(mode === 'copy' ? 'Copied' : 'Moved', mode === 'copy' ? r.copied : r.moved, r.errors.length) + moveSummary(opMode === 'copy' ? 'Copied' : 'Moved', opMode === 'copy' ? r.copied : r.moved, r.errors.length), + { id: tid } ); } else { // Folder reparent (move only). Translate both the folder's own // path and the destination parent to originals-relative for the // sidecar, which moves real directories on disk. - await moveFolder(toOriginalsPath(s.path), toOriginalsPath(pickedPath)); + await moveFolder(toOriginalsPath(s.path), toOriginalsPath(dest)); qc.invalidateQueries({ queryKey: ['photos'] }); qc.invalidateQueries({ queryKey: ['folders'] }); - const newUiPath = pickedPath === '' ? folderName : `${pickedPath}/${folderName}`; - toast.success(`Moved ${folderName} → ${pickedPath === '' ? '/' : pickedPath}`); + const newUiPath = dest === '' ? labelName : `${dest}/${labelName}`; + toast.success(`Moved ${labelName} → ${dest === '' ? '/' : dest}`, { id: tid }); // If we just moved the folder the timeline is showing, follow it. if (filters.folderPath === s.path) setFolderPath(newUiPath); } - closeMove(); } catch (err) { - toast.error(err instanceof Error ? err.message : 'Move failed'); - } finally { - submitting = false; + toast.error(err instanceof Error ? err.message : 'Move failed', { id: tid }); } }