import { Flex, Heading, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Text, useDisclosure, useColorMode, } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { VALID_LOG_LEVELS } from 'app/logging/logger'; import { LOCALSTORAGE_KEYS, LOCALSTORAGE_PREFIX } from 'app/store/constants'; import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIButton from 'common/components/IAIButton'; import IAIMantineSelect from 'common/components/IAIMantineSelect'; import { setShouldShowAdvancedOptions } from 'features/parameters/store/generationSlice'; import { consoleLogLevelChanged, setEnableImageDebugging, setIsNodesEnabled, setShouldConfirmOnDelete, shouldAntialiasProgressImageChanged, shouldLogToConsoleChanged, shouldUseNSFWCheckerChanged, shouldUseWatermarkerChanged, } from 'features/system/store/systemSlice'; import { setShouldShowProgressInViewer, setShouldUseCanvasBetaLayout, setShouldUseSliders, } from 'features/ui/store/uiSlice'; import { isEqual } from 'lodash-es'; import { ChangeEvent, ReactElement, cloneElement, useCallback, useEffect, } from 'react'; import { useTranslation } from 'react-i18next'; import { LogLevelName } from 'roarr'; import { useGetAppConfigQuery } from 'services/api/endpoints/appInfo'; import SettingSwitch from './SettingSwitch'; import SettingsClearIntermediates from './SettingsClearIntermediates'; import SettingsSchedulers from './SettingsSchedulers'; import StyledFlex from './StyledFlex'; import { useFeatureStatus } from '../../hooks/useFeatureStatus'; import { LANGUAGES } from '../../store/constants'; import { languageChanged } from '../../store/systemSlice'; import { languageSelector } from '../../store/systemSelectors'; const selector = createSelector( [stateSelector], ({ system, ui, generation }) => { const { shouldConfirmOnDelete, enableImageDebugging, consoleLogLevel, shouldLogToConsole, shouldAntialiasProgressImage, isNodesEnabled, shouldUseNSFWChecker, shouldUseWatermarker, } = system; const { shouldUseCanvasBetaLayout, shouldUseSliders, shouldShowProgressInViewer, } = ui; const { shouldShowAdvancedOptions } = generation; return { shouldConfirmOnDelete, enableImageDebugging, shouldUseCanvasBetaLayout, shouldUseSliders, shouldShowProgressInViewer, consoleLogLevel, shouldLogToConsole, shouldAntialiasProgressImage, shouldShowAdvancedOptions, isNodesEnabled, shouldUseNSFWChecker, shouldUseWatermarker, }; }, { memoizeOptions: { resultEqualityCheck: isEqual }, } ); type ConfigOptions = { shouldShowDeveloperSettings: boolean; shouldShowResetWebUiText: boolean; shouldShowBetaLayout: boolean; shouldShowAdvancedOptionsSettings: boolean; shouldShowClearIntermediates: boolean; shouldShowNodesToggle: boolean; shouldShowLocalizationToggle: boolean; }; type SettingsModalProps = { /* The button to open the Settings Modal */ children: ReactElement; config?: ConfigOptions; }; const SettingsModal = ({ children, config }: SettingsModalProps) => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const shouldShowBetaLayout = config?.shouldShowBetaLayout ?? true; const shouldShowDeveloperSettings = config?.shouldShowDeveloperSettings ?? true; const shouldShowResetWebUiText = config?.shouldShowResetWebUiText ?? true; const shouldShowAdvancedOptionsSettings = config?.shouldShowAdvancedOptionsSettings ?? true; const shouldShowClearIntermediates = config?.shouldShowClearIntermediates ?? true; const shouldShowNodesToggle = config?.shouldShowNodesToggle ?? true; const shouldShowLocalizationToggle = config?.shouldShowLocalizationToggle ?? true; useEffect(() => { if (!shouldShowDeveloperSettings) { dispatch(shouldLogToConsoleChanged(false)); } }, [shouldShowDeveloperSettings, dispatch]); const { isNSFWCheckerAvailable, isWatermarkerAvailable } = useGetAppConfigQuery(undefined, { selectFromResult: ({ data }) => ({ isNSFWCheckerAvailable: data?.nsfw_methods.includes('nsfw_checker') ?? false, isWatermarkerAvailable: data?.watermarking_methods.includes('invisible_watermark') ?? false, }), }); const { isOpen: isSettingsModalOpen, onOpen: onSettingsModalOpen, onClose: onSettingsModalClose, } = useDisclosure(); const { isOpen: isRefreshModalOpen, onOpen: onRefreshModalOpen, onClose: onRefreshModalClose, } = useDisclosure(); const { shouldConfirmOnDelete, enableImageDebugging, shouldUseCanvasBetaLayout, shouldUseSliders, shouldShowProgressInViewer, consoleLogLevel, shouldLogToConsole, shouldAntialiasProgressImage, shouldShowAdvancedOptions, isNodesEnabled, shouldUseNSFWChecker, shouldUseWatermarker, } = useAppSelector(selector); const handleClickResetWebUI = useCallback(() => { // Only remove our keys Object.keys(window.localStorage).forEach((key) => { if ( LOCALSTORAGE_KEYS.includes(key) || key.startsWith(LOCALSTORAGE_PREFIX) ) { localStorage.removeItem(key); } }); onSettingsModalClose(); onRefreshModalOpen(); }, [onSettingsModalClose, onRefreshModalOpen]); const handleLogLevelChanged = useCallback( (v: string) => { dispatch(consoleLogLevelChanged(v as LogLevelName)); }, [dispatch] ); const handleLanguageChanged = useCallback( (l: string) => { dispatch(languageChanged(l as keyof typeof LANGUAGES)); }, [dispatch] ); const handleLogToConsoleChanged = useCallback( (e: ChangeEvent) => { dispatch(shouldLogToConsoleChanged(e.target.checked)); }, [dispatch] ); const handleToggleNodes = useCallback( (e: ChangeEvent) => { dispatch(setIsNodesEnabled(e.target.checked)); }, [dispatch] ); const { colorMode, toggleColorMode } = useColorMode(); const isLocalizationEnabled = useFeatureStatus('localization').isFeatureEnabled; const language = useAppSelector(languageSelector); return ( <> {cloneElement(children, { onClick: onSettingsModalOpen, })} {t('common.settingsLabel')} {t('settings.general')} ) => dispatch(setShouldConfirmOnDelete(e.target.checked)) } /> {shouldShowAdvancedOptionsSettings && ( ) => dispatch(setShouldShowAdvancedOptions(e.target.checked)) } /> )} {t('settings.generation')} ) => dispatch(shouldUseNSFWCheckerChanged(e.target.checked)) } /> ) => dispatch(shouldUseWatermarkerChanged(e.target.checked)) } /> {t('settings.ui')} ) => dispatch(setShouldUseSliders(e.target.checked)) } /> ) => dispatch(setShouldShowProgressInViewer(e.target.checked)) } /> ) => dispatch( shouldAntialiasProgressImageChanged(e.target.checked) ) } /> {shouldShowBetaLayout && ( ) => dispatch(setShouldUseCanvasBetaLayout(e.target.checked)) } /> )} {shouldShowNodesToggle && ( )} {shouldShowLocalizationToggle && ( ({ value, label, }))} onChange={handleLanguageChanged} /> )} {shouldShowDeveloperSettings && ( {t('settings.developer')} ) => dispatch(setEnableImageDebugging(e.target.checked)) } /> )} {shouldShowClearIntermediates && } {t('settings.resetWebUI')} {t('settings.resetWebUI')} {shouldShowResetWebUiText && ( <> {t('settings.resetWebUIDesc1')} {t('settings.resetWebUIDesc2')} )} {t('common.close')} {t('settings.resetComplete')} ); }; export default SettingsModal;