wip change limit based on size of gallery

This commit is contained in:
Mary Hipp 2024-06-20 21:13:48 -04:00 committed by psychedelicious
parent 6a4b4ee340
commit e0a241fa4f
8 changed files with 99 additions and 42 deletions

View File

@ -12,6 +12,8 @@ import BoardsList from './Boards/BoardsList/BoardsList';
import GalleryBoardName from './GalleryBoardName'; import GalleryBoardName from './GalleryBoardName';
import GallerySettingsPopover from './GallerySettingsPopover'; import GallerySettingsPopover from './GallerySettingsPopover';
import GalleryImageGrid from './ImageGrid/GalleryImageGrid'; import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
import { GalleryImageGridContainer } from './ImageGrid/GalleryImageGridContainer';
import { GalleryPagination } from './ImageGrid/GalleryPagination';
const ImageGalleryContent = () => { const ImageGalleryContent = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -31,7 +33,7 @@ const ImageGalleryContent = () => {
}, [dispatch]); }, [dispatch]);
return ( return (
<VStack layerStyle="first" flexDirection="column" h="full" w="full" borderRadius="base" p={2}> <Flex layerStyle="first" flexDirection="column" h="full" w="full" borderRadius="base" p={2}>
{galleryHeader} {galleryHeader}
<Box w="full"> <Box w="full">
<Flex ref={resizeObserverRef} alignItems="center" justifyContent="space-between" gap={2}> <Flex ref={resizeObserverRef} alignItems="center" justifyContent="space-between" gap={2}>
@ -73,9 +75,10 @@ const ImageGalleryContent = () => {
</TabList> </TabList>
</Tabs> </Tabs>
</Flex> </Flex>
<GalleryImageGrid /> <GalleryImageGridContainer />
</Flex> </Flex>
</VStack> <GalleryPagination />
</Flex>
); );
}; };

View File

