From 9e6fb3bd3fea58dcc8072730ef511b9f16d8ec57 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:17:16 +1000 Subject: [PATCH] feat(ui): add hooks for new layer/canvas from image & use them --- .../controlLayers/hooks/addLayerHooks.ts | 50 +++++++++++++++++-- .../ImageMenuItemNewCanvasFromImage.tsx | 27 +++------- .../ImageMenuItemNewLayerFromImage.tsx | 25 +++------- 3 files changed, 61 insertions(+), 41 deletions(-) diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts index 382c86c991..db58750fd4 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts @@ -3,6 +3,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { deepClone } from 'common/util/deepClone'; import { getPrefixedId } from 'features/controlLayers/konva/util'; +import { canvasReset } from 'features/controlLayers/store/actions'; import { controlLayerAdded, inpaintMaskAdded, @@ -14,19 +15,25 @@ import { rgPositivePromptChanged, } from 'features/controlLayers/store/canvasSlice'; import { selectBase } from 'features/controlLayers/store/paramsSlice'; -import { selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors'; +import { selectBboxRect, selectCanvasSlice, selectEntityOrThrow } from 'features/controlLayers/store/selectors'; import type { CanvasEntityIdentifier, + CanvasRasterLayerState, CanvasRegionalGuidanceState, ControlNetConfig, IPAdapterConfig, T2IAdapterConfig, } from 'features/controlLayers/store/types'; -import { initialControlNet, initialIPAdapter, initialT2IAdapter } from 'features/controlLayers/store/util'; +import { + imageDTOToImageObject, + initialControlNet, + initialIPAdapter, + initialT2IAdapter, +} from 'features/controlLayers/store/util'; import { zModelIdentifierField } from 'features/nodes/types/common'; import { useCallback } from 'react'; import { modelConfigsAdapterSelectors, selectModelConfigsQuery } from 'services/api/endpoints/models'; -import type { ControlNetModelConfig, IPAdapterModelConfig, T2IAdapterModelConfig } from 'services/api/types'; +import type { ControlNetModelConfig, ImageDTO, IPAdapterModelConfig, T2IAdapterModelConfig } from 'services/api/types'; import { isControlNetOrT2IAdapterModelConfig, isIPAdapterModelConfig } from 'services/api/types'; export const selectDefaultControlAdapter = createSelector( @@ -90,6 +97,43 @@ export const useAddRasterLayer = () => { return func; }; +export const useNewRasterLayerFromImage = () => { + const dispatch = useAppDispatch(); + const bboxRect = useAppSelector(selectBboxRect); + const func = useCallback( + (imageDTO: ImageDTO) => { + const imageObject = imageDTOToImageObject(imageDTO); + const overrides: Partial = { + position: { x: bboxRect.x, y: bboxRect.y }, + objects: [imageObject], + }; + dispatch(rasterLayerAdded({ overrides, isSelected: true })); + }, + [bboxRect.x, bboxRect.y, dispatch] + ); + + return func; +}; + +export const useNewCanvasFromImage = () => { + const dispatch = useAppDispatch(); + const bboxRect = useAppSelector(selectBboxRect); + const func = useCallback( + (imageDTO: ImageDTO) => { + const imageObject = imageDTOToImageObject(imageDTO); + const overrides: Partial = { + position: { x: bboxRect.x, y: bboxRect.y }, + objects: [imageObject], + }; + dispatch(canvasReset()); + dispatch(rasterLayerAdded({ overrides, isSelected: true })); + }, + [bboxRect.x, bboxRect.y, dispatch] + ); + + return func; +}; + export const useAddInpaintMask = () => { const dispatch = useAppDispatch(); const func = useCallback(() => { diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewCanvasFromImage.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewCanvasFromImage.tsx index 39279c870e..d61af13650 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewCanvasFromImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewCanvasFromImage.tsx @@ -1,11 +1,6 @@ import { MenuItem } from '@invoke-ai/ui-library'; -import { createSelector } from '@reduxjs/toolkit'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { canvasReset } from 'features/controlLayers/store/actions'; -import { rasterLayerAdded } from 'features/controlLayers/store/canvasSlice'; -import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; -import type { CanvasRasterLayerState } from 'features/controlLayers/store/types'; -import { imageDTOToImageObject } from 'features/controlLayers/store/util'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { useNewCanvasFromImage } from 'features/controlLayers/hooks/addLayerHooks'; import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer'; import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext'; import { toast } from 'features/toast/toast'; @@ -14,23 +9,15 @@ import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { PiFileBold } from 'react-icons/pi'; -const selectBboxRect = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.rect); - export const ImageMenuItemNewCanvasFromImage = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const imageDTO = useImageDTOContext(); - const bboxRect = useAppSelector(selectBboxRect); const imageViewer = useImageViewer(); + const newCanvasFromImage = useNewCanvasFromImage(); - const handleSendToCanvas = useCallback(() => { - const imageObject = imageDTOToImageObject(imageDTO); - const overrides: Partial = { - position: { x: bboxRect.x, y: bboxRect.y }, - objects: [imageObject], - }; - dispatch(canvasReset()); - dispatch(rasterLayerAdded({ overrides, isSelected: true })); + const onClick = useCallback(() => { + newCanvasFromImage(imageDTO); dispatch(setActiveTab('canvas')); imageViewer.close(); toast({ @@ -38,10 +25,10 @@ export const ImageMenuItemNewCanvasFromImage = memo(() => { title: t('toast.sentToCanvas'), status: 'success', }); - }, [bboxRect.x, bboxRect.y, dispatch, imageDTO, imageViewer, t]); + }, [dispatch, imageDTO, imageViewer, newCanvasFromImage, t]); return ( - } onClickCapture={handleSendToCanvas}> + } onClickCapture={onClick}> {t('controlLayers.newCanvasFromImage')} ); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewLayerFromImage.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewLayerFromImage.tsx index 8594db8b50..c8a0dc449d 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewLayerFromImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewLayerFromImage.tsx @@ -1,11 +1,7 @@ import { MenuItem } from '@invoke-ai/ui-library'; -import { createSelector } from '@reduxjs/toolkit'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { useAppDispatch } from 'app/store/storeHooks'; import { NewLayerIcon } from 'features/controlLayers/components/common/icons'; -import { rasterLayerAdded } from 'features/controlLayers/store/canvasSlice'; -import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; -import type { CanvasRasterLayerState } from 'features/controlLayers/store/types'; -import { imageDTOToImageObject } from 'features/controlLayers/store/util'; +import { useNewRasterLayerFromImage } from 'features/controlLayers/hooks/addLayerHooks'; import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer'; import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext'; import { sentImageToCanvas } from 'features/gallery/store/actions'; @@ -14,23 +10,16 @@ import { setActiveTab } from 'features/ui/store/uiSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selectBboxRect = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.rect); - export const ImageMenuItemNewLayerFromImage = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const imageDTO = useImageDTOContext(); - const bboxRect = useAppSelector(selectBboxRect); const imageViewer = useImageViewer(); + const newRasterLayerFromImage = useNewRasterLayerFromImage(); - const handleSendToCanvas = useCallback(() => { - const imageObject = imageDTOToImageObject(imageDTO); - const overrides: Partial = { - position: { x: bboxRect.x, y: bboxRect.y }, - objects: [imageObject], - }; + const onClick = useCallback(() => { dispatch(sentImageToCanvas()); - dispatch(rasterLayerAdded({ overrides, isSelected: true })); + newRasterLayerFromImage(imageDTO); dispatch(setActiveTab('canvas')); imageViewer.close(); toast({ @@ -38,10 +27,10 @@ export const ImageMenuItemNewLayerFromImage = memo(() => { title: t('toast.sentToCanvas'), status: 'success', }); - }, [bboxRect.x, bboxRect.y, dispatch, imageDTO, imageViewer, t]); + }, [dispatch, imageDTO, imageViewer, newRasterLayerFromImage, t]); return ( - } onClickCapture={handleSendToCanvas}> + } onClickCapture={onClick}> {t('controlLayers.newLayerFromImage')} );