feat(ui): handle new image origin/category setup

- Update all thunks & network related things
- Update gallery

What I have not done yet is rename the gallery tabs and the relevant slices, but I believe the functionality is all there.

Also I fixed several bugs along the way but couldn't really commit them separately bc I was refactoring. Can't remember what they were, but related to the gallery image switching.
This commit is contained in:
psychedelicious
2023-05-27 21:46:03 +10:00
committed by Kent Keirsey
parent d78e3572e3
commit 29fcc92da9
29 changed files with 181 additions and 345 deletions

View File

@@ -67,6 +67,10 @@ import {
addReceivedUploadImagesPageFulfilledListener,
addReceivedUploadImagesPageRejectedListener,
} from './listeners/receivedUploadImages';
import {
addImageUpdatedFulfilledListener,
addImageUpdatedRejectedListener,
} from './listeners/imageUpdated';
export const listenerMiddleware = createListenerMiddleware();
@@ -90,6 +94,11 @@ export type AppListenerEffect = ListenerEffect<
addImageUploadedFulfilledListener();
addImageUploadedRejectedListener();
// Image updated
addImageUpdatedFulfilledListener();
addImageUpdatedRejectedListener();
// Image selected
addInitialImageSelectedListener();
// Image deleted

View File

@@ -57,7 +57,6 @@ export const addCanvasMergedListener = () => {
},
imageCategory: 'general',
isIntermediate: true,
showInGallery: false,
})
);

View File

@@ -37,8 +37,7 @@ export const addCanvasSavedToGalleryListener = () => {
file: new File([blob], filename, { type: 'image/png' }),
},
imageCategory: 'general',
isIntermediate: false,
showInGallery: true,
isIntermediate: true,
})
);

View File

