mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): add upload to IAIDndImage
Add uploading to IAIDndImage - add `postUploadAction` arg to `imageUploaded` thunk, with several current valid options (set control image, set init, set nodes image, set canvas, or toast) - updated IAIDndImage to optionally allow click to upload
This commit is contained in:
parent
f223ad7776
commit
58fec84858
@ -8,7 +8,6 @@ import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
|||||||
import { getFullBaseLayerBlob } from 'features/canvas/util/getFullBaseLayerBlob';
|
import { getFullBaseLayerBlob } from 'features/canvas/util/getFullBaseLayerBlob';
|
||||||
|
|
||||||
const moduleLog = log.child({ namespace: 'canvasCopiedToClipboardListener' });
|
const moduleLog = log.child({ namespace: 'canvasCopiedToClipboardListener' });
|
||||||
export const MERGED_CANVAS_FILENAME = 'mergedCanvas.png';
|
|
||||||
|
|
||||||
export const addCanvasMergedListener = () => {
|
export const addCanvasMergedListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -49,12 +48,15 @@ export const addCanvasMergedListener = () => {
|
|||||||
const imageUploadedRequest = dispatch(
|
const imageUploadedRequest = dispatch(
|
||||||
imageUploaded({
|
imageUploaded({
|
||||||
formData: {
|
formData: {
|
||||||
file: new File([blob], MERGED_CANVAS_FILENAME, {
|
file: new File([blob], 'mergedCanvas.png', {
|
||||||
type: 'image/png',
|
type: 'image/png',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
imageCategory: 'general',
|
imageCategory: 'general',
|
||||||
isIntermediate: true,
|
isIntermediate: true,
|
||||||
|
postUploadAction: {
|
||||||
|
type: 'TOAST_CANVAS_MERGED',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -6,8 +6,6 @@ import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { imageUpserted } from 'features/gallery/store/imagesSlice';
|
import { imageUpserted } from 'features/gallery/store/imagesSlice';
|
||||||
|
|
||||||
export const SAVED_CANVAS_FILENAME = 'savedCanvas.png';
|
|
||||||
|
|
||||||
const moduleLog = log.child({ namespace: 'canvasSavedToGalleryListener' });
|
const moduleLog = log.child({ namespace: 'canvasSavedToGalleryListener' });
|
||||||
|
|
||||||
export const addCanvasSavedToGalleryListener = () => {
|
export const addCanvasSavedToGalleryListener = () => {
|
||||||
@ -33,12 +31,15 @@ export const addCanvasSavedToGalleryListener = () => {
|
|||||||
const imageUploadedRequest = dispatch(
|
const imageUploadedRequest = dispatch(
|
||||||
imageUploaded({
|
imageUploaded({
|
||||||
formData: {
|
formData: {
|
||||||
file: new File([blob], SAVED_CANVAS_FILENAME, {
|
file: new File([blob], 'savedCanvas.png', {
|
||||||
type: 'image/png',
|
type: 'image/png',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
imageCategory: 'general',
|
imageCategory: 'general',
|
||||||
isIntermediate: false,
|
isIntermediate: false,
|
||||||
|
postUploadAction: {
|
||||||
|
type: 'TOAST_CANVAS_SAVED_TO_GALLERY',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3,8 +3,10 @@ import { imageUploaded } from 'services/thunks/image';
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
import { imageUpserted } from 'features/gallery/store/imagesSlice';
|
import { imageUpserted } from 'features/gallery/store/imagesSlice';
|
||||||
import { SAVED_CANVAS_FILENAME } from './canvasSavedToGallery';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
import { MERGED_CANVAS_FILENAME } from './canvasMerged';
|
import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlice';
|
||||||
|
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
||||||
|
import { fieldValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
|
|
||||||
const moduleLog = log.child({ namespace: 'image' });
|
const moduleLog = log.child({ namespace: 'image' });
|
||||||
|
|
||||||
@ -21,23 +23,48 @@ export const addImageUploadedFulfilledListener = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const originalFileName = action.meta.arg.formData.file.name;
|
|
||||||
|
|
||||||
dispatch(imageUpserted(image));
|
dispatch(imageUpserted(image));
|
||||||
|
|
||||||
if (originalFileName === SAVED_CANVAS_FILENAME) {
|
const { postUploadAction } = action.meta.arg;
|
||||||
|
|
||||||
|
if (postUploadAction?.type === 'TOAST_CANVAS_SAVED_TO_GALLERY') {
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({ title: 'Canvas Saved to Gallery', status: 'success' })
|
addToast({ title: 'Canvas Saved to Gallery', status: 'success' })
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (originalFileName === MERGED_CANVAS_FILENAME) {
|
if (postUploadAction?.type === 'TOAST_CANVAS_MERGED') {
|
||||||
dispatch(addToast({ title: 'Canvas Merged', status: 'success' }));
|
dispatch(addToast({ title: 'Canvas Merged', status: 'success' }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(addToast({ title: 'Image Uploaded', status: 'success' }));
|
if (postUploadAction?.type === 'SET_CANVAS_INITIAL_IMAGE') {
|
||||||
|
dispatch(setInitialCanvasImage(image));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postUploadAction?.type === 'SET_CONTROLNET_IMAGE') {
|
||||||
|
const { controlNetId } = postUploadAction;
|
||||||
|
dispatch(controlNetImageChanged({ controlNetId, controlImage: image }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postUploadAction?.type === 'SET_INITIAL_IMAGE') {
|
||||||
|
dispatch(initialImageChanged(image));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postUploadAction?.type === 'SET_NODES_IMAGE') {
|
||||||
|
const { nodeId, fieldName } = postUploadAction;
|
||||||
|
dispatch(fieldValueChanged({ nodeId, fieldName, value: image }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postUploadAction?.type === 'TOAST_UPLOADED') {
|
||||||
|
dispatch(addToast({ title: 'Image Uploaded', status: 'success' }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -5,12 +5,18 @@ import IAIIconButton from 'common/components/IAIIconButton';
|
|||||||
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
||||||
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
|
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
|
||||||
import { AnimatePresence } from 'framer-motion';
|
import { AnimatePresence } from 'framer-motion';
|
||||||
import { ReactElement, SyntheticEvent } from 'react';
|
import { ReactElement, SyntheticEvent, useCallback } from 'react';
|
||||||
import { memo, useRef } from 'react';
|
import { memo, useRef } from 'react';
|
||||||
import { FaImage, FaTimes } from 'react-icons/fa';
|
import { FaImage, FaTimes, FaUpload } from 'react-icons/fa';
|
||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import IAIDropOverlay from './IAIDropOverlay';
|
import IAIDropOverlay from './IAIDropOverlay';
|
||||||
|
import { PostUploadAction, imageUploaded } from 'services/thunks/image';
|
||||||
|
import { FileRejection, useDropzone } from 'react-dropzone';
|
||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { reject } from 'lodash-es';
|
||||||
|
|
||||||
type IAIDndImageProps = {
|
type IAIDndImageProps = {
|
||||||
image: ImageDTO | null | undefined;
|
image: ImageDTO | null | undefined;
|
||||||
@ -23,9 +29,11 @@ type IAIDndImageProps = {
|
|||||||
withMetadataOverlay?: boolean;
|
withMetadataOverlay?: boolean;
|
||||||
isDragDisabled?: boolean;
|
isDragDisabled?: boolean;
|
||||||
isDropDisabled?: boolean;
|
isDropDisabled?: boolean;
|
||||||
|
isUploadDisabled?: boolean;
|
||||||
fallback?: ReactElement;
|
fallback?: ReactElement;
|
||||||
payloadImage?: ImageDTO | null | undefined;
|
payloadImage?: ImageDTO | null | undefined;
|
||||||
minSize?: number;
|
minSize?: number;
|
||||||
|
postUploadAction?: PostUploadAction;
|
||||||
};
|
};
|
||||||
|
|
||||||
const IAIDndImage = (props: IAIDndImageProps) => {
|
const IAIDndImage = (props: IAIDndImageProps) => {
|
||||||
@ -39,11 +47,15 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
withMetadataOverlay = false,
|
withMetadataOverlay = false,
|
||||||
isDropDisabled = false,
|
isDropDisabled = false,
|
||||||
isDragDisabled = false,
|
isDragDisabled = false,
|
||||||
|
isUploadDisabled = false,
|
||||||
fallback = <IAIImageFallback />,
|
fallback = <IAIImageFallback />,
|
||||||
payloadImage,
|
payloadImage,
|
||||||
minSize = 24,
|
minSize = 24,
|
||||||
|
postUploadAction,
|
||||||
} = props;
|
} = props;
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
const dndId = useRef(uuidv4());
|
const dndId = useRef(uuidv4());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOver,
|
isOver,
|
||||||
setNodeRef: setDroppableRef,
|
setNodeRef: setDroppableRef,
|
||||||
@ -65,7 +77,36 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
data: {
|
data: {
|
||||||
image: payloadImage ? payloadImage : image,
|
image: payloadImage ? payloadImage : image,
|
||||||
},
|
},
|
||||||
disabled: isDragDisabled,
|
disabled: isDragDisabled || !image,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleOnDropAccepted = useCallback(
|
||||||
|
(files: Array<File>) => {
|
||||||
|
const file = files[0];
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(postUploadAction);
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
imageUploaded({
|
||||||
|
formData: { file },
|
||||||
|
imageCategory: 'user',
|
||||||
|
isIntermediate: 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 setNodeRef = useCombinedRefs(setDroppableRef, setDraggableRef);
|
const setNodeRef = useCombinedRefs(setDroppableRef, setDraggableRef);
|
||||||
@ -139,19 +180,28 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
minH: minSize,
|
minH: minSize,
|
||||||
bg: 'base.850',
|
bg: 'base.800',
|
||||||
w: 'full',
|
w: 'full',
|
||||||
h: 'full',
|
h: 'full',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
|
cursor: 'pointer',
|
||||||
|
transitionProperty: 'common',
|
||||||
|
transitionDuration: '0.1s',
|
||||||
|
color: 'base.500',
|
||||||
|
_hover: {
|
||||||
|
bg: 'base.750',
|
||||||
|
color: 'base.300',
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
|
{...getRootProps()}
|
||||||
>
|
>
|
||||||
|
<input {...getInputProps()} />
|
||||||
<Icon
|
<Icon
|
||||||
as={FaImage}
|
as={isUploadDisabled ? FaImage : FaUpload}
|
||||||
sx={{
|
sx={{
|
||||||
boxSize: 24,
|
boxSize: 12,
|
||||||
color: 'base.500',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -30,7 +30,7 @@ export const IAIDropOverlay = (props: Props) => {
|
|||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
insetInlineStart: 0,
|
||||||
w: 'full',
|
w: 'full',
|
||||||
h: 'full',
|
h: 'full',
|
||||||
}}
|
}}
|
||||||
@ -39,7 +39,7 @@ export const IAIDropOverlay = (props: Props) => {
|
|||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
insetInlineStart: 0,
|
||||||
w: 'full',
|
w: 'full',
|
||||||
h: 'full',
|
h: 'full',
|
||||||
bg: 'base.900',
|
bg: 'base.900',
|
||||||
@ -56,7 +56,7 @@ export const IAIDropOverlay = (props: Props) => {
|
|||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
insetInlineStart: 0,
|
||||||
w: 'full',
|
w: 'full',
|
||||||
h: 'full',
|
h: 'full',
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
import { Box } from '@chakra-ui/react';
|
import { Box } from '@chakra-ui/react';
|
||||||
import { ImageUploaderTriggerContext } from 'app/contexts/ImageUploaderTriggerContext';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import useImageUploader from 'common/hooks/useImageUploader';
|
import useImageUploader from 'common/hooks/useImageUploader';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { ResourceKey } from 'i18next';
|
|
||||||
import {
|
import {
|
||||||
KeyboardEvent,
|
KeyboardEvent,
|
||||||
memo,
|
memo,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { FileRejection, useDropzone } from 'react-dropzone';
|
import { FileRejection, useDropzone } from 'react-dropzone';
|
||||||
@ -19,10 +15,8 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { imageUploaded } from 'services/thunks/image';
|
import { imageUploaded } from 'services/thunks/image';
|
||||||
import ImageUploadOverlay from './ImageUploadOverlay';
|
import ImageUploadOverlay from './ImageUploadOverlay';
|
||||||
import { useAppToaster } from 'app/components/Toaster';
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
import { filter, map, some } from 'lodash-es';
|
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { ErrorCode } from 'react-dropzone';
|
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
[systemSelector, activeTabNameSelector],
|
[systemSelector, activeTabNameSelector],
|
||||||
@ -71,6 +65,7 @@ const ImageUploader = (props: ImageUploaderProps) => {
|
|||||||
formData: { file },
|
formData: { file },
|
||||||
imageCategory: 'user',
|
imageCategory: 'user',
|
||||||
isIntermediate: false,
|
isIntermediate: false,
|
||||||
|
postUploadAction: { type: 'TOAST_UPLOADED' },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -193,66 +193,6 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex sx={{ flexDir: 'column', gap: 3 }}>
|
|
||||||
<ControlNetImagePreview controlNet={props.controlNet} />
|
|
||||||
<ParamControlNetModel controlNetId={controlNetId} model={model} />
|
|
||||||
<Tabs
|
|
||||||
isFitted
|
|
||||||
orientation="horizontal"
|
|
||||||
variant="enclosed"
|
|
||||||
size="sm"
|
|
||||||
colorScheme="accent"
|
|
||||||
>
|
|
||||||
<TabList>
|
|
||||||
<Tab
|
|
||||||
sx={{ 'button&': { _selected: { borderBottomColor: 'base.800' } } }}
|
|
||||||
>
|
|
||||||
Model Config
|
|
||||||
</Tab>
|
|
||||||
<Tab
|
|
||||||
sx={{ 'button&': { _selected: { borderBottomColor: 'base.800' } } }}
|
|
||||||
>
|
|
||||||
Preprocess
|
|
||||||
</Tab>
|
|
||||||
</TabList>
|
|
||||||
<TabPanels sx={{ pt: 2 }}>
|
|
||||||
<TabPanel sx={{ p: 0 }}>
|
|
||||||
<ParamControlNetWeight
|
|
||||||
controlNetId={controlNetId}
|
|
||||||
weight={weight}
|
|
||||||
/>
|
|
||||||
<ParamControlNetBeginEnd
|
|
||||||
controlNetId={controlNetId}
|
|
||||||
beginStepPct={beginStepPct}
|
|
||||||
endStepPct={endStepPct}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel sx={{ p: 0 }}>
|
|
||||||
<ParamControlNetProcessorSelect
|
|
||||||
controlNetId={controlNetId}
|
|
||||||
processorNode={processorNode}
|
|
||||||
/>
|
|
||||||
<ControlNetProcessorComponent
|
|
||||||
controlNetId={controlNetId}
|
|
||||||
processorNode={processorNode}
|
|
||||||
/>
|
|
||||||
<ControlNetPreprocessButton controlNet={props.controlNet} />
|
|
||||||
{/* <IAIButton
|
|
||||||
size="sm"
|
|
||||||
leftIcon={<FaUndo />}
|
|
||||||
onClick={handleReset}
|
|
||||||
isDisabled={Boolean(!processedControlImage)}
|
|
||||||
>
|
|
||||||
Reset Processing
|
|
||||||
</IAIButton> */}
|
|
||||||
</TabPanel>
|
|
||||||
</TabPanels>
|
|
||||||
</Tabs>
|
|
||||||
<IAIButton onClick={handleDelete}>Remove ControlNet</IAIButton>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(ControlNet);
|
export default memo(ControlNet);
|
||||||
|
@ -70,6 +70,8 @@ const ControlNetImagePreview = (props: Props) => {
|
|||||||
isDropDisabled={Boolean(
|
isDropDisabled={Boolean(
|
||||||
processedControlImage && processorType !== 'none'
|
processedControlImage && processorType !== 'none'
|
||||||
)}
|
)}
|
||||||
|
isUploadDisabled={Boolean(controlImage)}
|
||||||
|
postUploadAction={{ type: 'SET_CONTROLNET_IMAGE', controlNetId }}
|
||||||
/>
|
/>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{shouldShowProcessedImage && (
|
{shouldShowProcessedImage && (
|
||||||
@ -118,6 +120,7 @@ const ControlNetImagePreview = (props: Props) => {
|
|||||||
image={processedControlImage}
|
image={processedControlImage}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
payloadImage={controlImage}
|
payloadImage={controlImage}
|
||||||
|
isUploadDisabled={true}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -50,21 +50,8 @@ const CurrentImagePreview = () => {
|
|||||||
shouldShowProgressInViewer,
|
shouldShowProgressInViewer,
|
||||||
shouldAntialiasProgressImage,
|
shouldAntialiasProgressImage,
|
||||||
} = useAppSelector(imagesSelector);
|
} = useAppSelector(imagesSelector);
|
||||||
const { shouldFetchImages } = useAppSelector(configSelector);
|
|
||||||
const toaster = useAppToaster();
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const handleError = useCallback(() => {
|
|
||||||
dispatch(imageSelected());
|
|
||||||
if (shouldFetchImages) {
|
|
||||||
toaster({
|
|
||||||
title: 'Something went wrong, please refresh',
|
|
||||||
status: 'error',
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [dispatch, toaster, shouldFetchImages]);
|
|
||||||
|
|
||||||
const handleDrop = useCallback(
|
const handleDrop = useCallback(
|
||||||
(droppedImage: ImageDTO) => {
|
(droppedImage: ImageDTO) => {
|
||||||
if (droppedImage.image_name === image?.image_name) {
|
if (droppedImage.image_name === image?.image_name) {
|
||||||
@ -112,8 +99,8 @@ const CurrentImagePreview = () => {
|
|||||||
<IAIDndImage
|
<IAIDndImage
|
||||||
image={image}
|
image={image}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
onError={handleError}
|
|
||||||
fallback={<IAIImageFallback sx={{ bg: 'none' }} />}
|
fallback={<IAIImageFallback sx={{ bg: 'none' }} />}
|
||||||
|
isUploadDisabled={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
@ -60,6 +60,11 @@ const ImageInputFieldComponent = (
|
|||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
resetIconSize="sm"
|
resetIconSize="sm"
|
||||||
|
postUploadAction={{
|
||||||
|
type: 'SET_NODES_IMAGE',
|
||||||
|
nodeId,
|
||||||
|
fieldName: field.name,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -6,11 +6,8 @@ import {
|
|||||||
initialImageChanged,
|
initialImageChanged,
|
||||||
} from 'features/parameters/store/generationSlice';
|
} from 'features/parameters/store/generationSlice';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import { configSelector } from '../../../../system/store/configSelectors';
|
|
||||||
import { useAppToaster } from 'app/components/Toaster';
|
|
||||||
import IAIDndImage from 'common/components/IAIDndImage';
|
import IAIDndImage from 'common/components/IAIDndImage';
|
||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
||||||
@ -28,28 +25,7 @@ const selector = createSelector(
|
|||||||
|
|
||||||
const InitialImagePreview = () => {
|
const InitialImagePreview = () => {
|
||||||
const { initialImage } = useAppSelector(selector);
|
const { initialImage } = useAppSelector(selector);
|
||||||
const { shouldFetchImages } = useAppSelector(configSelector);
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
|
||||||
const toaster = useAppToaster();
|
|
||||||
|
|
||||||
const handleError = useCallback(() => {
|
|
||||||
dispatch(clearInitialImage());
|
|
||||||
if (shouldFetchImages) {
|
|
||||||
toaster({
|
|
||||||
title: 'Something went wrong, please refresh',
|
|
||||||
status: 'error',
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
toaster({
|
|
||||||
title: t('toast.parametersFailed'),
|
|
||||||
description: t('toast.parametersFailedDesc'),
|
|
||||||
status: 'error',
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [dispatch, t, toaster, shouldFetchImages]);
|
|
||||||
|
|
||||||
const handleDrop = useCallback(
|
const handleDrop = useCallback(
|
||||||
(droppedImage: ImageDTO) => {
|
(droppedImage: ImageDTO) => {
|
||||||
@ -81,6 +57,7 @@ const InitialImagePreview = () => {
|
|||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
fallback={<IAIImageFallback sx={{ bg: 'none' }} />}
|
fallback={<IAIImageFallback sx={{ bg: 'none' }} />}
|
||||||
|
postUploadAction={{ type: 'SET_INITIAL_IMAGE' }}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -32,7 +32,49 @@ export const imageMetadataReceived = createAppAsyncThunk(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
type ImageUploadedArg = Parameters<(typeof ImagesService)['uploadImage']>[0];
|
type ControlNetAction = {
|
||||||
|
type: 'SET_CONTROLNET_IMAGE';
|
||||||
|
controlNetId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type InitialImageAction = {
|
||||||
|
type: 'SET_INITIAL_IMAGE';
|
||||||
|
};
|
||||||
|
|
||||||
|
type NodesAction = {
|
||||||
|
type: 'SET_NODES_IMAGE';
|
||||||
|
nodeId: string;
|
||||||
|
fieldName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CanvasInitialImageAction = {
|
||||||
|
type: 'SET_CANVAS_INITIAL_IMAGE';
|
||||||
|
};
|
||||||
|
|
||||||
|
type CanvasMergedAction = {
|
||||||
|
type: 'TOAST_CANVAS_MERGED';
|
||||||
|
};
|
||||||
|
|
||||||
|
type CanvasSavedToGalleryAction = {
|
||||||
|
type: 'TOAST_CANVAS_SAVED_TO_GALLERY';
|
||||||
|
};
|
||||||
|
|
||||||
|
type UploadedToastAction = {
|
||||||
|
type: 'TOAST_UPLOADED';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PostUploadAction =
|
||||||
|
| ControlNetAction
|
||||||
|
| InitialImageAction
|
||||||
|
| NodesAction
|
||||||
|
| CanvasInitialImageAction
|
||||||
|
| CanvasMergedAction
|
||||||
|
| CanvasSavedToGalleryAction
|
||||||
|
| UploadedToastAction;
|
||||||
|
|
||||||
|
type ImageUploadedArg = Parameters<(typeof ImagesService)['uploadImage']>[0] & {
|
||||||
|
postUploadAction?: PostUploadAction;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `ImagesService.uploadImage()` thunk
|
* `ImagesService.uploadImage()` thunk
|
||||||
@ -40,8 +82,9 @@ type ImageUploadedArg = Parameters<(typeof ImagesService)['uploadImage']>[0];
|
|||||||
export const imageUploaded = createAppAsyncThunk(
|
export const imageUploaded = createAppAsyncThunk(
|
||||||
'api/imageUploaded',
|
'api/imageUploaded',
|
||||||
async (arg: ImageUploadedArg) => {
|
async (arg: ImageUploadedArg) => {
|
||||||
// strip out `activeTabName` from arg - the route does not need it
|
// `postUploadAction` is only used by the listener middleware - destructure it out
|
||||||
const response = await ImagesService.uploadImage(arg);
|
const { postUploadAction, ...rest } = arg;
|
||||||
|
const response = await ImagesService.uploadImage(rest);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user