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

View File

@ -1,13 +1,11 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
setIsDrawing,
setIsMovingStage,
} from 'features/canvas/store/canvasNanostore';
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 { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import type Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node';
import type { MutableRefObject } from 'react';
@ -15,20 +13,10 @@ import { useCallback } from 'react';
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 dispatch = useAppDispatch();
const { tool, isStaging } = useAppSelector(selector);
const tool = useAppSelector((s) => s.canvas.tool);
const isStaging = useAppSelector(isStagingSelector);
const { commitColorUnderCursor } = useColorPicker();
return useCallback(

View File

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

View File

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

View File

@ -22,23 +22,17 @@ import {
useRemoveImagesFromBoardMutation,
} from 'services/api/endpoints/images';
const selector = createMemoizedSelector(
const selectImagesToChange = createMemoizedSelector(
selectChangeBoardModalSlice,
(changeBoardModal) => {
const { isModalOpen, imagesToChange } = changeBoardModal;
return {
isModalOpen,
imagesToChange,
};
}
(changeBoardModal) => changeBoardModal.imagesToChange
);
const ChangeBoardModal = () => {
const dispatch = useAppDispatch();
const [selectedBoard, setSelectedBoard] = useState<string | null>();
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 [removeImagesFromBoard] = useRemoveImagesFromBoardMutation();
const { t } = useTranslation();

View File

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

View File

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

View File

@ -20,7 +20,7 @@ type Props = {
id: string;
};
const selector = createMemoizedSelector(configSelector, (config) => {
const selectOptions = createMemoizedSelector(configSelector, (config) => {
const options: InvSelectOption[] = map(CONTROLNET_PROCESSORS, (p) => ({
value: p.type,
label: p.label,
@ -47,7 +47,7 @@ const ParamControlAdapterProcessorSelect = ({ id }: Props) => {
const isEnabled = useControlAdapterIsEnabled(id);
const processorNode = useControlAdapterProcessorNode(id);
const dispatch = useAppDispatch();
const options = useAppSelector(selector);
const options = useAppSelector(selectOptions);
const { t } = useTranslation();
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 {
selectControlAdapterById,
@ -9,7 +9,7 @@ import { useMemo } from 'react';
export const useControlAdapterControlImage = (id: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(
createSelector(
selectControlAdaptersSlice,
(controlAdapters) =>
selectControlAdapterById(controlAdapters, id)?.controlImage
@ -17,7 +17,7 @@ export const useControlAdapterControlImage = (id: string) => {
[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 {
selectControlAdapterById,
@ -10,7 +10,7 @@ import { useMemo } from 'react';
export const useControlAdapterControlMode = (id: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => {
createSelector(selectControlAdaptersSlice, (controlAdapters) => {
const ca = selectControlAdapterById(controlAdapters, id);
if (ca && isControlNet(ca)) {
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 {
selectControlAdapterById,
@ -9,7 +9,7 @@ import { useMemo } from 'react';
export const useControlAdapterIsEnabled = (id: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(
createSelector(
selectControlAdaptersSlice,
(controlAdapters) =>
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 {
selectControlAdapterById,
@ -10,7 +10,7 @@ import { useMemo } from 'react';
export const useControlAdapterProcessedControlImage = (id: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => {
createSelector(selectControlAdaptersSlice, (controlAdapters) => {
const ca = selectControlAdapterById(controlAdapters, id);
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 {
selectControlAdapterById,
@ -10,7 +10,7 @@ import { useMemo } from 'react';
export const useControlAdapterProcessorType = (id: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => {
createSelector(selectControlAdaptersSlice, (controlAdapters) => {
const ca = selectControlAdapterById(controlAdapters, id);
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 {
selectControlAdapterById,
@ -10,7 +10,7 @@ import { useMemo } from 'react';
export const useControlAdapterShouldAutoConfig = (id: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => {
createSelector(selectControlAdaptersSlice, (controlAdapters) => {
const ca = selectControlAdapterById(controlAdapters, id);
if (ca && isControlNetOrT2IAdapter(ca)) {
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 {
selectControlAdapterById,
@ -9,7 +9,7 @@ import { useMemo } from 'react';
export const useControlAdapterWeight = (id: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(
createSelector(
selectControlAdaptersSlice,
(controlAdapters) =>
selectControlAdapterById(controlAdapters, id)?.weight

View File

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

View File

@ -1,37 +1,23 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider';
import {
maxPromptsChanged,
selectDynamicPromptsSlice,
} from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { maxPromptsChanged } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { memo, useCallback } from 'react';
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 { maxPrompts, min, sliderMax, inputMax, initial, isDisabled } =
useAppSelector(selector);
const maxPrompts = useAppSelector((s) => s.dynamicPrompts.maxPrompts);
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 { t } = useTranslation();

View File

@ -11,18 +11,9 @@ import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { FaCircleExclamation } from 'react-icons/fa6';
const selector = createMemoizedSelector(
const selectPrompts = createMemoizedSelector(
selectDynamicPromptsSlice,
(dynamicPrompts) => {
const { isLoading, isError, prompts, parsingError } = dynamicPrompts;
return {
prompts,
parsingError,
isError,
isLoading,
};
}
(dynamicPrompts) => dynamicPrompts.prompts
);
const listItemStyles: ChakraProps['sx'] = {
@ -31,8 +22,10 @@ const listItemStyles: ChakraProps['sx'] = {
const ParamDynamicPromptsPreview = () => {
const { t } = useTranslation();
const { prompts, parsingError, isLoading, isError } =
useAppSelector(selector);
const parsingError = useAppSelector((s) => s.dynamicPrompts.parsingError);
const isError = useAppSelector((s) => s.dynamicPrompts.isError);
const isLoading = useAppSelector((s) => s.dynamicPrompts.isLoading);
const prompts = useAppSelector(selectPrompts);
const label = useMemo(() => {
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 type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu';
import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu';
@ -37,18 +37,19 @@ const BoardContextMenu = ({
}: Props) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const selector = useMemo(
const autoAssignBoardOnClick = useAppSelector(
(s) => s.gallery.autoAssignBoardOnClick
);
const selectIsSelectedForAutoAdd = useMemo(
() =>
createMemoizedSelector(selectGallerySlice, (gallery) => {
const isAutoAdd = gallery.autoAddBoardId === board_id;
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick;
return { isAutoAdd, autoAssignBoardOnClick };
}),
[board_id]
createSelector(
selectGallerySlice,
(gallery) => board && board.board_id === gallery.autoAddBoardId
),
[board]
);
const { isAutoAdd, autoAssignBoardOnClick } = useAppSelector(selector);
const isSelectedForAutoAdd = useAppSelector(selectIsSelectedForAutoAdd);
const boardName = useBoardName(board_id);
const isBulkDownloadEnabled =
useFeatureStatus('bulkDownload').isFeatureEnabled;
@ -99,7 +100,7 @@ const BoardContextMenu = ({
<InvMenuGroup title={boardName}>
<InvMenuItem
icon={<FaPlus />}
isDisabled={isAutoAdd || autoAssignBoardOnClick}
isDisabled={isSelectedForAutoAdd || autoAssignBoardOnClick}
onClick={handleSetAutoAdd}
>
{t('boards.menuItemAutoAdd')}
@ -127,8 +128,8 @@ const BoardContextMenu = ({
boardName,
handleBulkDownload,
handleSetAutoAdd,
isAutoAdd,
isBulkDownloadEnabled,
isSelectedForAutoAdd,
setBoardToDelete,
skipEvent,
t,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,15 +10,16 @@ import { useTranslation } from 'react-i18next';
import type { LoRAModelConfigEntity } from 'services/api/endpoints/models';
import { useGetLoRAModelsQuery } from 'services/api/endpoints/models';
const selector = createMemoizedSelector(selectLoraSlice, (lora) => ({
addedLoRAs: lora.loras,
}));
const selectAddedLoRAs = createMemoizedSelector(
selectLoraSlice,
(lora) => lora.loras
);
const LoRASelect = () => {
const dispatch = useAppDispatch();
const { data, isLoading } = useGetLoRAModelsQuery();
const { t } = useTranslation();
const { addedLoRAs } = useAppSelector(selector);
const addedLoRAs = useAppSelector(selectAddedLoRAs);
const currentBaseModel = useAppSelector(
(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 { colorTokenToCssVar } from 'common/util/colorTokenToCssVar';
import { getFieldColor } from 'features/nodes/components/flow/edges/util/getEdgeColor';
@ -8,25 +8,17 @@ import { memo } from 'react';
import type { ConnectionLineComponentProps } from 'reactflow';
import { getBezierPath } from 'reactflow';
const selector = createMemoizedSelector(selectNodesSlice, (nodes) => {
const { shouldAnimateEdges, connectionStartFieldType, shouldColorEdges } =
nodes;
const selectStroke = createSelector(selectNodesSlice, (nodes) =>
nodes.shouldColorEdges
? getFieldColor(nodes.connectionStartFieldType)
: colorTokenToCssVar('base.500')
);
const stroke = shouldColorEdges
? getFieldColor(connectionStartFieldType)
: colorTokenToCssVar('base.500');
let className = 'react-flow__custom_connection-path';
if (shouldAnimateEdges) {
className = className.concat(' animated');
}
return {
stroke,
className,
};
});
const selectClassName = createSelector(selectNodesSlice, (nodes) =>
nodes.shouldAnimateEdges
? 'react-flow__custom_connection-path animated'
: 'react-flow__custom_connection-path'
);
const pathStyles: CSSProperties = { opacity: 0.8 };
@ -38,7 +30,8 @@ const CustomConnectionLine = ({
toY,
toPosition,
}: ConnectionLineComponentProps) => {
const { stroke, className } = useAppSelector(selector);
const stroke = useAppSelector(selectStroke);
const className = useAppSelector(selectClassName);
const pathParams = {
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 InvocationNode from 'features/nodes/components/flow/nodes/Invocation/InvocationNode';
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
@ -14,15 +14,15 @@ const InvocationNodeWrapper = (props: NodeProps<InvocationNodeData>) => {
const hasTemplateSelector = useMemo(
() =>
createMemoizedSelector(selectNodeTemplatesSlice, (nodeTemplates) =>
createSelector(selectNodeTemplatesSlice, (nodeTemplates) =>
Boolean(nodeTemplates.templates[type])
),
[type]
);
const nodeTemplate = useAppSelector(hasTemplateSelector);
const hasTemplate = useAppSelector(hasTemplateSelector);
if (!nodeTemplate) {
if (!hasTemplate) {
return (
<InvocationNodeUnknownFallback
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 type { InvContextMenuProps } 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();
}, []);
const selector = useMemo(
const selectIsExposed = useMemo(
() =>
createMemoizedSelector(selectWorkflowSlice, (workflow) => {
const isExposed = Boolean(
createSelector(selectWorkflowSlice, (workflow) => {
return Boolean(
workflow.exposedFields.find(
(f) => f.nodeId === nodeId && f.fieldName === fieldName
)
);
return { isExposed };
}),
[fieldName, nodeId]
);
@ -55,7 +53,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
[input]
);
const { isExposed } = useAppSelector(selector);
const isExposed = useAppSelector(selectIsExposed);
const handleExposeField = useCallback(() => {
dispatch(workflowExposedFieldAdded({ nodeId, fieldName }));

View File

@ -1,6 +1,6 @@
import type { ChakraProps } 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 NodeSelectionOverlay from 'common/components/NodeSelectionOverlay';
import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger';
@ -30,7 +30,7 @@ const NodeWrapper = (props: NodeWrapperProps) => {
const selectIsInProgress = useMemo(
() =>
createMemoizedSelector(
createSelector(
selectNodesSlice,
(nodes) =>
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 { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { makeConnectionErrorSelector } from 'features/nodes/store/util/makeIsConnectionValidSelector';
@ -6,7 +6,7 @@ import { useMemo } from 'react';
import { useFieldType } from './useFieldType.ts';
const selectIsConnectionInProgress = createMemoizedSelector(
const selectIsConnectionInProgress = createSelector(
selectNodesSlice,
(nodes) =>
nodes.connectionStartFieldType !== null &&
@ -28,7 +28,7 @@ export const useConnectionState = ({
const selectIsConnected = useMemo(
() =>
createMemoizedSelector(selectNodesSlice, (nodes) =>
createSelector(selectNodesSlice, (nodes) =>
Boolean(
nodes.edges.filter((edge) => {
return (
@ -55,7 +55,7 @@ export const useConnectionState = ({
const selectIsConnectionStartField = useMemo(
() =>
createMemoizedSelector(selectNodesSlice, (nodes) =>
createSelector(selectNodesSlice, (nodes) =>
Boolean(
nodes.connectionStartParams?.nodeId === nodeId &&
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 { compareVersions } from 'compare-versions';
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
@ -9,7 +9,7 @@ import { useMemo } from 'react';
export const useDoNodeVersionsMatch = (nodeId: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(
createSelector(
selectNodesSlice,
selectNodeTemplatesSlice,
(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 { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
@ -8,7 +8,7 @@ import { useMemo } from 'react';
export const useFieldInputKind = (nodeId: string, fieldName: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(
createSelector(
selectNodesSlice,
selectNodeTemplatesSlice,
(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 { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation';
@ -7,7 +7,7 @@ import { useMemo } from 'react';
export const useFieldLabel = (nodeId: string, fieldName: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(selectNodesSlice, (nodes) => {
createSelector(selectNodesSlice, (nodes) => {
const node = nodes.nodes.find((node) => node.id === nodeId);
if (!isInvocationNode(node)) {
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 { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
@ -13,7 +13,7 @@ export const useFieldTemplateTitle = (
) => {
const selector = useMemo(
() =>
createMemoizedSelector(
createSelector(
selectNodesSlice,
selectNodeTemplatesSlice,
(nodes, nodeTemplates) => {
@ -28,7 +28,7 @@ export const useFieldTemplateTitle = (
[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 { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate';
const selector = createMemoizedSelector(
const selector = createSelector(
selectNodesSlice,
selectNodeTemplatesSlice,
(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 { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation';
@ -7,7 +7,7 @@ import { useMemo } from 'react';
export const useIsIntermediate = (nodeId: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(selectNodesSlice, (nodes) => {
createSelector(selectNodesSlice, (nodes) => {
const node = nodes.nodes.find((node) => node.id === nodeId);
if (!isInvocationNode(node)) {
return false;
@ -17,6 +17,6 @@ export const useIsIntermediate = (nodeId: string) => {
[nodeId]
);
const is_intermediate = useAppSelector(selector);
return is_intermediate;
const isIntermediate = useAppSelector(selector);
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 { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation';
@ -7,7 +7,7 @@ import { useMemo } from 'react';
export const useNodeLabel = (nodeId: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(selectNodesSlice, (nodes) => {
createSelector(selectNodesSlice, (nodes) => {
const node = nodes.nodes.find((node) => node.id === nodeId);
if (!isInvocationNode(node)) {
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 { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation';
@ -7,7 +7,7 @@ import { useMemo } from 'react';
export const useNodePack = (nodeId: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(selectNodesSlice, (nodes) => {
createSelector(selectNodesSlice, (nodes) => {
const node = nodes.nodes.find((node) => node.id === nodeId);
if (!isInvocationNode(node)) {
return false;
@ -17,6 +17,6 @@ export const useNodePack = (nodeId: string) => {
[nodeId]
);
const title = useAppSelector(selector);
return title;
const nodePack = useAppSelector(selector);
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 { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
@ -8,7 +8,7 @@ import { useMemo } from 'react';
export const useNodeTemplateTitle = (nodeId: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(
createSelector(
selectNodesSlice,
selectNodeTemplatesSlice,
(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 { selectNodesSlice } from 'features/nodes/store/nodesSlice';
import { isInvocationNode } from 'features/nodes/types/invocation';
@ -7,13 +7,11 @@ import { useMemo } from 'react';
export const useUseCache = (nodeId: string) => {
const selector = useMemo(
() =>
createMemoizedSelector(selectNodesSlice, (nodes) => {
createSelector(selectNodesSlice, (nodes) => {
const node = nodes.nodes.find((node) => node.id === nodeId);
if (!isInvocationNode(node)) {
return false;
}
// cast to boolean to support older workflows that didn't have useCache
// TODO: handle this better somehow
return node.data.useCache;
}),
[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 type { FieldType } from 'features/nodes/types/field';
import i18n from 'i18next';
@ -18,7 +18,7 @@ export const makeConnectionErrorSelector = (
handleType: HandleType,
fieldType?: FieldType
) => {
return createMemoizedSelector(selectNodesSlice, (nodesSlice) => {
return createSelector(selectNodesSlice, (nodesSlice) => {
if (!fieldType) {
return i18n.t('nodes.noFieldType');
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,11 +23,14 @@ const ParamVAEModelSelect = () => {
const { t } = useTranslation();
const { model, vae } = useAppSelector(selector);
const { data, isLoading } = useGetVaeModelsQuery();
const getIsDisabled = (vae: VaeModelConfigEntity): boolean => {
const isCompatible = model?.base_model === vae.base_model;
const hasMainModel = Boolean(model?.base_model);
return !hasMainModel || !isCompatible;
};
const getIsDisabled = useCallback(
(vae: VaeModelConfigEntity): boolean => {
const isCompatible = model?.base_model === vae.base_model;
const hasMainModel = Boolean(model?.base_model);
return !hasMainModel || !isCompatible;
},
[model?.base_model]
);
const _onChange = useCallback(
(vae: VaeModelConfigEntity | 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 { v4 as uuidv4 } from 'uuid';
const selector = createMemoizedSelector(
const selectModel = createMemoizedSelector(
selectGenerationSlice,
(generation) => generation.model
);
@ -108,7 +108,7 @@ export const useRecallParameters = () => {
const dispatch = useAppDispatch();
const toaster = useAppToaster();
const { t } = useTranslation();
const model = useAppSelector(selector);
const model = useAppSelector(selectModel);
const parameterSetToast = useCallback(() => {
toaster({

View File

@ -1,89 +1,24 @@
import { Flex, Spacer } from '@chakra-ui/layout';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
import { useAppSelector } from 'app/store/storeHooks';
import { InvButton } from 'common/components/InvButton/InvButton';
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput';
import type { InvNumberInputFieldProps } from 'common/components/InvNumberInput/types';
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import {
selectGenerationSlice,
setIterations,
} from 'features/parameters/store/generationSlice';
import { QueueIterationsNumberInput } from 'features/queue/components/QueueIterationsNumberInput';
import { useQueueBack } from 'features/queue/hooks/useQueueBack';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { memo, useCallback } from 'react';
import { memo } from 'react';
import { IoSparkles } from 'react-icons/io5';
import { QueueButtonTooltip } from './QueueButtonTooltip';
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(() => {
const { queueBack, isLoading, isDisabled } = useQueueBack();
const { iterations, step, fineStep, isLoadingDynamicPrompts } =
useAppSelector(selector);
const dispatch = useAppDispatch();
const handleChange = useCallback(
(v: number) => {
dispatch(setIterations(v));
},
[dispatch]
const isLoadingDynamicPrompts = useAppSelector(
(s) => s.dynamicPrompts.isLoading
);
return (
<Flex pos="relative" flexGrow={1} minW="240px">
<IAIInformationalPopover feature="paramIterations">
<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>
<QueueIterationsNumberInput />
<InvButton
onClick={queueBack}
isLoading={isLoading || isLoadingDynamicPrompts}

View File

@ -1,35 +1,23 @@
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 { InvText } from 'common/components/InvText/wrapper';
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useEnqueueBatchMutation } from 'services/api/endpoints/queue';
import { useBoardName } from 'services/api/hooks/useBoardName';
const StyledDivider = () => <Divider opacity={0.2} borderColor="base.900" />;
const tooltipSelector = createMemoizedSelector(
selectGallerySlice,
selectDynamicPromptsSlice,
const selectPromptsCount = createSelector(
selectGenerationSlice,
(gallery, dynamicPrompts, generation) => {
const { autoAddBoardId } = gallery;
const { iterations, positivePrompt } = generation;
const promptsCount = getShouldProcessPrompt(positivePrompt)
selectDynamicPromptsSlice,
(generation, dynamicPrompts) =>
getShouldProcessPrompt(generation.positivePrompt)
? dynamicPrompts.prompts.length
: 1;
return {
autoAddBoardId,
promptsCount,
iterations,
};
}
: 1
);
type Props = {
@ -42,8 +30,9 @@ export const QueueButtonTooltip = memo(({ prepend = false }: Props) => {
const isLoadingDynamicPrompts = useAppSelector(
(s) => s.dynamicPrompts.isLoading
);
const { autoAddBoardId, promptsCount, iterations } =
useAppSelector(tooltipSelector);
const promptsCount = useAppSelector(selectPromptsCount);
const iterations = useAppSelector((s) => s.generation.iterations);
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
const autoAddBoardName = useBoardName(autoAddBoardId);
const [_, { isLoading }] = useEnqueueBatchMutation({
fixedCacheKey: 'enqueueBatch',
@ -77,7 +66,7 @@ export const QueueButtonTooltip = memo(({ prepend = false }: Props) => {
</InvText>
{reasons.length > 0 && (
<>
<StyledDivider />
<Divider opacity={0.2} borderColor="base.900" />
<UnorderedList>
{reasons.map((reason, i) => (
<ListItem key={`${reason}.${i}`}>
@ -87,7 +76,7 @@ export const QueueButtonTooltip = memo(({ prepend = false }: Props) => {
</UnorderedList>
</>
)}
<StyledDivider />
<Divider opacity={0.2} borderColor="base.900" />
<InvText fontStyle="oblique 10deg">
{t('parameters.invoke.addingImagesTo')}{' '}
<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 { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { setRefinerCFGScale } from 'features/sdxl/store/sdxlSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { memo, useCallback } from 'react';
import { memo, useCallback, useMemo } from 'react';
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 refinerCFGScale = useAppSelector((s) => s.sdxl.refinerCFGScale);
const { marks, min, inputMax, sliderMax, coarseStep, fineStep, initial } =
useAppSelector(selector);
const dispatch = useAppDispatch();
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(
(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 { useGetMainModelsQuery } from 'services/api/endpoints/models';
const selector = createMemoizedSelector(selectSdxlSlice, (sdxl) => ({
model: sdxl.refinerModel,
}));
const selectModel = createMemoizedSelector(
selectSdxlSlice,
(sdxl) => sdxl.refinerModel
);
const optionsFilter = (model: MainModelConfigEntity) =>
model.base_model === 'sdxl-refiner';
const ParamSDXLRefinerModelSelect = () => {
const dispatch = useAppDispatch();
const { model } = useAppSelector(selector);
const model = useAppSelector(selectModel);
const { t } = useTranslation();
const { data, isLoading } = useGetMainModelsQuery(REFINER_BASE_MODELS);
const _onChange = useCallback(

View File

@ -1,32 +1,25 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { setRefinerSteps } from 'features/sdxl/store/sdxlSlice';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { memo, useCallback } from 'react';
import { memo, useCallback, useMemo } from 'react';
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 { initial, min, sliderMax, inputMax, step, fineStep, marks } =
useAppSelector(selector);
const refinerSteps = useAppSelector((s) => s.sdxl.refinerSteps);
const dispatch = useAppDispatch();
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(
(v: number) => {
@ -42,7 +35,7 @@ const ParamSDXLRefinerSteps = () => {
defaultValue={initial}
min={min}
max={sliderMax}
step={step}
step={coarseStep}
fineStep={fineStep}
onChange={onChange}
withNumberInput

View File

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

View File

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

View File

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

View File

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

View File

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