From 258895bcc9e9a932135eee763cecb09c3688a35e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sun, 30 Apr 2023 00:57:00 +1000 Subject: [PATCH] feat(ui): being dismantling old sio stuff, fix recall seed/prompt/init - still need to fix up metadataviewer's recall features --- .../web/src/app/components/InvokeAIUI.tsx | 5 +- invokeai/frontend/web/src/app/constants.ts | 13 +- .../frontend/web/src/app/socketio/actions.ts | 110 +- .../frontend/web/src/app/socketio/emitters.ts | 382 +++---- .../web/src/app/socketio/listeners.ts | 938 +++++++++--------- .../web/src/app/socketio/middleware.ts | 402 ++++---- invokeai/frontend/web/src/app/store/store.ts | 15 +- .../frontend/web/src/app/types/invokeai.ts | 56 +- .../IAICanvasStagingAreaToolbar.tsx | 2 +- .../components/CurrentImageButtons.tsx | 120 +-- .../gallery/components/HoverableImage.tsx | 169 ++-- .../components/ImageGalleryContent.tsx | 2 +- .../ProcessButtons/InvokeButton.tsx | 19 +- .../components/PromptInput/PromptInput.tsx | 8 +- .../parameters/hooks/useParameters.ts | 130 +++ .../components/ClearTempFolderButtonModal.tsx | 2 +- .../ModelManager/AddCheckpointModel.tsx | 2 +- .../ModelManager/AddDiffusersModel.tsx | 2 +- .../ModelManager/CheckpointModelEdit.tsx | 2 +- .../ModelManager/DiffusersModelEdit.tsx | 2 +- .../components/ModelManager/MergeModels.tsx | 2 +- .../components/ModelManager/ModelConvert.tsx | 2 +- .../components/ModelManager/ModelListItem.tsx | 2 +- .../components/ModelManager/SearchModels.tsx | 2 +- .../SettingsModal/SettingsModal.tsx | 58 +- .../system/components/StatusIndicator.tsx | 48 +- .../src/features/system/store/systemSlice.ts | 108 +- .../frontend/web/src/services/types/guards.ts | 17 + 28 files changed, 1231 insertions(+), 1389 deletions(-) create mode 100644 invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts diff --git a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx index a66f2ef2a3..97a8be6fc1 100644 --- a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx +++ b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx @@ -1,7 +1,7 @@ import React, { lazy, memo, PropsWithChildren, useEffect } from 'react'; import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; -import { buildMiddleware, store } from 'app/store/store'; +import { store } from 'app/store/store'; import { persistor } from '../store/persistor'; import { OpenAPI } from 'services/api'; import '@fontsource/inter/100.css'; @@ -19,6 +19,7 @@ import { addMiddleware, resetMiddlewares } from 'redux-dynamic-middlewares'; import { PartialAppConfig } from 'app/types/invokeai'; import '../../i18n'; +import { socketMiddleware } from 'services/events/middleware'; const App = lazy(() => import('./App')); const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider')); @@ -50,7 +51,7 @@ const InvokeAIUI = ({ apiUrl, token, config, children }: Props) => { // the `apiUrl`/`token` dynamically. // rebuild socket middleware with token and apiUrl - addMiddleware(buildMiddleware()); + addMiddleware(socketMiddleware()); }, [apiUrl, token]); return ( diff --git a/invokeai/frontend/web/src/app/constants.ts b/invokeai/frontend/web/src/app/constants.ts index 083f57f26f..534ca9e29a 100644 --- a/invokeai/frontend/web/src/app/constants.ts +++ b/invokeai/frontend/web/src/app/constants.ts @@ -1,7 +1,5 @@ // TODO: use Enums? -import { InProgressImageType } from 'features/system/store/systemSlice'; - export const DIFFUSERS_SCHEDULERS: Array = [ 'ddim', 'plms', @@ -33,17 +31,8 @@ export const UPSCALING_LEVELS: Array<{ key: string; value: number }> = [ export const NUMPY_RAND_MIN = 0; -export const NUMPY_RAND_MAX = 4294967295; +export const NUMPY_RAND_MAX = 2147483647; export const FACETOOL_TYPES = ['gfpgan', 'codeformer'] as const; -export const IN_PROGRESS_IMAGE_TYPES: Array<{ - key: string; - value: InProgressImageType; -}> = [ - { key: 'None', value: 'none' }, - { key: 'Fast', value: 'latents' }, - { key: 'Accurate', value: 'full-res' }, -]; - export const NODE_MIN_WIDTH = 250; diff --git a/invokeai/frontend/web/src/app/socketio/actions.ts b/invokeai/frontend/web/src/app/socketio/actions.ts index 923c1f59b0..bb2a0dd0cb 100644 --- a/invokeai/frontend/web/src/app/socketio/actions.ts +++ b/invokeai/frontend/web/src/app/socketio/actions.ts @@ -1,65 +1,67 @@ -import { createAction } from '@reduxjs/toolkit'; -import * as InvokeAI from 'app/types/invokeai'; -import { GalleryCategory } from 'features/gallery/store/gallerySlice'; -import { InvokeTabName } from 'features/ui/store/tabMap'; +// import { createAction } from '@reduxjs/toolkit'; +// import * as InvokeAI from 'app/types/invokeai'; +// import { GalleryCategory } from 'features/gallery/store/gallerySlice'; +// import { InvokeTabName } from 'features/ui/store/tabMap'; -/** - * We can't use redux-toolkit's createSlice() to make these actions, - * because they have no associated reducer. They only exist to dispatch - * requests to the server via socketio. These actions will be handled - * by the middleware. - */ +// /** +// * We can't use redux-toolkit's createSlice() to make these actions, +// * because they have no associated reducer. They only exist to dispatch +// * requests to the server via socketio. These actions will be handled +// * by the middleware. +// */ -export const generateImage = createAction( - 'socketio/generateImage' -); -export const runESRGAN = createAction('socketio/runESRGAN'); -export const runFacetool = createAction( - 'socketio/runFacetool' -); -export const deleteImage = createAction( - 'socketio/deleteImage' -); -export const requestImages = createAction( - 'socketio/requestImages' -); -export const requestNewImages = createAction( - 'socketio/requestNewImages' -); -export const cancelProcessing = createAction( - 'socketio/cancelProcessing' -); +// export const generateImage = createAction( +// 'socketio/generateImage' +// ); +// export const runESRGAN = createAction('socketio/runESRGAN'); +// export const runFacetool = createAction( +// 'socketio/runFacetool' +// ); +// export const deleteImage = createAction( +// 'socketio/deleteImage' +// ); +// export const requestImages = createAction( +// 'socketio/requestImages' +// ); +// export const requestNewImages = createAction( +// 'socketio/requestNewImages' +// ); +// export const cancelProcessing = createAction( +// 'socketio/cancelProcessing' +// ); -export const requestSystemConfig = createAction( - 'socketio/requestSystemConfig' -); +// export const requestSystemConfig = createAction( +// 'socketio/requestSystemConfig' +// ); -export const searchForModels = createAction('socketio/searchForModels'); +// export const searchForModels = createAction('socketio/searchForModels'); -export const addNewModel = createAction< - InvokeAI.InvokeModelConfigProps | InvokeAI.InvokeDiffusersModelConfigProps ->('socketio/addNewModel'); +// export const addNewModel = createAction< +// InvokeAI.InvokeModelConfigProps | InvokeAI.InvokeDiffusersModelConfigProps +// >('socketio/addNewModel'); -export const deleteModel = createAction('socketio/deleteModel'); +// export const deleteModel = createAction('socketio/deleteModel'); -export const convertToDiffusers = - createAction( - 'socketio/convertToDiffusers' - ); +// export const convertToDiffusers = +// createAction( +// 'socketio/convertToDiffusers' +// ); -export const mergeDiffusersModels = - createAction( - 'socketio/mergeDiffusersModels' - ); +// export const mergeDiffusersModels = +// createAction( +// 'socketio/mergeDiffusersModels' +// ); -export const requestModelChange = createAction( - 'socketio/requestModelChange' -); +// export const requestModelChange = createAction( +// 'socketio/requestModelChange' +// ); -export const saveStagingAreaImageToGallery = createAction( - 'socketio/saveStagingAreaImageToGallery' -); +// export const saveStagingAreaImageToGallery = createAction( +// 'socketio/saveStagingAreaImageToGallery' +// ); -export const emptyTempFolder = createAction( - 'socketio/requestEmptyTempFolder' -); +// export const emptyTempFolder = createAction( +// 'socketio/requestEmptyTempFolder' +// ); + +export default {}; diff --git a/invokeai/frontend/web/src/app/socketio/emitters.ts b/invokeai/frontend/web/src/app/socketio/emitters.ts index 610f05b826..ad7979503f 100644 --- a/invokeai/frontend/web/src/app/socketio/emitters.ts +++ b/invokeai/frontend/web/src/app/socketio/emitters.ts @@ -1,207 +1,209 @@ -import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit'; -import * as InvokeAI from 'app/types/invokeai'; -import type { RootState } from 'app/store/store'; -import { - frontendToBackendParameters, - FrontendToBackendParametersConfig, -} from 'common/util/parameterTranslation'; -import dateFormat from 'dateformat'; -import { - GalleryCategory, - GalleryState, - removeImage, -} from 'features/gallery/store/gallerySlice'; -import { - generationRequested, - modelChangeRequested, - modelConvertRequested, - modelMergingRequested, - setIsProcessing, -} from 'features/system/store/systemSlice'; -import { InvokeTabName } from 'features/ui/store/tabMap'; -import { Socket } from 'socket.io-client'; +// import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit'; +// import * as InvokeAI from 'app/types/invokeai'; +// import type { RootState } from 'app/store/store'; +// import { +// frontendToBackendParameters, +// FrontendToBackendParametersConfig, +// } from 'common/util/parameterTranslation'; +// import dateFormat from 'dateformat'; +// import { +// GalleryCategory, +// GalleryState, +// removeImage, +// } from 'features/gallery/store/gallerySlice'; +// import { +// generationRequested, +// modelChangeRequested, +// modelConvertRequested, +// modelMergingRequested, +// setIsProcessing, +// } from 'features/system/store/systemSlice'; +// import { InvokeTabName } from 'features/ui/store/tabMap'; +// import { Socket } from 'socket.io-client'; -/** - * Returns an object containing all functions which use `socketio.emit()`. - * i.e. those which make server requests. - */ -const makeSocketIOEmitters = ( - store: MiddlewareAPI, RootState>, - socketio: Socket -) => { - // We need to dispatch actions to redux and get pieces of state from the store. - const { dispatch, getState } = store; +// /** +// * Returns an object containing all functions which use `socketio.emit()`. +// * i.e. those which make server requests. +// */ +// const makeSocketIOEmitters = ( +// store: MiddlewareAPI, RootState>, +// socketio: Socket +// ) => { +// // We need to dispatch actions to redux and get pieces of state from the store. +// const { dispatch, getState } = store; - return { - emitGenerateImage: (generationMode: InvokeTabName) => { - dispatch(setIsProcessing(true)); +// return { +// emitGenerateImage: (generationMode: InvokeTabName) => { +// dispatch(setIsProcessing(true)); - const state: RootState = getState(); +// const state: RootState = getState(); - const { - generation: generationState, - postprocessing: postprocessingState, - system: systemState, - canvas: canvasState, - } = state; +// const { +// generation: generationState, +// postprocessing: postprocessingState, +// system: systemState, +// canvas: canvasState, +// } = state; - const frontendToBackendParametersConfig: FrontendToBackendParametersConfig = - { - generationMode, - generationState, - postprocessingState, - canvasState, - systemState, - }; +// const frontendToBackendParametersConfig: FrontendToBackendParametersConfig = +// { +// generationMode, +// generationState, +// postprocessingState, +// canvasState, +// systemState, +// }; - dispatch(generationRequested()); +// dispatch(generationRequested()); - const { generationParameters, esrganParameters, facetoolParameters } = - frontendToBackendParameters(frontendToBackendParametersConfig); +// const { generationParameters, esrganParameters, facetoolParameters } = +// frontendToBackendParameters(frontendToBackendParametersConfig); - socketio.emit( - 'generateImage', - generationParameters, - esrganParameters, - facetoolParameters - ); +// socketio.emit( +// 'generateImage', +// generationParameters, +// esrganParameters, +// facetoolParameters +// ); - // we need to truncate the init_mask base64 else it takes up the whole log - // TODO: handle maintaining masks for reproducibility in future - if (generationParameters.init_mask) { - generationParameters.init_mask = generationParameters.init_mask - .substr(0, 64) - .concat('...'); - } - if (generationParameters.init_img) { - generationParameters.init_img = generationParameters.init_img - .substr(0, 64) - .concat('...'); - } +// // we need to truncate the init_mask base64 else it takes up the whole log +// // TODO: handle maintaining masks for reproducibility in future +// if (generationParameters.init_mask) { +// generationParameters.init_mask = generationParameters.init_mask +// .substr(0, 64) +// .concat('...'); +// } +// if (generationParameters.init_img) { +// generationParameters.init_img = generationParameters.init_img +// .substr(0, 64) +// .concat('...'); +// } - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Image generation requested: ${JSON.stringify({ - ...generationParameters, - ...esrganParameters, - ...facetoolParameters, - })}`, - }) - ); - }, - emitRunESRGAN: (imageToProcess: InvokeAI._Image) => { - dispatch(setIsProcessing(true)); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Image generation requested: ${JSON.stringify({ +// ...generationParameters, +// ...esrganParameters, +// ...facetoolParameters, +// })}`, +// }) +// ); +// }, +// emitRunESRGAN: (imageToProcess: InvokeAI._Image) => { +// dispatch(setIsProcessing(true)); - const { - postprocessing: { - upscalingLevel, - upscalingDenoising, - upscalingStrength, - }, - } = getState(); +// const { +// postprocessing: { +// upscalingLevel, +// upscalingDenoising, +// upscalingStrength, +// }, +// } = getState(); - const esrganParameters = { - upscale: [upscalingLevel, upscalingDenoising, upscalingStrength], - }; - socketio.emit('runPostprocessing', imageToProcess, { - type: 'esrgan', - ...esrganParameters, - }); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `ESRGAN upscale requested: ${JSON.stringify({ - file: imageToProcess.url, - ...esrganParameters, - })}`, - }) - ); - }, - emitRunFacetool: (imageToProcess: InvokeAI._Image) => { - dispatch(setIsProcessing(true)); +// const esrganParameters = { +// upscale: [upscalingLevel, upscalingDenoising, upscalingStrength], +// }; +// socketio.emit('runPostprocessing', imageToProcess, { +// type: 'esrgan', +// ...esrganParameters, +// }); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `ESRGAN upscale requested: ${JSON.stringify({ +// file: imageToProcess.url, +// ...esrganParameters, +// })}`, +// }) +// ); +// }, +// emitRunFacetool: (imageToProcess: InvokeAI._Image) => { +// dispatch(setIsProcessing(true)); - const { - postprocessing: { facetoolType, facetoolStrength, codeformerFidelity }, - } = getState(); +// const { +// postprocessing: { facetoolType, facetoolStrength, codeformerFidelity }, +// } = getState(); - const facetoolParameters: Record = { - facetool_strength: facetoolStrength, - }; +// const facetoolParameters: Record = { +// facetool_strength: facetoolStrength, +// }; - if (facetoolType === 'codeformer') { - facetoolParameters.codeformer_fidelity = codeformerFidelity; - } +// if (facetoolType === 'codeformer') { +// facetoolParameters.codeformer_fidelity = codeformerFidelity; +// } - socketio.emit('runPostprocessing', imageToProcess, { - type: facetoolType, - ...facetoolParameters, - }); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Face restoration (${facetoolType}) requested: ${JSON.stringify( - { - file: imageToProcess.url, - ...facetoolParameters, - } - )}`, - }) - ); - }, - emitDeleteImage: (imageToDelete: InvokeAI._Image) => { - const { url, uuid, category, thumbnail } = imageToDelete; - dispatch(removeImage(imageToDelete)); - socketio.emit('deleteImage', url, thumbnail, uuid, category); - }, - emitRequestImages: (category: GalleryCategory) => { - const gallery: GalleryState = getState().gallery; - const { earliest_mtime } = gallery.categories[category]; - socketio.emit('requestImages', category, earliest_mtime); - }, - emitRequestNewImages: (category: GalleryCategory) => { - const gallery: GalleryState = getState().gallery; - const { latest_mtime } = gallery.categories[category]; - socketio.emit('requestLatestImages', category, latest_mtime); - }, - emitCancelProcessing: () => { - socketio.emit('cancel'); - }, - emitRequestSystemConfig: () => { - socketio.emit('requestSystemConfig'); - }, - emitSearchForModels: (modelFolder: string) => { - socketio.emit('searchForModels', modelFolder); - }, - emitAddNewModel: (modelConfig: InvokeAI.InvokeModelConfigProps) => { - socketio.emit('addNewModel', modelConfig); - }, - emitDeleteModel: (modelName: string) => { - socketio.emit('deleteModel', modelName); - }, - emitConvertToDiffusers: ( - modelToConvert: InvokeAI.InvokeModelConversionProps - ) => { - dispatch(modelConvertRequested()); - socketio.emit('convertToDiffusers', modelToConvert); - }, - emitMergeDiffusersModels: ( - modelMergeInfo: InvokeAI.InvokeModelMergingProps - ) => { - dispatch(modelMergingRequested()); - socketio.emit('mergeDiffusersModels', modelMergeInfo); - }, - emitRequestModelChange: (modelName: string) => { - dispatch(modelChangeRequested()); - socketio.emit('requestModelChange', modelName); - }, - emitSaveStagingAreaImageToGallery: (url: string) => { - socketio.emit('requestSaveStagingAreaImageToGallery', url); - }, - emitRequestEmptyTempFolder: () => { - socketio.emit('requestEmptyTempFolder'); - }, - }; -}; +// socketio.emit('runPostprocessing', imageToProcess, { +// type: facetoolType, +// ...facetoolParameters, +// }); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Face restoration (${facetoolType}) requested: ${JSON.stringify( +// { +// file: imageToProcess.url, +// ...facetoolParameters, +// } +// )}`, +// }) +// ); +// }, +// emitDeleteImage: (imageToDelete: InvokeAI._Image) => { +// const { url, uuid, category, thumbnail } = imageToDelete; +// dispatch(removeImage(imageToDelete)); +// socketio.emit('deleteImage', url, thumbnail, uuid, category); +// }, +// emitRequestImages: (category: GalleryCategory) => { +// const gallery: GalleryState = getState().gallery; +// const { earliest_mtime } = gallery.categories[category]; +// socketio.emit('requestImages', category, earliest_mtime); +// }, +// emitRequestNewImages: (category: GalleryCategory) => { +// const gallery: GalleryState = getState().gallery; +// const { latest_mtime } = gallery.categories[category]; +// socketio.emit('requestLatestImages', category, latest_mtime); +// }, +// emitCancelProcessing: () => { +// socketio.emit('cancel'); +// }, +// emitRequestSystemConfig: () => { +// socketio.emit('requestSystemConfig'); +// }, +// emitSearchForModels: (modelFolder: string) => { +// socketio.emit('searchForModels', modelFolder); +// }, +// emitAddNewModel: (modelConfig: InvokeAI.InvokeModelConfigProps) => { +// socketio.emit('addNewModel', modelConfig); +// }, +// emitDeleteModel: (modelName: string) => { +// socketio.emit('deleteModel', modelName); +// }, +// emitConvertToDiffusers: ( +// modelToConvert: InvokeAI.InvokeModelConversionProps +// ) => { +// dispatch(modelConvertRequested()); +// socketio.emit('convertToDiffusers', modelToConvert); +// }, +// emitMergeDiffusersModels: ( +// modelMergeInfo: InvokeAI.InvokeModelMergingProps +// ) => { +// dispatch(modelMergingRequested()); +// socketio.emit('mergeDiffusersModels', modelMergeInfo); +// }, +// emitRequestModelChange: (modelName: string) => { +// dispatch(modelChangeRequested()); +// socketio.emit('requestModelChange', modelName); +// }, +// emitSaveStagingAreaImageToGallery: (url: string) => { +// socketio.emit('requestSaveStagingAreaImageToGallery', url); +// }, +// emitRequestEmptyTempFolder: () => { +// socketio.emit('requestEmptyTempFolder'); +// }, +// }; +// }; -export default makeSocketIOEmitters; +// export default makeSocketIOEmitters; + +export default {}; diff --git a/invokeai/frontend/web/src/app/socketio/listeners.ts b/invokeai/frontend/web/src/app/socketio/listeners.ts index de2f86fd4c..cb6db260fc 100644 --- a/invokeai/frontend/web/src/app/socketio/listeners.ts +++ b/invokeai/frontend/web/src/app/socketio/listeners.ts @@ -1,500 +1,502 @@ -import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit'; -import dateFormat from 'dateformat'; -import i18n from 'i18n'; -import { v4 as uuidv4 } from 'uuid'; +// import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit'; +// import dateFormat from 'dateformat'; +// import i18n from 'i18n'; +// import { v4 as uuidv4 } from 'uuid'; -import * as InvokeAI from 'app/types/invokeai'; +// import * as InvokeAI from 'app/types/invokeai'; -import { - addToast, - errorOccurred, - processingCanceled, - setCurrentStatus, - setFoundModels, - setIsCancelable, - setIsConnected, - setIsProcessing, - setModelList, - setSearchFolder, - setSystemConfig, - setSystemStatus, -} from 'features/system/store/systemSlice'; +// import { +// addToast, +// errorOccurred, +// processingCanceled, +// setCurrentStatus, +// setFoundModels, +// setIsCancelable, +// setIsConnected, +// setIsProcessing, +// setModelList, +// setSearchFolder, +// setSystemConfig, +// setSystemStatus, +// } from 'features/system/store/systemSlice'; -import { - addGalleryImages, - addImage, - clearIntermediateImage, - GalleryState, - removeImage, - setIntermediateImage, -} from 'features/gallery/store/gallerySlice'; +// import { +// addGalleryImages, +// addImage, +// clearIntermediateImage, +// GalleryState, +// removeImage, +// setIntermediateImage, +// } from 'features/gallery/store/gallerySlice'; -import type { RootState } from 'app/store/store'; -import { addImageToStagingArea } from 'features/canvas/store/canvasSlice'; -import { - clearInitialImage, - initialImageSelected, - setInfillMethod, - // setInitialImage, - setMaskPath, -} from 'features/parameters/store/generationSlice'; -import { tabMap } from 'features/ui/store/tabMap'; -import { - requestImages, - requestNewImages, - requestSystemConfig, -} from './actions'; +// import type { RootState } from 'app/store/store'; +// import { addImageToStagingArea } from 'features/canvas/store/canvasSlice'; +// import { +// clearInitialImage, +// initialImageSelected, +// setInfillMethod, +// // setInitialImage, +// setMaskPath, +// } from 'features/parameters/store/generationSlice'; +// import { tabMap } from 'features/ui/store/tabMap'; +// import { +// requestImages, +// requestNewImages, +// requestSystemConfig, +// } from './actions'; -/** - * Returns an object containing listener callbacks for socketio events. - * TODO: This file is large, but simple. Should it be split up further? - */ -const makeSocketIOListeners = ( - store: MiddlewareAPI, RootState> -) => { - const { dispatch, getState } = store; +// /** +// * Returns an object containing listener callbacks for socketio events. +// * TODO: This file is large, but simple. Should it be split up further? +// */ +// const makeSocketIOListeners = ( +// store: MiddlewareAPI, RootState> +// ) => { +// const { dispatch, getState } = store; - return { - /** - * Callback to run when we receive a 'connect' event. - */ - onConnect: () => { - try { - dispatch(setIsConnected(true)); - dispatch(setCurrentStatus(i18n.t('common.statusConnected'))); - dispatch(requestSystemConfig()); - const gallery: GalleryState = getState().gallery; +// return { +// /** +// * Callback to run when we receive a 'connect' event. +// */ +// onConnect: () => { +// try { +// dispatch(setIsConnected(true)); +// dispatch(setCurrentStatus(i18n.t('common.statusConnected'))); +// dispatch(requestSystemConfig()); +// const gallery: GalleryState = getState().gallery; - if (gallery.categories.result.latest_mtime) { - dispatch(requestNewImages('result')); - } else { - dispatch(requestImages('result')); - } +// if (gallery.categories.result.latest_mtime) { +// dispatch(requestNewImages('result')); +// } else { +// dispatch(requestImages('result')); +// } - if (gallery.categories.user.latest_mtime) { - dispatch(requestNewImages('user')); - } else { - dispatch(requestImages('user')); - } - } catch (e) { - console.error(e); - } - }, - /** - * Callback to run when we receive a 'disconnect' event. - */ - onDisconnect: () => { - try { - dispatch(setIsConnected(false)); - dispatch(setCurrentStatus(i18n.t('common.statusDisconnected'))); +// if (gallery.categories.user.latest_mtime) { +// dispatch(requestNewImages('user')); +// } else { +// dispatch(requestImages('user')); +// } +// } catch (e) { +// console.error(e); +// } +// }, +// /** +// * Callback to run when we receive a 'disconnect' event. +// */ +// onDisconnect: () => { +// try { +// dispatch(setIsConnected(false)); +// dispatch(setCurrentStatus(i18n.t('common.statusDisconnected'))); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Disconnected from server`, - level: 'warning', - }) - ); - } catch (e) { - console.error(e); - } - }, - /** - * Callback to run when we receive a 'generationResult' event. - */ - onGenerationResult: (data: InvokeAI.ImageResultResponse) => { - try { - const state = getState(); - const { activeTab } = state.ui; - const { shouldLoopback } = state.postprocessing; - const { boundingBox: _, generationMode, ...rest } = data; +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Disconnected from server`, +// level: 'warning', +// }) +// ); +// } catch (e) { +// console.error(e); +// } +// }, +// /** +// * Callback to run when we receive a 'generationResult' event. +// */ +// onGenerationResult: (data: InvokeAI.ImageResultResponse) => { +// try { +// const state = getState(); +// const { activeTab } = state.ui; +// const { shouldLoopback } = state.postprocessing; +// const { boundingBox: _, generationMode, ...rest } = data; - const newImage = { - uuid: uuidv4(), - ...rest, - }; +// const newImage = { +// uuid: uuidv4(), +// ...rest, +// }; - if (['txt2img', 'img2img'].includes(generationMode)) { - dispatch( - addImage({ - category: 'result', - image: { ...newImage, category: 'result' }, - }) - ); - } +// if (['txt2img', 'img2img'].includes(generationMode)) { +// dispatch( +// addImage({ +// category: 'result', +// image: { ...newImage, category: 'result' }, +// }) +// ); +// } - if (generationMode === 'unifiedCanvas' && data.boundingBox) { - const { boundingBox } = data; - dispatch( - addImageToStagingArea({ - image: { ...newImage, category: 'temp' }, - boundingBox, - }) - ); +// if (generationMode === 'unifiedCanvas' && data.boundingBox) { +// const { boundingBox } = data; +// dispatch( +// addImageToStagingArea({ +// image: { ...newImage, category: 'temp' }, +// boundingBox, +// }) +// ); - if (state.canvas.shouldAutoSave) { - dispatch( - addImage({ - image: { ...newImage, category: 'result' }, - category: 'result', - }) - ); - } - } +// if (state.canvas.shouldAutoSave) { +// dispatch( +// addImage({ +// image: { ...newImage, category: 'result' }, +// category: 'result', +// }) +// ); +// } +// } - // TODO: fix - // if (shouldLoopback) { - // const activeTabName = tabMap[activeTab]; - // switch (activeTabName) { - // case 'img2img': { - // dispatch(initialImageSelected(newImage.uuid)); - // // dispatch(setInitialImage(newImage)); - // break; - // } - // } - // } +// // TODO: fix +// // if (shouldLoopback) { +// // const activeTabName = tabMap[activeTab]; +// // switch (activeTabName) { +// // case 'img2img': { +// // dispatch(initialImageSelected(newImage.uuid)); +// // // dispatch(setInitialImage(newImage)); +// // break; +// // } +// // } +// // } - dispatch(clearIntermediateImage()); +// dispatch(clearIntermediateImage()); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Image generated: ${data.url}`, - }) - ); - } catch (e) { - console.error(e); - } - }, - /** - * Callback to run when we receive a 'intermediateResult' event. - */ - onIntermediateResult: (data: InvokeAI.ImageResultResponse) => { - try { - dispatch( - setIntermediateImage({ - uuid: uuidv4(), - ...data, - category: 'result', - }) - ); - if (!data.isBase64) { - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Intermediate image generated: ${data.url}`, - }) - ); - } - } catch (e) { - console.error(e); - } - }, - /** - * Callback to run when we receive an 'esrganResult' event. - */ - onPostprocessingResult: (data: InvokeAI.ImageResultResponse) => { - try { - dispatch( - addImage({ - category: 'result', - image: { - uuid: uuidv4(), - ...data, - category: 'result', - }, - }) - ); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Image generated: ${data.url}`, +// }) +// ); +// } catch (e) { +// console.error(e); +// } +// }, +// /** +// * Callback to run when we receive a 'intermediateResult' event. +// */ +// onIntermediateResult: (data: InvokeAI.ImageResultResponse) => { +// try { +// dispatch( +// setIntermediateImage({ +// uuid: uuidv4(), +// ...data, +// category: 'result', +// }) +// ); +// if (!data.isBase64) { +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Intermediate image generated: ${data.url}`, +// }) +// ); +// } +// } catch (e) { +// console.error(e); +// } +// }, +// /** +// * Callback to run when we receive an 'esrganResult' event. +// */ +// onPostprocessingResult: (data: InvokeAI.ImageResultResponse) => { +// try { +// dispatch( +// addImage({ +// category: 'result', +// image: { +// uuid: uuidv4(), +// ...data, +// category: 'result', +// }, +// }) +// ); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Postprocessed: ${data.url}`, - }) - ); - } catch (e) { - console.error(e); - } - }, - /** - * Callback to run when we receive a 'progressUpdate' event. - * TODO: Add additional progress phases - */ - onProgressUpdate: (data: InvokeAI.SystemStatus) => { - try { - dispatch(setIsProcessing(true)); - dispatch(setSystemStatus(data)); - } catch (e) { - console.error(e); - } - }, - /** - * Callback to run when we receive a 'progressUpdate' event. - */ - onError: (data: InvokeAI.ErrorResponse) => { - const { message, additionalData } = data; +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Postprocessed: ${data.url}`, +// }) +// ); +// } catch (e) { +// console.error(e); +// } +// }, +// /** +// * Callback to run when we receive a 'progressUpdate' event. +// * TODO: Add additional progress phases +// */ +// onProgressUpdate: (data: InvokeAI.SystemStatus) => { +// try { +// dispatch(setIsProcessing(true)); +// dispatch(setSystemStatus(data)); +// } catch (e) { +// console.error(e); +// } +// }, +// /** +// * Callback to run when we receive a 'progressUpdate' event. +// */ +// onError: (data: InvokeAI.ErrorResponse) => { +// const { message, additionalData } = data; - if (additionalData) { - // TODO: handle more data than short message - } +// if (additionalData) { +// // TODO: handle more data than short message +// } - try { - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Server error: ${message}`, - level: 'error', - }) - ); - dispatch(errorOccurred()); - dispatch(clearIntermediateImage()); - } catch (e) { - console.error(e); - } - }, - /** - * Callback to run when we receive a 'galleryImages' event. - */ - onGalleryImages: (data: InvokeAI.GalleryImagesResponse) => { - const { images, areMoreImagesAvailable, category } = data; +// try { +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Server error: ${message}`, +// level: 'error', +// }) +// ); +// dispatch(errorOccurred()); +// dispatch(clearIntermediateImage()); +// } catch (e) { +// console.error(e); +// } +// }, +// /** +// * Callback to run when we receive a 'galleryImages' event. +// */ +// onGalleryImages: (data: InvokeAI.GalleryImagesResponse) => { +// const { images, areMoreImagesAvailable, category } = data; - /** - * the logic here ideally would be in the reducer but we have a side effect: - * generating a uuid. so the logic needs to be here, outside redux. - */ +// /** +// * the logic here ideally would be in the reducer but we have a side effect: +// * generating a uuid. so the logic needs to be here, outside redux. +// */ - // Generate a UUID for each image - const preparedImages = images.map((image): InvokeAI._Image => { - return { - uuid: uuidv4(), - ...image, - }; - }); +// // Generate a UUID for each image +// const preparedImages = images.map((image): InvokeAI._Image => { +// return { +// uuid: uuidv4(), +// ...image, +// }; +// }); - dispatch( - addGalleryImages({ - images: preparedImages, - areMoreImagesAvailable, - category, - }) - ); +// dispatch( +// addGalleryImages({ +// images: preparedImages, +// areMoreImagesAvailable, +// category, +// }) +// ); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Loaded ${images.length} images`, - }) - ); - }, - /** - * Callback to run when we receive a 'processingCanceled' event. - */ - onProcessingCanceled: () => { - dispatch(processingCanceled()); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Loaded ${images.length} images`, +// }) +// ); +// }, +// /** +// * Callback to run when we receive a 'processingCanceled' event. +// */ +// onProcessingCanceled: () => { +// dispatch(processingCanceled()); - const { intermediateImage } = getState().gallery; +// const { intermediateImage } = getState().gallery; - if (intermediateImage) { - if (!intermediateImage.isBase64) { - dispatch( - addImage({ - category: 'result', - image: intermediateImage, - }) - ); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Intermediate image saved: ${intermediateImage.url}`, - }) - ); - } - dispatch(clearIntermediateImage()); - } +// if (intermediateImage) { +// if (!intermediateImage.isBase64) { +// dispatch( +// addImage({ +// category: 'result', +// image: intermediateImage, +// }) +// ); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Intermediate image saved: ${intermediateImage.url}`, +// }) +// ); +// } +// dispatch(clearIntermediateImage()); +// } - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Processing canceled`, - level: 'warning', - }) - ); - }, - /** - * Callback to run when we receive a 'imageDeleted' event. - */ - onImageDeleted: (data: InvokeAI.ImageDeletedResponse) => { - const { url } = data; +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Processing canceled`, +// level: 'warning', +// }) +// ); +// }, +// /** +// * Callback to run when we receive a 'imageDeleted' event. +// */ +// onImageDeleted: (data: InvokeAI.ImageDeletedResponse) => { +// const { url } = data; - // remove image from gallery - dispatch(removeImage(data)); +// // remove image from gallery +// dispatch(removeImage(data)); - // remove references to image in options - const { - generation: { initialImage, maskPath }, - } = getState(); +// // remove references to image in options +// const { +// generation: { initialImage, maskPath }, +// } = getState(); - if ( - initialImage === url || - (initialImage as InvokeAI._Image)?.url === url - ) { - dispatch(clearInitialImage()); - } +// if ( +// initialImage === url || +// (initialImage as InvokeAI._Image)?.url === url +// ) { +// dispatch(clearInitialImage()); +// } - if (maskPath === url) { - dispatch(setMaskPath('')); - } +// if (maskPath === url) { +// dispatch(setMaskPath('')); +// } - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Image deleted: ${url}`, - }) - ); - }, - onSystemConfig: (data: InvokeAI.SystemConfig) => { - dispatch(setSystemConfig(data)); - if (!data.infill_methods.includes('patchmatch')) { - dispatch(setInfillMethod(data.infill_methods[0])); - } - }, - onFoundModels: (data: InvokeAI.FoundModelResponse) => { - const { search_folder, found_models } = data; - dispatch(setSearchFolder(search_folder)); - dispatch(setFoundModels(found_models)); - }, - onNewModelAdded: (data: InvokeAI.ModelAddedResponse) => { - const { new_model_name, model_list, update } = data; - dispatch(setModelList(model_list)); - dispatch(setIsProcessing(false)); - dispatch(setCurrentStatus(i18n.t('modelManager.modelAdded'))); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Model Added: ${new_model_name}`, - level: 'info', - }) - ); - dispatch( - addToast({ - title: !update - ? `${i18n.t('modelManager.modelAdded')}: ${new_model_name}` - : `${i18n.t('modelManager.modelUpdated')}: ${new_model_name}`, - status: 'success', - duration: 2500, - isClosable: true, - }) - ); - }, - onModelDeleted: (data: InvokeAI.ModelDeletedResponse) => { - const { deleted_model_name, model_list } = data; - dispatch(setModelList(model_list)); - dispatch(setIsProcessing(false)); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `${i18n.t( - 'modelManager.modelAdded' - )}: ${deleted_model_name}`, - level: 'info', - }) - ); - dispatch( - addToast({ - title: `${i18n.t( - 'modelManager.modelEntryDeleted' - )}: ${deleted_model_name}`, - status: 'success', - duration: 2500, - isClosable: true, - }) - ); - }, - onModelConverted: (data: InvokeAI.ModelConvertedResponse) => { - const { converted_model_name, model_list } = data; - dispatch(setModelList(model_list)); - dispatch(setCurrentStatus(i18n.t('common.statusModelConverted'))); - dispatch(setIsProcessing(false)); - dispatch(setIsCancelable(true)); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Model converted: ${converted_model_name}`, - level: 'info', - }) - ); - dispatch( - addToast({ - title: `${i18n.t( - 'modelManager.modelConverted' - )}: ${converted_model_name}`, - status: 'success', - duration: 2500, - isClosable: true, - }) - ); - }, - onModelsMerged: (data: InvokeAI.ModelsMergedResponse) => { - const { merged_models, merged_model_name, model_list } = data; - dispatch(setModelList(model_list)); - dispatch(setCurrentStatus(i18n.t('common.statusMergedModels'))); - dispatch(setIsProcessing(false)); - dispatch(setIsCancelable(true)); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Models merged: ${merged_models}`, - level: 'info', - }) - ); - dispatch( - addToast({ - title: `${i18n.t('modelManager.modelsMerged')}: ${merged_model_name}`, - status: 'success', - duration: 2500, - isClosable: true, - }) - ); - }, - onModelChanged: (data: InvokeAI.ModelChangeResponse) => { - const { model_name, model_list } = data; - dispatch(setModelList(model_list)); - dispatch(setCurrentStatus(i18n.t('common.statusModelChanged'))); - dispatch(setIsProcessing(false)); - dispatch(setIsCancelable(true)); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Model changed: ${model_name}`, - level: 'info', - }) - ); - }, - onModelChangeFailed: (data: InvokeAI.ModelChangeResponse) => { - const { model_name, model_list } = data; - dispatch(setModelList(model_list)); - dispatch(setIsProcessing(false)); - dispatch(setIsCancelable(true)); - dispatch(errorOccurred()); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Model change failed: ${model_name}`, - level: 'error', - }) - ); - }, - onTempFolderEmptied: () => { - dispatch( - addToast({ - title: i18n.t('toast.tempFoldersEmptied'), - status: 'success', - duration: 2500, - isClosable: true, - }) - ); - }, - }; -}; +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Image deleted: ${url}`, +// }) +// ); +// }, +// onSystemConfig: (data: InvokeAI.SystemConfig) => { +// dispatch(setSystemConfig(data)); +// if (!data.infill_methods.includes('patchmatch')) { +// dispatch(setInfillMethod(data.infill_methods[0])); +// } +// }, +// onFoundModels: (data: InvokeAI.FoundModelResponse) => { +// const { search_folder, found_models } = data; +// dispatch(setSearchFolder(search_folder)); +// dispatch(setFoundModels(found_models)); +// }, +// onNewModelAdded: (data: InvokeAI.ModelAddedResponse) => { +// const { new_model_name, model_list, update } = data; +// dispatch(setModelList(model_list)); +// dispatch(setIsProcessing(false)); +// dispatch(setCurrentStatus(i18n.t('modelManager.modelAdded'))); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Model Added: ${new_model_name}`, +// level: 'info', +// }) +// ); +// dispatch( +// addToast({ +// title: !update +// ? `${i18n.t('modelManager.modelAdded')}: ${new_model_name}` +// : `${i18n.t('modelManager.modelUpdated')}: ${new_model_name}`, +// status: 'success', +// duration: 2500, +// isClosable: true, +// }) +// ); +// }, +// onModelDeleted: (data: InvokeAI.ModelDeletedResponse) => { +// const { deleted_model_name, model_list } = data; +// dispatch(setModelList(model_list)); +// dispatch(setIsProcessing(false)); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `${i18n.t( +// 'modelManager.modelAdded' +// )}: ${deleted_model_name}`, +// level: 'info', +// }) +// ); +// dispatch( +// addToast({ +// title: `${i18n.t( +// 'modelManager.modelEntryDeleted' +// )}: ${deleted_model_name}`, +// status: 'success', +// duration: 2500, +// isClosable: true, +// }) +// ); +// }, +// onModelConverted: (data: InvokeAI.ModelConvertedResponse) => { +// const { converted_model_name, model_list } = data; +// dispatch(setModelList(model_list)); +// dispatch(setCurrentStatus(i18n.t('common.statusModelConverted'))); +// dispatch(setIsProcessing(false)); +// dispatch(setIsCancelable(true)); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Model converted: ${converted_model_name}`, +// level: 'info', +// }) +// ); +// dispatch( +// addToast({ +// title: `${i18n.t( +// 'modelManager.modelConverted' +// )}: ${converted_model_name}`, +// status: 'success', +// duration: 2500, +// isClosable: true, +// }) +// ); +// }, +// onModelsMerged: (data: InvokeAI.ModelsMergedResponse) => { +// const { merged_models, merged_model_name, model_list } = data; +// dispatch(setModelList(model_list)); +// dispatch(setCurrentStatus(i18n.t('common.statusMergedModels'))); +// dispatch(setIsProcessing(false)); +// dispatch(setIsCancelable(true)); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Models merged: ${merged_models}`, +// level: 'info', +// }) +// ); +// dispatch( +// addToast({ +// title: `${i18n.t('modelManager.modelsMerged')}: ${merged_model_name}`, +// status: 'success', +// duration: 2500, +// isClosable: true, +// }) +// ); +// }, +// onModelChanged: (data: InvokeAI.ModelChangeResponse) => { +// const { model_name, model_list } = data; +// dispatch(setModelList(model_list)); +// dispatch(setCurrentStatus(i18n.t('common.statusModelChanged'))); +// dispatch(setIsProcessing(false)); +// dispatch(setIsCancelable(true)); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Model changed: ${model_name}`, +// level: 'info', +// }) +// ); +// }, +// onModelChangeFailed: (data: InvokeAI.ModelChangeResponse) => { +// const { model_name, model_list } = data; +// dispatch(setModelList(model_list)); +// dispatch(setIsProcessing(false)); +// dispatch(setIsCancelable(true)); +// dispatch(errorOccurred()); +// dispatch( +// addLogEntry({ +// timestamp: dateFormat(new Date(), 'isoDateTime'), +// message: `Model change failed: ${model_name}`, +// level: 'error', +// }) +// ); +// }, +// onTempFolderEmptied: () => { +// dispatch( +// addToast({ +// title: i18n.t('toast.tempFoldersEmptied'), +// status: 'success', +// duration: 2500, +// isClosable: true, +// }) +// ); +// }, +// }; +// }; -export default makeSocketIOListeners; +// export default makeSocketIOListeners; + +export default {}; diff --git a/invokeai/frontend/web/src/app/socketio/middleware.ts b/invokeai/frontend/web/src/app/socketio/middleware.ts index 74752dc980..88013ea222 100644 --- a/invokeai/frontend/web/src/app/socketio/middleware.ts +++ b/invokeai/frontend/web/src/app/socketio/middleware.ts @@ -1,246 +1,248 @@ -import { Middleware } from '@reduxjs/toolkit'; -import { io } from 'socket.io-client'; +// import { Middleware } from '@reduxjs/toolkit'; +// import { io } from 'socket.io-client'; -import makeSocketIOEmitters from './emitters'; -import makeSocketIOListeners from './listeners'; +// import makeSocketIOEmitters from './emitters'; +// import makeSocketIOListeners from './listeners'; -import * as InvokeAI from 'app/types/invokeai'; +// import * as InvokeAI from 'app/types/invokeai'; -/** - * Creates a socketio middleware to handle communication with server. - * - * Special `socketio/actionName` actions are created in actions.ts and - * exported for use by the application, which treats them like any old - * action, using `dispatch` to dispatch them. - * - * These actions are intercepted here, where `socketio.emit()` calls are - * made on their behalf - see `emitters.ts`. The emitter functions - * are the outbound communication to the server. - * - * Listeners are also established here - see `listeners.ts`. The listener - * functions receive communication from the server and usually dispatch - * some new action to handle whatever data was sent from the server. - */ -export const socketioMiddleware = () => { - const { origin } = new URL(window.location.href); +// /** +// * Creates a socketio middleware to handle communication with server. +// * +// * Special `socketio/actionName` actions are created in actions.ts and +// * exported for use by the application, which treats them like any old +// * action, using `dispatch` to dispatch them. +// * +// * These actions are intercepted here, where `socketio.emit()` calls are +// * made on their behalf - see `emitters.ts`. The emitter functions +// * are the outbound communication to the server. +// * +// * Listeners are also established here - see `listeners.ts`. The listener +// * functions receive communication from the server and usually dispatch +// * some new action to handle whatever data was sent from the server. +// */ +// export const socketioMiddleware = () => { +// const { origin } = new URL(window.location.href); - const socketio = io(origin, { - timeout: 60000, - path: `${window.location.pathname}socket.io`, - }); +// const socketio = io(origin, { +// timeout: 60000, +// path: `${window.location.pathname}socket.io`, +// }); - socketio.disconnect(); +// socketio.disconnect(); - let areListenersSet = false; +// let areListenersSet = false; - const middleware: Middleware = (store) => (next) => (action) => { - const { - onConnect, - onDisconnect, - onError, - onPostprocessingResult, - onGenerationResult, - onIntermediateResult, - onProgressUpdate, - onGalleryImages, - onProcessingCanceled, - onImageDeleted, - onSystemConfig, - onModelChanged, - onFoundModels, - onNewModelAdded, - onModelDeleted, - onModelConverted, - onModelsMerged, - onModelChangeFailed, - onTempFolderEmptied, - } = makeSocketIOListeners(store); +// const middleware: Middleware = (store) => (next) => (action) => { +// const { +// onConnect, +// onDisconnect, +// onError, +// onPostprocessingResult, +// onGenerationResult, +// onIntermediateResult, +// onProgressUpdate, +// onGalleryImages, +// onProcessingCanceled, +// onImageDeleted, +// onSystemConfig, +// onModelChanged, +// onFoundModels, +// onNewModelAdded, +// onModelDeleted, +// onModelConverted, +// onModelsMerged, +// onModelChangeFailed, +// onTempFolderEmptied, +// } = makeSocketIOListeners(store); - const { - emitGenerateImage, - emitRunESRGAN, - emitRunFacetool, - emitDeleteImage, - emitRequestImages, - emitRequestNewImages, - emitCancelProcessing, - emitRequestSystemConfig, - emitSearchForModels, - emitAddNewModel, - emitDeleteModel, - emitConvertToDiffusers, - emitMergeDiffusersModels, - emitRequestModelChange, - emitSaveStagingAreaImageToGallery, - emitRequestEmptyTempFolder, - } = makeSocketIOEmitters(store, socketio); +// const { +// emitGenerateImage, +// emitRunESRGAN, +// emitRunFacetool, +// emitDeleteImage, +// emitRequestImages, +// emitRequestNewImages, +// emitCancelProcessing, +// emitRequestSystemConfig, +// emitSearchForModels, +// emitAddNewModel, +// emitDeleteModel, +// emitConvertToDiffusers, +// emitMergeDiffusersModels, +// emitRequestModelChange, +// emitSaveStagingAreaImageToGallery, +// emitRequestEmptyTempFolder, +// } = makeSocketIOEmitters(store, socketio); - /** - * If this is the first time the middleware has been called (e.g. during store setup), - * initialize all our socket.io listeners. - */ - if (!areListenersSet) { - socketio.on('connect', () => onConnect()); +// /** +// * If this is the first time the middleware has been called (e.g. during store setup), +// * initialize all our socket.io listeners. +// */ +// if (!areListenersSet) { +// socketio.on('connect', () => onConnect()); - socketio.on('disconnect', () => onDisconnect()); +// socketio.on('disconnect', () => onDisconnect()); - socketio.on('error', (data: InvokeAI.ErrorResponse) => onError(data)); +// socketio.on('error', (data: InvokeAI.ErrorResponse) => onError(data)); - socketio.on('generationResult', (data: InvokeAI.ImageResultResponse) => - onGenerationResult(data) - ); +// socketio.on('generationResult', (data: InvokeAI.ImageResultResponse) => +// onGenerationResult(data) +// ); - socketio.on( - 'postprocessingResult', - (data: InvokeAI.ImageResultResponse) => onPostprocessingResult(data) - ); +// socketio.on( +// 'postprocessingResult', +// (data: InvokeAI.ImageResultResponse) => onPostprocessingResult(data) +// ); - socketio.on('intermediateResult', (data: InvokeAI.ImageResultResponse) => - onIntermediateResult(data) - ); +// socketio.on('intermediateResult', (data: InvokeAI.ImageResultResponse) => +// onIntermediateResult(data) +// ); - socketio.on('progressUpdate', (data: InvokeAI.SystemStatus) => - onProgressUpdate(data) - ); +// socketio.on('progressUpdate', (data: InvokeAI.SystemStatus) => +// onProgressUpdate(data) +// ); - socketio.on('galleryImages', (data: InvokeAI.GalleryImagesResponse) => - onGalleryImages(data) - ); +// socketio.on('galleryImages', (data: InvokeAI.GalleryImagesResponse) => +// onGalleryImages(data) +// ); - socketio.on('processingCanceled', () => { - onProcessingCanceled(); - }); +// socketio.on('processingCanceled', () => { +// onProcessingCanceled(); +// }); - socketio.on('imageDeleted', (data: InvokeAI.ImageDeletedResponse) => { - onImageDeleted(data); - }); +// socketio.on('imageDeleted', (data: InvokeAI.ImageDeletedResponse) => { +// onImageDeleted(data); +// }); - socketio.on('systemConfig', (data: InvokeAI.SystemConfig) => { - onSystemConfig(data); - }); +// socketio.on('systemConfig', (data: InvokeAI.SystemConfig) => { +// onSystemConfig(data); +// }); - socketio.on('foundModels', (data: InvokeAI.FoundModelResponse) => { - onFoundModels(data); - }); +// socketio.on('foundModels', (data: InvokeAI.FoundModelResponse) => { +// onFoundModels(data); +// }); - socketio.on('newModelAdded', (data: InvokeAI.ModelAddedResponse) => { - onNewModelAdded(data); - }); +// socketio.on('newModelAdded', (data: InvokeAI.ModelAddedResponse) => { +// onNewModelAdded(data); +// }); - socketio.on('modelDeleted', (data: InvokeAI.ModelDeletedResponse) => { - onModelDeleted(data); - }); +// socketio.on('modelDeleted', (data: InvokeAI.ModelDeletedResponse) => { +// onModelDeleted(data); +// }); - socketio.on('modelConverted', (data: InvokeAI.ModelConvertedResponse) => { - onModelConverted(data); - }); +// socketio.on('modelConverted', (data: InvokeAI.ModelConvertedResponse) => { +// onModelConverted(data); +// }); - socketio.on('modelsMerged', (data: InvokeAI.ModelsMergedResponse) => { - onModelsMerged(data); - }); +// socketio.on('modelsMerged', (data: InvokeAI.ModelsMergedResponse) => { +// onModelsMerged(data); +// }); - socketio.on('modelChanged', (data: InvokeAI.ModelChangeResponse) => { - onModelChanged(data); - }); +// socketio.on('modelChanged', (data: InvokeAI.ModelChangeResponse) => { +// onModelChanged(data); +// }); - socketio.on('modelChangeFailed', (data: InvokeAI.ModelChangeResponse) => { - onModelChangeFailed(data); - }); +// socketio.on('modelChangeFailed', (data: InvokeAI.ModelChangeResponse) => { +// onModelChangeFailed(data); +// }); - socketio.on('tempFolderEmptied', () => { - onTempFolderEmptied(); - }); +// socketio.on('tempFolderEmptied', () => { +// onTempFolderEmptied(); +// }); - areListenersSet = true; - } +// areListenersSet = true; +// } - /** - * Handle redux actions caught by middleware. - */ - switch (action.type) { - case 'socketio/generateImage': { - emitGenerateImage(action.payload); - break; - } +// /** +// * Handle redux actions caught by middleware. +// */ +// switch (action.type) { +// case 'socketio/generateImage': { +// emitGenerateImage(action.payload); +// break; +// } - case 'socketio/runESRGAN': { - emitRunESRGAN(action.payload); - break; - } +// case 'socketio/runESRGAN': { +// emitRunESRGAN(action.payload); +// break; +// } - case 'socketio/runFacetool': { - emitRunFacetool(action.payload); - break; - } +// case 'socketio/runFacetool': { +// emitRunFacetool(action.payload); +// break; +// } - case 'socketio/deleteImage': { - emitDeleteImage(action.payload); - break; - } +// case 'socketio/deleteImage': { +// emitDeleteImage(action.payload); +// break; +// } - case 'socketio/requestImages': { - emitRequestImages(action.payload); - break; - } +// case 'socketio/requestImages': { +// emitRequestImages(action.payload); +// break; +// } - case 'socketio/requestNewImages': { - emitRequestNewImages(action.payload); - break; - } +// case 'socketio/requestNewImages': { +// emitRequestNewImages(action.payload); +// break; +// } - case 'socketio/cancelProcessing': { - emitCancelProcessing(); - break; - } +// case 'socketio/cancelProcessing': { +// emitCancelProcessing(); +// break; +// } - case 'socketio/requestSystemConfig': { - emitRequestSystemConfig(); - break; - } +// case 'socketio/requestSystemConfig': { +// emitRequestSystemConfig(); +// break; +// } - case 'socketio/searchForModels': { - emitSearchForModels(action.payload); - break; - } +// case 'socketio/searchForModels': { +// emitSearchForModels(action.payload); +// break; +// } - case 'socketio/addNewModel': { - emitAddNewModel(action.payload); - break; - } +// case 'socketio/addNewModel': { +// emitAddNewModel(action.payload); +// break; +// } - case 'socketio/deleteModel': { - emitDeleteModel(action.payload); - break; - } +// case 'socketio/deleteModel': { +// emitDeleteModel(action.payload); +// break; +// } - case 'socketio/convertToDiffusers': { - emitConvertToDiffusers(action.payload); - break; - } +// case 'socketio/convertToDiffusers': { +// emitConvertToDiffusers(action.payload); +// break; +// } - case 'socketio/mergeDiffusersModels': { - emitMergeDiffusersModels(action.payload); - break; - } +// case 'socketio/mergeDiffusersModels': { +// emitMergeDiffusersModels(action.payload); +// break; +// } - case 'socketio/requestModelChange': { - emitRequestModelChange(action.payload); - break; - } +// case 'socketio/requestModelChange': { +// emitRequestModelChange(action.payload); +// break; +// } - case 'socketio/saveStagingAreaImageToGallery': { - emitSaveStagingAreaImageToGallery(action.payload); - break; - } +// case 'socketio/saveStagingAreaImageToGallery': { +// emitSaveStagingAreaImageToGallery(action.payload); +// break; +// } - case 'socketio/requestEmptyTempFolder': { - emitRequestEmptyTempFolder(); - break; - } - } +// case 'socketio/requestEmptyTempFolder': { +// emitRequestEmptyTempFolder(); +// break; +// } +// } - next(action); - }; +// next(action); +// }; - return middleware; -}; +// return middleware; +// }; + +export default {}; diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index 627c4f0063..20c0fa8b2a 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -19,7 +19,6 @@ import hotkeysReducer from 'features/ui/store/hotkeysSlice'; import modelsReducer from 'features/system/store/modelSlice'; import nodesReducer from 'features/nodes/store/nodesSlice'; -import { socketioMiddleware } from '../socketio/middleware'; import { socketMiddleware } from 'services/events/middleware'; import { canvasDenylist } from 'features/canvas/store/canvasPersistDenylist'; import { galleryDenylist } from 'features/gallery/store/galleryPersistDenylist'; @@ -87,13 +86,13 @@ const rootPersistConfig = getPersistConfig({ const persistedReducer = persistReducer(rootPersistConfig, rootReducer); // TODO: rip the old middleware out when nodes is complete -export function buildMiddleware() { - if (import.meta.env.MODE === 'nodes' || import.meta.env.MODE === 'package') { - return socketMiddleware(); - } else { - return socketioMiddleware(); - } -} +// export function buildMiddleware() { +// if (import.meta.env.MODE === 'nodes' || import.meta.env.MODE === 'package') { +// return socketMiddleware(); +// } else { +// return socketioMiddleware(); +// } +// } export const store = configureStore({ reducer: persistedReducer, diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index 27ca9dc4a6..bd9642491d 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -111,9 +111,9 @@ export type FacetoolMetadata = CommonPostProcessedImageMetadata & { export type PostProcessedImageMetadata = ESRGANMetadata | FacetoolMetadata; // Metadata includes the system config and image metadata. -export type Metadata = SystemGenerationMetadata & { - image: GeneratedImageMetadata | PostProcessedImageMetadata; -}; +// export type Metadata = SystemGenerationMetadata & { +// image: GeneratedImageMetadata | PostProcessedImageMetadata; +// }; // An Image has a UUID, url, modified timestamp, width, height and maybe metadata export type _Image = { @@ -150,31 +150,31 @@ export type GalleryImages = { * Types related to the system status. */ -// This represents the processing status of the backend. -export type SystemStatus = { - isProcessing: boolean; - currentStep: number; - totalSteps: number; - currentIteration: number; - totalIterations: number; - currentStatus: string; - currentStatusHasSteps: boolean; - hasError: boolean; -}; +// // This represents the processing status of the backend. +// export type SystemStatus = { +// isProcessing: boolean; +// currentStep: number; +// totalSteps: number; +// currentIteration: number; +// totalIterations: number; +// currentStatus: string; +// currentStatusHasSteps: boolean; +// hasError: boolean; +// }; -export type SystemGenerationMetadata = { - model: string; - model_weights?: string; - model_id?: string; - model_hash: string; - app_id: string; - app_version: string; -}; +// export type SystemGenerationMetadata = { +// model: string; +// model_weights?: string; +// model_id?: string; +// model_hash: string; +// app_id: string; +// app_version: string; +// }; -export type SystemConfig = SystemGenerationMetadata & { - model_list: ModelList; - infill_methods: string[]; -}; +// export type SystemConfig = SystemGenerationMetadata & { +// model_list: ModelList; +// infill_methods: string[]; +// }; export type ModelStatus = 'active' | 'cached' | 'not loaded'; @@ -286,9 +286,9 @@ export type FoundModelResponse = { found_models: FoundModel[]; }; -export type SystemStatusResponse = SystemStatus; +// export type SystemStatusResponse = SystemStatus; -export type SystemConfigResponse = SystemConfig; +// export type SystemConfigResponse = SystemConfig; export type ImageResultResponse = Omit<_Image, 'uuid'> & { boundingBox?: IRect; diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingAreaToolbar.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingAreaToolbar.tsx index cbcd86d8d6..eeb51d955b 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingAreaToolbar.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingAreaToolbar.tsx @@ -1,6 +1,6 @@ import { ButtonGroup, Flex } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; -import { saveStagingAreaImageToGallery } from 'app/socketio/actions'; +// import { saveStagingAreaImageToGallery } from 'app/socketio/actions'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIIconButton from 'common/components/IAIIconButton'; import { canvasSelector } from 'features/canvas/store/canvasSelectors'; diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx index 481e3feb40..a30d8bcac1 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx @@ -1,5 +1,5 @@ import { createSelector } from '@reduxjs/toolkit'; -import { isEqual } from 'lodash-es'; +import { get, isEqual, isNumber, isString } from 'lodash-es'; import { ButtonGroup, @@ -10,7 +10,7 @@ import { useDisclosure, useToast, } from '@chakra-ui/react'; -import { runESRGAN, runFacetool } from 'app/socketio/actions'; +// import { runESRGAN, runFacetool } from 'app/socketio/actions'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIButton from 'common/components/IAIButton'; import IAIIconButton from 'common/components/IAIIconButton'; @@ -68,6 +68,7 @@ import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvas import { useGetUrl } from 'common/util/getUrl'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { imageDeleted } from 'services/thunks/image'; +import { useParameters } from 'features/parameters/hooks/useParameters'; const currentImageButtonsSelector = createSelector( [ @@ -112,6 +113,8 @@ const currentImageButtonsSelector = createSelector( isLightboxOpen, shouldHidePreview, image, + seed: image?.metadata?.invokeai?.node?.seed, + prompt: image?.metadata?.invokeai?.node?.prompt, }; }, { @@ -161,16 +164,8 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { const toast = useToast(); const { t } = useTranslation(); - const setBothPrompts = useSetBothPrompts(); - const handleClickUseAsInitialImage = useCallback(() => { - if (!image) return; - if (isLightboxOpen) dispatch(setIsLightboxOpen(false)); - dispatch(initialImageSelected({ name: image.name, type: image.type })); - // dispatch(setInitialImage(currentImage)); - - // dispatch(setActiveTab('img2img')); - }, [dispatch, image, isLightboxOpen]); + const { recallPrompt, recallSeed, sendToImageToImage } = useParameters(); const handleCopyImage = useCallback(async () => { if (!image?.url) { @@ -217,30 +212,6 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { }); }, [toast, shouldTransformUrls, getUrl, t, image]); - useHotkeys( - 'shift+i', - () => { - if (image) { - handleClickUseAsInitialImage(); - toast({ - title: t('toast.sentToImageToImage'), - status: 'success', - duration: 2500, - isClosable: true, - }); - } else { - toast({ - title: t('toast.imageNotLoaded'), - description: t('toast.imageNotLoadedDesc'), - status: 'error', - duration: 2500, - isClosable: true, - }); - } - }, - [image] - ); - const handlePreviewVisibility = useCallback(() => { dispatch(setShouldHidePreview(!shouldHidePreview)); }, [dispatch, shouldHidePreview]); @@ -259,7 +230,8 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { useHotkeys( 'a', () => { - if (['txt2img', 'img2img'].includes(image?.metadata?.sd_metadata?.type)) { + const type = image?.metadata?.invokeai?.node?.types; + if (isString(type) && ['txt2img', 'img2img'].includes(type)) { handleClickUseAllParameters(); toast({ title: t('toast.parametersSet'), @@ -280,63 +252,23 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { [image] ); - const handleClickUseSeed = () => { - image?.metadata && dispatch(setSeed(image.metadata.sd_metadata.seed)); - }; + const handleUseSeed = useCallback(() => { + recallSeed(image?.metadata?.invokeai?.node?.seed); + }, [image, recallSeed]); - useHotkeys( - 's', - () => { - if (image?.metadata?.sd_metadata?.seed) { - handleClickUseSeed(); - toast({ - title: t('toast.seedSet'), - status: 'success', - duration: 2500, - isClosable: true, - }); - } else { - toast({ - title: t('toast.seedNotSet'), - description: t('toast.seedNotSetDesc'), - status: 'error', - duration: 2500, - isClosable: true, - }); - } - }, - [image] - ); + useHotkeys('s', handleUseSeed, [image]); - const handleClickUsePrompt = useCallback(() => { - if (image?.metadata?.sd_metadata?.prompt) { - setBothPrompts(image?.metadata?.sd_metadata?.prompt); - } - }, [image?.metadata?.sd_metadata?.prompt, setBothPrompts]); + const handleUsePrompt = useCallback(() => { + recallPrompt(image?.metadata?.invokeai?.node?.prompt); + }, [image, recallPrompt]); - useHotkeys( - 'p', - () => { - if (image?.metadata?.sd_metadata?.prompt) { - handleClickUsePrompt(); - toast({ - title: t('toast.promptSet'), - status: 'success', - duration: 2500, - isClosable: true, - }); - } else { - toast({ - title: t('toast.promptNotSet'), - description: t('toast.promptNotSetDesc'), - status: 'error', - duration: 2500, - isClosable: true, - }); - } - }, - [image] - ); + useHotkeys('p', handleUsePrompt, [image]); + + const handleSendToImageToImage = useCallback(() => { + sendToImageToImage(image); + }, [image, sendToImageToImage]); + + useHotkeys('shift+i', handleSendToImageToImage, [image]); const handleClickUpscale = useCallback(() => { // selectedImage && dispatch(runESRGAN(selectedImage)); @@ -496,7 +428,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { > } > {t('parameters.sendToImg2Img')} @@ -570,8 +502,8 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { icon={} tooltip={`${t('parameters.usePrompt')} (P)`} aria-label={`${t('parameters.usePrompt')} (P)`} - isDisabled={!image?.metadata?.sd_metadata?.prompt} - onClick={handleClickUsePrompt} + isDisabled={!image?.metadata?.invokeai?.node?.prompt} + onClick={handleUsePrompt} /> { tooltip={`${t('parameters.useSeed')} (S)`} aria-label={`${t('parameters.useSeed')} (S)`} isDisabled={!image?.metadata?.sd_metadata?.seed} - onClick={handleClickUseSeed} + onClick={handleUseSeed} /> { + [gallerySelector, systemSelector, lightboxSelector, activeTabNameSelector], + (gallery, system, lightbox, activeTabName) => { const { galleryImageObjectFit, galleryImageMinimumWidth, @@ -71,7 +43,6 @@ export const selector = createSelector( } = gallery; const { isLightboxOpen } = lightbox; - const { disabledFeatures } = config; const { isConnected, isProcessing, shouldConfirmOnDelete } = system; return { @@ -82,7 +53,6 @@ export const selector = createSelector( shouldUseSingleGalleryColumn, activeTabName, isLightboxOpen, - disabledFeatures, }; }, { @@ -113,14 +83,15 @@ const HoverableImage = memo((props: HoverableImageProps) => { galleryImageMinimumWidth, canDeleteImage, shouldUseSingleGalleryColumn, - disabledFeatures, shouldConfirmOnDelete, } = useAppSelector(selector); + const { isOpen: isDeleteDialogOpen, onOpen: onDeleteDialogOpen, onClose: onDeleteDialogClose, } = useDisclosure(); + const { image, isSelected } = props; const { url, thumbnail, name, metadata } = image; const { getUrl } = useGetUrl(); @@ -130,53 +101,62 @@ const HoverableImage = memo((props: HoverableImageProps) => { const toast = useToast(); const { direction } = useTheme(); const { t } = useTranslation(); - const setBothPrompts = useSetBothPrompts(); + const { isFeatureEnabled: isLightboxEnabled } = useFeatureStatus('lightbox'); + const { recallSeed, recallPrompt, sendToImageToImage, recallInitialImage } = + useParameters(); const handleMouseOver = () => setIsHovered(true); - const handleMouseOut = () => setIsHovered(false); - const handleInitiateDelete = () => { + // Immediately deletes an image + const handleDelete = useCallback(() => { + if (canDeleteImage && image) { + dispatch(imageDeleted({ imageType: image.type, imageName: image.name })); + } + }, [dispatch, image, canDeleteImage]); + + // Opens the alert dialog to check if user is sure they want to delete + const handleInitiateDelete = useCallback(() => { if (shouldConfirmOnDelete) { onDeleteDialogOpen(); } else { handleDelete(); } - }; + }, [handleDelete, onDeleteDialogOpen, shouldConfirmOnDelete]); - const handleDelete = () => { - if (canDeleteImage && image) { - dispatch(imageDeleted({ imageType: image.type, imageName: image.name })); - } - }; + const handleSelectImage = useCallback(() => { + dispatch(imageSelected(image.name)); + }, [image, dispatch]); - const handleUsePrompt = () => { - if (typeof image.metadata?.invokeai?.node?.prompt === 'string') { - setBothPrompts(image.metadata?.invokeai?.node?.prompt); - } - toast({ - title: t('toast.promptSet'), - status: 'success', - duration: 2500, - isClosable: true, - }); - }; + const handleDragStart = useCallback( + (e: DragEvent) => { + e.dataTransfer.setData('invokeai/imageName', image.name); + e.dataTransfer.setData('invokeai/imageType', image.type); + e.dataTransfer.effectAllowed = 'move'; + }, + [image] + ); - const handleUseSeed = () => { - typeof image.metadata.invokeai?.node?.seed === 'number' && - dispatch(setSeed(image.metadata.invokeai?.node?.seed)); - toast({ - title: t('toast.seedSet'), - status: 'success', - duration: 2500, - isClosable: true, - }); - }; + // Recall parameters handlers + const handleRecallPrompt = useCallback(() => { + recallPrompt(image.metadata?.invokeai?.node?.prompt); + }, [image, recallPrompt]); - const handleSendToImageToImage = () => { - dispatch(initialImageSelected(image.name)); - }; + const handleRecallSeed = useCallback(() => { + recallSeed(image.metadata.invokeai?.node?.seed); + }, [image, recallSeed]); + const handleSendToImageToImage = useCallback(() => { + sendToImageToImage(image); + }, [image, sendToImageToImage]); + + const handleRecallInitialImage = useCallback(() => { + recallInitialImage(image.metadata.invokeai?.node?.image); + }, [image, recallInitialImage]); + + /** + * TODO: the rest of these + */ const handleSendToCanvas = () => { // dispatch(setInitialCanvasImage(image)); @@ -205,41 +185,6 @@ const HoverableImage = memo((props: HoverableImageProps) => { // }); }; - const handleUseInitialImage = async () => { - // if (metadata.invokeai?.node?.image?.init_image_path) { - // const response = await fetch( - // metadata.invokeai?.node?.image?.init_image_path - // ); - // if (response.ok) { - // dispatch(setAllImageToImageParameters(metadata?.invokeai?.node)); - // toast({ - // title: t('toast.initialImageSet'), - // status: 'success', - // duration: 2500, - // isClosable: true, - // }); - // return; - // } - // } - // toast({ - // title: t('toast.initialImageNotSet'), - // description: t('toast.initialImageNotSetDesc'), - // status: 'error', - // duration: 2500, - // isClosable: true, - // }); - }; - - const handleSelectImage = () => { - dispatch(imageSelected(image.name)); - }; - - const handleDragStart = (e: DragEvent) => { - e.dataTransfer.setData('invokeai/imageName', image.name); - e.dataTransfer.setData('invokeai/imageType', image.type); - e.dataTransfer.effectAllowed = 'move'; - }; - const handleLightBox = () => { // dispatch(setCurrentImage(image)); // dispatch(setIsLightboxOpen(true)); @@ -254,21 +199,21 @@ const HoverableImage = memo((props: HoverableImageProps) => { menuProps={{ size: 'sm', isLazy: true }} renderMenu={() => ( - + } onClickCapture={handleOpenInNewTab} > {t('common.openInNewTab')} - {!disabledFeatures.includes('lightbox') && ( + {isLightboxEnabled && ( } onClickCapture={handleLightBox}> {t('parameters.openInViewer')} )} } - onClickCapture={handleUsePrompt} + onClickCapture={handleRecallPrompt} isDisabled={image?.metadata?.invokeai?.node?.prompt === undefined} > {t('parameters.usePrompt')} @@ -276,14 +221,14 @@ const HoverableImage = memo((props: HoverableImageProps) => { } - onClickCapture={handleUseSeed} + onClickCapture={handleRecallSeed} isDisabled={image?.metadata?.invokeai?.node?.seed === undefined} > {t('parameters.useSeed')} } - onClickCapture={handleUseInitialImage} + onClickCapture={handleRecallInitialImage} isDisabled={image?.metadata?.invokeai?.node?.type !== 'img2img'} > {t('parameters.useInitImg')} diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx index 1b57dbea78..e08e934e75 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx @@ -1,5 +1,5 @@ import { ButtonGroup, Flex, Grid, Icon, Image, Text } from '@chakra-ui/react'; -import { requestImages } from 'app/socketio/actions'; +// import { requestImages } from 'app/socketio/actions'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIButton from 'common/components/IAIButton'; import IAICheckbox from 'common/components/IAICheckbox'; diff --git a/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/InvokeButton.tsx b/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/InvokeButton.tsx index e028fe4f8d..d2002eb04f 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/InvokeButton.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/InvokeButton.tsx @@ -1,6 +1,5 @@ import { Box } from '@chakra-ui/react'; import { readinessSelector } from 'app/selectors/readinessSelector'; -import { generateImage } from 'app/socketio/actions'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIButton, { IAIButtonProps } from 'common/components/IAIButton'; import IAIIconButton, { @@ -8,10 +7,11 @@ import IAIIconButton, { } from 'common/components/IAIIconButton'; import { clampSymmetrySteps } from 'features/parameters/store/generationSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { useCallback } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; import { FaPlay } from 'react-icons/fa'; -import { generateGraphBuilt, sessionCreated } from 'services/thunks/session'; +import { generateGraphBuilt } from 'services/thunks/session'; interface InvokeButton extends Omit { @@ -24,19 +24,16 @@ export default function InvokeButton(props: InvokeButton) { const { isReady } = useAppSelector(readinessSelector); const activeTabName = useAppSelector(activeTabNameSelector); - const handleClickGenerate = () => { - // dispatch(generateImage(activeTabName)); + const handleInvoke = useCallback(() => { + dispatch(clampSymmetrySteps()); dispatch(generateGraphBuilt()); - }; + }, [dispatch]); const { t } = useTranslation(); useHotkeys( ['ctrl+enter', 'meta+enter'], - () => { - dispatch(clampSymmetrySteps()); - dispatch(generateImage(activeTabName)); - }, + handleInvoke, { enabled: () => isReady, preventDefault: true, @@ -53,7 +50,7 @@ export default function InvokeButton(props: InvokeButton) { type="submit" icon={} isDisabled={!isReady} - onClick={handleClickGenerate} + onClick={handleInvoke} flexGrow={1} w="100%" tooltip={t('parameters.invoke')} @@ -66,7 +63,7 @@ export default function InvokeButton(props: InvokeButton) { aria-label={t('parameters.invoke')} type="submit" isDisabled={!isReady} - onClick={handleClickGenerate} + onClick={handleInvoke} flexGrow={1} w="100%" colorScheme="accent" diff --git a/invokeai/frontend/web/src/features/parameters/components/PromptInput/PromptInput.tsx b/invokeai/frontend/web/src/features/parameters/components/PromptInput/PromptInput.tsx index 13095ffefa..b54106733c 100644 --- a/invokeai/frontend/web/src/features/parameters/components/PromptInput/PromptInput.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/PromptInput/PromptInput.tsx @@ -1,5 +1,4 @@ import { Box, FormControl, Textarea } from '@chakra-ui/react'; -import { generateImage } from 'app/socketio/actions'; import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { ChangeEvent, KeyboardEvent, useRef } from 'react'; @@ -8,6 +7,7 @@ import { createSelector } from '@reduxjs/toolkit'; import { readinessSelector } from 'app/selectors/readinessSelector'; import { GenerationState, + clampSymmetrySteps, setPrompt, } from 'features/parameters/store/generationSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; @@ -15,6 +15,7 @@ import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { isEqual } from 'lodash-es'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; +import { generateGraphBuilt } from 'services/thunks/session'; const promptInputSelector = createSelector( [(state: RootState) => state.generation, activeTabNameSelector], @@ -36,7 +37,7 @@ const promptInputSelector = createSelector( */ const PromptInput = () => { const dispatch = useAppDispatch(); - const { prompt, activeTabName } = useAppSelector(promptInputSelector); + const { prompt } = useAppSelector(promptInputSelector); const { isReady } = useAppSelector(readinessSelector); const promptRef = useRef(null); @@ -58,7 +59,8 @@ const PromptInput = () => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Enter' && e.shiftKey === false && isReady) { e.preventDefault(); - dispatch(generateImage(activeTabName)); + dispatch(clampSymmetrySteps()); + dispatch(generateGraphBuilt()); } }; diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts new file mode 100644 index 0000000000..ac4aa83098 --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts @@ -0,0 +1,130 @@ +import { useToast } from '@chakra-ui/react'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { isFinite, isString } from 'lodash-es'; +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import useSetBothPrompts from './usePrompt'; +import { initialImageSelected, setSeed } from '../store/generationSlice'; +import { isImage, isImageField } from 'services/types/guards'; +import { NUMPY_RAND_MAX } from 'app/constants'; + +export const useParameters = () => { + const dispatch = useAppDispatch(); + const toast = useToast(); + const { t } = useTranslation(); + const setBothPrompts = useSetBothPrompts(); + + /** + * Sets prompt with toast + */ + const recallPrompt = useCallback( + (prompt: unknown) => { + if (!isString(prompt)) { + toast({ + title: t('toast.promptNotSet'), + description: t('toast.promptNotSetDesc'), + status: 'warning', + duration: 2500, + isClosable: true, + }); + return; + } + + setBothPrompts(prompt); + toast({ + title: t('toast.promptSet'), + status: 'info', + duration: 2500, + isClosable: true, + }); + }, + [t, toast, setBothPrompts] + ); + + /** + * Sets seed with toast + */ + const recallSeed = useCallback( + (seed: unknown) => { + const s = Number(seed); + if (!isFinite(s) || (isFinite(s) && !(s >= 0 && s <= NUMPY_RAND_MAX))) { + toast({ + title: t('toast.seedNotSet'), + description: t('toast.seedNotSetDesc'), + status: 'error', + duration: 2500, + isClosable: true, + }); + return; + } + + dispatch(setSeed(s)); + toast({ + title: t('toast.seedSet'), + status: 'success', + duration: 2500, + isClosable: true, + }); + }, + [t, toast, dispatch] + ); + + /** + * Sets initial image with toast + */ + const recallInitialImage = useCallback( + async (image: unknown) => { + if (!isImageField(image)) { + toast({ + title: t('toast.initialImageNotSet'), + description: t('toast.initialImageNotSetDesc'), + status: 'error', + duration: 2500, + isClosable: true, + }); + return; + } + + dispatch( + initialImageSelected({ name: image.image_name, type: image.image_type }) + ); + toast({ + title: t('toast.initialImageSet'), + status: 'success', + duration: 2500, + isClosable: true, + }); + }, + [t, toast, dispatch] + ); + + /** + * Sets image as initial image with toast + */ + const sendToImageToImage = useCallback( + (image: unknown) => { + if (!isImage(image)) { + toast({ + title: t('toast.imageNotLoaded'), + description: t('toast.imageNotLoadedDesc'), + status: 'error', + duration: 2500, + isClosable: true, + }); + return; + } + + dispatch(initialImageSelected({ name: image.name, type: image.type })); + + toast({ + title: t('toast.sentToImageToImage'), + status: 'success', + duration: 2500, + isClosable: true, + }); + }, + [t, toast, dispatch] + ); + + return { recallPrompt, recallSeed, recallInitialImage, sendToImageToImage }; +}; diff --git a/invokeai/frontend/web/src/features/system/components/ClearTempFolderButtonModal.tsx b/invokeai/frontend/web/src/features/system/components/ClearTempFolderButtonModal.tsx index 353eddc323..a220c93b3f 100644 --- a/invokeai/frontend/web/src/features/system/components/ClearTempFolderButtonModal.tsx +++ b/invokeai/frontend/web/src/features/system/components/ClearTempFolderButtonModal.tsx @@ -1,4 +1,4 @@ -import { emptyTempFolder } from 'app/socketio/actions'; +// import { emptyTempFolder } from 'app/socketio/actions'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIAlertDialog from 'common/components/IAIAlertDialog'; import IAIButton from 'common/components/IAIButton'; diff --git a/invokeai/frontend/web/src/features/system/components/ModelManager/AddCheckpointModel.tsx b/invokeai/frontend/web/src/features/system/components/ModelManager/AddCheckpointModel.tsx index 71d2b68a86..bb5db0302d 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelManager/AddCheckpointModel.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelManager/AddCheckpointModel.tsx @@ -17,7 +17,7 @@ import React from 'react'; import SearchModels from './SearchModels'; -import { addNewModel } from 'app/socketio/actions'; +// import { addNewModel } from 'app/socketio/actions'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; diff --git a/invokeai/frontend/web/src/features/system/components/ModelManager/AddDiffusersModel.tsx b/invokeai/frontend/web/src/features/system/components/ModelManager/AddDiffusersModel.tsx index 5a22472fc4..cb3af5f176 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelManager/AddDiffusersModel.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelManager/AddDiffusersModel.tsx @@ -8,7 +8,7 @@ import { VStack, } from '@chakra-ui/react'; import { InvokeDiffusersModelConfigProps } from 'app/types/invokeai'; -import { addNewModel } from 'app/socketio/actions'; +// import { addNewModel } from 'app/socketio/actions'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIButton from 'common/components/IAIButton'; import IAIInput from 'common/components/IAIInput'; diff --git a/invokeai/frontend/web/src/features/system/components/ModelManager/CheckpointModelEdit.tsx b/invokeai/frontend/web/src/features/system/components/ModelManager/CheckpointModelEdit.tsx index 3523e6fab7..b860a0848c 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelManager/CheckpointModelEdit.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelManager/CheckpointModelEdit.tsx @@ -17,7 +17,7 @@ import { VStack, } from '@chakra-ui/react'; -import { addNewModel } from 'app/socketio/actions'; +// import { addNewModel } from 'app/socketio/actions'; import { Field, Formik } from 'formik'; import { useTranslation } from 'react-i18next'; diff --git a/invokeai/frontend/web/src/features/system/components/ModelManager/DiffusersModelEdit.tsx b/invokeai/frontend/web/src/features/system/components/ModelManager/DiffusersModelEdit.tsx index f996d5a5d6..81998e4976 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelManager/DiffusersModelEdit.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelManager/DiffusersModelEdit.tsx @@ -9,7 +9,7 @@ import { systemSelector } from 'features/system/store/systemSelectors'; import { Flex, FormControl, FormLabel, Text, VStack } from '@chakra-ui/react'; -import { addNewModel } from 'app/socketio/actions'; +// import { addNewModel } from 'app/socketio/actions'; import { Field, Formik } from 'formik'; import { useTranslation } from 'react-i18next'; diff --git a/invokeai/frontend/web/src/features/system/components/ModelManager/MergeModels.tsx b/invokeai/frontend/web/src/features/system/components/ModelManager/MergeModels.tsx index 47e9277a59..6ba148cac4 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelManager/MergeModels.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelManager/MergeModels.tsx @@ -13,7 +13,7 @@ import { Tooltip, useDisclosure, } from '@chakra-ui/react'; -import { mergeDiffusersModels } from 'app/socketio/actions'; +// import { mergeDiffusersModels } from 'app/socketio/actions'; import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIButton from 'common/components/IAIButton'; diff --git a/invokeai/frontend/web/src/features/system/components/ModelManager/ModelConvert.tsx b/invokeai/frontend/web/src/features/system/components/ModelManager/ModelConvert.tsx index 3a5aa1264b..820ad546b3 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelManager/ModelConvert.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelManager/ModelConvert.tsx @@ -7,7 +7,7 @@ import { UnorderedList, Tooltip, } from '@chakra-ui/react'; -import { convertToDiffusers } from 'app/socketio/actions'; +// import { convertToDiffusers } from 'app/socketio/actions'; import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIAlertDialog from 'common/components/IAIAlertDialog'; diff --git a/invokeai/frontend/web/src/features/system/components/ModelManager/ModelListItem.tsx b/invokeai/frontend/web/src/features/system/components/ModelManager/ModelListItem.tsx index 47d139cc8f..aa9f87816c 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelManager/ModelListItem.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelManager/ModelListItem.tsx @@ -1,7 +1,7 @@ import { DeleteIcon, EditIcon } from '@chakra-ui/icons'; import { Box, Button, Flex, Spacer, Text, Tooltip } from '@chakra-ui/react'; import { ModelStatus } from 'app/types/invokeai'; -import { deleteModel, requestModelChange } from 'app/socketio/actions'; +// import { deleteModel, requestModelChange } from 'app/socketio/actions'; import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIAlertDialog from 'common/components/IAIAlertDialog'; diff --git a/invokeai/frontend/web/src/features/system/components/ModelManager/SearchModels.tsx b/invokeai/frontend/web/src/features/system/components/ModelManager/SearchModels.tsx index a7867efd5b..3a99997ac8 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelManager/SearchModels.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelManager/SearchModels.tsx @@ -20,7 +20,7 @@ import { useTranslation } from 'react-i18next'; import { FaSearch, FaTrash } from 'react-icons/fa'; -import { addNewModel, searchForModels } from 'app/socketio/actions'; +// import { addNewModel, searchForModels } from 'app/socketio/actions'; import { setFoundModels, setSearchFolder, diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx index a806ef262b..5cadae68be 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx @@ -1,7 +1,6 @@ import { ChakraProps, Flex, - Grid, Heading, Modal, ModalBody, @@ -14,22 +13,16 @@ import { useDisclosure, } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; -import { IN_PROGRESS_IMAGE_TYPES } from 'app/constants'; -import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIButton from 'common/components/IAIButton'; -import IAINumberInput from 'common/components/IAINumberInput'; import IAISelect from 'common/components/IAISelect'; import IAISwitch from 'common/components/IAISwitch'; import { systemSelector } from 'features/system/store/systemSelectors'; import { consoleLogLevelChanged, - InProgressImageType, setEnableImageDebugging, - setSaveIntermediatesInterval, setShouldConfirmOnDelete, setShouldDisplayGuides, - setShouldDisplayInProgressType, shouldLogToConsoleChanged, SystemState, } from 'features/system/store/systemSlice'; @@ -39,23 +32,19 @@ import { setShouldUseSliders, } from 'features/ui/store/uiSlice'; import { UIState } from 'features/ui/store/uiTypes'; -import { isEqual, map } from 'lodash-es'; +import { isEqual } from 'lodash-es'; import { persistor } from 'app/store/persistor'; import { ChangeEvent, cloneElement, ReactElement, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { InvokeLogLevel, VALID_LOG_LEVELS } from 'app/logging/useLogger'; +import { VALID_LOG_LEVELS } from 'app/logging/useLogger'; import { LogLevelName } from 'roarr'; -import { F } from 'ts-toolbelt'; const selector = createSelector( [systemSelector, uiSelector], (system: SystemState, ui: UIState) => { const { - shouldDisplayInProgressType, shouldConfirmOnDelete, shouldDisplayGuides, - model_list, - saveIntermediatesInterval, enableImageDebugging, consoleLogLevel, shouldLogToConsole, @@ -64,11 +53,8 @@ const selector = createSelector( const { shouldUseCanvasBetaLayout, shouldUseSliders } = ui; return { - shouldDisplayInProgressType, shouldConfirmOnDelete, shouldDisplayGuides, - models: map(model_list, (_model, key) => key), - saveIntermediatesInterval, enableImageDebugging, shouldUseCanvasBetaLayout, shouldUseSliders, @@ -104,8 +90,6 @@ const SettingsModal = ({ children }: SettingsModalProps) => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const steps = useAppSelector((state: RootState) => state.generation.steps); - const { isOpen: isSettingsModalOpen, onOpen: onSettingsModalOpen, @@ -119,10 +103,8 @@ const SettingsModal = ({ children }: SettingsModalProps) => { } = useDisclosure(); const { - shouldDisplayInProgressType, shouldConfirmOnDelete, shouldDisplayGuides, - saveIntermediatesInterval, enableImageDebugging, shouldUseCanvasBetaLayout, shouldUseSliders, @@ -134,18 +116,12 @@ const SettingsModal = ({ children }: SettingsModalProps) => { * Resets localstorage, then opens a secondary modal informing user to * refresh their browser. * */ - const handleClickResetWebUI = () => { + const handleClickResetWebUI = useCallback(() => { persistor.purge().then(() => { onSettingsModalClose(); onRefreshModalOpen(); }); - }; - - const handleChangeIntermediateSteps = (value: number) => { - if (value > steps) value = steps; - if (value < 1) value = 1; - dispatch(setSaveIntermediatesInterval(value)); - }; + }, [onSettingsModalClose, onRefreshModalOpen]); const handleLogLevelChanged = useCallback( (e: ChangeEvent) => { @@ -182,32 +158,6 @@ const SettingsModal = ({ children }: SettingsModalProps) => { {t('settings.general')} - ) => - dispatch( - setShouldDisplayInProgressType( - e.target.value as InProgressImageType - ) - ) - } - /> - {shouldDisplayInProgressType === 'full-res' && ( - - )} { + (system) => { return { isConnected: system.isConnected, isProcessing: system.isProcessing, currentIteration: system.currentIteration, totalIterations: system.totalIterations, currentStatus: system.currentStatus, - hasError: system.hasError, - wasErrorSeen: system.wasErrorSeen, }; }, { @@ -31,15 +28,12 @@ const StatusIndicator = () => { currentIteration, totalIterations, currentStatus, - hasError, - wasErrorSeen, } = useAppSelector(statusIndicatorSelector); - const dispatch = useAppDispatch(); const { t } = useTranslation(); let statusIdentifier; - if (isConnected && !hasError) { + if (isConnected) { statusIdentifier = 'ok'; } else { statusIdentifier = 'error'; @@ -60,34 +54,16 @@ const StatusIndicator = () => { } } - const tooltipLabel = - hasError && !wasErrorSeen - ? 'Click to clear, check logs for details' - : undefined; - - const statusIndicatorCursor = - hasError && !wasErrorSeen ? 'pointer' : 'initial'; - - const handleClickStatusIndicator = () => { - if (hasError || !wasErrorSeen) { - dispatch(errorSeen()); - } - }; - return ( - - - {t(statusMessage as keyof typeof t)} - - + + {t(statusMessage as keyof typeof t)} + ); }; diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index 84176bd096..6773314b6a 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -23,33 +23,14 @@ import { parsedOpenAPISchema } from 'features/nodes/store/nodesSlice'; import { LogLevelName } from 'roarr'; import { InvokeLogLevel } from 'app/logging/useLogger'; -export type LogLevel = 'info' | 'warning' | 'error'; - -export interface LogEntry { - timestamp: string; - level: LogLevel; - message: string; -} - -export interface Log { - [index: number]: LogEntry; -} - -export type InProgressImageType = 'none' | 'full-res' | 'latents'; - export type CancelType = 'immediate' | 'scheduled'; -export interface SystemState - extends InvokeAI.SystemStatus, - InvokeAI.SystemConfig { - shouldDisplayInProgressType: InProgressImageType; - shouldShowLogViewer: boolean; +export interface SystemState { isGFPGANAvailable: boolean; isESRGANAvailable: boolean; isConnected: boolean; - socketId: string; + isProcessing: boolean; shouldConfirmOnDelete: boolean; - openAccordions: ExpandedIndex; currentStep: number; totalSteps: number; currentIteration: number; @@ -57,18 +38,12 @@ export interface SystemState currentStatus: string; currentStatusHasSteps: boolean; shouldDisplayGuides: boolean; - wasErrorSeen: boolean; isCancelable: boolean; - saveIntermediatesInterval: number; enableImageDebugging: boolean; toastQueue: UseToastOptions[]; searchFolder: string | null; foundModels: InvokeAI.FoundModel[] | null; openModel: string | null; - cancelOptions: { - cancelType: CancelType; - cancelAfter: number | null; - }; /** * The current progress image */ @@ -107,14 +82,10 @@ export interface SystemState const initialSystemState: SystemState = { isConnected: false, isProcessing: false, - shouldShowLogViewer: false, - shouldDisplayInProgressType: 'latents', shouldDisplayGuides: true, isGFPGANAvailable: true, isESRGANAvailable: true, - socketId: '', shouldConfirmOnDelete: true, - openAccordions: [0], currentStep: 0, totalSteps: 0, currentIteration: 0, @@ -123,26 +94,12 @@ const initialSystemState: SystemState = { ? i18n.t('common.statusDisconnected') : 'Disconnected', currentStatusHasSteps: false, - model: '', - model_id: '', - model_hash: '', - app_id: '', - app_version: '', - model_list: {}, - infill_methods: [], - hasError: false, - wasErrorSeen: true, isCancelable: true, - saveIntermediatesInterval: 5, enableImageDebugging: false, toastQueue: [], searchFolder: null, foundModels: null, openModel: null, - cancelOptions: { - cancelType: 'immediate', - cancelAfter: null, - }, progressImage: null, sessionId: null, cancelType: 'immediate', @@ -158,23 +115,13 @@ export const systemSlice = createSlice({ name: 'system', initialState: initialSystemState, reducers: { - setShouldDisplayInProgressType: ( - state, - action: PayloadAction - ) => { - state.shouldDisplayInProgressType = action.payload; - }, setIsProcessing: (state, action: PayloadAction) => { state.isProcessing = action.payload; }, setCurrentStatus: (state, action: PayloadAction) => { state.currentStatus = action.payload; }, - setSystemStatus: (state, action: PayloadAction) => { - return { ...state, ...action.payload }; - }, errorOccurred: (state) => { - state.hasError = true; state.isProcessing = false; state.isCancelable = true; state.currentStep = 0; @@ -183,17 +130,6 @@ export const systemSlice = createSlice({ state.totalIterations = 0; state.currentStatusHasSteps = false; state.currentStatus = i18n.t('common.statusError'); - state.wasErrorSeen = false; - }, - errorSeen: (state) => { - state.hasError = false; - state.wasErrorSeen = true; - state.currentStatus = state.isConnected - ? i18n.t('common.statusConnected') - : i18n.t('common.statusDisconnected'); - }, - setShouldShowLogViewer: (state, action: PayloadAction) => { - state.shouldShowLogViewer = action.payload; }, setIsConnected: (state, action: PayloadAction) => { state.isConnected = action.payload; @@ -204,23 +140,10 @@ export const systemSlice = createSlice({ state.currentIteration = 0; state.totalIterations = 0; state.currentStatusHasSteps = false; - state.hasError = false; - }, - setSocketId: (state, action: PayloadAction) => { - state.socketId = action.payload; }, setShouldConfirmOnDelete: (state, action: PayloadAction) => { state.shouldConfirmOnDelete = action.payload; }, - setOpenAccordions: (state, action: PayloadAction) => { - state.openAccordions = action.payload; - }, - setSystemConfig: (state, action: PayloadAction) => { - return { - ...state, - ...action.payload, - }; - }, setShouldDisplayGuides: (state, action: PayloadAction) => { state.shouldDisplayGuides = action.payload; }, @@ -244,12 +167,6 @@ export const systemSlice = createSlice({ state.currentStatusHasSteps = false; state.currentStatus = i18n.t('common.statusPreparing'); }, - setModelList: ( - state, - action: PayloadAction> - ) => { - state.model_list = action.payload; - }, setIsCancelable: (state, action: PayloadAction) => { state.isCancelable = action.payload; }, @@ -271,9 +188,6 @@ export const systemSlice = createSlice({ state.isProcessing = true; state.currentStatusHasSteps = false; }, - setSaveIntermediatesInterval: (state, action: PayloadAction) => { - state.saveIntermediatesInterval = action.payload; - }, setEnableImageDebugging: (state, action: PayloadAction) => { state.enableImageDebugging = action.payload; }, @@ -300,12 +214,6 @@ export const systemSlice = createSlice({ setOpenModel: (state, action: PayloadAction) => { state.openModel = action.payload; }, - setCancelType: (state, action: PayloadAction) => { - state.cancelOptions.cancelType = action.payload; - }, - setCancelAfter: (state, action: PayloadAction) => { - state.cancelOptions.cancelAfter = action.payload; - }, /** * A cancel was scheduled */ @@ -420,7 +328,6 @@ export const systemSlice = createSlice({ builder.addCase(invocationError, (state, action) => { const { data, timestamp } = action.payload; - state.wasErrorSeen = true; state.progressImage = null; state.isProcessing = false; @@ -479,26 +386,17 @@ export const systemSlice = createSlice({ }); export const { - setShouldDisplayInProgressType, setIsProcessing, - setShouldShowLogViewer, setIsConnected, - setSocketId, setShouldConfirmOnDelete, - setOpenAccordions, - setSystemStatus, setCurrentStatus, - setSystemConfig, setShouldDisplayGuides, processingCanceled, errorOccurred, - errorSeen, - setModelList, setIsCancelable, modelChangeRequested, modelConvertRequested, modelMergingRequested, - setSaveIntermediatesInterval, setEnableImageDebugging, generationRequested, addToast, @@ -507,8 +405,6 @@ export const { setSearchFolder, setFoundModels, setOpenModel, - setCancelType, - setCancelAfter, cancelScheduled, scheduledCancelAborted, cancelTypeChanged, diff --git a/invokeai/frontend/web/src/services/types/guards.ts b/invokeai/frontend/web/src/services/types/guards.ts index 5a9d891395..72cf1108fb 100644 --- a/invokeai/frontend/web/src/services/types/guards.ts +++ b/invokeai/frontend/web/src/services/types/guards.ts @@ -1,3 +1,5 @@ +import { Image } from 'app/types/invokeai'; +import { get, isObject, isString } from 'lodash-es'; import { GraphExecutionState, GraphInvocationOutput, @@ -6,6 +8,8 @@ import { PromptOutput, IterateInvocationOutput, CollectInvocationOutput, + ImageType, + ImageField, } from 'services/api'; export const isImageOutput = ( @@ -31,3 +35,16 @@ export const isIterateOutput = ( export const isCollectOutput = ( output: GraphExecutionState['results'][string] ): output is CollectInvocationOutput => output.type === 'collect_output'; + +export const isImageType = (t: unknown): t is ImageType => + isString(t) && ['results', 'uploads', 'intermediates'].includes(t); + +export const isImage = (image: unknown): image is Image => + isObject(image) && + isString(get(image, 'name')) && + isImageType(get(image, 'type')); + +export const isImageField = (imageField: unknown): imageField is ImageField => + isObject(imageField) && + isString(get(imageField, 'image_name')) && + isImageType(get(imageField, 'image_type'));