feat(ui): store only image name in parameters

Images that are used as parameters (e.g. init image, canvas images) are stored as full `ImageDTO` objects in state, separate from and duplicating any object representing those same objects in the `imagesSlice`.

We cannot store only image names as parameters, then pull the full `ImageDTO` from `imagesSlice`, because if an image is not on a loaded page, it doesn't exist in `imagesSlice`. For example, if you scroll down a few pages in the gallery and send that image to canvas, on reloading the app, the canvas will be unable to load that image.

We solved this temporarily by storing the full `ImageDTO` object wherever it was needed, but this is both inefficient and allows for stale `ImageDTO`s across the app.

One other possible solution was to just fetch the `ImageDTO` for all images at startup, and insert them into the `imagesSlice`, but then we run into an issue where we are displaying images in the gallery totally out of context.

For example, if an image from several pages into the gallery was sent to canvas, and the user refreshes, we'd display the first 20 images in gallery. Then to populate the canvas, we'd fetch that image we sent to canvas and add it to `imagesSlice`. Now we'd have 21 images in the gallery: 1 to 20 and whichever image we sent to canvas. Weird.

Using `rtk-query` solves this by allowing us to very easily fetch individual images in the components that need them, and not directly interact with `imagesSlice`.

This commit changes all references to images-as-parameters to store only the name of the image, and not the full `ImageDTO` object. Then, we use an `rtk-query` generated `useGetImageDTOQuery()` hook in each of those components to fetch the image.

We can use cache invalidation when we mutate any image to trigger automated re-running of the query and all the images are automatically kept up to date.

This also obviates the need for the convoluted URL fetching scheme for images that are used as parameters. The `imagesSlice` still need this handling unfortunately.
This commit is contained in:
psychedelicious
2023-06-21 13:48:59 +10:00
parent cfda128e06
commit 8d3bec57d5
21 changed files with 630 additions and 140 deletions

View File

@ -11,6 +11,8 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDndImage from 'common/components/IAIDndImage';
import { ImageDTO } from 'services/api';
import { IAIImageFallback } from 'common/components/IAIImageFallback';
import { useGetImageDTOQuery } from 'services/apiSlice';
import { skipToken } from '@reduxjs/toolkit/dist/query';
const selector = createSelector(
[generationSelector],
@ -27,14 +29,21 @@ const InitialImagePreview = () => {
const { initialImage } = useAppSelector(selector);
const dispatch = useAppDispatch();
const {
data: image,
isLoading,
isError,
isSuccess,
} = useGetImageDTOQuery(initialImage ?? skipToken);
const handleDrop = useCallback(
(droppedImage: ImageDTO) => {
if (droppedImage.image_name === initialImage?.image_name) {
({ image_name }: ImageDTO) => {
if (image_name === initialImage) {
return;
}
dispatch(initialImageChanged(droppedImage));
dispatch(initialImageChanged(image_name));
},
[dispatch, initialImage?.image_name]
[dispatch, initialImage]
);
const handleReset = useCallback(() => {
@ -53,7 +62,7 @@ const InitialImagePreview = () => {
}}
>
<IAIDndImage
image={initialImage}
image={image}
onDrop={handleDrop}
onReset={handleReset}
fallback={<IAIImageFallback sx={{ bg: 'none' }} />}

View File

@ -24,7 +24,7 @@ export interface GenerationState {
height: HeightParam;
img2imgStrength: StrengthParam;
infillMethod: string;
initialImage?: ImageDTO;
initialImage?: string;
iterations: number;
perlin: number;
positivePrompt: PositivePromptParam;
@ -211,7 +211,7 @@ export const generationSlice = createSlice({
setShouldUseNoiseSettings: (state, action: PayloadAction<boolean>) => {
state.shouldUseNoiseSettings = action.payload;
},
initialImageChanged: (state, action: PayloadAction<ImageDTO>) => {
initialImageChanged: (state, action: PayloadAction<string>) => {
state.initialImage = action.payload;
},
modelSelected: (state, action: PayloadAction<string>) => {
@ -233,14 +233,14 @@ export const generationSlice = createSlice({
}
});
builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
const { image_name, image_url, thumbnail_url } = action.payload;
// builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
// const { image_name, image_url, thumbnail_url } = action.payload;
if (state.initialImage?.image_name === image_name) {
state.initialImage.image_url = image_url;
state.initialImage.thumbnail_url = thumbnail_url;
}
});
// if (state.initialImage?.image_name === image_name) {
// state.initialImage.image_url = image_url;
// state.initialImage.thumbnail_url = thumbnail_url;
// }
// });
},
});