import { ChakraProps, Flex, Heading, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Text, useDisclosure, } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIButton from 'common/components/IAIButton'; import IAISelect from 'common/components/IAISelect'; import IAISwitch from 'common/components/IAISwitch'; import { systemSelector } from 'features/system/store/systemSelectors'; import { consoleLogLevelChanged, setEnableImageDebugging, setShouldConfirmOnDelete, setShouldDisplayGuides, shouldAntialiasProgressImageChanged, shouldLogToConsoleChanged, SystemState, } from 'features/system/store/systemSlice'; import { uiSelector } from 'features/ui/store/uiSelectors'; import { setShouldShowProgressInViewer, setShouldUseCanvasBetaLayout, setShouldUseSliders, } from 'features/ui/store/uiSlice'; import { UIState } from 'features/ui/store/uiTypes'; import { isEqual } from 'lodash-es'; import { ChangeEvent, cloneElement, ReactElement, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { VALID_LOG_LEVELS } from 'app/logging/useLogger'; import { LogLevelName } from 'roarr'; import { LOCALSTORAGE_KEYS, LOCALSTORAGE_PREFIX } from 'app/store/constants'; import SettingsSchedulers from './SettingsSchedulers'; const selector = createSelector( [systemSelector, uiSelector], (system: SystemState, ui: UIState) => { const { shouldConfirmOnDelete, shouldDisplayGuides, enableImageDebugging, consoleLogLevel, shouldLogToConsole, shouldAntialiasProgressImage, } = system; const { shouldUseCanvasBetaLayout, shouldUseSliders, shouldShowProgressInViewer, } = ui; return { shouldConfirmOnDelete, shouldDisplayGuides, enableImageDebugging, shouldUseCanvasBetaLayout, shouldUseSliders, shouldShowProgressInViewer, consoleLogLevel, shouldLogToConsole, shouldAntialiasProgressImage, }; }, { memoizeOptions: { resultEqualityCheck: isEqual }, } ); const modalSectionStyles: ChakraProps['sx'] = { flexDirection: 'column', gap: 2, p: 4, bg: 'base.900', borderRadius: 'base', }; type SettingsModalProps = { /* The button to open the Settings Modal */ children: ReactElement; }; /** * Modal for app settings. Also provides Reset functionality in which the * app's localstorage is wiped via redux-persist. * * Secondary post-reset modal is included here. */ const SettingsModal = ({ children }: SettingsModalProps) => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const { isOpen: isSettingsModalOpen, onOpen: onSettingsModalOpen, onClose: onSettingsModalClose, } = useDisclosure(); const { isOpen: isRefreshModalOpen, onOpen: onRefreshModalOpen, onClose: onRefreshModalClose, } = useDisclosure(); const { shouldConfirmOnDelete, shouldDisplayGuides, enableImageDebugging, shouldUseCanvasBetaLayout, shouldUseSliders, shouldShowProgressInViewer, consoleLogLevel, shouldLogToConsole, shouldAntialiasProgressImage, } = 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( (e: ChangeEvent) => { dispatch(consoleLogLevelChanged(e.target.value as LogLevelName)); }, [dispatch] ); const handleLogToConsoleChanged = useCallback( (e: ChangeEvent) => { dispatch(shouldLogToConsoleChanged(e.target.checked)); }, [dispatch] ); return ( <> {cloneElement(children, { onClick: onSettingsModalOpen, })} {t('common.settingsLabel')} {t('settings.general')} ) => dispatch(setShouldConfirmOnDelete(e.target.checked)) } /> {t('settings.generation')} {t('settings.ui')} ) => dispatch(setShouldDisplayGuides(e.target.checked)) } /> ) => dispatch(setShouldUseCanvasBetaLayout(e.target.checked)) } /> ) => dispatch(setShouldUseSliders(e.target.checked)) } /> ) => dispatch(setShouldShowProgressInViewer(e.target.checked)) } /> ) => dispatch( shouldAntialiasProgressImageChanged(e.target.checked) ) } /> {t('settings.developer')} ) => dispatch(setEnableImageDebugging(e.target.checked)) } /> {t('settings.resetWebUI')} {t('settings.resetWebUI')} {t('settings.resetWebUIDesc1')} {t('settings.resetWebUIDesc2')} {t('common.close')} {t('settings.resetComplete')} ); }; export default SettingsModal;