perf: evict archived/deleted photos from cache immediately
Remove archived/restored/deleted/approved UIDs from all cached photo-list pages right after the API confirms, so the grid updates on the same tick instead of waiting for a network round-trip. Also removes the 400ms doneBulk animation delay (now unnecessary since tiles vanish instantly).
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { batchEdit } from '$lib/services/batch';
|
||||
import { invalidatePhotos } from '$lib/services/bulk';
|
||||
import { invalidatePhotos, evictFromCache } from '$lib/services/bulk';
|
||||
import {
|
||||
addToHeap,
|
||||
approvePhoto,
|
||||
@@ -194,6 +194,7 @@ export function gridKeyNav(node: HTMLElement, params: GridKeyNavParams = {}) {
|
||||
return;
|
||||
}
|
||||
doneBulk(doneLabel, ids);
|
||||
evictFromCache(ids);
|
||||
focusAfter(ids);
|
||||
clearSelection();
|
||||
invalidatePhotos(ids);
|
||||
@@ -233,6 +234,7 @@ export function gridKeyNav(node: HTMLElement, params: GridKeyNavParams = {}) {
|
||||
return;
|
||||
}
|
||||
doneBulk(`Deleted ${ids.length}`, ids);
|
||||
evictFromCache(ids);
|
||||
focusAfter(ids);
|
||||
clearSelection();
|
||||
invalidatePhotos(ids);
|
||||
@@ -270,6 +272,7 @@ export function gridKeyNav(node: HTMLElement, params: GridKeyNavParams = {}) {
|
||||
doneBulk(`Kept ${ids.length}`, ids);
|
||||
toast.success(`Kept ${ids.length}`, { id: tid });
|
||||
}
|
||||
evictFromCache(ids);
|
||||
focusAfter(ids);
|
||||
clearSelection();
|
||||
invalidatePhotos(ids);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import { useQueryClient } from '@tanstack/svelte-query';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { batchArchive } from '$lib/services/photoprism';
|
||||
import { evictFromCache } from '$lib/services/bulk';
|
||||
import { startBulk, doneBulk, failBulk } from '$lib/stores/bulkAction.svelte';
|
||||
import PhotoGrid from '$lib/components/timeline/PhotoGrid.svelte';
|
||||
import { type ReviewGroup } from '$lib/services/adapters/review';
|
||||
@@ -35,6 +36,7 @@
|
||||
startBulk(`Archiving…`, uids);
|
||||
try {
|
||||
await batchArchive(uids);
|
||||
evictFromCache(uids);
|
||||
doneBulk(`Archived ${uids.length}`, uids);
|
||||
toast.success(`Archived ${uids.length}`, { id: tid });
|
||||
void qc.invalidateQueries({ queryKey: ['review-groups'] });
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
} from '$lib/services/photoprism';
|
||||
import { batchEdit } from '$lib/services/batch';
|
||||
import { acceptDateAndKeep, cachedPhoto } from '$lib/services/photoActions';
|
||||
import { evictFromCache } from '$lib/services/bulk';
|
||||
import { suggestDateFromPath } from '$lib/utils/suggestDateFromPath';
|
||||
import { photoNameAndDir } from '$lib/types/photoprism';
|
||||
import {
|
||||
@@ -129,7 +130,6 @@
|
||||
const result = await fn();
|
||||
if (bulk) {
|
||||
doneBulk(bulk.doneLabel, bulk.ids);
|
||||
await delay(400);
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
@@ -152,6 +152,7 @@
|
||||
setDetail(p?.FileName ?? completedId);
|
||||
}
|
||||
});
|
||||
evictFromCache(ids);
|
||||
if (errors.length) {
|
||||
toast.error(`Kept ${updated.length}; ${errors.length} failed`, { id: tid });
|
||||
} else {
|
||||
@@ -179,6 +180,7 @@
|
||||
await withBusy(async () => {
|
||||
try {
|
||||
await batchArchive(ids);
|
||||
evictFromCache(ids);
|
||||
pushUndo(`Archived ${ids.length}`, async () => {
|
||||
await batchRestore(ids);
|
||||
void qc.invalidateQueries({ queryKey: ['photos'] });
|
||||
@@ -204,6 +206,7 @@
|
||||
await withBusy(async () => {
|
||||
try {
|
||||
await batchDelete(ids);
|
||||
evictFromCache(ids);
|
||||
focusAfter(ids);
|
||||
clearSelection();
|
||||
toast.success(`Deleted ${ids.length}`, { id: tid });
|
||||
@@ -220,6 +223,7 @@
|
||||
await withBusy(async () => {
|
||||
try {
|
||||
await batchRestore(ids);
|
||||
evictFromCache(ids);
|
||||
pushUndo(`Restored ${ids.length}`, async () => {
|
||||
await batchArchive(ids);
|
||||
void qc.invalidateQueries({ queryKey: ['photos'] });
|
||||
|
||||
@@ -32,6 +32,32 @@ export function invalidatePhotos(uids: string[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove uids from every cached photo-list page so the grid updates
|
||||
* instantly instead of waiting for a refetch round-trip. Call after the
|
||||
* API confirms the mutation, then still invalidate for eventual sync.
|
||||
*/
|
||||
export function evictFromCache(uids: string[]): void {
|
||||
const uidSet = new Set(uids);
|
||||
const lists = queryClient.getQueriesData<PpPhoto[] | { pages?: PpPhoto[][] }>({
|
||||
queryKey: ['photos']
|
||||
});
|
||||
for (const [key, data] of lists) {
|
||||
if (!data) continue;
|
||||
if (Array.isArray(data)) {
|
||||
const filtered = data.filter((p) => !uidSet.has(p.UID));
|
||||
if (filtered.length < data.length) {
|
||||
queryClient.setQueryData(key, filtered);
|
||||
}
|
||||
} else if (data && 'pages' in data && Array.isArray(data.pages)) {
|
||||
const pages = data.pages.map((page: PpPhoto[]) =>
|
||||
page.filter((p) => !uidSet.has(p.UID))
|
||||
);
|
||||
queryClient.setQueryData(key, { ...data, pages });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a patch to every uid. The patch can be a static body or a per-photo
|
||||
* function (used by keyword merges which need to read each photo's current
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { batchEdit } from './batch';
|
||||
import { invalidatePhotos } from './bulk';
|
||||
import { invalidatePhotos, evictFromCache } from './bulk';
|
||||
import {
|
||||
approvePhoto,
|
||||
batchArchive,
|
||||
@@ -68,6 +68,7 @@ export async function dismissPhotos(uids: string[]): Promise<void> {
|
||||
if (uids.length === 0) return;
|
||||
const tid = toast.loading(`Dismissing ${uids.length}…`);
|
||||
const { updated, errors } = await batchEdit(uids, (id) => approvePhoto(id));
|
||||
evictFromCache(uids);
|
||||
focusAfter(uids);
|
||||
clearSelection();
|
||||
invalidatePhotos(uids);
|
||||
@@ -107,6 +108,7 @@ export async function acceptDateAndKeep(uids: string[]): Promise<void> {
|
||||
await approvePhoto(id);
|
||||
return id;
|
||||
});
|
||||
evictFromCache(uids);
|
||||
focusAfter(uids);
|
||||
clearSelection();
|
||||
invalidatePhotos(uids);
|
||||
@@ -133,6 +135,7 @@ export async function archivePhotos(uids: string[]): Promise<void> {
|
||||
toast.error(err instanceof Error ? err.message : 'Archive failed', { id: tid });
|
||||
return;
|
||||
}
|
||||
evictFromCache(uids);
|
||||
pushUndo(`Archived ${uids.length}`, async () => {
|
||||
await batchRestore(uids);
|
||||
invalidatePhotos(uids);
|
||||
|
||||
Reference in New Issue
Block a user