web(review): scope date suggestion to EXIF Stripped tab + 'a' shortcut
- Gate the sidebar's "Suggested from path" row on /review?tab=stripped_exif instead of a per-photo TakenSrc heuristic — PhotoPrism stores a guessed TakenAt for stripped-EXIF photos too, so the heuristic was hiding the row even when a path-derived date was available. - Same gate on the BulkActionBar's "Accept date & Keep" button. - Extract acceptDateAndKeep() + cachedPhoto() into photoActions so the bar button and a new bare-'a' shortcut in gridKeyNav share one path. - Show an 'A' kbd hint on the bar button. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,10 +14,49 @@
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { batchEdit } from './batch';
|
||||
import { invalidatePhotos } from './bulk';
|
||||
import { approvePhoto, batchArchive, batchRestore } from './photoprism';
|
||||
import {
|
||||
approvePhoto,
|
||||
batchArchive,
|
||||
batchRestore,
|
||||
buildTakenAtPatch,
|
||||
updatePhoto
|
||||
} from './photoprism';
|
||||
import { queryClient } from '$lib/queryClient';
|
||||
import { clearSelection, focusAfter } from '$lib/stores/selection.svelte';
|
||||
import { push as pushUndo } from '$lib/stores/undo.svelte';
|
||||
import { suggestDateFromPath } from '$lib/utils/suggestDateFromPath';
|
||||
import type { PpPhoto } from '$lib/types/photoprism';
|
||||
|
||||
/** Walk every cache that might hold a photo's metadata — timeline list
|
||||
* (flat or infinite), review-groups bucket, per-photo detail — without
|
||||
* forcing a refetch. Returns undefined when the uid hasn't been seen.
|
||||
* Shared by callers that need to look up photo state by uid from
|
||||
* outside a component (gridKeyNav, photoActions). */
|
||||
export function cachedPhoto(uid: string): PpPhoto | undefined {
|
||||
const lists = queryClient.getQueriesData({ queryKey: ['photos'] });
|
||||
for (const [, data] of lists) {
|
||||
if (!data) continue;
|
||||
if (Array.isArray(data)) {
|
||||
const hit = (data as PpPhoto[]).find((p) => p.UID === uid);
|
||||
if (hit) return hit;
|
||||
continue;
|
||||
}
|
||||
const pages = (data as { pages?: PpPhoto[][] }).pages;
|
||||
if (!Array.isArray(pages)) continue;
|
||||
for (const pg of pages) {
|
||||
const hit = pg?.find?.((p) => p.UID === uid);
|
||||
if (hit) return hit;
|
||||
}
|
||||
}
|
||||
const review = queryClient.getQueryData<{ photos?: PpPhoto[] }[]>(['review-groups']);
|
||||
if (review) {
|
||||
for (const group of review) {
|
||||
const hit = group.photos?.find((p) => p.UID === uid);
|
||||
if (hit) return hit;
|
||||
}
|
||||
}
|
||||
return queryClient.getQueryData<PpPhoto>(['photo', uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss photos out of the review queue by bumping their quality
|
||||
@@ -44,6 +83,38 @@ export async function dismissPhotos(uids: string[]): Promise<void> {
|
||||
toast.success(`Dismissed ${uids.length}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the selected uids, applying each photo's path-derived date
|
||||
* suggestion (when one exists) before approving it. UIDs without a
|
||||
* suggestion fall through to a plain approve. Used by the EXIF Stripped
|
||||
* review tab — the `📅 Accept date & Keep` button and the bare `a`
|
||||
* keyboard shortcut both route here so wording / focus / toast
|
||||
* behaviour stay in lockstep.
|
||||
*/
|
||||
export async function acceptDateAndKeep(uids: string[]): Promise<void> {
|
||||
if (uids.length === 0) return;
|
||||
const { updated, errors } = await batchEdit(uids, async (id) => {
|
||||
const p = cachedPhoto(id);
|
||||
const iso = p ? suggestDateFromPath({ fileName: p.FileName, path: p.Path }) : null;
|
||||
if (p && iso) {
|
||||
await updatePhoto(p, buildTakenAtPatch(`${iso}T00:00:00Z`));
|
||||
}
|
||||
await approvePhoto(id);
|
||||
return id;
|
||||
});
|
||||
focusAfter(uids);
|
||||
clearSelection();
|
||||
invalidatePhotos(uids);
|
||||
void queryClient.invalidateQueries({ queryKey: ['review-groups'] });
|
||||
if (errors.length) {
|
||||
toast.error(`Kept ${updated.length}; ${errors.length} failed`, {
|
||||
description: errors[0].message
|
||||
});
|
||||
return;
|
||||
}
|
||||
toast.success(`Kept ${uids.length}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Archive photos. Reversible via the undo stack (Restore on ⌘Z).
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user