feat(ui): upgrade redux and RTK

There are a few breaking changes, which I've addressed.

The vast majority of changes are related to new handling of `reselect`'s `createSelector` options.

For better or worse, we memoize just about all our selectors using lodash `isEqual` for `resultEqualityCheck`. The upgrade requires we explicitly set the `memoize` option to `lruMemoize` to continue using lodash here.

Doing that required changing our `defaultSelectorOptions`.

Instead of changing that and finding dozens of instances where we weren't using that and instead were defining selector options manually, I've created a pre-configured selector: `createMemoizedSelector`.

This is now used everywhere instead of `createSelector`.
This commit is contained in:
psychedelicious 2023-12-09 00:16:02 +11:00
parent 99f14b1dfe
commit 72cb8b83fe
215 changed files with 1651 additions and 2444 deletions

View File

@ -65,7 +65,7 @@
"@mantine/form": "^6.0.19",
"@mantine/hooks": "^6.0.19",
"@nanostores/react": "^0.7.1",
"@reduxjs/toolkit": "^1.9.7",
"@reduxjs/toolkit": "^2.0.1",
"@roarr/browser-log-writer": "^1.3.0",
"@storybook/manager-api": "^7.6.4",
"@storybook/theming": "^7.6.4",
@ -92,7 +92,7 @@
"react-i18next": "^13.5.0",
"react-icons": "^4.12.0",
"react-konva": "^18.2.10",
"react-redux": "^8.1.3",
"react-redux": "^9.0.2",
"react-resizable-panels": "^0.0.55",
"react-use": "^17.4.2",
"react-virtuoso": "^4.6.2",
@ -130,7 +130,6 @@
"@types/node": "^20.9.0",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.17",
"@types/react-redux": "^7.1.32",
"@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",

View File

@ -57,8 +57,8 @@ dependencies:
specifier: ^0.7.1
version: 0.7.1(nanostores@0.9.5)(react@18.2.0)
'@reduxjs/toolkit':
specifier: ^1.9.7
version: 1.9.7(react-redux@8.1.3)(react@18.2.0)
specifier: ^2.0.1
version: 2.0.1(react-redux@9.0.2)(react@18.2.0)
'@roarr/browser-log-writer':
specifier: ^1.3.0
version: 1.3.0
@ -138,8 +138,8 @@ dependencies:
specifier: ^18.2.10
version: 18.2.10(konva@9.2.3)(react-dom@18.2.0)(react@18.2.0)
react-redux:
specifier: ^8.1.3
version: 8.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1)
specifier: ^9.0.2
version: 9.0.2(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0)(redux@5.0.0)
react-resizable-panels:
specifier: ^0.0.55
version: 0.0.55(react-dom@18.2.0)(react@18.2.0)
@ -157,7 +157,7 @@ dependencies:
version: 2.2.0
redux-remember:
specifier: ^4.2.2
version: 4.2.2(redux@4.2.1)
version: 4.2.2(redux@5.0.0)
roarr:
specifier: ^7.21.0
version: 7.21.0
@ -226,9 +226,6 @@ devDependencies:
'@types/react-dom':
specifier: ^18.2.17
version: 18.2.17
'@types/react-redux':
specifier: ^7.1.32
version: 7.1.32
'@types/uuid':
specifier: ^9.0.7
version: 9.0.7
@ -4493,23 +4490,23 @@ packages:
- immer
dev: false
/@reduxjs/toolkit@1.9.7(react-redux@8.1.3)(react@18.2.0):
resolution: {integrity: sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==}
/@reduxjs/toolkit@2.0.1(react-redux@9.0.2)(react@18.2.0):
resolution: {integrity: sha512-fxIjrR9934cmS8YXIGd9e7s1XRsEU++aFc9DVNMFMRTM5Vtsg2DCRMj21eslGtDt43IUf9bJL3h5bwUlZleibA==}
peerDependencies:
react: ^16.9.0 || ^17.0.0 || ^18
react-redux: ^7.2.1 || ^8.0.2
react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0
peerDependenciesMeta:
react:
optional: true
react-redux:
optional: true
dependencies:
immer: 9.0.21
immer: 10.0.3
react: 18.2.0
react-redux: 8.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1)
redux: 4.2.1
redux-thunk: 2.4.2(redux@4.2.1)
reselect: 4.1.8
react-redux: 9.0.2(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0)(redux@5.0.0)
redux: 5.0.0
redux-thunk: 3.1.0(redux@5.0.0)
reselect: 5.0.1
dev: false
/@roarr/browser-log-writer@1.3.0:
@ -5802,12 +5799,6 @@ packages:
'@types/node': 20.10.4
dev: true
/@types/hoist-non-react-statics@3.3.1:
resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==}
dependencies:
'@types/react': 18.2.42
hoist-non-react-statics: 3.3.2
/@types/http-errors@2.0.4:
resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
@ -5922,15 +5913,6 @@ packages:
'@types/react': 18.2.42
dev: false
/@types/react-redux@7.1.32:
resolution: {integrity: sha512-YJYV0M27cyHHJIacaRsZRx5OETzK8KWjEGnix7UH3ngItYo4It0MUBzU6WNwqnwhbrPw5wx9KXluuoTZ85Gg7A==}
dependencies:
'@types/hoist-non-react-statics': 3.3.1
'@types/react': 18.2.42
hoist-non-react-statics: 3.3.2
redux: 4.2.1
dev: true
/@types/react@18.2.42:
resolution: {integrity: sha512-c1zEr96MjakLYus/wPnuWDo1/zErfdU9rNsIGmE+NV71nx88FG9Ttgo5dqorXTu/LImX2f63WBP986gJkMPNbA==}
dependencies:
@ -8959,6 +8941,7 @@ packages:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
dependencies:
react-is: 16.13.1
dev: false
/hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
@ -9049,8 +9032,8 @@ packages:
engines: {node: '>= 4'}
dev: true
/immer@9.0.21:
resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==}
/immer@10.0.3:
resolution: {integrity: sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==}
dev: false
/import-fresh@3.3.0:
@ -11029,6 +11012,7 @@ packages:
/react-is@18.2.0:
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
dev: true
/react-konva@18.2.10(konva@9.2.3)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-ohcX1BJINL43m4ynjZ24MxFI1syjBdrXhqVxYVDw2rKgr3yuS0x/6m1Y2Z4sl4T/gKhfreBx8KHisd0XC6OT1g==}
@ -11057,15 +11041,15 @@ packages:
scheduler: 0.23.0
dev: false
/react-redux@8.1.3(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1):
resolution: {integrity: sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==}
/react-redux@9.0.2(@types/react-dom@18.2.17)(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0)(redux@5.0.0):
resolution: {integrity: sha512-34EI42cYZxJF59Iht6RDM5xDun5EdhV8CbJcTe+mYx97XMHLNYA6RrH9r/ZOZX3CetVCYfBEU9oAY9h3sZarsw==}
peerDependencies:
'@types/react': ^16.8 || ^17.0 || ^18.0
'@types/react-dom': ^16.8 || ^17.0 || ^18.0
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
react-native: '>=0.59'
redux: ^4 || ^5.0.0-beta.0
'@types/react': ^18.2.41
'@types/react-dom': ^18.2.17
react: ^18.0
react-dom: ^18.0
react-native: '>=0.71'
redux: ^5.0.0
peerDependenciesMeta:
'@types/react':
optional: true
@ -11078,16 +11062,12 @@ packages:
redux:
optional: true
dependencies:
'@babel/runtime': 7.23.5
'@types/hoist-non-react-statics': 3.3.1
'@types/react': 18.2.42
'@types/react-dom': 18.2.17
'@types/use-sync-external-store': 0.0.3
hoist-non-react-statics: 3.3.2
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-is: 18.2.0
redux: 4.2.1
redux: 5.0.0
use-sync-external-store: 1.2.0(react@18.2.0)
dev: false
@ -11329,26 +11309,25 @@ packages:
resolution: {integrity: sha512-GHESQC+Y0PV98ZBoaC6br6cDOsNiM1Cu4UleGMqMWCXX03jIr3BoozYVrRkLVVAl4sC216chakMnZOu6SwNdGA==}
dev: false
/redux-remember@4.2.2(redux@4.2.1):
/redux-remember@4.2.2(redux@5.0.0):
resolution: {integrity: sha512-1pY3FNk70uZ7Djya/SXkwL13hu0T6iVYSVMFqUApR4n3BDoGYmmaSouX8T3Y1Rg2pmZFaWw7e8ZaOAH82s8IiA==}
peerDependencies:
redux: '*'
dependencies:
redux: 4.2.1
redux: 5.0.0
dev: false
/redux-thunk@2.4.2(redux@4.2.1):
resolution: {integrity: sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==}
/redux-thunk@3.1.0(redux@5.0.0):
resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==}
peerDependencies:
redux: ^4
redux: ^5.0.0
dependencies:
redux: 4.2.1
redux: 5.0.0
dev: false
/redux@4.2.1:
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
dependencies:
'@babel/runtime': 7.23.5
/redux@5.0.0:
resolution: {integrity: sha512-blLIYmYetpZMET6Q6uCY7Jtl/Im5OBldy+vNPauA8vvsdqyt66oep4EUpAMWNHauTC6xa9JuRPhRB72rY82QGA==}
dev: false
/reflect.getprototypeof@1.0.3:
resolution: {integrity: sha512-TTAOZpkJ2YLxl7mVHWrNo3iDMEkYlva/kgFcXndqMgbo/AZUmmavEkdXV+hXtE4P8xdyEKRzalaFqZVuwIk/Nw==}
@ -11452,8 +11431,8 @@ packages:
hasBin: true
dev: true
/reselect@4.1.8:
resolution: {integrity: sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==}
/reselect@5.0.1:
resolution: {integrity: sha512-D72j2ubjgHpvuCiORWkOUxndHJrxDaSolheiz5CO+roz8ka97/4msh2E8F5qay4GawR5vzBt5MkbDHT+Rdy/Wg==}
dev: false
/resize-observer-polyfill@1.5.1:

View File

