1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-25 22:46:31 -04:00

Convert Quality Settings to TypeScript

This commit is contained in:
Mark McDowall
2024-12-28 12:10:18 -08:00
parent 60529f0bac
commit cde0a31ff0
20 changed files with 415 additions and 548 deletions
@@ -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,
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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);