@ -7,7 +7,6 @@ import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelecto
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiImageBold, PiWarningCircleBold } from 'react-icons/pi'; import { PiImageBold, PiWarningCircleBold } from 'react-icons/pi';
import type { ImageDTO } from 'services/api/types';
import GalleryImage from './GalleryImage'; import GalleryImage from './GalleryImage';
import { useListImagesQuery } from '../../../../services/api/endpoints/images'; import { useListImagesQuery } from '../../../../services/api/endpoints/images';
@ -19,7 +18,8 @@ export const imageItemContainerTestId = 'image-item-container';
const GalleryImageGrid = () => { const GalleryImageGrid = () => {
useGalleryHotkeys(); useGalleryHotkeys();
const { t } = useTranslation(); const { t } = useTranslation();
const galleryImageMinimumWidth = useAppSelector((s) => s.gallery.galleryImageMinimumWidth);
const { galleryImageMinimumWidth, limit } = useAppSelector((s) => s.gallery);
const queryArgs = useAppSelector(selectListImagesQueryArgs); const queryArgs = useAppSelector(selectListImagesQueryArgs);
const { imageDTOs, isLoading, isSuccess, isError } = useListImagesQuery(queryArgs, { const { imageDTOs, isLoading, isSuccess, isError } = useListImagesQuery(queryArgs, {
selectFromResult: ({ data, isLoading, isSuccess, isError }) => ({ selectFromResult: ({ data, isLoading, isSuccess, isError }) => ({
@ -38,7 +38,7 @@ const GalleryImageGrid = () => {
); );
} }
if (isLoading) { if (isLoading || !limit) {
return ( return (
<Flex w="full" h="full" alignItems="center" justifyContent="center"> <Flex w="full" h="full" alignItems="center" justifyContent="center">
<IAINoContentFallback label={t('gallery.loading')} icon={PiImageBold} /> <IAINoContentFallback label={t('gallery.loading')} icon={PiImageBold} />
@ -67,19 +67,9 @@ const GalleryImageGrid = () => {
))} ))}
</Grid> </Grid>
</Box> </Box>
<GalleryPagination /> {/* <GalleryPagination /> */}
</> </>
); );
}; };
export default memo(GalleryImageGrid); export default memo(GalleryImageGrid);
const GalleryImageContainer = memo(({ imageDTO, index }: { imageDTO: ImageDTO; index: number }) => {
return (
<Box className="item-container" p={1.5} data-testid={imageItemContainerTestId}>
<GalleryImage imageDTO={imageDTO} index={index} />
</Box>
);
});
GalleryImageContainer.displayName = 'GalleryImageContainer';

View File

@ -0,0 +1,58 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { Box, Flex } from '@invoke-ai/ui-library';
import GalleryImageGrid from './GalleryImageGrid';
import { useAppSelector, useAppDispatch } from '../../../../app/store/storeHooks';
import { limitChanged } from '../../store/gallerySlice';
export const GalleryImageGridContainer = () => {
const { galleryImageMinimumWidth, limit } = useAppSelector((s) => s.gallery);
const dispatch = useAppDispatch();
const containerRef = useRef<HTMLDivElement>(null);
const calculateItemsPerPage = useCallback(() => {
const containerWidth = containerRef.current?.clientWidth;
const containerHeight = containerRef.current?.clientHeight;
console.log({ containerWidth, containerHeight, galleryImageMinimumWidth });
if (containerHeight && containerWidth) {
const numberHorizontal = Math.floor(containerWidth / galleryImageMinimumWidth);
const imageWidth = containerWidth / numberHorizontal;
const numberAllowedVertical = Math.floor(containerHeight / imageWidth);
console.log({ numberAllowedVertical, numberHorizontal });
dispatch(limitChanged(numberAllowedVertical * numberHorizontal));
}
}, [containerRef, galleryImageMinimumWidth]);
useEffect(() => {
dispatch(limitChanged(undefined));
calculateItemsPerPage();
}, [galleryImageMinimumWidth]);
useEffect(() => {
if (!containerRef.current) {
return;
}
const resizeObserver = new ResizeObserver(() => {
console.log('resize');
if (!containerRef.current) {
return;
}
dispatch(limitChanged(undefined));
calculateItemsPerPage();
});
resizeObserver.observe(containerRef.current);
dispatch(limitChanged(undefined));
calculateItemsPerPage();
return () => {
resizeObserver.disconnect();
};
}, []);
return (
<Flex flexDir="column" w="full" h="full" overflow="hidden" ref={containerRef}>
{limit && <GalleryImageGrid />}
</Flex>
);
};

View File

@ -3,12 +3,15 @@ import { useGalleryPagination } from '../../hooks/useGalleryPagination';
import { PiCaretLeftBold, PiCaretRightBold } from 'react-icons/pi'; import { PiCaretLeftBold, PiCaretRightBold } from 'react-icons/pi';
export const GalleryPagination = () => { export const GalleryPagination = () => {
const { goPrev, goNext, isPrevEnabled, isNextEnabled, pageButtons, goToPage, currentPage, rangeDisplay } = const { goPrev, goNext, isPrevEnabled, isNextEnabled, pageButtons, goToPage, currentPage, rangeDisplay, total } =
useGalleryPagination(); useGalleryPagination();
console.log({ currentPage, pageButtons });
if (!total) {
return <Flex flexDir="column" alignItems="center" gap="2" height="48px"></Flex>;
}
return ( return (
<Flex flexDir="column" alignItems="center" gap="2"> <Flex flexDir="column" alignItems="center" gap="2" height="48px">
<Flex gap={2} alignItems="flex-end"> <Flex gap={2} alignItems="flex-end">
<IconButton <IconButton
size="sm" size="sm"

View File

@ -4,19 +4,18 @@ import { useGetBoardAssetsTotalQuery, useGetBoardImagesTotalQuery } from "../../
import { useListImagesQuery } from "../../../services/api/endpoints/images"; import { useListImagesQuery } from "../../../services/api/endpoints/images";
import { selectListImagesQueryArgs } from "../store/gallerySelectors"; import { selectListImagesQueryArgs } from "../store/gallerySelectors";
import { offsetChanged } from "../store/gallerySlice"; import { offsetChanged } from "../store/gallerySlice";
import { IMAGE_LIMIT } from "../store/types";
export const useGalleryPagination = () => { export const useGalleryPagination = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { offset } = useAppSelector((s) => s.gallery); const { offset, limit } = useAppSelector((s) => s.gallery);
const queryArgs = useAppSelector(selectListImagesQueryArgs); const queryArgs = useAppSelector(selectListImagesQueryArgs);
const { count, total } = useListImagesQuery(queryArgs, { const { count, total } = useListImagesQuery(queryArgs, {
selectFromResult: ({ data }) => ({ count: data?.items.length ?? 0, total: data?.total ?? 0 }), selectFromResult: ({ data }) => ({ count: data?.items.length ?? 0, total: data?.total ?? 0 }),
}); });
const currentPage = useMemo(() => Math.ceil(offset / IMAGE_LIMIT), [offset]); const currentPage = useMemo(() => Math.ceil(offset / (limit || 0)), [offset, limit]);
const pages = useMemo(() => Math.ceil(total / IMAGE_LIMIT), [total]); const pages = useMemo(() => Math.ceil(total / (limit || 0)), [total, limit]);
const isNextEnabled = useMemo(() => { const isNextEnabled = useMemo(() => {
if (!count) { if (!count) {
@ -32,26 +31,26 @@ export const useGalleryPagination = () => {
}, [count, offset]); }, [count, offset]);
const goNext = useCallback(() => { const goNext = useCallback(() => {
dispatch(offsetChanged(offset + IMAGE_LIMIT)); dispatch(offsetChanged(offset + (limit || 0)));
}, [dispatch, offset]); }, [dispatch, offset, limit]);
const goPrev = useCallback(() => { const goPrev = useCallback(() => {
dispatch(offsetChanged(Math.max(offset - IMAGE_LIMIT, 0))); dispatch(offsetChanged(Math.max(offset - (limit || 0), 0)));
}, [dispatch, offset]); }, [dispatch, offset, limit]);
const goToPage = useCallback( const goToPage = useCallback(
(page: number) => { (page: number) => {
const p = Math.max(0, Math.min(page, pages - 1)); const p = Math.max(0, Math.min(page, pages - 1));
dispatch(offsetChanged(page * IMAGE_LIMIT)); dispatch(offsetChanged(page * (limit || 0)));
}, },
[dispatch, pages] [dispatch, pages, limit]
); );
const goToFirst = useCallback(() => { const goToFirst = useCallback(() => {
dispatch(offsetChanged(0)); dispatch(offsetChanged(0));
}, [dispatch]); }, [dispatch]);
const goToLast = useCallback(() => { const goToLast = useCallback(() => {
dispatch(offsetChanged(pages * IMAGE_LIMIT)); dispatch(offsetChanged(pages * (limit || 0)));
}, [dispatch, pages]); }, [dispatch, pages, limit]);
// calculate the page buttons to display - current page with 3 around it // calculate the page buttons to display - current page with 3 around it
const pageButtons = useMemo(() => { const pageButtons = useMemo(() => {
@ -89,10 +88,10 @@ export const useGalleryPagination = () => {
const isLastEnabled = useMemo(() => currentPage < pages - 1, [currentPage, pages]); const isLastEnabled = useMemo(() => currentPage < pages - 1, [currentPage, pages]);
const rangeDisplay = useMemo(() => { const rangeDisplay = useMemo(() => {
const startItem = currentPage * IMAGE_LIMIT + 1; const startItem = currentPage * (limit || 0) + 1;
const endItem = Math.min((currentPage + 1) * IMAGE_LIMIT, total); const endItem = Math.min((currentPage + 1) * (limit || 0), total);
return `${startItem}-${endItem} of ${total}`; return `${startItem}-${endItem} of ${total}`;
}, [total, currentPage]); }, [total, currentPage, limit]);
const api = useMemo( const api = useMemo(
() => ({ () => ({

View File

@ -1,3 +1,4 @@
import { SkipToken, skipToken } from '@reduxjs/toolkit/query';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { selectGallerySlice } from 'features/gallery/store/gallerySlice'; import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
import { ASSETS_CATEGORIES, IMAGE_CATEGORIES } from 'features/gallery/store/types'; import { ASSETS_CATEGORIES, IMAGE_CATEGORIES } from 'features/gallery/store/types';
@ -10,11 +11,11 @@ export const selectLastSelectedImage = createMemoizedSelector(
export const selectListImagesQueryArgs = createMemoizedSelector( export const selectListImagesQueryArgs = createMemoizedSelector(
selectGallerySlice, selectGallerySlice,
(gallery): ListImagesArgs => ({ (gallery): ListImagesArgs | SkipToken => (gallery.limit ? {
board_id: gallery.selectedBoardId, board_id: gallery.selectedBoardId,
categories: gallery.galleryView === 'images' ? IMAGE_CATEGORIES : ASSETS_CATEGORIES, categories: gallery.galleryView === 'images' ? IMAGE_CATEGORIES : ASSETS_CATEGORIES,
offset: gallery.offset, offset: gallery.offset,
limit: gallery.limit, limit: gallery.limit,
is_intermediate: false, is_intermediate: false,
}) } : skipToken)
); );

View File

@ -19,7 +19,7 @@ const initialGalleryState: GalleryState = {
selectedBoardId: 'none', selectedBoardId: 'none',
galleryView: 'images', galleryView: 'images',
boardSearchText: '', boardSearchText: '',
limit: IMAGE_LIMIT, limit: undefined,
offset: 0, offset: 0,
isImageViewerOpen: true, isImageViewerOpen: true,
imageToCompare: null, imageToCompare: null,
@ -72,7 +72,6 @@ export const gallerySlice = createSlice({
state.selectedBoardId = action.payload.boardId; state.selectedBoardId = action.payload.boardId;
state.galleryView = 'images'; state.galleryView = 'images';
state.offset = 0; state.offset = 0;
state.limit = IMAGE_LIMIT;
}, },
autoAddBoardIdChanged: (state, action: PayloadAction<BoardId>) => { autoAddBoardIdChanged: (state, action: PayloadAction<BoardId>) => {
if (!action.payload) { if (!action.payload) {
@ -108,6 +107,9 @@ export const gallerySlice = createSlice({
offsetChanged: (state, action: PayloadAction<number>) => { offsetChanged: (state, action: PayloadAction<number>) => {
state.offset = action.payload; state.offset = action.payload;
}, },
limitChanged: (state, action: PayloadAction<number | undefined>) => {
state.limit = action.payload;
},
}, },
extraReducers: (builder) => { extraReducers: (builder) => {
builder.addMatcher(isAnyBoardDeleted, (state, action) => { builder.addMatcher(isAnyBoardDeleted, (state, action) => {
@ -150,7 +152,8 @@ export const {
comparedImagesSwapped, comparedImagesSwapped,
comparisonFitChanged, comparisonFitChanged,
comparisonModeCycled, comparisonModeCycled,
offsetChanged offsetChanged,
limitChanged
} = gallerySlice.actions; } = gallerySlice.actions;
const isAnyBoardDeleted = isAnyOf( const isAnyBoardDeleted = isAnyOf(

View File

@ -2,7 +2,7 @@ import type { ImageCategory, ImageDTO } from 'services/api/types';
export const IMAGE_CATEGORIES: ImageCategory[] = ['general']; export const IMAGE_CATEGORIES: ImageCategory[] = ['general'];
export const ASSETS_CATEGORIES: ImageCategory[] = ['control', 'mask', 'user', 'other']; export const ASSETS_CATEGORIES: ImageCategory[] = ['control', 'mask', 'user', 'other'];
export const IMAGE_LIMIT = 20; export const IMAGE_LIMIT = 15;
export type GalleryView = 'images' | 'assets'; export type GalleryView = 'images' | 'assets';
export type BoardId = 'none' | (string & Record<never, never>); export type BoardId = 'none' | (string & Record<never, never>);
@ -19,7 +19,7 @@ export type GalleryState = {
galleryView: GalleryView; galleryView: GalleryView;
boardSearchText: string; boardSearchText: string;
offset: number; offset: number;
limit: number; limit: number | undefined;
alwaysShowImageSizeBadge: boolean; alwaysShowImageSizeBadge: boolean;
imageToCompare: ImageDTO | null; imageToCompare: ImageDTO | null;
comparisonMode: ComparisonMode; comparisonMode: ComparisonMode;