fix: address all 6 P2 review issues from PR #8 round 4
- BackgroundTasks: per-task Retry now dispatches to the correct backend — retryFailedEmbeddings for embedding failures, queueTaggingJobs for tagging failures, or both; previously always called embedding retry only - commands: tag-cloud cache key now hashes both image IDs and embedding bytes (xxh3) so re-embedding a file after reindex correctly invalidates the cache; removed now-dead fnv_hash_ids helper - db: update_folder_path uses SUBSTR instead of replace() to rewrite only the leading path prefix, preventing corruption when the old folder name also appears in a subdirectory name - db: add_user_tag promotes an existing AI-owned tag to user-owned on conflict instead of DO NOTHING, preventing the tagger from deleting a manually added tag that coincides with an AI tag - db/commands: add clear_duplicate_scan_cache / invalidate_duplicate_scan_cache so deletion removes the stale persisted cache entry; without this a restart would reload groups containing deleted images - store: setSimilarScope re-runs loadSimilarByRegion (with saved crop) when the active collection is Region Search Results, instead of silently switching to whole-image similarity and discarding the crop
This commit is contained in:
@@ -57,6 +57,7 @@ export function BackgroundTasks() {
|
||||
const indexingProgress = useGalleryStore((state) => state.indexingProgress);
|
||||
const mediaJobProgress = useGalleryStore((state) => state.mediaJobProgress);
|
||||
const retryFailedEmbeddings = useGalleryStore((state) => state.retryFailedEmbeddings);
|
||||
const queueTaggingJobs = useGalleryStore((state) => state.queueTaggingJobs);
|
||||
const duplicateScanning = useGalleryStore((state) => state.duplicateScanning);
|
||||
const duplicateScanProgress = useGalleryStore((state) => state.duplicateScanProgress);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
@@ -475,7 +476,10 @@ export function BackgroundTasks() {
|
||||
{taskHasFailed && (
|
||||
<button
|
||||
className="rounded-lg border border-amber-500/20 bg-amber-500/10 px-2 py-0.5 text-[11px] text-amber-300 hover:bg-amber-500/20 transition-colors shrink-0"
|
||||
onClick={() => void retryFailedEmbeddings(task.id)}
|
||||
onClick={() => {
|
||||
if (task.hasFailedEmbeddings) void retryFailedEmbeddings(task.id);
|
||||
if (task.hasFailedTagging) void queueTaggingJobs(task.id);
|
||||
}}
|
||||
>
|
||||
Retry
|
||||
</button>
|
||||
|
||||
+9
-3
@@ -1126,10 +1126,14 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
|
||||
|
||||
setSimilarScope: (similarScope) => {
|
||||
set({ similarScope });
|
||||
const { similarSourceImageId, similarSourceFolderId, selectedFolderId } = get();
|
||||
const { similarSourceImageId, similarSourceFolderId, selectedFolderId, collectionTitle, similarCrop } = get();
|
||||
if (similarSourceImageId === null) return;
|
||||
const folderId = similarScope === "current_folder" ? (similarSourceFolderId ?? selectedFolderId) : null;
|
||||
void get().loadSimilarImages(similarSourceImageId, folderId, true, similarSourceFolderId);
|
||||
if (collectionTitle === "Region Search Results" && similarCrop !== null) {
|
||||
void get().loadSimilarByRegion(similarSourceImageId, similarCrop, folderId, similarSourceFolderId);
|
||||
} else {
|
||||
void get().loadSimilarImages(similarSourceImageId, folderId, true, similarSourceFolderId);
|
||||
}
|
||||
},
|
||||
|
||||
suggestImageTags: async (imageId) => {
|
||||
@@ -1498,7 +1502,7 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
|
||||
clearDuplicateSelection: () => set({ duplicateSelectedIds: new Set() }),
|
||||
|
||||
deleteSelectedDuplicates: async () => {
|
||||
const { duplicateSelectedIds } = get();
|
||||
const { duplicateSelectedIds, duplicateScanFolderId } = get();
|
||||
const ids = Array.from(duplicateSelectedIds);
|
||||
if (ids.length === 0) return 0;
|
||||
const deleted = await invoke<number>("delete_images_from_disk", { params: { image_ids: ids } });
|
||||
@@ -1509,6 +1513,8 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
|
||||
.map((g) => ({ ...g, images: g.images.filter((img) => !duplicateSelectedIds.has(img.id)) }))
|
||||
.filter((g) => g.images.length > 1),
|
||||
}));
|
||||
// Invalidate the persisted cache so a restart doesn't reload stale entries
|
||||
await invoke("invalidate_duplicate_scan_cache", { folderId: duplicateScanFolderId ?? null });
|
||||
return deleted;
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user