fix(ui): modals not staying open

TBH not sure exactly why this broke. Fixed by rollback back the use of a render prop in favor of global state. Also revised the API of `useBoolean` and `buildUseBoolean`.
This commit is contained in:
psychedelicious 2024-08-30 18:40:51 +10:00
parent 2e84327ca4
commit ab11d9af8e
10 changed files with 138 additions and 157 deletions

View File

@ -1,52 +1,53 @@
import { useStore } from '@nanostores/react';
import type { WritableAtom } from 'nanostores'; import type { WritableAtom } from 'nanostores';
import { useCallback, useMemo, useState } from 'react'; import { atom } from 'nanostores';
export const useBoolean = (initialValue: boolean) => { type UseBoolean = {
const [isTrue, set] = useState(initialValue); isTrue: boolean;
const setTrue = useCallback(() => set(true), []); setTrue: () => void;
const setFalse = useCallback(() => set(false), []); setFalse: () => void;
const toggle = useCallback(() => set((v) => !v), []); set: (value: boolean) => void;
toggle: () => void;
};
const api = useMemo( /**
() => ({ * Creates a hook to manage a boolean state. The boolean is stored in a nanostores atom.
* Returns a tuple containing the hook and the atom. Use this for global boolean state.
* @param initialValue Initial value of the boolean
*/
export const buildUseBoolean = (initialValue: boolean): [() => UseBoolean, WritableAtom<boolean>] => {
const $boolean = atom(initialValue);
const setTrue = () => {
$boolean.set(true);
};
const setFalse = () => {
$boolean.set(false);
};
const set = (value: boolean) => {
$boolean.set(value);
};
const toggle = () => {
$boolean.set(!$boolean.get());
};
const useBoolean = () => {
const isTrue = useStore($boolean);
return {
isTrue, isTrue,
set,
setTrue, setTrue,
setFalse, setFalse,
set,
toggle, toggle,
}), };
[isTrue, set, setTrue, setFalse, toggle]
);
return api;
};
export const buildUseBoolean = ($boolean: WritableAtom<boolean>) => {
return () => {
const setTrue = useCallback(() => {
$boolean.set(true);
}, []);
const setFalse = useCallback(() => {
$boolean.set(false);
}, []);
const set = useCallback((value: boolean) => {
$boolean.set(value);
}, []);
const toggle = useCallback(() => {
$boolean.set(!$boolean.get());
}, []);
const api = useMemo(
() => ({
setTrue,
setFalse,
set,
toggle,
$boolean,
}),
[set, setFalse, setTrue, toggle]
);
return api;
}; };
return [useBoolean, $boolean] as const;
}; };
/**
* Hook to manage a boolean state. Use this for a local boolean state.
* @param initialValue Initial value of the boolean
*/
export const useBoolean = (initialValue: boolean) => buildUseBoolean(initialValue)[0]();

View File

@ -4,6 +4,7 @@ import { Flex } from '@invoke-ai/ui-library';
import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { AddNodeCmdk } from 'features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk'; import { AddNodeCmdk } from 'features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk';
import TopPanel from 'features/nodes/components/flow/panels/TopPanel/TopPanel'; import TopPanel from 'features/nodes/components/flow/panels/TopPanel/TopPanel';
import WorkflowEditorSettings from 'features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings';
import { LoadWorkflowFromGraphModal } from 'features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal'; import { LoadWorkflowFromGraphModal } from 'features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal';
import { SaveWorkflowAsDialog } from 'features/workflowLibrary/components/SaveWorkflowAsDialog/SaveWorkflowAsDialog'; import { SaveWorkflowAsDialog } from 'features/workflowLibrary/components/SaveWorkflowAsDialog/SaveWorkflowAsDialog';
import { memo } from 'react'; import { memo } from 'react';
@ -39,6 +40,7 @@ const NodeEditor = () => {
<LoadWorkflowFromGraphModal /> <LoadWorkflowFromGraphModal />
</> </>
)} )}
<WorkflowEditorSettings />
{isLoading && <IAINoContentFallback label={t('nodes.loadingNodes')} icon={MdDeviceHub} />} {isLoading && <IAINoContentFallback label={t('nodes.loadingNodes')} icon={MdDeviceHub} />}
</Flex> </Flex>
); );

