mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): gallery filter dropdown -> Images/Assets toggle
This commit is contained in:
parent
1ddc620192
commit
7004430380
@ -122,7 +122,9 @@
|
|||||||
"noImagesInGallery": "No Images In Gallery",
|
"noImagesInGallery": "No Images In Gallery",
|
||||||
"deleteImage": "Delete Image",
|
"deleteImage": "Delete Image",
|
||||||
"deleteImageBin": "Deleted images will be sent to your operating system's Bin.",
|
"deleteImageBin": "Deleted images will be sent to your operating system's Bin.",
|
||||||
"deleteImagePermanent": "Deleted images cannot be restored."
|
"deleteImagePermanent": "Deleted images cannot be restored.",
|
||||||
|
"images": "Images",
|
||||||
|
"assets": "Assets"
|
||||||
},
|
},
|
||||||
"hotkeys": {
|
"hotkeys": {
|
||||||
"keyboardShortcuts": "Keyboard Shortcuts",
|
"keyboardShortcuts": "Keyboard Shortcuts",
|
||||||
|
@ -69,6 +69,7 @@ import {
|
|||||||
} from './listeners/receivedPageOfImages';
|
} from './listeners/receivedPageOfImages';
|
||||||
import { addStagingAreaImageSavedListener } from './listeners/stagingAreaImageSaved';
|
import { addStagingAreaImageSavedListener } from './listeners/stagingAreaImageSaved';
|
||||||
import { addCommitStagingAreaImageListener } from './listeners/addCommitStagingAreaImageListener';
|
import { addCommitStagingAreaImageListener } from './listeners/addCommitStagingAreaImageListener';
|
||||||
|
import { addImageCategoriesChangedListener } from './listeners/imageCategoriesChanged';
|
||||||
|
|
||||||
export const listenerMiddleware = createListenerMiddleware();
|
export const listenerMiddleware = createListenerMiddleware();
|
||||||
|
|
||||||
@ -166,6 +167,9 @@ addSessionCanceledPendingListener();
|
|||||||
addSessionCanceledFulfilledListener();
|
addSessionCanceledFulfilledListener();
|
||||||
addSessionCanceledRejectedListener();
|
addSessionCanceledRejectedListener();
|
||||||
|
|
||||||
// Images
|
// Fetching images
|
||||||
addReceivedPageOfImagesFulfilledListener();
|
addReceivedPageOfImagesFulfilledListener();
|
||||||
addReceivedPageOfImagesRejectedListener();
|
addReceivedPageOfImagesRejectedListener();
|
||||||
|
|
||||||
|
// Gallery
|
||||||
|
addImageCategoriesChangedListener();
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
import { log } from 'app/logging/useLogger';
|
||||||
|
import { startAppListening } from '..';
|
||||||
|
import { receivedPageOfImages } from 'services/thunks/image';
|
||||||
|
import {
|
||||||
|
imageCategoriesChanged,
|
||||||
|
selectFilteredImagesAsArray,
|
||||||
|
} from 'features/gallery/store/imagesSlice';
|
||||||
|
|
||||||
|
const moduleLog = log.child({ namespace: 'gallery' });
|
||||||
|
|
||||||
|
export const addImageCategoriesChangedListener = () => {
|
||||||
|
startAppListening({
|
||||||
|
actionCreator: imageCategoriesChanged,
|
||||||
|
effect: (action, { getState, dispatch }) => {
|
||||||
|
const filteredImagesCount = selectFilteredImagesAsArray(
|
||||||
|
getState()
|
||||||
|
).length;
|
||||||
|
|
||||||
|
if (!filteredImagesCount) {
|
||||||
|
dispatch(receivedPageOfImages());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
@ -11,7 +11,7 @@ export const addReceivedPageOfImagesFulfilledListener = () => {
|
|||||||
effect: (action, { getState, dispatch }) => {
|
effect: (action, { getState, dispatch }) => {
|
||||||
const page = action.payload;
|
const page = action.payload;
|
||||||
moduleLog.debug(
|
moduleLog.debug(
|
||||||
{ data: { page } },
|
{ data: { payload: action.payload } },
|
||||||
`Received ${page.items.length} images`
|
`Received ${page.items.length} images`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
ButtonGroup,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
CheckboxGroup,
|
CheckboxGroup,
|
||||||
Flex,
|
Flex,
|
||||||
@ -36,7 +37,13 @@ import {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
|
import { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
|
||||||
import { FaFilter, FaWrench } from 'react-icons/fa';
|
import {
|
||||||
|
FaFilter,
|
||||||
|
FaImage,
|
||||||
|
FaImages,
|
||||||
|
FaServer,
|
||||||
|
FaWrench,
|
||||||
|
} from 'react-icons/fa';
|
||||||
import { MdPhotoLibrary } from 'react-icons/md';
|
import { MdPhotoLibrary } from 'react-icons/md';
|
||||||
import HoverableImage from './HoverableImage';
|
import HoverableImage from './HoverableImage';
|
||||||
|
|
||||||
@ -47,18 +54,15 @@ import { Virtuoso, VirtuosoGrid } from 'react-virtuoso';
|
|||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { ImageCategory } from 'services/api';
|
import { ImageCategory } from 'services/api';
|
||||||
import { imageCategoriesChanged, selectImagesAll } from '../store/imagesSlice';
|
import {
|
||||||
|
ASSETS_CATEGORIES,
|
||||||
|
IMAGE_CATEGORIES,
|
||||||
|
imageCategoriesChanged,
|
||||||
|
selectImagesAll,
|
||||||
|
} from '../store/imagesSlice';
|
||||||
import { receivedPageOfImages } from 'services/thunks/image';
|
import { receivedPageOfImages } from 'services/thunks/image';
|
||||||
import { capitalize } from 'lodash-es';
|
import { capitalize } from 'lodash-es';
|
||||||
|
|
||||||
const IMAGE_CATEGORIES: ImageCategory[] = [
|
|
||||||
'general',
|
|
||||||
'control',
|
|
||||||
'mask',
|
|
||||||
'user',
|
|
||||||
'other',
|
|
||||||
];
|
|
||||||
|
|
||||||
const categorySelector = createSelector(
|
const categorySelector = createSelector(
|
||||||
[(state: RootState) => state],
|
[(state: RootState) => state],
|
||||||
(state) => {
|
(state) => {
|
||||||
@ -179,6 +183,14 @@ const ImageGalleryContent = () => {
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleClickImagesCategory = useCallback(() => {
|
||||||
|
dispatch(imageCategoriesChanged(IMAGE_CATEGORIES));
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handleClickAssetsCategory = useCallback(() => {
|
||||||
|
dispatch(imageCategoriesChanged(ASSETS_CATEGORIES));
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
@ -194,35 +206,31 @@ const ImageGalleryContent = () => {
|
|||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
>
|
>
|
||||||
<IAIPopover
|
<ButtonGroup isAttached>
|
||||||
triggerComponent={
|
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
aria-label="Gallery Filter"
|
tooltip={t('gallery.images')}
|
||||||
|
aria-label={t('gallery.images')}
|
||||||
|
onClick={handleClickImagesCategory}
|
||||||
|
isChecked={categories === IMAGE_CATEGORIES}
|
||||||
size="sm"
|
size="sm"
|
||||||
icon={<FaFilter />}
|
icon={<FaImage />}
|
||||||
/>
|
/>
|
||||||
}
|
<IAIIconButton
|
||||||
>
|
tooltip={t('gallery.assets')}
|
||||||
<Flex sx={{ flexDirection: 'column', gap: 2 }}>
|
aria-label={t('gallery.assets')}
|
||||||
<CheckboxGroup
|
onClick={handleClickAssetsCategory}
|
||||||
value={categories}
|
isChecked={categories === ASSETS_CATEGORIES}
|
||||||
onChange={handleCategoriesChanged}
|
size="sm"
|
||||||
>
|
icon={<FaServer />}
|
||||||
{IMAGE_CATEGORIES.map((c) => (
|
/>
|
||||||
<Checkbox key={c} value={c}>
|
</ButtonGroup>
|
||||||
{capitalize(c)}
|
|
||||||
</Checkbox>
|
|
||||||
))}
|
|
||||||
</CheckboxGroup>
|
|
||||||
</Flex>
|
|
||||||
</IAIPopover>
|
|
||||||
|
|
||||||
<Flex gap={2}>
|
<Flex gap={2}>
|
||||||
<IAIPopover
|
<IAIPopover
|
||||||
triggerComponent={
|
triggerComponent={
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
size="sm"
|
tooltip={t('gallery.gallerySettings')}
|
||||||
aria-label={t('gallery.gallerySettings')}
|
aria-label={t('gallery.gallerySettings')}
|
||||||
|
size="sm"
|
||||||
icon={<FaWrench />}
|
icon={<FaWrench />}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,14 @@ export const imagesAdapter = createEntityAdapter<ImageDTO>({
|
|||||||
sortComparer: (a, b) => dateComparator(b.created_at, a.created_at),
|
sortComparer: (a, b) => dateComparator(b.created_at, a.created_at),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const IMAGE_CATEGORIES: ImageCategory[] = ['general'];
|
||||||
|
export const ASSETS_CATEGORIES: ImageCategory[] = [
|
||||||
|
'control',
|
||||||
|
'mask',
|
||||||
|
'user',
|
||||||
|
'other',
|
||||||
|
];
|
||||||
|
|
||||||
type AdditionaImagesState = {
|
type AdditionaImagesState = {
|
||||||
offset: number;
|
offset: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
@ -29,7 +37,7 @@ export const initialImagesState =
|
|||||||
limit: 0,
|
limit: 0,
|
||||||
total: 0,
|
total: 0,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
categories: ['general', 'control', 'mask', 'other', 'user'],
|
categories: IMAGE_CATEGORIES,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ImagesState = typeof initialImagesState;
|
export type ImagesState = typeof initialImagesState;
|
||||||
|
@ -91,6 +91,8 @@ export const receivedPageOfImages = createAppAsyncThunk(
|
|||||||
categories.includes(i.image_category)
|
categories.includes(i.image_category)
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
|
console.log(categories);
|
||||||
|
|
||||||
const response = await ImagesService.listImagesWithMetadata({
|
const response = await ImagesService.listImagesWithMetadata({
|
||||||
categories,
|
categories,
|
||||||
isIntermediate: false,
|
isIntermediate: false,
|
||||||
|
Loading…
Reference in New Issue
Block a user