mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): fix uploads & other bugs
This commit is contained in:
parent
c406be6f4f
commit
6f78c073ed
@ -5,7 +5,6 @@ import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { imageUploaded } from 'services/thunks/image';
|
import { imageUploaded } from 'services/thunks/image';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
|
||||||
import { setMergedCanvas } from 'features/canvas/store/canvasSlice';
|
import { setMergedCanvas } from 'features/canvas/store/canvasSlice';
|
||||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||||
|
|
||||||
@ -66,7 +65,7 @@ export const addCanvasMergedListener = () => {
|
|||||||
action.meta.arg.formData.file.name === filename
|
action.meta.arg.formData.file.name === filename
|
||||||
);
|
);
|
||||||
|
|
||||||
const mergedCanvasImage = deserializeImageResponse(payload.response);
|
const mergedCanvasImage = payload.response;
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
setMergedCanvas({
|
setMergedCanvas({
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
import { uploadAdded } from 'features/gallery/store/uploadsSlice';
|
import { uploadAdded } from 'features/gallery/store/uploadsSlice';
|
||||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||||
@ -7,6 +6,7 @@ import { addToast } from 'features/system/store/systemSlice';
|
|||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
import { resultAdded } from 'features/gallery/store/resultsSlice';
|
import { resultAdded } from 'features/gallery/store/resultsSlice';
|
||||||
|
import { isResultsImageDTO, isUploadsImageDTO } from 'services/types/guards';
|
||||||
|
|
||||||
export const addImageUploadedListener = () => {
|
export const addImageUploadedListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -14,13 +14,11 @@ export const addImageUploadedListener = () => {
|
|||||||
imageUploaded.fulfilled.match(action) &&
|
imageUploaded.fulfilled.match(action) &&
|
||||||
action.payload.response.image_type !== 'intermediates',
|
action.payload.response.image_type !== 'intermediates',
|
||||||
effect: (action, { dispatch, getState }) => {
|
effect: (action, { dispatch, getState }) => {
|
||||||
const { response } = action.payload;
|
const { response: image } = action.payload;
|
||||||
const { imageType } = action.meta.arg;
|
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const image = deserializeImageResponse(response);
|
|
||||||
|
|
||||||
if (imageType === 'uploads') {
|
if (isUploadsImageDTO(image)) {
|
||||||
dispatch(uploadAdded(image));
|
dispatch(uploadAdded(image));
|
||||||
|
|
||||||
dispatch(addToast({ title: 'Image Uploaded', status: 'success' }));
|
dispatch(addToast({ title: 'Image Uploaded', status: 'success' }));
|
||||||
@ -38,7 +36,7 @@ export const addImageUploadedListener = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imageType === 'results') {
|
if (isResultsImageDTO(image)) {
|
||||||
dispatch(resultAdded(image));
|
dispatch(resultAdded(image));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -13,7 +13,6 @@ import { DragEvent, MouseEvent, memo, useCallback, useState } from 'react';
|
|||||||
import { FaCheck, FaExpand, FaImage, FaShare, FaTrash } from 'react-icons/fa';
|
import { FaCheck, FaExpand, FaImage, FaShare, FaTrash } from 'react-icons/fa';
|
||||||
import DeleteImageModal from './DeleteImageModal';
|
import DeleteImageModal from './DeleteImageModal';
|
||||||
import { ContextMenu } from 'chakra-ui-contextmenu';
|
import { ContextMenu } from 'chakra-ui-contextmenu';
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
|
||||||
import {
|
import {
|
||||||
resizeAndScaleCanvas,
|
resizeAndScaleCanvas,
|
||||||
setInitialCanvasImage,
|
setInitialCanvasImage,
|
||||||
@ -168,9 +167,9 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
dispatch(initialImageSelected(image));
|
dispatch(initialImageSelected(image));
|
||||||
}, [dispatch, image]);
|
}, [dispatch, image]);
|
||||||
|
|
||||||
const handleRecallInitialImage = useCallback(() => {
|
// const handleRecallInitialImage = useCallback(() => {
|
||||||
recallInitialImage(image.metadata.invokeai?.node?.image);
|
// recallInitialImage(image.metadata.invokeai?.node?.image);
|
||||||
}, [image, recallInitialImage]);
|
// }, [image, recallInitialImage]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: the rest of these
|
* TODO: the rest of these
|
||||||
@ -238,13 +237,13 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
>
|
>
|
||||||
{t('parameters.useSeed')}
|
{t('parameters.useSeed')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
{/* <MenuItem
|
||||||
icon={<IoArrowUndoCircleOutline />}
|
icon={<IoArrowUndoCircleOutline />}
|
||||||
onClickCapture={handleRecallInitialImage}
|
onClickCapture={handleRecallInitialImage}
|
||||||
isDisabled={image?.metadata?.type !== 'img2img'}
|
isDisabled={image?.metadata?.type !== 'img2img'}
|
||||||
>
|
>
|
||||||
{t('parameters.useInitImg')}
|
{t('parameters.useInitImg')}
|
||||||
</MenuItem>
|
</MenuItem> */}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<IoArrowUndoCircleOutline />}
|
icon={<IoArrowUndoCircleOutline />}
|
||||||
onClickCapture={handleUseAllParameters}
|
onClickCapture={handleUseAllParameters}
|
||||||
|
@ -164,9 +164,7 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
|||||||
isExternal
|
isExternal
|
||||||
maxW="calc(100% - 3rem)"
|
maxW="calc(100% - 3rem)"
|
||||||
>
|
>
|
||||||
{image.image_url.length > 64
|
{image.image_name}
|
||||||
? image.image_url.substring(0, 64).concat('...')
|
|
||||||
: image.image_url}
|
|
||||||
<ExternalLinkIcon mx="2px" />
|
<ExternalLinkIcon mx="2px" />
|
||||||
</Link>
|
</Link>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { AnyAction } from '@reduxjs/toolkit';
|
|
||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
import { createAppAsyncThunk } from 'app/store/storeUtils';
|
import { createAppAsyncThunk } from 'app/store/storeUtils';
|
||||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||||
@ -56,10 +55,7 @@ export const imageUploaded = createAppAsyncThunk(
|
|||||||
const response = await ImagesService.uploadImage(rest);
|
const response = await ImagesService.uploadImage(rest);
|
||||||
const { location } = getHeaders(response);
|
const { location } = getHeaders(response);
|
||||||
|
|
||||||
imagesLog.info(
|
imagesLog.debug({ arg: '<Blob>', response, location }, 'Image uploaded');
|
||||||
{ arg: '<Blob>', response, location },
|
|
||||||
`Image uploaded (${response.image_name})`
|
|
||||||
);
|
|
||||||
|
|
||||||
return { response, location };
|
return { response, location };
|
||||||
}
|
}
|
||||||
@ -75,10 +71,7 @@ export const imageDeleted = createAppAsyncThunk(
|
|||||||
async (arg: ImageDeletedArg) => {
|
async (arg: ImageDeletedArg) => {
|
||||||
const response = await ImagesService.deleteImage(arg);
|
const response = await ImagesService.deleteImage(arg);
|
||||||
|
|
||||||
imagesLog.info(
|
imagesLog.debug({ arg, response }, 'Image deleted');
|
||||||
{ arg, response },
|
|
||||||
`Image deleted (${arg.imageType} - ${arg.imageName})`
|
|
||||||
);
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { ResultsImageDTO } from 'features/gallery/store/resultsSlice';
|
||||||
|
import { UploadsImageDTO } from 'features/gallery/store/uploadsSlice';
|
||||||
import { get, isObject, isString } from 'lodash-es';
|
import { get, isObject, isString } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
GraphExecutionState,
|
GraphExecutionState,
|
||||||
@ -10,8 +12,15 @@ import {
|
|||||||
ImageType,
|
ImageType,
|
||||||
ImageField,
|
ImageField,
|
||||||
LatentsOutput,
|
LatentsOutput,
|
||||||
|
ImageDTO,
|
||||||
} from 'services/api';
|
} from 'services/api';
|
||||||
|
|
||||||
|
export const isUploadsImageDTO = (image: ImageDTO): image is UploadsImageDTO =>
|
||||||
|
image.image_type === 'uploads';
|
||||||
|
|
||||||
|
export const isResultsImageDTO = (image: ImageDTO): image is ResultsImageDTO =>
|
||||||
|
image.image_type === 'results';
|
||||||
|
|
||||||
export const isImageOutput = (
|
export const isImageOutput = (
|
||||||
output: GraphExecutionState['results'][string]
|
output: GraphExecutionState['results'][string]
|
||||||
): output is ImageOutput => output.type === 'image_output';
|
): output is ImageOutput => output.type === 'image_output';
|
||||||
@ -43,11 +52,6 @@ export const isCollectOutput = (
|
|||||||
export const isImageType = (t: unknown): t is ImageType =>
|
export const isImageType = (t: unknown): t is ImageType =>
|
||||||
isString(t) && ['results', 'uploads', 'intermediates'].includes(t);
|
isString(t) && ['results', 'uploads', 'intermediates'].includes(t);
|
||||||
|
|
||||||
export const isImage = (image: unknown): image is Image =>
|
|
||||||
isObject(image) &&
|
|
||||||
isString(get(image, 'name')) &&
|
|
||||||
isImageType(get(image, 'type'));
|
|
||||||
|
|
||||||
export const isImageField = (imageField: unknown): imageField is ImageField =>
|
export const isImageField = (imageField: unknown): imageField is ImageField =>
|
||||||
isObject(imageField) &&
|
isObject(imageField) &&
|
||||||
isString(get(imageField, 'image_name')) &&
|
isString(get(imageField, 'image_name')) &&
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
import { ImageType } from 'services/api';
|
|
||||||
|
|
||||||
export const buildImageUrls = (
|
|
||||||
imageType: ImageType,
|
|
||||||
imageName: string
|
|
||||||
): { url: string; thumbnail: string } => {
|
|
||||||
const url = `api/v1/images/${imageType}/${imageName}`;
|
|
||||||
|
|
||||||
const thumbnail = `api/v1/images/${imageType}/thumbnails/${
|
|
||||||
imageName.split('.')[0]
|
|
||||||
}.webp`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
url,
|
|
||||||
thumbnail,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const extractTimestampFromImageName = (imageName: string) => {
|
|
||||||
const timestamp = imageName.split('_')?.pop()?.split('.')[0];
|
|
||||||
|
|
||||||
if (timestamp === undefined) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Number(timestamp);
|
|
||||||
};
|
|
@ -1,29 +0,0 @@
|
|||||||
import { Image } from 'app/types/invokeai';
|
|
||||||
import { parseInvokeAIMetadata } from 'common/util/parseMetadata';
|
|
||||||
import { ImageResponse } from 'services/api';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process ImageReponse objects, which we get from the `list_images` endpoint.
|
|
||||||
*/
|
|
||||||
export const deserializeImageResponse = (
|
|
||||||
imageResponse: ImageResponse
|
|
||||||
): Image => {
|
|
||||||
const { image_name, image_type, image_url, metadata, thumbnail_url } =
|
|
||||||
imageResponse;
|
|
||||||
|
|
||||||
// TODO: parse metadata - just leaving it as-is for now
|
|
||||||
const { invokeai, ...rest } = metadata;
|
|
||||||
|
|
||||||
const parsedMetadata = parseInvokeAIMetadata(invokeai);
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: image_name,
|
|
||||||
type: image_type,
|
|
||||||
url: image_url,
|
|
||||||
thumbnail: thumbnail_url,
|
|
||||||
metadata: {
|
|
||||||
...rest,
|
|
||||||
...(invokeai ? { invokeai: parsedMetadata } : {}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user