diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx index 980c317ac3..65e9dcb43c 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx @@ -47,10 +47,7 @@ import { FaTrash, FaWrench, } from 'react-icons/fa'; -import { - gallerySelector, - selectedImageSelector, -} from '../store/gallerySelectors'; +import { gallerySelector } from '../store/gallerySelectors'; import DeleteImageModal from './DeleteImageModal'; import { useCallback } from 'react'; import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale'; @@ -73,15 +70,15 @@ const currentImageButtonsSelector = createSelector( uiSelector, lightboxSelector, activeTabNameSelector, - selectedImageSelector, ], - (system, gallery, postprocessing, ui, lightbox, activeTabName, image) => { + (system, gallery, postprocessing, ui, lightbox, activeTabName) => { const { isProcessing, isConnected, isGFPGANAvailable, isESRGANAvailable, shouldConfirmOnDelete, + progressImage, } = system; const { upscalingLevel, facetoolStrength } = postprocessing; @@ -90,7 +87,7 @@ const currentImageButtonsSelector = createSelector( const { shouldShowImageDetails, shouldHidePreview } = ui; - const { intermediateImage, currentImage } = gallery; + const { selectedImage } = gallery; return { canDeleteImage: isConnected && !isProcessing, @@ -101,15 +98,14 @@ const currentImageButtonsSelector = createSelector( isESRGANAvailable, upscalingLevel, facetoolStrength, - shouldDisableToolbarButtons: Boolean(intermediateImage) || !currentImage, - currentImage, + shouldDisableToolbarButtons: Boolean(progressImage) || !selectedImage, shouldShowImageDetails, activeTabName, isLightboxOpen, shouldHidePreview, - image, - seed: image?.metadata?.invokeai?.node?.seed, - prompt: image?.metadata?.invokeai?.node?.prompt, + image: selectedImage, + seed: selectedImage?.metadata?.invokeai?.node?.seed, + prompt: selectedImage?.metadata?.invokeai?.node?.prompt, }; }, { diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx index 9b928d45c4..5810c599c1 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImageDisplay.tsx @@ -4,18 +4,18 @@ import { useAppSelector } from 'app/store/storeHooks'; import { systemSelector } from 'features/system/store/systemSelectors'; import { isEqual } from 'lodash-es'; -import { selectedImageSelector } from '../store/gallerySelectors'; +import { gallerySelector } from '../store/gallerySelectors'; import CurrentImageButtons from './CurrentImageButtons'; import CurrentImagePreview from './CurrentImagePreview'; import { FaImage } from 'react-icons/fa'; export const currentImageDisplaySelector = createSelector( - [systemSelector, selectedImageSelector], - (system, selectedImage) => { + [systemSelector, gallerySelector], + (system, gallery) => { const { progressImage } = system; return { - hasAnImageToDisplay: selectedImage || progressImage, + hasAnImageToDisplay: gallery.selectedImage || progressImage, }; }, { diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx index b37cd5ca62..9770ed5887 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx @@ -15,10 +15,7 @@ import IAICheckbox from 'common/components/IAICheckbox'; import IAIIconButton from 'common/components/IAIIconButton'; import IAIPopover from 'common/components/IAIPopover'; import IAISlider from 'common/components/IAISlider'; -import { - gallerySelector, - imageGallerySelector, -} from 'features/gallery/store/gallerySelectors'; +import { gallerySelector } from 'features/gallery/store/gallerySelectors'; import { setCurrentCategory, setGalleryImageMinimumWidth, @@ -57,11 +54,12 @@ import { Virtuoso, VirtuosoGrid } from 'react-virtuoso'; import { Image as ImageType } from 'app/types/invokeai'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import GalleryProgressImage from './GalleryProgressImage'; +import { uiSelector } from 'features/ui/store/uiSelectors'; const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 290; const PROGRESS_IMAGE_PLACEHOLDER = 'PROGRESS_IMAGE_PLACEHOLDER'; -const selector = createSelector( +const categorySelector = createSelector( [(state: RootState) => state], (state) => { const { results, uploads, system, gallery } = state; @@ -92,6 +90,33 @@ const selector = createSelector( defaultSelectorOptions ); +const mainSelector = createSelector( + [gallerySelector, uiSelector], + (gallery, ui) => { + const { + currentCategory, + galleryImageMinimumWidth, + galleryImageObjectFit, + shouldAutoSwitchToNewImages, + shouldUseSingleGalleryColumn, + selectedImage, + } = gallery; + + const { shouldPinGallery } = ui; + + return { + currentCategory, + shouldPinGallery, + galleryImageMinimumWidth, + galleryImageObjectFit, + shouldAutoSwitchToNewImages, + shouldUseSingleGalleryColumn, + selectedImage, + }; + }, + defaultSelectorOptions +); + const ImageGalleryContent = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); @@ -113,7 +138,6 @@ const ImageGalleryContent = () => { }); const { - // images, currentCategory, shouldPinGallery, galleryImageMinimumWidth, @@ -121,10 +145,10 @@ const ImageGalleryContent = () => { shouldAutoSwitchToNewImages, shouldUseSingleGalleryColumn, selectedImage, - } = useAppSelector(imageGallerySelector); + } = useAppSelector(mainSelector); const { images, areMoreImagesAvailable, isLoading } = - useAppSelector(selector); + useAppSelector(categorySelector); const handleClickLoadMore = () => { if (currentCategory === 'results') { diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts index 3eeb2aa933..3c7e366bf7 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts @@ -1,83 +1,3 @@ -import { createSelector } from '@reduxjs/toolkit'; import { RootState } from 'app/store/store'; -import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors'; - -import { - activeTabNameSelector, - uiSelector, -} from 'features/ui/store/uiSelectors'; -import { isEqual } from 'lodash-es'; -import { selectResultsById, selectResultsEntities } from './resultsSlice'; -import { selectUploadsAll, selectUploadsById } from './uploadsSlice'; export const gallerySelector = (state: RootState) => state.gallery; - -export const imageGallerySelector = createSelector( - [ - (state: RootState) => state, - gallerySelector, - uiSelector, - lightboxSelector, - activeTabNameSelector, - ], - (state, gallery, ui, lightbox, activeTabName) => { - const { - currentCategory, - galleryImageMinimumWidth, - galleryImageObjectFit, - shouldAutoSwitchToNewImages, - galleryWidth, - shouldUseSingleGalleryColumn, - selectedImage, - } = gallery; - - const { shouldPinGallery } = ui; - - const { isLightboxOpen } = lightbox; - - const images = - currentCategory === 'results' - ? selectResultsEntities(state) - : selectUploadsAll(state); - - return { - shouldPinGallery, - galleryImageMinimumWidth, - galleryImageObjectFit, - galleryGridTemplateColumns: shouldUseSingleGalleryColumn - ? 'auto' - : `repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, auto))`, - shouldAutoSwitchToNewImages, - currentCategory, - images, - galleryWidth, - shouldEnableResize: - isLightboxOpen || - (activeTabName === 'unifiedCanvas' && shouldPinGallery) - ? false - : true, - shouldUseSingleGalleryColumn, - selectedImage, - }; - }, - { - memoizeOptions: { - resultEqualityCheck: isEqual, - }, - } -); - -export const selectedImageSelector = createSelector( - [(state: RootState) => state, gallerySelector], - (state, gallery) => { - const selectedImage = gallery.selectedImage; - - if (selectedImage?.type === 'results') { - return selectResultsById(state, selectedImage.name); - } - - if (selectedImage?.type === 'uploads') { - return selectUploadsById(state, selectedImage.name); - } - } -); diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index 1ae5ee8aee..96c3486b50 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -10,14 +10,10 @@ import { type GalleryImageObjectFitType = 'contain' | 'cover'; export interface GalleryState { - /** - * The selected image - */ selectedImage?: Image; galleryImageMinimumWidth: number; galleryImageObjectFit: GalleryImageObjectFitType; shouldAutoSwitchToNewImages: boolean; - galleryWidth: number; shouldUseSingleGalleryColumn: boolean; currentCategory: 'results' | 'uploads'; } @@ -26,7 +22,6 @@ export const initialGalleryState: GalleryState = { galleryImageMinimumWidth: 64, galleryImageObjectFit: 'cover', shouldAutoSwitchToNewImages: true, - galleryWidth: 300, shouldUseSingleGalleryColumn: false, currentCategory: 'results', }; @@ -58,9 +53,6 @@ export const gallerySlice = createSlice({ ) => { state.currentCategory = action.payload; }, - setGalleryWidth: (state, action: PayloadAction) => { - state.galleryWidth = action.payload; - }, setShouldUseSingleGalleryColumn: ( state, action: PayloadAction @@ -93,24 +85,28 @@ export const gallerySlice = createSlice({ builder.addCase(receivedResultImagesPage.fulfilled, (state, action) => { // rehydrate selectedImage URL when results list comes in // solves case when outdated URL is in local storage - if (state.selectedImage) { + const selectedImage = state.selectedImage; + if (selectedImage) { const selectedImageInResults = action.payload.items.find( - (image) => image.image_name === state.selectedImage!.name + (image) => image.image_name === selectedImage.name ); if (selectedImageInResults) { - state.selectedImage.url = selectedImageInResults.image_url; + selectedImage.url = selectedImageInResults.image_url; + state.selectedImage = selectedImage; } } }); builder.addCase(receivedUploadImagesPage.fulfilled, (state, action) => { // rehydrate selectedImage URL when results list comes in // solves case when outdated URL is in local storage - if (state.selectedImage) { + const selectedImage = state.selectedImage; + if (selectedImage) { const selectedImageInResults = action.payload.items.find( - (image) => image.image_name === state.selectedImage!.name + (image) => image.image_name === selectedImage.name ); if (selectedImageInResults) { - state.selectedImage.url = selectedImageInResults.image_url; + selectedImage.url = selectedImageInResults.image_url; + state.selectedImage = selectedImage; } } }); @@ -122,7 +118,6 @@ export const { setGalleryImageMinimumWidth, setGalleryImageObjectFit, setShouldAutoSwitchToNewImages, - setGalleryWidth, setShouldUseSingleGalleryColumn, setCurrentCategory, } = gallerySlice.actions;