@ -1,4 +1,4 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useQueueBack } from 'features/queue/hooks/useQueueBack';
@ -9,20 +9,14 @@ import {
shiftKeyPressed,
} from 'features/ui/store/hotkeysSlice';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { isEqual } from 'lodash-es';
import React, { memo } from 'react';
import { isHotkeyPressed, useHotkeys } from 'react-hotkeys-hook';
const globalHotkeysSelector = createSelector(
const globalHotkeysSelector = createMemoizedSelector(
[stateSelector],
({ hotkeys }) => {
const { shift, ctrl, meta } = hotkeys;
return { shift, ctrl, meta };
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,8 +1,7 @@
import { createSelector } from '@reduxjs/toolkit';
import { createLogWriter } from '@roarr/browser-log-writer';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { isEqual } from 'lodash-es';
import { useEffect, useMemo } from 'react';
import { ROARR, Roarr } from 'roarr';
import {
@ -13,22 +12,14 @@ import {
logger,
} from './logger';
const selector = createSelector(
stateSelector,
({ system }) => {
const { consoleLogLevel, shouldLogToConsole } = system;
const selector = createMemoizedSelector(stateSelector, ({ system }) => {
const { consoleLogLevel, shouldLogToConsole } = system;
return {
consoleLogLevel,
shouldLogToConsole,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
return {
consoleLogLevel,
shouldLogToConsole,
};
});
export const useLogger = (namespace: LoggerNamespace) => {
const { consoleLogLevel, shouldLogToConsole } = useAppSelector(selector);

View File

@ -0,0 +1,12 @@
import { createSelectorCreator, lruMemoize } from '@reduxjs/toolkit';
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,
},
});

View File

@ -1,10 +1,10 @@
import { AnyAction } from '@reduxjs/toolkit';
import { UnknownAction } from '@reduxjs/toolkit';
import { isAnyGraphBuilt } from 'features/nodes/store/actions';
import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice';
import { receivedOpenAPISchema } from 'services/api/thunks/schema';
import { Graph } from 'services/api/types';
export const actionSanitizer = <A extends AnyAction>(action: A): A => {
export const actionSanitizer = <A extends UnknownAction>(action: A): A => {
if (isAnyGraphBuilt(action)) {
if (action.payload.nodes) {
const sanitizedNodes: Graph['nodes'] = {};

View File

@ -1,11 +1,10 @@
import type { TypedAddListener, TypedStartListening } from '@reduxjs/toolkit';
import {
AnyAction,
UnknownAction,
ListenerEffect,
addListener,
createListenerMiddleware,
} from '@reduxjs/toolkit';
import type { AppDispatch, RootState } from 'app/store/store';
import { addCommitStagingAreaImageListener } from './listeners/addCommitStagingAreaImageListener';
import { addFirstListImagesListener } from './listeners/addFirstListImagesListener.ts';
@ -87,7 +86,7 @@ export const addAppListener = addListener as TypedAddListener<
>;
export type AppListenerEffect = ListenerEffect<
AnyAction,
UnknownAction,
RootState,
AppDispatch
>;

View File

@ -52,7 +52,11 @@ export const addBoardIdSelectedListener = () => {
const { data: boardImagesData } =
imagesApi.endpoints.listImages.select(queryArgs)(getState());
if (boardImagesData) {
if (
boardImagesData &&
boardIdSelected.match(action) &&
action.payload.selectedImageName
) {
const firstImage = imagesSelectors.selectAll(boardImagesData)[0];
const selectedImage = imagesSelectors.selectById(
boardImagesData,

View File

@ -24,7 +24,7 @@ export const addSocketQueueItemStatusChangedEventListener = () => {
dispatch(
queueApi.util.updateQueryData('listQueueItems', undefined, (draft) => {
queueItemsAdapter.updateOne(draft, {
id: queue_item.item_id,
id: String(queue_item.item_id),
changes: queue_item,
});
})

View File

@ -1,6 +1,6 @@
import {
AnyAction,
ThunkDispatch,
UnknownAction,
autoBatchEnhancer,
combineReducers,
configureStore,
@ -27,7 +27,6 @@ import { createStore as createIDBKeyValStore, get, set } from 'idb-keyval';
import dynamicMiddlewares from 'redux-dynamic-middlewares';
import { Driver, rememberEnhancer, rememberReducer } from 'redux-remember';
import { api } from 'services/api';
import { authToastMiddleware } from 'services/api/authToastMiddleware';
import { STORAGE_PREFIX } from './constants';
import { serialize } from './enhancers/reduxRemember/serialize';
import { unserialize } from './enhancers/reduxRemember/unserialize';
@ -90,8 +89,16 @@ const idbKeyValDriver: Driver = {
export const createStore = (uniqueStoreKey?: string, persist = true) =>
configureStore({
reducer: rememberedRootReducer,
enhancers: (existingEnhancers) => {
const _enhancers = existingEnhancers.concat(autoBatchEnhancer());
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
immutableCheck: false,
})
.concat(api.middleware)
.concat(dynamicMiddlewares)
.prepend(listenerMiddleware.middleware),
enhancers: (getDefaultEnhancers) => {
const _enhancers = getDefaultEnhancers().concat(autoBatchEnhancer());
if (persist) {
_enhancers.push(
rememberEnhancer(idbKeyValDriver, rememberedKeys, {
@ -106,15 +113,6 @@ export const createStore = (uniqueStoreKey?: string, persist = true) =>
}
return _enhancers;
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
immutableCheck: false,
})
.concat(api.middleware)
.concat(dynamicMiddlewares)
.concat(authToastMiddleware)
.prepend(listenerMiddleware.middleware),
devTools: {
actionSanitizer,
stateSanitizer,
@ -145,6 +143,6 @@ export type AppGetState = ReturnType<
>;
export type RootState = ReturnType<ReturnType<typeof createStore>['getState']>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AppThunkDispatch = ThunkDispatch<RootState, any, AnyAction>;
export type AppThunkDispatch = ThunkDispatch<RootState, any, UnknownAction>;
export type AppDispatch = ReturnType<typeof createStore>['dispatch'];
export const stateSelector = (state: RootState) => state;

View File

@ -1,7 +0,0 @@
import { isEqual } from 'lodash-es';
export const defaultSelectorOptions = {
memoizeOptions: {
resultEqualityCheck: isEqual,
},
};

View File

@ -12,7 +12,6 @@
* - increment it in `onPaneClick`
* - `useEffect()` to close the menu when `globalContextMenuCloseTrigger` changes
*/
import {
Menu,
MenuButton,

View File

@ -19,7 +19,6 @@ import { useAppDispatch } from 'app/store/storeHooks';
import { stopPastePropagation } from 'common/util/stopPastePropagation';
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
import { clamp } from 'lodash-es';
import {
FocusEvent,
KeyboardEvent,

View File

@ -1,9 +1,8 @@
import { Box } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppToaster } from 'app/components/Toaster';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { AnimatePresence, motion } from 'framer-motion';
import {
@ -20,7 +19,7 @@ import { useUploadImageMutation } from 'services/api/endpoints/images';
import { PostUploadAction } from 'services/api/types';
import ImageUploadOverlay from './ImageUploadOverlay';
const selector = createSelector(
const selector = createMemoizedSelector(
[stateSelector, activeTabNameSelector],
({ gallery }, activeTabName) => {
let postUploadAction: PostUploadAction = { type: 'TOAST' };
@ -39,8 +38,7 @@ const selector = createSelector(
autoAddBoardId,
postUploadAction,
};
},
defaultSelectorOptions
}
);
type ImageUploaderProps = {

View File

@ -1,7 +1,6 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterAll } from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
import { isInvocationNode } from 'features/nodes/types/invocation';
@ -10,7 +9,7 @@ import i18n from 'i18next';
import { forEach } from 'lodash-es';
import { getConnectedEdges } from 'reactflow';
const selector = createSelector(
const selector = createMemoizedSelector(
[stateSelector, activeTabNameSelector],
(
{ controlAdapters, generation, system, nodes, dynamicPrompts },
@ -125,8 +124,7 @@ const selector = createSelector(
}
return { isReady: !reasons.length, reasons };
},
defaultSelectorOptions
}
);
export const useIsReadyToEnqueue = () => {

View File

@ -1,5 +1,4 @@
// https://stackoverflow.com/a/73731908
import { useEffect, useState } from 'react';
export function useSingleAndDoubleClick(

View File

@ -1,16 +1,7 @@
import { Box, chakra, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import {
canvasSelector,
isStagingSelector,
} from 'features/canvas/store/canvasSelectors';
import Konva from 'konva';
import { KonvaEventObject } from 'konva/lib/Node';
import { Vector2d } from 'konva/lib/types';
import { memo, useCallback, useEffect, useRef } from 'react';
import { Layer, Stage } from 'react-konva';
import useCanvasDragMove from 'features/canvas/hooks/useCanvasDragMove';
import useCanvasHotkeys from 'features/canvas/hooks/useCanvasHotkeys';
import useCanvasMouseDown from 'features/canvas/hooks/useCanvasMouseDown';
@ -18,11 +9,17 @@ import useCanvasMouseMove from 'features/canvas/hooks/useCanvasMouseMove';
import useCanvasMouseOut from 'features/canvas/hooks/useCanvasMouseOut';
import useCanvasMouseUp from 'features/canvas/hooks/useCanvasMouseUp';
import useCanvasWheel from 'features/canvas/hooks/useCanvasZoom';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { canvasResized } from 'features/canvas/store/canvasSlice';
import {
setCanvasBaseLayer,
setCanvasStage,
} from 'features/canvas/util/konvaInstanceProvider';
import Konva from 'konva';
import { KonvaEventObject } from 'konva/lib/Node';
import { Vector2d } from 'konva/lib/types';
import { memo, useCallback, useEffect, useRef } from 'react';
import { Layer, Stage } from 'react-konva';
import IAICanvasBoundingBoxOverlay from './IAICanvasBoundingBoxOverlay';
import IAICanvasGrid from './IAICanvasGrid';
import IAICanvasIntermediateImage from './IAICanvasIntermediateImage';
@ -35,9 +32,9 @@ import IAICanvasStatusText from './IAICanvasStatusText';
import IAICanvasBoundingBox from './IAICanvasToolbar/IAICanvasBoundingBox';
import IAICanvasToolPreview from './IAICanvasToolPreview';
const selector = createSelector(
[canvasSelector, isStagingSelector],
(canvas, isStaging) => {
const selector = createMemoizedSelector(
[stateSelector, isStagingSelector],
({ canvas }, isStaging) => {
const {
isMaskEnabled,
stageScale,
@ -83,8 +80,7 @@ const selector = createSelector(
shouldShowIntermediates,
shouldAntialias,
};
},
defaultSelectorOptions
}
);
const ChakraStage = chakra(Stage, {

View File

@ -1,38 +1,28 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { isEqual } from 'lodash-es';
import { Group, Rect } from 'react-konva';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { memo } from 'react';
import { Group, Rect } from 'react-konva';
const selector = createSelector(
canvasSelector,
(canvas) => {
const {
boundingBoxCoordinates,
boundingBoxDimensions,
stageDimensions,
stageScale,
shouldDarkenOutsideBoundingBox,
stageCoordinates,
} = canvas;
const selector = createMemoizedSelector(stateSelector, ({ canvas }) => {
const {
boundingBoxCoordinates,
boundingBoxDimensions,
stageDimensions,
stageScale,
shouldDarkenOutsideBoundingBox,
stageCoordinates,
} = canvas;
return {
boundingBoxCoordinates,
boundingBoxDimensions,
shouldDarkenOutsideBoundingBox,
stageCoordinates,
stageDimensions,
stageScale,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
return {
boundingBoxCoordinates,
boundingBoxDimensions,
shouldDarkenOutsideBoundingBox,
stageCoordinates,
stageDimensions,
stageScale,
};
});
const IAICanvasBoundingBoxOverlay = () => {
const {
boundingBoxCoordinates,

View File

@ -1,26 +1,16 @@
// Grid drawing adapted from https://longviewcoder.com/2021/12/08/konva-a-better-grid/
import { useColorMode, useToken } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { isEqual, range } from 'lodash-es';
import { range } from 'lodash-es';
import { ReactNode, memo, useCallback, useLayoutEffect, useState } from 'react';
import { Group, Line as KonvaLine } from 'react-konva';
const selector = createSelector(
[canvasSelector],
(canvas) => {
const { stageScale, stageCoordinates, stageDimensions } = canvas;
return { stageScale, stageCoordinates, stageDimensions };
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
const selector = createMemoizedSelector([stateSelector], ({ canvas }) => {
const { stageScale, stageCoordinates, stageDimensions } = canvas;
return { stageScale, stageCoordinates, stageDimensions };
});
const IAICanvasGrid = () => {
const { stageScale, stageCoordinates, stageDimensions } =

View File

@ -1,4 +1,4 @@
import { skipToken } from '@reduxjs/toolkit/dist/query';
import { skipToken } from '@reduxjs/toolkit/query';
import { $authToken } from 'app/store/nanostores/authToken';
import { memo } from 'react';
import { Image } from 'react-konva';

View File

@ -2,31 +2,22 @@ import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { ImageConfig } from 'konva/lib/shapes/Image';
import { isEqual } from 'lodash-es';
import { memo, useEffect, useState } from 'react';
import { Image as KonvaImage } from 'react-konva';
const selector = createSelector(
[stateSelector],
({ system, canvas }) => {
const { denoiseProgress } = system;
const { boundingBox } = canvas.layerState.stagingArea;
const { batchIds } = canvas;
const selector = createSelector([stateSelector], ({ system, canvas }) => {
const { denoiseProgress } = system;
const { boundingBox } = canvas.layerState.stagingArea;
const { batchIds } = canvas;
return {
boundingBox,
progressImage:
denoiseProgress && batchIds.includes(denoiseProgress.batch_id)
? denoiseProgress.progress_image
: undefined,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
return {
boundingBox,
progressImage:
denoiseProgress && batchIds.includes(denoiseProgress.batch_id)
? denoiseProgress.progress_image
: undefined,
};
});
type Props = Omit<ImageConfig, 'image'>;

View File

@ -1,17 +1,16 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { RectConfig } from 'konva/lib/shapes/Rect';
import { Rect } from 'react-konva';
import { rgbaColorToString } from 'features/canvas/util/colorToString';
import Konva from 'konva';
import { RectConfig } from 'konva/lib/shapes/Rect';
import { isNumber } from 'lodash-es';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { Rect } from 'react-konva';
export const canvasMaskCompositerSelector = createSelector(
canvasSelector,
(canvas) => {
export const canvasMaskCompositerSelector = createMemoizedSelector(
stateSelector,
({ canvas }) => {
const { maskColor, stageCoordinates, stageDimensions, stageScale } = canvas;
return {

View File

@ -1,22 +1,15 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { GroupConfig } from 'konva/lib/Group';
import { isEqual } from 'lodash-es';
import { Group, Line } from 'react-konva';
import { isCanvasMaskLine } from 'features/canvas/store/canvasTypes';
import { GroupConfig } from 'konva/lib/Group';
import { memo } from 'react';
import { Group, Line } from 'react-konva';
export const canvasLinesSelector = createSelector(
[canvasSelector],
(canvas) => {
export const canvasLinesSelector = createMemoizedSelector(
[stateSelector],
({ canvas }) => {
return { objects: canvas.layerState.objects };
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,35 +1,25 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { rgbaColorToString } from 'features/canvas/util/colorToString';
import { isEqual } from 'lodash-es';
import { Group, Line, Rect } from 'react-konva';
import {
isCanvasBaseImage,
isCanvasBaseLine,
isCanvasEraseRect,
isCanvasFillRect,
} from 'features/canvas/store/canvasTypes';
import IAICanvasImage from './IAICanvasImage';
import { rgbaColorToString } from 'features/canvas/util/colorToString';
import { memo } from 'react';
import { Group, Line, Rect } from 'react-konva';
import IAICanvasImage from './IAICanvasImage';
const selector = createSelector(
[canvasSelector],
(canvas) => {
const {
layerState: { objects },
} = canvas;
return {
objects,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
const selector = createMemoizedSelector([stateSelector], ({ canvas }) => {
const {
layerState: { objects },
} = canvas;
return {
objects,
};
});
const IAICanvasObjectRenderer = () => {
const { objects } = useAppSelector(selector);

View File

@ -1,46 +1,37 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { GroupConfig } from 'konva/lib/Group';
import { isEqual } from 'lodash-es';
import { memo } from 'react';
import { Group, Rect } from 'react-konva';
import IAICanvasImage from './IAICanvasImage';
const selector = createSelector(
[canvasSelector],
(canvas) => {
const {
layerState,
shouldShowStagingImage,
shouldShowStagingOutline,
boundingBoxCoordinates: stageBoundingBoxCoordinates,
boundingBoxDimensions: stageBoundingBoxDimensions,
} = canvas;
const selector = createMemoizedSelector([stateSelector], ({ canvas }) => {
const {
layerState,
shouldShowStagingImage,
shouldShowStagingOutline,
boundingBoxCoordinates: stageBoundingBoxCoordinates,
boundingBoxDimensions: stageBoundingBoxDimensions,
} = canvas;
const { selectedImageIndex, images, boundingBox } = layerState.stagingArea;
const { selectedImageIndex, images, boundingBox } = layerState.stagingArea;
return {
currentStagingAreaImage:
images.length > 0 && selectedImageIndex !== undefined
? images[selectedImageIndex]
: undefined,
isOnFirstImage: selectedImageIndex === 0,
isOnLastImage: selectedImageIndex === images.length - 1,
shouldShowStagingImage,
shouldShowStagingOutline,
x: boundingBox?.x ?? stageBoundingBoxCoordinates.x,
y: boundingBox?.y ?? stageBoundingBoxCoordinates.y,
width: boundingBox?.width ?? stageBoundingBoxDimensions.width,
height: boundingBox?.height ?? stageBoundingBoxDimensions.height,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
return {
currentStagingAreaImage:
images.length > 0 && selectedImageIndex !== undefined
? images[selectedImageIndex]
: undefined,
isOnFirstImage: selectedImageIndex === 0,
isOnLastImage: selectedImageIndex === images.length - 1,
shouldShowStagingImage,
shouldShowStagingOutline,
x: boundingBox?.x ?? stageBoundingBoxCoordinates.x,
y: boundingBox?.y ?? stageBoundingBoxCoordinates.y,
width: boundingBox?.width ?? stageBoundingBoxDimensions.width,
height: boundingBox?.height ?? stageBoundingBoxDimensions.height,
};
});
type Props = GroupConfig;

View File

@ -1,8 +1,11 @@
import { ButtonGroup, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
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 IAIButton from 'common/components/IAIButton';
import IAIIconButton from 'common/components/IAIIconButton';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { stagingAreaImageSaved } from 'features/canvas/store/actions';
import {
commitStagingAreaImage,
discardStagedImages,
@ -11,10 +14,6 @@ import {
setShouldShowStagingImage,
setShouldShowStagingOutline,
} from 'features/canvas/store/canvasSlice';
import { skipToken } from '@reduxjs/toolkit/dist/query';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIButton from 'common/components/IAIButton';
import { memo, useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
@ -28,30 +27,25 @@ import {
FaTimes,
} from 'react-icons/fa';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { stagingAreaImageSaved } from 'features/canvas/store/actions';
const selector = createSelector(
[canvasSelector],
(canvas) => {
const {
layerState: {
stagingArea: { images, selectedImageIndex },
},
shouldShowStagingOutline,
shouldShowStagingImage,
} = canvas;
const selector = createMemoizedSelector([stateSelector], ({ canvas }) => {
const {
layerState: {
stagingArea: { images, selectedImageIndex },
},
shouldShowStagingOutline,
shouldShowStagingImage,
} = canvas;
return {
currentIndex: selectedImageIndex,
total: images.length,
currentStagingAreaImage:
images.length > 0 ? images[selectedImageIndex] : undefined,
shouldShowStagingImage,
shouldShowStagingOutline,
};
},
defaultSelectorOptions
);
return {
currentIndex: selectedImageIndex,
total: images.length,
currentStagingAreaImage:
images.length > 0 ? images[selectedImageIndex] : undefined,
shouldShowStagingImage,
shouldShowStagingOutline,
};
});
const IAICanvasStagingAreaToolbar = () => {
const dispatch = useAppDispatch();

View File

@ -1,74 +1,65 @@
import { Box, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import GenerationModeStatusText from 'features/parameters/components/Parameters/Canvas/GenerationModeStatusText';
import { isEqual } from 'lodash-es';
import { useTranslation } from 'react-i18next';
import roundToHundreth from 'features/canvas/util/roundToHundreth';
import IAICanvasStatusTextCursorPos from './IAICanvasStatusText/IAICanvasStatusTextCursorPos';
import GenerationModeStatusText from 'features/parameters/components/Parameters/Canvas/GenerationModeStatusText';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import IAICanvasStatusTextCursorPos from './IAICanvasStatusText/IAICanvasStatusTextCursorPos';
const warningColor = 'var(--invokeai-colors-warning-500)';
const selector = createSelector(
[canvasSelector],
(canvas) => {
const {
stageDimensions: { width: stageWidth, height: stageHeight },
stageCoordinates: { x: stageX, y: stageY },
boundingBoxDimensions: { width: boxWidth, height: boxHeight },
scaledBoundingBoxDimensions: {
width: scaledBoxWidth,
height: scaledBoxHeight,
},
boundingBoxCoordinates: { x: boxX, y: boxY },
stageScale,
shouldShowCanvasDebugInfo,
layer,
boundingBoxScaleMethod,
shouldPreserveMaskedArea,
} = canvas;
let boundingBoxColor = 'inherit';
if (
(boundingBoxScaleMethod === 'none' &&
(boxWidth < 512 || boxHeight < 512)) ||
(boundingBoxScaleMethod === 'manual' &&
scaledBoxWidth * scaledBoxHeight < 512 * 512)
) {
boundingBoxColor = warningColor;
}
const activeLayerColor = layer === 'mask' ? warningColor : 'inherit';
return {
activeLayerColor,
layer,
boundingBoxColor,
boundingBoxCoordinatesString: `(${roundToHundreth(
boxX
)}, ${roundToHundreth(boxY)})`,
boundingBoxDimensionsString: `${boxWidth}×${boxHeight}`,
scaledBoundingBoxDimensionsString: `${scaledBoxWidth}×${scaledBoxHeight}`,
canvasCoordinatesString: `${roundToHundreth(stageX)}×${roundToHundreth(
stageY
)}`,
canvasDimensionsString: `${stageWidth}×${stageHeight}`,
canvasScaleString: Math.round(stageScale * 100),
shouldShowCanvasDebugInfo,
shouldShowBoundingBox: boundingBoxScaleMethod !== 'auto',
shouldShowScaledBoundingBox: boundingBoxScaleMethod !== 'none',
shouldPreserveMaskedArea,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
const selector = createMemoizedSelector([stateSelector], ({ canvas }) => {
const {
stageDimensions: { width: stageWidth, height: stageHeight },
stageCoordinates: { x: stageX, y: stageY },
boundingBoxDimensions: { width: boxWidth, height: boxHeight },
scaledBoundingBoxDimensions: {
width: scaledBoxWidth,
height: scaledBoxHeight,
},
boundingBoxCoordinates: { x: boxX, y: boxY },
stageScale,
shouldShowCanvasDebugInfo,
layer,
boundingBoxScaleMethod,
shouldPreserveMaskedArea,
} = canvas;
let boundingBoxColor = 'inherit';
if (
(boundingBoxScaleMethod === 'none' &&
(boxWidth < 512 || boxHeight < 512)) ||
(boundingBoxScaleMethod === 'manual' &&
scaledBoxWidth * scaledBoxHeight < 512 * 512)
) {
boundingBoxColor = warningColor;
}
);
const activeLayerColor = layer === 'mask' ? warningColor : 'inherit';
return {
activeLayerColor,
layer,
boundingBoxColor,
boundingBoxCoordinatesString: `(${roundToHundreth(boxX)}, ${roundToHundreth(
boxY
)})`,
boundingBoxDimensionsString: `${boxWidth}×${boxHeight}`,
scaledBoundingBoxDimensionsString: `${scaledBoxWidth}×${scaledBoxHeight}`,
canvasCoordinatesString: `${roundToHundreth(stageX)}×${roundToHundreth(
stageY
)}`,
canvasDimensionsString: `${stageWidth}×${stageHeight}`,
canvasScaleString: Math.round(stageScale * 100),
shouldShowCanvasDebugInfo,
shouldShowBoundingBox: boundingBoxScaleMethod !== 'auto',
shouldShowScaledBoundingBox: boundingBoxScaleMethod !== 'none',
shouldPreserveMaskedArea,
};
});
const IAICanvasStatusText = () => {
const {

View File

@ -1,15 +1,13 @@
import { Box } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import roundToHundreth from 'features/canvas/util/roundToHundreth';
import { isEqual } from 'lodash-es';
import { useTranslation } from 'react-i18next';
const cursorPositionSelector = createSelector(
[canvasSelector],
(canvas) => {
const cursorPositionSelector = createMemoizedSelector(
[stateSelector],
({ canvas }) => {
const { cursorPosition } = canvas;
const { cursorX, cursorY } = cursorPosition
@ -21,11 +19,6 @@ const cursorPositionSelector = createSelector(
cursorY
)})`,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,20 +1,18 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { rgbaColorToString } from 'features/canvas/util/colorToString';
import { GroupConfig } from 'konva/lib/Group';
import { isEqual } from 'lodash-es';
import { Circle, Group } from 'react-konva';
import {
COLOR_PICKER_SIZE,
COLOR_PICKER_STROKE_RADIUS,
} from 'features/canvas/util/constants';
import { GroupConfig } from 'konva/lib/Group';
import { memo } from 'react';
import { Circle, Group } from 'react-konva';
const canvasBrushPreviewSelector = createSelector(
canvasSelector,
(canvas) => {
const canvasBrushPreviewSelector = createMemoizedSelector(
stateSelector,
({ canvas }) => {
const {
cursorPosition,
brushSize,
@ -105,11 +103,6 @@ const canvasBrushPreviewSelector = createSelector(
dotRadius: 1.5 / stageScale,
clip,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,4 +1,4 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
@ -17,13 +17,11 @@ import Konva from 'konva';
import { GroupConfig } from 'konva/lib/Group';
import { KonvaEventObject } from 'konva/lib/Node';
import { Vector2d } from 'konva/lib/types';
import { isEqual } from 'lodash-es';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { Group, Rect, Transformer } from 'react-konva';
const boundingBoxPreviewSelector = createSelector(
const boundingBoxPreviewSelector = createMemoizedSelector(
[stateSelector],
({ canvas, generation }) => {
const {
@ -51,11 +49,6 @@ const boundingBoxPreviewSelector = createSelector(
hitStrokeWidth: 20 / stageScale,
aspectRatio,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,5 +1,6 @@
import { Box, ButtonGroup, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIButton from 'common/components/IAIButton';
import IAIColorPicker from 'common/components/IAIColorPicker';
@ -7,10 +8,7 @@ import IAIIconButton from 'common/components/IAIIconButton';
import IAIPopover from 'common/components/IAIPopover';
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
import { canvasMaskSavedToGallery } from 'features/canvas/store/actions';
import {
canvasSelector,
isStagingSelector,
} from 'features/canvas/store/canvasSelectors';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
clearMask,
setIsMaskEnabled,
@ -19,17 +17,15 @@ import {
setShouldPreserveMaskedArea,
} from 'features/canvas/store/canvasSlice';
import { rgbaColorToString } from 'features/canvas/util/colorToString';
import { isEqual } from 'lodash-es';
import { ChangeEvent, memo, useCallback } from 'react';
import { RgbaColor } from 'react-colorful';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { FaMask, FaSave, FaTrash } from 'react-icons/fa';
export const selector = createSelector(
[canvasSelector, isStagingSelector],
(canvas, isStaging) => {
export const selector = createMemoizedSelector(
[stateSelector, isStagingSelector],
({ canvas }, isStaging) => {
const { maskColor, layer, isMaskEnabled, shouldPreserveMaskedArea } =
canvas;
@ -41,11 +37,6 @@ export const selector = createSelector(
shouldPreserveMaskedArea,
isStaging,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
const IAICanvasMaskOptions = () => {

View File

@ -1,18 +1,15 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton';
import { redo } from 'features/canvas/store/canvasSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { FaRedo } from 'react-icons/fa';
import { redo } from 'features/canvas/store/canvasSlice';
import { stateSelector } from 'app/store/store';
import { isEqual } from 'lodash-es';
import { useTranslation } from 'react-i18next';
import { useCallback } from 'react';
const canvasRedoSelector = createSelector(
const canvasRedoSelector = createMemoizedSelector(
[stateSelector, activeTabNameSelector],
({ canvas }, activeTabName) => {
const { futureLayerStates } = canvas;
@ -21,11 +18,6 @@ const canvasRedoSelector = createSelector(
canRedo: futureLayerStates.length > 0,
activeTabName,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,10 +1,11 @@
import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
import IAIIconButton from 'common/components/IAIIconButton';
import IAIPopover from 'common/components/IAIPopover';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
import ClearCanvasHistoryButtonModal from 'features/canvas/components/ClearCanvasHistoryButtonModal';
import {
setShouldAntialias,
setShouldAutoSave,
@ -16,17 +17,14 @@ import {
setShouldShowIntermediates,
setShouldSnapToGrid,
} from 'features/canvas/store/canvasSlice';
import { isEqual } from 'lodash-es';
import { ChangeEvent, memo, useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { FaWrench } from 'react-icons/fa';
import ClearCanvasHistoryButtonModal from 'features/canvas/components/ClearCanvasHistoryButtonModal';
export const canvasControlsSelector = createSelector(
[canvasSelector],
(canvas) => {
export const canvasControlsSelector = createMemoizedSelector(
[stateSelector],
({ canvas }) => {
const {
shouldAutoSave,
shouldCropToBoundingBoxOnSave,
@ -50,11 +48,6 @@ export const canvasControlsSelector = createSelector(
shouldRestrictStrokesToBox,
shouldAntialias,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,5 +1,5 @@
import { ButtonGroup, Flex, Box } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { Box, ButtonGroup, 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';
@ -14,10 +14,9 @@ import {
setBrushSize,
setTool,
} from 'features/canvas/store/canvasSlice';
import { clamp, isEqual } from 'lodash-es';
import { clamp } from 'lodash-es';
import { memo, useCallback } from 'react';
import { RgbaColor } from 'react-colorful';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import {
@ -29,7 +28,7 @@ import {
FaSlidersH,
} from 'react-icons/fa';
export const selector = createSelector(
export const selector = createMemoizedSelector(
[stateSelector, isStagingSelector],
({ canvas }, isStaging) => {
const { tool, brushColor, brushSize } = canvas;
@ -40,11 +39,6 @@ export const selector = createSelector(
brushColor,
brushSize,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,9 +1,10 @@
import { Box, ButtonGroup, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
import { useSingleAndDoubleClick } from 'common/hooks/useSingleAndDoubleClick';
import {
@ -25,8 +26,6 @@ import {
LAYER_NAMES_DICT,
} from 'features/canvas/store/canvasTypes';
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
import { isEqual } from 'lodash-es';
import { memo, useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
@ -46,7 +45,7 @@ import IAICanvasSettingsButtonPopover from './IAICanvasSettingsButtonPopover';
import IAICanvasToolChooserOptions from './IAICanvasToolChooserOptions';
import IAICanvasUndoButton from './IAICanvasUndoButton';
export const selector = createSelector(
export const selector = createMemoizedSelector(
[stateSelector, isStagingSelector],
({ canvas }, isStaging) => {
const { tool, shouldCropToBoundingBoxOnSave, layer, isMaskEnabled } =
@ -59,11 +58,6 @@ export const selector = createSelector(
layer,
shouldCropToBoundingBoxOnSave,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,18 +1,15 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton';
import { useHotkeys } from 'react-hotkeys-hook';
import { FaUndo } from 'react-icons/fa';
import { undo } from 'features/canvas/store/canvasSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { isEqual } from 'lodash-es';
import { useTranslation } from 'react-i18next';
import { stateSelector } from 'app/store/store';
import { useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { FaUndo } from 'react-icons/fa';
const canvasUndoSelector = createSelector(
const canvasUndoSelector = createMemoizedSelector(
[stateSelector, activeTabNameSelector],
({ canvas }, activeTabName) => {
const { pastLayerStates } = canvas;
@ -21,11 +18,6 @@ const canvasUndoSelector = createSelector(
canUndo: pastLayerStates.length > 0,
activeTabName,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,21 +1,18 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
canvasSelector,
isStagingSelector,
} from 'features/canvas/store/canvasSelectors';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
setIsMovingStage,
setStageCoordinates,
} from 'features/canvas/store/canvasSlice';
import { KonvaEventObject } from 'konva/lib/Node';
import { isEqual } from 'lodash-es';
import { useCallback } from 'react';
const selector = createSelector(
[canvasSelector, isStagingSelector],
(canvas, isStaging) => {
const selector = createMemoizedSelector(
[stateSelector, isStagingSelector],
({ canvas }, isStaging) => {
const { tool, isMovingBoundingBox } = canvas;
return {
tool,

View File

@ -1,9 +1,7 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
canvasSelector,
isStagingSelector,
} from 'features/canvas/store/canvasSelectors';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
clearMask,
resetCanvasInteractionState,
@ -12,17 +10,15 @@ import {
setShouldSnapToGrid,
setTool,
} from 'features/canvas/store/canvasSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { isEqual } from 'lodash-es';
import { useRef } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { CanvasTool } from 'features/canvas/store/canvasTypes';
import { getCanvasStage } from 'features/canvas/util/konvaInstanceProvider';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useRef } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
const selector = createSelector(
[canvasSelector, activeTabNameSelector, isStagingSelector],
(canvas, activeTabName, isStaging) => {
const selector = createMemoizedSelector(
[stateSelector, activeTabNameSelector, isStagingSelector],
({ canvas }, activeTabName, isStaging) => {
const {
cursorPosition,
shouldLockBoundingBox,
@ -42,11 +38,6 @@ const selector = createSelector(
isMaskEnabled,
shouldSnapToGrid,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,26 +1,23 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
canvasSelector,
isStagingSelector,
} from 'features/canvas/store/canvasSelectors';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
addLine,
setIsDrawing,
setIsMovingStage,
} from 'features/canvas/store/canvasSlice';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import Konva from 'konva';
import { KonvaEventObject } from 'konva/lib/Node';
import { isEqual } from 'lodash-es';
import { MutableRefObject, useCallback } from 'react';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
import useColorPicker from './useColorUnderCursor';
const selector = createSelector(
[activeTabNameSelector, canvasSelector, isStagingSelector],
(activeTabName, canvas, isStaging) => {
const selector = createMemoizedSelector(
[activeTabNameSelector, stateSelector, isStagingSelector],
(activeTabName, { canvas }, isStaging) => {
const { tool } = canvas;
return {
tool,

View File

@ -1,25 +1,22 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
canvasSelector,
isStagingSelector,
} from 'features/canvas/store/canvasSelectors';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
addPointToCurrentLine,
setCursorPosition,
} from 'features/canvas/store/canvasSlice';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import Konva from 'konva';
import { Vector2d } from 'konva/lib/types';
import { isEqual } from 'lodash-es';
import { MutableRefObject, useCallback } from 'react';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
import useColorPicker from './useColorUnderCursor';
const selector = createSelector(
[activeTabNameSelector, canvasSelector, isStagingSelector],
(activeTabName, canvas, isStaging) => {
const selector = createMemoizedSelector(
[activeTabNameSelector, stateSelector, isStagingSelector],
(activeTabName, { canvas }, isStaging) => {
const { tool, isDrawing } = canvas;
return {
tool,

View File

@ -1,25 +1,22 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
canvasSelector,
isStagingSelector,
} from 'features/canvas/store/canvasSelectors';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
// addPointToCurrentEraserLine,
addPointToCurrentLine,
setIsDrawing,
setIsMovingStage,
} from 'features/canvas/store/canvasSlice';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import Konva from 'konva';
import { isEqual } from 'lodash-es';
import { MutableRefObject, useCallback } from 'react';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
const selector = createSelector(
[activeTabNameSelector, canvasSelector, isStagingSelector],
(activeTabName, canvas, isStaging) => {
const selector = createMemoizedSelector(
[activeTabNameSelector, stateSelector, isStagingSelector],
(activeTabName, { canvas }, isStaging) => {
const { tool, isDrawing } = canvas;
return {
tool,

View File

@ -1,24 +1,23 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import {
setStageCoordinates,
setStageScale,
} from 'features/canvas/store/canvasSlice';
import Konva from 'konva';
import { KonvaEventObject } from 'konva/lib/Node';
import { clamp, isEqual } from 'lodash-es';
import { MutableRefObject, useCallback } from 'react';
import {
CANVAS_SCALE_BY,
MAX_CANVAS_SCALE,
MIN_CANVAS_SCALE,
} from 'features/canvas/util/constants';
import Konva from 'konva';
import { KonvaEventObject } from 'konva/lib/Node';
import { clamp, isEqual } from 'lodash-es';
import { MutableRefObject, useCallback } from 'react';
const selector = createSelector(
[canvasSelector],
(canvas) => {
const selector = createMemoizedSelector(
[stateSelector],
({ canvas }) => {
const { isMoveStageKeyHeld, stageScale } = canvas;
return {
isMoveStageKeyHeld,

View File

@ -1,10 +1,8 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { RootState, stateSelector } from 'app/store/store';
import { CanvasImage, CanvasState, isCanvasBaseImage } from './canvasTypes';
import { CanvasImage, isCanvasBaseImage } from './canvasTypes';
export const canvasSelector = (state: RootState): CanvasState => state.canvas;
export const isStagingSelector = createSelector(
export const isStagingSelector = createMemoizedSelector(
[stateSelector],
({ canvas }) =>
canvas.batchIds.length > 0 ||

View File

@ -8,25 +8,24 @@ import {
Flex,
Text,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIButton from 'common/components/IAIButton';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import {
changeBoardReset,
isModalOpenChanged,
} from 'features/changeBoardModal/store/slice';
import { memo, useCallback, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
import {
useAddImagesToBoardMutation,
useRemoveImagesFromBoardMutation,
} from 'services/api/endpoints/images';
import {
changeBoardReset,
isModalOpenChanged,
} from 'features/changeBoardModal/store/slice';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
const selector = createMemoizedSelector(
[stateSelector],
({ changeBoardModal }) => {
const { isModalOpen, imagesToChange } = changeBoardModal;
@ -35,8 +34,7 @@ const selector = createSelector(
isModalOpen,
imagesToChange,
};
},
defaultSelectorOptions
}
);
const ChangeBoardModal = () => {

View File

@ -9,7 +9,6 @@ import {
} from 'features/controlAdapters/store/controlAdaptersSlice';
import ParamControlAdapterModel from './parameters/ParamControlAdapterModel';
import ParamControlAdapterWeight from './parameters/ParamControlAdapterWeight';
import { ChevronUpIcon } from '@chakra-ui/icons';
import IAIIconButton from 'common/components/IAIIconButton';
import IAISwitch from 'common/components/IAISwitch';

View File

@ -1,9 +1,8 @@
import { Box, Flex, Spinner } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { skipToken } from '@reduxjs/toolkit/dist/query';
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 { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDndImage from 'common/components/IAIDndImage';
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
@ -33,7 +32,7 @@ type Props = {
isSmall?: boolean;
};
const selector = createSelector(
const selector = createMemoizedSelector(
stateSelector,
({ controlAdapters, gallery, system }) => {
const { pendingControlImages } = controlAdapters;
@ -45,8 +44,7 @@ const selector = createSelector(
autoAddBoardId,
isConnected,
};
},
defaultSelectorOptions
}
);
const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {

View File

@ -1,11 +1,11 @@
import { ButtonGroup, Divider, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIButton from 'common/components/IAIButton';
import IAICollapse from 'common/components/IAICollapse';
import ControlAdapterConfig from 'features/controlAdapters/components/ControlAdapterConfig';
import { useAddControlAdapter } from 'features/controlAdapters/hooks/useAddControlAdapter';
import {
selectAllControlNets,
selectAllIPAdapters,
@ -19,9 +19,8 @@ import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { Fragment, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { FaPlus } from 'react-icons/fa';
import { useAddControlAdapter } from 'features/controlAdapters/hooks/useAddControlAdapter';
const selector = createSelector(
const selector = createMemoizedSelector(
[stateSelector],
({ controlAdapters }) => {
const activeLabel: string[] = [];
@ -68,8 +67,7 @@ const selector = createSelector(
activeLabel: activeLabel.join(', '),
isError, // TODO: Add some visual indicator that the control adapters are in an error state
};
},
defaultSelectorOptions
}
);
const ControlAdaptersCollapse = () => {

View File

@ -1,7 +1,6 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip';
import { useControlAdapterIsEnabled } from 'features/controlAdapters/hooks/useControlAdapterIsEnabled';
@ -18,14 +17,10 @@ type ParamControlAdapterModelProps = {
id: string;
};
const selector = createSelector(
stateSelector,
({ generation }) => {
const { model } = generation;
return { mainModel: model };
},
defaultSelectorOptions
);
const selector = createMemoizedSelector(stateSelector, ({ generation }) => {
const { model } = generation;
return { mainModel: model };
});
const ParamControlAdapterModel = ({ id }: ParamControlAdapterModelProps) => {
const isEnabled = useControlAdapterIsEnabled(id);

View File

@ -1,53 +1,47 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { createSelector } from '@reduxjs/toolkit';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIMantineSearchableSelect, {
IAISelectDataType,
} from 'common/components/IAIMantineSearchableSelect';
import { useControlAdapterIsEnabled } from 'features/controlAdapters/hooks/useControlAdapterIsEnabled';
import { useControlAdapterProcessorNode } from 'features/controlAdapters/hooks/useControlAdapterProcessorNode';
import { CONTROLNET_PROCESSORS } from 'features/controlAdapters/store/constants';
import { controlAdapterProcessortTypeChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
import { ControlAdapterProcessorType } from 'features/controlAdapters/store/types';
import { configSelector } from 'features/system/store/configSelectors';
import { map } from 'lodash-es';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { CONTROLNET_PROCESSORS } from 'features/controlAdapters/store/constants';
import { controlAdapterProcessortTypeChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
import { ControlAdapterProcessorType } from 'features/controlAdapters/store/types';
type Props = {
id: string;
};
const selector = createSelector(
configSelector,
(config) => {
const controlNetProcessors: IAISelectDataType[] = map(
CONTROLNET_PROCESSORS,
(p) => ({
value: p.type,
label: p.label,
})
const selector = createMemoizedSelector(configSelector, (config) => {
const controlNetProcessors: IAISelectDataType[] = map(
CONTROLNET_PROCESSORS,
(p) => ({
value: p.type,
label: p.label,
})
)
.sort((a, b) =>
// sort 'none' to the top
a.value === 'none'
? -1
: b.value === 'none'
? 1
: a.label.localeCompare(b.label)
)
.sort((a, b) =>
// sort 'none' to the top
a.value === 'none'
? -1
: b.value === 'none'
? 1
: a.label.localeCompare(b.label)
)
.filter(
(d) =>
!config.sd.disabledControlNetProcessors.includes(
d.value as ControlAdapterProcessorType
)
);
.filter(
(d) =>
!config.sd.disabledControlNetProcessors.includes(
d.value as ControlAdapterProcessorType
)
);
return controlNetProcessors;
},
defaultSelectorOptions
);
return controlNetProcessors;
});
const ParamControlAdapterProcessorSelect = ({ id }: Props) => {
const isEnabled = useControlAdapterIsEnabled(id);

View File

@ -1,17 +1,14 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useMemo } from 'react';
export const useControlAdapter = (id: string) => {
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ controlAdapters }) => selectControlAdapterById(controlAdapters, id),
defaultSelectorOptions
createMemoizedSelector(stateSelector, ({ controlAdapters }) =>
selectControlAdapterById(controlAdapters, id)
),
[id]
);

View File

@ -1,26 +1,21 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useMemo } from 'react';
export const useControlAdapterBeginEndStepPct = (id: string) => {
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ controlAdapters }) => {
const cn = selectControlAdapterById(controlAdapters, id);
return cn
? {
beginStepPct: cn.beginStepPct,
endStepPct: cn.endStepPct,
}
: undefined;
},
defaultSelectorOptions
),
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
const cn = selectControlAdapterById(controlAdapters, id);
return cn
? {
beginStepPct: cn.beginStepPct,
endStepPct: cn.endStepPct,
}
: undefined;
}),
[id]
);

View File

@ -1,18 +1,16 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useMemo } from 'react';
export const useControlAdapterControlImage = (id: string) => {
const selector = useMemo(
() =>
createSelector(
createMemoizedSelector(
stateSelector,
({ controlAdapters }) =>
selectControlAdapterById(controlAdapters, id)?.controlImage,
defaultSelectorOptions
selectControlAdapterById(controlAdapters, id)?.controlImage
),
[id]
);

View File

@ -1,25 +1,20 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNet } from 'features/controlAdapters/store/types';
import { useMemo } from 'react';
export const useControlAdapterControlMode = (id: string) => {
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
if (ca && isControlNet(ca)) {
return ca.controlMode;
}
return undefined;
},
defaultSelectorOptions
),
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
if (ca && isControlNet(ca)) {
return ca.controlMode;
}
return undefined;
}),
[id]
);

View File

@ -1,18 +1,16 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useMemo } from 'react';
export const useControlAdapterIsEnabled = (id: string) => {
const selector = useMemo(
() =>
createSelector(
createMemoizedSelector(
stateSelector,
({ controlAdapters }) =>
selectControlAdapterById(controlAdapters, id)?.isEnabled ?? false,
defaultSelectorOptions
selectControlAdapterById(controlAdapters, id)?.isEnabled ?? false
),
[id]
);

View File

@ -1,18 +1,16 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useMemo } from 'react';
export const useControlAdapterModel = (id: string) => {
const selector = useMemo(
() =>
createSelector(
createMemoizedSelector(
stateSelector,
({ controlAdapters }) =>
selectControlAdapterById(controlAdapters, id)?.model,
defaultSelectorOptions
selectControlAdapterById(controlAdapters, id)?.model
),
[id]
);

View File

@ -1,25 +1,20 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
import { useMemo } from 'react';
export const useControlAdapterProcessedControlImage = (id: string) => {
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
return ca && isControlNetOrT2IAdapter(ca)
? ca.processedControlImage
: undefined;
},
defaultSelectorOptions
),
return ca && isControlNetOrT2IAdapter(ca)
? ca.processedControlImage
: undefined;
}),
[id]
);

View File

@ -1,25 +1,20 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
import { useMemo } from 'react';
export const useControlAdapterProcessorNode = (id: string) => {
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
return ca && isControlNetOrT2IAdapter(ca)
? ca.processorNode
: undefined;
},
defaultSelectorOptions
),
return ca && isControlNetOrT2IAdapter(ca)
? ca.processorNode
: undefined;
}),
[id]
);

View File

@ -1,25 +1,20 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
import { useMemo } from 'react';
export const useControlAdapterProcessorType = (id: string) => {
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
return ca && isControlNetOrT2IAdapter(ca)
? ca.processorType
: undefined;
},
defaultSelectorOptions
),
return ca && isControlNetOrT2IAdapter(ca)
? ca.processorType
: undefined;
}),
[id]
);

View File

@ -1,25 +1,20 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
import { useMemo } from 'react';
export const useControlAdapterResizeMode = (id: string) => {
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
if (ca && isControlNetOrT2IAdapter(ca)) {
return ca.resizeMode;
}
return undefined;
},
defaultSelectorOptions
),
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
if (ca && isControlNetOrT2IAdapter(ca)) {
return ca.resizeMode;
}
return undefined;
}),
[id]
);

View File

@ -1,25 +1,20 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
import { useMemo } from 'react';
export const useControlAdapterShouldAutoConfig = (id: string) => {
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
if (ca && isControlNetOrT2IAdapter(ca)) {
return ca.shouldAutoConfig;
}
return undefined;
},
defaultSelectorOptions
),
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
const ca = selectControlAdapterById(controlAdapters, id);
if (ca && isControlNetOrT2IAdapter(ca)) {
return ca.shouldAutoConfig;
}
return undefined;
}),
[id]
);

View File

@ -1,18 +1,16 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useMemo } from 'react';
export const useControlAdapterType = (id: string) => {
const selector = useMemo(
() =>
createSelector(
createMemoizedSelector(
stateSelector,
({ controlAdapters }) =>
selectControlAdapterById(controlAdapters, id)?.type,
defaultSelectorOptions
selectControlAdapterById(controlAdapters, id)?.type
),
[id]
);

View File

@ -1,18 +1,16 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useMemo } from 'react';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
import { useMemo } from 'react';
export const useControlAdapterWeight = (id: string) => {
const selector = useMemo(
() =>
createSelector(
createMemoizedSelector(
stateSelector,
({ controlAdapters }) =>
selectControlAdapterById(controlAdapters, id)?.weight,
defaultSelectorOptions
selectControlAdapterById(controlAdapters, id)?.weight
),
[id]
);

View File

@ -35,7 +35,9 @@ import {
isT2IAdapter,
} from './types';
export const caAdapter = createEntityAdapter<ControlAdapterConfig>();
export const caAdapter = createEntityAdapter<ControlAdapterConfig, string>({
selectId: (ca) => ca.id,
});
export const {
selectById: selectControlAdapterById,
@ -259,7 +261,7 @@ export const controlAdaptersSlice = createSlice({
return;
}
const update: Update<ControlNetConfig | T2IAdapterConfig> = {
const update: Update<ControlNetConfig | T2IAdapterConfig, string> = {
id,
changes: { model },
};
@ -398,7 +400,7 @@ export const controlAdaptersSlice = createSlice({
return;
}
const update: Update<ControlNetConfig | T2IAdapterConfig> = {
const update: Update<ControlNetConfig | T2IAdapterConfig, string> = {
id,
changes: { shouldAutoConfig: !cn.shouldAutoConfig },
};

View File

@ -425,7 +425,7 @@ export type ControlAdapterConfig =
export type ControlAdapterType = ControlAdapterConfig['type'];
export type ControlAdaptersState = EntityState<ControlAdapterConfig> & {
export type ControlAdaptersState = EntityState<ControlAdapterConfig, string> & {
pendingControlImages: string[];
};

View File

@ -9,16 +9,11 @@ import {
Flex,
Text,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIButton from 'common/components/IAIButton';
import IAISwitch from 'common/components/IAISwitch';
import { setShouldConfirmOnDelete } from 'features/system/store/systemSlice';
import { some } from 'lodash-es';
import { ChangeEvent, memo, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
import {
getImageUsage,
@ -29,9 +24,13 @@ import {
isModalOpenChanged,
} from 'features/deleteImageModal/store/slice';
import { ImageUsage } from 'features/deleteImageModal/store/types';
import { setShouldConfirmOnDelete } from 'features/system/store/systemSlice';
import { some } from 'lodash-es';
import { ChangeEvent, memo, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import ImageUsageMessage from './ImageUsageMessage';
const selector = createSelector(
const selector = createMemoizedSelector(
[stateSelector, selectImageUsage],
(state, imagesUsage) => {
const { system, config, deleteImageModal } = state;
@ -58,8 +57,7 @@ const selector = createSelector(
isModalOpen,
imageUsageSummary,
};
},
defaultSelectorOptions
}
);
const DeleteImageModal = () => {

View File

@ -1,12 +1,11 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { RootState } from 'app/store/store';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { some } from 'lodash-es';
import { ImageUsage } from './types';
import { selectControlAdapterAll } from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
import { isImageFieldInputInstance } from 'features/nodes/types/field';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { some } from 'lodash-es';
import { ImageUsage } from './types';
export const getImageUsage = (state: RootState, image_name: string) => {
const { generation, canvas, nodes, controlAdapters } = state;
@ -41,7 +40,7 @@ export const getImageUsage = (state: RootState, image_name: string) => {
return imageUsage;
};
export const selectImageUsage = createSelector(
export const selectImageUsage = createMemoizedSelector(
[(state: RootState) => state],
(state) => {
const { imagesToDelete } = state.deleteImageModal;
@ -55,6 +54,5 @@ export const selectImageUsage = createSelector(
);
return imagesUsage;
},
defaultSelectorOptions
}
);

View File

@ -1,12 +1,12 @@
import type { Modifier } from '@dnd-kit/core';
import { getEventCoordinates } from '@dnd-kit/utilities';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useCallback } from 'react';
const selectZoom = createSelector(
const selectZoom = createMemoizedSelector(
[stateSelector, activeTabNameSelector],
({ nodes }, activeTabName) =>
activeTabName === 'nodes' ? nodes.viewport.zoom : 1

View File

@ -1,11 +1,11 @@
import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import IAICollapse from 'common/components/IAICollapse';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import ParamDynamicPromptsMaxPrompts from './ParamDynamicPromptsMaxPrompts';
import ParamDynamicPromptsPreview from './ParamDynamicPromptsPreview';
import ParamDynamicPromptsSeedBehaviour from './ParamDynamicPromptsSeedBehaviour';
@ -14,7 +14,7 @@ const ParamDynamicPromptsCollapse = () => {
const { t } = useTranslation();
const selectActiveLabel = useMemo(
() =>
createSelector(stateSelector, ({ dynamicPrompts }) => {
createMemoizedSelector(stateSelector, ({ dynamicPrompts }) => {
const count = dynamicPrompts.prompts.length;
if (count > 1) {
return t('dynamicPrompts.promptsWithCount_other', {

View File

@ -1,21 +1,16 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISwitch from 'common/components/IAISwitch';
import { combinatorialToggled } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { combinatorialToggled } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
const selector = createSelector(
stateSelector,
(state) => {
const { combinatorial } = state.dynamicPrompts;
const selector = createMemoizedSelector(stateSelector, (state) => {
const { combinatorial } = state.dynamicPrompts;
return { combinatorial };
},
defaultSelectorOptions
);
return { combinatorial };
});
const ParamDynamicPromptsCombinatorial = () => {
const { combinatorial } = useAppSelector(selector);

View File

@ -1,33 +1,28 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
import IAISlider from 'common/components/IAISlider';
import { memo, useCallback } from 'react';
import {
maxPromptsChanged,
maxPromptsReset,
} from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
const selector = createSelector(
stateSelector,
(state) => {
const { maxPrompts, combinatorial } = state.dynamicPrompts;
const { min, sliderMax, inputMax } =
state.config.sd.dynamicPrompts.maxPrompts;
const selector = createMemoizedSelector(stateSelector, (state) => {
const { maxPrompts, combinatorial } = state.dynamicPrompts;
const { min, sliderMax, inputMax } =
state.config.sd.dynamicPrompts.maxPrompts;
return {
maxPrompts,
min,
sliderMax,
inputMax,
isDisabled: !combinatorial,
};
},
defaultSelectorOptions
);
return {
maxPrompts,
min,
sliderMax,
inputMax,
isDisabled: !combinatorial,
};
});
const ParamDynamicPromptsMaxPrompts = () => {
const { maxPrompts, min, sliderMax, inputMax, isDisabled } =

View File

@ -8,31 +8,26 @@ import {
Spinner,
Text,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent';
import { memo } from 'react';
import { FaCircleExclamation } from 'react-icons/fa6';
import { useTranslation } from 'react-i18next';
import { FaCircleExclamation } from 'react-icons/fa6';
const selector = createSelector(
stateSelector,
(state) => {
const { isLoading, isError, prompts, parsingError } = state.dynamicPrompts;
const selector = createMemoizedSelector(stateSelector, (state) => {
const { isLoading, isError, prompts, parsingError } = state.dynamicPrompts;
return {
prompts,
parsingError,
isError,
isLoading,
};
},
defaultSelectorOptions
);
return {
prompts,
parsingError,
isError,
isLoading,
};
});
const listItemStyles: ChakraProps['sx'] = {
'&::marker': { color: 'base.500', _dark: { color: 'base.500' } },

View File

@ -1,8 +1,7 @@
import { SelectItem } from '@mantine/core';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip';
import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice';
@ -10,18 +9,14 @@ import { memo, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
const selector = createSelector(
[stateSelector],
({ gallery }) => {
const { autoAddBoardId, autoAssignBoardOnClick } = gallery;
const selector = createMemoizedSelector([stateSelector], ({ gallery }) => {
const { autoAddBoardId, autoAssignBoardOnClick } = gallery;
return {
autoAddBoardId,
autoAssignBoardOnClick,
};
},
defaultSelectorOptions
);
return {
autoAddBoardId,
autoAssignBoardOnClick,
};
});
const BoardAutoAddSelect = () => {
const dispatch = useAppDispatch();

View File

@ -1,25 +1,24 @@
import { MenuGroup, MenuItem, MenuList } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import {
IAIContextMenu,
IAIContextMenuProps,
} from 'common/components/IAIContextMenu';
import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice';
import { BoardId } from 'features/gallery/store/types';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { addToast } from 'features/system/store/systemSlice';
import { MouseEvent, memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { FaDownload, FaPlus } from 'react-icons/fa';
import { useBulkDownloadImagesMutation } from 'services/api/endpoints/images';
import { useBoardName } from 'services/api/hooks/useBoardName';
import { BoardDTO } from 'services/api/types';
import { menuListMotionProps } from 'theme/components/menu';
import GalleryBoardContextMenuItems from './GalleryBoardContextMenuItems';
import NoBoardContextMenuItems from './NoBoardContextMenuItems';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { useBulkDownloadImagesMutation } from 'services/api/endpoints/images';
import { addToast } from 'features/system/store/systemSlice';
type Props = {
board?: BoardDTO;
@ -39,15 +38,11 @@ const BoardContextMenu = ({
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ gallery }) => {
const isAutoAdd = gallery.autoAddBoardId === board_id;
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick;
return { isAutoAdd, autoAssignBoardOnClick };
},
defaultSelectorOptions
),
createMemoizedSelector(stateSelector, ({ gallery }) => {
const isAutoAdd = gallery.autoAddBoardId === board_id;
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick;
return { isAutoAdd, autoAssignBoardOnClick };
}),
[board_id]
);

View File

@ -1,26 +1,21 @@
import { Collapse, Flex, Grid, GridItem } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import { memo, useState } from 'react';
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
import { BoardDTO } from 'services/api/types';
import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
import AddBoardButton from './AddBoardButton';
import BoardsSearch from './BoardsSearch';
import GalleryBoard from './GalleryBoard';
import NoBoardBoard from './NoBoardBoard';
const selector = createSelector(
[stateSelector],
({ gallery }) => {
const { selectedBoardId, boardSearchText } = gallery;
return { selectedBoardId, boardSearchText };
},
defaultSelectorOptions
);
const selector = createMemoizedSelector([stateSelector], ({ gallery }) => {
const { selectedBoardId, boardSearchText } = gallery;
return { selectedBoardId, boardSearchText };
});
type Props = {
isOpen: boolean;

View File

@ -5,10 +5,9 @@ import {
InputGroup,
InputRightElement,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice';
import {
ChangeEvent,
@ -20,14 +19,10 @@ import {
} from 'react';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
[stateSelector],
({ gallery }) => {
const { boardSearchText } = gallery;
return { boardSearchText };
},
defaultSelectorOptions
);
const selector = createMemoizedSelector([stateSelector], ({ gallery }) => {
const { boardSearchText } = gallery;
return { boardSearchText };
});
const BoardsSearch = () => {
const dispatch = useAppDispatch();

View File

@ -9,19 +9,21 @@ import {
Text,
Tooltip,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { skipToken } from '@reduxjs/toolkit/dist/query';
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 { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDroppable from 'common/components/IAIDroppable';
import SelectionOverlay from 'common/components/SelectionOverlay';
import { AddToBoardDropData } from 'features/dnd/types';
import AutoAddIcon from 'features/gallery/components/Boards/AutoAddIcon';
import BoardContextMenu from 'features/gallery/components/Boards/BoardContextMenu';
import {
autoAddBoardIdChanged,
boardIdSelected,
} from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaUser } from 'react-icons/fa';
import {
useGetBoardAssetsTotalQuery,
@ -30,9 +32,6 @@ import {
} from 'services/api/endpoints/boards';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { BoardDTO } from 'services/api/types';
import AutoAddIcon from 'features/gallery/components/Boards/AutoAddIcon';
import BoardContextMenu from 'features/gallery/components/Boards/BoardContextMenu';
import { useTranslation } from 'react-i18next';
interface GalleryBoardProps {
board: BoardDTO;
@ -48,20 +47,15 @@ const GalleryBoard = ({
const dispatch = useAppDispatch();
const selector = useMemo(
() =>
createSelector(
stateSelector,
({ gallery }) => {
const isSelectedForAutoAdd =
board.board_id === gallery.autoAddBoardId;
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick;
createMemoizedSelector(stateSelector, ({ gallery }) => {
const isSelectedForAutoAdd = board.board_id === gallery.autoAddBoardId;
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick;
return {
isSelectedForAutoAdd,
autoAssignBoardOnClick,
};
},
defaultSelectorOptions
),
return {
isSelectedForAutoAdd,
autoAssignBoardOnClick,
};
}),
[board.board_id]
);

View File

@ -1,38 +1,33 @@
import { Box, Flex, Image, Text, Tooltip } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import InvokeAILogoImage from 'assets/images/logo.png';
import IAIDroppable from 'common/components/IAIDroppable';
import SelectionOverlay from 'common/components/SelectionOverlay';
import { RemoveFromBoardDropData } from 'features/dnd/types';
import AutoAddIcon from 'features/gallery/components/Boards/AutoAddIcon';
import BoardContextMenu from 'features/gallery/components/Boards/BoardContextMenu';
import {
autoAddBoardIdChanged,
boardIdSelected,
} from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo, useState } from 'react';
import { useBoardName } from 'services/api/hooks/useBoardName';
import AutoAddIcon from 'features/gallery/components/Boards/AutoAddIcon';
import BoardContextMenu from 'features/gallery/components/Boards/BoardContextMenu';
import { useTranslation } from 'react-i18next';
import {
useGetBoardAssetsTotalQuery,
useGetBoardImagesTotalQuery,
} from 'services/api/endpoints/boards';
import { useTranslation } from 'react-i18next';
import { useBoardName } from 'services/api/hooks/useBoardName';
interface Props {
isSelected: boolean;
}
const selector = createSelector(
stateSelector,
({ gallery }) => {
const { autoAddBoardId, autoAssignBoardOnClick } = gallery;
return { autoAddBoardId, autoAssignBoardOnClick };
},
defaultSelectorOptions
);
const selector = createMemoizedSelector(stateSelector, ({ gallery }) => {
const { autoAddBoardId, autoAssignBoardOnClick } = gallery;
return { autoAddBoardId, autoAssignBoardOnClick };
});
const NoBoardBoard = memo(({ isSelected }: Props) => {
const dispatch = useAppDispatch();

View File

@ -9,8 +9,8 @@ import {
Skeleton,
Text,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { skipToken } from '@reduxjs/toolkit/dist/query';
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 IAIButton from 'common/components/IAIButton';
@ -43,7 +43,7 @@ const DeleteBoardModal = (props: Props) => {
const selectImageUsageSummary = useMemo(
() =>
createSelector([stateSelector], (state) => {
createMemoizedSelector([stateSelector], (state) => {
const allImageUsage = (boardImageNames ?? []).map((imageName) =>
getImageUsage(state, imageName)
);

View File

@ -1,6 +1,3 @@
import { createSelector } from '@reduxjs/toolkit';
import { isEqual } from 'lodash-es';
import {
ButtonGroup,
Flex,
@ -8,8 +5,9 @@ import {
MenuButton,
MenuList,
} from '@chakra-ui/react';
import { skipToken } from '@reduxjs/toolkit/dist/query';
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';
@ -45,7 +43,7 @@ import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
import { menuListMotionProps } from 'theme/components/menu';
const currentImageButtonsSelector = createSelector(
const currentImageButtonsSelector = createMemoizedSelector(
[stateSelector, activeTabNameSelector],
({ gallery, system, ui, config }, activeTabName) => {
const { isConnected, shouldConfirmOnDelete, denoiseProgress } = system;
@ -72,11 +70,6 @@ const currentImageButtonsSelector = createSelector(
lastSelectedImage,
shouldFetchMetadataFromApi,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,5 +1,4 @@
import { Flex } from '@chakra-ui/react';
import CurrentImageButtons from './CurrentImageButtons';
import CurrentImagePreview from './CurrentImagePreview';
import { memo } from 'react';

View File

@ -1,6 +1,6 @@
import { Box, Flex, Image } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { skipToken } from '@reduxjs/toolkit/dist/query';
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';
@ -9,19 +9,18 @@ import {
TypesafeDraggableData,
TypesafeDroppableData,
} from 'features/dnd/types';
import ImageMetadataViewer from 'features/gallery/components/ImageMetadataViewer/ImageMetadataViewer';
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
import { useNextPrevImage } from 'features/gallery/hooks/useNextPrevImage';
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
import { AnimatePresence, motion } from 'framer-motion';
import { isEqual } from 'lodash-es';
import { memo, useCallback, useMemo, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { FaImage } from 'react-icons/fa';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import ImageMetadataViewer from 'features/gallery/components/ImageMetadataViewer/ImageMetadataViewer';
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
import { useTranslation } from 'react-i18next';
export const imagesSelector = createSelector(
export const imagesSelector = createMemoizedSelector(
[stateSelector, selectLastSelectedImage],
({ ui, system }, lastSelectedImage) => {
const {
@ -38,11 +37,6 @@ export const imagesSelector = createSelector(
shouldShowProgressInViewer,
shouldAntialiasProgressImage,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,21 +1,16 @@
import { ChevronUpIcon } from '@chakra-ui/icons';
import { Button, Flex, Text } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { memo, useMemo } from 'react';
import { useBoardName } from 'services/api/hooks/useBoardName';
const selector = createSelector(
[stateSelector],
(state) => {
const { selectedBoardId } = state.gallery;
const selector = createMemoizedSelector([stateSelector], (state) => {
const { selectedBoardId } = state.gallery;
return { selectedBoardId };
},
defaultSelectorOptions
);
return { selectedBoardId };
});
type Props = {
isOpen: boolean;

View File

@ -1,10 +1,10 @@
import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIIconButton from 'common/components/IAIIconButton';
import IAIPopover from 'common/components/IAIPopover';
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
import IAISlider from 'common/components/IAISlider';
import IAISwitch from 'common/components/IAISwitch';
import {
@ -16,25 +16,17 @@ import { ChangeEvent, memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { FaWrench } from 'react-icons/fa';
import BoardAutoAddSelect from './Boards/BoardAutoAddSelect';
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
const selector = createSelector(
[stateSelector],
(state) => {
const {
galleryImageMinimumWidth,
shouldAutoSwitch,
autoAssignBoardOnClick,
} = state.gallery;
const selector = createMemoizedSelector([stateSelector], (state) => {
const { galleryImageMinimumWidth, shouldAutoSwitch, autoAssignBoardOnClick } =
state.gallery;
return {
galleryImageMinimumWidth,
shouldAutoSwitch,
autoAssignBoardOnClick,
};
},
defaultSelectorOptions
);
return {
galleryImageMinimumWidth,
shouldAutoSwitch,
autoAssignBoardOnClick,
};
});
const GallerySettingsPopover = () => {
const dispatch = useAppDispatch();

View File

@ -1,4 +1,7 @@
import { MenuList } from '@chakra-ui/react';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import {
IAIContextMenu,
IAIContextMenuProps,
@ -6,27 +9,19 @@ import {
import { MouseEvent, memo, useCallback } from 'react';
import { ImageDTO } from 'services/api/types';
import { menuListMotionProps } from 'theme/components/menu';
import SingleSelectionMenuItems from './SingleSelectionMenuItems';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { useAppSelector } from 'app/store/storeHooks';
import MultipleSelectionMenuItems from './MultipleSelectionMenuItems';
import SingleSelectionMenuItems from './SingleSelectionMenuItems';
type Props = {
imageDTO: ImageDTO | undefined;
children: IAIContextMenuProps<HTMLDivElement>['children'];
};
const selector = createSelector(
[stateSelector],
({ gallery }) => {
const selectionCount = gallery.selection.length;
const selector = createMemoizedSelector([stateSelector], ({ gallery }) => {
const selectionCount = gallery.selection.length;
return { selectionCount };
},
defaultSelectorOptions
);
return { selectionCount };
});
const ImageContextMenu = ({ imageDTO, children }: Props) => {
const { selectionCount } = useAppSelector(selector);

View File

@ -8,31 +8,26 @@ import {
VStack,
useDisclosure,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIButton from 'common/components/IAIButton';
import { memo, useCallback, useRef } from 'react';
import { FaImages, FaServer } from 'react-icons/fa';
import { galleryViewChanged } from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { FaImages, FaServer } from 'react-icons/fa';
import BoardsList from './Boards/BoardsList/BoardsList';
import GalleryBoardName from './GalleryBoardName';
import GallerySettingsPopover from './GallerySettingsPopover';
import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
[stateSelector],
(state) => {
const { galleryView } = state.gallery;
const selector = createMemoizedSelector([stateSelector], (state) => {
const { galleryView } = state.gallery;
return {
galleryView,
};
},
defaultSelectorOptions
);
return {
galleryView,
};
});
const ImageGalleryContent = () => {
const { t } = useTranslation();

View File

@ -1,16 +1,15 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { selectListImagesBaseQueryArgs } from 'features/gallery/store/gallerySelectors';
import { selectionChanged } from 'features/gallery/store/gallerySlice';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { MouseEvent, useCallback, useMemo } from 'react';
import { useListImagesQuery } from 'services/api/endpoints/images';
import { ImageDTO } from 'services/api/types';
import { imagesSelectors } from 'services/api/util';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { selectionChanged } from 'features/gallery/store/gallerySlice';
const selector = createSelector(
const selector = createMemoizedSelector(
[stateSelector, selectListImagesBaseQueryArgs],
({ gallery }, queryArgs) => {
const selection = gallery.selection;
@ -19,8 +18,7 @@ const selector = createSelector(
queryArgs,
selection,
};
},
defaultSelectorOptions
}
);
export const useMultiselect = (imageDTO?: ImageDTO) => {

View File

@ -1,20 +1,20 @@
import { createSelector } from '@reduxjs/toolkit';
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 { imageSelected } from 'features/gallery/store/gallerySlice';
import { clamp, isEqual } from 'lodash-es';
import { IMAGE_LIMIT } from 'features/gallery/store/types';
import { clamp } from 'lodash-es';
import { useCallback } from 'react';
import { boardsApi } from 'services/api/endpoints/boards';
import {
imagesApi,
useLazyListImagesQuery,
} from 'services/api/endpoints/images';
import { selectListImagesBaseQueryArgs } from 'features/gallery/store/gallerySelectors';
import { IMAGE_LIMIT } from 'features/gallery/store/types';
import { ListImagesArgs } from 'services/api/types';
import { imagesAdapter } from 'services/api/util';
export const nextPrevImageButtonsSelector = createSelector(
export const nextPrevImageButtonsSelector = createMemoizedSelector(
[stateSelector, selectListImagesBaseQueryArgs],
(state, baseQueryArgs) => {
const { data, status } =
@ -80,11 +80,6 @@ export const nextPrevImageButtonsSelector = createSelector(
prevImage,
queryArgs,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);

View File

@ -1,6 +1,5 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { RootState } from 'app/store/store';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { ListImagesArgs } from 'services/api/types';
import {
ASSETS_CATEGORIES,
@ -10,13 +9,12 @@ import {
export const gallerySelector = (state: RootState) => state.gallery;
export const selectLastSelectedImage = createSelector(
export const selectLastSelectedImage = createMemoizedSelector(
(state: RootState) => state,
(state) => state.gallery.selection[state.gallery.selection.length - 1],
defaultSelectorOptions
(state) => state.gallery.selection[state.gallery.selection.length - 1]
);
export const selectListImagesBaseQueryArgs = createSelector(
export const selectListImagesBaseQueryArgs = createMemoizedSelector(
[(state: RootState) => state],
(state) => {
const { selectedBoardId, galleryView } = state.gallery;
@ -32,6 +30,5 @@ export const selectListImagesBaseQueryArgs = createSelector(
};
return listImagesBaseQueryArgs;
},
defaultSelectorOptions
}
);

View File

@ -1,26 +1,21 @@
import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { size } from 'lodash-es';
import { memo } from 'react';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { useTranslation } from 'react-i18next';
import ParamLoraList from './ParamLoraList';
import ParamLoRASelect from './ParamLoraSelect';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
stateSelector,
(state) => {
const loraCount = size(state.lora.loras);
return {
activeLabel: loraCount > 0 ? `${loraCount} Active` : undefined,
};
},
defaultSelectorOptions
);
const selector = createMemoizedSelector(stateSelector, (state) => {
const loraCount = size(state.lora.loras);
return {
activeLabel: loraCount > 0 ? `${loraCount} Active` : undefined,
};
});
const ParamLoraCollapse = () => {
const { t } = useTranslation();

View File

@ -1,19 +1,14 @@
import { Divider, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { map } from 'lodash-es';
import ParamLora from './ParamLora';
import { memo } from 'react';
import ParamLora from './ParamLora';
const selector = createSelector(
stateSelector,
({ lora }) => {
return { lorasArray: map(lora.loras) };
},
defaultSelectorOptions
);
const selector = createMemoizedSelector(stateSelector, ({ lora }) => {
return { lorasArray: map(lora.loras) };
});
const ParamLoraList = () => {
const { lorasArray } = useAppSelector(selector);

View File

@ -1,9 +1,8 @@
import { Flex, Text } from '@chakra-ui/react';
import { SelectItem } from '@mantine/core';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { RootState, stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip';
import { loraAdded } from 'features/lora/store/loraSlice';
@ -13,13 +12,9 @@ import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetLoRAModelsQuery } from 'services/api/endpoints/models';
const selector = createSelector(
stateSelector,
({ lora }) => ({
loras: lora.loras,
}),
defaultSelectorOptions
);
const selector = createMemoizedSelector(stateSelector, ({ lora }) => ({
loras: lora.loras,
}));
const ParamLoRASelect = () => {
const dispatch = useAppDispatch();

View File

@ -1,7 +1,6 @@
import { Flex } from '@chakra-ui/react';
import { useAppDispatch } from 'app/store/storeHooks';
import { useTranslation } from 'react-i18next';
import { SelectItem } from '@mantine/core';
import { useForm } from '@mantine/form';
import IAIButton from 'common/components/IAIButton';

View File

@ -1,5 +1,4 @@
import { Flex, Text } from '@chakra-ui/react';
import { useState } from 'react';
import {
MainModelConfigEntity,

View File

@ -16,7 +16,6 @@ import IAIInput from 'common/components/IAIInput';
import { addToast } from 'features/system/store/systemSlice';
import { ChangeEvent, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useConvertMainModelsMutation } from 'services/api/endpoints/models';
import { CheckpointModelConfig } from 'services/api/types';

View File

@ -240,7 +240,7 @@ const modelsFilter = <
| LoRAModelConfigEntity
| OnnxModelConfigEntity,
>(
data: EntityState<T> | undefined,
data: EntityState<T, string> | undefined,
model_type: ModelType,
model_format: ModelFormat | undefined,
nameFilter: string

View File

@ -5,11 +5,10 @@ import {
PopoverBody,
PopoverContent,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
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 { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import { useBuildNode } from 'features/nodes/hooks/useBuildNode';
import {
@ -62,58 +61,54 @@ const AddNodePopover = () => {
(state) => state.nodes.connectionStartParams?.handleType
);
const selector = createSelector(
[stateSelector],
({ nodes }) => {
// If we have a connection in progress, we need to filter the node choices
const filteredNodeTemplates = fieldFilter
? filter(nodes.nodeTemplates, (template) => {
const handles =
handleFilter == 'source' ? template.inputs : template.outputs;
const selector = createMemoizedSelector([stateSelector], ({ nodes }) => {
// If we have a connection in progress, we need to filter the node choices
const filteredNodeTemplates = fieldFilter
? filter(nodes.nodeTemplates, (template) => {
const handles =
handleFilter == 'source' ? template.inputs : template.outputs;
return some(handles, (handle) => {
const sourceType =
handleFilter == 'source' ? fieldFilter : handle.type;
const targetType =
handleFilter == 'target' ? fieldFilter : handle.type;
return some(handles, (handle) => {
const sourceType =
handleFilter == 'source' ? fieldFilter : handle.type;
const targetType =
handleFilter == 'target' ? fieldFilter : handle.type;
return validateSourceAndTargetTypes(sourceType, targetType);
});
})
: map(nodes.nodeTemplates);
return validateSourceAndTargetTypes(sourceType, targetType);
});
})
: map(nodes.nodeTemplates);
const data: NodeTemplate[] = map(filteredNodeTemplates, (template) => {
return {
label: template.title,
value: template.type,
description: template.description,
tags: template.tags,
};
const data: NodeTemplate[] = map(filteredNodeTemplates, (template) => {
return {
label: template.title,
value: template.type,
description: template.description,
tags: template.tags,
};
});
//We only want these nodes if we're not filtered
if (fieldFilter === null) {
data.push({
label: t('nodes.currentImage'),
value: 'current_image',
description: t('nodes.currentImageDescription'),
tags: ['progress'],
});
//We only want these nodes if we're not filtered
if (fieldFilter === null) {
data.push({
label: t('nodes.currentImage'),
value: 'current_image',
description: t('nodes.currentImageDescription'),
tags: ['progress'],
});
data.push({
label: t('nodes.notes'),
value: 'notes',
description: t('nodes.notesDescription'),
tags: ['notes'],
});
}
data.push({
label: t('nodes.notes'),
value: 'notes',
description: t('nodes.notesDescription'),
tags: ['notes'],
});
}
data.sort((a, b) => a.label.localeCompare(b.label));
data.sort((a, b) => a.label.localeCompare(b.label));
return { data };
},
defaultSelectorOptions
);
return { data };
});
const { data } = useAppSelector(selector);
const isOpen = useAppSelector((state) => state.nodes.isAddNodePopoverOpen);

View File

@ -1,8 +1,7 @@
import { useToken } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { useIsValidConnection } from 'features/nodes/hooks/useIsValidConnection';
import {
connectionEnded,
@ -67,17 +66,13 @@ const nodeTypes = {
// TODO: can we support reactflow? if not, we could style the attribution so it matches the app
const proOptions: ProOptions = { hideAttribution: true };
const selector = createSelector(
stateSelector,
({ nodes }) => {
const { shouldSnapToGrid, selectionMode } = nodes;
return {
shouldSnapToGrid,
selectionMode,
};
},
defaultSelectorOptions
);
const selector = createMemoizedSelector(stateSelector, ({ nodes }) => {
const { shouldSnapToGrid, selectionMode } = nodes;
return {
shouldSnapToGrid,
selectionMode,
};
});
export const Flow = () => {
const dispatch = useAppDispatch();

View File

@ -1,12 +1,12 @@
import { createSelector } from '@reduxjs/toolkit';
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 { memo } from 'react';
import { ConnectionLineComponentProps, getBezierPath } from 'reactflow';
import { getFieldColor } from 'features/nodes/components/flow/edges/util/getEdgeColor';
const selector = createSelector(stateSelector, ({ nodes }) => {
const selector = createMemoizedSelector(stateSelector, ({ nodes }) => {
const { shouldAnimateEdges, connectionStartFieldType, shouldColorEdges } =
nodes;

View File

@ -1,6 +1,5 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { getFieldColor } from './getEdgeColor';
@ -12,31 +11,26 @@ export const makeEdgeSelector = (
targetHandleId: string | null | undefined,
selected?: boolean
) =>
createSelector(
stateSelector,
({ nodes }) => {
const sourceNode = nodes.nodes.find((node) => node.id === source);
const targetNode = nodes.nodes.find((node) => node.id === target);
createMemoizedSelector(stateSelector, ({ nodes }) => {
const sourceNode = nodes.nodes.find((node) => node.id === source);
const targetNode = nodes.nodes.find((node) => node.id === target);
const isInvocationToInvocationEdge =
isInvocationNode(sourceNode) && isInvocationNode(targetNode);
const isInvocationToInvocationEdge =
isInvocationNode(sourceNode) && isInvocationNode(targetNode);
const isSelected =
sourceNode?.selected || targetNode?.selected || selected;
const sourceType = isInvocationToInvocationEdge
? sourceNode?.data?.outputs[sourceHandleId || '']?.type
: undefined;
const isSelected = sourceNode?.selected || targetNode?.selected || selected;
const sourceType = isInvocationToInvocationEdge
? sourceNode?.data?.outputs[sourceHandleId || '']?.type
: undefined;
const stroke =
sourceType && nodes.shouldColorEdges
? getFieldColor(sourceType)
: colorTokenToCssVar('base.500');
const stroke =
sourceType && nodes.shouldColorEdges
? getFieldColor(sourceType)
: colorTokenToCssVar('base.500');
return {
isSelected,
shouldAnimate: nodes.shouldAnimateEdges && isSelected,
stroke,
};
},
defaultSelectorOptions
);
return {
isSelected,
shouldAnimate: nodes.shouldAnimateEdges && isSelected,
stroke,
};
});

View File

@ -1,25 +1,28 @@
import { useState, PropsWithChildren, memo, useCallback } from 'react';
import { useSelector } from 'react-redux';
import { createSelector } from '@reduxjs/toolkit';
import { Flex, Image, Text } from '@chakra-ui/react';
import { motion } from 'framer-motion';
import { NodeProps } from 'reactflow';
import NodeWrapper from 'features/nodes/components/flow/nodes/common/NodeWrapper';
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import IAIDndImage from 'common/components/IAIDndImage';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
import NodeWrapper from 'features/nodes/components/flow/nodes/common/NodeWrapper';
import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants';
import { stateSelector } from 'app/store/store';
import { motion } from 'framer-motion';
import { PropsWithChildren, memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { NodeProps } from 'reactflow';
const selector = createSelector(stateSelector, ({ system, gallery }) => {
const imageDTO = gallery.selection[gallery.selection.length - 1];
const selector = createMemoizedSelector(
stateSelector,
({ system, gallery }) => {
const imageDTO = gallery.selection[gallery.selection.length - 1];
return {
imageDTO,
progressImage: system.denoiseProgress?.progress_image,
};
});
return {
imageDTO,
progressImage: system.denoiseProgress?.progress_image,
};
}
);
const CurrentImageNode = (props: NodeProps) => {
const { progressImage, imageDTO } = useSelector(selector);

Some files were not shown because too many files have changed in this diff Show More