feat(ui): improve image upload handling

This commit is contained in:
psychedelicious 2023-05-26 14:19:32 +10:00
parent 17164a37a8
commit 0d5f44b153
7 changed files with 45 additions and 26 deletions

View File

@ -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();

View File

@ -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({

View File

@ -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' }),
}, },

View File

@ -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',
})
);
},
});
};

View File

@ -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,
}; };
} }

View File

@ -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,
}) })

View File

@ -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 };
} }
); );