From 7004430380919d153839ce2d29f983b6b4ef7082 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 29 May 2023 19:54:04 +1000 Subject: [PATCH] feat(ui): gallery filter dropdown -> Images/Assets toggle --- invokeai/frontend/web/public/locales/en.json | 4 +- .../middleware/listenerMiddleware/index.ts | 6 +- .../listeners/imageCategoriesChanged.ts | 24 ++++++ .../listeners/receivedPageOfImages.ts | 2 +- .../components/ImageGalleryContent.tsx | 76 ++++++++++--------- .../src/features/gallery/store/imagesSlice.ts | 10 ++- .../frontend/web/src/services/thunks/image.ts | 2 + 7 files changed, 86 insertions(+), 38 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageCategoriesChanged.ts diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 94dff3934a..94ad949023 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -122,7 +122,9 @@ "noImagesInGallery": "No Images In Gallery", "deleteImage": "Delete Image", "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": { "keyboardShortcuts": "Keyboard Shortcuts", diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index 26f32252d1..ba16e56371 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -69,6 +69,7 @@ import { } from './listeners/receivedPageOfImages'; import { addStagingAreaImageSavedListener } from './listeners/stagingAreaImageSaved'; import { addCommitStagingAreaImageListener } from './listeners/addCommitStagingAreaImageListener'; +import { addImageCategoriesChangedListener } from './listeners/imageCategoriesChanged'; export const listenerMiddleware = createListenerMiddleware(); @@ -166,6 +167,9 @@ addSessionCanceledPendingListener(); addSessionCanceledFulfilledListener(); addSessionCanceledRejectedListener(); -// Images +// Fetching images addReceivedPageOfImagesFulfilledListener(); addReceivedPageOfImagesRejectedListener(); + +// Gallery +addImageCategoriesChangedListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageCategoriesChanged.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageCategoriesChanged.ts new file mode 100644 index 0000000000..85d56d3913 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageCategoriesChanged.ts @@ -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()); + } + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedPageOfImages.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedPageOfImages.ts index 9a2ec0e7a5..cde7e22e3d 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedPageOfImages.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedPageOfImages.ts @@ -11,7 +11,7 @@ export const addReceivedPageOfImagesFulfilledListener = () => { effect: (action, { getState, dispatch }) => { const page = action.payload; moduleLog.debug( - { data: { page } }, + { data: { payload: action.payload } }, `Received ${page.items.length} images` ); }, diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx index 758c077b54..ce7eb00404 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx @@ -1,5 +1,6 @@ import { Box, + ButtonGroup, Checkbox, CheckboxGroup, Flex, @@ -36,7 +37,13 @@ import { } from 'react'; import { useTranslation } from 'react-i18next'; 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 HoverableImage from './HoverableImage'; @@ -47,18 +54,15 @@ import { Virtuoso, VirtuosoGrid } from 'react-virtuoso'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { uiSelector } from 'features/ui/store/uiSelectors'; 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 { capitalize } from 'lodash-es'; -const IMAGE_CATEGORIES: ImageCategory[] = [ - 'general', - 'control', - 'mask', - 'user', - 'other', -]; - const categorySelector = createSelector( [(state: RootState) => state], (state) => { @@ -179,6 +183,14 @@ const ImageGalleryContent = () => { [dispatch] ); + const handleClickImagesCategory = useCallback(() => { + dispatch(imageCategoriesChanged(IMAGE_CATEGORIES)); + }, [dispatch]); + + const handleClickAssetsCategory = useCallback(() => { + dispatch(imageCategoriesChanged(ASSETS_CATEGORIES)); + }, [dispatch]); + return ( { alignItems="center" justifyContent="space-between" > - } - /> - } - > - - - {IMAGE_CATEGORIES.map((c) => ( - - {capitalize(c)} - - ))} - - - - + + } + /> + } + /> + } /> } diff --git a/invokeai/frontend/web/src/features/gallery/store/imagesSlice.ts b/invokeai/frontend/web/src/features/gallery/store/imagesSlice.ts index 8ab34fccf0..cb6469aeb4 100644 --- a/invokeai/frontend/web/src/features/gallery/store/imagesSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/imagesSlice.ts @@ -15,6 +15,14 @@ export const imagesAdapter = createEntityAdapter({ 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 = { offset: number; limit: number; @@ -29,7 +37,7 @@ export const initialImagesState = limit: 0, total: 0, isLoading: false, - categories: ['general', 'control', 'mask', 'other', 'user'], + categories: IMAGE_CATEGORIES, }); export type ImagesState = typeof initialImagesState; diff --git a/invokeai/frontend/web/src/services/thunks/image.ts b/invokeai/frontend/web/src/services/thunks/image.ts index 87832c6b1e..4ef492f1c6 100644 --- a/invokeai/frontend/web/src/services/thunks/image.ts +++ b/invokeai/frontend/web/src/services/thunks/image.ts @@ -91,6 +91,8 @@ export const receivedPageOfImages = createAppAsyncThunk( categories.includes(i.image_category) ).length; + console.log(categories); + const response = await ImagesService.listImagesWithMetadata({ categories, isIntermediate: false,