diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts index 3f81400fa7..dfcca1a4a4 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts @@ -1,6 +1,13 @@ import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; -import { ipaImageChanged, rgIPAdapterImageChanged } from 'features/controlLayers/store/canvasSlice'; +import { + entityRasterized, + entitySelected, + ipaImageChanged, + rgIPAdapterImageChanged, +} from 'features/controlLayers/store/canvasSlice'; +import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; +import { imageDTOToImageObject } from 'features/controlLayers/store/types'; import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors'; import { boardIdSelected, galleryViewChanged } from 'features/gallery/store/gallerySlice'; import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice'; @@ -114,6 +121,17 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis toast({ ...DEFAULT_UPLOADED_TOAST, description: `${t('toast.setNodeField')} ${fieldName}` }); return; } + + if (postUploadAction?.type === 'REPLACE_LAYER_WITH_IMAGE') { + const { entityIdentifier } = postUploadAction; + + const state = getState(); + const imageObject = imageDTOToImageObject(imageDTO); + const { x, y } = selectCanvasSlice(state).bbox.rect; + dispatch(entityRasterized({ entityIdentifier, imageObject, position: { x, y }, replaceObjects: true })); + dispatch(entitySelected({ entityIdentifier })); + return; + } }, }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapter.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapter.tsx index cd119b83e0..eda72bd55a 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapter.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapter.tsx @@ -1,6 +1,7 @@ import { Flex, IconButton } from '@invoke-ai/ui-library'; import { createMemoizedAppSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { useImageUploadButton } from 'common/hooks/useImageUploadButton'; import { BeginEndStepPct } from 'features/controlLayers/components/common/BeginEndStepPct'; import { Weight } from 'features/controlLayers/components/common/Weight'; import { ControlLayerControlAdapterControlMode } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapterControlMode'; @@ -18,8 +19,8 @@ import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/s import type { CanvasEntityIdentifier, ControlModeV2 } from 'features/controlLayers/store/types'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { PiBoundingBoxBold, PiShootingStarBold } from 'react-icons/pi'; -import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types'; +import { PiBoundingBoxBold, PiShootingStarBold, PiUploadBold } from 'react-icons/pi'; +import type { ControlNetModelConfig, PostUploadAction, T2IAdapterModelConfig } from 'services/api/types'; const useControlLayerControlAdapter = (entityIdentifier: CanvasEntityIdentifier<'control_layer'>) => { const selectControlAdapter = useMemo( @@ -71,6 +72,11 @@ export const ControlLayerControlAdapter = memo(() => { const pullBboxIntoLayer = usePullBboxIntoLayer(entityIdentifier); const isSaving = useIsSavingCanvas(); + const postUploadAction = useMemo( + () => ({ type: 'REPLACE_LAYER_WITH_IMAGE', entityIdentifier }), + [entityIdentifier] + ); + const uploadApi = useImageUploadButton({ postUploadAction }); return ( @@ -79,7 +85,9 @@ export const ControlLayerControlAdapter = memo(() => { } @@ -87,11 +95,24 @@ export const ControlLayerControlAdapter = memo(() => { } /> + } + {...uploadApi.getUploadButtonProps()} + /> + diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts index 357fc52b12..aea56817c1 100644 --- a/invokeai/frontend/web/src/services/api/types.ts +++ b/invokeai/frontend/web/src/services/api/types.ts @@ -1,3 +1,4 @@ +import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types'; import type { components, paths } from 'services/api/schema'; import type { O } from 'ts-toolbelt'; @@ -219,10 +220,16 @@ type AddToBatchAction = { type: 'ADD_TO_BATCH'; }; +type ReplaceLayerWithImagePostUploadAction = { + type: 'REPLACE_LAYER_WITH_IMAGE'; + entityIdentifier: CanvasEntityIdentifier<'control_layer' | 'raster_layer'>; +}; + export type PostUploadAction = | NodesAction | ToastAction | AddToBatchAction | IPALayerImagePostUploadAction | RGIPAdapterImagePostUploadAction - | UpscaleInitialImageAction; + | UpscaleInitialImageAction + | ReplaceLayerWithImagePostUploadAction;