feat(archive): red-cross flash + instant tile removal on archive/delete
Archive/delete now flash a red cross then drop tiles from the grid immediately, instead of a green check that lingered until the slow server-reconcile refetch landed. Keyboard `x` archive previously never called markRemoved, so tiles only vanished on refetch — that lag is gone. - Add 'removed' bulk state + removedBulk() helper (red cross overlay) - gridKeyNav archive/delete: removedBulk -> 500ms flash -> markRemoved, clearRemoved once refetch settles; restore stays green check - BulkActionBar: BulkConfig.removing routes archive/delete through the red flash; approve/restore/label/note unchanged Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,15 @@ import {
|
||||
toggle
|
||||
} from '$lib/stores/selection.svelte';
|
||||
import { popAndRun, push as pushUndo } from '$lib/stores/undo.svelte';
|
||||
import { startBulk, doneBulk, failBulk, setDetail } from '$lib/stores/bulkAction.svelte';
|
||||
import {
|
||||
startBulk,
|
||||
doneBulk,
|
||||
removedBulk,
|
||||
failBulk,
|
||||
setDetail,
|
||||
markRemoved,
|
||||
clearRemoved
|
||||
} from '$lib/stores/bulkAction.svelte';
|
||||
import { openPreview, toggleLeftSidebar, toggleRightSidebar, view } from '$lib/stores/view.svelte';
|
||||
|
||||
/**
|
||||
@@ -163,6 +171,8 @@ export function gridKeyNav(node: HTMLElement, params: GridKeyNavParams = {}) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const delay = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
|
||||
|
||||
async function toggleArchive(direction: 'archive' | 'restore' | 'toggle') {
|
||||
const ids = cullTargets();
|
||||
if (ids.length === 0) {
|
||||
@@ -193,11 +203,27 @@ export function gridKeyNav(node: HTMLElement, params: GridKeyNavParams = {}) {
|
||||
toast.error(err instanceof Error ? err.message : 'Archive/restore failed', { id: tid });
|
||||
return;
|
||||
}
|
||||
doneBulk(doneLabel, ids);
|
||||
focusAfter(ids);
|
||||
clearSelection();
|
||||
invalidatePhotos(ids);
|
||||
void queryClient.invalidateQueries({ queryKey: ['marks'] });
|
||||
if (target) {
|
||||
// Destructive removal: flash a red cross, then pull the tiles out of
|
||||
// the grid immediately (markRemoved) rather than waiting on the slow
|
||||
// server-reconcile refetch. clearRemoved once the refetch settles so
|
||||
// the archived-filtered page replaces the optimistic hide.
|
||||
removedBulk(doneLabel, ids);
|
||||
focusAfter(ids);
|
||||
clearSelection();
|
||||
await delay(500);
|
||||
markRemoved(ids);
|
||||
invalidatePhotos(ids);
|
||||
const settled = queryClient.invalidateQueries({ queryKey: ['photos'] });
|
||||
void queryClient.invalidateQueries({ queryKey: ['marks'] });
|
||||
void settled.then(() => clearRemoved(ids));
|
||||
} else {
|
||||
doneBulk(doneLabel, ids);
|
||||
focusAfter(ids);
|
||||
clearSelection();
|
||||
invalidatePhotos(ids);
|
||||
void queryClient.invalidateQueries({ queryKey: ['marks'] });
|
||||
}
|
||||
toast.success(doneLabel, { id: tid });
|
||||
pushUndo(doneLabel, async () => {
|
||||
if (target) await batchRestore(ids);
|
||||
@@ -233,11 +259,16 @@ export function gridKeyNav(node: HTMLElement, params: GridKeyNavParams = {}) {
|
||||
toast.error(err instanceof Error ? err.message : 'Delete failed', { id: tid });
|
||||
return;
|
||||
}
|
||||
doneBulk(`Deleted ${ids.length}`, ids);
|
||||
// Destructive removal — same red-cross flash then immediate hide as archive.
|
||||
removedBulk(`Deleted ${ids.length}`, ids);
|
||||
focusAfter(ids);
|
||||
clearSelection();
|
||||
await delay(500);
|
||||
markRemoved(ids);
|
||||
invalidatePhotos(ids);
|
||||
const settled = queryClient.invalidateQueries({ queryKey: ['photos'] });
|
||||
void queryClient.invalidateQueries({ queryKey: ['marks'] });
|
||||
void settled.then(() => clearRemoved(ids));
|
||||
toast.success(`Deleted ${ids.length}`, { id: tid });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user