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:
@@ -30,6 +30,7 @@
|
||||
startBulk,
|
||||
setDetail,
|
||||
doneBulk,
|
||||
removedBulk,
|
||||
failBulk,
|
||||
markRemoved,
|
||||
clearRemoved
|
||||
@@ -127,6 +128,9 @@
|
||||
ids: string[];
|
||||
label: string;
|
||||
doneLabel: string;
|
||||
/** Destructive removal (archive / delete): flash a red cross, then hide
|
||||
* the tiles via markRemoved after the flash instead of green check. */
|
||||
removing?: boolean;
|
||||
}
|
||||
|
||||
async function withBusy<T>(fn: () => Promise<T>, bulk?: BulkConfig): Promise<T> {
|
||||
@@ -135,8 +139,15 @@
|
||||
try {
|
||||
const result = await fn();
|
||||
if (bulk) {
|
||||
doneBulk(bulk.doneLabel, bulk.ids);
|
||||
await delay(1000);
|
||||
if (bulk.removing) {
|
||||
// Destructive: red-cross flash, then pull tiles from the grid.
|
||||
removedBulk(bulk.doneLabel, bulk.ids);
|
||||
await delay(500);
|
||||
markRemoved(bulk.ids);
|
||||
} else {
|
||||
doneBulk(bulk.doneLabel, bulk.ids);
|
||||
await delay(1000);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
@@ -196,7 +207,6 @@
|
||||
await withBusy(async () => {
|
||||
try {
|
||||
await batchArchive(ids);
|
||||
markRemoved(ids);
|
||||
pushUndo(`Archived ${ids.length}`, async () => {
|
||||
await batchRestore(ids);
|
||||
void qc.invalidateQueries({ queryKey: ['photos'] });
|
||||
@@ -207,7 +217,7 @@
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : 'Archive failed', { id: tid });
|
||||
}
|
||||
}, { ids, label: 'Archiving', doneLabel: `Archived ${ids.length}` });
|
||||
}, { ids, label: 'Archiving', doneLabel: `Archived ${ids.length}`, removing: true });
|
||||
}
|
||||
|
||||
async function onDelete() {
|
||||
@@ -222,14 +232,13 @@
|
||||
await withBusy(async () => {
|
||||
try {
|
||||
await batchDelete(ids);
|
||||
markRemoved(ids);
|
||||
focusAfter(ids);
|
||||
clearSelection();
|
||||
toast.success(`Deleted ${ids.length}`, { id: tid });
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : 'Delete failed', { id: tid });
|
||||
}
|
||||
}, { ids, label: 'Deleting', doneLabel: `Deleted ${ids.length}` });
|
||||
}, { ids, label: 'Deleting', doneLabel: `Deleted ${ids.length}`, removing: true });
|
||||
}
|
||||
|
||||
async function onRestore() {
|
||||
|
||||
Reference in New Issue
Block a user