mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
169 lines
5.2 KiB
TypeScript
169 lines
5.2 KiB
TypeScript
import { createSlice } from '@reduxjs/toolkit';
|
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
|
import _, { clamp } from 'lodash';
|
|
import * as InvokeAI from '../../app/invokeai';
|
|
|
|
export interface GalleryState {
|
|
currentImage?: InvokeAI.Image;
|
|
currentImageUuid: string;
|
|
images: Array<InvokeAI.Image>;
|
|
intermediateImage?: InvokeAI.Image;
|
|
areMoreImagesAvailable: boolean;
|
|
latest_mtime?: number;
|
|
earliest_mtime?: number;
|
|
}
|
|
|
|
const initialState: GalleryState = {
|
|
currentImageUuid: '',
|
|
images: [],
|
|
areMoreImagesAvailable: true,
|
|
};
|
|
|
|
export const gallerySlice = createSlice({
|
|
name: 'gallery',
|
|
initialState,
|
|
reducers: {
|
|
setCurrentImage: (state, action: PayloadAction<InvokeAI.Image>) => {
|
|
state.currentImage = action.payload;
|
|
state.currentImageUuid = action.payload.uuid;
|
|
},
|
|
removeImage: (state, action: PayloadAction<string>) => {
|
|
const uuid = action.payload;
|
|
|
|
const newImages = state.images.filter((image) => image.uuid !== uuid);
|
|
|
|
if (uuid === state.currentImageUuid) {
|
|
/**
|
|
* We are deleting the currently selected image.
|
|
*
|
|
* We want the new currentl selected image to be under the cursor in the
|
|
* gallery, so we need to do some fanagling. The currently selected image
|
|
* is set by its UUID, not its index in the image list.
|
|
*
|
|
* Get the currently selected image's index.
|
|
*/
|
|
const imageToDeleteIndex = state.images.findIndex(
|
|
(image) => image.uuid === uuid
|
|
);
|
|
|
|
/**
|
|
* New current image needs to be in the same spot, but because the gallery
|
|
* is sorted in reverse order, the new current image's index will actuall be
|
|
* one less than the deleted image's index.
|
|
*
|
|
* Clamp the new index to ensure it is valid..
|
|
*/
|
|
const newCurrentImageIndex = clamp(
|
|
imageToDeleteIndex,
|
|
0,
|
|
newImages.length - 1
|
|
);
|
|
|
|
state.currentImage = newImages.length
|
|
? newImages[newCurrentImageIndex]
|
|
: undefined;
|
|
|
|
state.currentImageUuid = newImages.length
|
|
? newImages[newCurrentImageIndex].uuid
|
|
: '';
|
|
}
|
|
|
|
state.images = newImages;
|
|
},
|
|
addImage: (state, action: PayloadAction<InvokeAI.Image>) => {
|
|
const newImage = action.payload;
|
|
const { uuid, url, mtime } = newImage;
|
|
|
|
// Do not add duplicate images
|
|
if (state.images.find((i) => i.url === url && i.mtime === mtime)) {
|
|
return;
|
|
}
|
|
|
|
state.images.unshift(newImage);
|
|
state.currentImageUuid = uuid;
|
|
state.intermediateImage = undefined;
|
|
state.currentImage = newImage;
|
|
state.latest_mtime = mtime;
|
|
},
|
|
setIntermediateImage: (state, action: PayloadAction<InvokeAI.Image>) => {
|
|
state.intermediateImage = action.payload;
|
|
},
|
|
clearIntermediateImage: (state) => {
|
|
state.intermediateImage = undefined;
|
|
},
|
|
selectNextImage: (state) => {
|
|
const { images, currentImage } = state;
|
|
if (currentImage) {
|
|
const currentImageIndex = images.findIndex(
|
|
(i) => i.uuid === currentImage.uuid
|
|
);
|
|
if (_.inRange(currentImageIndex, 0, images.length)) {
|
|
const newCurrentImage = images[currentImageIndex + 1];
|
|
state.currentImage = newCurrentImage;
|
|
state.currentImageUuid = newCurrentImage.uuid;
|
|
}
|
|
}
|
|
},
|
|
selectPrevImage: (state) => {
|
|
const { images, currentImage } = state;
|
|
if (currentImage) {
|
|
const currentImageIndex = images.findIndex(
|
|
(i) => i.uuid === currentImage.uuid
|
|
);
|
|
if (_.inRange(currentImageIndex, 1, images.length + 1)) {
|
|
const newCurrentImage = images[currentImageIndex - 1];
|
|
state.currentImage = newCurrentImage;
|
|
state.currentImageUuid = newCurrentImage.uuid;
|
|
}
|
|
}
|
|
},
|
|
addGalleryImages: (
|
|
state,
|
|
action: PayloadAction<{
|
|
images: Array<InvokeAI.Image>;
|
|
areMoreImagesAvailable: boolean;
|
|
}>
|
|
) => {
|
|
const { images, areMoreImagesAvailable } = action.payload;
|
|
if (images.length > 0) {
|
|
// Filter images that already exist in the gallery
|
|
const newImages = images.filter(
|
|
(newImage) =>
|
|
!state.images.find(
|
|
(i) => i.url === newImage.url && i.mtime === newImage.mtime
|
|
)
|
|
);
|
|
state.images = state.images
|
|
.concat(newImages)
|
|
.sort((a, b) => b.mtime - a.mtime);
|
|
|
|
if (!state.currentImage) {
|
|
const newCurrentImage = images[0];
|
|
state.currentImage = newCurrentImage;
|
|
state.currentImageUuid = newCurrentImage.uuid;
|
|
}
|
|
|
|
// keep track of the timestamps of latest and earliest images received
|
|
state.latest_mtime = images[0].mtime;
|
|
state.earliest_mtime = images[images.length - 1].mtime;
|
|
}
|
|
if (areMoreImagesAvailable !== undefined) {
|
|
state.areMoreImagesAvailable = areMoreImagesAvailable;
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
export const {
|
|
addImage,
|
|
clearIntermediateImage,
|
|
removeImage,
|
|
setCurrentImage,
|
|
addGalleryImages,
|
|
setIntermediateImage,
|
|
selectNextImage,
|
|
selectPrevImage,
|
|
} = gallerySlice.actions;
|
|
|
|
export default gallerySlice.reducer;
|