From 7a1de3887e0d4eaaa96950b3be642a125908a63b Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 22 May 2023 22:21:57 +1000 Subject: [PATCH] feat(ui): wip update UI for migration --- .../listeners/initialImageSelected.ts | 21 +++++++----- .../listeners/invocationComplete.ts | 33 ++++++++----------- .../frontend/web/src/app/types/invokeai.ts | 26 +++++++-------- .../components/ImageMetadataOverlay.tsx | 1 - .../src/features/canvas/store/canvasSlice.ts | 6 ++-- .../src/features/canvas/store/canvasTypes.ts | 3 +- .../ImageActionButtons/DeleteImageButton.tsx | 4 +-- .../components/ImageGalleryContent.tsx | 2 -- .../web/src/features/gallery/store/actions.ts | 6 ++-- .../features/gallery/store/uploadsSlice.ts | 2 -- .../lightbox/components/ReactPanZoomImage.tsx | 6 ++-- .../parameters/hooks/useParameters.ts | 18 +++++----- .../src/features/parameters/store/actions.ts | 31 +++++++++++++---- .../parameters/store/generationSlice.ts | 5 +-- .../store/setAllParametersReducer.ts | 5 ++- .../frontend/web/src/services/types/guards.ts | 1 - 16 files changed, 89 insertions(+), 81 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/initialImageSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/initialImageSelected.ts index ae3a35f537..d6cfc260f3 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/initialImageSelected.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/initialImageSelected.ts @@ -1,12 +1,15 @@ import { initialImageChanged } from 'features/parameters/store/generationSlice'; -import { Image, isInvokeAIImage } from 'app/types/invokeai'; import { selectResultsById } from 'features/gallery/store/resultsSlice'; import { selectUploadsById } from 'features/gallery/store/uploadsSlice'; import { t } from 'i18next'; import { addToast } from 'features/system/store/systemSlice'; import { startAppListening } from '..'; -import { initialImageSelected } from 'features/parameters/store/actions'; +import { + initialImageSelected, + isImageDTO, +} from 'features/parameters/store/actions'; import { makeToast } from 'app/components/Toaster'; +import { ImageDTO } from 'services/api'; export const addInitialImageSelectedListener = () => { startAppListening({ @@ -21,21 +24,21 @@ export const addInitialImageSelectedListener = () => { return; } - if (isInvokeAIImage(action.payload)) { + if (isImageDTO(action.payload)) { dispatch(initialImageChanged(action.payload)); dispatch(addToast(makeToast(t('toast.sentToImageToImage')))); return; } - const { name, type } = action.payload; + const { image_name, image_type } = action.payload; - let image: Image | undefined; + let image: ImageDTO | undefined; const state = getState(); - if (type === 'results') { - image = selectResultsById(state, name); - } else if (type === 'uploads') { - image = selectUploadsById(state, name); + if (image_type === 'results') { + image = selectResultsById(state, image_name); + } else if (image_type === 'uploads') { + image = selectUploadsById(state, image_name); } if (!image) { diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/invocationComplete.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/invocationComplete.ts index 3db8490a6e..3755b38d41 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/invocationComplete.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/invocationComplete.ts @@ -1,15 +1,10 @@ import { invocationComplete } from 'services/events/actions'; import { isImageOutput } from 'services/types/guards'; import { - buildImageUrls, - extractTimestampFromImageName, -} from 'services/util/deserializeImageField'; -import { Image } from 'app/types/invokeai'; -import { resultAdded } from 'features/gallery/store/resultsSlice'; -import { imageMetadataReceived } from 'services/thunks/image'; + imageMetadataReceived, + imageUrlsReceived, +} from 'services/thunks/image'; import { startAppListening } from '..'; -import { imageSelected } from 'features/gallery/store/gallerySlice'; -import { addImageToStagingArea } from 'features/canvas/store/canvasSlice'; const nodeDenylist = ['dataURL_image']; @@ -33,20 +28,18 @@ export const addImageResultReceivedListener = () => { const { result, node, graph_execution_state_id } = data; if (isImageOutput(result) && !nodeDenylist.includes(node.type)) { - const name = result.image.image_name; - const type = result.image.image_type; + const { image_name, image_type } = result.image; - // dispatch(imageUrlsReceived({ imageName: name, imageType: type })); + dispatch( + imageUrlsReceived({ imageName: image_name, imageType: image_type }) + ); - // const [{ payload }] = await take( - // (action): action is ReturnType => - // imageUrlsReceived.fulfilled.match(action) && - // action.payload.image_name === name - // ); - - // console.log(payload); - - dispatch(imageMetadataReceived({ imageName: name, imageType: type })); + dispatch( + imageMetadataReceived({ + imageName: image_name, + imageType: image_type, + }) + ); // const [x] = await take( // ( diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index f684dc1ccf..9cd2028984 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -122,21 +122,21 @@ export type PostProcessedImageMetadata = ESRGANMetadata | FacetoolMetadata; /** * ResultImage */ -export type Image = { - name: string; - type: ImageType; - url: string; - thumbnail: string; - metadata: ImageResponseMetadata; -}; +// export ty`pe Image = { +// name: string; +// type: ImageType; +// url: string; +// thumbnail: string; +// metadata: ImageResponseMetadata; +// }; -export const isInvokeAIImage = (obj: Image | SelectedImage): obj is Image => { - if ('url' in obj && 'thumbnail' in obj) { - return true; - } +// export const isInvokeAIImage = (obj: Image | SelectedImage): obj is Image => { +// if ('url' in obj && 'thumbnail' in obj) { +// return true; +// } - return false; -}; +// return false; +// }; /** * Types related to the system status. diff --git a/invokeai/frontend/web/src/common/components/ImageMetadataOverlay.tsx b/invokeai/frontend/web/src/common/components/ImageMetadataOverlay.tsx index b96bc2ffe2..bed0a26831 100644 --- a/invokeai/frontend/web/src/common/components/ImageMetadataOverlay.tsx +++ b/invokeai/frontend/web/src/common/components/ImageMetadataOverlay.tsx @@ -1,5 +1,4 @@ import { Badge, Flex } from '@chakra-ui/react'; -import { Image } from 'app/types/invokeai'; import { isNumber, isString } from 'lodash-es'; import { useMemo } from 'react'; import { ImageDTO } from 'services/api'; diff --git a/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts b/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts index 037d353f42..67074b8953 100644 --- a/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts +++ b/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts @@ -1,6 +1,5 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import * as InvokeAI from 'app/types/invokeai'; import { roundDownToMultiple, roundToMultiple, @@ -29,6 +28,7 @@ import { isCanvasBaseImage, isCanvasMaskLine, } from './canvasTypes'; +import { ImageDTO } from 'services/api'; export const initialLayerState: CanvasLayerState = { objects: [], @@ -157,7 +157,7 @@ export const canvasSlice = createSlice({ setCursorPosition: (state, action: PayloadAction) => { state.cursorPosition = action.payload; }, - setInitialCanvasImage: (state, action: PayloadAction) => { + setInitialCanvasImage: (state, action: PayloadAction) => { const image = action.payload; const { width, height } = image.metadata; const { stageDimensions } = state; @@ -302,7 +302,7 @@ export const canvasSlice = createSlice({ selectedImageIndex: -1, }; }, - addImageToStagingArea: (state, action: PayloadAction) => { + addImageToStagingArea: (state, action: PayloadAction) => { const image = action.payload; if (!image || !state.layerState.stagingArea.boundingBox) { diff --git a/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts b/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts index 2a6461aaf6..804e06f88f 100644 --- a/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts +++ b/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts @@ -1,6 +1,7 @@ import * as InvokeAI from 'app/types/invokeai'; import { IRect, Vector2d } from 'konva/lib/types'; import { RgbaColor } from 'react-colorful'; +import { ImageDTO } from 'services/api'; export const LAYER_NAMES_DICT = [ { key: 'Base', value: 'base' }, @@ -37,7 +38,7 @@ export type CanvasImage = { y: number; width: number; height: number; - image: InvokeAI.Image; + image: ImageDTO; }; export type CanvasMaskLine = { diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageActionButtons/DeleteImageButton.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageActionButtons/DeleteImageButton.tsx index 6e35ccd63b..4b0f6e60dd 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageActionButtons/DeleteImageButton.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageActionButtons/DeleteImageButton.tsx @@ -12,7 +12,7 @@ import { memo, useCallback } from 'react'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import DeleteImageModal from '../DeleteImageModal'; import { requestedImageDeletion } from 'features/gallery/store/actions'; -import { Image } from 'app/types/invokeai'; +import { ImageDTO } from 'services/api'; const selector = createSelector( [systemSelector], @@ -30,7 +30,7 @@ const selector = createSelector( ); type DeleteImageButtonProps = { - image: Image | undefined; + image: ImageDTO | undefined; }; const DeleteImageButton = (props: DeleteImageButtonProps) => { diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx index ce375f9580..468dfd694f 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx @@ -5,7 +5,6 @@ import { FlexProps, Grid, Icon, - Image, Text, forwardRef, } from '@chakra-ui/react'; @@ -51,7 +50,6 @@ import { uploadsAdapter } from '../store/uploadsSlice'; import { createSelector } from '@reduxjs/toolkit'; import { RootState } from 'app/store/store'; import { Virtuoso, VirtuosoGrid } from 'react-virtuoso'; -import { Image as ImageType } from 'app/types/invokeai'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import GalleryProgressImage from './GalleryProgressImage'; import { uiSelector } from 'features/ui/store/uiSelectors'; diff --git a/invokeai/frontend/web/src/features/gallery/store/actions.ts b/invokeai/frontend/web/src/features/gallery/store/actions.ts index a7454047b1..7e071f279d 100644 --- a/invokeai/frontend/web/src/features/gallery/store/actions.ts +++ b/invokeai/frontend/web/src/features/gallery/store/actions.ts @@ -1,9 +1,9 @@ import { createAction } from '@reduxjs/toolkit'; -import { Image } from 'app/types/invokeai'; -import { SelectedImage } from 'features/parameters/store/actions'; +import { ImageNameAndType } from 'features/parameters/store/actions'; +import { ImageDTO } from 'services/api'; export const requestedImageDeletion = createAction< - Image | SelectedImage | undefined + ImageDTO | ImageNameAndType | undefined >('gallery/requestedImageDeletion'); export const sentImageToCanvas = createAction('gallery/sentImageToCanvas'); diff --git a/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts b/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts index 5910cc087b..2864db2660 100644 --- a/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts @@ -1,5 +1,4 @@ import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; -import { Image } from 'app/types/invokeai'; import { RootState } from 'app/store/store'; import { @@ -7,7 +6,6 @@ import { IMAGES_PER_PAGE, } from 'services/thunks/gallery'; import { imageDeleted, imageUrlsReceived } from 'services/thunks/image'; -import { deserializeImageResponse } from 'services/util/deserializeImageResponse'; import { ImageDTO } from 'services/api'; import { dateComparator } from 'common/util/dateComparator'; diff --git a/invokeai/frontend/web/src/features/lightbox/components/ReactPanZoomImage.tsx b/invokeai/frontend/web/src/features/lightbox/components/ReactPanZoomImage.tsx index 9781625949..b1e822c309 100644 --- a/invokeai/frontend/web/src/features/lightbox/components/ReactPanZoomImage.tsx +++ b/invokeai/frontend/web/src/features/lightbox/components/ReactPanZoomImage.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; import { TransformComponent, useTransformContext } from 'react-zoom-pan-pinch'; -import * as InvokeAI from 'app/types/invokeai'; import { useGetUrl } from 'common/util/getUrl'; +import { ImageDTO } from 'services/api'; type ReactPanZoomProps = { - image: InvokeAI.Image; + image: ImageDTO; styleClass?: string; alt?: string; ref?: React.Ref; @@ -37,7 +37,7 @@ export default function ReactPanZoomImage({ transform: `rotate(${rotation}deg) scaleX(${scaleX}) scaleY(${scaleY})`, width: '100%', }} - src={getUrl(image.url)} + src={getUrl(image.image_url)} alt={alt} ref={ref} className={styleClass ? styleClass : ''} diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts index 138d54402c..ad9985b5de 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts @@ -7,9 +7,9 @@ import { allParametersSet, setSeed } from '../store/generationSlice'; import { isImageField } from 'services/types/guards'; import { NUMPY_RAND_MAX } from 'app/constants'; import { initialImageSelected } from '../store/actions'; -import { Image } from 'app/types/invokeai'; import { setActiveTab } from 'features/ui/store/uiSlice'; import { useAppToaster } from 'app/components/Toaster'; +import { ImageDTO } from 'services/api'; export const useParameters = () => { const dispatch = useAppDispatch(); @@ -88,9 +88,7 @@ export const useParameters = () => { return; } - dispatch( - initialImageSelected({ name: image.image_name, type: image.image_type }) - ); + dispatch(initialImageSelected(image)); toaster({ title: t('toast.initialImageSet'), status: 'info', @@ -105,21 +103,21 @@ export const useParameters = () => { * Sets image as initial image with toast */ const sendToImageToImage = useCallback( - (image: Image) => { - dispatch(initialImageSelected({ name: image.name, type: image.type })); + (image: ImageDTO) => { + dispatch(initialImageSelected(image)); }, [dispatch] ); const recallAllParameters = useCallback( - (image: Image | undefined) => { - const type = image?.metadata?.invokeai?.node?.type; + (image: ImageDTO | undefined) => { + const type = image?.metadata?.type; if (['txt2img', 'img2img', 'inpaint'].includes(String(type))) { dispatch(allParametersSet(image)); - if (image?.metadata?.invokeai?.node?.type === 'img2img') { + if (image?.metadata?.type === 'img2img') { dispatch(setActiveTab('img2img')); - } else if (image?.metadata?.invokeai?.node?.type === 'txt2img') { + } else if (image?.metadata?.type === 'txt2img') { dispatch(setActiveTab('txt2img')); } diff --git a/invokeai/frontend/web/src/features/parameters/store/actions.ts b/invokeai/frontend/web/src/features/parameters/store/actions.ts index 4b261d7783..853597c809 100644 --- a/invokeai/frontend/web/src/features/parameters/store/actions.ts +++ b/invokeai/frontend/web/src/features/parameters/store/actions.ts @@ -1,12 +1,31 @@ import { createAction } from '@reduxjs/toolkit'; -import { Image } from 'app/types/invokeai'; -import { ImageType } from 'services/api'; +import { isObject } from 'lodash-es'; +import { ImageDTO, ImageType } from 'services/api'; -export type SelectedImage = { - name: string; - type: ImageType; +export type ImageNameAndType = { + image_name: string; + image_type: ImageType; +}; + +export const isImageDTO = (image: any): image is ImageDTO => { + return ( + image && + isObject(image) && + 'image_name' in image && + image?.image_name !== undefined && + 'image_type' in image && + image?.image_type !== undefined && + 'image_url' in image && + image?.image_url !== undefined && + 'thumbnail_url' in image && + image?.thumbnail_url !== undefined && + 'image_category' in image && + image?.image_category !== undefined && + 'created_at' in image && + image?.created_at !== undefined + ); }; export const initialImageSelected = createAction< - Image | SelectedImage | undefined + ImageDTO | ImageNameAndType | undefined >('generation/initialImageSelected'); diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index f9e857e7e3..b471ffc783 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -6,13 +6,14 @@ import { clamp, sample } from 'lodash-es'; import { setAllParametersReducer } from './setAllParametersReducer'; import { receivedModels } from 'services/thunks/model'; import { Scheduler } from 'app/constants'; +import { ImageDTO } from 'services/api'; export interface GenerationState { cfgScale: number; height: number; img2imgStrength: number; infillMethod: string; - initialImage?: InvokeAI.Image; + initialImage?: ImageDTO; iterations: number; perlin: number; prompt: string; @@ -213,7 +214,7 @@ export const generationSlice = createSlice({ setShouldUseNoiseSettings: (state, action: PayloadAction) => { state.shouldUseNoiseSettings = action.payload; }, - initialImageChanged: (state, action: PayloadAction) => { + initialImageChanged: (state, action: PayloadAction) => { state.initialImage = action.payload; }, modelSelected: (state, action: PayloadAction) => { diff --git a/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts b/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts index a816d358ce..dc147090b4 100644 --- a/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts +++ b/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts @@ -1,12 +1,11 @@ import { Draft, PayloadAction } from '@reduxjs/toolkit'; -import { Image } from 'app/types/invokeai'; import { GenerationState } from './generationSlice'; -import { ImageToImageInvocation } from 'services/api'; +import { ImageDTO, ImageToImageInvocation } from 'services/api'; import { isScheduler } from 'app/constants'; export const setAllParametersReducer = ( state: Draft, - action: PayloadAction + action: PayloadAction ) => { const node = action.payload?.metadata.invokeai?.node; diff --git a/invokeai/frontend/web/src/services/types/guards.ts b/invokeai/frontend/web/src/services/types/guards.ts index 5065290220..f7eba6e9d6 100644 --- a/invokeai/frontend/web/src/services/types/guards.ts +++ b/invokeai/frontend/web/src/services/types/guards.ts @@ -1,4 +1,3 @@ -import { Image } from 'app/types/invokeai'; import { get, isObject, isString } from 'lodash-es'; import { GraphExecutionState,