1
0
Fork 0
mirror of https://github.com/Sonarr/Sonarr synced 2025-03-10 06:03:32 +00:00

Convert Quality Settings to TypeScript

This commit is contained in:
Mark McDowall 2024-12-28 12:10:18 -08:00
parent 89f584d1b3
commit 9ce473d9bb
No known key found for this signature in database
20 changed files with 415 additions and 548 deletions

View file

@ -20,7 +20,7 @@ import MetadataSettings from 'Settings/Metadata/MetadataSettings';
import MetadataSourceSettings from 'Settings/MetadataSource/MetadataSourceSettings';
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
import Profiles from 'Settings/Profiles/Profiles';
import QualityConnector from 'Settings/Quality/QualityConnector';
import Quality from 'Settings/Quality/Quality';
import Settings from 'Settings/Settings';
import TagSettings from 'Settings/Tags/TagSettings';
import UISettings from 'Settings/UI/UISettings';
@ -105,7 +105,7 @@ function AppRoutes() {
<Route path="/settings/profiles" component={Profiles} />
<Route path="/settings/quality" component={QualityConnector} />
<Route path="/settings/quality" component={Quality} />
<Route
path="/settings/customformats"

View file

@ -74,6 +74,14 @@ export interface NotificationAppState
extends AppSectionState<Notification>,
AppSectionDeleteState {}
export interface QualityDefinitionsAppState
extends AppSectionState<QualityProfile>,
AppSectionSaveState {
pendingChanges: {
[key: number]: Partial<QualityProfile>;
};
}
export interface QualityProfilesAppState
extends AppSectionState<QualityProfile>,
AppSectionItemSchemaState<QualityProfile> {}
@ -123,6 +131,7 @@ interface SettingsAppState {
naming: NamingAppState;
namingExamples: NamingExamplesAppState;
notifications: NotificationAppState;
qualityDefinitions: QualityDefinitionsAppState;
qualityProfiles: QualityProfilesAppState;
releaseProfiles: ReleaseProfilesAppState;
ui: UiSettingsAppState;

View file

@ -0,0 +1,8 @@
import { useSelector } from 'react-redux';
import AppState from 'App/State/AppState';
function useShowAdvancedSettings() {
return useSelector((state: AppState) => state.settings.advancedSettings);
}
export default useShowAdvancedSettings;

View file

@ -0,0 +1,11 @@
import Quality from './Quality';
export default interface QualityDefinition {
quality: Quality;
title: string;
weight: number;
minSize: number;
maxSize: number;
preferredSize: number;
id: number;
}

View file

@ -60,6 +60,7 @@ class QualityDefinitionConnector extends Component {
QualityDefinitionConnector.propTypes = {
id: PropTypes.number.isRequired,
advancedSettings: PropTypes.bool.isRequired,
minSize: PropTypes.number,
maxSize: PropTypes.number,
preferredSize: PropTypes.number,

View file

@ -1,40 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
function QualityDefinitionLimits(props) {
const {
bytes,
message
} = props;
if (!bytes) {
return <div>{message}</div>;
}
const thirty = formatBytes(bytes * 30);
const fortyFive = formatBytes(bytes * 45);
const sixty = formatBytes(bytes * 60);
return (
<div>
<div>
{translate('MinutesThirty', { thirty })}
</div>
<div>
{translate('MinutesFortyFive', { fortyFive })}
</div>
<div>
{translate('MinutesSixty', { sixty })}
</div>
</div>
);
}
QualityDefinitionLimits.propTypes = {
bytes: PropTypes.number,
message: PropTypes.string.isRequired
};
export default QualityDefinitionLimits;

View file

@ -0,0 +1,30 @@
import React from 'react';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
interface QualityDefinitionLimitsProps {
bytes?: number;
message: string;
}
function QualityDefinitionLimits(props: QualityDefinitionLimitsProps) {
const { bytes, message } = props;
if (!bytes) {
return <div>{message}</div>;
}
const thirty = formatBytes(bytes * 30);
const fortyFive = formatBytes(bytes * 45);
const sixty = formatBytes(bytes * 60);
return (
<div>
<div>{translate('MinutesThirty', { thirty })}</div>
<div>{translate('MinutesFortyFive', { fortyFive })}</div>
<div>{translate('MinutesSixty', { sixty })}</div>
</div>
);
}
export default QualityDefinitionLimits;

View file

@ -1,80 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import FieldSet from 'Components/FieldSet';
import PageSectionContent from 'Components/Page/PageSectionContent';
import translate from 'Utilities/String/translate';
import QualityDefinitionConnector from './QualityDefinitionConnector';
import styles from './QualityDefinitions.css';
class QualityDefinitions extends Component {
//
// Render
render() {
const {
items,
advancedSettings,
...otherProps
} = this.props;
return (
<FieldSet legend={translate('QualityDefinitions')}>
<PageSectionContent
errorMessage={translate('QualityDefinitionsLoadError')}
{...otherProps}
>
<div className={styles.header}>
<div className={styles.quality}>
{translate('Quality')}
</div>
<div className={styles.title}>
{translate('Title')}
</div>
<div className={styles.sizeLimit}>
{translate('SizeLimit')}
</div>
{
advancedSettings ?
<div className={styles.megabytesPerMinute}>
{translate('MegabytesPerMinute')}
</div> :
null
}
</div>
<div className={styles.definitions}>
{
items.map((item) => {
return (
<QualityDefinitionConnector
key={item.id}
{...item}
advancedSettings={advancedSettings}
/>
);
})
}
</div>
<div className={styles.sizeLimitHelpTextContainer}>
<div className={styles.sizeLimitHelpText}>
{translate('QualityLimitsSeriesRuntimeHelpText')}
</div>
</div>
</PageSectionContent>
</FieldSet>
);
}
}
QualityDefinitions.propTypes = {
isFetching: PropTypes.bool.isRequired,
error: PropTypes.object,
defaultProfile: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
advancedSettings: PropTypes.bool.isRequired
};
export default QualityDefinitions;

View file

@ -0,0 +1,124 @@
import { isEmpty } from 'lodash';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import FieldSet from 'Components/FieldSet';
import PageSectionContent from 'Components/Page/PageSectionContent';
import usePrevious from 'Helpers/Hooks/usePrevious';
import useShowAdvancedSettings from 'Helpers/Hooks/useShowAdvancedSettings';
import {
fetchQualityDefinitions,
saveQualityDefinitions,
} from 'Store/Actions/settingsActions';
import {
OnChildStateChange,
SetChildSave,
} from 'typings/Settings/SettingsState';
import translate from 'Utilities/String/translate';
import QualityDefinitionConnector from './QualityDefinitionConnector';
import styles from './QualityDefinitions.css';
function createQualityDefinitionsSelector() {
return createSelector(
(state: AppState) => state.settings.qualityDefinitions,
(qualityDefinitions) => {
const items = qualityDefinitions.items.map((item) => {
const pendingChanges = qualityDefinitions.pendingChanges[item.id] || {};
return Object.assign({}, item, pendingChanges);
});
return {
...qualityDefinitions,
items,
hasPendingChanges: !isEmpty(qualityDefinitions.pendingChanges),
};
}
);
}
interface QualityDefinitionsProps {
isResettingQualityDefinitions: boolean;
setChildSave: SetChildSave;
onChildStateChange: OnChildStateChange;
}
function QualityDefinitions({
isResettingQualityDefinitions,
setChildSave,
onChildStateChange,
}: QualityDefinitionsProps) {
const dispatch = useDispatch();
const showAdvancedSettings = useShowAdvancedSettings();
const { items, isFetching, isPopulated, isSaving, error, hasPendingChanges } =
useSelector(createQualityDefinitionsSelector());
const wasResettingQualityDefinitions = usePrevious(
isResettingQualityDefinitions
);
useEffect(() => {
dispatch(fetchQualityDefinitions());
setChildSave(() => {
dispatch(saveQualityDefinitions());
});
}, [dispatch, setChildSave]);
useEffect(() => {
onChildStateChange({
isSaving,
hasPendingChanges,
});
}, [hasPendingChanges, isSaving, onChildStateChange]);
useEffect(() => {
if (wasResettingQualityDefinitions && !isResettingQualityDefinitions) {
dispatch(fetchQualityDefinitions());
}
}, [isResettingQualityDefinitions, wasResettingQualityDefinitions, dispatch]);
return (
<FieldSet legend={translate('QualityDefinitions')}>
<PageSectionContent
errorMessage={translate('QualityDefinitionsLoadError')}
isFetching={isFetching}
isPopulated={isPopulated}
error={error}
>
<div className={styles.header}>
<div className={styles.quality}>{translate('Quality')}</div>
<div className={styles.title}>{translate('Title')}</div>
<div className={styles.sizeLimit}>{translate('SizeLimit')}</div>
{showAdvancedSettings ? (
<div className={styles.megabytesPerMinute}>
{translate('MegabytesPerMinute')}
</div>
) : null}
</div>
<div className={styles.definitions}>
{items.map((item) => {
return (
<QualityDefinitionConnector
key={item.id}
{...item}
advancedSettings={showAdvancedSettings}
/>
);
})}
</div>
<div className={styles.sizeLimitHelpTextContainer}>
<div className={styles.sizeLimitHelpText}>
{translate('QualityLimitsSeriesRuntimeHelpText')}
</div>
</div>
</PageSectionContent>
</FieldSet>
);
}
export default QualityDefinitions;

View file

@ -1,90 +0,0 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchQualityDefinitions, saveQualityDefinitions } from 'Store/Actions/settingsActions';
import QualityDefinitions from './QualityDefinitions';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.qualityDefinitions,
(state) => state.settings.advancedSettings,
(qualityDefinitions, advancedSettings) => {
const items = qualityDefinitions.items.map((item) => {
const pendingChanges = qualityDefinitions.pendingChanges[item.id] || {};
return Object.assign({}, item, pendingChanges);
});
return {
...qualityDefinitions,
items,
hasPendingChanges: !_.isEmpty(qualityDefinitions.pendingChanges),
advancedSettings
};
}
);
}
const mapDispatchToProps = {
dispatchFetchQualityDefinitions: fetchQualityDefinitions,
dispatchSaveQualityDefinitions: saveQualityDefinitions
};
class QualityDefinitionsConnector extends Component {
//
// Lifecycle
componentDidMount() {
const {
dispatchFetchQualityDefinitions,
dispatchSaveQualityDefinitions,
onChildMounted
} = this.props;
dispatchFetchQualityDefinitions();
onChildMounted(dispatchSaveQualityDefinitions);
}
componentDidUpdate(prevProps) {
const {
hasPendingChanges,
isSaving,
onChildStateChange
} = this.props;
if (
prevProps.isSaving !== isSaving ||
prevProps.hasPendingChanges !== hasPendingChanges
) {
onChildStateChange({
isSaving,
hasPendingChanges
});
}
}
//
// Render
render() {
return (
<QualityDefinitions
{...this.props}
/>
);
}
}
QualityDefinitionsConnector.propTypes = {
isSaving: PropTypes.bool.isRequired,
hasPendingChanges: PropTypes.bool.isRequired,
dispatchFetchQualityDefinitions: PropTypes.func.isRequired,
dispatchSaveQualityDefinitions: PropTypes.func.isRequired,
onChildMounted: PropTypes.func.isRequired,
onChildStateChange: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps, null)(QualityDefinitionsConnector);

View file

@ -1,105 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component, Fragment } from 'react';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import { icons } from 'Helpers/Props';
import SettingsToolbar from 'Settings/SettingsToolbar';
import translate from 'Utilities/String/translate';
import QualityDefinitionsConnector from './Definition/QualityDefinitionsConnector';
import ResetQualityDefinitionsModal from './Reset/ResetQualityDefinitionsModal';
class Quality extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this._saveCallback = null;
this.state = {
isSaving: false,
hasPendingChanges: false,
isConfirmQualityDefinitionResetModalOpen: false
};
}
//
// Listeners
onChildMounted = (saveCallback) => {
this._saveCallback = saveCallback;
};
onChildStateChange = (payload) => {
this.setState(payload);
};
onResetQualityDefinitionsPress = () => {
this.setState({ isConfirmQualityDefinitionResetModalOpen: true });
};
onCloseResetQualityDefinitionsModal = () => {
this.setState({ isConfirmQualityDefinitionResetModalOpen: false });
};
onSavePress = () => {
if (this._saveCallback) {
this._saveCallback();
}
};
//
// Render
render() {
const {
isSaving,
isResettingQualityDefinitions,
hasPendingChanges
} = this.state;
return (
<PageContent title={translate('QualitySettings')}>
<SettingsToolbar
isSaving={isSaving}
hasPendingChanges={hasPendingChanges}
additionalButtons={
<Fragment>
<PageToolbarSeparator />
<PageToolbarButton
label={translate('ResetDefinitions')}
iconName={icons.REFRESH}
isSpinning={isResettingQualityDefinitions}
onPress={this.onResetQualityDefinitionsPress}
/>
</Fragment>
}
onSavePress={this.onSavePress}
/>
<PageContentBody>
<QualityDefinitionsConnector
onChildMounted={this.onChildMounted}
onChildStateChange={this.onChildStateChange}
/>
</PageContentBody>
<ResetQualityDefinitionsModal
isOpen={this.state.isConfirmQualityDefinitionResetModalOpen}
onModalClose={this.onCloseResetQualityDefinitionsModal}
/>
</PageContent>
);
}
}
Quality.propTypes = {
isResettingQualityDefinitions: PropTypes.bool.isRequired
};
export default Quality;

