From 2013a1f87796ea09624c6ff47d43b6f0bfae5d03 Mon Sep 17 00:00:00 2001 From: Treycos Date: Wed, 7 Aug 2024 20:15:05 +0200 Subject: [PATCH] Refactoring: Moved First run components to Typescript function components --- frontend/src/App/State/SettingsAppState.ts | 4 +- .../FirstRun/AuthenticationRequiredModal.js | 34 --- .../FirstRun/AuthenticationRequiredModal.tsx | 27 +++ .../AuthenticationRequiredModalContent.js | 170 --------------- .../AuthenticationRequiredModalContent.tsx | 194 ++++++++++++++++++ ...enticationRequiredModalContentConnector.js | 86 -------- frontend/src/typings/inputs.ts | 6 +- 7 files changed, 228 insertions(+), 293 deletions(-) delete mode 100644 frontend/src/FirstRun/AuthenticationRequiredModal.js create mode 100644 frontend/src/FirstRun/AuthenticationRequiredModal.tsx delete mode 100644 frontend/src/FirstRun/AuthenticationRequiredModalContent.js create mode 100644 frontend/src/FirstRun/AuthenticationRequiredModalContent.tsx delete mode 100644 frontend/src/FirstRun/AuthenticationRequiredModalContentConnector.js diff --git a/frontend/src/App/State/SettingsAppState.ts b/frontend/src/App/State/SettingsAppState.ts index ddca5b2ba..ac08aa127 100644 --- a/frontend/src/App/State/SettingsAppState.ts +++ b/frontend/src/App/State/SettingsAppState.ts @@ -24,7 +24,9 @@ export interface DownloadClientAppState isTestingAll: boolean; } -export type GeneralAppState = AppSectionItemState; +export interface GeneralAppState + extends AppSectionItemState, + AppSectionSaveState {} export interface ImportListAppState extends AppSectionState, diff --git a/frontend/src/FirstRun/AuthenticationRequiredModal.js b/frontend/src/FirstRun/AuthenticationRequiredModal.js deleted file mode 100644 index caa855cb7..000000000 --- a/frontend/src/FirstRun/AuthenticationRequiredModal.js +++ /dev/null @@ -1,34 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import Modal from 'Components/Modal/Modal'; -import { sizes } from 'Helpers/Props'; -import AuthenticationRequiredModalContentConnector from './AuthenticationRequiredModalContentConnector'; - -function onModalClose() { - // No-op -} - -function AuthenticationRequiredModal(props) { - const { - isOpen - } = props; - - return ( - - - - ); -} - -AuthenticationRequiredModal.propTypes = { - isOpen: PropTypes.bool.isRequired -}; - -export default AuthenticationRequiredModal; diff --git a/frontend/src/FirstRun/AuthenticationRequiredModal.tsx b/frontend/src/FirstRun/AuthenticationRequiredModal.tsx new file mode 100644 index 000000000..1b4b519ce --- /dev/null +++ b/frontend/src/FirstRun/AuthenticationRequiredModal.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import Modal from 'Components/Modal/Modal'; +import { sizes } from 'Helpers/Props'; +import AuthenticationRequiredModalContent from './AuthenticationRequiredModalContent'; + +function onModalClose() { + // No-op +} + +interface AuthenticationRequiredModalProps { + isOpen: boolean; +} + +export default function AuthenticationRequiredModal({ + isOpen, +}: AuthenticationRequiredModalProps) { + return ( + + + + ); +} diff --git a/frontend/src/FirstRun/AuthenticationRequiredModalContent.js b/frontend/src/FirstRun/AuthenticationRequiredModalContent.js deleted file mode 100644 index f3646bc96..000000000 --- a/frontend/src/FirstRun/AuthenticationRequiredModalContent.js +++ /dev/null @@ -1,170 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { useEffect, useRef } from 'react'; -import Alert from 'Components/Alert'; -import FormGroup from 'Components/Form/FormGroup'; -import FormInputGroup from 'Components/Form/FormInputGroup'; -import FormLabel from 'Components/Form/FormLabel'; -import SpinnerButton from 'Components/Link/SpinnerButton'; -import LoadingIndicator from 'Components/Loading/LoadingIndicator'; -import ModalBody from 'Components/Modal/ModalBody'; -import ModalContent from 'Components/Modal/ModalContent'; -import ModalFooter from 'Components/Modal/ModalFooter'; -import ModalHeader from 'Components/Modal/ModalHeader'; -import { inputTypes, kinds } from 'Helpers/Props'; -import { authenticationMethodOptions, authenticationRequiredOptions } from 'Settings/General/SecuritySettings'; -import translate from 'Utilities/String/translate'; -import styles from './AuthenticationRequiredModalContent.css'; - -function onModalClose() { - // No-op -} - -function AuthenticationRequiredModalContent(props) { - const { - isPopulated, - error, - isSaving, - settings, - onInputChange, - onSavePress, - dispatchFetchStatus - } = props; - - const { - authenticationMethod, - authenticationRequired, - username, - password, - passwordConfirmation - } = settings; - - const authenticationEnabled = authenticationMethod && authenticationMethod.value !== 'none'; - - const didMount = useRef(false); - - useEffect(() => { - if (!isSaving && didMount.current) { - dispatchFetchStatus(); - } - - didMount.current = true; - }, [isSaving, dispatchFetchStatus]); - - return ( - - - {translate('AuthenticationRequired')} - - - - - {translate('AuthenticationRequiredWarning')} - - - { - isPopulated && !error ? -
- - {translate('AuthenticationMethod')} - - - - - - {translate('AuthenticationRequired')} - - - - - - {translate('Username')} - - - - - - {translate('Password')} - - - - - - {translate('PasswordConfirmation')} - - - -
: - null - } - - { - !isPopulated && !error ? : null - } -
- - - - {translate('Save')} - - -
- ); -} - -AuthenticationRequiredModalContent.propTypes = { - isPopulated: PropTypes.bool.isRequired, - error: PropTypes.object, - isSaving: PropTypes.bool.isRequired, - saveError: PropTypes.object, - settings: PropTypes.object.isRequired, - onInputChange: PropTypes.func.isRequired, - onSavePress: PropTypes.func.isRequired, - dispatchFetchStatus: PropTypes.func.isRequired -}; - -export default AuthenticationRequiredModalContent; diff --git a/frontend/src/FirstRun/AuthenticationRequiredModalContent.tsx b/frontend/src/FirstRun/AuthenticationRequiredModalContent.tsx new file mode 100644 index 000000000..092406aaf --- /dev/null +++ b/frontend/src/FirstRun/AuthenticationRequiredModalContent.tsx @@ -0,0 +1,194 @@ +import React, { useCallback, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import Alert from 'Components/Alert'; +import FormGroup from 'Components/Form/FormGroup'; +import FormInputGroup from 'Components/Form/FormInputGroup'; +import FormLabel from 'Components/Form/FormLabel'; +import SpinnerButton from 'Components/Link/SpinnerButton'; +import LoadingIndicator from 'Components/Loading/LoadingIndicator'; +import ModalBody from 'Components/Modal/ModalBody'; +import ModalContent from 'Components/Modal/ModalContent'; +import ModalFooter from 'Components/Modal/ModalFooter'; +import ModalHeader from 'Components/Modal/ModalHeader'; +import usePrevious from 'Helpers/Hooks/usePrevious'; +import { inputTypes, kinds } from 'Helpers/Props'; +import { + authenticationMethodOptions, + authenticationRequiredOptions, +} from 'Settings/General/SecuritySettings'; +import { clearPendingChanges } from 'Store/Actions/baseActions'; +import { + fetchGeneralSettings, + saveGeneralSettings, + setGeneralSettingsValue, +} from 'Store/Actions/settingsActions'; +import { fetchStatus } from 'Store/Actions/systemActions'; +import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector'; +import { InputChanged } from 'typings/inputs'; +import translate from 'Utilities/String/translate'; +import styles from './AuthenticationRequiredModalContent.css'; + +const SECTION = 'general'; + +const selector = createSettingsSectionSelector(SECTION); + +function onModalClose() { + // No-op +} + +export default function AuthenticationRequiredModalContent() { + const { isPopulated, error, isSaving, settings } = useSelector(selector); + const dispatch = useDispatch(); + + const { + authenticationMethod, + authenticationRequired, + username, + password, + passwordConfirmation, + } = settings; + + const wasSaving = usePrevious(isSaving); + + useEffect(() => { + dispatch(fetchGeneralSettings()); + + return () => { + dispatch(clearPendingChanges()); + }; + }, [dispatch]); + + const onInputChange = useCallback( + (args: InputChanged) => { + // @ts-expect-error Actions aren't typed + dispatch(setGeneralSettingsValue(args)); + }, + [dispatch] + ); + + const authenticationEnabled = + authenticationMethod && authenticationMethod.value !== 'none'; + + useEffect(() => { + if (isSaving || !wasSaving) { + return; + } + + dispatch(fetchStatus()); + }, [isSaving, wasSaving, dispatch]); + + const onPress = useCallback(() => { + dispatch(saveGeneralSettings()); + }, [dispatch]); + + return ( + + {translate('AuthenticationRequired')} + + + + {translate('AuthenticationRequiredWarning')} + + + {isPopulated && !error ? ( +
+ + {translate('AuthenticationMethod')} + + + + + + {translate('AuthenticationRequired')} + + + + + + {translate('Username')} + + + + + + {translate('Password')} + + + + + + {translate('PasswordConfirmation')} + + + +
+ ) : null} + + {!isPopulated && !error ? : null} +
+ + + + {translate('Save')} + + +
+ ); +} diff --git a/frontend/src/FirstRun/AuthenticationRequiredModalContentConnector.js b/frontend/src/FirstRun/AuthenticationRequiredModalContentConnector.js deleted file mode 100644 index 6653a9d34..000000000 --- a/frontend/src/FirstRun/AuthenticationRequiredModalContentConnector.js +++ /dev/null @@ -1,86 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { clearPendingChanges } from 'Store/Actions/baseActions'; -import { fetchGeneralSettings, saveGeneralSettings, setGeneralSettingsValue } from 'Store/Actions/settingsActions'; -import { fetchStatus } from 'Store/Actions/systemActions'; -import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector'; -import AuthenticationRequiredModalContent from './AuthenticationRequiredModalContent'; - -const SECTION = 'general'; - -function createMapStateToProps() { - return createSelector( - createSettingsSectionSelector(SECTION), - (sectionSettings) => { - return { - ...sectionSettings - }; - } - ); -} - -const mapDispatchToProps = { - dispatchClearPendingChanges: clearPendingChanges, - dispatchSetGeneralSettingsValue: setGeneralSettingsValue, - dispatchSaveGeneralSettings: saveGeneralSettings, - dispatchFetchGeneralSettings: fetchGeneralSettings, - dispatchFetchStatus: fetchStatus -}; - -class AuthenticationRequiredModalContentConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - this.props.dispatchFetchGeneralSettings(); - } - - componentWillUnmount() { - this.props.dispatchClearPendingChanges({ section: `settings.${SECTION}` }); - } - - // - // Listeners - - onInputChange = ({ name, value }) => { - this.props.dispatchSetGeneralSettingsValue({ name, value }); - }; - - onSavePress = () => { - this.props.dispatchSaveGeneralSettings(); - }; - - // - // Render - - render() { - const { - dispatchClearPendingChanges, - dispatchFetchGeneralSettings, - dispatchSetGeneralSettingsValue, - dispatchSaveGeneralSettings, - ...otherProps - } = this.props; - - return ( - - ); - } -} - -AuthenticationRequiredModalContentConnector.propTypes = { - dispatchClearPendingChanges: PropTypes.func.isRequired, - dispatchFetchGeneralSettings: PropTypes.func.isRequired, - dispatchSetGeneralSettingsValue: PropTypes.func.isRequired, - dispatchSaveGeneralSettings: PropTypes.func.isRequired, - dispatchFetchStatus: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(AuthenticationRequiredModalContentConnector); diff --git a/frontend/src/typings/inputs.ts b/frontend/src/typings/inputs.ts index c0fda305c..cf91149b6 100644 --- a/frontend/src/typings/inputs.ts +++ b/frontend/src/typings/inputs.ts @@ -1,4 +1,6 @@ -export type CheckInputChanged = { +export type InputChanged = { name: string; - value: boolean; + value: T; }; + +export type CheckInputChanged = InputChanged;