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

@ -392,6 +392,7 @@
"viewerImage": "Viewer Image",
"compareImage": "Compare Image",
"openInViewer": "Open in Viewer",
"searchImages": "Search Images by Metadata...",
"selectAllOnPage": "Select All On Page",
"selectAllOnBoard": "Select All On Board",
"showArchivedBoards": "Show Archived Boards",

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;

View File

@ -7293,145 +7293,145 @@ export type components = {
project_id: string | null;
};
InvocationOutputMap: {
img_channel_offset: components["schemas"]["ImageOutput"];
create_denoise_mask: components["schemas"]["DenoiseMaskOutput"];
round_float: components["schemas"]["FloatOutput"];
img_blur: components["schemas"]["ImageOutput"];
sdxl_model_loader: components["schemas"]["SDXLModelLoaderOutput"];
lblend: components["schemas"]["LatentsOutput"];
img_lerp: components["schemas"]["ImageOutput"];
zoe_depth_image_processor: components["schemas"]["ImageOutput"];
range_of_size: components["schemas"]["IntegerCollectionOutput"];
freeu: components["schemas"]["UNetOutput"];
alpha_mask_to_tensor: components["schemas"]["MaskOutput"];
latents: components["schemas"]["LatentsOutput"];
controlnet: components["schemas"]["ControlOutput"];
canny_image_processor: components["schemas"]["ImageOutput"];
color_map_image_processor: components["schemas"]["ImageOutput"];
l2i: components["schemas"]["ImageOutput"];
string_join_three: components["schemas"]["StringOutput"];
crop_latents: components["schemas"]["LatentsOutput"];
img_resize: components["schemas"]["ImageOutput"];
img_crop: components["schemas"]["ImageOutput"];
face_off: components["schemas"]["FaceOffOutput"];
seamless: components["schemas"]["SeamlessModeOutput"];
string: components["schemas"]["StringOutput"];
img_watermark: components["schemas"]["ImageOutput"];
img_paste: components["schemas"]["ImageOutput"];
ideal_size: components["schemas"]["IdealSizeOutput"];
img_scale: components["schemas"]["ImageOutput"];
mul: components["schemas"]["IntegerOutput"];
conditioning: components["schemas"]["ConditioningOutput"];
add: components["schemas"]["IntegerOutput"];
conditioning_collection: components["schemas"]["ConditioningCollectionOutput"];
create_gradient_mask: components["schemas"]["GradientMaskOutput"];
float: components["schemas"]["FloatOutput"];
sdxl_refiner_compel_prompt: components["schemas"]["ConditioningOutput"];
string_collection: components["schemas"]["StringCollectionOutput"];
image_mask_to_tensor: components["schemas"]["MaskOutput"];
infill_cv2: components["schemas"]["ImageOutput"];
boolean_collection: components["schemas"]["BooleanCollectionOutput"];
color: components["schemas"]["ColorOutput"];
lresize: components["schemas"]["LatentsOutput"];
image: components["schemas"]["ImageOutput"];
esrgan: components["schemas"]["ImageOutput"];
image_collection: components["schemas"]["ImageCollectionOutput"];
metadata: components["schemas"]["MetadataOutput"];
scheduler: components["schemas"]["SchedulerOutput"];
img_pad_crop: components["schemas"]["ImageOutput"];
integer: components["schemas"]["IntegerOutput"];
boolean: components["schemas"]["BooleanOutput"];
float_math: components["schemas"]["FloatOutput"];
sdxl_compel_prompt: components["schemas"]["ConditioningOutput"];
tile_image_processor: components["schemas"]["ImageOutput"];
img_channel_multiply: components["schemas"]["ImageOutput"];
tomask: components["schemas"]["ImageOutput"];
sub: components["schemas"]["IntegerOutput"];
img_mul: components["schemas"]["ImageOutput"];
string_split: components["schemas"]["String2Output"];
model_identifier: components["schemas"]["ModelIdentifierOutput"];
pair_tile_image: components["schemas"]["PairTileImageOutput"];
heuristic_resize: components["schemas"]["ImageOutput"];
infill_lama: components["schemas"]["ImageOutput"];
lineart_anime_image_processor: components["schemas"]["ImageOutput"];
float_range: components["schemas"]["FloatCollectionOutput"];
infill_patchmatch: components["schemas"]["ImageOutput"];
mediapipe_face_processor: components["schemas"]["ImageOutput"];
cv_inpaint: components["schemas"]["ImageOutput"];
denoise_latents: components["schemas"]["LatentsOutput"];
range: components["schemas"]["IntegerCollectionOutput"];
content_shuffle_image_processor: components["schemas"]["ImageOutput"];
compel: components["schemas"]["ConditioningOutput"];
leres_image_processor: components["schemas"]["ImageOutput"];
prompt_from_file: components["schemas"]["StringCollectionOutput"];
noise: components["schemas"]["NoiseOutput"];
i2l: components["schemas"]["LatentsOutput"];
pidi_image_processor: components["schemas"]["ImageOutput"];
merge_metadata: components["schemas"]["MetadataOutput"];
img_conv: components["schemas"]["ImageOutput"];
ip_adapter: components["schemas"]["IPAdapterOutput"];
div: components["schemas"]["IntegerOutput"];
dw_openpose_image_processor: components["schemas"]["ImageOutput"];
rand_int: components["schemas"]["IntegerOutput"];
calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"];
show_image: components["schemas"]["ImageOutput"];
calculate_image_tiles_min_overlap: components["schemas"]["CalculateImageTilesOutput"];
float_to_int: components["schemas"]["IntegerOutput"];
infill_rgba: components["schemas"]["ImageOutput"];
face_mask_detection: components["schemas"]["FaceMaskOutput"];
sdxl_lora_collection_loader: components["schemas"]["SDXLLoRALoaderOutput"];
iterate: components["schemas"]["IterateInvocationOutput"];
rectangle_mask: components["schemas"]["MaskOutput"];
merge_tiles_to_image: components["schemas"]["ImageOutput"];
lineart_image_processor: components["schemas"]["ImageOutput"];
lora_collection_loader: components["schemas"]["LoRALoaderOutput"];
lora_selector: components["schemas"]["LoRASelectorOutput"];
midas_depth_image_processor: components["schemas"]["ImageOutput"];
face_identifier: components["schemas"]["ImageOutput"];
save_image: components["schemas"]["ImageOutput"];
integer_math: components["schemas"]["IntegerOutput"];
segment_anything_processor: components["schemas"]["ImageOutput"];
sdxl_refiner_compel_prompt: components["schemas"]["ConditioningOutput"];
zoe_depth_image_processor: components["schemas"]["ImageOutput"];
collect: components["schemas"]["CollectInvocationOutput"];
range: components["schemas"]["IntegerCollectionOutput"];
unsharp_mask: components["schemas"]["ImageOutput"];
string_split_neg: components["schemas"]["StringPosNegOutput"];
img_chan: components["schemas"]["ImageOutput"];
float_collection: components["schemas"]["FloatCollectionOutput"];
lora_loader: components["schemas"]["LoRALoaderOutput"];
random_range: components["schemas"]["IntegerCollectionOutput"];
invert_tensor_mask: components["schemas"]["MaskOutput"];
clip_skip: components["schemas"]["CLIPSkipInvocationOutput"];
string_replace: components["schemas"]["StringOutput"];
face_identifier: components["schemas"]["ImageOutput"];
heuristic_resize: components["schemas"]["ImageOutput"];
range_of_size: components["schemas"]["IntegerCollectionOutput"];
latents_collection: components["schemas"]["LatentsCollectionOutput"];
color_map_image_processor: components["schemas"]["ImageOutput"];
img_ilerp: components["schemas"]["ImageOutput"];
infill_patchmatch: components["schemas"]["ImageOutput"];
face_off: components["schemas"]["FaceOffOutput"];
string_collection: components["schemas"]["StringCollectionOutput"];
sdxl_model_loader: components["schemas"]["SDXLModelLoaderOutput"];
sdxl_lora_loader: components["schemas"]["SDXLLoRALoaderOutput"];
string_join: components["schemas"]["StringOutput"];
img_hue_adjust: components["schemas"]["ImageOutput"];
img_nsfw: components["schemas"]["ImageOutput"];
lblend: components["schemas"]["LatentsOutput"];
conditioning_collection: components["schemas"]["ConditioningCollectionOutput"];
string_split_neg: components["schemas"]["StringPosNegOutput"];
img_watermark: components["schemas"]["ImageOutput"];
infill_lama: components["schemas"]["ImageOutput"];
div: components["schemas"]["IntegerOutput"];
show_image: components["schemas"]["ImageOutput"];
tile_to_properties: components["schemas"]["TileToPropertiesOutput"];
calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"];
mask_combine: components["schemas"]["ImageOutput"];
main_model_loader: components["schemas"]["ModelLoaderOutput"];
img_ilerp: components["schemas"]["ImageOutput"];
string_replace: components["schemas"]["StringOutput"];
sdxl_refiner_model_loader: components["schemas"]["SDXLRefinerModelLoaderOutput"];
hed_image_processor: components["schemas"]["ImageOutput"];
latents_collection: components["schemas"]["LatentsCollectionOutput"];
infill_tile: components["schemas"]["ImageOutput"];
vae_loader: components["schemas"]["VAEOutput"];
depth_anything_image_processor: components["schemas"]["ImageOutput"];
lscale: components["schemas"]["LatentsOutput"];
t2i_adapter: components["schemas"]["T2IAdapterOutput"];
metadata_item: components["schemas"]["MetadataItemOutput"];
blank_image: components["schemas"]["ImageOutput"];
tiled_multi_diffusion_denoise_latents: components["schemas"]["LatentsOutput"];
canvas_paste_back: components["schemas"]["ImageOutput"];
rand_float: components["schemas"]["FloatOutput"];
mask_from_id: components["schemas"]["ImageOutput"];
segment_anything_processor: components["schemas"]["ImageOutput"];
sub: components["schemas"]["IntegerOutput"];
normalbae_image_processor: components["schemas"]["ImageOutput"];
mask_edge: components["schemas"]["ImageOutput"];
dynamic_prompt: components["schemas"]["StringCollectionOutput"];
color_correct: components["schemas"]["ImageOutput"];
integer_math: components["schemas"]["IntegerOutput"];
core_metadata: components["schemas"]["MetadataOutput"];
integer_collection: components["schemas"]["IntegerCollectionOutput"];
collect: components["schemas"]["CollectInvocationOutput"];
mlsd_image_processor: components["schemas"]["ImageOutput"];
invert_tensor_mask: components["schemas"]["MaskOutput"];
create_gradient_mask: components["schemas"]["GradientMaskOutput"];
string_split: components["schemas"]["String2Output"];
step_param_easing: components["schemas"]["FloatCollectionOutput"];
metadata: components["schemas"]["MetadataOutput"];
img_pad_crop: components["schemas"]["ImageOutput"];
integer: components["schemas"]["IntegerOutput"];
img_mul: components["schemas"]["ImageOutput"];
calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"];
color: components["schemas"]["ColorOutput"];
infill_rgba: components["schemas"]["ImageOutput"];
t2i_adapter: components["schemas"]["T2IAdapterOutput"];
denoise_latents: components["schemas"]["LatentsOutput"];
img_lerp: components["schemas"]["ImageOutput"];
img_channel_offset: components["schemas"]["ImageOutput"];
img_crop: components["schemas"]["ImageOutput"];
alpha_mask_to_tensor: components["schemas"]["MaskOutput"];
color_correct: components["schemas"]["ImageOutput"];
calculate_image_tiles_min_overlap: components["schemas"]["CalculateImageTilesOutput"];
img_hue_adjust: components["schemas"]["ImageOutput"];
lresize: components["schemas"]["LatentsOutput"];
img_blur: components["schemas"]["ImageOutput"];
compel: components["schemas"]["ConditioningOutput"];
sdxl_lora_collection_loader: components["schemas"]["SDXLLoRALoaderOutput"];
float_to_int: components["schemas"]["IntegerOutput"];
boolean: components["schemas"]["BooleanOutput"];
string_join_three: components["schemas"]["StringOutput"];
add: components["schemas"]["IntegerOutput"];
merge_tiles_to_image: components["schemas"]["ImageOutput"];
core_metadata: components["schemas"]["MetadataOutput"];
lscale: components["schemas"]["LatentsOutput"];
mlsd_image_processor: components["schemas"]["ImageOutput"];
image_collection: components["schemas"]["ImageCollectionOutput"];
crop_latents: components["schemas"]["LatentsOutput"];
image_mask_to_tensor: components["schemas"]["MaskOutput"];
lora_collection_loader: components["schemas"]["LoRALoaderOutput"];
ip_adapter: components["schemas"]["IPAdapterOutput"];
pidi_image_processor: components["schemas"]["ImageOutput"];
rand_int: components["schemas"]["IntegerOutput"];
img_conv: components["schemas"]["ImageOutput"];
scheduler: components["schemas"]["SchedulerOutput"];
img_paste: components["schemas"]["ImageOutput"];
noise: components["schemas"]["NoiseOutput"];
img_scale: components["schemas"]["ImageOutput"];
i2l: components["schemas"]["LatentsOutput"];
main_model_loader: components["schemas"]["ModelLoaderOutput"];
blank_image: components["schemas"]["ImageOutput"];
mask_edge: components["schemas"]["ImageOutput"];
seamless: components["schemas"]["SeamlessModeOutput"];
esrgan: components["schemas"]["ImageOutput"];
canvas_paste_back: components["schemas"]["ImageOutput"];
mul: components["schemas"]["IntegerOutput"];
dynamic_prompt: components["schemas"]["StringCollectionOutput"];
controlnet: components["schemas"]["ControlOutput"];
l2i: components["schemas"]["ImageOutput"];
ideal_size: components["schemas"]["IdealSizeOutput"];
latents: components["schemas"]["LatentsOutput"];
midas_depth_image_processor: components["schemas"]["ImageOutput"];
tomask: components["schemas"]["ImageOutput"];
float_math: components["schemas"]["FloatOutput"];
round_float: components["schemas"]["FloatOutput"];
cv_inpaint: components["schemas"]["ImageOutput"];
create_denoise_mask: components["schemas"]["DenoiseMaskOutput"];
model_identifier: components["schemas"]["ModelIdentifierOutput"];
pair_tile_image: components["schemas"]["PairTileImageOutput"];
lineart_image_processor: components["schemas"]["ImageOutput"];
img_nsfw: components["schemas"]["ImageOutput"];
infill_cv2: components["schemas"]["ImageOutput"];
clip_skip: components["schemas"]["CLIPSkipInvocationOutput"];
dw_openpose_image_processor: components["schemas"]["ImageOutput"];
img_resize: components["schemas"]["ImageOutput"];
iterate: components["schemas"]["IterateInvocationOutput"];
rectangle_mask: components["schemas"]["MaskOutput"];
canny_image_processor: components["schemas"]["ImageOutput"];
calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"];
mask_from_id: components["schemas"]["ImageOutput"];
metadata_item: components["schemas"]["MetadataItemOutput"];
infill_tile: components["schemas"]["ImageOutput"];
tiled_multi_diffusion_denoise_latents: components["schemas"]["LatentsOutput"];
img_channel_multiply: components["schemas"]["ImageOutput"];
boolean_collection: components["schemas"]["BooleanCollectionOutput"];
lora_loader: components["schemas"]["LoRALoaderOutput"];
float_collection: components["schemas"]["FloatCollectionOutput"];
string: components["schemas"]["StringOutput"];
freeu: components["schemas"]["UNetOutput"];
lineart_anime_image_processor: components["schemas"]["ImageOutput"];
depth_anything_image_processor: components["schemas"]["ImageOutput"];
image: components["schemas"]["ImageOutput"];
face_mask_detection: components["schemas"]["FaceMaskOutput"];
rand_float: components["schemas"]["FloatOutput"];
float: components["schemas"]["FloatOutput"];
random_range: components["schemas"]["IntegerCollectionOutput"];
integer_collection: components["schemas"]["IntegerCollectionOutput"];
sdxl_refiner_model_loader: components["schemas"]["SDXLRefinerModelLoaderOutput"];
mask_combine: components["schemas"]["ImageOutput"];
tile_image_processor: components["schemas"]["ImageOutput"];
img_chan: components["schemas"]["ImageOutput"];
vae_loader: components["schemas"]["VAEOutput"];
prompt_from_file: components["schemas"]["StringCollectionOutput"];
float_range: components["schemas"]["FloatCollectionOutput"];
merge_metadata: components["schemas"]["MetadataOutput"];
sdxl_compel_prompt: components["schemas"]["ConditioningOutput"];
hed_image_processor: components["schemas"]["ImageOutput"];
lora_selector: components["schemas"]["LoRASelectorOutput"];
conditioning: components["schemas"]["ConditioningOutput"];
leres_image_processor: components["schemas"]["ImageOutput"];
mediapipe_face_processor: components["schemas"]["ImageOutput"];
content_shuffle_image_processor: components["schemas"]["ImageOutput"];
};
/**
* InvocationStartedEvent
@ -14836,6 +14836,8 @@ export type operations = {
order_dir?: components["schemas"]["SQLiteDirection"];
/** @description Whether to sort by starred images first */
starred_first?: boolean;
/** @description The term to search for */
search_term?: string | null;
};
};
responses: {