mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
perf(ui): improved gallery perf
This commit is contained in:
@@ -9,14 +9,12 @@ import { $customStarUI } from 'app/store/nanostores/customStarUI';
|
||||
import { useAppStore } from 'app/store/nanostores/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||
import IAIFillSkeleton from 'common/components/IAIFillSkeleton';
|
||||
import { useBoolean } from 'common/hooks/useBoolean';
|
||||
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||
import { multipleImageDndSource, singleImageDndSource } from 'features/dnd2/types';
|
||||
import { useImageContextMenu } from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
|
||||
import { getGalleryImageDataTestId } from 'features/gallery/components/ImageGrid/getGalleryImageDataTestId';
|
||||
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
|
||||
import { useScrollIntoView } from 'features/gallery/hooks/useScrollIntoView';
|
||||
import { imageToCompareChanged, selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
||||
import type { MouseEvent, MouseEventHandler } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
@@ -81,7 +79,6 @@ const galleryImageContainerSX = {
|
||||
|
||||
interface HoverableImageProps {
|
||||
imageDTO: ImageDTO;
|
||||
index: number;
|
||||
}
|
||||
|
||||
const selectAlwaysShouldImageSizeBadge = createSelector(
|
||||
@@ -89,17 +86,7 @@ const selectAlwaysShouldImageSizeBadge = createSelector(
|
||||
(gallery) => gallery.alwaysShowImageSizeBadge
|
||||
);
|
||||
|
||||
export const GalleryImage = memo(({ index, imageDTO }: HoverableImageProps) => {
|
||||
if (!imageDTO) {
|
||||
return <IAIFillSkeleton />;
|
||||
}
|
||||
|
||||
return <GalleryImageContent index={index} imageDTO={imageDTO} />;
|
||||
});
|
||||
|
||||
GalleryImage.displayName = 'GalleryImage';
|
||||
|
||||
const GalleryImageContent = memo(({ index, imageDTO }: HoverableImageProps) => {
|
||||
export const GalleryImage = memo(({ imageDTO }: HoverableImageProps) => {
|
||||
const store = useAppStore();
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [element, ref] = useState<HTMLImageElement | null>(null);
|
||||
@@ -118,8 +105,6 @@ const GalleryImageContent = memo(({ index, imageDTO }: HoverableImageProps) => {
|
||||
);
|
||||
const isSelected = useAppSelector(selectIsSelected);
|
||||
|
||||
useScrollIntoView(element, isSelected, index);
|
||||
|
||||
useEffect(() => {
|
||||
if (!element) {
|
||||
return;
|
||||
@@ -224,7 +209,7 @@ const GalleryImageContent = memo(({ index, imageDTO }: HoverableImageProps) => {
|
||||
);
|
||||
});
|
||||
|
||||
GalleryImageContent.displayName = 'GalleryImageContent';
|
||||
GalleryImage.displayName = 'GalleryImage';
|
||||
|
||||
const HoverIcons = memo(({ imageDTO, isHovered }: { imageDTO: ImageDTO; isHovered: boolean }) => {
|
||||
const alwaysShowImageSizeBadge = useAppSelector(selectAlwaysShouldImageSizeBadge);
|
||||
|
||||
@@ -20,9 +20,9 @@ const GalleryImageGrid = () => {
|
||||
useGalleryHotkeys();
|
||||
const { t } = useTranslation();
|
||||
const queryArgs = useAppSelector(selectListImagesQueryArgs);
|
||||
const { imageDTOs, isLoading, isError } = useListImagesQuery(queryArgs, {
|
||||
const { hasImages, isLoading, isError } = useListImagesQuery(queryArgs, {
|
||||
selectFromResult: ({ data, isLoading, isSuccess, isError }) => ({
|
||||
imageDTOs: data?.items ?? EMPTY_ARRAY,
|
||||
hasImages: data && data.items.length > 0,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
@@ -45,7 +45,7 @@ const GalleryImageGrid = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (imageDTOs.length === 0) {
|
||||
if (!hasImages) {
|
||||
return (
|
||||
<Flex w="full" h="full" alignItems="center" justifyContent="center">
|
||||
<IAINoContentFallback label={t('gallery.noImagesInGallery')} icon={PiImageBold} />
|
||||
@@ -53,12 +53,12 @@ const GalleryImageGrid = () => {
|
||||
);
|
||||
}
|
||||
|
||||
return <Content />;
|
||||
return <GalleryImageGridContent />;
|
||||
};
|
||||
|
||||
export default memo(GalleryImageGrid);
|
||||
|
||||
const Content = () => {
|
||||
const GalleryImageGridContent = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const galleryImageMinimumWidth = useAppSelector(selectGalleryImageMinimumWidth);
|
||||
|
||||
@@ -178,12 +178,14 @@ const Content = () => {
|
||||
gridTemplateColumns={`repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, 1fr))`}
|
||||
gap={1}
|
||||
>
|
||||
{imageDTOs.map((imageDTO, index) => (
|
||||
<GalleryImage key={imageDTO.image_name} imageDTO={imageDTO} index={index} />
|
||||
{imageDTOs.map((imageDTO) => (
|
||||
<GalleryImage key={imageDTO.image_name} imageDTO={imageDTO} />
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
<GallerySelectionCountTag />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
GalleryImageGridContent.displayName = 'GalleryImageGridContent';
|
||||
|
||||
@@ -17,6 +17,8 @@ import { useEffect } from 'react';
|
||||
* @param isSelected Whether the image is selected.
|
||||
* @param index The index of the image in the gallery.
|
||||
* @returns
|
||||
*
|
||||
* @knip-ignore
|
||||
*/
|
||||
export const useScrollIntoView = (imageContainerRef: HTMLElement | null, isSelected: boolean, index: number) => {
|
||||
const areMultiplesSelected = useAppSelector(selectHasMultipleImagesSelected);
|
||||
|
||||
Reference in New Issue
Block a user