@@ -4,8 +4,15 @@ import { imageDeleted } from 'services/thunks/image';
import { log } from 'app/logging/useLogger';
import { clamp } from 'lodash-es';
import { imageSelected } from 'features/gallery/store/gallerySlice';
import { uploadsAdapter } from 'features/gallery/store/uploadsSlice';
import { resultsAdapter } from 'features/gallery/store/resultsSlice';
import {
uploadRemoved,
uploadsAdapter,
} from 'features/gallery/store/uploadsSlice';
import {
resultRemoved,
resultsAdapter,
} from 'features/gallery/store/resultsSlice';
import { isUploadsImageDTO } from 'services/types/guards';
const moduleLog = log.child({ namespace: 'addRequestedImageDeletionListener' });
@@ -22,13 +29,17 @@ export const addRequestedImageDeletionListener = () => {
return;
}
const { image_name, image_type } = image;
const { image_name, image_origin } = image;
const selectedImageName = getState().gallery.selectedImage?.image_name;
const state = getState();
const selectedImage = state.gallery.selectedImage;
const isUserImage = isUploadsImageDTO(selectedImage);
if (selectedImage && selectedImage.image_name === image_name) {
const allIds = isUserImage ? state.uploads.ids : state.results.ids;
if (selectedImageName === image_name) {
const allIds = getState()[image_type].ids;
const allEntities = getState()[image_type].entities;
const allEntities = isUserImage
? state.uploads.entities
: state.results.entities;
const deletedImageIndex = allIds.findIndex(
(result) => result.toString() === image_name
@@ -53,7 +64,15 @@ export const addRequestedImageDeletionListener = () => {
}
}
dispatch(imageDeleted({ imageName: image_name, imageType: image_type }));
if (isUserImage) {
dispatch(uploadRemoved(image_name));
} else {
dispatch(resultRemoved(image_name));
}
dispatch(
imageDeleted({ imageName: image_name, imageOrigin: image_origin })
);
},
});
};
@@ -65,12 +84,12 @@ export const addImageDeletedPendingListener = () => {
startAppListening({
actionCreator: imageDeleted.pending,
effect: (action, { dispatch, getState }) => {
const { imageName, imageType } = action.meta.arg;
const { imageName, imageOrigin } = action.meta.arg;
// Preemptively remove the image from the gallery
if (imageType === 'uploads') {
if (imageOrigin === 'external') {
uploadsAdapter.removeOne(getState().uploads, imageName);
}
if (imageType === 'results') {
if (imageOrigin === 'internal') {
resultsAdapter.removeOne(getState().results, imageName);
}
},

View File

@@ -1,14 +1,9 @@
import { log } from 'app/logging/useLogger';
import { startAppListening } from '..';
import { imageMetadataReceived } from 'services/thunks/image';
import {
ResultsImageDTO,
resultUpserted,
} from 'features/gallery/store/resultsSlice';
import {
UploadsImageDTO,
uploadUpserted,
} from 'features/gallery/store/uploadsSlice';
import { resultUpserted } from 'features/gallery/store/resultsSlice';
import { uploadUpserted } from 'features/gallery/store/uploadsSlice';
import { imageSelected } from 'features/gallery/store/gallerySlice';
const moduleLog = log.child({ namespace: 'image' });
@@ -16,15 +11,15 @@ export const addImageMetadataReceivedFulfilledListener = () => {
startAppListening({
actionCreator: imageMetadataReceived.fulfilled,
effect: (action, { getState, dispatch }) => {
const image = action.payload;
moduleLog.debug({ data: { image } }, 'Image metadata received');
const imageDTO = action.payload;
moduleLog.debug({ data: { imageDTO } }, 'Image metadata received');
if (image.image_type === 'results') {
dispatch(resultUpserted(action.payload as ResultsImageDTO));
if (imageDTO.image_origin === 'internal') {
dispatch(resultUpserted(imageDTO));
}
if (image.image_type === 'uploads') {
dispatch(uploadUpserted(action.payload as UploadsImageDTO));
if (imageDTO.image_origin === 'external') {
dispatch(uploadUpserted(imageDTO));
}
},
});

View File

@@ -0,0 +1,26 @@
import { startAppListening } from '..';
import { imageUpdated } from 'services/thunks/image';
import { log } from 'app/logging/useLogger';
const moduleLog = log.child({ namespace: 'image' });
export const addImageUpdatedFulfilledListener = () => {
startAppListening({
actionCreator: imageUpdated.fulfilled,
effect: (action, { dispatch, getState }) => {
moduleLog.debug(
{ oldImage: action.meta.arg, updatedImage: action.payload },
'Image updated'
);
},
});
};
export const addImageUpdatedRejectedListener = () => {
startAppListening({
actionCreator: imageUpdated.rejected,
effect: (action, { dispatch }) => {
moduleLog.debug({ oldImage: action.meta.arg }, 'Image update failed');
},
});
};

View File

@@ -1,6 +1,9 @@
import { startAppListening } from '..';
import { uploadUpserted } from 'features/gallery/store/uploadsSlice';
import { imageSelected } from 'features/gallery/store/gallerySlice';
import {
imageSelected,
setCurrentCategory,
} from 'features/gallery/store/gallerySlice';
import { imageUploaded } from 'services/thunks/image';
import { addToast } from 'features/system/store/systemSlice';
import { resultUpserted } from 'features/gallery/store/resultsSlice';
@@ -10,31 +13,30 @@ const moduleLog = log.child({ namespace: 'image' });
export const addImageUploadedFulfilledListener = () => {
startAppListening({
predicate: (action): action is ReturnType<typeof imageUploaded.fulfilled> =>
imageUploaded.fulfilled.match(action) &&
action.payload.is_intermediate === false,
actionCreator: imageUploaded.fulfilled,
effect: (action, { dispatch, getState }) => {
const image = action.payload;
moduleLog.debug({ arg: '<Blob>', image }, 'Image uploaded');
if (action.payload.is_intermediate) {
// No further actions needed for intermediate images
return;
}
const state = getState();
// Handle uploads
if (!image.show_in_gallery && image.image_type === 'uploads') {
if (image.image_category === 'user' && !image.is_intermediate) {
dispatch(uploadUpserted(image));
dispatch(addToast({ title: 'Image Uploaded', status: 'success' }));
if (state.gallery.shouldAutoSwitchToNewImages) {
dispatch(imageSelected(image));
}
}
// Handle results
// TODO: Can this ever happen? I don't think so...
if (image.show_in_gallery) {
if (image.image_category !== 'user' && !image.is_intermediate) {
dispatch(resultUpserted(image));
dispatch(setCurrentCategory('results'));
}
},
});
@@ -44,6 +46,9 @@ export const addImageUploadedRejectedListener = () => {
startAppListening({
actionCreator: imageUploaded.rejected,
effect: (action, { dispatch }) => {
const { formData, ...rest } = action.meta.arg;
const sanitizedData = { arg: { ...rest, formData: { file: '<Blob>' } } };
moduleLog.error({ data: sanitizedData }, 'Image upload failed');
dispatch(
addToast({
title: 'Image Upload Failed',

View File

@@ -13,9 +13,9 @@ export const addImageUrlsReceivedFulfilledListener = () => {
const image = action.payload;
moduleLog.debug({ data: { image } }, 'Image URLs received');
const { image_type, image_name, image_url, thumbnail_url } = image;
const { image_origin, image_name, image_url, thumbnail_url } = image;
if (image_type === 'results') {
if (image_origin === 'results') {
resultsAdapter.updateOne(getState().results, {
id: image_name,
changes: {
@@ -25,7 +25,7 @@ export const addImageUrlsReceivedFulfilledListener = () => {
});
}
if (image_type === 'uploads') {
if (image_origin === 'uploads') {
uploadsAdapter.updateOne(getState().uploads, {
id: image_name,
changes: {

View File

@@ -30,14 +30,14 @@ export const addInitialImageSelectedListener = () => {
return;
}
const { image_name, image_type } = action.payload;
const { image_name, image_origin } = action.payload;
let image: ImageDTO | undefined;
const state = getState();
if (image_type === 'results') {
if (image_origin === 'results') {
image = selectResultsById(state, image_name);
} else if (image_type === 'uploads') {
} else if (image_origin === 'uploads') {
image = selectUploadsById(state, image_name);
}

View File

@@ -34,13 +34,13 @@ export const addInvocationCompleteListener = () => {
// This complete event has an associated image output
if (isImageOutput(result) && !nodeDenylist.includes(node.type)) {
const { image_name, image_type } = result.image;
const { image_name, image_origin } = result.image;
// Get its metadata
dispatch(
imageMetadataReceived({
imageName: image_name,
imageType: image_type,
imageOrigin: image_origin,
})
);
@@ -48,10 +48,6 @@ export const addInvocationCompleteListener = () => {
imageMetadataReceived.fulfilled.match
);
if (getState().gallery.shouldAutoSwitchToNewImages) {
dispatch(imageSelected(imageDTO));
}
// Handle canvas image
if (
graph_execution_state_id ===

View File

@@ -103,7 +103,6 @@ export const addUserInvokedCanvasListener = () => {
},
imageCategory: 'general',
isIntermediate: true,
showInGallery: false,
})
);
@@ -117,7 +116,7 @@ export const addUserInvokedCanvasListener = () => {
// Update the base node with the image name and type
baseNode.image = {
image_name: baseImageDTO.image_name,
image_type: baseImageDTO.image_type,
image_origin: baseImageDTO.image_origin,
};
}
@@ -131,7 +130,6 @@ export const addUserInvokedCanvasListener = () => {
},
imageCategory: 'mask',
isIntermediate: true,
showInGallery: false,
})
);
@@ -145,7 +143,7 @@ export const addUserInvokedCanvasListener = () => {
// Update the base node with the image name and type
baseNode.mask = {
image_name: maskImageDTO.image_name,
image_type: maskImageDTO.image_type,
image_origin: maskImageDTO.image_origin,
};
}
@@ -162,7 +160,7 @@ export const addUserInvokedCanvasListener = () => {
dispatch(
imageUpdated({
imageName: baseNode.image.image_name,
imageType: baseNode.image.image_type,
imageOrigin: baseNode.image.image_origin,
requestBody: { session_id: sessionId },
})
);
@@ -173,7 +171,7 @@ export const addUserInvokedCanvasListener = () => {
dispatch(
imageUpdated({
imageName: baseNode.mask.image_name,
imageType: baseNode.mask.image_type,
imageOrigin: baseNode.mask.image_origin,
requestBody: { session_id: sessionId },
})
);

View File

@@ -15,7 +15,7 @@
import { SelectedImage } from 'features/parameters/store/actions';
import { InvokeTabName } from 'features/ui/store/tabMap';
import { IRect } from 'konva/lib/types';
import { ImageResponseMetadata, ImageType } from 'services/api';
import { ImageResponseMetadata, ResourceOrigin } from 'services/api';
import { O } from 'ts-toolbelt';
/**
@@ -124,7 +124,7 @@ export type PostProcessedImageMetadata = ESRGANMetadata | FacetoolMetadata;
*/
// export ty`pe Image = {
// name: string;
// type: ImageType;
// type: image_origin;
// url: string;
// thumbnail: string;
// metadata: ImageResponseMetadata;