diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json index a41a4781b8..d9b44793bb 100644 --- a/invokeai/frontend/web/package.json +++ b/invokeai/frontend/web/package.json @@ -167,10 +167,5 @@ "vite-plugin-dts": "^3.7.0", "vite-plugin-eslint": "^1.8.1", "vite-tsconfig-paths": "^4.2.3" - }, - "pnpm": { - "patchedDependencies": { - "reselect@5.0.1": "patches/reselect@5.0.1.patch" - } } } diff --git a/invokeai/frontend/web/pnpm-lock.yaml b/invokeai/frontend/web/pnpm-lock.yaml index 6cdbe20700..703a12542f 100644 --- a/invokeai/frontend/web/pnpm-lock.yaml +++ b/invokeai/frontend/web/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - reselect@5.0.1: - hash: kvbgwzjyy4x4fnh7znyocvb75q - path: patches/reselect@5.0.1.patch - dependencies: '@chakra-ui/anatomy': specifier: ^2.2.2 @@ -4570,7 +4565,7 @@ packages: react-redux: 9.0.4(@types/react@18.2.46)(react@18.2.0)(redux@5.0.1) redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) - reselect: 5.0.1(patch_hash=kvbgwzjyy4x4fnh7znyocvb75q) + reselect: 5.0.1 dev: false /@roarr/browser-log-writer@1.3.0: @@ -11937,10 +11932,9 @@ packages: hasBin: true dev: true - /reselect@5.0.1(patch_hash=kvbgwzjyy4x4fnh7znyocvb75q): + /reselect@5.0.1: resolution: {integrity: sha512-D72j2ubjgHpvuCiORWkOUxndHJrxDaSolheiz5CO+roz8ka97/4msh2E8F5qay4GawR5vzBt5MkbDHT+Rdy/Wg==} dev: false - patched: true /resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} diff --git a/invokeai/frontend/web/src/app/components/Toaster.ts b/invokeai/frontend/web/src/app/components/Toaster.ts index 1ba47bdc8f..ad10f58a5e 100644 --- a/invokeai/frontend/web/src/app/components/Toaster.ts +++ b/invokeai/frontend/web/src/app/components/Toaster.ts @@ -11,7 +11,7 @@ import { memo, useCallback, useEffect } from 'react'; */ const Toaster = () => { const dispatch = useAppDispatch(); - const toastQueue = useAppSelector((state) => state.system.toastQueue); + const toastQueue = useAppSelector((s) => s.system.toastQueue); const toast = useToast(); useEffect(() => { toastQueue.forEach((t) => { diff --git a/invokeai/frontend/web/src/app/logging/useLogger.ts b/invokeai/frontend/web/src/app/logging/useLogger.ts index 5028935514..653822987a 100644 --- a/invokeai/frontend/web/src/app/logging/useLogger.ts +++ b/invokeai/frontend/web/src/app/logging/useLogger.ts @@ -1,6 +1,6 @@ import { createLogWriter } from '@roarr/browser-log-writer'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; +import type { RootState} from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { useEffect, useMemo } from 'react'; import { ROARR, Roarr } from 'roarr'; @@ -8,14 +8,17 @@ import { ROARR, Roarr } from 'roarr'; import type { LoggerNamespace } from './logger'; import { $logger, BASE_CONTEXT, LOG_LEVEL_MAP, logger } from './logger'; -const selector = createMemoizedSelector(stateSelector, ({ system }) => { - const { consoleLogLevel, shouldLogToConsole } = system; +const selector = createMemoizedSelector( + (state: RootState) => state.system, + (system) => { + const { consoleLogLevel, shouldLogToConsole } = system; - return { - consoleLogLevel, - shouldLogToConsole, - }; -}); + return { + consoleLogLevel, + shouldLogToConsole, + }; + } +); export const useLogger = (namespace: LoggerNamespace) => { const { consoleLogLevel, shouldLogToConsole } = useAppSelector(selector); diff --git a/invokeai/frontend/web/src/app/store/createMemoizedSelector.ts b/invokeai/frontend/web/src/app/store/createMemoizedSelector.ts index b4d46a4ef8..04dcf9d889 100644 --- a/invokeai/frontend/web/src/app/store/createMemoizedSelector.ts +++ b/invokeai/frontend/web/src/app/store/createMemoizedSelector.ts @@ -1,33 +1,32 @@ import { createDraftSafeSelectorCreator, createSelectorCreator, - lruMemoize, + weakMapMemoize, } from '@reduxjs/toolkit'; import type { GetSelectorsOptions } from '@reduxjs/toolkit/dist/entities/state_selectors'; -import { isEqual } from 'lodash-es'; /** * A memoized selector creator that uses LRU cache and lodash's isEqual for equality check. */ export const createMemoizedSelector = createSelectorCreator({ - memoize: lruMemoize, - memoizeOptions: { - resultEqualityCheck: isEqual, - }, - argsMemoize: lruMemoize, + memoize: weakMapMemoize, + // memoizeOptions: { + // resultEqualityCheck: isEqual, + // }, + argsMemoize: weakMapMemoize, }); /** * A memoized selector creator that uses LRU cache default shallow equality check. */ export const createLruSelector = createSelectorCreator({ - memoize: lruMemoize, - argsMemoize: lruMemoize, + memoize: weakMapMemoize, + argsMemoize: weakMapMemoize, }); export const createLruDraftSafeSelector = createDraftSafeSelectorCreator({ - memoize: lruMemoize, - argsMemoize: lruMemoize, + memoize: weakMapMemoize, + argsMemoize: weakMapMemoize, }); export const getSelectorsOptions: GetSelectorsOptions = { diff --git a/invokeai/frontend/web/src/app/store/enhancers/reduxRemember/unserialize.ts b/invokeai/frontend/web/src/app/store/enhancers/reduxRemember/unserialize.ts index 247961fed9..01f993382b 100644 --- a/invokeai/frontend/web/src/app/store/enhancers/reduxRemember/unserialize.ts +++ b/invokeai/frontend/web/src/app/store/enhancers/reduxRemember/unserialize.ts @@ -8,7 +8,6 @@ import { initialPostprocessingState } from 'features/parameters/store/postproces import { initialSDXLState } from 'features/sdxl/store/sdxlSlice'; import { initialConfigState } from 'features/system/store/configSlice'; import { initialSystemState } from 'features/system/store/systemSlice'; -import { initialHotkeysState } from 'features/ui/store/hotkeysSlice'; import { initialUIState } from 'features/ui/store/uiSlice'; import { defaultsDeep } from 'lodash-es'; import type { UnserializeFunction } from 'redux-remember'; @@ -24,7 +23,6 @@ const initialStates: { system: initialSystemState, config: initialConfigState, ui: initialUIState, - hotkeys: initialHotkeysState, controlAdapters: initialControlAdapterState, dynamicPrompts: initialDynamicPromptsState, sdxl: initialSDXLState, diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts index 19346f5acd..83fab1df54 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts @@ -20,9 +20,15 @@ export const addDeleteBoardAndImagesFulfilledListener = () => { let wasNodeEditorReset = false; let wereControlAdaptersReset = false; - const state = getState(); + const { generation, canvas, nodes, controlAdapters } = getState(); deleted_images.forEach((image_name) => { - const imageUsage = getImageUsage(state, image_name); + const imageUsage = getImageUsage( + generation, + canvas, + nodes, + controlAdapters, + image_name + ); if (imageUsage.isInitialImage && !wasInitialImageReset) { dispatch(clearInitialImage()); diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index e7540e25b4..6b85dbfcc0 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -151,6 +151,3 @@ export type RootState = ReturnType['getState']>; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type AppThunkDispatch = ThunkDispatch; export type AppDispatch = ReturnType['dispatch']; -export function stateSelector(state: RootState) { - return state; -} diff --git a/invokeai/frontend/web/src/common/components/IAIInformationalPopover/IAIInformationalPopover.tsx b/invokeai/frontend/web/src/common/components/IAIInformationalPopover/IAIInformationalPopover.tsx index d57838678c..e435d550d6 100644 --- a/invokeai/frontend/web/src/common/components/IAIInformationalPopover/IAIInformationalPopover.tsx +++ b/invokeai/frontend/web/src/common/components/IAIInformationalPopover/IAIInformationalPopover.tsx @@ -32,7 +32,7 @@ const IAIInformationalPopover = ({ ...rest }: Props) => { const shouldEnableInformationalPopovers = useAppSelector( - (state) => state.system.shouldEnableInformationalPopovers + (s) => s.system.shouldEnableInformationalPopovers ); const data = useMemo(() => POPOVER_DATA[feature], [feature]); diff --git a/invokeai/frontend/web/src/common/components/InvControl/InvLabel.tsx b/invokeai/frontend/web/src/common/components/InvControl/InvLabel.tsx index cad37de589..e8ec3e72e3 100644 --- a/invokeai/frontend/web/src/common/components/InvControl/InvLabel.tsx +++ b/invokeai/frontend/web/src/common/components/InvControl/InvLabel.tsx @@ -13,7 +13,7 @@ export const InvLabel = memo( ref ) => { const shouldEnableInformationalPopovers = useAppSelector( - (state) => state.system.shouldEnableInformationalPopovers + (s) => s.system.shouldEnableInformationalPopovers ); const ctx = useContext(InvControlGroupContext); diff --git a/invokeai/frontend/web/src/common/components/InvSelect/useGroupedModelInvSelect.ts b/invokeai/frontend/web/src/common/components/InvSelect/useGroupedModelInvSelect.ts index c673b091d2..708906f495 100644 --- a/invokeai/frontend/web/src/common/components/InvSelect/useGroupedModelInvSelect.ts +++ b/invokeai/frontend/web/src/common/components/InvSelect/useGroupedModelInvSelect.ts @@ -30,7 +30,7 @@ export const useGroupedModelInvSelect = ( ): UseGroupedModelInvSelectReturn => { const { t } = useTranslation(); const base_model = useAppSelector( - (state) => state.generation.model?.base_model ?? 'sdxl' + (s) => s.generation.model?.base_model ?? 'sdxl' ); const { modelEntities, selectedModel, getIsDisabled, onChange, isLoading } = arg; diff --git a/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts b/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts index 3ae97f2416..cac7967279 100644 --- a/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts +++ b/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts @@ -1,6 +1,6 @@ import { useAppToaster } from 'app/components/Toaster'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; +import type { RootState } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { useCallback, useEffect, useState } from 'react'; @@ -16,8 +16,8 @@ const accept: Accept = { }; const selector = createMemoizedSelector( - [stateSelector, activeTabNameSelector], - ({ gallery }, activeTabName) => { + [(state: RootState) => state.gallery, activeTabNameSelector], + (gallery, activeTabName) => { let postUploadAction: PostUploadAction = { type: 'TOAST' }; if (activeTabName === 'unifiedCanvas') { diff --git a/invokeai/frontend/web/src/common/hooks/useImageUploadButton.tsx b/invokeai/frontend/web/src/common/hooks/useImageUploadButton.tsx index fd362d886b..45c8ab7dbe 100644 --- a/invokeai/frontend/web/src/common/hooks/useImageUploadButton.tsx +++ b/invokeai/frontend/web/src/common/hooks/useImageUploadButton.tsx @@ -33,7 +33,7 @@ export const useImageUploadButton = ({ isDisabled, }: UseImageUploadButtonArgs) => { const autoAddBoardId = useAppSelector( - (state) => state.gallery.autoAddBoardId + (s) => s.gallery.autoAddBoardId ); const [uploadImage] = useUploadImageMutation(); const onDropAccepted = useCallback( diff --git a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts index 97b3190e3e..27eceab1a9 100644 --- a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts +++ b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts @@ -1,5 +1,5 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; +import type { RootState } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { selectControlAdapterAll } from 'features/controlAdapters/store/controlAdaptersSlice'; import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types'; @@ -11,16 +11,22 @@ import { forEach } from 'lodash-es'; import { getConnectedEdges } from 'reactflow'; const selector = createMemoizedSelector( - [stateSelector, activeTabNameSelector], + [ + (state: RootState) => state.controlAdapters, + (state: RootState) => state.generation, + (state: RootState) => state.system, + (state: RootState) => state.nodes, + (state: RootState) => state.nodeTemplates, + (state: RootState) => state.dynamicPrompts, + activeTabNameSelector, + ], ( - { - controlAdapters, - generation, - system, - nodes, - nodeTemplates, - dynamicPrompts, - }, + controlAdapters, + generation, + system, + nodes, + nodeTemplates, + dynamicPrompts, activeTabName ) => { const { initialImage, model, positivePrompt } = generation; diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvas.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvas.tsx index d3629bea2a..30ebc073f7 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvas.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvas.tsx @@ -1,7 +1,6 @@ import { Box, chakra, Flex } from '@chakra-ui/react'; import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import useCanvasDragMove from 'features/canvas/hooks/useCanvasDragMove'; import useCanvasHotkeys from 'features/canvas/hooks/useCanvasHotkeys'; @@ -17,7 +16,10 @@ import { $isTransformingBoundingBox, } from 'features/canvas/store/canvasNanostore'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { canvasResized } from 'features/canvas/store/canvasSlice'; +import { + canvasResized, + selectCanvasSlice, +} from 'features/canvas/store/canvasSlice'; import { setCanvasBaseLayer, setCanvasStage, @@ -41,8 +43,8 @@ import IAICanvasBoundingBox from './IAICanvasToolbar/IAICanvasBoundingBox'; import IAICanvasToolPreview from './IAICanvasToolPreview'; const selector = createMemoizedSelector( - [stateSelector, isStagingSelector], - ({ canvas }, isStaging) => { + [selectCanvasSlice, isStagingSelector], + (canvas, isStaging) => { const { isMaskEnabled, stageScale, diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasBoundingBoxOverlay.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasBoundingBoxOverlay.tsx index 54054ce2bf..6b43be9ff1 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasBoundingBoxOverlay.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasBoundingBoxOverlay.tsx @@ -1,10 +1,10 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import { memo } from 'react'; import { Group, Rect } from 'react-konva'; -const selector = createMemoizedSelector(stateSelector, ({ canvas }) => { +const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => { const { boundingBoxCoordinates, boundingBoxDimensions, diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasGrid.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasGrid.tsx index 225873d6bb..d1059262ce 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasGrid.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasGrid.tsx @@ -1,13 +1,13 @@ // Grid drawing adapted from https://longviewcoder.com/2021/12/08/konva-a-better-grid/ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import type { ReactElement } from 'react'; import { memo, useCallback, useMemo } from 'react'; import { Group, Line as KonvaLine } from 'react-konva'; import { getArbitraryBaseColor } from 'theme/colors'; -const selector = createMemoizedSelector([stateSelector], ({ canvas }) => { +const selector = createMemoizedSelector([selectCanvasSlice], (canvas) => { const { stageScale, stageCoordinates, stageDimensions } = canvas; return { stageScale, stageCoordinates, stageDimensions }; }); diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasIntermediateImage.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasIntermediateImage.tsx index 72f8c23dd8..8c5c8b844a 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasIntermediateImage.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasIntermediateImage.tsx @@ -2,14 +2,15 @@ import { createLruSelector, createMemoizedSelector, } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; +import { selectSystemSlice } from 'features/system/store/systemSlice'; import { memo, useEffect, useState } from 'react'; import { Image as KonvaImage } from 'react-konva'; const progressImageSelector = createLruSelector( - [stateSelector], - ({ system, canvas }) => { + [selectSystemSlice, selectCanvasSlice], + (system, canvas) => { const { denoiseProgress } = system; const { batchIds } = canvas; @@ -20,8 +21,8 @@ const progressImageSelector = createLruSelector( ); const boundingBoxSelector = createMemoizedSelector( - [stateSelector], - ({ canvas }) => canvas.layerState.stagingArea.boundingBox + [selectCanvasSlice], + (canvas) => canvas.layerState.stagingArea.boundingBox ); const IAICanvasIntermediateImage = () => { diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasMaskCompositer.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasMaskCompositer.tsx index 5998a367ac..7ce2e36d67 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasMaskCompositer.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasMaskCompositer.tsx @@ -1,6 +1,6 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import { rgbaColorToString } from 'features/canvas/util/colorToString'; import type Konva from 'konva'; import type { RectConfig } from 'konva/lib/shapes/Rect'; @@ -9,8 +9,8 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Rect } from 'react-konva'; export const canvasMaskCompositerSelector = createMemoizedSelector( - stateSelector, - ({ canvas }) => { + selectCanvasSlice, + (canvas) => { const { maskColor, stageCoordinates, stageDimensions, stageScale } = canvas; return { diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasMaskLines.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasMaskLines.tsx index 028f8b4fd2..5511652e8d 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasMaskLines.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasMaskLines.tsx @@ -1,18 +1,9 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { isCanvasMaskLine } from 'features/canvas/store/canvasTypes'; import type { GroupConfig } from 'konva/lib/Group'; import { memo } from 'react'; import { Group, Line } from 'react-konva'; -export const canvasLinesSelector = createMemoizedSelector( - [stateSelector], - ({ canvas }) => { - return canvas.layerState.objects.filter(isCanvasMaskLine); - } -); - type InpaintingCanvasLinesProps = GroupConfig; /** @@ -21,7 +12,7 @@ type InpaintingCanvasLinesProps = GroupConfig; * Uses globalCompositeOperation to handle the brush and eraser tools. */ const IAICanvasLines = (props: InpaintingCanvasLinesProps) => { - const objects = useAppSelector((state) => state.canvas.layerState.objects); + const objects = useAppSelector((s) => s.canvas.layerState.objects); return ( diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasObjectRenderer.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasObjectRenderer.tsx index 3e0171e874..bc089fe28a 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasObjectRenderer.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasObjectRenderer.tsx @@ -12,7 +12,7 @@ import { Group, Line, Rect } from 'react-konva'; import IAICanvasImage from './IAICanvasImage'; const IAICanvasObjectRenderer = () => { - const objects = useAppSelector((state) => state.canvas.layerState.objects); + const objects = useAppSelector((s) => s.canvas.layerState.objects); return ( diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingArea.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingArea.tsx index 41ef422195..361da7658c 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingArea.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingArea.tsx @@ -1,6 +1,6 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import type { GroupConfig } from 'konva/lib/Group'; import { memo } from 'react'; import { Group, Rect } from 'react-konva'; @@ -9,7 +9,7 @@ import IAICanvasImage from './IAICanvasImage'; const dash = [4, 4]; -const selector = createMemoizedSelector([stateSelector], ({ canvas }) => { +const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => { const { layerState, shouldShowStagingImage, diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingAreaToolbar.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingAreaToolbar.tsx index f1c040af94..ffc2eda332 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingAreaToolbar.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasStagingAreaToolbar.tsx @@ -1,7 +1,6 @@ import { Flex } from '@chakra-ui/react'; import { skipToken } from '@reduxjs/toolkit/query'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvButton } from 'common/components/InvButton/InvButton'; import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup'; @@ -11,6 +10,7 @@ import { discardStagedImages, nextStagingAreaImage, prevStagingAreaImage, + selectCanvasSlice, setShouldShowStagingImage, setShouldShowStagingOutline, } from 'features/canvas/store/canvasSlice'; @@ -29,7 +29,7 @@ import { } from 'react-icons/fa'; import { useGetImageDTOQuery } from 'services/api/endpoints/images'; -const selector = createMemoizedSelector([stateSelector], ({ canvas }) => { +const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => { const { layerState: { stagingArea: { images, selectedImageIndex }, diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasStatusText.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasStatusText.tsx index 34604c806d..ad57301670 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasStatusText.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasStatusText.tsx @@ -1,7 +1,7 @@ import { Box, Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import roundToHundreth from 'features/canvas/util/roundToHundreth'; import GenerationModeStatusText from 'features/parameters/components/Canvas/GenerationModeStatusText'; import { memo } from 'react'; @@ -11,7 +11,7 @@ import IAICanvasStatusTextCursorPos from './IAICanvasStatusText/IAICanvasStatusT const warningColor = 'var(--invokeai-colors-warning-500)'; -const selector = createMemoizedSelector([stateSelector], ({ canvas }) => { +const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => { const { stageDimensions: { width: stageWidth, height: stageHeight }, stageCoordinates: { x: stageX, y: stageY }, diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolPreview.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolPreview.tsx index 4b8a2ba781..1921f08e6d 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolPreview.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolPreview.tsx @@ -1,12 +1,12 @@ import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { $cursorPosition, $isMovingBoundingBox, $isTransformingBoundingBox, } from 'features/canvas/store/canvasNanostore'; +import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import { rgbaColorToString } from 'features/canvas/util/colorToString'; import { COLOR_PICKER_SIZE, @@ -17,8 +17,8 @@ import { memo, useMemo } from 'react'; import { Circle, Group } from 'react-konva'; const canvasBrushPreviewSelector = createMemoizedSelector( - stateSelector, - ({ canvas }) => { + selectCanvasSlice, + (canvas) => { const { brushSize, colorPickerColor, diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasBoundingBox.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasBoundingBox.tsx index 50a6d12cc9..caeac04055 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasBoundingBox.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasBoundingBox.tsx @@ -1,6 +1,5 @@ import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { $shift } from 'common/hooks/useGlobalModifiers'; import { @@ -18,6 +17,7 @@ import { } from 'features/canvas/store/canvasNanostore'; import { aspectRatioChanged, + selectCanvasSlice, setBoundingBoxCoordinates, setBoundingBoxDimensions, setShouldSnapToGrid, @@ -39,8 +39,8 @@ import { Group, Rect, Transformer } from 'react-konva'; const borderDash = [4, 4]; const boundingBoxPreviewSelector = createMemoizedSelector( - [stateSelector, selectOptimalDimension], - ({ canvas }, optimalDimension) => { + [selectCanvasSlice, selectOptimalDimension], + (canvas, optimalDimension) => { const { boundingBoxCoordinates, boundingBoxDimensions, diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasMaskOptions.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasMaskOptions.tsx index 0a526d4faa..b4f2086284 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasMaskOptions.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasMaskOptions.tsx @@ -1,6 +1,5 @@ import { Box, Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIColorPicker from 'common/components/IAIColorPicker'; import { InvButton } from 'common/components/InvButton/InvButton'; @@ -17,6 +16,7 @@ import { canvasMaskSavedToGallery } from 'features/canvas/store/actions'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { clearMask, + selectCanvasSlice, setIsMaskEnabled, setLayer, setMaskColor, @@ -31,8 +31,8 @@ import { useTranslation } from 'react-i18next'; import { FaMask, FaSave, FaTrash } from 'react-icons/fa'; export const selector = createMemoizedSelector( - [stateSelector, isStagingSelector], - ({ canvas }, isStaging) => { + [selectCanvasSlice, isStagingSelector], + (canvas, isStaging) => { const { maskColor, layer, isMaskEnabled, shouldPreserveMaskedArea } = canvas; diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton.tsx index 1c3c375d6a..53abd3fe4d 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton.tsx @@ -1,8 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; -import { redo } from 'features/canvas/store/canvasSlice'; +import { redo, selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { memo, useCallback } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -10,8 +9,8 @@ import { useTranslation } from 'react-i18next'; import { FaRedo } from 'react-icons/fa'; const canvasRedoSelector = createMemoizedSelector( - [stateSelector, activeTabNameSelector], - ({ canvas }, activeTabName) => { + [selectCanvasSlice, activeTabNameSelector], + (canvas, activeTabName) => { const { futureLayerStates } = canvas; return { diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasSettingsButtonPopover.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasSettingsButtonPopover.tsx index 41d2f1477b..8dc28766ff 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasSettingsButtonPopover.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasSettingsButtonPopover.tsx @@ -1,6 +1,5 @@ import { Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvCheckbox } from 'common/components/InvCheckbox/wrapper'; import { InvControl } from 'common/components/InvControl/InvControl'; @@ -11,6 +10,7 @@ import { } from 'common/components/InvPopover/wrapper'; import ClearCanvasHistoryButtonModal from 'features/canvas/components/ClearCanvasHistoryButtonModal'; import { + selectCanvasSlice, setShouldAntialias, setShouldAutoSave, setShouldCropToBoundingBoxOnSave, @@ -29,8 +29,8 @@ import { useTranslation } from 'react-i18next'; import { FaWrench } from 'react-icons/fa'; export const canvasControlsSelector = createMemoizedSelector( - [stateSelector], - ({ canvas }) => { + [selectCanvasSlice], + (canvas) => { const { shouldAutoSave, shouldCropToBoundingBoxOnSave, diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx index 38f3909cfb..af2f93c5f4 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx @@ -1,6 +1,5 @@ import { Box, Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIColorPicker from 'common/components/IAIColorPicker'; import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup'; @@ -17,6 +16,7 @@ import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { addEraseRect, addFillRect, + selectCanvasSlice, setBrushColor, setBrushSize, setTool, @@ -37,8 +37,8 @@ import { } from 'react-icons/fa'; export const selector = createMemoizedSelector( - [stateSelector, isStagingSelector], - ({ canvas }, isStaging) => { + [selectCanvasSlice, isStagingSelector], + (canvas, isStaging) => { const { tool, brushColor, brushSize } = canvas; return { diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx index 9e279699eb..c62c00d9a9 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx @@ -1,6 +1,5 @@ import { Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup'; import { InvControl } from 'common/components/InvControl/InvControl'; @@ -20,6 +19,7 @@ import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { resetCanvas, resetCanvasView, + selectCanvasSlice, setIsMaskEnabled, setLayer, setTool, @@ -49,8 +49,8 @@ import IAICanvasToolChooserOptions from './IAICanvasToolChooserOptions'; import IAICanvasUndoButton from './IAICanvasUndoButton'; export const selector = createMemoizedSelector( - [stateSelector, isStagingSelector], - ({ canvas }, isStaging) => { + [selectCanvasSlice, isStagingSelector], + (canvas, isStaging) => { const { tool, shouldCropToBoundingBoxOnSave, layer, isMaskEnabled } = canvas; diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasUndoButton.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasUndoButton.tsx index ccbda4e053..597b1f2b2d 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasUndoButton.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasUndoButton.tsx @@ -1,8 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; -import { undo } from 'features/canvas/store/canvasSlice'; +import { selectCanvasSlice,undo } from 'features/canvas/store/canvasSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { memo, useCallback } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -10,8 +9,8 @@ import { useTranslation } from 'react-i18next'; import { FaUndo } from 'react-icons/fa'; const canvasUndoSelector = createMemoizedSelector( - [stateSelector, activeTabNameSelector], - ({ canvas }, activeTabName) => { + [selectCanvasSlice, activeTabNameSelector], + (canvas, activeTabName) => { const { pastLayerStates } = canvas; return { diff --git a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasDragMove.ts b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasDragMove.ts index 19005509f7..1fba87175a 100644 --- a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasDragMove.ts +++ b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasDragMove.ts @@ -12,7 +12,7 @@ import { useCallback } from 'react'; const useCanvasDrag = () => { const dispatch = useAppDispatch(); const isStaging = useAppSelector(isStagingSelector); - const tool = useAppSelector((state) => state.canvas.tool); + const tool = useAppSelector((s) => s.canvas.tool); const isMovingBoundingBox = useStore($isMovingBoundingBox); const handleDragStart = useCallback(() => { if (!((tool === 'move' || isStaging) && !isMovingBoundingBox)) { diff --git a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasGenerationMode.ts b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasGenerationMode.ts index 19d5c7c0e4..a7d6a173ad 100644 --- a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasGenerationMode.ts +++ b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasGenerationMode.ts @@ -6,18 +6,18 @@ import { useEffect, useState } from 'react'; import { useDebounce } from 'react-use'; export const useCanvasGenerationMode = () => { - const layerState = useAppSelector((state) => state.canvas.layerState); + const layerState = useAppSelector((s) => s.canvas.layerState); const boundingBoxCoordinates = useAppSelector( - (state) => state.canvas.boundingBoxCoordinates + (s) => s.canvas.boundingBoxCoordinates ); const boundingBoxDimensions = useAppSelector( - (state) => state.canvas.boundingBoxDimensions + (s) => s.canvas.boundingBoxDimensions ); - const isMaskEnabled = useAppSelector((state) => state.canvas.isMaskEnabled); + const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled); const shouldPreserveMaskedArea = useAppSelector( - (state) => state.canvas.shouldPreserveMaskedArea + (s) => s.canvas.shouldPreserveMaskedArea ); const [generationMode, setGenerationMode] = useState< GenerationMode | undefined diff --git a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasHotkeys.ts b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasHotkeys.ts index 16599c566b..39a617ffba 100644 --- a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasHotkeys.ts +++ b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasHotkeys.ts @@ -1,5 +1,4 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { resetCanvasInteractionState, @@ -8,6 +7,7 @@ import { import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { clearMask, + selectCanvasSlice, setIsMaskEnabled, setShouldShowBoundingBox, setShouldSnapToGrid, @@ -20,8 +20,8 @@ import { useRef } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; const selector = createMemoizedSelector( - [stateSelector, activeTabNameSelector, isStagingSelector], - ({ canvas }, activeTabName, isStaging) => { + [selectCanvasSlice, activeTabNameSelector, isStagingSelector], + (canvas, activeTabName, isStaging) => { const { shouldLockBoundingBox, shouldShowBoundingBox, diff --git a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseDown.ts b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseDown.ts index 9204dd1517..e6c6e1425b 100644 --- a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseDown.ts +++ b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseDown.ts @@ -1,12 +1,11 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { setIsDrawing, setIsMovingStage, } from 'features/canvas/store/canvasNanostore'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { addLine } from 'features/canvas/store/canvasSlice'; +import { addLine, selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import type Konva from 'konva'; @@ -17,11 +16,10 @@ import { useCallback } from 'react'; import useColorPicker from './useColorUnderCursor'; const selector = createMemoizedSelector( - [activeTabNameSelector, stateSelector, isStagingSelector], - (activeTabName, { canvas }, isStaging) => { - const { tool } = canvas; + [activeTabNameSelector, selectCanvasSlice, isStagingSelector], + (activeTabName, canvas, isStaging) => { return { - tool, + tool: canvas.tool, activeTabName, isStaging, }; diff --git a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseMove.ts b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseMove.ts index 8adc556768..add621d799 100644 --- a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseMove.ts +++ b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseMove.ts @@ -1,13 +1,15 @@ import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { $isDrawing, setCursorPosition, } from 'features/canvas/store/canvasNanostore'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { addPointToCurrentLine } from 'features/canvas/store/canvasSlice'; +import { + addPointToCurrentLine, + selectCanvasSlice, +} from 'features/canvas/store/canvasSlice'; import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import type Konva from 'konva'; @@ -18,11 +20,10 @@ import { useCallback } from 'react'; import useColorPicker from './useColorUnderCursor'; const selector = createMemoizedSelector( - [activeTabNameSelector, stateSelector, isStagingSelector], - (activeTabName, { canvas }, isStaging) => { - const { tool } = canvas; + [activeTabNameSelector, selectCanvasSlice, isStagingSelector], + (activeTabName, canvas, isStaging) => { return { - tool, + tool: canvas.tool, activeTabName, isStaging, }; diff --git a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseUp.ts b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseUp.ts index 5d81a6cb65..63676a44a9 100644 --- a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseUp.ts +++ b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasMouseUp.ts @@ -1,6 +1,4 @@ import { useStore } from '@nanostores/react'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { $isDrawing, @@ -8,32 +6,20 @@ import { setIsMovingStage, } from 'features/canvas/store/canvasNanostore'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { - // addPointToCurrentEraserLine, - addPointToCurrentLine, -} from 'features/canvas/store/canvasSlice'; +import { addPointToCurrentLine } from 'features/canvas/store/canvasSlice'; import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition'; import type Konva from 'konva'; import type { MutableRefObject } from 'react'; import { useCallback } from 'react'; -const selector = createMemoizedSelector( - [stateSelector, isStagingSelector], - ({ canvas }, isStaging) => { - return { - tool: canvas.tool, - isStaging, - }; - } -); - const useCanvasMouseUp = ( stageRef: MutableRefObject, didMouseMoveRef: MutableRefObject ) => { const dispatch = useAppDispatch(); const isDrawing = useStore($isDrawing); - const { tool, isStaging } = useAppSelector(selector); + const tool = useAppSelector((s) => s.canvas.tool); + const isStaging = useAppSelector(isStagingSelector); return useCallback(() => { if (tool === 'move' || isStaging) { diff --git a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasZoom.ts b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasZoom.ts index f86f435842..8c1ac9833b 100644 --- a/invokeai/frontend/web/src/features/canvas/hooks/useCanvasZoom.ts +++ b/invokeai/frontend/web/src/features/canvas/hooks/useCanvasZoom.ts @@ -1,6 +1,4 @@ import { useStore } from '@nanostores/react'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { $isMoveStageKeyHeld } from 'features/canvas/store/canvasNanostore'; import { @@ -18,14 +16,9 @@ import { clamp } from 'lodash-es'; import type { MutableRefObject } from 'react'; import { useCallback } from 'react'; -const selector = createMemoizedSelector( - [stateSelector], - (state) => state.canvas.stageScale -); - const useCanvasWheel = (stageRef: MutableRefObject) => { const dispatch = useAppDispatch(); - const stageScale = useAppSelector(selector); + const stageScale = useAppSelector((s) => s.canvas.stageScale); const isMoveStageKeyHeld = useStore($isMoveStageKeyHeld); return useCallback( diff --git a/invokeai/frontend/web/src/features/canvas/store/canvasSelectors.ts b/invokeai/frontend/web/src/features/canvas/store/canvasSelectors.ts index 3f913233ad..442d0fff1e 100644 --- a/invokeai/frontend/web/src/features/canvas/store/canvasSelectors.ts +++ b/invokeai/frontend/web/src/features/canvas/store/canvasSelectors.ts @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import type { RootState } from 'app/store/store'; -import { stateSelector } from 'app/store/store'; +import { selectCanvasSlice } from './canvasSlice'; import type { CanvasImage } from './canvasTypes'; import { isCanvasBaseImage } from './canvasTypes'; export const isStagingSelector = createMemoizedSelector( - [stateSelector], - ({ canvas }) => + selectCanvasSlice, + (canvas) => canvas.batchIds.length > 0 || canvas.layerState.stagingArea.images.length > 0 ); diff --git a/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts b/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts index 15fcc81fd9..2ca1e1eaed 100644 --- a/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts +++ b/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import { roundDownToMultiple, roundToMultiple, @@ -781,3 +782,5 @@ export const { } = canvasSlice.actions; export default canvasSlice.reducer; + +export const selectCanvasSlice = (state: RootState) => state.canvas; diff --git a/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx b/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx index 899ed0deb7..f77c6f3c73 100644 --- a/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx +++ b/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx @@ -1,6 +1,5 @@ import { Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvConfirmationAlertDialog } from 'common/components/InvConfirmationAlertDialog/InvConfirmationAlertDialog'; import { InvControl } from 'common/components/InvControl/InvControl'; @@ -13,6 +12,7 @@ import { InvText } from 'common/components/InvText/wrapper'; import { changeBoardReset, isModalOpenChanged, + selectChangeBoardModalSlice, } from 'features/changeBoardModal/store/slice'; import { memo, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -23,8 +23,8 @@ import { } from 'services/api/endpoints/images'; const selector = createMemoizedSelector( - [stateSelector], - ({ changeBoardModal }) => { + selectChangeBoardModalSlice, + (changeBoardModal) => { const { isModalOpen, imagesToChange } = changeBoardModal; return { diff --git a/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts b/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts index dde72b263d..d4abf3069a 100644 --- a/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts +++ b/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import type { ImageDTO } from 'services/api/types'; import { initialState } from './initialState'; @@ -25,3 +26,6 @@ export const { isModalOpenChanged, imagesToChangeSelected, changeBoardReset } = changeBoardModal.actions; export default changeBoardModal.reducer; + +export const selectChangeBoardModalSlice = (state: RootState) => + state.changeBoardModal; diff --git a/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterImagePreview.tsx b/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterImagePreview.tsx index 80f0b6a59c..4145a8cfcf 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterImagePreview.tsx +++ b/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterImagePreview.tsx @@ -2,7 +2,6 @@ import type { SystemStyleObject } from '@chakra-ui/react'; import { Box, Flex, Spinner } from '@chakra-ui/react'; import { skipToken } from '@reduxjs/toolkit/query'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIDndImage from 'common/components/IAIDndImage'; import IAIDndImageIcon from 'common/components/IAIDndImageIcon'; @@ -10,16 +9,21 @@ import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice'; import { useControlAdapterControlImage } from 'features/controlAdapters/hooks/useControlAdapterControlImage'; import { useControlAdapterProcessedControlImage } from 'features/controlAdapters/hooks/useControlAdapterProcessedControlImage'; import { useControlAdapterProcessorType } from 'features/controlAdapters/hooks/useControlAdapterProcessorType'; -import { controlAdapterImageChanged } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + controlAdapterImageChanged, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import type { TypesafeDraggableData, TypesafeDroppableData, } from 'features/dnd/types'; +import { selectGallerySlice } from 'features/gallery/store/gallerySlice'; import { heightChanged, selectOptimalDimension, widthChanged, } from 'features/parameters/store/generationSlice'; +import { selectSystemSlice } from 'features/system/store/systemSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -38,8 +42,14 @@ type Props = { }; const selector = createMemoizedSelector( - [stateSelector, activeTabNameSelector, selectOptimalDimension], - ({ controlAdapters, gallery, system }, activeTabName, optimalDimension) => { + [ + selectControlAdaptersSlice, + selectGallerySlice, + selectSystemSlice, + activeTabNameSelector, + selectOptimalDimension, + ], + (controlAdapters, gallery, system, activeTabName, optimalDimension) => { const { pendingControlImages } = controlAdapters; const { autoAddBoardId } = gallery; const { isConnected } = system; diff --git a/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterModel.tsx b/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterModel.tsx index 44b36c3598..c5793a4ae7 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterModel.tsx +++ b/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterModel.tsx @@ -1,5 +1,4 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSelect } from 'common/components/InvSelect/InvSelect'; @@ -10,6 +9,7 @@ import { useControlAdapterModel } from 'features/controlAdapters/hooks/useContro import { useControlAdapterModelEntities } from 'features/controlAdapters/hooks/useControlAdapterModelEntities'; import { useControlAdapterType } from 'features/controlAdapters/hooks/useControlAdapterType'; import { controlAdapterModelChanged } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { pick } from 'lodash-es'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -24,9 +24,8 @@ type ParamControlAdapterModelProps = { id: string; }; -const selector = createMemoizedSelector(stateSelector, ({ generation }) => { - const { model } = generation; - return { mainModel: model }; +const selector = createMemoizedSelector(selectGenerationSlice, (generation) => { + return { mainModel: generation.model }; }); const ParamControlAdapterModel = ({ id }: ParamControlAdapterModelProps) => { @@ -35,7 +34,7 @@ const ParamControlAdapterModel = ({ id }: ParamControlAdapterModelProps) => { const model = useControlAdapterModel(id); const dispatch = useAppDispatch(); const currentBaseModel = useAppSelector( - (state) => state.generation.model?.base_model + (s) => s.generation.model?.base_model ); const { mainModel } = useAppSelector(selector); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useAddControlAdapter.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useAddControlAdapter.ts index 42d401c1ab..023b38d5a7 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useAddControlAdapter.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useAddControlAdapter.ts @@ -7,7 +7,7 @@ import { useControlAdapterModels } from './useControlAdapterModels'; export const useAddControlAdapter = (type: ControlAdapterType) => { const baseModel = useAppSelector( - (state) => state.generation.model?.base_model + (s) => s.generation.model?.base_model ); const dispatch = useAppDispatch(); diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapter.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapter.ts index 290946a790..6fdee62922 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapter.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapter.ts @@ -1,13 +1,15 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { useMemo } from 'react'; export const useControlAdapter = (id: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ controlAdapters }) => + createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => selectControlAdapterById(controlAdapters, id) ), [id] diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterBeginEndStepPct.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterBeginEndStepPct.ts index d4a960136d..c9c99fcb2e 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterBeginEndStepPct.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterBeginEndStepPct.ts @@ -1,13 +1,15 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { useMemo } from 'react'; export const useControlAdapterBeginEndStepPct = (id: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ controlAdapters }) => { + createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { const cn = selectControlAdapterById(controlAdapters, id); return cn ? { diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterControlImage.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterControlImage.ts index 46b58d0fe7..f38af7acc4 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterControlImage.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterControlImage.ts @@ -1,15 +1,17 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { useMemo } from 'react'; export const useControlAdapterControlImage = (id: string) => { const selector = useMemo( () => createMemoizedSelector( - stateSelector, - ({ controlAdapters }) => + selectControlAdaptersSlice, + (controlAdapters) => selectControlAdapterById(controlAdapters, id)?.controlImage ), [id] diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterControlMode.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterControlMode.ts index 6ad9353c98..bd5590fe63 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterControlMode.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterControlMode.ts @@ -1,14 +1,16 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { isControlNet } from 'features/controlAdapters/store/types'; import { useMemo } from 'react'; export const useControlAdapterControlMode = (id: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ controlAdapters }) => { + createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { const ca = selectControlAdapterById(controlAdapters, id); if (ca && isControlNet(ca)) { return ca.controlMode; diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterIsEnabled.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterIsEnabled.ts index 4e8e69be34..dc675007ae 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterIsEnabled.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterIsEnabled.ts @@ -1,15 +1,17 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { useMemo } from 'react'; export const useControlAdapterIsEnabled = (id: string) => { const selector = useMemo( () => createMemoizedSelector( - stateSelector, - ({ controlAdapters }) => + selectControlAdaptersSlice, + (controlAdapters) => selectControlAdapterById(controlAdapters, id)?.isEnabled ?? false ), [id] diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterModel.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterModel.ts index f1b73ab3e6..dd23c8ff44 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterModel.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterModel.ts @@ -1,15 +1,17 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { useMemo } from 'react'; export const useControlAdapterModel = (id: string) => { const selector = useMemo( () => createMemoizedSelector( - stateSelector, - ({ controlAdapters }) => + selectControlAdaptersSlice, + (controlAdapters) => selectControlAdapterById(controlAdapters, id)?.model ), [id] diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessedControlImage.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessedControlImage.ts index b5c773928e..f70d532914 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessedControlImage.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessedControlImage.ts @@ -1,14 +1,16 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types'; import { useMemo } from 'react'; export const useControlAdapterProcessedControlImage = (id: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ controlAdapters }) => { + createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { const ca = selectControlAdapterById(controlAdapters, id); return ca && isControlNetOrT2IAdapter(ca) diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessorNode.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessorNode.ts index af932bac46..6626374dd2 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessorNode.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessorNode.ts @@ -1,14 +1,16 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types'; import { useMemo } from 'react'; export const useControlAdapterProcessorNode = (id: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ controlAdapters }) => { + createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { const ca = selectControlAdapterById(controlAdapters, id); return ca && isControlNetOrT2IAdapter(ca) diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessorType.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessorType.ts index ae2f508ae3..672002219b 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessorType.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterProcessorType.ts @@ -1,14 +1,16 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types'; import { useMemo } from 'react'; export const useControlAdapterProcessorType = (id: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ controlAdapters }) => { + createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { const ca = selectControlAdapterById(controlAdapters, id); return ca && isControlNetOrT2IAdapter(ca) diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterResizeMode.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterResizeMode.ts index 2a0119efa5..c6140bced7 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterResizeMode.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterResizeMode.ts @@ -1,14 +1,16 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types'; import { useMemo } from 'react'; export const useControlAdapterResizeMode = (id: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ controlAdapters }) => { + createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { const ca = selectControlAdapterById(controlAdapters, id); if (ca && isControlNetOrT2IAdapter(ca)) { return ca.resizeMode; diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterShouldAutoConfig.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterShouldAutoConfig.ts index 2f371fe79a..f1796ae8ce 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterShouldAutoConfig.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterShouldAutoConfig.ts @@ -1,14 +1,16 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types'; import { useMemo } from 'react'; export const useControlAdapterShouldAutoConfig = (id: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ controlAdapters }) => { + createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { const ca = selectControlAdapterById(controlAdapters, id); if (ca && isControlNetOrT2IAdapter(ca)) { return ca.shouldAutoConfig; diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterType.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterType.ts index 08a37ec0ff..4e15dc9e64 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterType.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterType.ts @@ -1,16 +1,17 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { useMemo } from 'react'; export const useControlAdapterType = (id: string) => { const selector = useMemo( () => createMemoizedSelector( - stateSelector, - ({ controlAdapters }) => - selectControlAdapterById(controlAdapters, id)?.type + selectControlAdaptersSlice, + (controlAdapters) => selectControlAdapterById(controlAdapters, id)?.type ), [id] ); diff --git a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterWeight.ts b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterWeight.ts index 8f5b08539d..d84a48b006 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterWeight.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/hooks/useControlAdapterWeight.ts @@ -1,15 +1,17 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { + selectControlAdapterById, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; import { useMemo } from 'react'; export const useControlAdapterWeight = (id: string) => { const selector = useMemo( () => createMemoizedSelector( - stateSelector, - ({ controlAdapters }) => + selectControlAdaptersSlice, + (controlAdapters) => selectControlAdapterById(controlAdapters, id)?.weight ), [id] diff --git a/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersSlice.ts b/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersSlice.ts index f52374692c..a1a075e250 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersSlice.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersSlice.ts @@ -1,6 +1,7 @@ import type { PayloadAction, Update } from '@reduxjs/toolkit'; import { createEntityAdapter, createSlice, isAnyOf } from '@reduxjs/toolkit'; import { getSelectorsOptions } from 'app/store/createMemoizedSelector'; +import type { RootState } from 'app/store/store'; import { buildControlAdapter } from 'features/controlAdapters/util/buildControlAdapter'; import type { ParameterControlNetModel, @@ -489,3 +490,6 @@ export const isAnyControlAdapterAdded = isAnyOf( controlAdapterAddedFromImage, controlAdapterRecalled ); + +export const selectControlAdaptersSlice = (state: RootState) => + state.controlAdapters; diff --git a/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageButton.tsx b/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageButton.tsx index 8660e140f4..69741a45fc 100644 --- a/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageButton.tsx +++ b/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageButton.tsx @@ -12,7 +12,7 @@ type DeleteImageButtonProps = Omit & { export const DeleteImageButton = memo((props: DeleteImageButtonProps) => { const { onClick, isDisabled } = props; const { t } = useTranslation(); - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); return ( { - const { system, config, deleteImageModal } = state; + [ + selectSystemSlice, + selectConfigSlice, + selectDeleteImageModalSlice, + selectGenerationSlice, + selectCanvasSlice, + selectNodesSlice, + selectControlAdaptersSlice, + selectImageUsage, + ], + ( + system, + config, + deleteImageModal, + generation, + canvas, + nodes, + controlAdapters, + imagesUsage + ) => { const { shouldConfirmOnDelete } = system; const { canRestoreDeletedImagesFromBin } = config; const { imagesToDelete, isModalOpen } = deleteImageModal; const allImageUsage = (imagesToDelete ?? []).map(({ image_name }) => - getImageUsage(state, image_name) + getImageUsage(generation, canvas, nodes, controlAdapters, image_name) ); const imageUsageSummary: ImageUsage = { diff --git a/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts b/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts index d410e85f41..24192647fc 100644 --- a/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts +++ b/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts @@ -1,15 +1,30 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import type { RootState } from 'app/store/store'; -import { selectControlAdapterAll } from 'features/controlAdapters/store/controlAdaptersSlice'; +import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; +import type { CanvasState } from 'features/canvas/store/canvasTypes'; +import { + selectControlAdapterAll, + selectControlAdaptersSlice, +} from 'features/controlAdapters/store/controlAdaptersSlice'; +import type { ControlAdaptersState } from 'features/controlAdapters/store/types'; import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types'; +import { selectDeleteImageModalSlice } from 'features/deleteImageModal/store/slice'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import type { NodesState } from 'features/nodes/store/types'; import { isImageFieldInputInstance } from 'features/nodes/types/field'; import { isInvocationNode } from 'features/nodes/types/invocation'; +import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; +import type { GenerationState } from 'features/parameters/store/types'; import { some } from 'lodash-es'; import type { ImageUsage } from './types'; -export const getImageUsage = (state: RootState, image_name: string) => { - const { generation, canvas, nodes, controlAdapters } = state; +export const getImageUsage = ( + generation: GenerationState, + canvas: CanvasState, + nodes: NodesState, + controlAdapters: ControlAdaptersState, + image_name: string +) => { const isInitialImage = generation.initialImage?.imageName === image_name; const isCanvasImage = canvas.layerState.objects.some( @@ -42,16 +57,20 @@ export const getImageUsage = (state: RootState, image_name: string) => { }; export const selectImageUsage = createMemoizedSelector( - [(state: RootState) => state], - (state) => { - const { imagesToDelete } = state.deleteImageModal; + selectDeleteImageModalSlice, + selectGenerationSlice, + selectCanvasSlice, + selectNodesSlice, + selectControlAdaptersSlice, + (deleteImageModal, generation, canvas, nodes, controlAdapters) => { + const { imagesToDelete } = deleteImageModal; if (!imagesToDelete.length) { return []; } const imagesUsage = imagesToDelete.map((i) => - getImageUsage(state, i.image_name) + getImageUsage(generation, canvas, nodes, controlAdapters, i.image_name) ); return imagesUsage; diff --git a/invokeai/frontend/web/src/features/deleteImageModal/store/slice.ts b/invokeai/frontend/web/src/features/deleteImageModal/store/slice.ts index 456abb62f3..03d1901d99 100644 --- a/invokeai/frontend/web/src/features/deleteImageModal/store/slice.ts +++ b/invokeai/frontend/web/src/features/deleteImageModal/store/slice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import type { ImageDTO } from 'services/api/types'; import { initialDeleteImageState } from './initialState'; @@ -28,3 +29,6 @@ export const { } = deleteImageModal.actions; export default deleteImageModal.reducer; + +export const selectDeleteImageModalSlice = (state: RootState) => + state.deleteImageModal; diff --git a/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts b/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts index b3ceb97ce4..19f6c7baff 100644 --- a/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts +++ b/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts @@ -1,14 +1,14 @@ import type { Modifier } from '@dnd-kit/core'; import { getEventCoordinates } from '@dnd-kit/utilities'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { useCallback } from 'react'; const selectZoom = createMemoizedSelector( - [stateSelector, activeTabNameSelector], - ({ nodes }, activeTabName) => + [selectNodesSlice, activeTabNameSelector], + (nodes, activeTabName) => activeTabName === 'nodes' ? nodes.viewport.zoom : 1 ); diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCombinatorial.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCombinatorial.tsx index 0d183d7155..7b0df5e602 100644 --- a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCombinatorial.tsx +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCombinatorial.tsx @@ -1,5 +1,3 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSwitch } from 'common/components/InvSwitch/wrapper'; @@ -7,14 +5,8 @@ import { combinatorialToggled } from 'features/dynamicPrompts/store/dynamicPromp import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector(stateSelector, (state) => { - const { combinatorial } = state.dynamicPrompts; - - return { combinatorial }; -}); - const ParamDynamicPromptsCombinatorial = () => { - const { combinatorial } = useAppSelector(selector); + const combinatorial = useAppSelector((s) => s.dynamicPrompts.combinatorial); const dispatch = useAppDispatch(); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx index c613637be1..61dd320616 100644 --- a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx @@ -1,26 +1,33 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; -import { maxPromptsChanged } from 'features/dynamicPrompts/store/dynamicPromptsSlice'; +import { + maxPromptsChanged, + selectDynamicPromptsSlice, +} from 'features/dynamicPrompts/store/dynamicPromptsSlice'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector(stateSelector, (state) => { - const { maxPrompts, combinatorial } = state.dynamicPrompts; - const { min, sliderMax, inputMax, initial } = - state.config.sd.dynamicPrompts.maxPrompts; +const selector = createMemoizedSelector( + selectDynamicPromptsSlice, + selectConfigSlice, + (dynamicPrompts, config) => { + const { maxPrompts, combinatorial } = dynamicPrompts; + const { min, sliderMax, inputMax, initial } = + config.sd.dynamicPrompts.maxPrompts; - return { - maxPrompts, - min, - sliderMax, - inputMax, - initial, - isDisabled: !combinatorial, - }; -}); + return { + maxPrompts, + min, + sliderMax, + inputMax, + initial, + isDisabled: !combinatorial, + }; + } +); const ParamDynamicPromptsMaxPrompts = () => { const { maxPrompts, min, sliderMax, inputMax, initial, isDisabled } = diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx index dfc7c51834..5f715279f7 100644 --- a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx @@ -1,26 +1,29 @@ import type { ChakraProps } from '@chakra-ui/react'; import { Flex, ListItem, OrderedList, Spinner } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover'; import { InvText } from 'common/components/InvText/wrapper'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; +import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice'; import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { FaCircleExclamation } from 'react-icons/fa6'; -const selector = createMemoizedSelector(stateSelector, (state) => { - const { isLoading, isError, prompts, parsingError } = state.dynamicPrompts; +const selector = createMemoizedSelector( + selectDynamicPromptsSlice, + (dynamicPrompts) => { + const { isLoading, isError, prompts, parsingError } = dynamicPrompts; - return { - prompts, - parsingError, - isError, - isLoading, - }; -}); + return { + prompts, + parsingError, + isError, + isLoading, + }; + } +); const listItemStyles: ChakraProps['sx'] = { '&::marker': { color: 'base.500' }, diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx index 36333cd992..08194db0b8 100644 --- a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx @@ -16,7 +16,7 @@ const ParamDynamicPromptsSeedBehaviour = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const seedBehaviour = useAppSelector( - (state) => state.dynamicPrompts.seedBehaviour + (s) => s.dynamicPrompts.seedBehaviour ); const options = useMemo(() => { diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx index c560c81d7d..a66cc09141 100644 --- a/invokeai/frontend/web/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx @@ -14,7 +14,7 @@ const loadingStyles: SystemStyleObject = { export const ShowDynamicPromptsPreviewButton = memo(() => { const { t } = useTranslation(); - const isLoading = useAppSelector((state) => state.dynamicPrompts.isLoading); + const isLoading = useAppSelector((s) => s.dynamicPrompts.isLoading); const { isOpen, onOpen } = useDynamicPromptsModal(); return ( + state.dynamicPrompts; diff --git a/invokeai/frontend/web/src/features/embedding/EmbeddingSelect.tsx b/invokeai/frontend/web/src/features/embedding/EmbeddingSelect.tsx index 46bf750caf..80fcb8f06c 100644 --- a/invokeai/frontend/web/src/features/embedding/EmbeddingSelect.tsx +++ b/invokeai/frontend/web/src/features/embedding/EmbeddingSelect.tsx @@ -18,7 +18,7 @@ export const EmbeddingSelect = memo( const { t } = useTranslation(); const currentBaseModel = useAppSelector( - (state) => state.generation.model?.base_model + (s) => s.generation.model?.base_model ); const getIsDisabled = useCallback( diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx index 05c2c97d8b..d13dac4e93 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx @@ -1,5 +1,3 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSelect } from 'common/components/InvSelect/InvSelect'; @@ -7,24 +5,22 @@ import type { InvSelectOnChange, InvSelectOption, } from 'common/components/InvSelect/types'; -import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice'; +import { + autoAddBoardIdChanged, +} from 'features/gallery/store/gallerySlice'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useListAllBoardsQuery } from 'services/api/endpoints/boards'; -const selector = createMemoizedSelector([stateSelector], ({ gallery }) => { - const { autoAddBoardId, autoAssignBoardOnClick } = gallery; - - return { - autoAddBoardId, - autoAssignBoardOnClick, - }; -}); - const BoardAutoAddSelect = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const { autoAddBoardId, autoAssignBoardOnClick } = useAppSelector(selector); + const autoAddBoardId = useAppSelector( + (s) => s.gallery.autoAddBoardId + ); + const autoAssignBoardOnClick = useAppSelector( + (s) => s.gallery.autoAssignBoardOnClick + ); const { options, hasBoards } = useListAllBoardsQuery(undefined, { selectFromResult: ({ data }) => { const options: InvSelectOption[] = [ diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx index cddb1b981b..7cf26f5c37 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx @@ -1,12 +1,14 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu'; import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu'; import { InvMenuItem } from 'common/components/InvMenu/InvMenuItem'; import { InvMenuList } from 'common/components/InvMenu/InvMenuList'; import { InvMenuGroup } from 'common/components/InvMenu/wrapper'; -import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice'; +import { + autoAddBoardIdChanged, + selectGallerySlice, +} from 'features/gallery/store/gallerySlice'; import type { BoardId } from 'features/gallery/store/types'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { addToast } from 'features/system/store/systemSlice'; @@ -38,7 +40,7 @@ const BoardContextMenu = ({ const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ gallery }) => { + createMemoizedSelector(selectGallerySlice, (gallery) => { const isAutoAdd = gallery.autoAddBoardId === board_id; const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick; return { isAutoAdd, autoAssignBoardOnClick }; diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx index 9b713777c2..77e34af5ab 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx @@ -1,6 +1,4 @@ import { Collapse, Flex, Grid, GridItem } from '@chakra-ui/react'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants'; import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal'; @@ -20,18 +18,18 @@ const overlayScrollbarsStyles: CSSProperties = { width: '100%', }; -const selector = createMemoizedSelector([stateSelector], ({ gallery }) => { - const { selectedBoardId, boardSearchText } = gallery; - return { selectedBoardId, boardSearchText }; -}); - type Props = { isOpen: boolean; }; const BoardsList = (props: Props) => { const { isOpen } = props; - const { selectedBoardId, boardSearchText } = useAppSelector(selector); + const selectedBoardId = useAppSelector( + (s) => s.gallery.selectedBoardId + ); + const boardSearchText = useAppSelector( + (s) => s.gallery.boardSearchText + ); const { data: boards } = useListAllBoardsQuery(); const filteredBoards = boardSearchText ? boards?.filter((board) => diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx index 1e9d0257e1..193fdf0982 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx @@ -1,7 +1,5 @@ import { CloseIcon } from '@chakra-ui/icons'; import { Input, InputGroup, InputRightElement } from '@chakra-ui/react'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice'; @@ -9,14 +7,9 @@ import type { ChangeEvent, KeyboardEvent } from 'react'; import { memo, useCallback, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector([stateSelector], ({ gallery }) => { - const { boardSearchText } = gallery; - return { boardSearchText }; -}); - const BoardsSearch = () => { const dispatch = useAppDispatch(); - const { boardSearchText } = useAppSelector(selector); + const boardSearchText = useAppSelector((s) => s.gallery.boardSearchText); const inputRef = useRef(null); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx index 11cd901a4e..61df0af648 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx @@ -10,7 +10,6 @@ import { } from '@chakra-ui/react'; import { skipToken } from '@reduxjs/toolkit/query'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIDroppable from 'common/components/IAIDroppable'; import { InvText } from 'common/components/InvText/wrapper'; @@ -22,6 +21,7 @@ import BoardContextMenu from 'features/gallery/components/Boards/BoardContextMen import { autoAddBoardIdChanged, boardIdSelected, + selectGallerySlice, } from 'features/gallery/store/gallerySlice'; import { memo, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -56,7 +56,7 @@ const GalleryBoard = ({ const dispatch = useAppDispatch(); const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ gallery }) => { + createMemoizedSelector(selectGallerySlice, (gallery) => { const isSelectedForAutoAdd = board.board_id === gallery.autoAddBoardId; const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick; diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx index 1484db850b..00a7f66832 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx @@ -1,6 +1,4 @@ import { Box, Flex, Image } from '@chakra-ui/react'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import InvokeAILogoImage from 'assets/images/logo.png'; import IAIDroppable from 'common/components/IAIDroppable'; @@ -26,14 +24,12 @@ interface Props { isSelected: boolean; } -const selector = createMemoizedSelector(stateSelector, ({ gallery }) => { - const { autoAddBoardId, autoAssignBoardOnClick } = gallery; - return { autoAddBoardId, autoAssignBoardOnClick }; -}); - const NoBoardBoard = memo(({ isSelected }: Props) => { const dispatch = useAppDispatch(); - const { autoAddBoardId, autoAssignBoardOnClick } = useAppSelector(selector); + const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId); + const autoAssignBoardOnClick = useAppSelector( + (s) => s.gallery.autoAssignBoardOnClick + ); const boardName = useBoardName('none'); const handleSelectBoard = useCallback(() => { dispatch(boardIdSelected({ boardId: 'none' })); diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx index 9fb5771abd..4d25e3dfa9 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx @@ -1,7 +1,6 @@ import { Flex, Skeleton } from '@chakra-ui/react'; import { skipToken } from '@reduxjs/toolkit/query'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { InvAlertDialog, @@ -13,9 +12,13 @@ import { } from 'common/components/InvAlertDialog/wrapper'; import { InvButton } from 'common/components/InvButton/InvButton'; import { InvText } from 'common/components/InvText/wrapper'; +import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; +import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice'; import ImageUsageMessage from 'features/deleteImageModal/components/ImageUsageMessage'; import { getImageUsage } from 'features/deleteImageModal/store/selectors'; import type { ImageUsage } from 'features/deleteImageModal/store/types'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { some } from 'lodash-es'; import { memo, useCallback, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; @@ -35,26 +38,34 @@ const DeleteBoardModal = (props: Props) => { const { boardToDelete, setBoardToDelete } = props; const { t } = useTranslation(); const canRestoreDeletedImagesFromBin = useAppSelector( - (state) => state.config.canRestoreDeletedImagesFromBin + (s) => s.config.canRestoreDeletedImagesFromBin ); const { currentData: boardImageNames, isFetching: isFetchingBoardNames } = useListAllImageNamesForBoardQuery(boardToDelete?.board_id ?? skipToken); const selectImageUsageSummary = useMemo( () => - createMemoizedSelector([stateSelector], (state) => { - const allImageUsage = (boardImageNames ?? []).map((imageName) => - getImageUsage(state, imageName) - ); + createMemoizedSelector( + [ + selectGenerationSlice, + selectCanvasSlice, + selectNodesSlice, + selectControlAdaptersSlice, + ], + (generation, canvas, nodes, controlAdapters) => { + const allImageUsage = (boardImageNames ?? []).map((imageName) => + getImageUsage(generation, canvas, nodes, controlAdapters, imageName) + ); - const imageUsageSummary: ImageUsage = { - isInitialImage: some(allImageUsage, (i) => i.isInitialImage), - isCanvasImage: some(allImageUsage, (i) => i.isCanvasImage), - isNodesImage: some(allImageUsage, (i) => i.isNodesImage), - isControlImage: some(allImageUsage, (i) => i.isControlImage), - }; - return { imageUsageSummary }; - }), + const imageUsageSummary: ImageUsage = { + isInitialImage: some(allImageUsage, (i) => i.isInitialImage), + isCanvasImage: some(allImageUsage, (i) => i.isCanvasImage), + isNodesImage: some(allImageUsage, (i) => i.isNodesImage), + isControlImage: some(allImageUsage, (i) => i.isControlImage), + }; + return { imageUsageSummary }; + } + ), [boardImageNames] ); diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx index cd37070374..dee0ee0a94 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageButtons.tsx @@ -3,7 +3,6 @@ import { skipToken } from '@reduxjs/toolkit/query'; import { useAppToaster } from 'app/components/Toaster'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { upscaleRequested } from 'app/store/middleware/listenerMiddleware/listeners/upscaleRequested'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; @@ -13,13 +12,17 @@ import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteIm import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice'; import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems'; import { sentImageToImg2Img } from 'features/gallery/store/actions'; +import { selectGallerySlice } from 'features/gallery/store/gallerySlice'; import ParamUpscalePopover from 'features/parameters/components/Upscale/ParamUpscaleSettings'; import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters'; import { initialImageSelected } from 'features/parameters/store/actions'; import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; +import { selectConfigSlice } from 'features/system/store/configSlice'; +import { selectSystemSlice } from 'features/system/store/systemSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { + selectUiSlice, setShouldShowImageDetails, setShouldShowProgressInViewer, } from 'features/ui/store/uiSlice'; @@ -40,8 +43,14 @@ import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata'; const currentImageButtonsSelector = createMemoizedSelector( - [stateSelector, activeTabNameSelector], - ({ gallery, system, ui, config }, activeTabName) => { + [ + selectGallerySlice, + selectSystemSlice, + selectUiSlice, + selectConfigSlice, + activeTabNameSelector, + ], + (gallery, system, ui, config, activeTabName) => { const { isConnected, shouldConfirmOnDelete, denoiseProgress } = system; const { diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImagePreview.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImagePreview.tsx index bfe6017815..acd0da3f27 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImagePreview.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImagePreview.tsx @@ -1,7 +1,6 @@ import { Box, Flex } from '@chakra-ui/react'; import { skipToken } from '@reduxjs/toolkit/query'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import IAIDndImage from 'common/components/IAIDndImage'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; @@ -14,6 +13,8 @@ import ImageMetadataViewer from 'features/gallery/components/ImageMetadataViewer import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons'; import { useNextPrevImage } from 'features/gallery/hooks/useNextPrevImage'; import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors'; +import { selectSystemSlice } from 'features/system/store/systemSlice'; +import { selectUiSlice } from 'features/ui/store/uiSlice'; import type { AnimationProps } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion'; import type { CSSProperties } from 'react'; @@ -24,8 +25,10 @@ import { FaImage } from 'react-icons/fa'; import { useGetImageDTOQuery } from 'services/api/endpoints/images'; export const imagesSelector = createMemoizedSelector( - [stateSelector, selectLastSelectedImage], - ({ ui, system }, lastSelectedImage) => { + selectUiSlice, + selectSystemSlice, + selectLastSelectedImage, + (ui, system, lastSelectedImage) => { const { shouldShowImageDetails, shouldHidePreview, diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/ProgressImage.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/ProgressImage.tsx index d36f6517b6..94147e6751 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/ProgressImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/ProgressImage.tsx @@ -5,10 +5,10 @@ import { memo, useMemo } from 'react'; const CurrentImagePreview = () => { const progress_image = useAppSelector( - (state) => state.system.denoiseProgress?.progress_image + (s) => s.system.denoiseProgress?.progress_image ); const shouldAntialiasProgressImage = useAppSelector( - (state) => state.system.shouldAntialiasProgressImage + (s) => s.system.shouldAntialiasProgressImage ); const sx = useMemo( diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx b/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx index b6553ab232..a91b7d557f 100644 --- a/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx @@ -1,17 +1,9 @@ import { ChevronUpIcon } from '@chakra-ui/icons'; import { Button, Flex, Spacer } from '@chakra-ui/react'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { memo, useMemo } from 'react'; import { useBoardName } from 'services/api/hooks/useBoardName'; -const selector = createMemoizedSelector([stateSelector], (state) => { - const { selectedBoardId } = state.gallery; - - return { selectedBoardId }; -}); - type Props = { isOpen: boolean; onToggle: () => void; @@ -19,7 +11,7 @@ type Props = { const GalleryBoardName = (props: Props) => { const { isOpen, onToggle } = props; - const { selectedBoardId } = useAppSelector(selector); + const selectedBoardId = useAppSelector((s) => s.gallery.selectedBoardId); const boardName = useBoardName(selectedBoardId); const formattedBoardName = useMemo(() => { diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx b/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx index 6b93ca0e03..0b9e0b696d 100644 --- a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx @@ -1,6 +1,5 @@ import { Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvCheckbox } from 'common/components/InvCheckbox/wrapper'; import { InvControl } from 'common/components/InvControl/InvControl'; @@ -15,6 +14,7 @@ import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSwitch } from 'common/components/InvSwitch/wrapper'; import { autoAssignBoardOnClickChanged, + selectGallerySlice, setGalleryImageMinimumWidth, shouldAutoSwitchChanged, } from 'features/gallery/store/gallerySlice'; @@ -25,9 +25,9 @@ import { FaWrench } from 'react-icons/fa'; import BoardAutoAddSelect from './Boards/BoardAutoAddSelect'; -const selector = createMemoizedSelector([stateSelector], (state) => { +const selector = createMemoizedSelector(selectGallerySlice, (gallery) => { const { galleryImageMinimumWidth, shouldAutoSwitch, autoAssignBoardOnClick } = - state.gallery; + gallery; return { galleryImageMinimumWidth, diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageContextMenu.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageContextMenu.tsx index b46dfb2d03..e60c9372d6 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageContextMenu.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageContextMenu.tsx @@ -1,5 +1,3 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu'; import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu'; @@ -16,14 +14,8 @@ type Props = { children: InvContextMenuProps['children']; }; -const selector = createMemoizedSelector([stateSelector], ({ gallery }) => { - const selectionCount = gallery.selection.length; - - return { selectionCount }; -}); - const ImageContextMenu = ({ imageDTO, children }: Props) => { - const { selectionCount } = useAppSelector(selector); + const selectionCount = useAppSelector((s) => s.gallery.selection.length); const skipEvent = useCallback((e: MouseEvent) => { e.preventDefault(); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx index 1d32bac5b9..2361e5a294 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx @@ -22,7 +22,7 @@ import { const MultipleSelectionMenuItems = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const selection = useAppSelector((state) => state.gallery.selection); + const selection = useAppSelector((s) => s.gallery.selection); const customStarUi = useStore($customStarUI); const isBulkDownloadEnabled = diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx index b121a73b3e..fed5eecce8 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx @@ -8,9 +8,7 @@ import { VStack, } from '@chakra-ui/react'; import { useStore } from '@nanostores/react'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { $galleryHeader } from 'app/store/nanostores/galleryHeader'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvButton } from 'common/components/InvButton/InvButton'; import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup'; @@ -24,19 +22,11 @@ import GalleryBoardName from './GalleryBoardName'; import GallerySettingsPopover from './GallerySettingsPopover'; import GalleryImageGrid from './ImageGrid/GalleryImageGrid'; -const selector = createMemoizedSelector([stateSelector], (state) => { - const { galleryView } = state.gallery; - - return { - galleryView, - }; -}); - const ImageGalleryContent = () => { const { t } = useTranslation(); const resizeObserverRef = useRef(null); const galleryGridRef = useRef(null); - const { galleryView } = useAppSelector(selector); + const galleryView = useAppSelector((s) => s.gallery.galleryView); const dispatch = useAppDispatch(); const galleryHeader = useStore($galleryHeader); const { isOpen: isBoardListOpen, onToggle: onToggleBoardList } = diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImageGrid.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImageGrid.tsx index bb181e6118..a2719ad46a 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImageGrid.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImageGrid.tsx @@ -45,7 +45,7 @@ const GalleryImageGrid = () => { overlayScrollbarsParams ); const selectedBoardId = useAppSelector( - (state) => state.gallery.selectedBoardId + (s) => s.gallery.selectedBoardId ); const { currentViewTotal } = useBoardTotal(selectedBoardId); const queryArgs = useAppSelector(selectListImagesBaseQueryArgs); diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useMultiselect.ts b/invokeai/frontend/web/src/features/gallery/hooks/useMultiselect.ts index e50e59ce4f..960937201e 100644 --- a/invokeai/frontend/web/src/features/gallery/hooks/useMultiselect.ts +++ b/invokeai/frontend/web/src/features/gallery/hooks/useMultiselect.ts @@ -1,8 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { selectListImagesBaseQueryArgs } from 'features/gallery/store/gallerySelectors'; -import { selectionChanged } from 'features/gallery/store/gallerySlice'; +import { selectGallerySlice, selectionChanged } from 'features/gallery/store/gallerySlice'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import type { MouseEvent } from 'react'; import { useCallback, useMemo } from 'react'; @@ -11,13 +10,11 @@ import type { ImageDTO } from 'services/api/types'; import { imagesSelectors } from 'services/api/util'; const selector = createMemoizedSelector( - [stateSelector, selectListImagesBaseQueryArgs], - ({ gallery }, queryArgs) => { - const selection = gallery.selection; - + [selectGallerySlice, selectListImagesBaseQueryArgs], + (gallery, queryArgs) => { return { queryArgs, - selection, + selection: gallery.selection, }; } ); diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useNextPrevImage.ts b/invokeai/frontend/web/src/features/gallery/hooks/useNextPrevImage.ts index d3ba7550d6..f5bb376acd 100644 --- a/invokeai/frontend/web/src/features/gallery/hooks/useNextPrevImage.ts +++ b/invokeai/frontend/web/src/features/gallery/hooks/useNextPrevImage.ts @@ -1,5 +1,5 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; +import type { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { selectListImagesBaseQueryArgs } from 'features/gallery/store/gallerySelectors'; import { imageSelected } from 'features/gallery/store/gallerySlice'; @@ -29,7 +29,7 @@ export const $useNextPrevImageState = map({ }); export const nextPrevImageButtonsSelector = createMemoizedSelector( - [stateSelector, selectListImagesBaseQueryArgs], + [(state: RootState) => state, selectListImagesBaseQueryArgs], (state, baseQueryArgs) => { const { data, status } = imagesApi.endpoints.listImages.select(baseQueryArgs)(state); diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts index d7155e785c..11a682067c 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts @@ -12,7 +12,7 @@ export const gallerySelector = (state: RootState) => state.gallery; export const selectLastSelectedImage = createMemoizedSelector( (state: RootState) => state, - (state) => state.gallery.selection[state.gallery.selection.length - 1] + (s) => s.gallery.selection[state.gallery.selection.length - 1] ); export const selectListImagesBaseQueryArgs = createMemoizedSelector( diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index 5af0bd28a7..1dc8534323 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice, isAnyOf } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import { uniqBy } from 'lodash-es'; import { boardsApi } from 'services/api/endpoints/boards'; import { imagesApi } from 'services/api/endpoints/images'; @@ -103,3 +104,5 @@ const isAnyBoardDeleted = isAnyOf( imagesApi.endpoints.deleteBoard.matchFulfilled, imagesApi.endpoints.deleteBoardAndImages.matchFulfilled ); + +export const selectGallerySlice = (state: RootState) => state.gallery; diff --git a/invokeai/frontend/web/src/features/hrf/components/HrfSettings.tsx b/invokeai/frontend/web/src/features/hrf/components/HrfSettings.tsx index 464181ac60..b2517dc09c 100644 --- a/invokeai/frontend/web/src/features/hrf/components/HrfSettings.tsx +++ b/invokeai/frontend/web/src/features/hrf/components/HrfSettings.tsx @@ -9,7 +9,7 @@ import ParamHrfToggle from './ParamHrfToggle'; export const HrfSettings = memo(() => { const isHRFFeatureEnabled = useFeatureStatus('hrf').isFeatureEnabled; - const hrfEnabled = useAppSelector((state) => state.hrf.hrfEnabled); + const hrfEnabled = useAppSelector((s) => s.hrf.hrfEnabled); if (!isHRFFeatureEnabled) { return null; diff --git a/invokeai/frontend/web/src/features/hrf/components/ParamHrfMethod.tsx b/invokeai/frontend/web/src/features/hrf/components/ParamHrfMethod.tsx index 02c7527b0d..6eafd57a2d 100644 --- a/invokeai/frontend/web/src/features/hrf/components/ParamHrfMethod.tsx +++ b/invokeai/frontend/web/src/features/hrf/components/ParamHrfMethod.tsx @@ -18,7 +18,7 @@ const options: InvSelectOption[] = [ const ParamHrfMethodSelect = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const hrfMethod = useAppSelector((state) => state.hrf.hrfMethod); + const hrfMethod = useAppSelector((s) => s.hrf.hrfMethod); const onChange = useCallback( (v) => { diff --git a/invokeai/frontend/web/src/features/hrf/components/ParamHrfStrength.tsx b/invokeai/frontend/web/src/features/hrf/components/ParamHrfStrength.tsx index 274a1a2e8b..17eecdbe3d 100644 --- a/invokeai/frontend/web/src/features/hrf/components/ParamHrfStrength.tsx +++ b/invokeai/frontend/web/src/features/hrf/components/ParamHrfStrength.tsx @@ -1,27 +1,31 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; -import { setHrfStrength } from 'features/hrf/store/hrfSlice'; +import { selectHrfSlice, setHrfStrength } from 'features/hrf/store/hrfSlice'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector([stateSelector], ({ hrf, config }) => { - const { initial, min, sliderMax, inputMax, fineStep, coarseStep } = - config.sd.hrfStrength; - const { hrfStrength } = hrf; +const selector = createMemoizedSelector( + selectHrfSlice, + selectConfigSlice, + (hrf, config) => { + const { initial, min, sliderMax, inputMax, fineStep, coarseStep } = + config.sd.hrfStrength; + const { hrfStrength } = hrf; - return { - hrfStrength, - initial, - min, - sliderMax, - inputMax, - step: coarseStep, - fineStep, - }; -}); + return { + hrfStrength, + initial, + min, + sliderMax, + inputMax, + step: coarseStep, + fineStep, + }; + } +); const ParamHrfStrength = () => { const { hrfStrength, initial, min, sliderMax, step, fineStep } = diff --git a/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts b/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts index 98d84b1db0..c3e6af88a3 100644 --- a/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts +++ b/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import type { ParameterHRFMethod, ParameterStrength, @@ -38,3 +39,5 @@ export const hrfSlice = createSlice({ export const { setHrfEnabled, setHrfStrength, setHrfMethod } = hrfSlice.actions; export default hrfSlice.reducer; + +export const selectHrfSlice = (state: RootState) => state.hrf; diff --git a/invokeai/frontend/web/src/features/lora/components/LoRAList.tsx b/invokeai/frontend/web/src/features/lora/components/LoRAList.tsx index 66ac489681..545f8f48ad 100644 --- a/invokeai/frontend/web/src/features/lora/components/LoRAList.tsx +++ b/invokeai/frontend/web/src/features/lora/components/LoRAList.tsx @@ -1,12 +1,12 @@ import { Flex } from '@chakra-ui/layout'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { LoRACard } from 'features/lora/components/LoRACard'; +import { selectLoraSlice } from 'features/lora/store/loraSlice'; import { map } from 'lodash-es'; import { memo } from 'react'; -const selector = createMemoizedSelector(stateSelector, ({ lora }) => { +const selector = createMemoizedSelector(selectLoraSlice, (lora) => { return { lorasArray: map(lora.loras) }; }); diff --git a/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx b/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx index d25a4f6963..1daa90c27e 100644 --- a/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx +++ b/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx @@ -1,17 +1,16 @@ import type { ChakraProps } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSelect } from 'common/components/InvSelect/InvSelect'; import { useGroupedModelInvSelect } from 'common/components/InvSelect/useGroupedModelInvSelect'; -import { loraAdded } from 'features/lora/store/loraSlice'; +import { loraAdded, selectLoraSlice } from 'features/lora/store/loraSlice'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import type { LoRAModelConfigEntity } from 'services/api/endpoints/models'; import { useGetLoRAModelsQuery } from 'services/api/endpoints/models'; -const selector = createMemoizedSelector(stateSelector, ({ lora }) => ({ +const selector = createMemoizedSelector(selectLoraSlice, (lora) => ({ addedLoRAs: lora.loras, })); @@ -21,7 +20,7 @@ const LoRASelect = () => { const { t } = useTranslation(); const { addedLoRAs } = useAppSelector(selector); const currentBaseModel = useAppSelector( - (state) => state.generation.model?.base_model + (s) => s.generation.model?.base_model ); const getIsDisabled = (lora: LoRAModelConfigEntity): boolean => { diff --git a/invokeai/frontend/web/src/features/lora/store/loraSlice.ts b/invokeai/frontend/web/src/features/lora/store/loraSlice.ts index 1119ee571d..a8025224c5 100644 --- a/invokeai/frontend/web/src/features/lora/store/loraSlice.ts +++ b/invokeai/frontend/web/src/features/lora/store/loraSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import type { ParameterLoRAModel } from 'features/parameters/types/parameterSchemas'; import type { LoRAModelConfigEntity } from 'services/api/endpoints/models'; @@ -74,3 +75,5 @@ export const { } = loraSlice.actions; export default loraSlice.reducer; + +export const selectLoraSlice = (state: RootState) => state.lora; diff --git a/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts b/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts index e9408f5f9f..ce5dca40c7 100644 --- a/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts +++ b/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; type ModelManagerState = { searchFolder: string | null; @@ -28,3 +29,5 @@ export const { setSearchFolder, setAdvancedAddScanModel } = modelManagerSlice.actions; export default modelManagerSlice.reducer; + +export const selectModelManagerSlice = (state: RootState) => state.modelmanager; diff --git a/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx b/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx index 3709ab0837..54f525f3e0 100644 --- a/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx @@ -39,7 +39,7 @@ const exit: AnimationProps['exit'] = { }; const NodeEditor = () => { - const isReady = useAppSelector((state) => state.nodes.isReady); + const isReady = useAppSelector((s) => s.nodes.isReady); const { t } = useTranslation(); return ( { const inputRef = useRef(null); const fieldFilter = useAppSelector( - (state) => state.nodes.connectionStartFieldType + (s) => s.nodes.connectionStartFieldType ); const handleFilter = useAppSelector( - (state) => state.nodes.connectionStartParams?.handleType + (s) => s.nodes.connectionStartParams?.handleType ); const selector = createMemoizedSelector( - [stateSelector], - ({ nodeTemplates }) => { + selectNodeTemplatesSlice, + (nodeTemplates) => { // If we have a connection in progress, we need to filter the node choices const filteredNodeTemplates = fieldFilter ? filter(nodeTemplates.templates, (template) => { @@ -130,7 +129,7 @@ const AddNodePopover = () => { ); const { options } = useAppSelector(selector); - const isOpen = useAppSelector((state) => state.nodes.isAddNodePopoverOpen); + const isOpen = useAppSelector((s) => s.nodes.isAddNodePopoverOpen); const addNode = useCallback( (nodeType: string) => { diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx index b25ee9da8a..26cd524d24 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx @@ -71,13 +71,13 @@ const snapGrid: [number, number] = [25, 25]; export const Flow = memo(() => { const dispatch = useAppDispatch(); - const nodes = useAppSelector((state) => state.nodes.nodes); - const edges = useAppSelector((state) => state.nodes.edges); - const viewport = useAppSelector((state) => state.nodes.viewport); + const nodes = useAppSelector((s) => s.nodes.nodes); + const edges = useAppSelector((s) => s.nodes.edges); + const viewport = useAppSelector((s) => s.nodes.viewport); const shouldSnapToGrid = useAppSelector( - (state) => state.nodes.shouldSnapToGrid + (s) => s.nodes.shouldSnapToGrid ); - const selectionMode = useAppSelector((state) => state.nodes.selectionMode); + const selectionMode = useAppSelector((s) => s.nodes.selectionMode); const flowWrapper = useRef(null); const cursorPosition = useRef(null); const isValidConnection = useIsValidConnection(); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx index fcff72b564..06f0b9eb97 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx @@ -1,14 +1,14 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar'; import { getFieldColor } from 'features/nodes/components/flow/edges/util/getEdgeColor'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import type { CSSProperties } from 'react'; import { memo } from 'react'; import type { ConnectionLineComponentProps } from 'reactflow'; import { getBezierPath } from 'reactflow'; -const selector = createMemoizedSelector(stateSelector, ({ nodes }) => { +const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { const { shouldAnimateEdges, connectionStartFieldType, shouldColorEdges } = nodes; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts b/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts index e7395fb35f..89d9e514ff 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts +++ b/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts @@ -1,6 +1,6 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { getFieldColor } from './getEdgeColor'; @@ -12,7 +12,7 @@ export const makeEdgeSelector = ( targetHandleId: string | null | undefined, selected?: boolean ) => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const sourceNode = nodes.nodes.find((node) => node.id === source); const targetNode = nodes.nodes.find((node) => node.id === target); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx index 2cca753fb1..6e12957874 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx @@ -1,13 +1,14 @@ import { Flex, Image } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import IAIDndImage from 'common/components/IAIDndImage'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import { InvText } from 'common/components/InvText/wrapper'; import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons'; +import { selectGallerySlice } from 'features/gallery/store/gallerySlice'; import NodeWrapper from 'features/nodes/components/flow/nodes/common/NodeWrapper'; import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants'; +import { selectSystemSlice } from 'features/system/store/systemSlice'; import type { AnimationProps } from 'framer-motion'; import { motion } from 'framer-motion'; import type { CSSProperties, PropsWithChildren } from 'react'; @@ -16,8 +17,9 @@ import { useTranslation } from 'react-i18next'; import type { NodeProps } from 'reactflow'; const selector = createMemoizedSelector( - stateSelector, - ({ system, gallery }) => { + selectSystemSlice, + selectGallerySlice, + (system, gallery) => { const imageDTO = gallery.selection[gallery.selection.length - 1]; return { diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx index 186428f446..56addaf9a6 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx @@ -1,10 +1,10 @@ import type { SystemStyleObject } from '@chakra-ui/react'; import { Badge, CircularProgress, Flex, Icon, Image } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { InvText } from 'common/components/InvText/wrapper'; import { InvTooltip } from 'common/components/InvTooltip/InvTooltip'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants'; import type { NodeExecutionState } from 'features/nodes/types/invocation'; import { zNodeStatus } from 'features/nodes/types/invocation'; @@ -29,8 +29,8 @@ const InvocationNodeStatusIndicator = ({ nodeId }: Props) => { const selectNodeExecutionState = useMemo( () => createMemoizedSelector( - stateSelector, - ({ nodes }) => nodes.nodeExecutionStates[nodeId] + selectNodesSlice, + (nodes) => nodes.nodeExecutionStates[nodeId] ), [nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx index e80a6a31bc..763227b4fa 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx @@ -1,7 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import InvocationNode from 'features/nodes/components/flow/nodes/Invocation/InvocationNode'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import type { InvocationNodeData } from 'features/nodes/types/invocation'; import { memo, useMemo } from 'react'; import type { NodeProps } from 'reactflow'; @@ -14,7 +14,7 @@ const InvocationNodeWrapper = (props: NodeProps) => { const hasTemplateSelector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodeTemplates }) => + createMemoizedSelector(selectNodeTemplatesSlice, (nodeTemplates) => Boolean(nodeTemplates.templates[type]) ), [type] diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldContextMenu.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldContextMenu.tsx index 0afa3b8b1a..72f7832426 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldContextMenu.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldContextMenu.tsx @@ -1,5 +1,4 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu'; import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu'; @@ -10,6 +9,7 @@ import { useFieldInputKind } from 'features/nodes/hooks/useFieldInputKind'; import { useFieldLabel } from 'features/nodes/hooks/useFieldLabel'; import { useFieldTemplateTitle } from 'features/nodes/hooks/useFieldTemplateTitle'; import { + selectWorkflowSlice, workflowExposedFieldAdded, workflowExposedFieldRemoved, } from 'features/nodes/store/workflowSlice'; @@ -38,7 +38,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ workflow }) => { + createMemoizedSelector(selectWorkflowSlice, (workflow) => { const isExposed = Boolean( workflow.exposedFields.find( (f) => f.nodeId === nodeId && f.fieldName === fieldName diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldInputComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldInputComponent.tsx index cba0a9fb15..2c0cefb681 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldInputComponent.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldInputComponent.tsx @@ -26,7 +26,7 @@ const ImageFieldInputComponent = ( ) => { const { nodeId, field } = props; const dispatch = useAppDispatch(); - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const { currentData: imageDTO, isError } = useGetImageDTOQuery( field.value?.image_name ?? skipToken ); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx index 754b404237..b4a1d52ca1 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx @@ -1,12 +1,11 @@ import type { ChakraProps } from '@chakra-ui/react'; import { Box, useToken } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import NodeSelectionOverlay from 'common/components/NodeSelectionOverlay'; import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger'; import { useMouseOverNode } from 'features/nodes/hooks/useMouseOverNode'; -import { nodeExclusivelySelected } from 'features/nodes/store/nodesSlice'; +import { nodeExclusivelySelected , selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { DRAG_HANDLE_CLASSNAME, NODE_WIDTH, @@ -29,8 +28,8 @@ const NodeWrapper = (props: NodeWrapperProps) => { const selectIsInProgress = useMemo( () => createMemoizedSelector( - stateSelector, - ({ nodes }) => + selectNodesSlice, + (nodes) => nodes.nodeExecutionStates[nodeId]?.status === zNodeStatus.enum.IN_PROGRESS ), @@ -47,7 +46,7 @@ const NodeWrapper = (props: NodeWrapperProps) => { const dispatch = useAppDispatch(); - const opacity = useAppSelector((state) => state.nodes.nodeOpacity); + const opacity = useAppSelector((s) => s.nodes.nodeOpacity); const { onCloseGlobal } = useGlobalMenuCloseTrigger(); const handleClick = useCallback( diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx index d1195ce9b2..688e2ce549 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; const NodeOpacitySlider = () => { const dispatch = useAppDispatch(); - const nodeOpacity = useAppSelector((state) => state.nodes.nodeOpacity); + const nodeOpacity = useAppSelector((s) => s.nodes.nodeOpacity); const { t } = useTranslation(); const handleChange = useCallback( diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx index e238aac95b..e4ac98a109 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx @@ -20,10 +20,10 @@ const ViewportControls = () => { const { zoomIn, zoomOut, fitView } = useReactFlow(); const dispatch = useAppDispatch(); // const shouldShowFieldTypeLegend = useAppSelector( - // (state) => state.nodes.shouldShowFieldTypeLegend + // (s) => s.nodes.shouldShowFieldTypeLegend // ); const shouldShowMinimapPanel = useAppSelector( - (state) => state.nodes.shouldShowMinimapPanel + (s) => s.nodes.shouldShowMinimapPanel ); const handleClickedZoomIn = useCallback(() => { diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/WorkflowName.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/WorkflowName.tsx index 9befa32d84..b08afa5290 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/WorkflowName.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/WorkflowName.tsx @@ -6,8 +6,8 @@ import { useTranslation } from 'react-i18next'; const TopCenterPanel = () => { const { t } = useTranslation(); - const name = useAppSelector((state) => state.workflow.name); - const isTouched = useAppSelector((state) => state.workflow.isTouched); + const name = useAppSelector((s) => s.workflow.name); + const isTouched = useAppSelector((s) => s.workflow.isTouched); const isWorkflowLibraryEnabled = useFeatureStatus('workflowLibrary').isFeatureEnabled; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx index 2f55ace94a..8a347c534d 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx @@ -1,6 +1,5 @@ import { Divider, Flex, Heading, useDisclosure } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { @@ -15,17 +14,16 @@ import { InvSwitch } from 'common/components/InvSwitch/wrapper'; import ReloadNodeTemplatesButton from 'features/nodes/components/flow/panels/TopRightPanel/ReloadSchemaButton'; import { selectionModeChanged, - shouldAnimateEdgesChanged, + selectNodesSlice, shouldAnimateEdgesChanged, shouldColorEdgesChanged, shouldSnapToGridChanged, - shouldValidateGraphChanged, -} from 'features/nodes/store/nodesSlice'; + shouldValidateGraphChanged } from 'features/nodes/store/nodesSlice'; import type { ChangeEvent, ReactNode } from 'react'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { SelectionMode } from 'reactflow'; -const selector = createMemoizedSelector(stateSelector, ({ nodes }) => { +const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { const { shouldAnimateEdges, shouldValidateGraph, diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx index f0ceed6ffe..3478cc2056 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx @@ -1,11 +1,11 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { memo } from 'react'; -const selector = createMemoizedSelector(stateSelector, ({ nodes }) => { +const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1]; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx index 6f0b99c401..058610d580 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx @@ -1,6 +1,5 @@ import { Box, Flex, HStack } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import { InvControl } from 'common/components/InvControl/InvControl'; @@ -8,6 +7,8 @@ import { InvText } from 'common/components/InvText/wrapper'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import NotesTextarea from 'features/nodes/components/flow/nodes/Invocation/NotesTextarea'; import { useNodeNeedsUpdate } from 'features/nodes/hooks/useNodeNeedsUpdate'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -15,8 +16,9 @@ import { useTranslation } from 'react-i18next'; import EditableNodeTitle from './details/EditableNodeTitle'; const selector = createMemoizedSelector( - stateSelector, - ({ nodes, nodeTemplates }) => { + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1]; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx index 1e53aaa6b1..0a44c79a0b 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx @@ -1,10 +1,11 @@ import { Box, Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -14,8 +15,9 @@ import type { AnyResult } from 'services/events/types'; import ImageOutputPreview from './outputs/ImageOutputPreview'; const selector = createMemoizedSelector( - stateSelector, - ({ nodes, nodeTemplates }) => { + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1]; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx index ef2ad34908..cd37d685b2 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx @@ -1,14 +1,16 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( - stateSelector, - ({ nodes, nodeTemplates }) => { + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1]; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowGeneralTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowGeneralTab.tsx index 235ae8acbc..a54f25f8de 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowGeneralTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowGeneralTab.tsx @@ -1,6 +1,5 @@ import { Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControlGroup } from 'common/components/InvControl/InvControlGroup'; @@ -8,19 +7,18 @@ import { InvInput } from 'common/components/InvInput/InvInput'; import { InvTextarea } from 'common/components/InvTextarea/InvTextarea'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import { - workflowAuthorChanged, + selectWorkflowSlice, workflowAuthorChanged, workflowContactChanged, workflowDescriptionChanged, workflowNameChanged, workflowNotesChanged, workflowTagsChanged, - workflowVersionChanged, -} from 'features/nodes/store/workflowSlice'; + workflowVersionChanged } from 'features/nodes/store/workflowSlice'; import type { ChangeEvent } from 'react'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector(stateSelector, ({ workflow }) => { +const selector = createMemoizedSelector(selectWorkflowSlice, (workflow) => { const { author, name, description, tags, version, contact, notes } = workflow; return { diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLinearTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLinearTab.tsx index fdfc46ef6f..9cb2c2cf13 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLinearTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLinearTab.tsx @@ -1,21 +1,20 @@ import { Box, Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import LinearViewField from 'features/nodes/components/flow/nodes/Invocation/fields/LinearViewField'; +import { selectWorkflowSlice } from 'features/nodes/store/workflowSlice'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector(stateSelector, ({ workflow }) => { - return { - fields: workflow.exposedFields, - }; -}); +const selector = createMemoizedSelector( + selectWorkflowSlice, + (workflow) => workflow.exposedFields +); const WorkflowLinearTab = () => { - const { fields } = useAppSelector(selector); + const fields = useAppSelector(selector); const { t } = useTranslation(); return ( diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useAnyOrDirectInputFieldNames.ts b/invokeai/frontend/web/src/features/nodes/hooks/useAnyOrDirectInputFieldNames.ts index c5a3909e42..dbb9e1f799 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useAnyOrDirectInputFieldNames.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useAnyOrDirectInputFieldNames.ts @@ -1,6 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { getSortedFilteredFieldNames } from 'features/nodes/util/node/getSortedFilteredFieldNames'; import { TEMPLATE_BUILDER_MAP } from 'features/nodes/util/schema/buildFieldInputTemplate'; @@ -10,23 +11,27 @@ import { useMemo } from 'react'; export const useAnyOrDirectInputFieldNames = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return []; + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return []; + } + const nodeTemplate = nodeTemplates.templates[node.data.type]; + if (!nodeTemplate) { + return []; + } + const fields = map(nodeTemplate.inputs).filter( + (field) => + (['any', 'direct'].includes(field.input) || + field.type.isCollectionOrScalar) && + keys(TEMPLATE_BUILDER_MAP).includes(field.type.name) + ); + return getSortedFilteredFieldNames(fields); } - const nodeTemplate = nodeTemplates.templates[node.data.type]; - if (!nodeTemplate) { - return []; - } - const fields = map(nodeTemplate.inputs).filter( - (field) => - (['any', 'direct'].includes(field.input) || - field.type.isCollectionOrScalar) && - keys(TEMPLATE_BUILDER_MAP).includes(field.type.name) - ); - return getSortedFilteredFieldNames(fields); - }), + ), [nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts b/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts index 88d723fff9..7368074f4b 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts @@ -20,7 +20,7 @@ export const SHARED_NODE_PROPERTIES: Partial = { export const useBuildNode = () => { const nodeTemplates = useAppSelector( - (state) => state.nodeTemplates.templates + (s) => s.nodeTemplates.templates ); const flow = useReactFlow(); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useConnectionInputFieldNames.ts b/invokeai/frontend/web/src/features/nodes/hooks/useConnectionInputFieldNames.ts index 9091062193..22b7fe2778 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useConnectionInputFieldNames.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useConnectionInputFieldNames.ts @@ -1,6 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { getSortedFilteredFieldNames } from 'features/nodes/util/node/getSortedFilteredFieldNames'; import { TEMPLATE_BUILDER_MAP } from 'features/nodes/util/schema/buildFieldInputTemplate'; @@ -10,26 +11,30 @@ import { useMemo } from 'react'; export const useConnectionInputFieldNames = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return []; - } - const nodeTemplate = nodeTemplates.templates[node.data.type]; - if (!nodeTemplate) { - return []; - } + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return []; + } + const nodeTemplate = nodeTemplates.templates[node.data.type]; + if (!nodeTemplate) { + return []; + } - // get the visible fields - const fields = map(nodeTemplate.inputs).filter( - (field) => - (field.input === 'connection' && - !field.type.isCollectionOrScalar) || - !keys(TEMPLATE_BUILDER_MAP).includes(field.type.name) - ); + // get the visible fields + const fields = map(nodeTemplate.inputs).filter( + (field) => + (field.input === 'connection' && + !field.type.isCollectionOrScalar) || + !keys(TEMPLATE_BUILDER_MAP).includes(field.type.name) + ); - return getSortedFilteredFieldNames(fields); - }), + return getSortedFilteredFieldNames(fields); + } + ), [nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useConnectionState.ts b/invokeai/frontend/web/src/features/nodes/hooks/useConnectionState.ts index a197f885a4..05c92bb674 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useConnectionState.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useConnectionState.ts @@ -1,14 +1,14 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { makeConnectionErrorSelector } from 'features/nodes/store/util/makeIsConnectionValidSelector'; import { useMemo } from 'react'; import { useFieldType } from './useFieldType.ts'; const selectIsConnectionInProgress = createMemoizedSelector( - stateSelector, - ({ nodes }) => + selectNodesSlice, + (nodes) => nodes.connectionStartFieldType !== null && nodes.connectionStartParams !== null ); @@ -28,7 +28,7 @@ export const useConnectionState = ({ const selectIsConnected = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => + createMemoizedSelector(selectNodesSlice, (nodes) => Boolean( nodes.edges.filter((edge) => { return ( @@ -55,7 +55,7 @@ export const useConnectionState = ({ const selectIsConnectionStartField = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => + createMemoizedSelector(selectNodesSlice, (nodes) => Boolean( nodes.connectionStartParams?.nodeId === nodeId && nodes.connectionStartParams?.handleId === fieldName && diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useDoNodeVersionsMatch.ts b/invokeai/frontend/web/src/features/nodes/hooks/useDoNodeVersionsMatch.ts index d996926fa6..e12c35a69d 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useDoNodeVersionsMatch.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useDoNodeVersionsMatch.ts @@ -1,24 +1,29 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { compareVersions } from 'compare-versions'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useDoNodeVersionsMatch = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return false; + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return false; + } + const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; + if (!nodeTemplate?.version || !node.data?.version) { + return false; + } + return compareVersions(nodeTemplate.version, node.data.version) === 0; } - const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; - if (!nodeTemplate?.version || !node.data?.version) { - return false; - } - return compareVersions(nodeTemplate.version, node.data.version) === 0; - }), + ), [nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useDoesInputHaveValue.ts b/invokeai/frontend/web/src/features/nodes/hooks/useDoesInputHaveValue.ts index bd3d4e2770..cfe5c90d9c 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useDoesInputHaveValue.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useDoesInputHaveValue.ts @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useDoesInputHaveValue = (nodeId: string, fieldName: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldData.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldData.ts index 41083b3105..8b35a2d44b 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldData.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldData.ts @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useFieldInstance = (nodeId: string, fieldName: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputInstance.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputInstance.ts index a1761fda64..0793f1f952 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputInstance.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputInstance.ts @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useFieldInputInstance = (nodeId: string, fieldName: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputKind.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputKind.ts index 0b20559f07..70c4ec9544 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputKind.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputKind.ts @@ -1,21 +1,26 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useFieldInputKind = (nodeId: string, fieldName: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return; + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return; + } + const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; + const fieldTemplate = nodeTemplate?.inputs[fieldName]; + return fieldTemplate?.input; } - const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; - const fieldTemplate = nodeTemplate?.inputs[fieldName]; - return fieldTemplate?.input; - }), + ), [fieldName, nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputTemplate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputTemplate.ts index 5a381be1a8..46a22e107d 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputTemplate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputTemplate.ts @@ -1,20 +1,25 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useFieldInputTemplate = (nodeId: string, fieldName: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return; + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return; + } + const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; + return nodeTemplate?.inputs[fieldName]; } - const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; - return nodeTemplate?.inputs[fieldName]; - }), + ), [fieldName, nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldLabel.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldLabel.ts index 7bfb14779b..48eebacd33 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldLabel.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldLabel.ts @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useFieldLabel = (nodeId: string, fieldName: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputInstance.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputInstance.ts index 1d7630a5c8..8b71f1ea01 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputInstance.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputInstance.ts @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useFieldOutputInstance = (nodeId: string, fieldName: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputTemplate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputTemplate.ts index 4ad16b4308..e81c24f8c8 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputTemplate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputTemplate.ts @@ -1,20 +1,25 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useFieldOutputTemplate = (nodeId: string, fieldName: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return; + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return; + } + const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; + return nodeTemplate?.outputs[fieldName]; } - const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; - return nodeTemplate?.outputs[fieldName]; - }), + ), [fieldName, nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplate.ts index d018706f67..7588ebbbfc 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplate.ts @@ -1,6 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { KIND_MAP } from 'features/nodes/types/constants'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; @@ -12,14 +13,18 @@ export const useFieldTemplate = ( ) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return; + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return; + } + const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; + return nodeTemplate?.[KIND_MAP[kind]][fieldName]; } - const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; - return nodeTemplate?.[KIND_MAP[kind]][fieldName]; - }), + ), [fieldName, kind, nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplateTitle.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplateTitle.ts index 05897a7823..5862df020d 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplateTitle.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplateTitle.ts @@ -1,6 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { KIND_MAP } from 'features/nodes/types/constants'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; @@ -12,14 +13,18 @@ export const useFieldTemplateTitle = ( ) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return; + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return; + } + const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; + return nodeTemplate?.[KIND_MAP[kind]][fieldName]?.title; } - const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; - return nodeTemplate?.[KIND_MAP[kind]][fieldName]?.title; - }), + ), [fieldName, kind, nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldType.ts.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldType.ts.ts index 8c720fab01..13190f51c1 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldType.ts.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldType.ts.ts @@ -1,6 +1,6 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { KIND_MAP } from 'features/nodes/types/constants'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; @@ -12,7 +12,7 @@ export const useFieldType = ( ) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useGetNodesNeedUpdate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useGetNodesNeedUpdate.ts index 175aa31209..f614d78b5d 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useGetNodesNeedUpdate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useGetNodesNeedUpdate.ts @@ -1,17 +1,21 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate'; -const selector = createMemoizedSelector(stateSelector, (state) => - state.nodes.nodes.filter(isInvocationNode).some((node) => { - const template = state.nodeTemplates.templates[node.data.type]; - if (!template) { - return false; - } - return getNeedsUpdate(node, template); - }) +const selector = createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => + nodes.nodes.filter(isInvocationNode).some((node) => { + const template = nodeTemplates.templates[node.data.type]; + if (!template) { + return false; + } + return getNeedsUpdate(node, template); + }) ); export const useGetNodesNeedUpdate = () => { diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useHasImageOutput.ts b/invokeai/frontend/web/src/features/nodes/hooks/useHasImageOutput.ts index 59b64e6985..617e713c7c 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useHasImageOutput.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useHasImageOutput.ts @@ -1,6 +1,6 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { some } from 'lodash-es'; import { useMemo } from 'react'; @@ -8,7 +8,7 @@ import { useMemo } from 'react'; export const useHasImageOutput = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return false; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useIsIntermediate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useIsIntermediate.ts index 5cea0843ac..3a708ceb35 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useIsIntermediate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useIsIntermediate.ts @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useIsIntermediate = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return false; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts b/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts index 00cdbe4ac9..2c80de0e97 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts @@ -15,7 +15,7 @@ import { useReactFlow } from 'reactflow'; export const useIsValidConnection = () => { const flow = useReactFlow(); const shouldValidateGraph = useAppSelector( - (state) => state.nodes.shouldValidateGraph + (s) => s.nodes.shouldValidateGraph ); const isValidConnection = useCallback( ({ source, sourceHandle, target, targetHandle }: Connection): boolean => { diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeClassification.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeClassification.ts index 1af0c0ce1e..55979ddd96 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeClassification.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeClassification.ts @@ -1,20 +1,25 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useNodeClassification = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return false; + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return false; + } + const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; + return nodeTemplate?.classification; } - const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; - return nodeTemplate?.classification; - }), + ), [nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeData.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeData.ts index e4965b5741..c507def5ee 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeData.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeData.ts @@ -1,12 +1,12 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { useMemo } from 'react'; export const useNodeData = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); return node?.data; }), diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeLabel.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeLabel.ts index b322f3c104..6125ec2fd0 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeLabel.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeLabel.ts @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useNodeLabel = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return false; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeNeedsUpdate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeNeedsUpdate.ts index ae115e6e5c..cebf3eef22 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeNeedsUpdate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeNeedsUpdate.ts @@ -1,6 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate'; import { useMemo } from 'react'; @@ -8,14 +9,18 @@ import { useMemo } from 'react'; export const useNodeNeedsUpdate = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - const template = nodeTemplates.templates[node?.data.type ?? '']; - if (isInvocationNode(node) && template) { - return getNeedsUpdate(node, template); + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + const template = nodeTemplates.templates[node?.data.type ?? '']; + if (isInvocationNode(node) && template) { + return getNeedsUpdate(node, template); + } + return false; } - return false; - }), + ), [nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodePack.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodePack.ts index 12cb770e9f..f05a685d42 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodePack.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodePack.ts @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useNodePack = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return false; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplate.ts index c5ecfe0ed1..c9c635610d 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplate.ts @@ -1,16 +1,21 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { useMemo } from 'react'; export const useNodeTemplate = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; - return nodeTemplate; - }), + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + const nodeTemplate = nodeTemplates.templates[node?.data.type ?? '']; + return nodeTemplate; + } + ), [nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateByType.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateByType.ts index eff72add80..f6eb529d9c 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateByType.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateByType.ts @@ -1,6 +1,6 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import type { InvocationTemplate } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; @@ -8,8 +8,8 @@ export const useNodeTemplateByType = (type: string) => { const selector = useMemo( () => createMemoizedSelector( - stateSelector, - ({ nodeTemplates }): InvocationTemplate | undefined => { + selectNodeTemplatesSlice, + (nodeTemplates): InvocationTemplate | undefined => { return nodeTemplates.templates[type]; } ), diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateTitle.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateTitle.ts index 7aada521c4..2aff26cfca 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateTitle.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateTitle.ts @@ -1,23 +1,28 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useNodeTemplateTitle = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return false; - } - const nodeTemplate = node - ? nodeTemplates.templates[node.data.type] - : undefined; + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return false; + } + const nodeTemplate = node + ? nodeTemplates.templates[node.data.type] + : undefined; - return nodeTemplate?.title; - }), + return nodeTemplate?.title; + } + ), [nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldNames.ts b/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldNames.ts index fe57c11e1e..1320f07f83 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldNames.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldNames.ts @@ -1,6 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { getSortedFilteredFieldNames } from 'features/nodes/util/node/getSortedFilteredFieldNames'; import { map } from 'lodash-es'; @@ -9,18 +10,22 @@ import { useMemo } from 'react'; export const useOutputFieldNames = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes, nodeTemplates }) => { - const node = nodes.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return []; - } - const nodeTemplate = nodeTemplates.templates[node.data.type]; - if (!nodeTemplate) { - return []; - } + createMemoizedSelector( + selectNodesSlice, + selectNodeTemplatesSlice, + (nodes, nodeTemplates) => { + const node = nodes.nodes.find((node) => node.id === nodeId); + if (!isInvocationNode(node)) { + return []; + } + const nodeTemplate = nodeTemplates.templates[node.data.type]; + if (!nodeTemplate) { + return []; + } - return getSortedFilteredFieldNames(map(nodeTemplate.outputs)); - }), + return getSortedFilteredFieldNames(map(nodeTemplate.outputs)); + } + ), [nodeId] ); diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useUseCache.ts b/invokeai/frontend/web/src/features/nodes/hooks/useUseCache.ts index c1701064ce..f36b6bd0ab 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useUseCache.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useUseCache.ts @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; export const useUseCache = (nodeId: string) => { const selector = useMemo( () => - createMemoizedSelector(stateSelector, ({ nodes }) => { + createMemoizedSelector(selectNodesSlice, (nodes) => { const node = nodes.nodes.find((node) => node.id === nodeId); if (!isInvocationNode(node)) { return false; diff --git a/invokeai/frontend/web/src/features/nodes/store/nodeTemplatesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodeTemplatesSlice.ts index 8080dd8eca..13d2a70fd5 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodeTemplatesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodeTemplatesSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import type { InvocationTemplate } from 'features/nodes/types/invocation'; import type { NodeTemplatesState } from './types'; @@ -24,3 +25,6 @@ const nodesTemplatesSlice = createSlice({ export const { nodeTemplatesBuilt } = nodesTemplatesSlice.actions; export default nodesTemplatesSlice.reducer; + +export const selectNodeTemplatesSlice = (state: RootState) => + state.nodeTemplates; diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts index dbe2135dbd..bad01048b8 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice, isAnyOf } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import { workflowLoaded } from 'features/nodes/store/actions'; import { nodeTemplatesBuilt } from 'features/nodes/store/nodeTemplatesSlice'; import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants'; @@ -987,3 +988,5 @@ export const isAnyNodeOrEdgeMutation = isAnyOf( ); export default nodesSlice.reducer; + +export const selectNodesSlice = (state: RootState) => state.nodes; diff --git a/invokeai/frontend/web/src/features/nodes/store/util/makeIsConnectionValidSelector.ts b/invokeai/frontend/web/src/features/nodes/store/util/makeIsConnectionValidSelector.ts index d64905e61c..fe6f752533 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/makeIsConnectionValidSelector.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/makeIsConnectionValidSelector.ts @@ -1,5 +1,5 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import type { FieldType } from 'features/nodes/types/field'; import i18n from 'i18next'; import type { HandleType } from 'reactflow'; @@ -18,13 +18,13 @@ export const makeConnectionErrorSelector = ( handleType: HandleType, fieldType?: FieldType ) => { - return createMemoizedSelector(stateSelector, (state): string | undefined => { + return createMemoizedSelector(selectNodesSlice, (nodesSlice) => { if (!fieldType) { return i18n.t('nodes.noFieldType'); } const { connectionStartFieldType, connectionStartParams, nodes, edges } = - state.nodes; + nodesSlice; if (!connectionStartParams || !connectionStartFieldType) { return i18n.t('nodes.noConnectionInProgress'); diff --git a/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts b/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts index d4ae182052..dd2f3a94b9 100644 --- a/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import { workflowLoaded } from 'features/nodes/store/actions'; import { isAnyNodeOrEdgeMutation, @@ -120,3 +121,5 @@ export const { } = workflowSlice.actions; export default workflowSlice.reducer; + +export const selectWorkflowSlice = (state: RootState) => state.workflow; diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx b/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx index c53bd39820..a1e65b5ea9 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; const ParamCFGRescaleMultiplier = () => { const cfgRescaleMultiplier = useAppSelector( - (state) => state.generation.cfgRescaleMultiplier + (s) => s.generation.cfgRescaleMultiplier ); const dispatch = useAppDispatch(); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx index d0c630f93a..1789d5b55e 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx @@ -13,7 +13,7 @@ import { useGetAppConfigQuery } from 'services/api/endpoints/appInfo'; const ParamInfillMethod = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const infillMethod = useAppSelector((state) => state.generation.infillMethod); + const infillMethod = useAppSelector((s) => s.generation.infillMethod); const { data: appConfigData } = useGetAppConfigQuery(); const options = useMemo( () => diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillOptions.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillOptions.tsx index 20a1367631..16cbffe56a 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillOptions.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillOptions.tsx @@ -5,7 +5,7 @@ import ParamInfillPatchmatchDownscaleSize from './ParamInfillPatchmatchDownscale import ParamInfillTilesize from './ParamInfillTilesize'; const ParamInfillOptions = () => { - const infillMethod = useAppSelector((state) => state.generation.infillMethod); + const infillMethod = useAppSelector((s) => s.generation.infillMethod); if (infillMethod === 'tile') { return ; } diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillPatchmatchDownscaleSize.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillPatchmatchDownscaleSize.tsx index fe63edb3bf..ddef55f3cc 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillPatchmatchDownscaleSize.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillPatchmatchDownscaleSize.tsx @@ -1,5 +1,3 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; @@ -7,19 +5,12 @@ import { setInfillPatchmatchDownscaleSize } from 'features/parameters/store/gene import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector([stateSelector], ({ generation }) => { - const { infillPatchmatchDownscaleSize, infillMethod } = generation; - - return { - infillPatchmatchDownscaleSize, - infillMethod, - }; -}); - const ParamInfillPatchmatchDownscaleSize = () => { const dispatch = useAppDispatch(); - const { infillPatchmatchDownscaleSize, infillMethod } = - useAppSelector(selector); + const infillMethod = useAppSelector((s) => s.generation.infillMethod); + const infillPatchmatchDownscaleSize = useAppSelector( + (s) => s.generation.infillPatchmatchDownscaleSize + ); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillTilesize.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillTilesize.tsx index db9f13949d..09c215036f 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillTilesize.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillTilesize.tsx @@ -1,5 +1,3 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; @@ -7,18 +5,10 @@ import { setInfillTileSize } from 'features/parameters/store/generationSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector([stateSelector], ({ generation }) => { - const { infillTileSize, infillMethod } = generation; - - return { - infillTileSize, - infillMethod, - }; -}); - const ParamInfillTileSize = () => { const dispatch = useAppDispatch(); - const { infillTileSize, infillMethod } = useAppSelector(selector); + const infillTileSize = useAppSelector((s) => s.generation.infillTileSize); + const infillMethod = useAppSelector((s) => s.generation.infillMethod); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx index 021dcc50fc..9cd978518d 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx @@ -21,7 +21,7 @@ const ParamScaleBeforeProcessing = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const boundingBoxScaleMethod = useAppSelector( - (state) => state.canvas.boundingBoxScaleMethod + (s) => s.canvas.boundingBoxScaleMethod ); const optimalDimension = useAppSelector(selectOptimalDimension); diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaledHeight.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaledHeight.tsx index d518aa440b..713a93a10c 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaledHeight.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaledHeight.tsx @@ -1,9 +1,11 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; -import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice'; +import { + selectCanvasSlice, + setScaledBoundingBoxDimensions, +} from 'features/canvas/store/canvasSlice'; import { CANVAS_GRID_SIZE_COARSE, CANVAS_GRID_SIZE_FINE, @@ -13,8 +15,8 @@ import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( - [stateSelector, selectOptimalDimension], - ({ canvas }, optimalDimension) => { + [selectCanvasSlice, selectOptimalDimension], + (canvas, optimalDimension) => { const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = canvas; return { diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaledWidth.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaledWidth.tsx index 02f03e2ad0..1805b576fe 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaledWidth.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaledWidth.tsx @@ -1,9 +1,11 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; -import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice'; +import { + selectCanvasSlice, + setScaledBoundingBoxDimensions, +} from 'features/canvas/store/canvasSlice'; import { CANVAS_GRID_SIZE_COARSE, CANVAS_GRID_SIZE_FINE, @@ -13,8 +15,8 @@ import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( - [stateSelector, selectOptimalDimension], - ({ canvas }, optimalDimension) => { + [selectCanvasSlice, selectOptimalDimension], + (canvas, optimalDimension) => { const { boundingBoxScaleMethod, scaledBoundingBoxDimensions } = canvas; return { diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamCFGScale.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamCFGScale.tsx index f10438cddb..2954f9eb2d 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamCFGScale.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamCFGScale.tsx @@ -1,15 +1,19 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; -import { setCfgScale } from 'features/parameters/store/generationSlice'; +import { + selectGenerationSlice, + setCfgScale, +} from 'features/parameters/store/generationSlice'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( - [stateSelector], - ({ generation, config }) => { + selectGenerationSlice, + selectConfigSlice, + (generation, config) => { const { min, inputMax, sliderMax, coarseStep, fineStep, initial } = config.sd.guidance; const { cfgScale } = generation; diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamHeight.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamHeight.tsx index f9647d5b8b..f54e218a02 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamHeight.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamHeight.tsx @@ -1,17 +1,17 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext'; import { selectOptimalDimension } from 'features/parameters/store/generationSlice'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( - [stateSelector, selectOptimalDimension], - ({ config }, optimalDimension) => { + [selectConfigSlice, selectOptimalDimension], + (config, optimalDimension) => { const { min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.height; return { diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx index ba82e92f3d..b231d1173d 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx @@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next'; export const ParamNegativePrompt = memo(() => { const dispatch = useAppDispatch(); - const prompt = useAppSelector((state) => state.generation.negativePrompt); + const prompt = useAppSelector((s) => s.generation.negativePrompt); const textareaRef = useRef(null); const { t } = useTranslation(); const _onChange = useCallback( diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx index 3c607804d7..e44dc35cdf 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx @@ -15,8 +15,8 @@ import { useTranslation } from 'react-i18next'; export const ParamPositivePrompt = memo(() => { const dispatch = useAppDispatch(); - const prompt = useAppSelector((state) => state.generation.positivePrompt); - const baseModel = useAppSelector((state) => state.generation.model) + const prompt = useAppSelector((s) => s.generation.positivePrompt); + const baseModel = useAppSelector((s) => s.generation.model) ?.base_model; const textareaRef = useRef(null); diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamScheduler.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamScheduler.tsx index 07e6d932ba..830e7a1c85 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamScheduler.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamScheduler.tsx @@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next'; const ParamScheduler = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const scheduler = useAppSelector((state) => state.generation.scheduler); + const scheduler = useAppSelector((s) => s.generation.scheduler); const onChange = useCallback( (v) => { diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx index e81f851399..58fe238525 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx @@ -1,18 +1,20 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { clampSymmetrySteps, + selectGenerationSlice, setSteps, } from 'features/parameters/store/generationSlice'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( - [stateSelector], - ({ generation, config }) => { + selectGenerationSlice, + selectConfigSlice, + (generation, config) => { const { initial, min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.steps; const { steps } = generation; diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamWidth.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamWidth.tsx index 7926c603d5..b8649d46b8 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamWidth.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamWidth.tsx @@ -1,17 +1,17 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext'; import { selectOptimalDimension } from 'features/parameters/store/generationSlice'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( - [stateSelector, selectOptimalDimension], - ({ config }, optimalDimension) => { + [selectConfigSlice, selectOptimalDimension], + (config, optimalDimension) => { const { min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.width; return { diff --git a/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx b/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx index cee276b2b0..616e423783 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx @@ -1,15 +1,19 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; -import { setImg2imgStrength } from 'features/parameters/store/generationSlice'; +import { + selectGenerationSlice, + setImg2imgStrength, +} from 'features/parameters/store/generationSlice'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( - [stateSelector], - ({ generation, config }) => { + selectGenerationSlice, + selectConfigSlice, + (generation, config) => { const { initial, min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.img2imgStrength; const { img2imgStrength } = generation; diff --git a/invokeai/frontend/web/src/features/parameters/components/ImageToImage/InitialImage.tsx b/invokeai/frontend/web/src/features/parameters/components/ImageToImage/InitialImage.tsx index f787d155cc..dcffef9069 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ImageToImage/InitialImage.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ImageToImage/InitialImage.tsx @@ -1,6 +1,5 @@ import { skipToken } from '@reduxjs/toolkit/query'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIDndImage from 'common/components/IAIDndImage'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; @@ -8,20 +7,28 @@ import type { TypesafeDraggableData, TypesafeDroppableData, } from 'features/dnd/types'; -import { clearInitialImage } from 'features/parameters/store/generationSlice'; +import { + clearInitialImage, + selectGenerationSlice, +} from 'features/parameters/store/generationSlice'; +import { selectSystemSlice } from 'features/system/store/systemSlice'; import { memo, useEffect, useMemo } from 'react'; import { useGetImageDTOQuery } from 'services/api/endpoints/images'; -const selector = createMemoizedSelector([stateSelector], (state) => { - const { initialImage } = state.generation; - const { isConnected } = state.system; +const selector = createMemoizedSelector( + selectGenerationSlice, + selectSystemSlice, + (generation, system) => { + const { initialImage } = generation; + const { isConnected } = system; - return { - initialImage, - isResetButtonDisabled: !initialImage, - isConnected, - }; -}); + return { + initialImage, + isResetButtonDisabled: !initialImage, + isConnected, + }; + } +); const InitialImage = () => { const dispatch = useAppDispatch(); diff --git a/invokeai/frontend/web/src/features/parameters/components/ImageToImage/InitialImageDisplay.tsx b/invokeai/frontend/web/src/features/parameters/components/ImageToImage/InitialImageDisplay.tsx index 2816925168..21c725781f 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ImageToImage/InitialImageDisplay.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ImageToImage/InitialImageDisplay.tsx @@ -1,12 +1,14 @@ import { Flex, Spacer } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import { InvText } from 'common/components/InvText/wrapper'; import { useImageUploadButton } from 'common/hooks/useImageUploadButton'; import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters'; -import { clearInitialImage } from 'features/parameters/store/generationSlice'; +import { + clearInitialImage, + selectGenerationSlice, +} from 'features/parameters/store/generationSlice'; import { memo, useCallback } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; @@ -15,8 +17,8 @@ import type { PostUploadAction } from 'services/api/types'; import InitialImage from './InitialImage'; -const selector = createMemoizedSelector([stateSelector], (state) => { - const { initialImage } = state.generation; +const selector = createMemoizedSelector(selectGenerationSlice, (generation) => { + const { initialImage } = generation; return { isResetButtonDisabled: !initialImage, initialImage, diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx index 911dd3ed6d..e91b5a929b 100644 --- a/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx @@ -1,10 +1,10 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSelect } from 'common/components/InvSelect/InvSelect'; import { useGroupedModelInvSelect } from 'common/components/InvSelect/useGroupedModelInvSelect'; import { modelSelected } from 'features/parameters/store/actions'; +import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { pick } from 'lodash-es'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -12,9 +12,12 @@ import { NON_REFINER_BASE_MODELS } from 'services/api/constants'; import type { MainModelConfigEntity } from 'services/api/endpoints/models'; import { useGetMainModelsQuery } from 'services/api/endpoints/models'; -const selector = createMemoizedSelector(stateSelector, (state) => ({ - model: state.generation.model, -})); +const selector = createMemoizedSelector( + selectGenerationSlice, + (generation) => ({ + model: generation.model, + }) +); const ParamMainModelSelect = () => { const dispatch = useAppDispatch(); diff --git a/invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessXAxis.tsx b/invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessXAxis.tsx index a7dccc7edc..e4d932794d 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessXAxis.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessXAxis.tsx @@ -1,5 +1,3 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSwitch } from 'common/components/InvSwitch/wrapper'; @@ -8,15 +6,9 @@ import type { ChangeEvent } from 'react'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector(stateSelector, ({ generation }) => { - const { seamlessXAxis } = generation; - - return { seamlessXAxis }; -}); - const ParamSeamlessXAxis = () => { const { t } = useTranslation(); - const { seamlessXAxis } = useAppSelector(selector); + const seamlessXAxis = useAppSelector((s) => s.generation.seamlessXAxis); const dispatch = useAppDispatch(); diff --git a/invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessYAxis.tsx b/invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessYAxis.tsx index 76a6986158..926dcd03fa 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessYAxis.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessYAxis.tsx @@ -1,5 +1,3 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSwitch } from 'common/components/InvSwitch/wrapper'; @@ -8,18 +6,10 @@ import type { ChangeEvent } from 'react'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector(stateSelector, ({ generation }) => { - const { seamlessYAxis } = generation; - - return { seamlessYAxis }; -}); - const ParamSeamlessYAxis = () => { const { t } = useTranslation(); - const { seamlessYAxis } = useAppSelector(selector); - + const seamlessYAxis = useAppSelector((s) => s.generation.seamlessYAxis); const dispatch = useAppDispatch(); - const handleChange = useCallback( (e: ChangeEvent) => { dispatch(setSeamlessYAxis(e.target.checked)); diff --git a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx b/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx index 8aeefd3907..ff2ff7cd05 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx @@ -7,9 +7,9 @@ import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; export const ParamSeedNumberInput = memo(() => { - const seed = useAppSelector((state) => state.generation.seed); + const seed = useAppSelector((s) => s.generation.seed); const shouldRandomizeSeed = useAppSelector( - (state) => state.generation.shouldRandomizeSeed + (s) => s.generation.shouldRandomizeSeed ); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx index 1ede92e12d..9cf774fbfa 100644 --- a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx @@ -1,17 +1,19 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSelect } from 'common/components/InvSelect/InvSelect'; import { useGroupedModelInvSelect } from 'common/components/InvSelect/useGroupedModelInvSelect'; -import { vaeSelected } from 'features/parameters/store/generationSlice'; +import { + selectGenerationSlice, + vaeSelected, +} from 'features/parameters/store/generationSlice'; import { pick } from 'lodash-es'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import type { VaeModelConfigEntity } from 'services/api/endpoints/models'; import { useGetVaeModelsQuery } from 'services/api/endpoints/models'; -const selector = createMemoizedSelector(stateSelector, ({ generation }) => { +const selector = createMemoizedSelector(selectGenerationSlice, (generation) => { const { model, vae } = generation; return { model, vae }; }); diff --git a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx b/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx index a51c8e2f29..d89d0045aa 100644 --- a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx @@ -15,7 +15,7 @@ const options = [ const ParamVAEModelSelect = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const vaePrecision = useAppSelector((state) => state.generation.vaePrecision); + const vaePrecision = useAppSelector((s) => s.generation.vaePrecision); const onChange = useCallback( (v) => { diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useCoreParametersCollapseLabel.ts b/invokeai/frontend/web/src/features/parameters/hooks/useCoreParametersCollapseLabel.ts index 3d17ae47ea..b0412e06cd 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useCoreParametersCollapseLabel.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useCoreParametersCollapseLabel.ts @@ -5,9 +5,9 @@ import { useTranslation } from 'react-i18next'; export const useCoreParametersCollapseLabel = () => { const { t } = useTranslation(); const shouldRandomizeSeed = useAppSelector( - (state) => state.generation.shouldRandomizeSeed + (s) => s.generation.shouldRandomizeSeed ); - const iterations = useAppSelector((state) => state.generation.iterations); + const iterations = useAppSelector((s) => s.generation.iterations); const iterationsLabel = useMemo(() => { if (iterations === 1) { diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useIsAllowedToUpscale.ts b/invokeai/frontend/web/src/features/parameters/hooks/useIsAllowedToUpscale.ts index 6df8162837..62e14ab3d1 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useIsAllowedToUpscale.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useIsAllowedToUpscale.ts @@ -1,6 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectPostprocessingSlice } from 'features/parameters/store/postprocessingSlice'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import type { ImageDTO } from 'services/api/types'; @@ -60,23 +61,27 @@ const getDetailTKey = ( }; export const createIsAllowedToUpscaleSelector = (imageDTO?: ImageDTO) => - createMemoizedSelector(stateSelector, ({ postprocessing, config }) => { - const { esrganModelName } = postprocessing; - const { maxUpscalePixels } = config; + createMemoizedSelector( + selectPostprocessingSlice, + selectConfigSlice, + (postprocessing, config) => { + const { esrganModelName } = postprocessing; + const { maxUpscalePixels } = config; - const upscaledPixels = getUpscaledPixels(imageDTO, maxUpscalePixels); - const isAllowedToUpscale = getIsAllowedToUpscale( - upscaledPixels, - maxUpscalePixels - ); - const scaleFactor = esrganModelName.includes('x2') ? 2 : 4; - const detailTKey = getDetailTKey(isAllowedToUpscale, scaleFactor); - return { - isAllowedToUpscale: - scaleFactor === 2 ? isAllowedToUpscale.x2 : isAllowedToUpscale.x4, - detailTKey, - }; - }); + const upscaledPixels = getUpscaledPixels(imageDTO, maxUpscalePixels); + const isAllowedToUpscale = getIsAllowedToUpscale( + upscaledPixels, + maxUpscalePixels + ); + const scaleFactor = esrganModelName.includes('x2') ? 2 : 4; + const detailTKey = getDetailTKey(isAllowedToUpscale, scaleFactor); + return { + isAllowedToUpscale: + scaleFactor === 2 ? isAllowedToUpscale.x2 : isAllowedToUpscale.x4, + detailTKey, + }; + } + ); export const useIsAllowedToUpscale = (imageDTO?: ImageDTO) => { const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts index 1065d39046..b99f98e2d0 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts @@ -1,6 +1,5 @@ import { useAppToaster } from 'app/components/Toaster'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { CONTROLNET_PROCESSORS } from 'features/controlAdapters/store/constants'; import { @@ -36,6 +35,7 @@ import { } from 'features/parameters/store/actions'; import { heightChanged, + selectGenerationSlice, setCfgRescaleMultiplier, setCfgScale, setImg2imgStrength, @@ -100,8 +100,8 @@ import type { ImageDTO } from 'services/api/types'; import { v4 as uuidv4 } from 'uuid'; const selector = createMemoizedSelector( - stateSelector, - ({ generation }) => generation.model + selectGenerationSlice, + (generation) => generation.model ); export const useRecallParameters = () => { diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index 142cf5413a..8efad67954 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import { roundToMultiple } from 'common/util/roundDownToMultiple'; import { isAnyControlAdapterAdded } from 'features/controlAdapters/store/controlAdaptersSlice'; import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize'; @@ -328,3 +329,5 @@ export const { export const { selectOptimalDimension } = generationSlice.selectors; export default generationSlice.reducer; + +export const selectGenerationSlice = (state: RootState) => state.generation; diff --git a/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts b/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts index 932fcf7c59..d197158930 100644 --- a/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import { z } from 'zod'; export const zParamESRGANModelName = z.enum([ @@ -36,3 +37,6 @@ export const postprocessingSlice = createSlice({ export const { esrganModelNameChanged } = postprocessingSlice.actions; export default postprocessingSlice.reducer; + +export const selectPostprocessingSlice = (state: RootState) => + state.postprocessing; diff --git a/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx b/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx index 155a77858f..5b54a58520 100644 --- a/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx @@ -1,13 +1,14 @@ import { Flex, Spacer } from '@chakra-ui/layout'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover'; import { InvButton } from 'common/components/InvButton/InvButton'; import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput'; import type { InvNumberInputFieldProps } from 'common/components/InvNumberInput/types'; -import { setIterations } from 'features/parameters/store/generationSlice'; +import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice'; +import { selectGenerationSlice, setIterations } from 'features/parameters/store/generationSlice'; import { useQueueBack } from 'features/queue/hooks/useQueueBack'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback } from 'react'; import { IoSparkles } from 'react-icons/io5'; @@ -24,23 +25,28 @@ const numberInputFieldProps: InvNumberInputFieldProps = { fontWeight: 'semibold', }; -const selector = createMemoizedSelector([stateSelector], (state) => { - const { initial, min, sliderMax, inputMax, fineStep, coarseStep } = - state.config.sd.iterations; - const { iterations } = state.generation; - const isLoadingDynamicPrompts = state.dynamicPrompts.isLoading; +const selector = createMemoizedSelector( + selectConfigSlice, + selectGenerationSlice, + selectDynamicPromptsSlice, + (config, generation, dynamicPrompts) => { + const { initial, min, sliderMax, inputMax, fineStep, coarseStep } = + config.sd.iterations; + const { iterations } = generation; + const isLoadingDynamicPrompts = dynamicPrompts.isLoading; - return { - iterations, - initial, - min, - sliderMax, - inputMax, - step: coarseStep, - fineStep, - isLoadingDynamicPrompts, - }; -}); + return { + iterations, + initial, + min, + sliderMax, + inputMax, + step: coarseStep, + fineStep, + isLoadingDynamicPrompts, + }; + } +); export const InvokeQueueBackButton = memo(() => { const { queueBack, isLoading, isDisabled } = useQueueBack(); diff --git a/invokeai/frontend/web/src/features/queue/components/QueueButtonTooltip.tsx b/invokeai/frontend/web/src/features/queue/components/QueueButtonTooltip.tsx index 5c09599477..60f99b1ff2 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueButtonTooltip.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueButtonTooltip.tsx @@ -1,10 +1,12 @@ import { Divider, Flex, ListItem, UnorderedList } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { InvText } from 'common/components/InvText/wrapper'; import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue'; +import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice'; import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt'; +import { selectGallerySlice } from 'features/gallery/store/gallerySlice'; +import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useEnqueueBatchMutation } from 'services/api/endpoints/queue'; @@ -13,8 +15,10 @@ import { useBoardName } from 'services/api/hooks/useBoardName'; const StyledDivider = () => ; const tooltipSelector = createMemoizedSelector( - [stateSelector], - ({ gallery, dynamicPrompts, generation }) => { + selectGallerySlice, + selectDynamicPromptsSlice, + selectGenerationSlice, + (gallery, dynamicPrompts, generation) => { const { autoAddBoardId } = gallery; const { iterations, positivePrompt } = generation; const promptsCount = getShouldProcessPrompt(positivePrompt) @@ -36,7 +40,7 @@ export const QueueButtonTooltip = memo(({ prepend = false }: Props) => { const { t } = useTranslation(); const { isReady, reasons } = useIsReadyToEnqueue(); const isLoadingDynamicPrompts = useAppSelector( - (state) => state.dynamicPrompts.isLoading + (s) => s.dynamicPrompts.isLoading ); const { autoAddBoardId, promptsCount, iterations } = useAppSelector(tooltipSelector); diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueList.tsx b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueList.tsx index 6e44dfd14b..90ee7cd42d 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueList.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueList.tsx @@ -1,6 +1,4 @@ import { Flex, Heading } from '@chakra-ui/react'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallbackWithSpinner } from 'common/components/IAIImageFallback'; import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants'; @@ -27,11 +25,6 @@ import type { ListContext } from './types'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type TableVirtuosoScrollerRef = (ref: HTMLElement | Window | null) => any; -const selector = createMemoizedSelector(stateSelector, ({ queue }) => { - const { listCursor, listPriority } = queue; - return { listCursor, listPriority }; -}); - const computeItemKey = (index: number, item: SessionQueueItemDTO): number => item.item_id; @@ -46,7 +39,8 @@ const itemContent: ItemContent = ( ) => ; const QueueList = () => { - const { listCursor, listPriority } = useAppSelector(selector); + const listCursor = useAppSelector((s) => s.queue.listCursor); + const listPriority = useAppSelector((s) => s.queue.listPriority); const dispatch = useAppDispatch(); const rootRef = useRef(null); const [scroller, setScroller] = useState(null); diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelBatch.ts b/invokeai/frontend/web/src/features/queue/hooks/useCancelBatch.ts index 42825f151b..b0a565d20e 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useCancelBatch.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useCancelBatch.ts @@ -8,7 +8,7 @@ import { } from 'services/api/endpoints/queue'; export const useCancelBatch = (batch_id: string) => { - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const { isCanceled } = useGetBatchStatusQuery( { batch_id }, { diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelCurrentQueueItem.ts b/invokeai/frontend/web/src/features/queue/hooks/useCancelCurrentQueueItem.ts index 1b07221a74..9e4b1cd4ab 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useCancelCurrentQueueItem.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useCancelCurrentQueueItem.ts @@ -9,7 +9,7 @@ import { } from 'services/api/endpoints/queue'; export const useCancelCurrentQueueItem = () => { - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const { data: queueStatus } = useGetQueueStatusQuery(); const [trigger, { isLoading }] = useCancelQueueItemMutation(); const dispatch = useAppDispatch(); diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItem.ts b/invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItem.ts index 4582ef1223..f22b98a7ee 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItem.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItem.ts @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import { useCancelQueueItemMutation } from 'services/api/endpoints/queue'; export const useCancelQueueItem = (item_id: number) => { - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const [trigger, { isLoading }] = useCancelQueueItemMutation(); const dispatch = useAppDispatch(); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/queue/hooks/useClearInvocationCache.ts b/invokeai/frontend/web/src/features/queue/hooks/useClearInvocationCache.ts index ce2fc0e46b..468864d765 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useClearInvocationCache.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useClearInvocationCache.ts @@ -11,7 +11,7 @@ export const useClearInvocationCache = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const { data: cacheStatus } = useGetInvocationCacheStatusQuery(); - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const [trigger, { isLoading }] = useClearInvocationCacheMutation({ fixedCacheKey: 'clearInvocationCache', }); diff --git a/invokeai/frontend/web/src/features/queue/hooks/useClearQueue.ts b/invokeai/frontend/web/src/features/queue/hooks/useClearQueue.ts index d422b8bf8e..19a01f8b50 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useClearQueue.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useClearQueue.ts @@ -15,7 +15,7 @@ export const useClearQueue = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const { data: queueStatus } = useGetQueueStatusQuery(); - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const [trigger, { isLoading }] = useClearQueueMutation({ fixedCacheKey: 'clearQueue', }); diff --git a/invokeai/frontend/web/src/features/queue/hooks/useDisableInvocationCache.ts b/invokeai/frontend/web/src/features/queue/hooks/useDisableInvocationCache.ts index 8d3288aad2..62e7e689be 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useDisableInvocationCache.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useDisableInvocationCache.ts @@ -11,7 +11,7 @@ export const useDisableInvocationCache = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const { data: cacheStatus } = useGetInvocationCacheStatusQuery(); - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const [trigger, { isLoading }] = useDisableInvocationCacheMutation({ fixedCacheKey: 'disableInvocationCache', }); diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnableInvocationCache.ts b/invokeai/frontend/web/src/features/queue/hooks/useEnableInvocationCache.ts index 2ffef29b19..31b1fc6e04 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useEnableInvocationCache.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useEnableInvocationCache.ts @@ -11,7 +11,7 @@ export const useEnableInvocationCache = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const { data: cacheStatus } = useGetInvocationCacheStatusQuery(); - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const [trigger, { isLoading }] = useEnableInvocationCacheMutation({ fixedCacheKey: 'enableInvocationCache', }); diff --git a/invokeai/frontend/web/src/features/queue/hooks/usePauseProcessor.ts b/invokeai/frontend/web/src/features/queue/hooks/usePauseProcessor.ts index 938f989449..784b5f81d6 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/usePauseProcessor.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/usePauseProcessor.ts @@ -10,7 +10,7 @@ import { export const usePauseProcessor = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const { data: queueStatus } = useGetQueueStatusQuery(); const [trigger, { isLoading }] = usePauseProcessorMutation({ fixedCacheKey: 'pauseProcessor', diff --git a/invokeai/frontend/web/src/features/queue/hooks/usePruneQueue.ts b/invokeai/frontend/web/src/features/queue/hooks/usePruneQueue.ts index f8e89a0112..e499a29a20 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/usePruneQueue.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/usePruneQueue.ts @@ -14,7 +14,7 @@ import { export const usePruneQueue = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const [trigger, { isLoading }] = usePruneQueueMutation({ fixedCacheKey: 'pruneQueue', }); diff --git a/invokeai/frontend/web/src/features/queue/hooks/useResumeProcessor.ts b/invokeai/frontend/web/src/features/queue/hooks/useResumeProcessor.ts index 19e21339dd..89029267ca 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useResumeProcessor.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useResumeProcessor.ts @@ -9,7 +9,7 @@ import { export const useResumeProcessor = () => { const dispatch = useAppDispatch(); - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const { data: queueStatus } = useGetQueueStatusQuery(); const { t } = useTranslation(); const [trigger, { isLoading }] = useResumeProcessorMutation({ diff --git a/invokeai/frontend/web/src/features/queue/store/queueSlice.ts b/invokeai/frontend/web/src/features/queue/store/queueSlice.ts index af7ef6e3ab..461ae72dfe 100644 --- a/invokeai/frontend/web/src/features/queue/store/queueSlice.ts +++ b/invokeai/frontend/web/src/features/queue/store/queueSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; export interface QueueState { listCursor: number | undefined; @@ -59,3 +60,5 @@ export const { } = queueSlice.actions; export default queueSlice.reducer; + +export const selectQueueSlice = (state: RootState) => state.queue; diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx index a1491cab18..a6f9f07312 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx @@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next'; export const ParamSDXLNegativeStylePrompt = memo(() => { const dispatch = useAppDispatch(); - const prompt = useAppSelector((state) => state.sdxl.negativeStylePrompt); + const prompt = useAppSelector((s) => s.sdxl.negativeStylePrompt); const textareaRef = useRef(null); const { t } = useTranslation(); const handleChange = useCallback( diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx index 83952dbe7c..5ee3862e6c 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx @@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next'; export const ParamSDXLPositiveStylePrompt = memo(() => { const dispatch = useAppDispatch(); - const prompt = useAppSelector((state) => state.sdxl.positiveStylePrompt); + const prompt = useAppSelector((s) => s.sdxl.positiveStylePrompt); const textareaRef = useRef(null); const { t } = useTranslation(); const handleChange = useCallback( diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLConcatButton.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLConcatButton.tsx index 046d372358..4dbb396560 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLConcatButton.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLConcatButton.tsx @@ -8,7 +8,7 @@ import { FaLink, FaUnlink } from 'react-icons/fa'; export const SDXLConcatButton = memo(() => { const shouldConcatSDXLStylePrompt = useAppSelector( - (state) => state.sdxl.shouldConcatSDXLStylePrompt + (s) => s.sdxl.shouldConcatSDXLStylePrompt ); const dispatch = useAppDispatch(); diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLPrompts.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLPrompts.tsx index c0b530bc69..63c6d604ab 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLPrompts.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLPrompts.tsx @@ -9,7 +9,7 @@ import { ParamSDXLPositiveStylePrompt } from './ParamSDXLPositiveStylePrompt'; export const SDXLPrompts = memo(() => { const shouldConcatSDXLStylePrompt = useAppSelector( - (state) => state.sdxl.shouldConcatSDXLStylePrompt + (s) => s.sdxl.shouldConcatSDXLStylePrompt ); return ( diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerCFGScale.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerCFGScale.tsx index 6d7694b56c..796b3559ba 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerCFGScale.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerCFGScale.tsx @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { setRefinerCFGScale } from 'features/sdxl/store/sdxlSlice'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector([stateSelector], ({ config }) => { +const selector = createMemoizedSelector(selectConfigSlice, (config) => { const { min, inputMax, sliderMax, coarseStep, fineStep, initial } = config.sd.guidance; @@ -23,7 +23,7 @@ const selector = createMemoizedSelector([stateSelector], ({ config }) => { }); const ParamSDXLRefinerCFGScale = () => { - const refinerCFGScale = useAppSelector((state) => state.sdxl.refinerCFGScale); + const refinerCFGScale = useAppSelector((s) => s.sdxl.refinerCFGScale); const { marks, min, inputMax, sliderMax, coarseStep, fineStep, initial } = useAppSelector(selector); const dispatch = useAppDispatch(); diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx index e71716db89..7a72392dcb 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx @@ -1,18 +1,20 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSelect } from 'common/components/InvSelect/InvSelect'; import { useModelInvSelect } from 'common/components/InvSelect/useModelInvSelect'; -import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice'; +import { + refinerModelChanged, + selectSdxlSlice, +} from 'features/sdxl/store/sdxlSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { REFINER_BASE_MODELS } from 'services/api/constants'; import type { MainModelConfigEntity } from 'services/api/endpoints/models'; import { useGetMainModelsQuery } from 'services/api/endpoints/models'; -const selector = createMemoizedSelector(stateSelector, (state) => ({ - model: state.sdxl.refinerModel, +const selector = createMemoizedSelector(selectSdxlSlice, (sdxl) => ({ + model: sdxl.refinerModel, })); const optionsFilter = (model: MainModelConfigEntity) => diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore.tsx index 6922ed80b1..f6325149dc 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; const ParamSDXLRefinerNegativeAestheticScore = () => { const refinerNegativeAestheticScore = useAppSelector( - (state) => state.sdxl.refinerNegativeAestheticScore + (s) => s.sdxl.refinerNegativeAestheticScore ); const dispatch = useAppDispatch(); diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerPositiveAestheticScore.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerPositiveAestheticScore.tsx index 9225003074..6a4c8006df 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerPositiveAestheticScore.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerPositiveAestheticScore.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; const ParamSDXLRefinerPositiveAestheticScore = () => { const refinerPositiveAestheticScore = useAppSelector( - (state) => state.sdxl.refinerPositiveAestheticScore + (s) => s.sdxl.refinerPositiveAestheticScore ); const dispatch = useAppDispatch(); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerScheduler.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerScheduler.tsx index 0453b52fc1..746e156894 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerScheduler.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerScheduler.tsx @@ -12,7 +12,7 @@ const ParamSDXLRefinerScheduler = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const refinerScheduler = useAppSelector( - (state) => state.sdxl.refinerScheduler + (s) => s.sdxl.refinerScheduler ); const onChange = useCallback( diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerStart.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerStart.tsx index da5487b831..8957fbecf9 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerStart.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerStart.tsx @@ -1,5 +1,3 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; @@ -7,15 +5,8 @@ import { setRefinerStart } from 'features/sdxl/store/sdxlSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector([stateSelector], ({ sdxl }) => { - const { refinerStart } = sdxl; - return { - refinerStart, - }; -}); - const ParamSDXLRefinerStart = () => { - const { refinerStart } = useAppSelector(selector); + const refinerStart = useAppSelector((s) => s.sdxl.refinerStart); const dispatch = useAppDispatch(); const handleChange = useCallback( (v: number) => dispatch(setRefinerStart(v)), diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerSteps.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerSteps.tsx index 0819c39862..ebbbf0cfe5 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerSteps.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerSteps.tsx @@ -1,13 +1,13 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { setRefinerSteps } from 'features/sdxl/store/sdxlSlice'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector([stateSelector], ({ config }) => { +const selector = createMemoizedSelector(selectConfigSlice, (config) => { const { initial, min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.steps; @@ -24,7 +24,7 @@ const selector = createMemoizedSelector([stateSelector], ({ config }) => { const ParamSDXLRefinerSteps = () => { const { initial, min, sliderMax, inputMax, step, fineStep, marks } = useAppSelector(selector); - const refinerSteps = useAppSelector((state) => state.sdxl.refinerSteps); + const refinerSteps = useAppSelector((s) => s.sdxl.refinerSteps); const dispatch = useAppDispatch(); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/sdxl/store/sdxlSlice.ts b/invokeai/frontend/web/src/features/sdxl/store/sdxlSlice.ts index 6612129023..035407755a 100644 --- a/invokeai/frontend/web/src/features/sdxl/store/sdxlSlice.ts +++ b/invokeai/frontend/web/src/features/sdxl/store/sdxlSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import type { ParameterNegativeStylePromptSDXL, ParameterPositiveStylePromptSDXL, @@ -93,3 +94,5 @@ export const { } = sdxlSlice.actions; export default sdxlSlice.reducer; + +export const selectSdxlSlice = (state: RootState) => state.sdxl; diff --git a/invokeai/frontend/web/src/features/settingsAccordions/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx index 777945f501..26a1edc1fc 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx @@ -1,6 +1,5 @@ import { Flex } from '@chakra-ui/layout'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { InvControlGroup } from 'common/components/InvControl/InvControlGroup'; import type { InvLabelProps } from 'common/components/InvControl/types'; @@ -11,6 +10,7 @@ import ParamSeamlessXAxis from 'features/parameters/components/Seamless/ParamSea import ParamSeamlessYAxis from 'features/parameters/components/Seamless/ParamSeamlessYAxis'; import ParamVAEModelSelect from 'features/parameters/components/VAEModel/ParamVAEModelSelect'; import ParamVAEPrecision from 'features/parameters/components/VAEModel/ParamVAEPrecision'; +import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -22,28 +22,31 @@ const labelProps2: InvLabelProps = { flexGrow: 1, }; -const selectBadges = createMemoizedSelector(stateSelector, (state) => { - const badges: (string | number)[] = []; - if (state.generation.vae) { - let vaeBadge = state.generation.vae.model_name; - if (state.generation.vaePrecision === 'fp16') { - vaeBadge += ` ${state.generation.vaePrecision}`; +const selectBadges = createMemoizedSelector( + selectGenerationSlice, + (generation) => { + const badges: (string | number)[] = []; + if (generation.vae) { + let vaeBadge = generation.vae.model_name; + if (generation.vaePrecision === 'fp16') { + vaeBadge += ` ${generation.vaePrecision}`; + } + badges.push(vaeBadge); + } else if (generation.vaePrecision === 'fp16') { + badges.push(`VAE ${generation.vaePrecision}`); } - badges.push(vaeBadge); - } else if (state.generation.vaePrecision === 'fp16') { - badges.push(`VAE ${state.generation.vaePrecision}`); + if (generation.clipSkip) { + badges.push(`Skip ${generation.clipSkip}`); + } + if (generation.cfgRescaleMultiplier) { + badges.push(`Rescale ${generation.cfgRescaleMultiplier}`); + } + if (generation.seamlessXAxis || generation.seamlessYAxis) { + badges.push('seamless'); + } + return badges; } - if (state.generation.clipSkip) { - badges.push(`Skip ${state.generation.clipSkip}`); - } - if (state.generation.cfgRescaleMultiplier) { - badges.push(`Rescale ${state.generation.cfgRescaleMultiplier}`); - } - if (state.generation.seamlessXAxis || state.generation.seamlessYAxis) { - badges.push('seamless'); - } - return badges; -}); +); export const AdvancedSettingsAccordion = memo(() => { const badges = useAppSelector(selectBadges); diff --git a/invokeai/frontend/web/src/features/settingsAccordions/ControlSettingsAccordion/ControlSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/ControlSettingsAccordion/ControlSettingsAccordion.tsx index 34bed38a1c..cf86e7e9d8 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/ControlSettingsAccordion/ControlSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/ControlSettingsAccordion/ControlSettingsAccordion.tsx @@ -1,7 +1,6 @@ import { Flex } from '@chakra-ui/layout'; import { Divider } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { InvButton } from 'common/components/InvButton/InvButton'; import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup'; @@ -13,6 +12,7 @@ import { selectAllIPAdapters, selectAllT2IAdapters, selectControlAdapterIds, + selectControlAdaptersSlice, selectValidControlNets, selectValidIPAdapters, selectValidT2IAdapters, @@ -23,8 +23,8 @@ import { useTranslation } from 'react-i18next'; import { FaPlus } from 'react-icons/fa'; const selector = createMemoizedSelector( - [stateSelector], - ({ controlAdapters }) => { + selectControlAdaptersSlice, + (controlAdapters) => { const badges: string[] = []; let isError = false; diff --git a/invokeai/frontend/web/src/features/settingsAccordions/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx index 568aeb8088..6942b790a4 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx @@ -1,6 +1,5 @@ import { Flex } from '@chakra-ui/layout'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { InvControlGroup } from 'common/components/InvControl/InvControlGroup'; import type { InvLabelProps } from 'common/components/InvControl/types'; @@ -15,11 +14,13 @@ import { } from 'common/components/InvTabs/wrapper'; import { LoRAList } from 'features/lora/components/LoRAList'; import LoRASelect from 'features/lora/components/LoRASelect'; +import { selectLoraSlice } from 'features/lora/store/loraSlice'; import { SyncModelsIconButton } from 'features/modelManager/components/SyncModels/SyncModelsIconButton'; import ParamCFGScale from 'features/parameters/components/Core/ParamCFGScale'; import ParamScheduler from 'features/parameters/components/Core/ParamScheduler'; import ParamSteps from 'features/parameters/components/Core/ParamSteps'; import ParamMainModelSelect from 'features/parameters/components/MainModel/ParamMainModelSelect'; +import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { size } from 'lodash-es'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -29,8 +30,9 @@ const labelProps: InvLabelProps = { }; const badgesSelector = createMemoizedSelector( - stateSelector, - ({ lora, generation }) => { + selectLoraSlice, + selectGenerationSlice, + (lora, generation) => { const loraTabBadges = size(lora.loras) ? [size(lora.loras)] : []; const accordionBadges: (string | number)[] = []; if (generation.model) { diff --git a/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSettingsAccordion.tsx index 568dd2ea5a..dfbfe3d3c4 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSettingsAccordion.tsx @@ -1,12 +1,13 @@ import { Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { InvControlGroup } from 'common/components/InvControl/InvControlGroup'; import type { InvLabelProps } from 'common/components/InvControl/types'; import { InvExpander } from 'common/components/InvExpander/InvExpander'; import { InvSingleAccordion } from 'common/components/InvSingleAccordion/InvSingleAccordion'; +import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import { HrfSettings } from 'features/hrf/components/HrfSettings'; +import { selectHrfSlice } from 'features/hrf/store/hrfSlice'; import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing'; import ParamScaledHeight from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledHeight'; import ParamScaledWidth from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledWidth'; @@ -15,6 +16,7 @@ import ImageToImageStrength from 'features/parameters/components/ImageToImage/Im import { ParamSeedNumberInput } from 'features/parameters/components/Seed/ParamSeedNumberInput'; import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSeedRandomize'; import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedShuffle'; +import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -23,8 +25,13 @@ import { ImageSizeCanvas } from './ImageSizeCanvas'; import { ImageSizeLinear } from './ImageSizeLinear'; const selector = createMemoizedSelector( - [stateSelector, activeTabNameSelector], - ({ generation, canvas, hrf }, activeTabName) => { + [ + selectGenerationSlice, + selectCanvasSlice, + selectHrfSlice, + activeTabNameSelector, + ], + (generation, canvas, hrf, activeTabName) => { const { shouldRandomizeSeed } = generation; const { hrfEnabled } = hrf; const badges: string[] = []; diff --git a/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSizeCanvas.tsx b/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSizeCanvas.tsx index 3317b7224e..327afacb94 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSizeCanvas.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSizeCanvas.tsx @@ -13,9 +13,9 @@ import { memo, useCallback } from 'react'; export const ImageSizeCanvas = memo(() => { const dispatch = useAppDispatch(); const { width, height } = useAppSelector( - (state) => state.canvas.boundingBoxDimensions + (s) => s.canvas.boundingBoxDimensions ); - const aspectRatioState = useAppSelector((state) => state.canvas.aspectRatio); + const aspectRatioState = useAppSelector((s) => s.canvas.aspectRatio); const optimalDimension = useAppSelector(selectOptimalDimension); const onChangeWidth = useCallback( diff --git a/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSizeLinear.tsx b/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSizeLinear.tsx index c32092a493..371e16345b 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSizeLinear.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSizeLinear.tsx @@ -12,10 +12,10 @@ import { memo, useCallback } from 'react'; export const ImageSizeLinear = memo(() => { const dispatch = useAppDispatch(); - const width = useAppSelector((state) => state.generation.width); - const height = useAppSelector((state) => state.generation.height); + const width = useAppSelector((s) => s.generation.width); + const height = useAppSelector((s) => s.generation.height); const aspectRatioState = useAppSelector( - (state) => state.generation.aspectRatio + (s) => s.generation.aspectRatio ); const onChangeWidth = useCallback( diff --git a/invokeai/frontend/web/src/features/settingsAccordions/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx index 3ae135d174..267d9a58e8 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx @@ -1,6 +1,5 @@ import { Flex } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { InvControlGroup } from 'common/components/InvControl/InvControlGroup'; import type { InvLabelProps } from 'common/components/InvControl/types'; @@ -13,6 +12,7 @@ import ParamSDXLRefinerPositiveAestheticScore from 'features/sdxl/components/SDX import ParamSDXLRefinerScheduler from 'features/sdxl/components/SDXLRefiner/ParamSDXLRefinerScheduler'; import ParamSDXLRefinerStart from 'features/sdxl/components/SDXLRefiner/ParamSDXLRefinerStart'; import ParamSDXLRefinerSteps from 'features/sdxl/components/SDXLRefiner/ParamSDXLRefinerSteps'; +import { selectSdxlSlice } from 'features/sdxl/store/sdxlSlice'; import { isNil } from 'lodash-es'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -26,10 +26,9 @@ const stepsScaleLabelProps: InvLabelProps = { minW: '5rem', }; -const selector = createMemoizedSelector(stateSelector, (state) => { - const { refinerModel } = state.sdxl; +const selector = createMemoizedSelector(selectSdxlSlice, (sdxl) => { return { - badges: refinerModel ? ['Enabled'] : undefined, + badges: sdxl.refinerModel ? ['Enabled'] : undefined, }; }); diff --git a/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx b/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx index 9e8fd6db1e..40aec1105d 100644 --- a/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx +++ b/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx @@ -1,13 +1,13 @@ import { Progress } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { selectSystemSlice } from 'features/system/store/systemSlice'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { useGetQueueStatusQuery } from 'services/api/endpoints/queue'; const progressBarSelector = createMemoizedSelector( - stateSelector, - ({ system }) => { + selectSystemSlice, + (system) => { return { isConnected: system.isConnected, hasSteps: Boolean(system.denoiseProgress), diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx index 1e0d9178d7..862fb615fe 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx @@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next'; export const SettingsLanguageSelect = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const language = useAppSelector((state) => state.system.language); + const language = useAppSelector((s) => s.system.language); const options = useMemo( () => [ { label: t('common.langArabic'), value: 'ar' }, diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx index c50ea0b360..0b1b8d08c9 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx @@ -11,10 +11,10 @@ export const SettingsLogLevelSelect = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const consoleLogLevel = useAppSelector( - (state) => state.system.consoleLogLevel + (s) => s.system.consoleLogLevel ); const shouldLogToConsole = useAppSelector( - (state) => state.system.shouldLogToConsole + (s) => s.system.shouldLogToConsole ); const options = useMemo( () => zLogLevel.options.map((o) => ({ label: o, value: o })), 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 f524450819..d078ffc006 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx @@ -1,6 +1,5 @@ import { Flex, useDisclosure } from '@chakra-ui/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvButton } from 'common/components/InvButton/InvButton'; import { InvControl } from 'common/components/InvControl/InvControl'; @@ -17,10 +16,14 @@ import { InvSwitch } from 'common/components/InvSwitch/wrapper'; import { InvText } from 'common/components/InvText/wrapper'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import { useClearStorage } from 'common/hooks/useClearStorage'; -import { shouldUseCpuNoiseChanged } from 'features/parameters/store/generationSlice'; +import { + selectGenerationSlice, + shouldUseCpuNoiseChanged, +} from 'features/parameters/store/generationSlice'; import { useClearIntermediates } from 'features/system/components/SettingsModal/useClearIntermediates'; import { StickyScrollable } from 'features/system/components/StickyScrollable'; import { + selectSystemSlice, setEnableImageDebugging, setShouldConfirmOnDelete, setShouldEnableInformationalPopovers, @@ -29,7 +32,10 @@ import { shouldUseNSFWCheckerChanged, shouldUseWatermarkerChanged, } from 'features/system/store/systemSlice'; -import { setShouldShowProgressInViewer } from 'features/ui/store/uiSlice'; +import { + selectUiSlice, + setShouldShowProgressInViewer, +} from 'features/ui/store/uiSlice'; import type { ChangeEvent, ReactElement } from 'react'; import { cloneElement, memo, useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -39,8 +45,10 @@ import { SettingsLanguageSelect } from './SettingsLanguageSelect'; import { SettingsLogLevelSelect } from './SettingsLogLevelSelect'; const selector = createMemoizedSelector( - [stateSelector], - ({ system, ui, generation }) => { + selectSystemSlice, + selectUiSlice, + selectGenerationSlice, + (system, ui, generation) => { const { shouldConfirmOnDelete, enableImageDebugging, diff --git a/invokeai/frontend/web/src/features/system/components/StatusIndicator.tsx b/invokeai/frontend/web/src/features/system/components/StatusIndicator.tsx index 5d7d375cd7..4be4bca637 100644 --- a/invokeai/frontend/web/src/features/system/components/StatusIndicator.tsx +++ b/invokeai/frontend/web/src/features/system/components/StatusIndicator.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'; import { FaExclamationTriangle } from 'react-icons/fa'; const StatusIndicator = () => { - const isConnected = useAppSelector((state) => state.system.isConnected); + const isConnected = useAppSelector((s) => s.system.isConnected); const { t } = useTranslation(); if (!isConnected) { diff --git a/invokeai/frontend/web/src/features/system/store/configSlice.ts b/invokeai/frontend/web/src/features/system/store/configSlice.ts index b0daf28c34..3264883c2b 100644 --- a/invokeai/frontend/web/src/features/system/store/configSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/configSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import type { AppConfig, PartialAppConfig } from 'app/types/invokeai'; import { merge } from 'lodash-es'; @@ -101,3 +102,5 @@ export const configSlice = createSlice({ export const { configChanged } = configSlice.actions; export default configSlice.reducer; + +export const selectConfigSlice = (state: RootState) => state.config; diff --git a/invokeai/frontend/web/src/features/system/store/systemSelectors.ts b/invokeai/frontend/web/src/features/system/store/systemSelectors.ts index ffff11eac6..be535157e9 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSelectors.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSelectors.ts @@ -1,7 +1,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { stateSelector } from 'app/store/store'; +import { selectSystemSlice } from 'features/system/store/systemSlice'; export const languageSelector = createMemoizedSelector( - stateSelector, - ({ system }) => system.language + selectSystemSlice, + (system) => system.language ); diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index 665bac03c7..57ef46373e 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -1,6 +1,7 @@ import type { UseToastOptions } from '@chakra-ui/react'; import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice, isAnyOf } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import { calculateStepPercentage } from 'features/system/util/calculateStepPercentage'; import { makeToast } from 'features/system/util/makeToast'; import { t } from 'i18next'; @@ -214,3 +215,5 @@ const isAnyServerError = isAnyOf( appSocketSessionRetrievalError, appSocketInvocationRetrievalError ); + +export const selectSystemSlice = (state: RootState) => state.system; diff --git a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx index 03a3f21e68..f0644fe39c 100644 --- a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx +++ b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx @@ -2,7 +2,6 @@ import { Flex, Spacer } from '@chakra-ui/react'; import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { $customNavComponent } from 'app/store/nanostores/customNavComponent'; -import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import { InvTab } from 'common/components/InvTabs/InvTab'; @@ -18,6 +17,7 @@ import NodeEditorPanelGroup from 'features/nodes/components/sidePanel/NodeEditor import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent'; import SettingsMenu from 'features/system/components/SettingsModal/SettingsMenu'; import StatusIndicator from 'features/system/components/StatusIndicator'; +import { selectConfigSlice } from 'features/system/store/configSlice'; import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton'; import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons'; import type { UsePanelOptions } from 'features/ui/hooks/usePanel'; @@ -95,12 +95,8 @@ const tabs: InvokeTabInfo[] = [ ]; const enabledTabsSelector = createMemoizedSelector( - [stateSelector], - ({ config }) => { - const { disabledTabs } = config; - const enabledTabs = tabs.filter((tab) => !disabledTabs.includes(tab.id)); - return enabledTabs; - } + selectConfigSlice, + (config) => tabs.filter((tab) => !config.disabledTabs.includes(tab.id)) ); export const NO_GALLERY_PANEL_TABS: InvokeTabName[] = ['modelManager', 'queue']; diff --git a/invokeai/frontend/web/src/features/ui/hooks/usePanelStorage.ts b/invokeai/frontend/web/src/features/ui/hooks/usePanelStorage.ts index 0fdd9814ce..bacbad0a8c 100644 --- a/invokeai/frontend/web/src/features/ui/hooks/usePanelStorage.ts +++ b/invokeai/frontend/web/src/features/ui/hooks/usePanelStorage.ts @@ -4,7 +4,7 @@ import { useCallback } from 'react'; export const usePanelStorage = () => { const dispatch = useAppDispatch(); - const panels = useAppSelector((state) => state.ui.panels); + const panels = useAppSelector((s) => s.ui.panels); const getItem = useCallback((name: string) => panels[name] ?? '', [panels]); const setItem = useCallback( (name: string, value: string) => { diff --git a/invokeai/frontend/web/src/features/ui/store/hotkeysSlice.ts b/invokeai/frontend/web/src/features/ui/store/hotkeysSlice.ts deleted file mode 100644 index 2b59300ddd..0000000000 --- a/invokeai/frontend/web/src/features/ui/store/hotkeysSlice.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { PayloadAction } from '@reduxjs/toolkit'; -import { createSlice } from '@reduxjs/toolkit'; - -type HotkeysState = { - shift: boolean; - ctrl: boolean; - meta: boolean; -}; - -export const initialHotkeysState: HotkeysState = { - shift: false, - ctrl: false, - meta: false, -}; - -export const hotkeysSlice = createSlice({ - name: 'hotkeys', - initialState: initialHotkeysState, - reducers: { - shiftKeyPressed: (state, action: PayloadAction) => { - state.shift = action.payload; - }, - ctrlKeyPressed: (state, action: PayloadAction) => { - state.ctrl = action.payload; - }, - metaKeyPressed: (state, action: PayloadAction) => { - state.meta = action.payload; - }, - }, -}); - -export const { shiftKeyPressed, ctrlKeyPressed, metaKeyPressed } = - hotkeysSlice.actions; - -export default hotkeysSlice.reducer; diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts index 0abe18dcba..e80d28e86a 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts @@ -1,5 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; import { initialImageChanged } from 'features/parameters/store/generationSlice'; import type { InvokeTabName } from './tabMap'; @@ -60,3 +61,5 @@ export const { } = uiSlice.actions; export default uiSlice.reducer; + +export const selectUiSlice = (state: RootState) => state.ui; diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryListItem.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryListItem.tsx index 69bffa8ec4..372882ffa0 100644 --- a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryListItem.tsx +++ b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryListItem.tsx @@ -16,7 +16,7 @@ type Props = { const WorkflowLibraryListItem = ({ workflowDTO }: Props) => { const { t } = useTranslation(); - const workflowId = useAppSelector((state) => state.workflow.id); + const workflowId = useAppSelector((s) => s.workflow.id); const { onClose } = useWorkflowLibraryModalContext(); const { deleteWorkflow, deleteWorkflowResult } = useDeleteLibraryWorkflow({}); const { getAndLoadWorkflow, getAndLoadWorkflowResult } = diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem.tsx index fc5218bce8..47b74e41da 100644 --- a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem.tsx +++ b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem.tsx @@ -14,7 +14,7 @@ const NewWorkflowMenuItem = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const { isOpen, onOpen, onClose } = useDisclosure(); - const isTouched = useAppSelector((state) => state.workflow.isTouched); + const isTouched = useAppSelector((s) => s.workflow.isTouched); const handleNewWorkflow = useCallback(() => { dispatch(nodeEditorReset()); diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem.tsx index 049cf82e6d..8146a09b33 100644 --- a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem.tsx +++ b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem.tsx @@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next'; import { FaClone } from 'react-icons/fa'; const SaveWorkflowAsButton = () => { - const currentName = useAppSelector((state) => state.workflow.name); + const currentName = useAppSelector((s) => s.workflow.name); const { t } = useTranslation(); const { saveWorkflowAs } = useSaveWorkflowAs(); const [name, setName] = useState(getWorkflowCopyName(currentName)); diff --git a/invokeai/frontend/web/src/services/api/hooks/useBoardTotal.ts b/invokeai/frontend/web/src/services/api/hooks/useBoardTotal.ts index 984fe96e74..b8ea8a3080 100644 --- a/invokeai/frontend/web/src/services/api/hooks/useBoardTotal.ts +++ b/invokeai/frontend/web/src/services/api/hooks/useBoardTotal.ts @@ -7,7 +7,7 @@ import { } from 'services/api/endpoints/boards'; export const useBoardTotal = (board_id: BoardId) => { - const galleryView = useAppSelector((state) => state.gallery.galleryView); + const galleryView = useAppSelector((s) => s.gallery.galleryView); const { data: totalImages } = useGetBoardImagesTotalQuery(board_id); const { data: totalAssets } = useGetBoardAssetsTotalQuery(board_id); diff --git a/invokeai/frontend/web/src/services/api/hooks/useDebouncedImageWorkflow.ts b/invokeai/frontend/web/src/services/api/hooks/useDebouncedImageWorkflow.ts index 3ff7e2e6b9..ce31e83d5d 100644 --- a/invokeai/frontend/web/src/services/api/hooks/useDebouncedImageWorkflow.ts +++ b/invokeai/frontend/web/src/services/api/hooks/useDebouncedImageWorkflow.ts @@ -6,7 +6,7 @@ import { useDebounce } from 'use-debounce'; export const useDebouncedImageWorkflow = (imageDTO?: ImageDTO | null) => { const workflowFetchDebounce = useAppSelector( - (state) => state.config.workflowFetchDebounce ?? 300 + (s) => s.config.workflowFetchDebounce ?? 300 ); const [debouncedImageName] = useDebounce( diff --git a/invokeai/frontend/web/src/services/api/hooks/useDebouncedMetadata.ts b/invokeai/frontend/web/src/services/api/hooks/useDebouncedMetadata.ts index 1ed3b27475..9a8fee31af 100644 --- a/invokeai/frontend/web/src/services/api/hooks/useDebouncedMetadata.ts +++ b/invokeai/frontend/web/src/services/api/hooks/useDebouncedMetadata.ts @@ -5,7 +5,7 @@ import { useDebounce } from 'use-debounce'; export const useDebouncedMetadata = (imageName?: string | null) => { const metadataFetchDebounce = useAppSelector( - (state) => state.config.metadataFetchDebounce ?? 300 + (s) => s.config.metadataFetchDebounce ?? 300 ); const [debouncedImageName] = useDebounce(imageName, metadataFetchDebounce);