Improve indexing updates and thumbnail streaming
This commit is contained in:
+42
-27
@@ -79,15 +79,20 @@ function ContextMenu({
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
className="ml-auto rounded-lg px-2 py-1 text-sm text-gray-400 hover:bg-white/5 hover:text-white"
|
||||
onClick={async () => {
|
||||
await updateImageDetails(image.id, { rating: 0 });
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
{image.rating > 0 ? (
|
||||
<button
|
||||
className="ml-auto rounded-lg p-1.5 text-gray-400 hover:bg-white/5 hover:text-white"
|
||||
onClick={async () => {
|
||||
await updateImageDetails(image.id, { rating: 0 });
|
||||
onClose();
|
||||
}}
|
||||
title="Remove rating"
|
||||
>
|
||||
<svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -119,15 +124,7 @@ function ImageTile({
|
||||
onContextMenu={onContextMenu}
|
||||
title={image.filename}
|
||||
>
|
||||
{image.media_kind === "video" ? (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-fuchsia-500/10 via-white/5 to-cyan-500/10">
|
||||
<div className="rounded-full border border-white/10 bg-black/30 p-4 text-white shadow-lg">
|
||||
<svg className="h-8 w-8" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
) : src && !errored ? (
|
||||
{src && !errored ? (
|
||||
<>
|
||||
{!loaded ? <div className="absolute inset-0 animate-pulse bg-white/5" /> : null}
|
||||
<img
|
||||
@@ -140,18 +137,36 @@ function ImageTile({
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/5 text-gray-600">
|
||||
<svg className="h-9 w-9" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1}
|
||||
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-fuchsia-500/10 via-white/5 to-cyan-500/10 text-gray-300">
|
||||
{image.media_kind === "video" ? (
|
||||
<div className="rounded-full border border-white/10 bg-black/30 p-4 text-white shadow-lg">
|
||||
<svg className="h-8 w-8" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
) : (
|
||||
<svg className="h-9 w-9" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1}
|
||||
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{image.media_kind === "video" ? (
|
||||
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||
<div className="rounded-full border border-white/10 bg-black/35 p-3 text-white shadow-lg backdrop-blur-sm">
|
||||
<svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M8 5v14l11-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/75 via-black/20 to-transparent opacity-80 transition-opacity group-hover:opacity-100" />
|
||||
|
||||
<div className="absolute left-3 right-3 top-3 flex items-start justify-between gap-2">
|
||||
|
||||
+51
-1
@@ -49,6 +49,10 @@ export interface IndexedImagesBatch {
|
||||
images: ImageRecord[];
|
||||
}
|
||||
|
||||
export interface ThumbnailBatch {
|
||||
images: ImageRecord[];
|
||||
}
|
||||
|
||||
export type SortOrder =
|
||||
| "date_desc"
|
||||
| "date_asc"
|
||||
@@ -153,6 +157,20 @@ function mergeImages(currentImages: ImageRecord[], newImages: ImageRecord[], sor
|
||||
return Array.from(merged.values()).sort((a, b) => compareImages(a, b, sort));
|
||||
}
|
||||
|
||||
function countNewImages(currentImages: ImageRecord[], newImages: ImageRecord[]): number {
|
||||
const existingPaths = new Set(currentImages.map((image) => image.path));
|
||||
let count = 0;
|
||||
|
||||
for (const image of newImages) {
|
||||
if (!existingPaths.has(image.path)) {
|
||||
existingPaths.add(image.path);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
function replaceImage(images: ImageRecord[], updatedImage: ImageRecord, sort: SortOrder): ImageRecord[] {
|
||||
return mergeImages(images, [updatedImage], sort);
|
||||
}
|
||||
@@ -340,11 +358,42 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
|
||||
return state;
|
||||
}
|
||||
|
||||
const newVisibleCount = countNewImages(state.images, visibleImages);
|
||||
const images = mergeImages(state.images, visibleImages, state.sort);
|
||||
return {
|
||||
images,
|
||||
loadedCount: images.length,
|
||||
totalImages: Math.max(state.totalImages, images.length),
|
||||
totalImages: Math.max(state.totalImages + newVisibleCount, images.length),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const unlistenThumbnails = await listen<ThumbnailBatch>("thumbnail-updated", (event) => {
|
||||
const batch = event.payload;
|
||||
|
||||
set((state) => {
|
||||
const visibleImages = batch.images.filter((image) =>
|
||||
matchesFilters(
|
||||
image,
|
||||
state.selectedFolderId,
|
||||
state.mediaFilter,
|
||||
state.favoritesOnly,
|
||||
state.search,
|
||||
),
|
||||
);
|
||||
|
||||
const selectedImage =
|
||||
state.selectedImage && batch.images.some((image) => image.id === state.selectedImage?.id)
|
||||
? batch.images.find((image) => image.id === state.selectedImage?.id) ?? state.selectedImage
|
||||
: state.selectedImage;
|
||||
|
||||
if (visibleImages.length === 0) {
|
||||
return { selectedImage };
|
||||
}
|
||||
|
||||
return {
|
||||
images: mergeImages(state.images, visibleImages, state.sort),
|
||||
selectedImage,
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -352,6 +401,7 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
|
||||
return () => {
|
||||
unlistenProgress();
|
||||
unlistenImages();
|
||||
unlistenThumbnails();
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user