mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): improve image upload handling
This commit is contained in:
parent
17164a37a8
commit
0d5f44b153
@ -8,7 +8,10 @@ import type { TypedStartListening, TypedAddListener } from '@reduxjs/toolkit';
|
|||||||
|
|
||||||
import type { RootState, AppDispatch } from '../../store';
|
import type { RootState, AppDispatch } from '../../store';
|
||||||
import { addInitialImageSelectedListener } from './listeners/initialImageSelected';
|
import { addInitialImageSelectedListener } from './listeners/initialImageSelected';
|
||||||
import { addImageUploadedListener } from './listeners/imageUploaded';
|
import {
|
||||||
|
addImageUploadedFulfilledListener,
|
||||||
|
addImageUploadedRejectedListener,
|
||||||
|
} from './listeners/imageUploaded';
|
||||||
import { addRequestedImageDeletionListener } from './listeners/imageDeleted';
|
import { addRequestedImageDeletionListener } from './listeners/imageDeleted';
|
||||||
import { addUserInvokedCanvasListener } from './listeners/userInvokedCanvas';
|
import { addUserInvokedCanvasListener } from './listeners/userInvokedCanvas';
|
||||||
import { addUserInvokedNodesListener } from './listeners/userInvokedNodes';
|
import { addUserInvokedNodesListener } from './listeners/userInvokedNodes';
|
||||||
@ -47,23 +50,28 @@ export type AppListenerEffect = ListenerEffect<
|
|||||||
AppDispatch
|
AppDispatch
|
||||||
>;
|
>;
|
||||||
|
|
||||||
addImageUploadedListener();
|
// Image uploads
|
||||||
|
addImageUploadedFulfilledListener();
|
||||||
|
addImageUploadedRejectedListener();
|
||||||
|
|
||||||
addInitialImageSelectedListener();
|
addInitialImageSelectedListener();
|
||||||
|
|
||||||
addRequestedImageDeletionListener();
|
addRequestedImageDeletionListener();
|
||||||
|
|
||||||
|
// Invoking stuff
|
||||||
addUserInvokedCanvasListener();
|
addUserInvokedCanvasListener();
|
||||||
addUserInvokedNodesListener();
|
addUserInvokedNodesListener();
|
||||||
addUserInvokedTextToImageListener();
|
addUserInvokedTextToImageListener();
|
||||||
addUserInvokedImageToImageListener();
|
addUserInvokedImageToImageListener();
|
||||||
addSessionReadyToInvokeListener();
|
addSessionReadyToInvokeListener();
|
||||||
|
|
||||||
|
// Canvas actions
|
||||||
addCanvasSavedToGalleryListener();
|
addCanvasSavedToGalleryListener();
|
||||||
addCanvasDownloadedAsImageListener();
|
addCanvasDownloadedAsImageListener();
|
||||||
addCanvasCopiedToClipboardListener();
|
addCanvasCopiedToClipboardListener();
|
||||||
addCanvasMergedListener();
|
addCanvasMergedListener();
|
||||||
|
|
||||||
// socketio
|
// socketio
|
||||||
|
|
||||||
addGeneratorProgressListener();
|
addGeneratorProgressListener();
|
||||||
addGraphExecutionStateCompleteListener();
|
addGraphExecutionStateCompleteListener();
|
||||||
addInvocationCompleteListener();
|
addInvocationCompleteListener();
|
||||||
|
@ -52,7 +52,6 @@ export const addCanvasMergedListener = () => {
|
|||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
imageUploaded({
|
imageUploaded({
|
||||||
imageType: 'intermediates',
|
|
||||||
formData: {
|
formData: {
|
||||||
file: new File([blob], filename, { type: 'image/png' }),
|
file: new File([blob], filename, { type: 'image/png' }),
|
||||||
},
|
},
|
||||||
@ -65,7 +64,7 @@ export const addCanvasMergedListener = () => {
|
|||||||
action.meta.arg.formData.file.name === filename
|
action.meta.arg.formData.file.name === filename
|
||||||
);
|
);
|
||||||
|
|
||||||
const mergedCanvasImage = payload.response;
|
const mergedCanvasImage = payload;
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
setMergedCanvas({
|
setMergedCanvas({
|
||||||
|
@ -29,7 +29,6 @@ export const addCanvasSavedToGalleryListener = () => {
|
|||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
imageUploaded({
|
imageUploaded({
|
||||||
imageType: 'results',
|
|
||||||
formData: {
|
formData: {
|
||||||
file: new File([blob], 'mergedCanvas.png', { type: 'image/png' }),
|
file: new File([blob], 'mergedCanvas.png', { type: 'image/png' }),
|
||||||
},
|
},
|
||||||
|
@ -7,17 +7,23 @@ 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';
|
import { isResultsImageDTO, isUploadsImageDTO } from 'services/types/guards';
|
||||||
|
import { log } from 'app/logging/useLogger';
|
||||||
|
|
||||||
export const addImageUploadedListener = () => {
|
const moduleLog = log.child({ namespace: 'image' });
|
||||||
|
|
||||||
|
export const addImageUploadedFulfilledListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
predicate: (action): action is ReturnType<typeof imageUploaded.fulfilled> =>
|
predicate: (action): action is ReturnType<typeof imageUploaded.fulfilled> =>
|
||||||
imageUploaded.fulfilled.match(action) &&
|
imageUploaded.fulfilled.match(action) &&
|
||||||
action.payload.response.is_intermediate === false,
|
action.payload.is_intermediate === false,
|
||||||
effect: (action, { dispatch, getState }) => {
|
effect: (action, { dispatch, getState }) => {
|
||||||
const { response: image } = action.payload;
|
const image = action.payload;
|
||||||
|
|
||||||
|
moduleLog.debug({ arg: '<Blob>', image }, 'Image uploaded');
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
|
// Handle uploads
|
||||||
if (isUploadsImageDTO(image)) {
|
if (isUploadsImageDTO(image)) {
|
||||||
dispatch(uploadAdded(image));
|
dispatch(uploadAdded(image));
|
||||||
|
|
||||||
@ -36,9 +42,26 @@ export const addImageUploadedListener = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle results
|
||||||
|
// TODO: Can this ever happen? I don't think so...
|
||||||
if (isResultsImageDTO(image)) {
|
if (isResultsImageDTO(image)) {
|
||||||
dispatch(resultAdded(image));
|
dispatch(resultAdded(image));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const addImageUploadedRejectedListener = () => {
|
||||||
|
startAppListening({
|
||||||
|
actionCreator: imageUploaded.rejected,
|
||||||
|
effect: (action, { dispatch }) => {
|
||||||
|
dispatch(
|
||||||
|
addToast({
|
||||||
|
title: 'Image Upload Failed',
|
||||||
|
description: action.error.message,
|
||||||
|
status: 'error',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
@ -106,19 +106,16 @@ export const addUserInvokedCanvasListener = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Wait for the image to be uploaded
|
// Wait for the image to be uploaded
|
||||||
const [{ payload: basePayload }] = await take(
|
const [{ payload: baseImageDTO }] = await take(
|
||||||
(action): action is ReturnType<typeof imageUploaded.fulfilled> =>
|
(action): action is ReturnType<typeof imageUploaded.fulfilled> =>
|
||||||
imageUploaded.fulfilled.match(action) &&
|
imageUploaded.fulfilled.match(action) &&
|
||||||
action.meta.arg.formData.file.name === baseFilename
|
action.meta.arg.formData.file.name === baseFilename
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update the base node with the image name and type
|
// Update the base node with the image name and type
|
||||||
const { image_name: baseName, image_type: baseType } =
|
|
||||||
basePayload.response;
|
|
||||||
|
|
||||||
baseNode.image = {
|
baseNode.image = {
|
||||||
image_name: baseName,
|
image_name: baseImageDTO.image_name,
|
||||||
image_type: baseType,
|
image_type: baseImageDTO.image_type,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,19 +132,16 @@ export const addUserInvokedCanvasListener = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Wait for the mask to be uploaded
|
// Wait for the mask to be uploaded
|
||||||
const [{ payload: maskPayload }] = await take(
|
const [{ payload: maskImageDTO }] = await take(
|
||||||
(action): action is ReturnType<typeof imageUploaded.fulfilled> =>
|
(action): action is ReturnType<typeof imageUploaded.fulfilled> =>
|
||||||
imageUploaded.fulfilled.match(action) &&
|
imageUploaded.fulfilled.match(action) &&
|
||||||
action.meta.arg.formData.file.name === maskFilename
|
action.meta.arg.formData.file.name === maskFilename
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update the base node with the image name and type
|
// Update the base node with the image name and type
|
||||||
const { image_name: maskName, image_type: maskType } =
|
|
||||||
maskPayload.response;
|
|
||||||
|
|
||||||
baseNode.mask = {
|
baseNode.mask = {
|
||||||
image_name: maskName,
|
image_name: maskImageDTO.image_name,
|
||||||
image_type: maskType,
|
image_type: maskImageDTO.image_type,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,6 @@ const ImageUploader = (props: ImageUploaderProps) => {
|
|||||||
async (file: File) => {
|
async (file: File) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
imageUploaded({
|
imageUploaded({
|
||||||
imageType: 'uploads',
|
|
||||||
formData: { file },
|
formData: { file },
|
||||||
activeTabName,
|
activeTabName,
|
||||||
})
|
})
|
||||||
|
@ -53,11 +53,8 @@ export const imageUploaded = createAppAsyncThunk(
|
|||||||
// strip out `activeTabName` from arg - the route does not need it
|
// strip out `activeTabName` from arg - the route does not need it
|
||||||
const { activeTabName, ...rest } = arg;
|
const { activeTabName, ...rest } = arg;
|
||||||
const response = await ImagesService.uploadImage(rest);
|
const response = await ImagesService.uploadImage(rest);
|
||||||
const { location } = getHeaders(response);
|
|
||||||
|
|
||||||
imagesLog.debug({ arg: '<Blob>', response, location }, 'Image uploaded');
|
return response;
|
||||||
|
|
||||||
return { response, location };
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user