View file

@ -0,0 +1,95 @@
import React, { useCallback, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
import * as commandNames from 'Commands/commandNames';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import { icons } from 'Helpers/Props';
import SettingsToolbar from 'Settings/SettingsToolbar';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import {
SaveCallback,
SettingsStateChange,
} from 'typings/Settings/SettingsState';
import translate from 'Utilities/String/translate';
import QualityDefinitions from './Definition/QualityDefinitions';
import ResetQualityDefinitionsModal from './Reset/ResetQualityDefinitionsModal';
function Quality() {
const isResettingQualityDefinitions = useSelector(
createCommandExecutingSelector(commandNames.RESET_QUALITY_DEFINITIONS)
);
const saveDefinitions = useRef<() => void>();
const [isSaving, setIsSaving] = useState(false);
const [
isConfirmQualityDefinitionResetModalOpen,
setIsConfirmQualityDefinitionResetModalOpen,
] = useState(false);
const [hasPendingChanges, setHasPendingChanges] = useState(false);
const handleSetChildSave = useCallback((saveCallback: SaveCallback) => {
saveDefinitions.current = saveCallback;
}, []);
const handleChildStateChange = useCallback(
({ isSaving, hasPendingChanges }: SettingsStateChange) => {
setIsSaving(isSaving);
setHasPendingChanges(hasPendingChanges);
},
[]
);
const handleResetQualityDefinitionsPress = useCallback(() => {
setIsConfirmQualityDefinitionResetModalOpen(true);
}, []);
const handleCloseResetQualityDefinitionsModal = useCallback(() => {
setIsConfirmQualityDefinitionResetModalOpen(false);
}, []);
const handleSavePress = useCallback(() => {
saveDefinitions.current?.();
}, []);
return (
<PageContent title={translate('QualitySettings')}>
<SettingsToolbar
isSaving={isSaving}
hasPendingChanges={hasPendingChanges}
additionalButtons={
<>
<PageToolbarSeparator />
<PageToolbarButton
label={translate('ResetDefinitions')}
iconName={icons.REFRESH}
isSpinning={isResettingQualityDefinitions}
onPress={handleResetQualityDefinitionsPress}
/>
</>
}
onSavePress={handleSavePress}
/>
<PageContentBody>
<QualityDefinitions
isResettingQualityDefinitions={isResettingQualityDefinitions}
setChildSave={handleSetChildSave}
onChildStateChange={handleChildStateChange}
/>
</PageContentBody>
<ResetQualityDefinitionsModal
isOpen={isConfirmQualityDefinitionResetModalOpen}
onModalClose={handleCloseResetQualityDefinitionsModal}
/>
</PageContent>
);
}
export default Quality;

View file

@ -1,38 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import * as commandNames from 'Commands/commandNames';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import Quality from './Quality';
function createMapStateToProps() {
return createSelector(
createCommandExecutingSelector(commandNames.RESET_QUALITY_DEFINITIONS),
(isResettingQualityDefinitions) => {
return {
isResettingQualityDefinitions
};
}
);
}
class QualityConnector extends Component {
//
// Render
render() {
return (
<Quality
{...this.props}
/>
);
}
}
QualityConnector.propTypes = {
isResettingQualityDefinitions: PropTypes.bool.isRequired
};
export default connect(createMapStateToProps)(QualityConnector);

View file

@ -1,33 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import Modal from 'Components/Modal/Modal';
import { sizes } from 'Helpers/Props';
import ResetQualityDefinitionsModalContentConnector from './ResetQualityDefinitionsModalContentConnector';
function ResetQualityDefinitionsModal(props) {
const {
isOpen,
onModalClose,
...otherProps
} = props;
return (
<Modal
isOpen={isOpen}
size={sizes.MEDIUM}
onModalClose={onModalClose}
>
<ResetQualityDefinitionsModalContentConnector
{...otherProps}
onModalClose={onModalClose}
/>
</Modal>
);
}
ResetQualityDefinitionsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default ResetQualityDefinitionsModal;

View file

@ -0,0 +1,22 @@
import React from 'react';
import Modal from 'Components/Modal/Modal';
import { sizes } from 'Helpers/Props';
import ResetQualityDefinitionsModalContent from './ResetQualityDefinitionsModalContent';
interface ResetQualityDefinitionsModalProps {
isOpen: boolean;
onModalClose: () => void;
}
function ResetQualityDefinitionsModal({
isOpen,
onModalClose,
}: ResetQualityDefinitionsModalProps) {
return (
<Modal isOpen={isOpen} size={sizes.MEDIUM} onModalClose={onModalClose}>
<ResetQualityDefinitionsModalContent onModalClose={onModalClose} />
</Modal>
);
}
export default ResetQualityDefinitionsModal;

View file

@ -1,106 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import Button from 'Components/Link/Button';
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 translate from 'Utilities/String/translate';
import styles from './ResetQualityDefinitionsModalContent.css';
class ResetQualityDefinitionsModalContent extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
resetDefinitionTitles: false
};
}
//
// Listeners
onResetDefinitionTitlesChange = ({ value }) => {
this.setState({ resetDefinitionTitles: value });
};
onResetQualityDefinitionsConfirmed = () => {
const resetDefinitionTitles = this.state.resetDefinitionTitles;
this.setState({ resetDefinitionTitles: false });
this.props.onResetQualityDefinitions(resetDefinitionTitles);
};
//
// Render
render() {
const {
onModalClose,
isResettingQualityDefinitions
} = this.props;
const resetDefinitionTitles = this.state.resetDefinitionTitles;
return (
<ModalContent
onModalClose={onModalClose}
>
<ModalHeader>
{translate('ResetQualityDefinitions')}
</ModalHeader>
<ModalBody>
<div className={styles.messageContainer}>
{translate('ResetQualityDefinitionsMessageText')}
</div>
<FormGroup>
<FormLabel>
{translate('ResetTitles')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="resetDefinitionTitles"
value={resetDefinitionTitles}
helpText={translate('ResetDefinitionTitlesHelpText')}
onChange={this.onResetDefinitionTitlesChange}
/>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>
{translate('Cancel')}
</Button>
<Button
kind={kinds.DANGER}
onPress={this.onResetQualityDefinitionsConfirmed}
isDisabled={isResettingQualityDefinitions}
>
{translate('Reset')}
</Button>
</ModalFooter>
</ModalContent>
);
}
}
ResetQualityDefinitionsModalContent.propTypes = {
onResetQualityDefinitions: PropTypes.func.isRequired,
isResettingQualityDefinitions: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default ResetQualityDefinitionsModalContent;

View file

@ -0,0 +1,92 @@
import React, { useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import * as commandNames from 'Commands/commandNames';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import Button from 'Components/Link/Button';
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 { executeCommand } from 'Store/Actions/commandActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import { InputChanged } from 'typings/inputs';
import translate from 'Utilities/String/translate';
import styles from './ResetQualityDefinitionsModalContent.css';
interface ResetQualityDefinitionsModalContentProps {
onModalClose: () => void;
}
function ResetQualityDefinitionsModalContent({
onModalClose,
}: ResetQualityDefinitionsModalContentProps) {
const dispatch = useDispatch();
const isResettingQualityDefinitions = useSelector(
createCommandExecutingSelector(commandNames.RESET_QUALITY_DEFINITIONS)
);
const [resetDefinitionTitles, setResetDefinitionTitles] = useState(false);
const handleResetDefinitionTitlesChange = useCallback(
({ value }: InputChanged<boolean>) => {
setResetDefinitionTitles(value);
},
[]
);
const handleResetQualityDefinitionsConfirmed = useCallback(() => {
const resetTitles = resetDefinitionTitles;
setResetDefinitionTitles(false);
dispatch(
executeCommand({
name: commandNames.RESET_QUALITY_DEFINITIONS,
resetTitles,
})
);
onModalClose();
}, [resetDefinitionTitles, dispatch, onModalClose]);
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{translate('ResetQualityDefinitions')}</ModalHeader>
<ModalBody>
<div className={styles.messageContainer}>
{translate('ResetQualityDefinitionsMessageText')}
</div>
<FormGroup>
<FormLabel>{translate('ResetTitles')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="resetDefinitionTitles"
value={resetDefinitionTitles}
helpText={translate('ResetDefinitionTitlesHelpText')}
onChange={handleResetDefinitionTitlesChange}
/>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button
kind={kinds.DANGER}
isDisabled={isResettingQualityDefinitions}
onPress={handleResetQualityDefinitionsConfirmed}
>
{translate('Reset')}
</Button>
</ModalFooter>
</ModalContent>
);
}
export default ResetQualityDefinitionsModalContent;

View file

@ -1,54 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import * as commandNames from 'Commands/commandNames';
import { executeCommand } from 'Store/Actions/commandActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import ResetQualityDefinitionsModalContent from './ResetQualityDefinitionsModalContent';
function createMapStateToProps() {
return createSelector(
createCommandExecutingSelector(commandNames.RESET_QUALITY_DEFINITIONS),
(isResettingQualityDefinitions) => {
return {
isResettingQualityDefinitions
};
}
);
}
const mapDispatchToProps = {
executeCommand
};
class ResetQualityDefinitionsModalContentConnector extends Component {
//
// Listeners
onResetQualityDefinitions = (resetTitles) => {
this.props.executeCommand({ name: commandNames.RESET_QUALITY_DEFINITIONS, resetTitles });
this.props.onModalClose(true);
};
//
// Render
render() {
return (
<ResetQualityDefinitionsModalContent
{...this.props}
onResetQualityDefinitions={this.onResetQualityDefinitions}
/>
);
}
}
ResetQualityDefinitionsModalContentConnector.propTypes = {
onModalClose: PropTypes.func.isRequired,
isResettingQualityDefinitions: PropTypes.bool.isRequired,
executeCommand: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(ResetQualityDefinitionsModalContentConnector);

View file

@ -0,0 +1,11 @@
function isEmpty<T extends object>(obj: T) {
for (const prop in obj) {
if (Object.hasOwn(obj, prop)) {
return false;
}
}
return true;
}
export default isEmpty;

View file

@ -0,0 +1,10 @@
export type SaveCallback = () => void;
export interface SettingsStateChange {
isSaving: boolean;
hasPendingChanges: boolean;
}
export type SetChildSave = (SaveCallback: SaveCallback) => void;
export type OnChildStateChange = (change: SettingsStateChange) => void;