From 9f742a669e3d90c1ab005c3fbcffdd9fe9866d8e Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Wed, 28 Aug 2024 16:51:38 +1000
Subject: [PATCH] feat(ui): split settings modal
---
.../frontend/web/src/app/components/App.tsx | 4 +
.../SettingsModal/RefreshAfterResetModal.tsx | 72 +++++
.../components/SettingsModal/SettingsMenu.tsx | 11 +-
.../SettingsModal/SettingsModal.tsx | 282 ++++++++----------
4 files changed, 204 insertions(+), 165 deletions(-)
create mode 100644 invokeai/frontend/web/src/features/system/components/SettingsModal/RefreshAfterResetModal.tsx
diff --git a/invokeai/frontend/web/src/app/components/App.tsx b/invokeai/frontend/web/src/app/components/App.tsx
index 07f484d200..68acdb4aec 100644
--- a/invokeai/frontend/web/src/app/components/App.tsx
+++ b/invokeai/frontend/web/src/app/components/App.tsx
@@ -17,6 +17,8 @@ import { useStarterModelsToast } from 'features/modelManagerV2/hooks/useStarterM
import { ClearQueueConfirmationsAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
import { StylePresetModal } from 'features/stylePresets/components/StylePresetForm/StylePresetModal';
import { activeStylePresetIdChanged } from 'features/stylePresets/store/stylePresetSlice';
+import RefreshAfterResetModal from 'features/system/components/SettingsModal/RefreshAfterResetModal';
+import SettingsModal from 'features/system/components/SettingsModal/SettingsModal';
import { configChanged } from 'features/system/store/configSlice';
import { selectLanguage } from 'features/system/store/systemSelectors';
import { AppContent } from 'features/ui/components/AppContent';
@@ -135,6 +137,8 @@ const App = ({
+
+
);
};
diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/RefreshAfterResetModal.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/RefreshAfterResetModal.tsx
new file mode 100644
index 0000000000..2530b705d1
--- /dev/null
+++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/RefreshAfterResetModal.tsx
@@ -0,0 +1,72 @@
+import {
+ Flex,
+ Modal,
+ ModalBody,
+ ModalContent,
+ ModalFooter,
+ ModalHeader,
+ ModalOverlay,
+ Text,
+} from '@invoke-ai/ui-library';
+import { useStore } from '@nanostores/react';
+import { buildUseBoolean } from 'common/hooks/useBoolean';
+import { atom } from 'nanostores';
+import { memo, useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+
+const $refreshAfterResetModalState = atom(false);
+export const useRefreshAfterResetModal = buildUseBoolean($refreshAfterResetModalState);
+
+const RefreshAfterResetModal = () => {
+ const { t } = useTranslation();
+ const [countdown, setCountdown] = useState(3);
+
+ const refreshModal = useRefreshAfterResetModal();
+ const isOpen = useStore(refreshModal.$boolean);
+
+ useEffect(() => {
+ if (!isOpen) {
+ return;
+ }
+ const i = window.setInterval(() => setCountdown((prev) => prev - 1), 1000);
+ return () => {
+ window.clearInterval(i);
+ };
+ }, [isOpen]);
+
+ useEffect(() => {
+ if (countdown <= 0) {
+ window.location.reload();
+ }
+ }, [countdown]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {t('settings.resetComplete')} {t('settings.reloadingIn')} {countdown}...
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default memo(RefreshAfterResetModal);
diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsMenu.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsMenu.tsx
index 33455e50fa..82c8c264af 100644
--- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsMenu.tsx
+++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsMenu.tsx
@@ -25,12 +25,13 @@ import {
} from 'react-icons/pi';
import { RiDiscordFill, RiGithubFill, RiSettings4Line } from 'react-icons/ri';
-import SettingsModal from './SettingsModal';
+import { useSettingsModal } from './SettingsModal';
import { SettingsUpsellMenuItem } from './SettingsUpsellMenuItem';
const SettingsMenu = () => {
const { t } = useTranslation();
const { isOpen, onOpen, onClose } = useDisclosure();
useGlobalMenuClose(onClose);
+ const settingsModal = useSettingsModal();
const isBugLinkEnabled = useFeatureStatus('bugLink');
const isDiscordLinkEnabled = useFeatureStatus('discordLink');
@@ -75,11 +76,9 @@ const SettingsMenu = () => {
{t('common.hotkeysLabel')}
-
- }>
- {t('common.settingsLabel')}
-
-
+ }>
+ {t('common.settingsLabel')}
+
diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx
index b9569a1a5c..4ee5850654 100644
--- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx
+++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx
@@ -13,13 +13,15 @@ import {
ModalOverlay,
Switch,
Text,
- useDisclosure,
} from '@invoke-ai/ui-library';
+import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
+import { buildUseBoolean } from 'common/hooks/useBoolean';
import { useClearStorage } from 'common/hooks/useClearStorage';
import { selectShouldUseCPUNoise, shouldUseCpuNoiseChanged } from 'features/controlLayers/store/paramsSlice';
+import { useRefreshAfterResetModal } from 'features/system/components/SettingsModal/RefreshAfterResetModal';
import { SettingsDeveloperLogIsEnabled } from 'features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled';
import { SettingsDeveloperLogLevel } from 'features/system/components/SettingsModal/SettingsDeveloperLogLevel';
import { SettingsDeveloperLogNamespaces } from 'features/system/components/SettingsModal/SettingsDeveloperLogNamespaces';
@@ -40,8 +42,9 @@ import {
} from 'features/system/store/systemSlice';
import { selectShouldShowProgressInViewer } from 'features/ui/store/uiSelectors';
import { setShouldShowProgressInViewer } from 'features/ui/store/uiSlice';
-import type { ChangeEvent, ReactElement } from 'react';
-import { cloneElement, memo, useCallback, useEffect, useState } from 'react';
+import { atom } from 'nanostores';
+import type { ChangeEvent } from 'react';
+import { memo, useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetAppConfigQuery } from 'services/api/endpoints/appInfo';
@@ -54,27 +57,29 @@ type ConfigOptions = {
shouldShowLocalizationToggle?: boolean;
};
+const defaultConfig: ConfigOptions = {
+ shouldShowDeveloperSettings: true,
+ shouldShowResetWebUiText: true,
+ shouldShowClearIntermediates: true,
+ shouldShowLocalizationToggle: true,
+};
+
type SettingsModalProps = {
- /* The button to open the Settings Modal */
- children: ReactElement;
config?: ConfigOptions;
};
-const SettingsModal = ({ children, config }: SettingsModalProps) => {
+const $settingsModal = atom(false);
+export const useSettingsModal = buildUseBoolean($settingsModal);
+
+const SettingsModal = ({ config = defaultConfig }: SettingsModalProps) => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
- const [countdown, setCountdown] = useState(3);
-
- const shouldShowDeveloperSettings = config?.shouldShowDeveloperSettings ?? true;
- const shouldShowResetWebUiText = config?.shouldShowResetWebUiText ?? true;
- const shouldShowClearIntermediates = config?.shouldShowClearIntermediates ?? true;
- const shouldShowLocalizationToggle = config?.shouldShowLocalizationToggle ?? true;
useEffect(() => {
- if (!shouldShowDeveloperSettings) {
+ if (!config?.shouldShowDeveloperSettings) {
dispatch(logIsEnabledChanged(false));
}
- }, [shouldShowDeveloperSettings, dispatch]);
+ }, [dispatch, config?.shouldShowDeveloperSettings]);
const { isNSFWCheckerAvailable, isWatermarkerAvailable } = useGetAppConfigQuery(undefined, {
selectFromResult: ({ data }) => ({
@@ -89,11 +94,10 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
intermediatesCount,
isLoading: isLoadingClearIntermediates,
refetchIntermediatesCount,
- } = useClearIntermediates(shouldShowClearIntermediates);
-
- const { isOpen: isSettingsModalOpen, onOpen: _onSettingsModalOpen, onClose: onSettingsModalClose } = useDisclosure();
-
- const { isOpen: isRefreshModalOpen, onOpen: onRefreshModalOpen, onClose: onRefreshModalClose } = useDisclosure();
+ } = useClearIntermediates(Boolean(config?.shouldShowClearIntermediates));
+ const settingsModal = useSettingsModal();
+ const settingsModalIsOpen = useStore(settingsModal.$boolean);
+ const refreshModal = useRefreshAfterResetModal();
const shouldUseCpuNoise = useAppSelector(selectShouldUseCPUNoise);
const shouldConfirmOnDelete = useAppSelector(selectSystemShouldConfirmOnDelete);
@@ -105,25 +109,17 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
const clearStorage = useClearStorage();
- const handleOpenSettingsModel = useCallback(() => {
- if (shouldShowClearIntermediates) {
+ useEffect(() => {
+ if (settingsModalIsOpen && Boolean(config?.shouldShowClearIntermediates)) {
refetchIntermediatesCount();
}
- _onSettingsModalOpen();
- }, [_onSettingsModalOpen, refetchIntermediatesCount, shouldShowClearIntermediates]);
+ }, [config?.shouldShowClearIntermediates, refetchIntermediatesCount, settingsModalIsOpen]);
const handleClickResetWebUI = useCallback(() => {
clearStorage();
- onSettingsModalClose();
- onRefreshModalOpen();
- setInterval(() => setCountdown((prev) => prev - 1), 1000);
- }, [clearStorage, onSettingsModalClose, onRefreshModalOpen]);
-
- useEffect(() => {
- if (countdown <= 0) {
- window.location.reload();
- }
- }, [countdown]);
+ settingsModal.setFalse();
+ refreshModal.setTrue();
+ }, [clearStorage, settingsModal, refreshModal]);
const handleChangeShouldConfirmOnDelete = useCallback(
(e: ChangeEvent) => {
@@ -169,139 +165,107 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
);
return (
- <>
- {cloneElement(children, {
- onClick: handleOpenSettingsModel,
- })}
+
+
+
+ {t('common.settingsLabel')}
+
+
+
+
+
+
+
+ {t('settings.confirmOnDelete')}
+
+
+
-
-
-
- {t('common.settingsLabel')}
-
-
-
-
-
-
-
- {t('settings.confirmOnDelete')}
-
-
+
+
+ {t('settings.enableNSFWChecker')}
+
+
+
+ {t('settings.enableInvisibleWatermark')}
+
+
+
+
+
+
+ {t('settings.showProgressInViewer')}
+
+
+
+ {t('settings.antialiasProgressImages')}
+
+
+
+
+ {t('parameters.useCpuNoise')}
+
+
+
+ {Boolean(config?.shouldShowLocalizationToggle) && }
+
+ {t('settings.enableInformationalPopovers')}
+
+
+
+
+ {Boolean(config?.shouldShowDeveloperSettings) && (
+
+
+
+
+ )}
-
-
- {t('settings.enableNSFWChecker')}
-
-
-
- {t('settings.enableInvisibleWatermark')}
-
-
-
-
-
-
- {t('settings.showProgressInViewer')}
-
-
-
- {t('settings.antialiasProgressImages')}
-
-
-
-
- {t('parameters.useCpuNoise')}
-
-
-
- {shouldShowLocalizationToggle && }
-
- {t('settings.enableInformationalPopovers')}
-
-
-
-
- {shouldShowDeveloperSettings && (
-
-
-
-
-
- )}
-
- {shouldShowClearIntermediates && (
-
-
- {t('settings.clearIntermediatesDesc1')}
- {t('settings.clearIntermediatesDesc2')}
- {t('settings.clearIntermediatesDesc3')}
-
- )}
-
-
-
-
-
-
+ )}
-
-
-
-
-
-
-
-
-
-
-
-
- {t('settings.resetComplete')} {t('settings.reloadingIn')} {countdown}...
-
-
+
+
+ {t('settings.resetWebUI')}
+
+ {Boolean(config?.shouldShowResetWebUiText) && (
+ <>
+ {t('settings.resetWebUIDesc1')}
+ {t('settings.resetWebUIDesc2')}
+ >
+ )}
+
+
-
-
-
-
- >
+
+
+
+
+
+
);
};