View File

@ -163,7 +163,6 @@ const cmdkRootSx: SystemStyleObject = {
export const AddNodeCmdk = memo(() => { export const AddNodeCmdk = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const addNodeCmdk = useAddNodeCmdk(); const addNodeCmdk = useAddNodeCmdk();
const addNodeCmdkIsOpen = useStore(addNodeCmdk.$boolean);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const addNode = useAddNode(); const addNode = useAddNode();
@ -192,7 +191,7 @@ export const AddNodeCmdk = memo(() => {
return ( return (
<Modal <Modal
isOpen={addNodeCmdkIsOpen} isOpen={addNodeCmdk.isTrue}
onClose={onClose} onClose={onClose}
useInert={false} useInert={false}
initialFocusRef={inputRef} initialFocusRef={inputRef}

View File

@ -14,9 +14,9 @@ import {
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
Switch, Switch,
useDisclosure,
} from '@invoke-ai/ui-library'; } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { buildUseBoolean } from 'common/hooks/useBoolean';
import ReloadNodeTemplatesButton from 'features/nodes/components/flow/panels/TopRightPanel/ReloadSchemaButton'; import ReloadNodeTemplatesButton from 'features/nodes/components/flow/panels/TopRightPanel/ReloadSchemaButton';
import { import {
selectionModeChanged, selectionModeChanged,
@ -32,20 +32,17 @@ import {
shouldSnapToGridChanged, shouldSnapToGridChanged,
shouldValidateGraphChanged, shouldValidateGraphChanged,
} from 'features/nodes/store/workflowSettingsSlice'; } from 'features/nodes/store/workflowSettingsSlice';
import type { ChangeEvent, ReactNode } from 'react'; import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { SelectionMode } from 'reactflow'; import { SelectionMode } from 'reactflow';
const formLabelProps: FormLabelProps = { flexGrow: 1 }; const formLabelProps: FormLabelProps = { flexGrow: 1 };
export const [useWorkflowEditorSettingsModal] = buildUseBoolean(false);
type Props = { const WorkflowEditorSettings = () => {
children: (props: { onOpen: () => void }) => ReactNode;
};
const WorkflowEditorSettings = ({ children }: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const modal = useWorkflowEditorSettingsModal();
const shouldSnapToGrid = useAppSelector(selectShouldSnapToGrid); const shouldSnapToGrid = useAppSelector(selectShouldSnapToGrid);
const selectionMode = useAppSelector(selectSelectionMode); const selectionMode = useAppSelector(selectSelectionMode);
@ -99,76 +96,72 @@ const WorkflowEditorSettings = ({ children }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<> <Modal isOpen={modal.isTrue} onClose={modal.setFalse} size="2xl" isCentered useInert={false}>
{children({ onOpen })} <ModalOverlay />
<ModalContent>
<Modal isOpen={isOpen} onClose={onClose} size="2xl" isCentered useInert={false}> <ModalHeader>{t('nodes.workflowSettings')}</ModalHeader>
<ModalOverlay /> <ModalCloseButton />
<ModalContent> <ModalBody>
<ModalHeader>{t('nodes.workflowSettings')}</ModalHeader> <Flex flexDirection="column" gap={4} py={4}>
<ModalCloseButton /> <Heading size="sm">{t('parameters.general')}</Heading>
<ModalBody> <FormControlGroup orientation="vertical" formLabelProps={formLabelProps}>
<Flex flexDirection="column" gap={4} py={4}> <FormControl>
<Heading size="sm">{t('parameters.general')}</Heading> <Flex w="full">
<FormControlGroup orientation="vertical" formLabelProps={formLabelProps}> <FormLabel>{t('nodes.animatedEdges')}</FormLabel>
<FormControl> <Switch onChange={handleChangeShouldAnimate} isChecked={shouldAnimateEdges} />
<Flex w="full"> </Flex>
<FormLabel>{t('nodes.animatedEdges')}</FormLabel> <FormHelperText>{t('nodes.animatedEdgesHelp')}</FormHelperText>
<Switch onChange={handleChangeShouldAnimate} isChecked={shouldAnimateEdges} /> </FormControl>
</Flex> <Divider />
<FormHelperText>{t('nodes.animatedEdgesHelp')}</FormHelperText> <FormControl>
</FormControl> <Flex w="full">
<Divider /> <FormLabel>{t('nodes.snapToGrid')}</FormLabel>
<FormControl> <Switch isChecked={shouldSnapToGrid} onChange={handleChangeShouldSnap} />
<Flex w="full"> </Flex>
<FormLabel>{t('nodes.snapToGrid')}</FormLabel> <FormHelperText>{t('nodes.snapToGridHelp')}</FormHelperText>
<Switch isChecked={shouldSnapToGrid} onChange={handleChangeShouldSnap} /> </FormControl>
</Flex> <Divider />
<FormHelperText>{t('nodes.snapToGridHelp')}</FormHelperText> <FormControl>
</FormControl> <Flex w="full">
<Divider /> <FormLabel>{t('nodes.colorCodeEdges')}</FormLabel>
<FormControl> <Switch isChecked={shouldColorEdges} onChange={handleChangeShouldColor} />
<Flex w="full"> </Flex>
<FormLabel>{t('nodes.colorCodeEdges')}</FormLabel> <FormHelperText>{t('nodes.colorCodeEdgesHelp')}</FormHelperText>
<Switch isChecked={shouldColorEdges} onChange={handleChangeShouldColor} /> </FormControl>
</Flex> <Divider />
<FormHelperText>{t('nodes.colorCodeEdgesHelp')}</FormHelperText> <FormControl>
</FormControl> <Flex w="full">
<Divider /> <FormLabel>{t('nodes.fullyContainNodes')}</FormLabel>
<FormControl> <Switch isChecked={selectionMode === SelectionMode.Full} onChange={handleChangeSelectionMode} />
<Flex w="full"> </Flex>
<FormLabel>{t('nodes.fullyContainNodes')}</FormLabel> <FormHelperText>{t('nodes.fullyContainNodesHelp')}</FormHelperText>
<Switch isChecked={selectionMode === SelectionMode.Full} onChange={handleChangeSelectionMode} /> </FormControl>
</Flex> <Divider />
<FormHelperText>{t('nodes.fullyContainNodesHelp')}</FormHelperText> <FormControl>
</FormControl> <Flex w="full">
<Divider /> <FormLabel>{t('nodes.showEdgeLabels')}</FormLabel>
<FormControl> <Switch isChecked={shouldShowEdgeLabels} onChange={handleChangeShouldShowEdgeLabels} />
<Flex w="full"> </Flex>
<FormLabel>{t('nodes.showEdgeLabels')}</FormLabel> <FormHelperText>{t('nodes.showEdgeLabelsHelp')}</FormHelperText>
<Switch isChecked={shouldShowEdgeLabels} onChange={handleChangeShouldShowEdgeLabels} /> </FormControl>
</Flex> <Divider />
<FormHelperText>{t('nodes.showEdgeLabelsHelp')}</FormHelperText> <Heading size="sm" pt={4}>
</FormControl> {t('common.advanced')}
<Divider /> </Heading>
<Heading size="sm" pt={4}> <FormControl>
{t('common.advanced')} <Flex w="full">
</Heading> <FormLabel>{t('nodes.validateConnections')}</FormLabel>
<FormControl> <Switch isChecked={shouldValidateGraph} onChange={handleChangeShouldValidate} />
<Flex w="full"> </Flex>
<FormLabel>{t('nodes.validateConnections')}</FormLabel> <FormHelperText>{t('nodes.validateConnectionsHelp')}</FormHelperText>
<Switch isChecked={shouldValidateGraph} onChange={handleChangeShouldValidate} /> </FormControl>
</Flex> <Divider />
<FormHelperText>{t('nodes.validateConnectionsHelp')}</FormHelperText> </FormControlGroup>
</FormControl> <ReloadNodeTemplatesButton />
<Divider /> </Flex>
</FormControlGroup> </ModalBody>
<ReloadNodeTemplatesButton /> </ModalContent>
</Flex> </Modal>
</ModalBody>
</ModalContent>
</Modal>
</>
); );
}; };

View File

@ -2,12 +2,12 @@ import { useStore } from '@nanostores/react';
import { useAppStore } from 'app/store/storeHooks'; import { useAppStore } from 'app/store/storeHooks';
import { $mouseOverNode } from 'features/nodes/hooks/useMouseOverNode'; import { $mouseOverNode } from 'features/nodes/hooks/useMouseOverNode';
import { import {
$addNodeCmdk,
$didUpdateEdge, $didUpdateEdge,
$edgePendingUpdate, $edgePendingUpdate,
$pendingConnection, $pendingConnection,
$templates, $templates,
edgesChanged, edgesChanged,
useAddNodeCmdk,
} from 'features/nodes/store/nodesSlice'; } from 'features/nodes/store/nodesSlice';
import { selectNodes, selectNodesSlice } from 'features/nodes/store/selectors'; import { selectNodes, selectNodesSlice } from 'features/nodes/store/selectors';
import { getFirstValidConnection } from 'features/nodes/store/util/getFirstValidConnection'; import { getFirstValidConnection } from 'features/nodes/store/util/getFirstValidConnection';
@ -21,6 +21,7 @@ export const useConnection = () => {
const store = useAppStore(); const store = useAppStore();
const templates = useStore($templates); const templates = useStore($templates);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const addNodeCmdk = useAddNodeCmdk();
const onConnectStart = useCallback<OnConnectStart>( const onConnectStart = useCallback<OnConnectStart>(
(event, { nodeId, handleId, handleType }) => { (event, { nodeId, handleId, handleType }) => {
@ -107,9 +108,9 @@ export const useConnection = () => {
$pendingConnection.set(null); $pendingConnection.set(null);
} else { } else {
// The mouse is not over a node - we should open the add node popover // The mouse is not over a node - we should open the add node popover
$addNodeCmdk.set(true); addNodeCmdk.setTrue();
} }
}, [store, templates, updateNodeInternals]); }, [addNodeCmdk, store, templates, updateNodeInternals]);
const api = useMemo(() => ({ onConnectStart, onConnect, onConnectEnd }), [onConnectStart, onConnect, onConnectEnd]); const api = useMemo(() => ({ onConnectStart, onConnect, onConnectEnd }), [onConnectStart, onConnect, onConnectEnd]);
return api; return api;

View File

@ -444,8 +444,7 @@ export const $didUpdateEdge = atom(false);
export const $lastEdgeUpdateMouseEvent = atom<MouseEvent | null>(null); export const $lastEdgeUpdateMouseEvent = atom<MouseEvent | null>(null);
export const $viewport = atom<Viewport>({ x: 0, y: 0, zoom: 1 }); export const $viewport = atom<Viewport>({ x: 0, y: 0, zoom: 1 });
export const $addNodeCmdk = atom(false); export const [useAddNodeCmdk, $addNodeCmdk] = buildUseBoolean(false);
export const useAddNodeCmdk = buildUseBoolean($addNodeCmdk);
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const migrateNodesState = (state: any): any => { const migrateNodesState = (state: any): any => {

View File

@ -5,19 +5,16 @@ import { useAppDispatch } from 'app/store/storeHooks';
import { buildUseBoolean } from 'common/hooks/useBoolean'; import { buildUseBoolean } from 'common/hooks/useBoolean';
import { listCursorChanged, listPriorityChanged } from 'features/queue/store/queueSlice'; import { listCursorChanged, listPriorityChanged } from 'features/queue/store/queueSlice';
import { toast } from 'features/toast/toast'; import { toast } from 'features/toast/toast';
import { atom } from 'nanostores';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useClearQueueMutation, useGetQueueStatusQuery } from 'services/api/endpoints/queue'; import { useClearQueueMutation, useGetQueueStatusQuery } from 'services/api/endpoints/queue';
const $boolean = atom(false); const [useClearQueueConfirmationAlertDialog] = buildUseBoolean(false);
const useClearQueueConfirmationAlertDialog = buildUseBoolean($boolean);
export const useClearQueue = () => { export const useClearQueue = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const dialog = useClearQueueConfirmationAlertDialog(); const dialog = useClearQueueConfirmationAlertDialog();
const isOpen = useStore(dialog.$boolean);
const { data: queueStatus } = useGetQueueStatusQuery(); const { data: queueStatus } = useGetQueueStatusQuery();
const isConnected = useStore($isConnected); const isConnected = useStore($isConnected);
const [trigger, { isLoading }] = useClearQueueMutation({ const [trigger, { isLoading }] = useClearQueueMutation({
@ -51,7 +48,7 @@ export const useClearQueue = () => {
return { return {
clearQueue, clearQueue,
isOpen, isOpen: dialog.isTrue,
openDialog: dialog.setTrue, openDialog: dialog.setTrue,
closeDialog: dialog.setFalse, closeDialog: dialog.setFalse,
isLoading, isLoading,

View File

@ -8,31 +8,27 @@ import {
ModalOverlay, ModalOverlay,
Text, Text,
} from '@invoke-ai/ui-library'; } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { buildUseBoolean } from 'common/hooks/useBoolean'; import { buildUseBoolean } from 'common/hooks/useBoolean';
import { atom } from 'nanostores';
import { memo, useEffect, useState } from 'react'; import { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const $refreshAfterResetModalState = atom(false); export const [useRefreshAfterResetModal] = buildUseBoolean(false);
export const useRefreshAfterResetModal = buildUseBoolean($refreshAfterResetModalState);
const RefreshAfterResetModal = () => { const RefreshAfterResetModal = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [countdown, setCountdown] = useState(3); const [countdown, setCountdown] = useState(3);
const refreshModal = useRefreshAfterResetModal(); const refreshModal = useRefreshAfterResetModal();
const isOpen = useStore(refreshModal.$boolean);
useEffect(() => { useEffect(() => {
if (!isOpen) { if (!refreshModal.isTrue) {
return; return;
} }
const i = window.setInterval(() => setCountdown((prev) => prev - 1), 1000); const i = window.setInterval(() => setCountdown((prev) => prev - 1), 1000);
return () => { return () => {
window.clearInterval(i); window.clearInterval(i);
}; };
}, [isOpen]); }, [refreshModal.isTrue]);
useEffect(() => { useEffect(() => {
if (countdown <= 0) { if (countdown <= 0) {
@ -44,7 +40,7 @@ const RefreshAfterResetModal = () => {
<> <>
<Modal <Modal
closeOnOverlayClick={false} closeOnOverlayClick={false}
isOpen={isOpen} isOpen={refreshModal.isTrue}
onClose={refreshModal.setFalse} onClose={refreshModal.setFalse}
isCentered isCentered
closeOnEsc={false} closeOnEsc={false}

View File

@ -14,7 +14,6 @@ import {
Switch, Switch,
Text, Text,
} from '@invoke-ai/ui-library'; } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
@ -42,7 +41,6 @@ import {
} from 'features/system/store/systemSlice'; } from 'features/system/store/systemSlice';
import { selectShouldShowProgressInViewer } from 'features/ui/store/uiSelectors'; import { selectShouldShowProgressInViewer } from 'features/ui/store/uiSelectors';
import { setShouldShowProgressInViewer } from 'features/ui/store/uiSlice'; import { setShouldShowProgressInViewer } from 'features/ui/store/uiSlice';
import { atom } from 'nanostores';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import { memo, useCallback, useEffect } from 'react'; import { memo, useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -68,8 +66,7 @@ type SettingsModalProps = {
config?: ConfigOptions; config?: ConfigOptions;
}; };
const $settingsModal = atom(false); export const [useSettingsModal] = buildUseBoolean(false);
export const useSettingsModal = buildUseBoolean($settingsModal);
const SettingsModal = ({ config = defaultConfig }: SettingsModalProps) => { const SettingsModal = ({ config = defaultConfig }: SettingsModalProps) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -96,7 +93,6 @@ const SettingsModal = ({ config = defaultConfig }: SettingsModalProps) => {
refetchIntermediatesCount, refetchIntermediatesCount,
} = useClearIntermediates(Boolean(config?.shouldShowClearIntermediates)); } = useClearIntermediates(Boolean(config?.shouldShowClearIntermediates));
const settingsModal = useSettingsModal(); const settingsModal = useSettingsModal();
const settingsModalIsOpen = useStore(settingsModal.$boolean);
const refreshModal = useRefreshAfterResetModal(); const refreshModal = useRefreshAfterResetModal();
const shouldUseCpuNoise = useAppSelector(selectShouldUseCPUNoise); const shouldUseCpuNoise = useAppSelector(selectShouldUseCPUNoise);
@ -110,10 +106,10 @@ const SettingsModal = ({ config = defaultConfig }: SettingsModalProps) => {
const clearStorage = useClearStorage(); const clearStorage = useClearStorage();
useEffect(() => { useEffect(() => {
if (settingsModalIsOpen && Boolean(config?.shouldShowClearIntermediates)) { if (settingsModal.isTrue && Boolean(config?.shouldShowClearIntermediates)) {
refetchIntermediatesCount(); refetchIntermediatesCount();
} }
}, [config?.shouldShowClearIntermediates, refetchIntermediatesCount, settingsModalIsOpen]); }, [config?.shouldShowClearIntermediates, refetchIntermediatesCount, settingsModal.isTrue]);
const handleClickResetWebUI = useCallback(() => { const handleClickResetWebUI = useCallback(() => {
clearStorage(); clearStorage();
@ -165,7 +161,7 @@ const SettingsModal = ({ config = defaultConfig }: SettingsModalProps) => {
); );
return ( return (
<Modal isOpen={settingsModalIsOpen} onClose={settingsModal.setFalse} size="2xl" isCentered useInert={false}> <Modal isOpen={settingsModal.isTrue} onClose={settingsModal.setFalse} size="2xl" isCentered useInert={false}>
<ModalOverlay /> <ModalOverlay />
<ModalContent maxH="80vh" h="68rem"> <ModalContent maxH="80vh" h="68rem">
<ModalHeader bg="none">{t('common.settingsLabel')}</ModalHeader> <ModalHeader bg="none">{t('common.settingsLabel')}</ModalHeader>

View File

@ -1,20 +1,17 @@
import { MenuItem } from '@invoke-ai/ui-library'; import { MenuItem } from '@invoke-ai/ui-library';
import WorkflowEditorSettings from 'features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings'; import { useWorkflowEditorSettingsModal } from 'features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings';
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { RiSettings4Line } from 'react-icons/ri'; import { RiSettings4Line } from 'react-icons/ri';
const DownloadWorkflowMenuItem = () => { const DownloadWorkflowMenuItem = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const modal = useWorkflowEditorSettingsModal();
return ( return (
<WorkflowEditorSettings> <MenuItem as="button" icon={<RiSettings4Line />} onClick={modal.setTrue}>
{({ onOpen }) => ( {t('nodes.workflowSettings')}
<MenuItem as="button" icon={<RiSettings4Line />} onClick={onOpen}> </MenuItem>
{t('nodes.workflowSettings')}
</MenuItem>
)}
</WorkflowEditorSettings>
); );
}; };