From d95fe5925aaccaffd827c19fb09a016501470263 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 15 May 2023 18:52:48 +1000 Subject: [PATCH] feat(ui): restore image post-upload actions eg set init image if on img2img when uploading --- .../listeners/imageUploaded.ts | 10 ++++++++++ .../src/common/components/ImageUploader.tsx | 16 +++++++++++++-- ...ageButtons.tsx => InitialImageButtons.tsx} | 0 .../ImageToImage/ImageToImageSettings.tsx | 20 ------------------- .../ImageToImage/InitialImageDisplay.tsx | 2 +- .../frontend/web/src/services/thunks/image.ts | 11 ++++++++-- 6 files changed, 34 insertions(+), 25 deletions(-) rename invokeai/frontend/web/src/common/components/{ImageToImageButtons.tsx => InitialImageButtons.tsx} (100%) delete mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageSettings.tsx diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts index 5b67be418f..d676ee6a1d 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts @@ -4,6 +4,8 @@ import { uploadAdded } from 'features/gallery/store/uploadsSlice'; import { imageSelected } from 'features/gallery/store/gallerySlice'; import { imageUploaded } from 'services/thunks/image'; import { addToast } from 'features/system/store/systemSlice'; +import { initialImageSelected } from 'features/parameters/store/actions'; +import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; export const addImageUploadedListener = () => { startAppListening({ @@ -23,6 +25,14 @@ export const addImageUploadedListener = () => { if (state.gallery.shouldAutoSwitchToNewImages) { dispatch(imageSelected(image)); } + + if (action.meta.arg.activeTabName === 'img2img') { + dispatch(initialImageSelected(image)); + } + + if (action.meta.arg.activeTabName === 'unifiedCanvas') { + dispatch(setInitialCanvasImage(image)); + } }, }); }; diff --git a/invokeai/frontend/web/src/common/components/ImageUploader.tsx b/invokeai/frontend/web/src/common/components/ImageUploader.tsx index a070b69bbc..db6b9ee517 100644 --- a/invokeai/frontend/web/src/common/components/ImageUploader.tsx +++ b/invokeai/frontend/web/src/common/components/ImageUploader.tsx @@ -66,9 +66,15 @@ const ImageUploader = (props: ImageUploaderProps) => { const fileAcceptedCallback = useCallback( async (file: File) => { - dispatch(imageUploaded({ imageType: 'uploads', formData: { file } })); + dispatch( + imageUploaded({ + imageType: 'uploads', + formData: { file }, + activeTabName, + }) + ); }, - [dispatch] + [dispatch, activeTabName] ); const onDrop = useCallback( @@ -111,18 +117,24 @@ const ImageUploader = (props: ImageUploaderProps) => { }); useEffect(() => { + // This is a hack to allow pasting images into the uploader const handlePaste = async (e: ClipboardEvent) => { if (!inputRef.current) { return; } if (e.clipboardData?.files) { + // Set the files on the inputRef inputRef.current.files = e.clipboardData.files; + // Dispatch the change event, dropzone catches this and we get to use its own validation inputRef.current?.dispatchEvent(new Event('change', { bubbles: true })); } }; + // Set the open function so we can open the uploader from anywhere setOpenUploaderFunction(open); + + // Add the paste event listener document.addEventListener('paste', handlePaste); return () => { diff --git a/invokeai/frontend/web/src/common/components/ImageToImageButtons.tsx b/invokeai/frontend/web/src/common/components/InitialImageButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/common/components/ImageToImageButtons.tsx rename to invokeai/frontend/web/src/common/components/InitialImageButtons.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageSettings.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageSettings.tsx deleted file mode 100644 index e8198c75ad..0000000000 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageSettings.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { VStack } from '@chakra-ui/react'; - -import ImageToImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageFit'; -import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength'; - -import { useTranslation } from 'react-i18next'; -import InitialImagePreview from './InitialImagePreview'; -import InitialImageButtons from 'common/components/ImageToImageButtons'; - -export default function ImageToImageSettings() { - const { t } = useTranslation(); - return ( - - - - - - - ); -} diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImageDisplay.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImageDisplay.tsx index c9c6e525b4..f17ebcbdc0 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImageDisplay.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImageDisplay.tsx @@ -1,6 +1,6 @@ import { Flex } from '@chakra-ui/react'; import InitialImagePreview from './InitialImagePreview'; -import InitialImageButtons from 'common/components/ImageToImageButtons'; +import InitialImageButtons from 'common/components/InitialImageButtons'; const InitialImageDisplay = () => { return ( diff --git a/invokeai/frontend/web/src/services/thunks/image.ts b/invokeai/frontend/web/src/services/thunks/image.ts index de1361be38..ec2533b61b 100644 --- a/invokeai/frontend/web/src/services/thunks/image.ts +++ b/invokeai/frontend/web/src/services/thunks/image.ts @@ -1,5 +1,6 @@ import { log } from 'app/logging/useLogger'; import { createAppAsyncThunk } from 'app/store/storeUtils'; +import { InvokeTabName } from 'features/ui/store/tabMap'; import { ImagesService } from 'services/api'; import { getHeaders } from 'services/util/getHeaders'; @@ -39,7 +40,11 @@ export const thumbnailReceived = createAppAsyncThunk( } ); -type ImageUploadedArg = Parameters<(typeof ImagesService)['uploadImage']>[0]; +type ImageUploadedArg = Parameters<(typeof ImagesService)['uploadImage']>[0] & { + // extra arg to determine post-upload actions - we check for this when the image is uploaded + // to determine if we should set the init image + activeTabName?: InvokeTabName; +}; /** * `ImagesService.uploadImage()` thunk @@ -47,7 +52,9 @@ type ImageUploadedArg = Parameters<(typeof ImagesService)['uploadImage']>[0]; export const imageUploaded = createAppAsyncThunk( 'api/imageUploaded', async (arg: ImageUploadedArg) => { - const response = await ImagesService.uploadImage(arg); + // strip out `activeTabName` from arg - the route does not need it + const { activeTabName, ...rest } = arg; + const response = await ImagesService.uploadImage(rest); const { location } = getHeaders(response); imagesLog.info(