feat(ui): debounced gallery search

This commit is contained in:
Mary Hipp
2024-06-30 18:39:56 -04:00
committed by psychedelicious
parent d579aefb3e
commit da05034e20
8 changed files with 204 additions and 135 deletions

View File

@@ -13,6 +13,7 @@ import GalleryBoardName from './GalleryBoardName';
import GallerySettingsPopover from './GallerySettingsPopover/GallerySettingsPopover';
import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
import { GalleryPagination } from './ImageGrid/GalleryPagination';
import { GallerySearch } from './ImageGrid/GallerySearch';
const ImageGalleryContent = () => {
const { t } = useTranslation();
@@ -81,6 +82,7 @@ const ImageGalleryContent = () => {
</Tabs>
</Flex>
<GallerySearch />
<GalleryImageGrid />
<GalleryPagination />
</Flex>

View File

@@ -19,10 +19,11 @@ const GalleryImageGrid = () => {
useGalleryHotkeys();
const { t } = useTranslation();
const queryArgs = useAppSelector(selectListImagesQueryArgs);
const { imageDTOs, isLoading, isError } = useListImagesQuery(queryArgs, {
selectFromResult: ({ data, isLoading, isSuccess, isError }) => ({
const { imageDTOs, isLoading, isFetching, isError } = useListImagesQuery(queryArgs, {
selectFromResult: ({ data, isLoading, isFetching, isSuccess, isError }) => ({
imageDTOs: data?.items ?? EMPTY_ARRAY,
isLoading,
isFetching,
isSuccess,
isError,
}),
@@ -36,7 +37,7 @@ const GalleryImageGrid = () => {
);
}
if (isLoading) {
if (isLoading || isFetching) {
return (
<Flex w="full" h="full" alignItems="center" justifyContent="center">
<IAINoContentFallback label={t('gallery.loading')} icon={PiImageBold} />

View File

@@ -0,0 +1,57 @@
import { IconButton, Input, InputGroup, InputRightElement } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { searchTermChanged } from 'features/gallery/store/gallerySlice';
import { debounce } from 'lodash-es';
import type { ChangeEvent } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi';
export const GallerySearch = () => {
const dispatch = useAppDispatch();
const { searchTerm } = useAppSelector((s) => s.gallery);
const { t } = useTranslation();
const [searchTermInput, setSearchTermInput] = useState(searchTerm);
const debouncedSetSearchTerm = useMemo(() => {
return debounce((value: string) => {
dispatch(searchTermChanged(value));
}, 1000);
}, [dispatch]);
const handleChangeInput = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
setSearchTermInput(e.target.value);
debouncedSetSearchTerm(e.target.value);
},
[debouncedSetSearchTerm]
);
const handleClearInput = useCallback(() => {
setSearchTermInput('');
dispatch(searchTermChanged(''));
}, [dispatch]);
return (
<InputGroup>
<Input
placeholder={t('gallery.searchImages')}
value={searchTermInput}
onChange={handleChangeInput}
data-testid="image-search-input"
/>
{searchTermInput && searchTermInput.length && (
<InputRightElement h="full" pe={2}>
<IconButton
onClick={handleClearInput}
size="sm"
variant="link"
aria-label={t('boards.clearSearch')}
icon={<PiXBold />}
/>
</InputRightElement>
)}
</InputGroup>
);
};

View File

@@ -22,6 +22,7 @@ export const selectListImagesQueryArgs = createMemoizedSelector(
is_intermediate: false,
starred_first: gallery.starredFirst,
order_dir: gallery.orderDir,
search_term: gallery.searchTerm,
}
: skipToken
);

View File

@@ -118,6 +118,9 @@ export const gallerySlice = createSlice({
orderDirChanged: (state, action: PayloadAction<OrderDir>) => {
state.orderDir = action.payload;
},
searchTermChanged: (state, action: PayloadAction<string | undefined>) => {
state.searchTerm = action.payload;
},
},
});
@@ -143,6 +146,7 @@ export const {
orderDirChanged,
starredFirstChanged,
shouldShowArchivedBoardsChanged,
searchTermChanged,
} = gallerySlice.actions;
export const selectGallerySlice = (state: RootState) => state.gallery;

View File

@@ -22,6 +22,7 @@ export type GalleryState = {
limit: number;
starredFirst: boolean;
orderDir: OrderDir;
searchTerm?: string;
alwaysShowImageSizeBadge: boolean;
imageToCompare: ImageDTO | null;
comparisonMode: ComparisonMode;