mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-19 13:10:13 -07:00
parent
3beac03c00
commit
54e9f88648
89 changed files with 2354 additions and 995 deletions
31
frontend/src/Settings/AdvancedSettingsButton.css
Normal file
31
frontend/src/Settings/AdvancedSettingsButton.css
Normal file
|
@ -0,0 +1,31 @@
|
|||
.button {
|
||||
composes: toolbarButton from 'Components/Page/Toolbar/PageToolbarButton.css';
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.labelContainer {
|
||||
composes: labelContainer from 'Components/Page/Toolbar/PageToolbarButton.css';
|
||||
}
|
||||
|
||||
.label {
|
||||
composes: label from 'Components/Page/Toolbar/PageToolbarButton.css';
|
||||
}
|
||||
|
||||
.indicatorContainer {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
.indicatorBackground {
|
||||
color: $themeDarkColor;
|
||||
}
|
||||
|
||||
.enabled {
|
||||
color: $successColor;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: $dangerColor;
|
||||
}
|
59
frontend/src/Settings/AdvancedSettingsButton.js
Normal file
59
frontend/src/Settings/AdvancedSettingsButton.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import Icon from 'Components/Icon';
|
||||
import Link from 'Components/Link/Link';
|
||||
import styles from './AdvancedSettingsButton.css';
|
||||
|
||||
function AdvancedSettingsButton(props) {
|
||||
const {
|
||||
advancedSettings,
|
||||
onAdvancedSettingsPress
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={styles.button}
|
||||
title={advancedSettings ? 'Shown, click to hide' : 'Hidden, click to show'}
|
||||
onPress={onAdvancedSettingsPress}
|
||||
>
|
||||
<Icon
|
||||
name={icons.ADVANCED_SETTINGS}
|
||||
size={21}
|
||||
/>
|
||||
|
||||
<span
|
||||
className={classNames(
|
||||
styles.indicatorContainer,
|
||||
'fa-layers fa-fw'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
className={styles.indicatorBackground}
|
||||
name={icons.CIRCLE}
|
||||
size={16}
|
||||
/>
|
||||
|
||||
<Icon
|
||||
className={advancedSettings ? styles.enabled : styles.disabled}
|
||||
name={advancedSettings ? icons.CHECK : icons.CLOSE}
|
||||
size={10}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<div className={styles.labelContainer}>
|
||||
<div className={styles.label}>
|
||||
{advancedSettings ? 'Hide Advanced' : 'Show Advanced'}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
AdvancedSettingsButton.propTypes = {
|
||||
advancedSettings: PropTypes.bool.isRequired,
|
||||
onAdvancedSettingsPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default AdvancedSettingsButton;
|
|
@ -14,7 +14,10 @@ class DownloadClientSettings extends Component {
|
|||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this._saveCallback = null;
|
||||
|
||||
this.state = {
|
||||
isSaving: false,
|
||||
hasPendingChanges: false
|
||||
};
|
||||
}
|
||||
|
@ -22,28 +25,34 @@ class DownloadClientSettings extends Component {
|
|||
//
|
||||
// Listeners
|
||||
|
||||
setDownloadClientOptionsRef = (ref) => {
|
||||
this._downloadClientOptions = ref;
|
||||
onChildMounted = (saveCallback) => {
|
||||
this._saveCallback = saveCallback;
|
||||
}
|
||||
|
||||
onHasPendingChange = (hasPendingChanges) => {
|
||||
this.setState({
|
||||
hasPendingChanges
|
||||
});
|
||||
onChildStateChange = (payload) => {
|
||||
this.setState(payload);
|
||||
}
|
||||
|
||||
onSavePress = () => {
|
||||
this._downloadClientOptions.getWrappedInstance().save();
|
||||
if (this._saveCallback) {
|
||||
this._saveCallback();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isSaving,
|
||||
hasPendingChanges
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<PageContent title="Download Client Settings">
|
||||
<SettingsToolbarConnector
|
||||
hasPendingChanges={this.state.hasPendingChanges}
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
onSavePress={this.onSavePress}
|
||||
/>
|
||||
|
||||
|
@ -51,8 +60,8 @@ class DownloadClientSettings extends Component {
|
|||
<DownloadClientsConnector />
|
||||
|
||||
<DownloadClientOptionsConnector
|
||||
ref={this.setDownloadClientOptionsRef}
|
||||
onHasPendingChange={this.onHasPendingChange}
|
||||
onChildMounted={this.onChildMounted}
|
||||
onChildStateChange={this.onChildStateChange}
|
||||
/>
|
||||
|
||||
<RemotePathMappingsConnector />
|
||||
|
|
|
@ -60,6 +60,7 @@ class DownloadClient extends Component {
|
|||
return (
|
||||
<Card
|
||||
className={styles.downloadClient}
|
||||
overlayContent={true}
|
||||
onPress={this.onEditDownloadClientPress}
|
||||
>
|
||||
<div className={styles.name}>
|
||||
|
|
|
@ -21,10 +21,10 @@ function createMapStateToProps() {
|
|||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchDownloadClientOptions,
|
||||
setDownloadClientOptionsValue,
|
||||
saveDownloadClientOptions,
|
||||
clearPendingChanges
|
||||
dispatchFetchDownloadClientOptions: fetchDownloadClientOptions,
|
||||
dispatchSetDownloadClientOptionsValue: setDownloadClientOptionsValue,
|
||||
dispatchSaveDownloadClientOptions: saveDownloadClientOptions,
|
||||
dispatchClearPendingChanges: clearPendingChanges
|
||||
};
|
||||
|
||||
class DownloadClientOptionsConnector extends Component {
|
||||
|
@ -33,31 +33,43 @@ class DownloadClientOptionsConnector extends Component {
|
|||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchDownloadClientOptions();
|
||||
const {
|
||||
dispatchFetchDownloadClientOptions,
|
||||
dispatchSaveDownloadClientOptions,
|
||||
onChildMounted
|
||||
} = this.props;
|
||||
|
||||
dispatchFetchDownloadClientOptions();
|
||||
onChildMounted(dispatchSaveDownloadClientOptions);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.hasPendingChanges !== prevProps.hasPendingChanges) {
|
||||
this.props.onHasPendingChange(this.props.hasPendingChanges);
|
||||
const {
|
||||
hasPendingChanges,
|
||||
isSaving,
|
||||
onChildStateChange
|
||||
} = this.props;
|
||||
|
||||
if (
|
||||
prevProps.isSaving !== isSaving ||
|
||||
prevProps.hasPendingChanges !== hasPendingChanges
|
||||
) {
|
||||
onChildStateChange({
|
||||
isSaving,
|
||||
hasPendingChanges
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.clearPendingChanges({ section: this.props.section });
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
save = () => {
|
||||
this.props.saveDownloadClientOptions();
|
||||
this.props.dispatchClearPendingChanges({ section: this.props.section });
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onInputChange = ({ name, value }) => {
|
||||
this.props.setDownloadClientOptionsValue({ name, value });
|
||||
this.props.dispatchSetDownloadClientOptionsValue({ name, value });
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -75,18 +87,20 @@ class DownloadClientOptionsConnector extends Component {
|
|||
|
||||
DownloadClientOptionsConnector.propTypes = {
|
||||
section: PropTypes.string.isRequired,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
hasPendingChanges: PropTypes.bool.isRequired,
|
||||
fetchDownloadClientOptions: PropTypes.func.isRequired,
|
||||
setDownloadClientOptionsValue: PropTypes.func.isRequired,
|
||||
saveDownloadClientOptions: PropTypes.func.isRequired,
|
||||
clearPendingChanges: PropTypes.func.isRequired,
|
||||
onHasPendingChange: PropTypes.func.isRequired
|
||||
dispatchFetchDownloadClientOptions: PropTypes.func.isRequired,
|
||||
dispatchSetDownloadClientOptionsValue: PropTypes.func.isRequired,
|
||||
dispatchSaveDownloadClientOptions: PropTypes.func.isRequired,
|
||||
dispatchClearPendingChanges: PropTypes.func.isRequired,
|
||||
onChildMounted: PropTypes.func.isRequired,
|
||||
onChildStateChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connectSection(
|
||||
createMapStateToProps,
|
||||
mapDispatchToProps,
|
||||
undefined,
|
||||
{ withRef: true },
|
||||
undefined,
|
||||
{ section: 'settings.downloadClientOptions' }
|
||||
)(DownloadClientOptionsConnector);
|
||||
|
|
|
@ -49,6 +49,8 @@ function HostSettings(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="port"
|
||||
min={1}
|
||||
max={65535}
|
||||
helpTextWarning="Requires restart to take effect"
|
||||
onChange={onInputChange}
|
||||
{...port}
|
||||
|
@ -95,6 +97,8 @@ function HostSettings(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="sslPort"
|
||||
min={1}
|
||||
max={65535}
|
||||
helpTextWarning="Requires restart to take effect"
|
||||
onChange={onInputChange}
|
||||
{...sslPort}
|
||||
|
|
|
@ -74,6 +74,8 @@ function ProxySettings(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="proxyPort"
|
||||
min={1}
|
||||
max={65535}
|
||||
onChange={onInputChange}
|
||||
{...proxyPort}
|
||||
/>
|
||||
|
|
|
@ -14,7 +14,10 @@ class IndexerSettings extends Component {
|
|||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this._saveCallback = null;
|
||||
|
||||
this.state = {
|
||||
isSaving: false,
|
||||
hasPendingChanges: false
|
||||
};
|
||||
}
|
||||
|
@ -22,28 +25,34 @@ class IndexerSettings extends Component {
|
|||
//
|
||||
// Listeners
|
||||
|
||||
setIndexerOptionsRef = (ref) => {
|
||||
this._indexerOptions = ref;
|
||||
onChildMounted = (saveCallback) => {
|
||||
this._saveCallback = saveCallback;
|
||||
}
|
||||
|
||||
onHasPendingChange = (hasPendingChanges) => {
|
||||
this.setState({
|
||||
hasPendingChanges
|
||||
});
|
||||
onChildStateChange = (payload) => {
|
||||
this.setState(payload);
|
||||
}
|
||||
|
||||
onSavePress = () => {
|
||||
this._indexerOptions.getWrappedInstance().save();
|
||||
if (this._saveCallback) {
|
||||
this._saveCallback();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isSaving,
|
||||
hasPendingChanges
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<PageContent title="Indexer Settings">
|
||||
<SettingsToolbarConnector
|
||||
hasPendingChanges={this.state.hasPendingChanges}
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
onSavePress={this.onSavePress}
|
||||
/>
|
||||
|
||||
|
@ -51,8 +60,8 @@ class IndexerSettings extends Component {
|
|||
<IndexersConnector />
|
||||
|
||||
<IndexerOptionsConnector
|
||||
ref={this.setIndexerOptionsRef}
|
||||
onHasPendingChange={this.onHasPendingChange}
|
||||
onChildMounted={this.onChildMounted}
|
||||
onChildStateChange={this.onChildStateChange}
|
||||
/>
|
||||
|
||||
<RestrictionsConnector />
|
||||
|
|
|
@ -76,6 +76,7 @@ class Indexer extends Component {
|
|||
return (
|
||||
<Card
|
||||
className={styles.indexer}
|
||||
overlayContent={true}
|
||||
onPress={this.onEditIndexerPress}
|
||||
>
|
||||
<div className={styles.name}>
|
||||
|
|
|
@ -41,6 +41,7 @@ function IndexerOptions(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="minimumAge"
|
||||
min={0}
|
||||
helpText="Usenet only: Minimum age in minutes of NZBs before they are grabbed. Use this to give new releases time to propagate to your usenet provider."
|
||||
onChange={onInputChange}
|
||||
{...settings.minimumAge}
|
||||
|
@ -53,6 +54,7 @@ function IndexerOptions(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="maximumSize"
|
||||
min={0}
|
||||
helpText="Maximum size for a release to be grabbed in MB. Set to zero to set to unlimited."
|
||||
onChange={onInputChange}
|
||||
{...settings.maximumSize}
|
||||
|
@ -65,6 +67,7 @@ function IndexerOptions(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="retention"
|
||||
min={0}
|
||||
helpText="Usenet only: Set to zero to set for unlimited retention"
|
||||
onChange={onInputChange}
|
||||
{...settings.retention}
|
||||
|
@ -80,6 +83,7 @@ function IndexerOptions(props) {
|
|||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="rssSyncInterval"
|
||||
min={0}
|
||||
helpText="Interval in minutes. Set to zero to disable (this will stop all automatic release grabbing)"
|
||||
helpTextWarning="This will apply to all indexers, please follow the rules set forth by them"
|
||||
helpLink="https://github.com/Sonarr/Sonarr/wiki/RSS-Sync"
|
||||
|
|
|
@ -21,10 +21,10 @@ function createMapStateToProps() {
|
|||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchIndexerOptions,
|
||||
setIndexerOptionsValue,
|
||||
saveIndexerOptions,
|
||||
clearPendingChanges
|
||||
dispatchFetchIndexerOptions: fetchIndexerOptions,
|
||||
dispatchSetIndexerOptionsValue: setIndexerOptionsValue,
|
||||
dispatchSaveIndexerOptions: saveIndexerOptions,
|
||||
dispatchClearPendingChanges: clearPendingChanges
|
||||
};
|
||||
|
||||
class IndexerOptionsConnector extends Component {
|
||||
|
@ -33,31 +33,43 @@ class IndexerOptionsConnector extends Component {
|
|||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchIndexerOptions();
|
||||
const {
|
||||
dispatchFetchIndexerOptions,
|
||||
dispatchSaveIndexerOptions,
|
||||
onChildMounted
|
||||
} = this.props;
|
||||
|
||||
dispatchFetchIndexerOptions();
|
||||
onChildMounted(dispatchSaveIndexerOptions);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.hasPendingChanges !== prevProps.hasPendingChanges) {
|
||||
this.props.onHasPendingChange(this.props.hasPendingChanges);
|
||||
const {
|
||||
hasPendingChanges,
|
||||
isSaving,
|
||||
onChildStateChange
|
||||
} = this.props;
|
||||
|
||||
if (
|
||||
prevProps.isSaving !== isSaving ||
|
||||
prevProps.hasPendingChanges !== hasPendingChanges
|
||||
) {
|
||||
onChildStateChange({
|
||||
isSaving,
|
||||
hasPendingChanges
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.clearPendingChanges({ section: this.props.section });
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
save = () => {
|
||||
this.props.saveIndexerOptions();
|
||||
this.props.dispatchClearPendingChanges({ section: this.props.section });
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onInputChange = ({ name, value }) => {
|
||||
this.props.setIndexerOptionsValue({ name, value });
|
||||
this.props.dispatchSetIndexerOptionsValue({ name, value });
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -75,18 +87,20 @@ class IndexerOptionsConnector extends Component {
|
|||
|
||||
IndexerOptionsConnector.propTypes = {
|
||||
section: PropTypes.string.isRequired,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
hasPendingChanges: PropTypes.bool.isRequired,
|
||||
fetchIndexerOptions: PropTypes.func.isRequired,
|
||||
setIndexerOptionsValue: PropTypes.func.isRequired,
|
||||
saveIndexerOptions: PropTypes.func.isRequired,
|
||||
clearPendingChanges: PropTypes.func.isRequired,
|
||||
onHasPendingChange: PropTypes.func.isRequired
|
||||
dispatchFetchIndexerOptions: PropTypes.func.isRequired,
|
||||
dispatchSetIndexerOptionsValue: PropTypes.func.isRequired,
|
||||
dispatchSaveIndexerOptions: PropTypes.func.isRequired,
|
||||
dispatchClearPendingChanges: PropTypes.func.isRequired,
|
||||
onChildMounted: PropTypes.func.isRequired,
|
||||
onChildStateChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connectSection(
|
||||
createMapStateToProps,
|
||||
mapDispatchToProps,
|
||||
undefined,
|
||||
{ withRef: true },
|
||||
undefined,
|
||||
{ section: 'settings.indexerOptions' }
|
||||
)(IndexerOptionsConnector);
|
||||
|
|
|
@ -64,6 +64,7 @@ class Restriction extends Component {
|
|||
return (
|
||||
<Card
|
||||
className={styles.restriction}
|
||||
overlayContent={true}
|
||||
onPress={this.onEditRestrictionPress}
|
||||
>
|
||||
<div>
|
||||
|
|
|
@ -52,6 +52,7 @@ class Metadata extends Component {
|
|||
return (
|
||||
<Card
|
||||
className={styles.metadata}
|
||||
overlayContent={true}
|
||||
onPress={this.onEditMetadataPress}
|
||||
>
|
||||
<div className={styles.name}>
|
||||
|
|
|
@ -79,6 +79,7 @@ class Notification extends Component {
|
|||
return (
|
||||
<Card
|
||||
className={styles.notification}
|
||||
overlayContent={true}
|
||||
onPress={this.onEditNotificationPress}
|
||||
>
|
||||
<div className={styles.name}>
|
||||
|
|
|
@ -101,7 +101,7 @@ function EditLanguageProfileModalContent(props) {
|
|||
id &&
|
||||
<div
|
||||
className={styles.deleteButtonContainer}
|
||||
title={isInUse && 'Can\'t delete a language profile that is attached to a artist'}
|
||||
title={isInUse ? 'Can\'t delete a language profile that is attached to a artist' : undefined}
|
||||
>
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
|
|
|
@ -97,7 +97,7 @@ function EditMetadataProfileModalContent(props) {
|
|||
id &&
|
||||
<div
|
||||
className={styles.deleteButtonContainer}
|
||||
title={isInUse && 'Can\'t delete a metadata profile that is attached to a artist'}
|
||||
title={isInUse ? 'Can\'t delete a metadata profile that is attached to a artist' : undefined}
|
||||
>
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
|
|
|
@ -200,7 +200,7 @@ class EditQualityProfileModalContent extends Component {
|
|||
id &&
|
||||
<div
|
||||
className={styles.deleteButtonContainer}
|
||||
title={isInUse && 'Can\'t delete a quality profile that is attached to a artist'}
|
||||
title={isInUse ? 'Can\'t delete a quality profile that is attached to a artist' : undefined}
|
||||
>
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
|
|
|
@ -28,6 +28,29 @@ function getValue(value) {
|
|||
|
||||
class QualityDefinition extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this._forceUpdateTimeout = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// A hack to deal with a bug in the slider component until a fix for it
|
||||
// lands and an updated version is available.
|
||||
// See: https://github.com/mpowaga/react-slider/issues/115
|
||||
|
||||
this._forceUpdateTimeout = setTimeout(() => this.forceUpdate(), 1);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._forceUpdateTimeout) {
|
||||
clearTimeout(this._forceUpdateTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
|
@ -131,6 +154,8 @@ class QualityDefinition extends Component {
|
|||
<NumberInput
|
||||
className={styles.sizeInput}
|
||||
name={`${id}.min`}
|
||||
min={slider.min}
|
||||
max={maxSize ? maxSize - 10 : slider.max - 10}
|
||||
value={minSize || slider.min}
|
||||
isFloat={true}
|
||||
onChange={this.onMinSizeChange}
|
||||
|
@ -143,6 +168,7 @@ class QualityDefinition extends Component {
|
|||
<NumberInput
|
||||
className={styles.sizeInput}
|
||||
name={`${id}.max`}
|
||||
min={minSize + 10}
|
||||
value={maxSize || slider.max}
|
||||
isFloat={true}
|
||||
onChange={this.onMaxSizeChange}
|
||||
|
|
|
@ -40,7 +40,7 @@ class QualityDefinitionConnector extends Component {
|
|||
this.props.setQualityDefinitionValue({ id, name: 'minSize', value: minSize });
|
||||
}
|
||||
|
||||
if (minSize !== currentMaxSize) {
|
||||
if (maxSize !== currentMaxSize) {
|
||||
this.props.setQualityDefinitionValue({ id, name: 'maxSize', value: maxSize });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ function createMapStateToProps() {
|
|||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchQualityDefinitions,
|
||||
saveQualityDefinitions
|
||||
dispatchFetchQualityDefinitions: fetchQualityDefinitions,
|
||||
dispatchSaveQualityDefinitions: saveQualityDefinitions
|
||||
};
|
||||
|
||||
class QualityDefinitionsConnector extends Component {
|
||||
|
@ -36,26 +36,36 @@ class QualityDefinitionsConnector extends Component {
|
|||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchQualityDefinitions();
|
||||
this.props.dispatchFetchQualityDefinitions();
|
||||
|
||||
const {
|
||||
dispatchFetchQualityDefinitions,
|
||||
dispatchSaveQualityDefinitions,
|
||||
onChildMounted
|
||||
} = this.props;
|
||||
|
||||
dispatchFetchQualityDefinitions();
|
||||
onChildMounted(dispatchSaveQualityDefinitions);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
hasPendingChanges
|
||||
hasPendingChanges,
|
||||
isSaving,
|
||||
onChildStateChange
|
||||
} = this.props;
|
||||
|
||||
if (hasPendingChanges !== prevProps.hasPendingChanges) {
|
||||
this.props.onHasPendingChange(hasPendingChanges);
|
||||
if (
|
||||
prevProps.isSaving !== isSaving ||
|
||||
prevProps.hasPendingChanges !== hasPendingChanges
|
||||
) {
|
||||
onChildStateChange({
|
||||
isSaving,
|
||||
hasPendingChanges
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
save = () => {
|
||||
this.props.saveQualityDefinitions();
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
|
@ -69,10 +79,12 @@ class QualityDefinitionsConnector extends Component {
|
|||
}
|
||||
|
||||
QualityDefinitionsConnector.propTypes = {
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
hasPendingChanges: PropTypes.bool.isRequired,
|
||||
fetchQualityDefinitions: PropTypes.func.isRequired,
|
||||
saveQualityDefinitions: PropTypes.func.isRequired,
|
||||
onHasPendingChange: PropTypes.func.isRequired
|
||||
dispatchFetchQualityDefinitions: PropTypes.func.isRequired,
|
||||
dispatchSaveQualityDefinitions: PropTypes.func.isRequired,
|
||||
onChildMounted: PropTypes.func.isRequired,
|
||||
onChildStateChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps, null, { withRef: true })(QualityDefinitionsConnector);
|
||||
|
|
|
@ -12,7 +12,10 @@ class Quality extends Component {
|
|||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this._saveCallback = null;
|
||||
|
||||
this.state = {
|
||||
isSaving: false,
|
||||
hasPendingChanges: false
|
||||
};
|
||||
}
|
||||
|
@ -20,35 +23,41 @@ class Quality extends Component {
|
|||
//
|
||||
// Listeners
|
||||
|
||||
setQualityDefinitionsRef = (ref) => {
|
||||
this._qualityDefinitions = ref;
|
||||
onChildMounted = (saveCallback) => {
|
||||
this._saveCallback = saveCallback;
|
||||
}
|
||||
|
||||
onHasPendingChange = (hasPendingChanges) => {
|
||||
this.setState({
|
||||
hasPendingChanges
|
||||
});
|
||||
onChildStateChange = (payload) => {
|
||||
this.setState(payload);
|
||||
}
|
||||
|
||||
onSavePress = () => {
|
||||
this._qualityDefinitions.getWrappedInstance().save();
|
||||
if (this._saveCallback) {
|
||||
this._saveCallback();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isSaving,
|
||||
hasPendingChanges
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<PageContent title="Quality Settings">
|
||||
<SettingsToolbarConnector
|
||||
hasPendingChanges={this.state.hasPendingChanges}
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasPendingChanges}
|
||||
onSavePress={this.onSavePress}
|
||||
/>
|
||||
|
||||
<PageContentBodyConnector>
|
||||
<QualityDefinitionsConnector
|
||||
ref={this.setQualityDefinitionsRef}
|
||||
onHasPendingChange={this.onHasPendingChange}
|
||||
onChildMounted={this.onChildMounted}
|
||||
onChildStateChange={this.onChildStateChange}
|
||||
/>
|
||||
</PageContentBodyConnector>
|
||||
</PageContent>
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
.advancedSettings {
|
||||
composes: toolbarButton from 'Components/Page/Toolbar/PageToolbarButton.css';
|
||||
}
|
||||
|
||||
.advancedSettingsEnabled {
|
||||
color: $toobarButtonHoverColor;
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import keyboardShortcuts, { shortcuts } from 'Components/keyboardShortcuts';
|
||||
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PendingChangesModal from './PendingChangesModal';
|
||||
import styles from './SettingsToolbar.css';
|
||||
import AdvancedSettingsButton from './AdvancedSettingsButton';
|
||||
|
||||
class SettingsToolbar extends Component {
|
||||
|
||||
|
@ -53,14 +52,9 @@ class SettingsToolbar extends Component {
|
|||
return (
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label={advancedSettings ? 'Hide Advanced' : 'Show Advanced'}
|
||||
className={classNames(
|
||||
styles.advancedSettings,
|
||||
advancedSettings && styles.advancedSettingsEnabled
|
||||
)}
|
||||
iconName={icons.ADVANCED_SETTINGS}
|
||||
onPress={onAdvancedSettingsPress}
|
||||
<AdvancedSettingsButton
|
||||
advancedSettings={advancedSettings}
|
||||
onAdvancedSettingsPress={onAdvancedSettingsPress}
|
||||
/>
|
||||
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue