From dde497404b9539ae08412e727e7ad27547af6e1f Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 24 Jun 2023 15:57:13 +1000 Subject: [PATCH] fix(ui): fix init image display buttons - Reset and Upload buttons along top of initial image - Also had to mess around with the control net & DnD image stuff after changing the styles - Abstract image upload logic into hook - does not handle native HTML drag and drop upload - only the button click upload --- .../web/src/common/components/IAIDndImage.tsx | 47 +++------- .../src/common/hooks/useImageUploadButton.tsx | 64 +++++++++++++ .../components/ControlNetImagePreview.tsx | 93 ++++++++----------- .../components/CurrentImageDisplay.tsx | 30 +----- .../components/CurrentImagePreview.tsx | 27 ++---- .../ImageToImage/InitialImagePreview.tsx | 58 +++++++++++- 6 files changed, 179 insertions(+), 140 deletions(-) create mode 100644 invokeai/frontend/web/src/common/hooks/useImageUploadButton.tsx diff --git a/invokeai/frontend/web/src/common/components/IAIDndImage.tsx b/invokeai/frontend/web/src/common/components/IAIDndImage.tsx index 4b95fc36df..bdf22c2df1 100644 --- a/invokeai/frontend/web/src/common/components/IAIDndImage.tsx +++ b/invokeai/frontend/web/src/common/components/IAIDndImage.tsx @@ -12,15 +12,14 @@ import IAIIconButton from 'common/components/IAIIconButton'; import { IAIImageLoadingFallback } from 'common/components/IAIImageFallback'; import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay'; import { AnimatePresence } from 'framer-motion'; -import { ReactElement, SyntheticEvent, useCallback } from 'react'; +import { ReactElement, SyntheticEvent } from 'react'; import { memo, useRef } from 'react'; -import { FaImage, FaTimes, FaUndo, FaUpload } from 'react-icons/fa'; +import { FaImage, FaUndo, FaUpload } from 'react-icons/fa'; import { ImageDTO } from 'services/api/types'; import { v4 as uuidv4 } from 'uuid'; import IAIDropOverlay from './IAIDropOverlay'; -import { PostUploadAction, imageUploaded } from 'services/api/thunks/image'; -import { useDropzone } from 'react-dropzone'; -import { useAppDispatch } from 'app/store/storeHooks'; +import { PostUploadAction } from 'services/api/thunks/image'; +import { useImageUploadButton } from 'common/hooks/useImageUploadButton'; type IAIDndImageProps = { image: ImageDTO | null | undefined; @@ -39,6 +38,7 @@ type IAIDndImageProps = { minSize?: number; postUploadAction?: PostUploadAction; imageSx?: ChakraProps['sx']; + fitContainer?: boolean; }; const IAIDndImage = (props: IAIDndImageProps) => { @@ -58,8 +58,9 @@ const IAIDndImage = (props: IAIDndImageProps) => { minSize = 24, postUploadAction, imageSx, + fitContainer = false, } = props; - const dispatch = useAppDispatch(); + const dndId = useRef(uuidv4()); const { @@ -87,31 +88,9 @@ const IAIDndImage = (props: IAIDndImageProps) => { disabled: isDragDisabled || !image, }); - const handleOnDropAccepted = useCallback( - (files: Array) => { - const file = files[0]; - if (!file) { - return; - } - - dispatch( - imageUploaded({ - file, - image_category: 'user', - is_intermediate: false, - postUploadAction, - }) - ); - }, - [dispatch, postUploadAction] - ); - - const { getRootProps, getInputProps } = useDropzone({ - accept: { 'image/png': ['.png'], 'image/jpeg': ['.jpg', '.jpeg', '.png'] }, - onDropAccepted: handleOnDropAccepted, - noDrag: true, - multiple: false, - disabled: isUploadDisabled, + const { getUploadButtonProps, getUploadInputProps } = useImageUploadButton({ + postUploadAction, + isDisabled: isUploadDisabled, }); const setNodeRef = useCombinedRefs(setDroppableRef, setDraggableRef); @@ -149,13 +128,13 @@ const IAIDndImage = (props: IAIDndImageProps) => { sx={{ w: 'full', h: 'full', + position: fitContainer ? 'absolute' : 'relative', alignItems: 'center', justifyContent: 'center', }} > { color: 'base.500', ...uploadButtonStyles, }} - {...getRootProps()} + {...getUploadButtonProps()} > - + // will open the file dialog on click + * // hidden, handles native upload functionality + */ +export const useImageUploadButton = ({ + postUploadAction, + isDisabled, +}: UseImageUploadButtonArgs) => { + const dispatch = useAppDispatch(); + const onDropAccepted = useCallback( + (files: File[]) => { + const file = files[0]; + if (!file) { + return; + } + + dispatch( + imageUploaded({ + file, + image_category: 'user', + is_intermediate: false, + postUploadAction, + }) + ); + }, + [dispatch, postUploadAction] + ); + + const { + getRootProps: getUploadButtonProps, + getInputProps: getUploadInputProps, + open: openUploader, + } = useDropzone({ + accept: { 'image/png': ['.png'], 'image/jpeg': ['.jpg', '.jpeg', '.png'] }, + onDropAccepted, + disabled: isDisabled, + noDrag: true, + multiple: false, + }); + + return { getUploadButtonProps, getUploadInputProps, openUploader }; +}; diff --git a/invokeai/frontend/web/src/features/controlNet/components/ControlNetImagePreview.tsx b/invokeai/frontend/web/src/features/controlNet/components/ControlNetImagePreview.tsx index ba173f7715..ec6b18c377 100644 --- a/invokeai/frontend/web/src/features/controlNet/components/ControlNetImagePreview.tsx +++ b/invokeai/frontend/web/src/features/controlNet/components/ControlNetImagePreview.tsx @@ -10,7 +10,6 @@ import { Box, ChakraProps, Flex } from '@chakra-ui/react'; import IAIDndImage from 'common/components/IAIDndImage'; import { createSelector } from '@reduxjs/toolkit'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; -import { AnimatePresence, motion } from 'framer-motion'; import { IAIImageLoadingFallback } from 'common/components/IAIImageFallback'; import IAIIconButton from 'common/components/IAIIconButton'; import { FaUndo } from 'react-icons/fa'; @@ -105,64 +104,46 @@ const ControlNetImagePreview = (props: Props) => { - - {shouldShowProcessedImage && ( - - <> - {shouldShowProcessedImageBackdrop && ( - - )} - - - - - - )} - + {shouldShowProcessedImage && ( + + {shouldShowProcessedImageBackdrop && ( + + )} + + + )} {pendingControlImages.includes(controlNetId) && ( { - const { hasSelectedImage, hasProgressImage } = useAppSelector( - currentImageDisplaySelector - ); + const { hasSelectedImage } = useAppSelector(currentImageDisplaySelector); return ( { justifyContent: 'center', }} > - - - - {hasSelectedImage && ( - - - - )} + {hasSelectedImage && } + ); }; diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx index b0998d128f..8e70e9310a 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx @@ -51,10 +51,6 @@ const CurrentImagePreview = () => { shouldAntialiasProgressImage, } = useAppSelector(imagesSelector); - // const image = useAppSelector((state: RootState) => - // selectImagesById(state, selectedImage ?? '') - // ); - const { currentData: image, isLoading, @@ -79,7 +75,6 @@ const CurrentImagePreview = () => { sx={{ width: 'full', height: 'full', - position: 'relative', alignItems: 'center', justifyContent: 'center', }} @@ -101,21 +96,13 @@ const CurrentImagePreview = () => { }} /> ) : ( - - } - isUploadDisabled={true} - /> - + } + isUploadDisabled={true} + fitContainer + /> )} {shouldShowImageDetails && image && ( { const { initialImage } = useAppSelector(selector); const dispatch = useAppDispatch(); + const { openUploader } = useImageUploader(); const { currentData: image, @@ -36,6 +41,10 @@ const InitialImagePreview = () => { isSuccess, } = useGetImageDTOQuery(initialImage?.imageName ?? skipToken); + const { getUploadButtonProps, getUploadInputProps } = useImageUploadButton({ + postUploadAction: { type: 'SET_INITIAL_IMAGE' }, + }); + const handleDrop = useCallback( (droppedImage: ImageDTO) => { if (droppedImage.image_name === initialImage?.imageName) { @@ -50,25 +59,66 @@ const InitialImagePreview = () => { dispatch(clearInitialImage()); }, [dispatch]); + const handleUpload = useCallback(() => { + openUploader(); + }, [openUploader]); + return ( + + + Initial Image + + + } + onClick={handleUpload} + {...getUploadButtonProps()} + /> + } + onClick={handleReset} + isDisabled={!initialImage} + /> + } - postUploadAction={{ type: 'SET_INITIAL_IMAGE' }} - withResetIcon + isUploadDisabled={true} + fitContainer /> + ); };