fix(ui): update most other selectors

Just a few stragglers left. Good enough for now.
This commit is contained in:
psychedelicious 2024-01-05 23:35:24 +11:00
parent b71b14d582
commit 3c4150d153
68 changed files with 492 additions and 856 deletions

View File

@ -1,7 +1,6 @@
import { useAppToaster } from 'app/components/Toaster'; import { useAppToaster } from 'app/components/Toaster';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import type { Accept, FileRejection } from 'react-dropzone'; import type { Accept, FileRejection } from 'react-dropzone';
@ -15,9 +14,9 @@ const accept: Accept = {
'image/jpeg': ['.jpg', '.jpeg', '.png'], 'image/jpeg': ['.jpg', '.jpeg', '.png'],
}; };
const selector = createMemoizedSelector( const selectPostUploadAction = createMemoizedSelector(
[selectGallerySlice, activeTabNameSelector], activeTabNameSelector,
(gallery, activeTabName) => { (activeTabName) => {
let postUploadAction: PostUploadAction = { type: 'TOAST' }; let postUploadAction: PostUploadAction = { type: 'TOAST' };
if (activeTabName === 'unifiedCanvas') { if (activeTabName === 'unifiedCanvas') {
@ -28,19 +27,15 @@ const selector = createMemoizedSelector(
postUploadAction = { type: 'SET_INITIAL_IMAGE' }; postUploadAction = { type: 'SET_INITIAL_IMAGE' };
} }
const { autoAddBoardId } = gallery; return postUploadAction;
return {
autoAddBoardId,
postUploadAction,
};
} }
); );
export const useFullscreenDropzone = () => { export const useFullscreenDropzone = () => {
const { autoAddBoardId, postUploadAction } = useAppSelector(selector);
const toaster = useAppToaster();
const { t } = useTranslation(); const { t } = useTranslation();
const toaster = useAppToaster();
const postUploadAction = useAppSelector(selectPostUploadAction);
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
const [isHandlingUpload, setIsHandlingUpload] = useState<boolean>(false); const [isHandlingUpload, setIsHandlingUpload] = useState<boolean>(false);
const [uploadImage] = useUploadImageMutation(); const [uploadImage] = useUploadImageMutation();

View File

@ -1,13 +1,11 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { import {
setIsDrawing, setIsDrawing,
setIsMovingStage, setIsMovingStage,
} from 'features/canvas/store/canvasNanostore'; } from 'features/canvas/store/canvasNanostore';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { addLine, selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import { addLine } from 'features/canvas/store/canvasSlice';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition'; import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import type Konva from 'konva'; import type Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node'; import type { KonvaEventObject } from 'konva/lib/Node';
import type { MutableRefObject } from 'react'; import type { MutableRefObject } from 'react';
@ -15,20 +13,10 @@ import { useCallback } from 'react';
import useColorPicker from './useColorUnderCursor'; import useColorPicker from './useColorUnderCursor';
const selector = createMemoizedSelector(
[activeTabNameSelector, selectCanvasSlice, isStagingSelector],
(activeTabName, canvas, isStaging) => {
return {
tool: canvas.tool,
activeTabName,
isStaging,
};
}
);
const useCanvasMouseDown = (stageRef: MutableRefObject<Konva.Stage | null>) => { const useCanvasMouseDown = (stageRef: MutableRefObject<Konva.Stage | null>) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { tool, isStaging } = useAppSelector(selector); const tool = useAppSelector((s) => s.canvas.tool);
const isStaging = useAppSelector(isStagingSelector);
const { commitColorUnderCursor } = useColorPicker(); const { commitColorUnderCursor } = useColorPicker();
return useCallback( return useCallback(

View File

@ -5,9 +5,7 @@ import {
setCursorPosition, setCursorPosition,
} from 'features/canvas/store/canvasNanostore'; } from 'features/canvas/store/canvasNanostore';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { import { addPointToCurrentLine } from 'features/canvas/store/canvasSlice';
addPointToCurrentLine,
} from 'features/canvas/store/canvasSlice';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition'; import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
import type Konva from 'konva'; import type Konva from 'konva';
import type { Vector2d } from 'konva/lib/types'; import type { Vector2d } from 'konva/lib/types';

View File

@ -1,18 +1,17 @@
import { createSelector } from '@reduxjs/toolkit';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import type { RootState } from 'app/store/store';
import { selectCanvasSlice } from './canvasSlice'; import { selectCanvasSlice } from './canvasSlice';
import type { CanvasImage } from './canvasTypes';
import { isCanvasBaseImage } from './canvasTypes'; import { isCanvasBaseImage } from './canvasTypes';
export const isStagingSelector = createMemoizedSelector( export const isStagingSelector = createSelector(
selectCanvasSlice, selectCanvasSlice,
(canvas) => (canvas) =>
canvas.batchIds.length > 0 || canvas.batchIds.length > 0 ||
canvas.layerState.stagingArea.images.length > 0 canvas.layerState.stagingArea.images.length > 0
); );
export const initialCanvasImageSelector = ( export const initialCanvasImageSelector = createMemoizedSelector(
state: RootState selectCanvasSlice,
): CanvasImage | undefined => (canvas) => canvas.layerState.objects.find(isCanvasBaseImage)
state.canvas.layerState.objects.find(isCanvasBaseImage); );

View File

@ -22,23 +22,17 @@ import {
useRemoveImagesFromBoardMutation, useRemoveImagesFromBoardMutation,
} from 'services/api/endpoints/images'; } from 'services/api/endpoints/images';
const selector = createMemoizedSelector( const selectImagesToChange = createMemoizedSelector(
selectChangeBoardModalSlice, selectChangeBoardModalSlice,
(changeBoardModal) => { (changeBoardModal) => changeBoardModal.imagesToChange
const { isModalOpen, imagesToChange } = changeBoardModal;
return {
isModalOpen,
imagesToChange,
};
}
); );
const ChangeBoardModal = () => { const ChangeBoardModal = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [selectedBoard, setSelectedBoard] = useState<string | null>(); const [selectedBoard, setSelectedBoard] = useState<string | null>();
const { data: boards, isFetching } = useListAllBoardsQuery(); const { data: boards, isFetching } = useListAllBoardsQuery();
const { imagesToChange, isModalOpen } = useAppSelector(selector); const isModalOpen = useAppSelector((s) => s.changeBoardModal.isModalOpen);
const imagesToChange = useAppSelector(selectImagesToChange);
const [addImagesToBoard] = useAddImagesToBoardMutation(); const [addImagesToBoard] = useAddImagesToBoardMutation();
const [removeImagesFromBoard] = useRemoveImagesFromBoardMutation(); const [removeImagesFromBoard] = useRemoveImagesFromBoardMutation();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -17,14 +17,11 @@ import type {
TypesafeDraggableData, TypesafeDraggableData,
TypesafeDroppableData, TypesafeDroppableData,
} from 'features/dnd/types'; } from 'features/dnd/types';
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
import { import {
heightChanged, heightChanged,
selectOptimalDimension, selectOptimalDimension,
widthChanged, widthChanged,
} from 'features/parameters/store/generationSlice'; } from 'features/parameters/store/generationSlice';
import { selectSystemSlice } from 'features/system/store/systemSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FaRulerVertical, FaSave, FaUndo } from 'react-icons/fa'; import { FaRulerVertical, FaSave, FaUndo } from 'react-icons/fa';
@ -41,42 +38,22 @@ type Props = {
isSmall?: boolean; isSmall?: boolean;
}; };
const selector = createMemoizedSelector( const selectPendingControlImages = createMemoizedSelector(
[ selectControlAdaptersSlice,
selectControlAdaptersSlice, (controlAdapters) => controlAdapters.pendingControlImages
selectGallerySlice,
selectSystemSlice,
activeTabNameSelector,
selectOptimalDimension,
],
(controlAdapters, gallery, system, activeTabName, optimalDimension) => {
const { pendingControlImages } = controlAdapters;
const { autoAddBoardId } = gallery;
const { isConnected } = system;
return {
pendingControlImages,
autoAddBoardId,
isConnected,
activeTabName,
optimalDimension,
};
}
); );
const ControlAdapterImagePreview = ({ isSmall, id }: Props) => { const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const controlImageName = useControlAdapterControlImage(id); const controlImageName = useControlAdapterControlImage(id);
const processedControlImageName = useControlAdapterProcessedControlImage(id); const processedControlImageName = useControlAdapterProcessedControlImage(id);
const processorType = useControlAdapterProcessorType(id); const processorType = useControlAdapterProcessorType(id);
const dispatch = useAppDispatch(); const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
const { t } = useTranslation(); const isConnected = useAppSelector((s) => s.system.isConnected);
const { const activeTabName = useAppSelector(selectActiveTabname);
pendingControlImages, const optimalDimension = useAppSelector(selectOptimalDimension);
autoAddBoardId, const pendingControlImages = useAppSelector(selectPendingControlImages);
isConnected,
activeTabName,
optimalDimension,
} = useAppSelector(selector);
const [isMouseOverImage, setIsMouseOverImage] = useState(false); const [isMouseOverImage, setIsMouseOverImage] = useState(false);

View File

@ -24,9 +24,10 @@ type ParamControlAdapterModelProps = {
id: string; id: string;
}; };
const selector = createMemoizedSelector(selectGenerationSlice, (generation) => { const selectMainModel = createMemoizedSelector(
return { mainModel: generation.model }; selectGenerationSlice,
}); (generation) => generation.model
);
const ParamControlAdapterModel = ({ id }: ParamControlAdapterModelProps) => { const ParamControlAdapterModel = ({ id }: ParamControlAdapterModelProps) => {
const isEnabled = useControlAdapterIsEnabled(id); const isEnabled = useControlAdapterIsEnabled(id);
@ -36,7 +37,7 @@ const ParamControlAdapterModel = ({ id }: ParamControlAdapterModelProps) => {
const currentBaseModel = useAppSelector( const currentBaseModel = useAppSelector(
(s) => s.generation.model?.base_model (s) => s.generation.model?.base_model
); );
const { mainModel } = useAppSelector(selector); const { mainModel } = useAppSelector(selectMainModel);
const { t } = useTranslation(); const { t } = useTranslation();
const models = useControlAdapterModelEntities(controlAdapterType); const models = useControlAdapterModelEntities(controlAdapterType);

View File

@ -20,7 +20,7 @@ type Props = {
id: string; id: string;
}; };
const selector = createMemoizedSelector(configSelector, (config) => { const selectOptions = createMemoizedSelector(configSelector, (config) => {
const options: InvSelectOption[] = map(CONTROLNET_PROCESSORS, (p) => ({ const options: InvSelectOption[] = map(CONTROLNET_PROCESSORS, (p) => ({
value: p.type, value: p.type,
label: p.label, label: p.label,
@ -47,7 +47,7 @@ const ParamControlAdapterProcessorSelect = ({ id }: Props) => {
const isEnabled = useControlAdapterIsEnabled(id); const isEnabled = useControlAdapterIsEnabled(id);
const processorNode = useControlAdapterProcessorNode(id); const processorNode = useControlAdapterProcessorNode(id);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const options = useAppSelector(selector); const options = useAppSelector(selectOptions);
const { t } = useTranslation(); const { t } = useTranslation();
const onChange = useCallback<InvSelectOnChange>( const onChange = useCallback<InvSelectOnChange>(

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { import {
selectControlAdapterById, selectControlAdapterById,
@ -9,7 +9,7 @@ import { useMemo } from 'react';
export const useControlAdapterControlImage = (id: string) => { export const useControlAdapterControlImage = (id: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector( createSelector(
selectControlAdaptersSlice, selectControlAdaptersSlice,
(controlAdapters) => (controlAdapters) =>
selectControlAdapterById(controlAdapters, id)?.controlImage selectControlAdapterById(controlAdapters, id)?.controlImage
@ -17,7 +17,7 @@ export const useControlAdapterControlImage = (id: string) => {
[id] [id]
); );
const weight = useAppSelector(selector); const controlImageName = useAppSelector(selector);
return weight; return controlImageName;
}; };

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { import {
selectControlAdapterById, selectControlAdapterById,
@ -10,7 +10,7 @@ import { useMemo } from 'react';
export const useControlAdapterControlMode = (id: string) => { export const useControlAdapterControlMode = (id: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { createSelector(selectControlAdaptersSlice, (controlAdapters) => {
const ca = selectControlAdapterById(controlAdapters, id); const ca = selectControlAdapterById(controlAdapters, id);
if (ca && isControlNet(ca)) { if (ca && isControlNet(ca)) {
return ca.controlMode; return ca.controlMode;

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { import {
selectControlAdapterById, selectControlAdapterById,
@ -9,7 +9,7 @@ import { useMemo } from 'react';
export const useControlAdapterIsEnabled = (id: string) => { export const useControlAdapterIsEnabled = (id: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector( createSelector(
selectControlAdaptersSlice, selectControlAdaptersSlice,
(controlAdapters) => (controlAdapters) =>
selectControlAdapterById(controlAdapters, id)?.isEnabled ?? false selectControlAdapterById(controlAdapters, id)?.isEnabled ?? false

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { import {
selectControlAdapterById, selectControlAdapterById,
@ -10,7 +10,7 @@ import { useMemo } from 'react';
export const useControlAdapterProcessedControlImage = (id: string) => { export const useControlAdapterProcessedControlImage = (id: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { createSelector(selectControlAdaptersSlice, (controlAdapters) => {
const ca = selectControlAdapterById(controlAdapters, id); const ca = selectControlAdapterById(controlAdapters, id);
return ca && isControlNetOrT2IAdapter(ca) return ca && isControlNetOrT2IAdapter(ca)

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { import {
selectControlAdapterById, selectControlAdapterById,
@ -10,7 +10,7 @@ import { useMemo } from 'react';
export const useControlAdapterProcessorType = (id: string) => { export const useControlAdapterProcessorType = (id: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { createSelector(selectControlAdaptersSlice, (controlAdapters) => {
const ca = selectControlAdapterById(controlAdapters, id); const ca = selectControlAdapterById(controlAdapters, id);
return ca && isControlNetOrT2IAdapter(ca) return ca && isControlNetOrT2IAdapter(ca)

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { import {
selectControlAdapterById, selectControlAdapterById,
@ -10,7 +10,7 @@ import { useMemo } from 'react';
export const useControlAdapterShouldAutoConfig = (id: string) => { export const useControlAdapterShouldAutoConfig = (id: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => { createSelector(selectControlAdaptersSlice, (controlAdapters) => {
const ca = selectControlAdapterById(controlAdapters, id); const ca = selectControlAdapterById(controlAdapters, id);
if (ca && isControlNetOrT2IAdapter(ca)) { if (ca && isControlNetOrT2IAdapter(ca)) {
return ca.shouldAutoConfig; return ca.shouldAutoConfig;

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { import {
selectControlAdapterById, selectControlAdapterById,
@ -9,7 +9,7 @@ import { useMemo } from 'react';
export const useControlAdapterWeight = (id: string) => { export const useControlAdapterWeight = (id: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector( createSelector(
selectControlAdaptersSlice, selectControlAdaptersSlice,
(controlAdapters) => (controlAdapters) =>
selectControlAdapterById(controlAdapters, id)?.weight selectControlAdapterById(controlAdapters, id)?.weight

View File

@ -20,11 +20,7 @@ import {
import type { ImageUsage } from 'features/deleteImageModal/store/types'; import type { ImageUsage } from 'features/deleteImageModal/store/types';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
import { selectConfigSlice } from 'features/system/store/configSlice'; import { setShouldConfirmOnDelete } from 'features/system/store/systemSlice';
import {
selectSystemSlice,
setShouldConfirmOnDelete,
} from 'features/system/store/systemSlice';
import { some } from 'lodash-es'; import { some } from 'lodash-es';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
@ -32,10 +28,8 @@ import { useTranslation } from 'react-i18next';
import ImageUsageMessage from './ImageUsageMessage'; import ImageUsageMessage from './ImageUsageMessage';
const selector = createMemoizedSelector( const selectImageUsages = createMemoizedSelector(
[ [
selectSystemSlice,
selectConfigSlice,
selectDeleteImageModalSlice, selectDeleteImageModalSlice,
selectGenerationSlice, selectGenerationSlice,
selectCanvasSlice, selectCanvasSlice,
@ -44,8 +38,6 @@ const selector = createMemoizedSelector(
selectImageUsage, selectImageUsage,
], ],
( (
system,
config,
deleteImageModal, deleteImageModal,
generation, generation,
canvas, canvas,
@ -53,9 +45,7 @@ const selector = createMemoizedSelector(
controlAdapters, controlAdapters,
imagesUsage imagesUsage
) => { ) => {
const { shouldConfirmOnDelete } = system; const { imagesToDelete } = deleteImageModal;
const { canRestoreDeletedImagesFromBin } = config;
const { imagesToDelete, isModalOpen } = deleteImageModal;
const allImageUsage = (imagesToDelete ?? []).map(({ image_name }) => const allImageUsage = (imagesToDelete ?? []).map(({ image_name }) =>
getImageUsage(generation, canvas, nodes, controlAdapters, image_name) getImageUsage(generation, canvas, nodes, controlAdapters, image_name)
@ -69,11 +59,8 @@ const selector = createMemoizedSelector(
}; };
return { return {
shouldConfirmOnDelete,
canRestoreDeletedImagesFromBin,
imagesToDelete, imagesToDelete,
imagesUsage, imagesUsage,
isModalOpen,
imageUsageSummary, imageUsageSummary,
}; };
} }
@ -82,15 +69,15 @@ const selector = createMemoizedSelector(
const DeleteImageModal = () => { const DeleteImageModal = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const shouldConfirmOnDelete = useAppSelector(
const { (s) => s.system.shouldConfirmOnDelete
shouldConfirmOnDelete, );
canRestoreDeletedImagesFromBin, const canRestoreDeletedImagesFromBin = useAppSelector(
imagesToDelete, (s) => s.config.canRestoreDeletedImagesFromBin
imagesUsage, );
isModalOpen, const isModalOpen = useAppSelector((s) => s.deleteImageModal.isModalOpen);
imageUsageSummary, const { imagesToDelete, imagesUsage, imageUsageSummary } =
} = useAppSelector(selector); useAppSelector(selectImageUsages);
const handleChangeShouldConfirmOnDelete = useCallback( const handleChangeShouldConfirmOnDelete = useCallback(
(e: ChangeEvent<HTMLInputElement>) => (e: ChangeEvent<HTMLInputElement>) =>

View File

@ -1,37 +1,23 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { import { maxPromptsChanged } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
maxPromptsChanged,
selectDynamicPromptsSlice,
} from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
selectDynamicPromptsSlice,
selectConfigSlice,
(dynamicPrompts, config) => {
const { maxPrompts, combinatorial } = dynamicPrompts;
const { min, sliderMax, inputMax, initial } =
config.sd.dynamicPrompts.maxPrompts;
return {
maxPrompts,
min,
sliderMax,
inputMax,
initial,
isDisabled: !combinatorial,
};
}
);
const ParamDynamicPromptsMaxPrompts = () => { const ParamDynamicPromptsMaxPrompts = () => {
const { maxPrompts, min, sliderMax, inputMax, initial, isDisabled } = const maxPrompts = useAppSelector((s) => s.dynamicPrompts.maxPrompts);
useAppSelector(selector); const min = useAppSelector((s) => s.config.sd.dynamicPrompts.maxPrompts.min);
const sliderMax = useAppSelector(
(s) => s.config.sd.dynamicPrompts.maxPrompts.sliderMax
);
const inputMax = useAppSelector(
(s) => s.config.sd.dynamicPrompts.maxPrompts.inputMax
);
const initial = useAppSelector(
(s) => s.config.sd.dynamicPrompts.maxPrompts.initial
);
const isDisabled = useAppSelector((s) => !s.dynamicPrompts.combinatorial);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -11,18 +11,9 @@ import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FaCircleExclamation } from 'react-icons/fa6'; import { FaCircleExclamation } from 'react-icons/fa6';
const selector = createMemoizedSelector( const selectPrompts = createMemoizedSelector(
selectDynamicPromptsSlice, selectDynamicPromptsSlice,
(dynamicPrompts) => { (dynamicPrompts) => dynamicPrompts.prompts
const { isLoading, isError, prompts, parsingError } = dynamicPrompts;
return {
prompts,
parsingError,
isError,
isLoading,
};
}
); );
const listItemStyles: ChakraProps['sx'] = { const listItemStyles: ChakraProps['sx'] = {
@ -31,8 +22,10 @@ const listItemStyles: ChakraProps['sx'] = {
const ParamDynamicPromptsPreview = () => { const ParamDynamicPromptsPreview = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { prompts, parsingError, isLoading, isError } = const parsingError = useAppSelector((s) => s.dynamicPrompts.parsingError);
useAppSelector(selector); const isError = useAppSelector((s) => s.dynamicPrompts.isError);
const isLoading = useAppSelector((s) => s.dynamicPrompts.isLoading);
const prompts = useAppSelector(selectPrompts);
const label = useMemo(() => { const label = useMemo(() => {
let _label = `${t('dynamicPrompts.promptsPreview')} (${prompts.length})`; let _label = `${t('dynamicPrompts.promptsPreview')} (${prompts.length})`;

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu'; import type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu';
import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu'; import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu';
@ -37,18 +37,19 @@ const BoardContextMenu = ({
}: Props) => { }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const autoAssignBoardOnClick = useAppSelector(
const selector = useMemo( (s) => s.gallery.autoAssignBoardOnClick
);
const selectIsSelectedForAutoAdd = useMemo(
() => () =>
createMemoizedSelector(selectGallerySlice, (gallery) => { createSelector(
const isAutoAdd = gallery.autoAddBoardId === board_id; selectGallerySlice,
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick; (gallery) => board && board.board_id === gallery.autoAddBoardId
return { isAutoAdd, autoAssignBoardOnClick }; ),
}), [board]
[board_id]
); );
const { isAutoAdd, autoAssignBoardOnClick } = useAppSelector(selector); const isSelectedForAutoAdd = useAppSelector(selectIsSelectedForAutoAdd);
const boardName = useBoardName(board_id); const boardName = useBoardName(board_id);
const isBulkDownloadEnabled = const isBulkDownloadEnabled =
useFeatureStatus('bulkDownload').isFeatureEnabled; useFeatureStatus('bulkDownload').isFeatureEnabled;
@ -99,7 +100,7 @@ const BoardContextMenu = ({
<InvMenuGroup title={boardName}> <InvMenuGroup title={boardName}>
<InvMenuItem <InvMenuItem
icon={<FaPlus />} icon={<FaPlus />}
isDisabled={isAutoAdd || autoAssignBoardOnClick} isDisabled={isSelectedForAutoAdd || autoAssignBoardOnClick}
onClick={handleSetAutoAdd} onClick={handleSetAutoAdd}
> >
{t('boards.menuItemAutoAdd')} {t('boards.menuItemAutoAdd')}
@ -127,8 +128,8 @@ const BoardContextMenu = ({
boardName, boardName,
handleBulkDownload, handleBulkDownload,
handleSetAutoAdd, handleSetAutoAdd,
isAutoAdd,
isBulkDownloadEnabled, isBulkDownloadEnabled,
isSelectedForAutoAdd,
setBoardToDelete, setBoardToDelete,
skipEvent, skipEvent,
t, t,

View File

@ -8,8 +8,8 @@ import {
Icon, Icon,
Image, Image,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { skipToken } from '@reduxjs/toolkit/query'; import { skipToken } from '@reduxjs/toolkit/query';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIDroppable from 'common/components/IAIDroppable'; import IAIDroppable from 'common/components/IAIDroppable';
import { InvText } from 'common/components/InvText/wrapper'; import { InvText } from 'common/components/InvText/wrapper';
@ -54,22 +54,19 @@ const GalleryBoard = ({
setBoardToDelete, setBoardToDelete,
}: GalleryBoardProps) => { }: GalleryBoardProps) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const selector = useMemo( const autoAssignBoardOnClick = useAppSelector(
(s) => s.gallery.autoAssignBoardOnClick
);
const selectIsSelectedForAutoAdd = useMemo(
() => () =>
createMemoizedSelector(selectGallerySlice, (gallery) => { createSelector(
const isSelectedForAutoAdd = board.board_id === gallery.autoAddBoardId; selectGallerySlice,
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick; (gallery) => board.board_id === gallery.autoAddBoardId
),
return {
isSelectedForAutoAdd,
autoAssignBoardOnClick,
};
}),
[board.board_id] [board.board_id]
); );
const { isSelectedForAutoAdd, autoAssignBoardOnClick } = const isSelectedForAutoAdd = useAppSelector(selectIsSelectedForAutoAdd);
useAppSelector(selector);
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
const handleMouseOver = useCallback(() => { const handleMouseOver = useCallback(() => {
setIsHovered(true); setIsHovered(true);

View File

@ -63,7 +63,8 @@ const DeleteBoardModal = (props: Props) => {
isNodesImage: some(allImageUsage, (i) => i.isNodesImage), isNodesImage: some(allImageUsage, (i) => i.isNodesImage),
isControlImage: some(allImageUsage, (i) => i.isControlImage), isControlImage: some(allImageUsage, (i) => i.isControlImage),
}; };
return { imageUsageSummary };
return imageUsageSummary;
} }
), ),
[boardImageNames] [boardImageNames]
@ -75,7 +76,7 @@ const DeleteBoardModal = (props: Props) => {
const [deleteBoardAndImages, { isLoading: isDeleteBoardAndImagesLoading }] = const [deleteBoardAndImages, { isLoading: isDeleteBoardAndImagesLoading }] =
useDeleteBoardAndImagesMutation(); useDeleteBoardAndImagesMutation();
const { imageUsageSummary } = useAppSelector(selectImageUsageSummary); const imageUsageSummary = useAppSelector(selectImageUsageSummary);
const handleDeleteBoardOnly = useCallback(() => { const handleDeleteBoardOnly = useCallback(() => {
if (!boardToDelete) { if (!boardToDelete) {

View File

@ -1,7 +1,7 @@
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { skipToken } from '@reduxjs/toolkit/query'; import { skipToken } from '@reduxjs/toolkit/query';
import { useAppToaster } from 'app/components/Toaster'; import { useAppToaster } from 'app/components/Toaster';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { upscaleRequested } from 'app/store/middleware/listenerMiddleware/listeners/upscaleRequested'; import { upscaleRequested } from 'app/store/middleware/listenerMiddleware/listeners/upscaleRequested';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup'; import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup';
@ -12,17 +12,15 @@ import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteIm
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice'; import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems'; import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
import { sentImageToImg2Img } from 'features/gallery/store/actions'; import { sentImageToImg2Img } from 'features/gallery/store/actions';
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
import { selectGallerySlice } from 'features/gallery/store/gallerySlice'; import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
import ParamUpscalePopover from 'features/parameters/components/Upscale/ParamUpscaleSettings'; import ParamUpscalePopover from 'features/parameters/components/Upscale/ParamUpscaleSettings';
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters'; import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
import { initialImageSelected } from 'features/parameters/store/actions'; import { initialImageSelected } from 'features/parameters/store/actions';
import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress'; import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { selectSystemSlice } from 'features/system/store/systemSlice'; import { selectSystemSlice } from 'features/system/store/systemSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { import {
selectUiSlice,
setShouldShowImageDetails, setShouldShowImageDetails,
setShouldShowProgressInViewer, setShouldShowProgressInViewer,
} from 'features/ui/store/uiSlice'; } from 'features/ui/store/uiSlice';
@ -42,51 +40,29 @@ import { FaCircleNodes, FaEllipsis } from 'react-icons/fa6';
import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata'; import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
const currentImageButtonsSelector = createMemoizedSelector( const selectShouldDisableToolbarButtons = createSelector(
[ selectSystemSlice,
selectGallerySlice, selectGallerySlice,
selectSystemSlice, selectLastSelectedImage,
selectUiSlice, (system, gallery, lastSelectedImage) => {
selectConfigSlice, const hasProgressImage = Boolean(system.denoiseProgress?.progress_image);
activeTabNameSelector, return hasProgressImage || !lastSelectedImage;
],
(gallery, system, ui, config, activeTabName) => {
const { isConnected, shouldConfirmOnDelete, denoiseProgress } = system;
const {
shouldShowImageDetails,
shouldHidePreview,
shouldShowProgressInViewer,
} = ui;
const { shouldFetchMetadataFromApi } = config;
const lastSelectedImage = gallery.selection[gallery.selection.length - 1];
return {
shouldConfirmOnDelete,
isConnected,
shouldDisableToolbarButtons:
Boolean(denoiseProgress?.progress_image) || !lastSelectedImage,
shouldShowImageDetails,
activeTabName,
shouldHidePreview,
shouldShowProgressInViewer,
lastSelectedImage,
shouldFetchMetadataFromApi,
};
} }
); );
const CurrentImageButtons = () => { const CurrentImageButtons = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { const isConnected = useAppSelector((s) => s.system.isConnected);
isConnected, const shouldShowImageDetails = useAppSelector(
shouldDisableToolbarButtons, (s) => s.ui.shouldShowImageDetails
shouldShowImageDetails, );
lastSelectedImage, const shouldShowProgressInViewer = useAppSelector(
shouldShowProgressInViewer, (s) => s.ui.shouldShowProgressInViewer
} = useAppSelector(currentImageButtonsSelector); );
const lastSelectedImage = useAppSelector(selectLastSelectedImage);
const shouldDisableToolbarButtons = useAppSelector(
selectShouldDisableToolbarButtons
);
const isUpscalingEnabled = useFeatureStatus('upscaling').isFeatureEnabled; const isUpscalingEnabled = useFeatureStatus('upscaling').isFeatureEnabled;
const isQueueMutationInProgress = useIsQueueMutationInProgress(); const isQueueMutationInProgress = useIsQueueMutationInProgress();

View File

@ -1,6 +1,6 @@
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { skipToken } from '@reduxjs/toolkit/query'; import { skipToken } from '@reduxjs/toolkit/query';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import IAIDndImage from 'common/components/IAIDndImage'; import IAIDndImage from 'common/components/IAIDndImage';
import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import { IAINoContentFallback } from 'common/components/IAIImageFallback';
@ -13,8 +13,6 @@ import ImageMetadataViewer from 'features/gallery/components/ImageMetadataViewer
import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons'; import NextPrevImageButtons from 'features/gallery/components/NextPrevImageButtons';
import { useNextPrevImage } from 'features/gallery/hooks/useNextPrevImage'; import { useNextPrevImage } from 'features/gallery/hooks/useNextPrevImage';
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors'; import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
import { selectSystemSlice } from 'features/system/store/systemSlice';
import { selectUiSlice } from 'features/ui/store/uiSlice';
import type { AnimationProps } from 'framer-motion'; import type { AnimationProps } from 'framer-motion';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
@ -24,34 +22,22 @@ import { useTranslation } from 'react-i18next';
import { FaImage } from 'react-icons/fa'; import { FaImage } from 'react-icons/fa';
import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { useGetImageDTOQuery } from 'services/api/endpoints/images';
export const imagesSelector = createMemoizedSelector( const selectLastSelectedImageName = createSelector(
selectUiSlice,
selectSystemSlice,
selectLastSelectedImage, selectLastSelectedImage,
(ui, system, lastSelectedImage) => { (lastSelectedImage) => lastSelectedImage?.image_name
const {
shouldShowImageDetails,
shouldHidePreview,
shouldShowProgressInViewer,
} = ui;
const { denoiseProgress } = system;
return {
shouldShowImageDetails,
shouldHidePreview,
imageName: lastSelectedImage?.image_name,
hasDenoiseProgress: Boolean(denoiseProgress),
shouldShowProgressInViewer,
};
}
); );
const CurrentImagePreview = () => { const CurrentImagePreview = () => {
const { const shouldShowImageDetails = useAppSelector(
shouldShowImageDetails, (s) => s.ui.shouldShowImageDetails
imageName, );
hasDenoiseProgress, const imageName = useAppSelector(selectLastSelectedImageName);
shouldShowProgressInViewer, const hasDenoiseProgress = useAppSelector((s) =>
} = useAppSelector(imagesSelector); Boolean(s.system.denoiseProgress)
);
const shouldShowProgressInViewer = useAppSelector(
(s) => s.ui.shouldShowProgressInViewer
);
const { const {
handlePrevImage, handlePrevImage,

View File

@ -1,5 +1,4 @@
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvCheckbox } from 'common/components/InvCheckbox/wrapper'; import { InvCheckbox } from 'common/components/InvCheckbox/wrapper';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
@ -14,7 +13,6 @@ import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { InvSwitch } from 'common/components/InvSwitch/wrapper'; import { InvSwitch } from 'common/components/InvSwitch/wrapper';
import { import {
autoAssignBoardOnClickChanged, autoAssignBoardOnClickChanged,
selectGallerySlice,
setGalleryImageMinimumWidth, setGalleryImageMinimumWidth,
shouldAutoSwitchChanged, shouldAutoSwitchChanged,
} from 'features/gallery/store/gallerySlice'; } from 'features/gallery/store/gallerySlice';
@ -25,23 +23,16 @@ import { FaWrench } from 'react-icons/fa';
import BoardAutoAddSelect from './Boards/BoardAutoAddSelect'; import BoardAutoAddSelect from './Boards/BoardAutoAddSelect';
const selector = createMemoizedSelector(selectGallerySlice, (gallery) => {
const { galleryImageMinimumWidth, shouldAutoSwitch, autoAssignBoardOnClick } =
gallery;
return {
galleryImageMinimumWidth,
shouldAutoSwitch,
autoAssignBoardOnClick,
};
});
const GallerySettingsPopover = () => { const GallerySettingsPopover = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const galleryImageMinimumWidth = useAppSelector(
const { galleryImageMinimumWidth, shouldAutoSwitch, autoAssignBoardOnClick } = (s) => s.gallery.galleryImageMinimumWidth
useAppSelector(selector); );
const shouldAutoSwitch = useAppSelector((s) => s.gallery.shouldAutoSwitch);
const autoAssignBoardOnClick = useAppSelector(
(s) => s.gallery.autoAssignBoardOnClick
);
const handleChangeGalleryImageMinimumWidth = useCallback( const handleChangeGalleryImageMinimumWidth = useCallback(
(v: number) => { (v: number) => {

View File

@ -1,39 +1,21 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { selectHrfSlice, setHrfStrength } from 'features/hrf/store/hrfSlice'; import { setHrfStrength } from 'features/hrf/store/hrfSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
selectHrfSlice,
selectConfigSlice,
(hrf, config) => {
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
config.sd.hrfStrength;
const { hrfStrength } = hrf;
return {
hrfStrength,
initial,
min,
sliderMax,
inputMax,
step: coarseStep,
fineStep,
};
}
);
const ParamHrfStrength = () => { const ParamHrfStrength = () => {
const { hrfStrength, initial, min, sliderMax, step, fineStep } = const hrfStrength = useAppSelector((s) => s.hrf.hrfStrength);
useAppSelector(selector); const initial = useAppSelector((s) => s.config.sd.hrfStrength.initial);
const min = useAppSelector((s) => s.config.sd.hrfStrength.min);
const sliderMax = useAppSelector((s) => s.config.sd.hrfStrength.sliderMax);
const coarseStep = useAppSelector((s) => s.config.sd.hrfStrength.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.hrfStrength.fineStep);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const handleHrfStrengthChange = useCallback( const onChange = useCallback(
(v: number) => { (v: number) => {
dispatch(setHrfStrength(v)); dispatch(setHrfStrength(v));
}, },
@ -45,11 +27,11 @@ const ParamHrfStrength = () => {
<InvSlider <InvSlider
min={min} min={min}
max={sliderMax} max={sliderMax}
step={step} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
value={hrfStrength} value={hrfStrength}
defaultValue={initial} defaultValue={initial}
onChange={handleHrfStrengthChange} onChange={onChange}
marks marks
withNumberInput withNumberInput
/> />

View File

@ -6,12 +6,12 @@ import { selectLoraSlice } from 'features/lora/store/loraSlice';
import { map } from 'lodash-es'; import { map } from 'lodash-es';
import { memo } from 'react'; import { memo } from 'react';
const selector = createMemoizedSelector(selectLoraSlice, (lora) => { const selectLoRAsArray = createMemoizedSelector(selectLoraSlice, (lora) =>
return { lorasArray: map(lora.loras) }; map(lora.loras)
}); );
export const LoRAList = memo(() => { export const LoRAList = memo(() => {
const { lorasArray } = useAppSelector(selector); const lorasArray = useAppSelector(selectLoRAsArray);
if (!lorasArray.length) { if (!lorasArray.length) {
return null; return null;

View File

@ -10,15 +10,16 @@ import { useTranslation } from 'react-i18next';
import type { LoRAModelConfigEntity } from 'services/api/endpoints/models'; import type { LoRAModelConfigEntity } from 'services/api/endpoints/models';
import { useGetLoRAModelsQuery } from 'services/api/endpoints/models'; import { useGetLoRAModelsQuery } from 'services/api/endpoints/models';
const selector = createMemoizedSelector(selectLoraSlice, (lora) => ({ const selectAddedLoRAs = createMemoizedSelector(
addedLoRAs: lora.loras, selectLoraSlice,
})); (lora) => lora.loras
);
const LoRASelect = () => { const LoRASelect = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { data, isLoading } = useGetLoRAModelsQuery(); const { data, isLoading } = useGetLoRAModelsQuery();
const { t } = useTranslation(); const { t } = useTranslation();
const { addedLoRAs } = useAppSelector(selector); const addedLoRAs = useAppSelector(selectAddedLoRAs);
const currentBaseModel = useAppSelector( const currentBaseModel = useAppSelector(
(s) => s.generation.model?.base_model (s) => s.generation.model?.base_model
); );

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar'; import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar';
import { getFieldColor } from 'features/nodes/components/flow/edges/util/getEdgeColor'; import { getFieldColor } from 'features/nodes/components/flow/edges/util/getEdgeColor';
@ -8,25 +8,17 @@ import { memo } from 'react';
import type { ConnectionLineComponentProps } from 'reactflow'; import type { ConnectionLineComponentProps } from 'reactflow';
import { getBezierPath } from 'reactflow'; import { getBezierPath } from 'reactflow';
const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { const selectStroke = createSelector(selectNodesSlice, (nodes) =>
const { shouldAnimateEdges, connectionStartFieldType, shouldColorEdges } = nodes.shouldColorEdges
nodes; ? getFieldColor(nodes.connectionStartFieldType)
: colorTokenToCssVar('base.500')
);
const stroke = shouldColorEdges const selectClassName = createSelector(selectNodesSlice, (nodes) =>
? getFieldColor(connectionStartFieldType) nodes.shouldAnimateEdges
: colorTokenToCssVar('base.500'); ? 'react-flow__custom_connection-path animated'
: 'react-flow__custom_connection-path'
let className = 'react-flow__custom_connection-path'; );
if (shouldAnimateEdges) {
className = className.concat(' animated');
}
return {
stroke,
className,
};
});
const pathStyles: CSSProperties = { opacity: 0.8 }; const pathStyles: CSSProperties = { opacity: 0.8 };
@ -38,7 +30,8 @@ const CustomConnectionLine = ({
toY, toY,
toPosition, toPosition,
}: ConnectionLineComponentProps) => { }: ConnectionLineComponentProps) => {
const { stroke, className } = useAppSelector(selector); const stroke = useAppSelector(selectStroke);
const className = useAppSelector(selectClassName);
const pathParams = { const pathParams = {
sourceX: fromX, sourceX: fromX,

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import InvocationNode from 'features/nodes/components/flow/nodes/Invocation/InvocationNode'; import InvocationNode from 'features/nodes/components/flow/nodes/Invocation/InvocationNode';
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
@ -14,15 +14,15 @@ const InvocationNodeWrapper = (props: NodeProps<InvocationNodeData>) => {
const hasTemplateSelector = useMemo( const hasTemplateSelector = useMemo(
() => () =>
createMemoizedSelector(selectNodeTemplatesSlice, (nodeTemplates) => createSelector(selectNodeTemplatesSlice, (nodeTemplates) =>
Boolean(nodeTemplates.templates[type]) Boolean(nodeTemplates.templates[type])
), ),
[type] [type]
); );
const nodeTemplate = useAppSelector(hasTemplateSelector); const hasTemplate = useAppSelector(hasTemplateSelector);
if (!nodeTemplate) { if (!hasTemplate) {
return ( return (
<InvocationNodeUnknownFallback <InvocationNodeUnknownFallback
nodeId={nodeId} nodeId={nodeId}

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu'; import type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu';
import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu'; import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu';
@ -36,16 +36,14 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
e.preventDefault(); e.preventDefault();
}, []); }, []);
const selector = useMemo( const selectIsExposed = useMemo(
() => () =>
createMemoizedSelector(selectWorkflowSlice, (workflow) => { createSelector(selectWorkflowSlice, (workflow) => {
const isExposed = Boolean( return Boolean(
workflow.exposedFields.find( workflow.exposedFields.find(
(f) => f.nodeId === nodeId && f.fieldName === fieldName (f) => f.nodeId === nodeId && f.fieldName === fieldName
) )
); );
return { isExposed };
}), }),
[fieldName, nodeId] [fieldName, nodeId]
); );
@ -55,7 +53,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
[input] [input]
); );
const { isExposed } = useAppSelector(selector); const isExposed = useAppSelector(selectIsExposed);
const handleExposeField = useCallback(() => { const handleExposeField = useCallback(() => {
dispatch(workflowExposedFieldAdded({ nodeId, fieldName })); dispatch(workflowExposedFieldAdded({ nodeId, fieldName }));

View File

@ -1,6 +1,6 @@
import type { ChakraProps } from '@chakra-ui/react'; import type { ChakraProps } from '@chakra-ui/react';
import { Box, useToken } from '@chakra-ui/react'; import { Box, useToken } from '@chakra-ui/react';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import NodeSelectionOverlay from 'common/components/NodeSelectionOverlay'; import NodeSelectionOverlay from 'common/components/NodeSelectionOverlay';
import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger'; import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger';
@ -30,7 +30,7 @@ const NodeWrapper = (props: NodeWrapperProps) => {
const selectIsInProgress = useMemo( const selectIsInProgress = useMemo(
() => () =>
createMemoizedSelector( createSelector(
selectNodesSlice, selectNodesSlice,
(nodes) => (nodes) =>
nodes.nodeExecutionStates[nodeId]?.status === nodes.nodeExecutionStates[nodeId]?.status ===

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { makeConnectionErrorSelector } from 'features/nodes/store/util/makeIsConnectionValidSelector'; import { makeConnectionErrorSelector } from 'features/nodes/store/util/makeIsConnectionValidSelector';
@ -6,7 +6,7 @@ import { useMemo } from 'react';
import { useFieldType } from './useFieldType.ts'; import { useFieldType } from './useFieldType.ts';
const selectIsConnectionInProgress = createMemoizedSelector( const selectIsConnectionInProgress = createSelector(
selectNodesSlice, selectNodesSlice,
(nodes) => (nodes) =>
nodes.connectionStartFieldType !== null && nodes.connectionStartFieldType !== null &&
@ -28,7 +28,7 @@ export const useConnectionState = ({
const selectIsConnected = useMemo( const selectIsConnected = useMemo(
() => () =>
createMemoizedSelector(selectNodesSlice, (nodes) => createSelector(selectNodesSlice, (nodes) =>
Boolean( Boolean(
nodes.edges.filter((edge) => { nodes.edges.filter((edge) => {
return ( return (
@ -55,7 +55,7 @@ export const useConnectionState = ({
const selectIsConnectionStartField = useMemo( const selectIsConnectionStartField = useMemo(
() => () =>
createMemoizedSelector(selectNodesSlice, (nodes) => createSelector(selectNodesSlice, (nodes) =>
Boolean( Boolean(
nodes.connectionStartParams?.nodeId === nodeId && nodes.connectionStartParams?.nodeId === nodeId &&
nodes.connectionStartParams?.handleId === fieldName && nodes.connectionStartParams?.handleId === fieldName &&

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { compareVersions } from 'compare-versions'; import { compareVersions } from 'compare-versions';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
@ -9,7 +9,7 @@ import { useMemo } from 'react';
export const useDoNodeVersionsMatch = (nodeId: string) => { export const useDoNodeVersionsMatch = (nodeId: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector( createSelector(
selectNodesSlice, selectNodesSlice,
selectNodeTemplatesSlice, selectNodeTemplatesSlice,
(nodes, nodeTemplates) => { (nodes, nodeTemplates) => {

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
@ -8,7 +8,7 @@ import { useMemo } from 'react';
export const useFieldInputKind = (nodeId: string, fieldName: string) => { export const useFieldInputKind = (nodeId: string, fieldName: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector( createSelector(
selectNodesSlice, selectNodesSlice,
selectNodeTemplatesSlice, selectNodeTemplatesSlice,
(nodes, nodeTemplates) => { (nodes, nodeTemplates) => {

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation'; import { isInvocationNode } from 'features/nodes/types/invocation';
@ -7,7 +7,7 @@ import { useMemo } from 'react';
export const useFieldLabel = (nodeId: string, fieldName: string) => { export const useFieldLabel = (nodeId: string, fieldName: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector(selectNodesSlice, (nodes) => { createSelector(selectNodesSlice, (nodes) => {
const node = nodes.nodes.find((node) => node.id === nodeId); const node = nodes.nodes.find((node) => node.id === nodeId);
if (!isInvocationNode(node)) { if (!isInvocationNode(node)) {
return; return;

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
@ -13,7 +13,7 @@ export const useFieldTemplateTitle = (
) => { ) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector( createSelector(
selectNodesSlice, selectNodesSlice,
selectNodeTemplatesSlice, selectNodeTemplatesSlice,
(nodes, nodeTemplates) => { (nodes, nodeTemplates) => {
@ -28,7 +28,7 @@ export const useFieldTemplateTitle = (
[fieldName, kind, nodeId] [fieldName, kind, nodeId]
); );
const fieldTemplate = useAppSelector(selector); const fieldTemplateTitle = useAppSelector(selector);
return fieldTemplate; return fieldTemplateTitle;
}; };

View File

@ -1,11 +1,11 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation'; import { isInvocationNode } from 'features/nodes/types/invocation';
import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate'; import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate';
const selector = createMemoizedSelector( const selector = createSelector(
selectNodesSlice, selectNodesSlice,
selectNodeTemplatesSlice, selectNodeTemplatesSlice,
(nodes, nodeTemplates) => (nodes, nodeTemplates) =>

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation'; import { isInvocationNode } from 'features/nodes/types/invocation';
@ -7,7 +7,7 @@ import { useMemo } from 'react';
export const useIsIntermediate = (nodeId: string) => { export const useIsIntermediate = (nodeId: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector(selectNodesSlice, (nodes) => { createSelector(selectNodesSlice, (nodes) => {
const node = nodes.nodes.find((node) => node.id === nodeId); const node = nodes.nodes.find((node) => node.id === nodeId);
if (!isInvocationNode(node)) { if (!isInvocationNode(node)) {
return false; return false;
@ -17,6 +17,6 @@ export const useIsIntermediate = (nodeId: string) => {
[nodeId] [nodeId]
); );
const is_intermediate = useAppSelector(selector); const isIntermediate = useAppSelector(selector);
return is_intermediate; return isIntermediate;
}; };

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation'; import { isInvocationNode } from 'features/nodes/types/invocation';
@ -7,7 +7,7 @@ import { useMemo } from 'react';
export const useNodeLabel = (nodeId: string) => { export const useNodeLabel = (nodeId: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector(selectNodesSlice, (nodes) => { createSelector(selectNodesSlice, (nodes) => {
const node = nodes.nodes.find((node) => node.id === nodeId); const node = nodes.nodes.find((node) => node.id === nodeId);
if (!isInvocationNode(node)) { if (!isInvocationNode(node)) {
return false; return false;

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation'; import { isInvocationNode } from 'features/nodes/types/invocation';
@ -7,7 +7,7 @@ import { useMemo } from 'react';
export const useNodePack = (nodeId: string) => { export const useNodePack = (nodeId: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector(selectNodesSlice, (nodes) => { createSelector(selectNodesSlice, (nodes) => {
const node = nodes.nodes.find((node) => node.id === nodeId); const node = nodes.nodes.find((node) => node.id === nodeId);
if (!isInvocationNode(node)) { if (!isInvocationNode(node)) {
return false; return false;
@ -17,6 +17,6 @@ export const useNodePack = (nodeId: string) => {
[nodeId] [nodeId]
); );
const title = useAppSelector(selector); const nodePack = useAppSelector(selector);
return title; return nodePack;
}; };

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
@ -8,7 +8,7 @@ import { useMemo } from 'react';
export const useNodeTemplateTitle = (nodeId: string) => { export const useNodeTemplateTitle = (nodeId: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector( createSelector(
selectNodesSlice, selectNodesSlice,
selectNodeTemplatesSlice, selectNodeTemplatesSlice,
(nodes, nodeTemplates) => { (nodes, nodeTemplates) => {

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation'; import { isInvocationNode } from 'features/nodes/types/invocation';
@ -7,13 +7,11 @@ import { useMemo } from 'react';
export const useUseCache = (nodeId: string) => { export const useUseCache = (nodeId: string) => {
const selector = useMemo( const selector = useMemo(
() => () =>
createMemoizedSelector(selectNodesSlice, (nodes) => { createSelector(selectNodesSlice, (nodes) => {
const node = nodes.nodes.find((node) => node.id === nodeId); const node = nodes.nodes.find((node) => node.id === nodeId);
if (!isInvocationNode(node)) { if (!isInvocationNode(node)) {
return false; return false;
} }
// cast to boolean to support older workflows that didn't have useCache
// TODO: handle this better somehow
return node.data.useCache; return node.data.useCache;
}), }),
[nodeId] [nodeId]

View File

@ -1,4 +1,4 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import type { FieldType } from 'features/nodes/types/field'; import type { FieldType } from 'features/nodes/types/field';
import i18n from 'i18next'; import i18n from 'i18next';
@ -18,7 +18,7 @@ export const makeConnectionErrorSelector = (
handleType: HandleType, handleType: HandleType,
fieldType?: FieldType fieldType?: FieldType
) => { ) => {
return createMemoizedSelector(selectNodesSlice, (nodesSlice) => { return createSelector(selectNodesSlice, (nodesSlice) => {
if (!fieldType) { if (!fieldType) {
return i18n.t('nodes.noFieldType'); return i18n.t('nodes.noFieldType');
} }

View File

@ -1,4 +1,3 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
@ -12,18 +11,9 @@ import { selectOptimalDimension } from 'features/parameters/store/generationSlic
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
[selectOptimalDimension, isStagingSelector],
(optimalDimension, isStaging) => {
return {
initial: optimalDimension,
isStaging,
};
}
);
const ParamBoundingBoxWidth = () => { const ParamBoundingBoxWidth = () => {
const { isStaging, initial } = useAppSelector(selector); const isStaging = useAppSelector(isStagingSelector);
const initial = useAppSelector(selectOptimalDimension);
const ctx = useImageSizeContext(); const ctx = useImageSizeContext();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -1,4 +1,3 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
@ -12,18 +11,9 @@ import { selectOptimalDimension } from 'features/parameters/store/generationSlic
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
[selectOptimalDimension, isStagingSelector],
(optimalDimension, isStaging) => {
return {
initial: optimalDimension,
isStaging,
};
}
);
const ParamBoundingBoxWidth = () => { const ParamBoundingBoxWidth = () => {
const { isStaging, initial } = useAppSelector(selector); const isStaging = useAppSelector(isStagingSelector);
const initial = useAppSelector(selectOptimalDimension);
const ctx = useImageSizeContext(); const ctx = useImageSizeContext();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -1,11 +1,7 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
selectCanvasSlice,
setScaledBoundingBoxDimensions,
} from 'features/canvas/store/canvasSlice';
import { import {
CANVAS_GRID_SIZE_COARSE, CANVAS_GRID_SIZE_COARSE,
CANVAS_GRID_SIZE_FINE, CANVAS_GRID_SIZE_FINE,
@ -14,34 +10,26 @@ import { selectOptimalDimension } from 'features/parameters/store/generationSlic
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
[selectCanvasSlice, selectOptimalDimension],
(canvas, optimalDimension) => {
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = canvas;
return {
optimalDimension,
scaledBoundingBoxDimensions,
isManual: boundingBoxScaleMethod === 'manual',
};
}
);
const ParamScaledHeight = () => { const ParamScaledHeight = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { isManual, scaledBoundingBoxDimensions, optimalDimension } = const optimalDimension = useAppSelector(selectOptimalDimension);
useAppSelector(selector); const isManual = useAppSelector(
(s) => s.canvas.boundingBoxScaleMethod === 'manual'
);
const height = useAppSelector(
(s) => s.canvas.scaledBoundingBoxDimensions.height
);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangeScaledHeight = useCallback( const onChange = useCallback(
(height: number) => { (height: number) => {
dispatch(setScaledBoundingBoxDimensions({ height })); dispatch(setScaledBoundingBoxDimensions({ height }));
}, },
[dispatch] [dispatch]
); );
const handleResetScaledHeight = useCallback(() => { const onReset = useCallback(() => {
dispatch(setScaledBoundingBoxDimensions({ height: optimalDimension })); dispatch(setScaledBoundingBoxDimensions({ height: optimalDimension }));
}, [dispatch, optimalDimension]); }, [dispatch, optimalDimension]);
@ -52,12 +40,12 @@ const ParamScaledHeight = () => {
max={1536} max={1536}
step={CANVAS_GRID_SIZE_COARSE} step={CANVAS_GRID_SIZE_COARSE}
fineStep={CANVAS_GRID_SIZE_FINE} fineStep={CANVAS_GRID_SIZE_FINE}
value={scaledBoundingBoxDimensions.height} value={height}
onChange={handleChangeScaledHeight} onChange={onChange}
marks marks
withNumberInput withNumberInput
numberInputMax={4096} numberInputMax={4096}
onReset={handleResetScaledHeight} onReset={onReset}
/> />
</InvControl> </InvControl>
); );

View File

@ -1,11 +1,7 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
selectCanvasSlice,
setScaledBoundingBoxDimensions,
} from 'features/canvas/store/canvasSlice';
import { import {
CANVAS_GRID_SIZE_COARSE, CANVAS_GRID_SIZE_COARSE,
CANVAS_GRID_SIZE_FINE, CANVAS_GRID_SIZE_FINE,
@ -14,34 +10,25 @@ import { selectOptimalDimension } from 'features/parameters/store/generationSlic
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
[selectCanvasSlice, selectOptimalDimension],
(canvas, optimalDimension) => {
const { boundingBoxScaleMethod, scaledBoundingBoxDimensions } = canvas;
return {
optimalDimension,
scaledBoundingBoxDimensions,
isManual: boundingBoxScaleMethod === 'manual',
};
}
);
const ParamScaledWidth = () => { const ParamScaledWidth = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { optimalDimension, isManual, scaledBoundingBoxDimensions } =
useAppSelector(selector);
const { t } = useTranslation(); const { t } = useTranslation();
const optimalDimension = useAppSelector(selectOptimalDimension);
const isManual = useAppSelector(
(s) => s.canvas.boundingBoxScaleMethod === 'manual'
);
const width = useAppSelector(
(s) => s.canvas.scaledBoundingBoxDimensions.width
);
const handleChangeScaledWidth = useCallback( const onChange = useCallback(
(width: number) => { (width: number) => {
dispatch(setScaledBoundingBoxDimensions({ width })); dispatch(setScaledBoundingBoxDimensions({ width }));
}, },
[dispatch] [dispatch]
); );
const handleResetScaledWidth = useCallback(() => { const onReset = useCallback(() => {
dispatch(setScaledBoundingBoxDimensions({ width: optimalDimension })); dispatch(setScaledBoundingBoxDimensions({ width: optimalDimension }));
}, [dispatch, optimalDimension]); }, [dispatch, optimalDimension]);
@ -52,12 +39,12 @@ const ParamScaledWidth = () => {
max={1536} max={1536}
step={CANVAS_GRID_SIZE_COARSE} step={CANVAS_GRID_SIZE_COARSE}
fineStep={CANVAS_GRID_SIZE_FINE} fineStep={CANVAS_GRID_SIZE_FINE}
value={scaledBoundingBoxDimensions.width} value={width}
onChange={handleChangeScaledWidth} onChange={onChange}
numberInputMax={4096} numberInputMax={4096}
marks marks
withNumberInput withNumberInput
onReset={handleResetScaledWidth} onReset={onReset}
/> />
</InvControl> </InvControl>
); );

View File

@ -1,50 +1,24 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { import { setCfgScale } from 'features/parameters/store/generationSlice';
selectGenerationSlice, import { memo, useCallback, useMemo } from 'react';
setCfgScale,
} from 'features/parameters/store/generationSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
selectGenerationSlice,
selectConfigSlice,
(generation, config) => {
const { min, inputMax, sliderMax, coarseStep, fineStep, initial } =
config.sd.guidance;
const { cfgScale } = generation;
return {
marks: [min, Math.floor(sliderMax / 2), sliderMax],
cfgScale,
min,
inputMax,
sliderMax,
coarseStep,
fineStep,
initial,
};
}
);
const ParamCFGScale = () => { const ParamCFGScale = () => {
const { const cfgScale = useAppSelector((s) => s.generation.cfgScale);
cfgScale, const min = useAppSelector((s) => s.config.sd.guidance.min);
min, const inputMax = useAppSelector((s) => s.config.sd.guidance.inputMax);
inputMax, const sliderMax = useAppSelector((s) => s.config.sd.guidance.sliderMax);
sliderMax, const coarseStep = useAppSelector((s) => s.config.sd.guidance.coarseStep);
coarseStep, const fineStep = useAppSelector((s) => s.config.sd.guidance.fineStep);
fineStep, const initial = useAppSelector((s) => s.config.sd.guidance.initial);
initial,
marks,
} = useAppSelector(selector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const marks = useMemo(
() => [min, Math.floor(sliderMax / 2), sliderMax],
[sliderMax, min]
);
const onChange = useCallback( const onChange = useCallback(
(v: number) => dispatch(setCfgScale(v)), (v: number) => dispatch(setCfgScale(v)),
[dispatch] [dispatch]

View File

@ -1,35 +1,21 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput'; import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext'; import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice'; import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
[selectConfigSlice, selectOptimalDimension],
(config, optimalDimension) => {
const { min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.height;
return {
initial: optimalDimension,
min,
max: sliderMax,
inputMax,
step: coarseStep,
fineStep,
};
}
);
export const ParamHeight = memo(() => { export const ParamHeight = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const ctx = useImageSizeContext(); const ctx = useImageSizeContext();
const { initial, min, max, inputMax, step, fineStep } = const optimalDimension = useAppSelector(selectOptimalDimension);
useAppSelector(selector); const min = useAppSelector((s) => s.config.sd.height.min);
const sliderMax = useAppSelector((s) => s.config.sd.height.sliderMax);
const inputMax = useAppSelector((s) => s.config.sd.height.inputMax);
const coarseStep = useAppSelector((s) => s.config.sd.height.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.height.fineStep);
const onChange = useCallback( const onChange = useCallback(
(v: number) => { (v: number) => {
@ -38,17 +24,20 @@ export const ParamHeight = memo(() => {
[ctx] [ctx]
); );
const marks = useMemo(() => [min, initial, max], [min, initial, max]); const marks = useMemo(
() => [min, optimalDimension, sliderMax],
[min, optimalDimension, sliderMax]
);
return ( return (
<InvControl label={t('parameters.height')}> <InvControl label={t('parameters.height')}>
<InvSlider <InvSlider
value={ctx.height} value={ctx.height}
defaultValue={initial} defaultValue={optimalDimension}
onChange={onChange} onChange={onChange}
min={min} min={min}
max={max} max={sliderMax}
step={step} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
marks={marks} marks={marks}
/> />
@ -57,9 +46,9 @@ export const ParamHeight = memo(() => {
onChange={onChange} onChange={onChange}
min={min} min={min}
max={inputMax} max={inputMax}
step={step} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
defaultValue={initial} defaultValue={optimalDimension}
/> />
</InvControl> </InvControl>
); );

View File

@ -1,43 +1,27 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { import {
clampSymmetrySteps, clampSymmetrySteps,
selectGenerationSlice,
setSteps, setSteps,
} from 'features/parameters/store/generationSlice'; } from 'features/parameters/store/generationSlice';
import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback, useMemo } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
selectGenerationSlice,
selectConfigSlice,
(generation, config) => {
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
config.sd.steps;
const { steps } = generation;
return {
marks: [min, Math.floor(sliderMax / 2), sliderMax],
steps,
initial,
min,
sliderMax,
inputMax,
step: coarseStep,
fineStep,
};
}
);
const ParamSteps = () => { const ParamSteps = () => {
const { steps, initial, min, sliderMax, inputMax, step, fineStep, marks } = const steps = useAppSelector((s) => s.generation.steps);
useAppSelector(selector); const initial = useAppSelector((s) => s.config.sd.steps.initial);
const min = useAppSelector((s) => s.config.sd.steps.min);
const sliderMax = useAppSelector((s) => s.config.sd.steps.sliderMax);
const inputMax = useAppSelector((s) => s.config.sd.steps.inputMax);
const coarseStep = useAppSelector((s) => s.config.sd.steps.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.steps.fineStep);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const marks = useMemo(
() => [min, Math.floor(sliderMax / 2), sliderMax],
[sliderMax, min]
);
const onChange = useCallback( const onChange = useCallback(
(v: number) => { (v: number) => {
dispatch(setSteps(v)); dispatch(setSteps(v));
@ -56,7 +40,7 @@ const ParamSteps = () => {
defaultValue={initial} defaultValue={initial}
min={min} min={min}
max={sliderMax} max={sliderMax}
step={step} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
onChange={onChange} onChange={onChange}
onBlur={onBlur} onBlur={onBlur}

View File

@ -1,34 +1,21 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput'; import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext'; import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice'; import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
[selectConfigSlice, selectOptimalDimension],
(config, optimalDimension) => {
const { min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.width;
return {
initial: optimalDimension,
min,
max: sliderMax,
step: coarseStep,
inputMax,
fineStep,
};
}
);
export const ParamWidth = memo(() => { export const ParamWidth = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const ctx = useImageSizeContext(); const ctx = useImageSizeContext();
const { initial, min, max, inputMax, step, fineStep } = const optimalDimension = useAppSelector(selectOptimalDimension);
useAppSelector(selector); const min = useAppSelector((s) => s.config.sd.width.min);
const sliderMax = useAppSelector((s) => s.config.sd.width.sliderMax);
const inputMax = useAppSelector((s) => s.config.sd.width.inputMax);
const coarseStep = useAppSelector((s) => s.config.sd.width.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.width.fineStep);
const onChange = useCallback( const onChange = useCallback(
(v: number) => { (v: number) => {
@ -37,17 +24,20 @@ export const ParamWidth = memo(() => {
[ctx] [ctx]
); );
const marks = useMemo(() => [min, initial, max], [min, initial, max]); const marks = useMemo(
() => [min, optimalDimension, sliderMax],
[min, optimalDimension, sliderMax]
);
return ( return (
<InvControl label={t('parameters.width')}> <InvControl label={t('parameters.width')}>
<InvSlider <InvSlider
value={ctx.width} value={ctx.width}
onChange={onChange} onChange={onChange}
defaultValue={initial} defaultValue={optimalDimension}
min={min} min={min}
max={max} max={sliderMax}
step={step} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
marks={marks} marks={marks}
/> />
@ -56,9 +46,9 @@ export const ParamWidth = memo(() => {
onChange={onChange} onChange={onChange}
min={min} min={min}
max={inputMax} max={inputMax}
step={step} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
defaultValue={initial} defaultValue={optimalDimension}
/> />
</InvControl> </InvControl>
); );

View File

@ -1,40 +1,24 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
selectGenerationSlice,
setImg2imgStrength,
} from 'features/parameters/store/generationSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(
selectGenerationSlice,
selectConfigSlice,
(generation, config) => {
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
config.sd.img2imgStrength;
const { img2imgStrength } = generation;
return {
img2imgStrength,
initial,
min,
sliderMax,
inputMax,
step: coarseStep,
fineStep,
};
}
);
const marks = [0, 0.5, 1]; const marks = [0, 0.5, 1];
const ImageToImageStrength = () => { const ImageToImageStrength = () => {
const { img2imgStrength, initial, min, sliderMax, inputMax, step, fineStep } = const img2imgStrength = useAppSelector((s) => s.generation.img2imgStrength);
useAppSelector(selector); const initial = useAppSelector((s) => s.config.sd.img2imgStrength.initial);
const min = useAppSelector((s) => s.config.sd.img2imgStrength.min);
const sliderMax = useAppSelector(
(s) => s.config.sd.img2imgStrength.sliderMax
);
const inputMax = useAppSelector((s) => s.config.sd.img2imgStrength.inputMax);
const coarseStep = useAppSelector(
(s) => s.config.sd.img2imgStrength.coarseStep
);
const fineStep = useAppSelector((s) => s.config.sd.img2imgStrength.fineStep);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
@ -49,7 +33,7 @@ const ImageToImageStrength = () => {
feature="paramDenoisingStrength" feature="paramDenoisingStrength"
> >
<InvSlider <InvSlider
step={step} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
min={min} min={min}
max={sliderMax} max={sliderMax}

View File

@ -11,28 +11,18 @@ import {
clearInitialImage, clearInitialImage,
selectGenerationSlice, selectGenerationSlice,
} from 'features/parameters/store/generationSlice'; } from 'features/parameters/store/generationSlice';
import { selectSystemSlice } from 'features/system/store/systemSlice';
import { memo, useEffect, useMemo } from 'react'; import { memo, useEffect, useMemo } from 'react';
import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { useGetImageDTOQuery } from 'services/api/endpoints/images';
const selector = createMemoizedSelector( const selectInitialImage = createMemoizedSelector(
selectGenerationSlice, selectGenerationSlice,
selectSystemSlice, (generation) => generation.initialImage
(generation, system) => {
const { initialImage } = generation;
const { isConnected } = system;
return {
initialImage,
isResetButtonDisabled: !initialImage,
isConnected,
};
}
); );
const InitialImage = () => { const InitialImage = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { initialImage, isConnected } = useAppSelector(selector); const initialImage = useAppSelector(selectInitialImage);
const isConnected = useAppSelector((s) => s.system.isConnected);
const { currentData: imageDTO, isError } = useGetImageDTOQuery( const { currentData: imageDTO, isError } = useGetImageDTOQuery(
initialImage?.imageName ?? skipToken initialImage?.imageName ?? skipToken

View File

@ -17,13 +17,10 @@ import type { PostUploadAction } from 'services/api/types';
import InitialImage from './InitialImage'; import InitialImage from './InitialImage';
const selector = createMemoizedSelector(selectGenerationSlice, (generation) => { const selectInitialImage = createMemoizedSelector(
const { initialImage } = generation; selectGenerationSlice,
return { (generation) => generation.initialImage
isResetButtonDisabled: !initialImage, );
initialImage,
};
});
const postUploadAction: PostUploadAction = { const postUploadAction: PostUploadAction = {
type: 'SET_INITIAL_IMAGE', type: 'SET_INITIAL_IMAGE',
@ -32,7 +29,7 @@ const postUploadAction: PostUploadAction = {
const InitialImageDisplay = () => { const InitialImageDisplay = () => {
const { recallWidthAndHeight } = useRecallParameters(); const { recallWidthAndHeight } = useRecallParameters();
const { t } = useTranslation(); const { t } = useTranslation();
const { isResetButtonDisabled, initialImage } = useAppSelector(selector); const initialImage = useAppSelector(selectInitialImage);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { getUploadButtonProps, getUploadInputProps } = useImageUploadButton({ const { getUploadButtonProps, getUploadInputProps } = useImageUploadButton({
@ -91,14 +88,14 @@ const InitialImageDisplay = () => {
aria-label={`${t('parameters.useSize')} (Shift+D)`} aria-label={`${t('parameters.useSize')} (Shift+D)`}
icon={<FaRulerVertical />} icon={<FaRulerVertical />}
onClick={handleUseSizeInitialImage} onClick={handleUseSizeInitialImage}
isDisabled={isResetButtonDisabled} isDisabled={!initialImage}
/> />
<InvIconButton <InvIconButton
tooltip="Reset Initial Image" tooltip="Reset Initial Image"
aria-label="Reset Initial Image" aria-label="Reset Initial Image"
icon={<FaUndo />} icon={<FaUndo />}
onClick={handleReset} onClick={handleReset}
isDisabled={isResetButtonDisabled} isDisabled={!initialImage}
/> />
</Flex> </Flex>
<InitialImage /> <InitialImage />

View File

@ -12,17 +12,15 @@ import { NON_REFINER_BASE_MODELS } from 'services/api/constants';
import type { MainModelConfigEntity } from 'services/api/endpoints/models'; import type { MainModelConfigEntity } from 'services/api/endpoints/models';
import { useGetMainModelsQuery } from 'services/api/endpoints/models'; import { useGetMainModelsQuery } from 'services/api/endpoints/models';
const selector = createMemoizedSelector( const selectModel = createMemoizedSelector(
selectGenerationSlice, selectGenerationSlice,
(generation) => ({ (generation) => generation.model
model: generation.model,
})
); );
const ParamMainModelSelect = () => { const ParamMainModelSelect = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const { model } = useAppSelector(selector); const model = useAppSelector(selectModel);
const { data, isLoading } = useGetMainModelsQuery(NON_REFINER_BASE_MODELS); const { data, isLoading } = useGetMainModelsQuery(NON_REFINER_BASE_MODELS);
const _onChange = useCallback( const _onChange = useCallback(
(model: MainModelConfigEntity | null) => { (model: MainModelConfigEntity | null) => {

View File

@ -23,11 +23,14 @@ const ParamVAEModelSelect = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { model, vae } = useAppSelector(selector); const { model, vae } = useAppSelector(selector);
const { data, isLoading } = useGetVaeModelsQuery(); const { data, isLoading } = useGetVaeModelsQuery();
const getIsDisabled = (vae: VaeModelConfigEntity): boolean => { const getIsDisabled = useCallback(
const isCompatible = model?.base_model === vae.base_model; (vae: VaeModelConfigEntity): boolean => {
const hasMainModel = Boolean(model?.base_model); const isCompatible = model?.base_model === vae.base_model;
return !hasMainModel || !isCompatible; const hasMainModel = Boolean(model?.base_model);
}; return !hasMainModel || !isCompatible;
},
[model?.base_model]
);
const _onChange = useCallback( const _onChange = useCallback(
(vae: VaeModelConfigEntity | null) => { (vae: VaeModelConfigEntity | null) => {
dispatch(vaeSelected(vae ? pick(vae, 'base_model', 'model_name') : null)); dispatch(vaeSelected(vae ? pick(vae, 'base_model', 'model_name') : null));

View File

@ -99,7 +99,7 @@ import {
import type { ImageDTO } from 'services/api/types'; import type { ImageDTO } from 'services/api/types';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
const selector = createMemoizedSelector( const selectModel = createMemoizedSelector(
selectGenerationSlice, selectGenerationSlice,
(generation) => generation.model (generation) => generation.model
); );
@ -108,7 +108,7 @@ export const useRecallParameters = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const toaster = useAppToaster(); const toaster = useAppToaster();
const { t } = useTranslation(); const { t } = useTranslation();
const model = useAppSelector(selector); const model = useAppSelector(selectModel);
const parameterSetToast = useCallback(() => { const parameterSetToast = useCallback(() => {
toaster({ toaster({

View File

@ -1,89 +1,24 @@
import { Flex, Spacer } from '@chakra-ui/layout'; import { Flex, Spacer } from '@chakra-ui/layout';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
import { InvButton } from 'common/components/InvButton/InvButton'; import { InvButton } from 'common/components/InvButton/InvButton';
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput'; import { QueueIterationsNumberInput } from 'features/queue/components/QueueIterationsNumberInput';
import type { InvNumberInputFieldProps } from 'common/components/InvNumberInput/types';
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import {
selectGenerationSlice,
setIterations,
} from 'features/parameters/store/generationSlice';
import { useQueueBack } from 'features/queue/hooks/useQueueBack'; import { useQueueBack } from 'features/queue/hooks/useQueueBack';
import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo } from 'react';
import { memo, useCallback } from 'react';
import { IoSparkles } from 'react-icons/io5'; import { IoSparkles } from 'react-icons/io5';
import { QueueButtonTooltip } from './QueueButtonTooltip'; import { QueueButtonTooltip } from './QueueButtonTooltip';
const invoke = 'Invoke'; const invoke = 'Invoke';
const numberInputFieldProps: InvNumberInputFieldProps = {
ps: 6,
borderInlineStartRadius: 'base',
h: 'full',
textAlign: 'center',
fontSize: 'md',
fontWeight: 'semibold',
};
const selector = createMemoizedSelector(
selectConfigSlice,
selectGenerationSlice,
selectDynamicPromptsSlice,
(config, generation, dynamicPrompts) => {
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
config.sd.iterations;
const { iterations } = generation;
const isLoadingDynamicPrompts = dynamicPrompts.isLoading;
return {
iterations,
initial,
min,
sliderMax,
inputMax,
step: coarseStep,
fineStep,
isLoadingDynamicPrompts,
};
}
);
export const InvokeQueueBackButton = memo(() => { export const InvokeQueueBackButton = memo(() => {
const { queueBack, isLoading, isDisabled } = useQueueBack(); const { queueBack, isLoading, isDisabled } = useQueueBack();
const { iterations, step, fineStep, isLoadingDynamicPrompts } = const isLoadingDynamicPrompts = useAppSelector(
useAppSelector(selector); (s) => s.dynamicPrompts.isLoading
const dispatch = useAppDispatch();
const handleChange = useCallback(
(v: number) => {
dispatch(setIterations(v));
},
[dispatch]
); );
return ( return (
<Flex pos="relative" flexGrow={1} minW="240px"> <Flex pos="relative" flexGrow={1} minW="240px">
<IAIInformationalPopover feature="paramIterations"> <QueueIterationsNumberInput />
<InvNumberInput
step={step}
fineStep={fineStep}
min={1}
max={999}
onChange={handleChange}
value={iterations}
defaultValue={1}
numberInputFieldProps={numberInputFieldProps}
pos="absolute"
insetInlineEnd={0}
h="full"
ps={0}
w="72px"
flexShrink={0}
variant="darkFilled"
/>
</IAIInformationalPopover>
<InvButton <InvButton
onClick={queueBack} onClick={queueBack}
isLoading={isLoading || isLoadingDynamicPrompts} isLoading={isLoading || isLoadingDynamicPrompts}

View File

@ -1,35 +1,23 @@
import { Divider, Flex, ListItem, UnorderedList } from '@chakra-ui/react'; import { Divider, Flex, ListItem, UnorderedList } from '@chakra-ui/react';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InvText } from 'common/components/InvText/wrapper'; import { InvText } from 'common/components/InvText/wrapper';
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue'; import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice'; import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt'; import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useEnqueueBatchMutation } from 'services/api/endpoints/queue'; import { useEnqueueBatchMutation } from 'services/api/endpoints/queue';
import { useBoardName } from 'services/api/hooks/useBoardName'; import { useBoardName } from 'services/api/hooks/useBoardName';
const StyledDivider = () => <Divider opacity={0.2} borderColor="base.900" />; const selectPromptsCount = createSelector(
const tooltipSelector = createMemoizedSelector(
selectGallerySlice,
selectDynamicPromptsSlice,
selectGenerationSlice, selectGenerationSlice,
(gallery, dynamicPrompts, generation) => { selectDynamicPromptsSlice,
const { autoAddBoardId } = gallery; (generation, dynamicPrompts) =>
const { iterations, positivePrompt } = generation; getShouldProcessPrompt(generation.positivePrompt)
const promptsCount = getShouldProcessPrompt(positivePrompt)
? dynamicPrompts.prompts.length ? dynamicPrompts.prompts.length
: 1; : 1
return {
autoAddBoardId,
promptsCount,
iterations,
};
}
); );
type Props = { type Props = {
@ -42,8 +30,9 @@ export const QueueButtonTooltip = memo(({ prepend = false }: Props) => {
const isLoadingDynamicPrompts = useAppSelector( const isLoadingDynamicPrompts = useAppSelector(
(s) => s.dynamicPrompts.isLoading (s) => s.dynamicPrompts.isLoading
); );
const { autoAddBoardId, promptsCount, iterations } = const promptsCount = useAppSelector(selectPromptsCount);
useAppSelector(tooltipSelector); const iterations = useAppSelector((s) => s.generation.iterations);
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
const autoAddBoardName = useBoardName(autoAddBoardId); const autoAddBoardName = useBoardName(autoAddBoardId);
const [_, { isLoading }] = useEnqueueBatchMutation({ const [_, { isLoading }] = useEnqueueBatchMutation({
fixedCacheKey: 'enqueueBatch', fixedCacheKey: 'enqueueBatch',
@ -77,7 +66,7 @@ export const QueueButtonTooltip = memo(({ prepend = false }: Props) => {
</InvText> </InvText>
{reasons.length > 0 && ( {reasons.length > 0 && (
<> <>
<StyledDivider /> <Divider opacity={0.2} borderColor="base.900" />
<UnorderedList> <UnorderedList>
{reasons.map((reason, i) => ( {reasons.map((reason, i) => (
<ListItem key={`${reason}.${i}`}> <ListItem key={`${reason}.${i}`}>
@ -87,7 +76,7 @@ export const QueueButtonTooltip = memo(({ prepend = false }: Props) => {
</UnorderedList> </UnorderedList>
</> </>
)} )}
<StyledDivider /> <Divider opacity={0.2} borderColor="base.900" />
<InvText fontStyle="oblique 10deg"> <InvText fontStyle="oblique 10deg">
{t('parameters.invoke.addingImagesTo')}{' '} {t('parameters.invoke.addingImagesTo')}{' '}
<InvText as="span" fontWeight="semibold"> <InvText as="span" fontWeight="semibold">

View File

@ -0,0 +1,52 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput';
import type { InvNumberInputFieldProps } from 'common/components/InvNumberInput/types';
import { setIterations } from 'features/parameters/store/generationSlice';
import { memo, useCallback } from 'react';
const numberInputFieldProps: InvNumberInputFieldProps = {
ps: 6,
borderInlineStartRadius: 'base',
h: 'full',
textAlign: 'center',
fontSize: 'md',
fontWeight: 'semibold',
};
export const QueueIterationsNumberInput = memo(() => {
const iterations = useAppSelector((s) => s.generation.iterations);
const coarseStep = useAppSelector((s) => s.config.sd.iterations.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.iterations.fineStep);
const dispatch = useAppDispatch();
const handleChange = useCallback(
(v: number) => {
dispatch(setIterations(v));
},
[dispatch]
);
return (
<IAIInformationalPopover feature="paramIterations">
<InvNumberInput
step={coarseStep}
fineStep={fineStep}
min={1}
max={999}
onChange={handleChange}
value={iterations}
defaultValue={1}
numberInputFieldProps={numberInputFieldProps}
pos="absolute"
insetInlineEnd={0}
h="full"
ps={0}
w="72px"
flexShrink={0}
variant="darkFilled"
/>
</IAIInformationalPopover>
);
});
QueueIterationsNumberInput.displayName = 'QueueIterationsNumberInput';

View File

@ -1,33 +1,24 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { setRefinerCFGScale } from 'features/sdxl/store/sdxlSlice'; import { setRefinerCFGScale } from 'features/sdxl/store/sdxlSlice';
import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback, useMemo } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(selectConfigSlice, (config) => {
const { min, inputMax, sliderMax, coarseStep, fineStep, initial } =
config.sd.guidance;
return {
marks: [min, Math.floor(sliderMax / 2), sliderMax],
min,
inputMax,
sliderMax,
coarseStep,
fineStep,
initial,
};
});
const ParamSDXLRefinerCFGScale = () => { const ParamSDXLRefinerCFGScale = () => {
const refinerCFGScale = useAppSelector((s) => s.sdxl.refinerCFGScale);
const { marks, min, inputMax, sliderMax, coarseStep, fineStep, initial } =
useAppSelector(selector);
const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch();
const refinerCFGScale = useAppSelector((s) => s.sdxl.refinerCFGScale);
const min = useAppSelector((s) => s.config.sd.guidance.min);
const inputMax = useAppSelector((s) => s.config.sd.guidance.inputMax);
const sliderMax = useAppSelector((s) => s.config.sd.guidance.sliderMax);
const coarseStep = useAppSelector((s) => s.config.sd.guidance.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.guidance.fineStep);
const initial = useAppSelector((s) => s.config.sd.guidance.initial);
const marks = useMemo(
() => [min, Math.floor(sliderMax / 2), sliderMax],
[sliderMax, min]
);
const onChange = useCallback( const onChange = useCallback(
(v: number) => dispatch(setRefinerCFGScale(v)), (v: number) => dispatch(setRefinerCFGScale(v)),

View File

@ -13,16 +13,17 @@ import { REFINER_BASE_MODELS } from 'services/api/constants';
import type { MainModelConfigEntity } from 'services/api/endpoints/models'; import type { MainModelConfigEntity } from 'services/api/endpoints/models';
import { useGetMainModelsQuery } from 'services/api/endpoints/models'; import { useGetMainModelsQuery } from 'services/api/endpoints/models';
const selector = createMemoizedSelector(selectSdxlSlice, (sdxl) => ({ const selectModel = createMemoizedSelector(
model: sdxl.refinerModel, selectSdxlSlice,
})); (sdxl) => sdxl.refinerModel
);
const optionsFilter = (model: MainModelConfigEntity) => const optionsFilter = (model: MainModelConfigEntity) =>
model.base_model === 'sdxl-refiner'; model.base_model === 'sdxl-refiner';
const ParamSDXLRefinerModelSelect = () => { const ParamSDXLRefinerModelSelect = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { model } = useAppSelector(selector); const model = useAppSelector(selectModel);
const { t } = useTranslation(); const { t } = useTranslation();
const { data, isLoading } = useGetMainModelsQuery(REFINER_BASE_MODELS); const { data, isLoading } = useGetMainModelsQuery(REFINER_BASE_MODELS);
const _onChange = useCallback( const _onChange = useCallback(

View File

@ -1,32 +1,25 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { setRefinerSteps } from 'features/sdxl/store/sdxlSlice'; import { setRefinerSteps } from 'features/sdxl/store/sdxlSlice';
import { selectConfigSlice } from 'features/system/store/configSlice'; import { memo, useCallback, useMemo } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createMemoizedSelector(selectConfigSlice, (config) => {
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
config.sd.steps;
return {
marks: [min, Math.floor(sliderMax / 2), sliderMax],
initial,
min,
sliderMax,
inputMax,
step: coarseStep,
fineStep,
};
});
const ParamSDXLRefinerSteps = () => { const ParamSDXLRefinerSteps = () => {
const { initial, min, sliderMax, inputMax, step, fineStep, marks } =
useAppSelector(selector);
const refinerSteps = useAppSelector((s) => s.sdxl.refinerSteps);
const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch();
const refinerSteps = useAppSelector((s) => s.sdxl.refinerSteps);
const initial = useAppSelector((s) => s.config.sd.steps.initial);
const min = useAppSelector((s) => s.config.sd.steps.min);
const sliderMax = useAppSelector((s) => s.config.sd.steps.sliderMax);
const inputMax = useAppSelector((s) => s.config.sd.steps.inputMax);
const coarseStep = useAppSelector((s) => s.config.sd.steps.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.steps.fineStep);
const marks = useMemo(
() => [min, Math.floor(sliderMax / 2), sliderMax],
[sliderMax, min]
);
const onChange = useCallback( const onChange = useCallback(
(v: number) => { (v: number) => {
@ -42,7 +35,7 @@ const ParamSDXLRefinerSteps = () => {
defaultValue={initial} defaultValue={initial}
min={min} min={min}
max={sliderMax} max={sliderMax}
step={step} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
onChange={onChange} onChange={onChange}
withNumberInput withNumberInput

View File

@ -61,8 +61,7 @@ const selector = createMemoizedSelector(
isError = true; isError = true;
} }
const controlAdapterIds = const controlAdapterIds = selectControlAdapterIds(controlAdapters);
selectControlAdapterIds(controlAdapters).map(String);
return { return {
controlAdapterIds, controlAdapterIds,

View File

@ -26,16 +26,14 @@ const stepsScaleLabelProps: InvLabelProps = {
minW: '5rem', minW: '5rem',
}; };
const selector = createMemoizedSelector(selectSdxlSlice, (sdxl) => { const selectBadges = createMemoizedSelector(selectSdxlSlice, (sdxl) =>
return { sdxl.refinerModel ? ['Enabled'] : undefined
badges: sdxl.refinerModel ? ['Enabled'] : undefined, );
};
});
export const RefinerSettingsAccordion: React.FC = memo(() => { export const RefinerSettingsAccordion: React.FC = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const isRefinerAvailable = useIsRefinerAvailable(); const isRefinerAvailable = useIsRefinerAvailable();
const { badges } = useAppSelector(selector); const badges = useAppSelector(selectBadges);
if (!isRefinerAvailable) { if (!isRefinerAvailable) {
return ( return (

View File

@ -1,25 +1,22 @@
import { Progress } from '@chakra-ui/react'; import { Progress } from '@chakra-ui/react';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectSystemSlice } from 'features/system/store/systemSlice'; import { selectSystemSlice } from 'features/system/store/systemSlice';
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue'; import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
const progressBarSelector = createMemoizedSelector(
const selectProgressValue = createSelector(
selectSystemSlice, selectSystemSlice,
(system) => { (system) => (system.denoiseProgress?.percentage ?? 0) * 100
return {
isConnected: system.isConnected,
hasSteps: Boolean(system.denoiseProgress),
value: (system.denoiseProgress?.percentage ?? 0) * 100,
};
}
); );
const ProgressBar = () => { const ProgressBar = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { data: queueStatus } = useGetQueueStatusQuery(); const { data: queueStatus } = useGetQueueStatusQuery();
const { hasSteps, value, isConnected } = useAppSelector(progressBarSelector); const isConnected = useAppSelector((s) => s.system.isConnected);
const hasSteps = useAppSelector((s) => Boolean(s.system.denoiseProgress));
const value = useAppSelector(selectProgressValue);
return ( return (
<Progress <Progress

View File

@ -1,5 +1,4 @@
import { Flex, useDisclosure } from '@chakra-ui/react'; import { Flex, useDisclosure } from '@chakra-ui/react';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvButton } from 'common/components/InvButton/InvButton'; import { InvButton } from 'common/components/InvButton/InvButton';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
@ -16,14 +15,10 @@ import { InvSwitch } from 'common/components/InvSwitch/wrapper';
import { InvText } from 'common/components/InvText/wrapper'; import { InvText } from 'common/components/InvText/wrapper';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { useClearStorage } from 'common/hooks/useClearStorage'; import { useClearStorage } from 'common/hooks/useClearStorage';
import { import { shouldUseCpuNoiseChanged } from 'features/parameters/store/generationSlice';
selectGenerationSlice,
shouldUseCpuNoiseChanged,
} from 'features/parameters/store/generationSlice';
import { useClearIntermediates } from 'features/system/components/SettingsModal/useClearIntermediates'; import { useClearIntermediates } from 'features/system/components/SettingsModal/useClearIntermediates';
import { StickyScrollable } from 'features/system/components/StickyScrollable'; import { StickyScrollable } from 'features/system/components/StickyScrollable';
import { import {
selectSystemSlice,
setEnableImageDebugging, setEnableImageDebugging,
setShouldConfirmOnDelete, setShouldConfirmOnDelete,
setShouldEnableInformationalPopovers, setShouldEnableInformationalPopovers,
@ -32,10 +27,7 @@ import {
shouldUseNSFWCheckerChanged, shouldUseNSFWCheckerChanged,
shouldUseWatermarkerChanged, shouldUseWatermarkerChanged,
} from 'features/system/store/systemSlice'; } from 'features/system/store/systemSlice';
import { import { setShouldShowProgressInViewer } from 'features/ui/store/uiSlice';
selectUiSlice,
setShouldShowProgressInViewer,
} from 'features/ui/store/uiSlice';
import type { ChangeEvent, ReactElement } from 'react'; import type { ChangeEvent, ReactElement } from 'react';
import { cloneElement, memo, useCallback, useEffect, useState } from 'react'; import { cloneElement, memo, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -44,37 +36,6 @@ import { useGetAppConfigQuery } from 'services/api/endpoints/appInfo';
import { SettingsLanguageSelect } from './SettingsLanguageSelect'; import { SettingsLanguageSelect } from './SettingsLanguageSelect';
import { SettingsLogLevelSelect } from './SettingsLogLevelSelect'; import { SettingsLogLevelSelect } from './SettingsLogLevelSelect';
const selector = createMemoizedSelector(
selectSystemSlice,
selectUiSlice,
selectGenerationSlice,
(system, ui, generation) => {
const {
shouldConfirmOnDelete,
enableImageDebugging,
shouldLogToConsole,
shouldAntialiasProgressImage,
shouldUseNSFWChecker,
shouldUseWatermarker,
shouldEnableInformationalPopovers,
} = system;
const { shouldUseCpuNoise } = generation;
const { shouldShowProgressInViewer } = ui;
return {
shouldUseCpuNoise,
shouldConfirmOnDelete,
enableImageDebugging,
shouldShowProgressInViewer,
shouldLogToConsole,
shouldAntialiasProgressImage,
shouldUseNSFWChecker,
shouldUseWatermarker,
shouldEnableInformationalPopovers,
};
}
);
type ConfigOptions = { type ConfigOptions = {
shouldShowDeveloperSettings: boolean; shouldShowDeveloperSettings: boolean;
shouldShowResetWebUiText: boolean; shouldShowResetWebUiText: boolean;
@ -137,17 +98,31 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
onClose: onRefreshModalClose, onClose: onRefreshModalClose,
} = useDisclosure(); } = useDisclosure();
const { const shouldUseCpuNoise = useAppSelector(
shouldUseCpuNoise, (s) => s.generation.shouldUseCpuNoise
shouldConfirmOnDelete, );
enableImageDebugging, const shouldConfirmOnDelete = useAppSelector(
shouldShowProgressInViewer, (s) => s.system.shouldConfirmOnDelete
shouldLogToConsole, );
shouldAntialiasProgressImage, const enableImageDebugging = useAppSelector(
shouldUseNSFWChecker, (s) => s.system.enableImageDebugging
shouldUseWatermarker, );
shouldEnableInformationalPopovers, const shouldShowProgressInViewer = useAppSelector(
} = useAppSelector(selector); (s) => s.ui.shouldShowProgressInViewer
);
const shouldLogToConsole = useAppSelector((s) => s.system.shouldLogToConsole);
const shouldAntialiasProgressImage = useAppSelector(
(s) => s.system.shouldAntialiasProgressImage
);
const shouldUseNSFWChecker = useAppSelector(
(s) => s.system.shouldUseNSFWChecker
);
const shouldUseWatermarker = useAppSelector(
(s) => s.system.shouldUseWatermarker
);
const shouldEnableInformationalPopovers = useAppSelector(
(s) => s.system.shouldEnableInformationalPopovers
);
const clearStorage = useClearStorage(); const clearStorage = useClearStorage();

View File

@ -1,7 +1,7 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createSelector } from '@reduxjs/toolkit';
import { selectSystemSlice } from 'features/system/store/systemSlice'; import { selectSystemSlice } from 'features/system/store/systemSlice';
export const languageSelector = createMemoizedSelector( export const languageSelector = createSelector(
selectSystemSlice, selectSystemSlice,
(system) => system.language (system) => system.language
); );