feat(timeline): focus follows archive, snaps to first on view load
- New selection.focusAfter(excluded) walks selection.order forward past the archived/restored set so X-ing through the timeline keeps the cursor on the next live photo instead of falling back to photo[0] via the auto-anchor effect. Wired into gridKeyNav.toggleArchive (X key) and BulkActionBar.onArchive. - Auto-focus effect on the timeline always re-anchors to photos[0] on view load (pageCount → 1), instead of preserving a stale uid from the previous filter. - PhotoGrid re-anchors focus when the previously focused uid isn't in the new photo set, so drilling into a /tags category drops the cursor on its first tile instead of carrying a stale selection from whatever view the user came from. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,12 @@
|
||||
type PpAlbum
|
||||
} from '$lib/services/photoprism';
|
||||
import { batchEdit } from '$lib/services/batch';
|
||||
import { clearSelection, selection, setFocused } from '$lib/stores/selection.svelte';
|
||||
import {
|
||||
clearSelection,
|
||||
focusAfter,
|
||||
selection,
|
||||
setFocused
|
||||
} from '$lib/stores/selection.svelte';
|
||||
import { filters } from '$lib/stores/filters.svelte';
|
||||
import { popAndRun, push as pushUndo, undoStack } from '$lib/stores/undo.svelte';
|
||||
import { isAuthenticated } from '$lib/stores/session.svelte';
|
||||
@@ -98,6 +103,10 @@
|
||||
await batchRestore(ids);
|
||||
void qc.invalidateQueries({ queryKey: ['photos'] });
|
||||
});
|
||||
// Advance focus to the photo immediately after the archived
|
||||
// set before the multi-selection is dropped — lets the user
|
||||
// keep stepping through the timeline with X.
|
||||
focusAfter(ids);
|
||||
clearSelection();
|
||||
toast.success(`Archived ${ids.length}`);
|
||||
} catch (err) {
|
||||
@@ -173,11 +182,27 @@
|
||||
heapPickerOpen = false;
|
||||
await withBusy(async () => {
|
||||
try {
|
||||
await addToHeap(heap.UID, ids);
|
||||
const { added } = await addToHeap(heap.UID, ids);
|
||||
qc.invalidateQueries({ queryKey: ['heaps'] });
|
||||
toast.success(`Added ${ids.length} → ${heap.Title}`);
|
||||
pushUndo(`Added ${ids.length} to ${heap.Title}`, async () => {
|
||||
await removeFromHeap(heap.UID, ids);
|
||||
// PhotoPrism returns 200 even when nothing was added (UIDs
|
||||
// already present or unknown to the index) — surface the
|
||||
// real delta so the user isn't fooled by a green toast over
|
||||
// a no-op.
|
||||
if (added.length === 0) {
|
||||
toast.error(`Nothing added to ${heap.Title}`, {
|
||||
description: `PhotoPrism rejected all ${ids.length} UIDs (already in heap, or not indexed).`
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (added.length < ids.length) {
|
||||
toast.success(`Added ${added.length}/${ids.length} → ${heap.Title}`, {
|
||||
description: 'The rest were already in this heap.'
|
||||
});
|
||||
} else {
|
||||
toast.success(`Added ${added.length} → ${heap.Title}`);
|
||||
}
|
||||
pushUndo(`Added ${added.length} to ${heap.Title}`, async () => {
|
||||
await removeFromHeap(heap.UID, added);
|
||||
qc.invalidateQueries({ queryKey: ['heaps'] });
|
||||
});
|
||||
clearSelection();
|
||||
|
||||
Reference in New Issue
Block a user