Sonarr/frontend/src/Settings/Quality/Definition/QualityDefinition.js

347 lines
9.0 KiB
JavaScript
Raw Normal View History

2018-01-13 02:01:27 +00:00
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ReactSlider from 'react-slider';
import NumberInput from 'Components/Form/NumberInput';
import TextInput from 'Components/Form/TextInput';
2022-05-20 04:15:43 +00:00
import Label from 'Components/Label';
2019-04-24 06:48:43 +00:00
import Popover from 'Components/Tooltip/Popover';
2022-05-20 04:15:43 +00:00
import { kinds, tooltipPositions } from 'Helpers/Props';
import formatBytes from 'Utilities/Number/formatBytes';
import roundNumber from 'Utilities/Number/roundNumber';
2023-07-22 19:14:33 +00:00
import translate from 'Utilities/String/translate';
2019-04-24 06:48:43 +00:00
import QualityDefinitionLimits from './QualityDefinitionLimits';
2018-01-13 02:01:27 +00:00
import styles from './QualityDefinition.css';
const MIN = 0;
const MAX = 400;
const MIN_DISTANCE = 1;
2018-01-13 02:01:27 +00:00
const slider = {
min: MIN,
max: roundNumber(Math.pow(MAX, 1 / 1.1)),
2018-01-13 02:01:27 +00:00
step: 0.1
};
function getValue(inputValue) {
if (inputValue < MIN) {
return MIN;
2018-01-13 02:01:27 +00:00
}
if (inputValue > MAX) {
return MAX;
2018-01-13 02:01:27 +00:00
}
return roundNumber(inputValue);
}
function getSliderValue(value, defaultValue) {
const sliderValue = value ? Math.pow(value, 1 / 1.1) : defaultValue;
return roundNumber(sliderValue);
2018-01-13 02:01:27 +00:00
}
class QualityDefinition extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this._forceUpdateTimeout = null;
this.state = {
sliderMinSize: getSliderValue(props.minSize, slider.min),
sliderMaxSize: getSliderValue(props.maxSize, slider.max),
sliderPreferredSize: getSliderValue(props.preferredSize, (slider.max - 3))
};
2018-01-13 02:01:27 +00:00
}
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);
}
}
//
// Control
trackRenderer(props, state) {
return (
<div
{...props}
className={styles.track}
/>
);
}
thumbRenderer(props, state) {
return (
<div
{...props}
className={styles.thumb}
/>
);
}
2018-01-13 02:01:27 +00:00
//
// Listeners
onSliderChange = ([sliderMinSize, sliderPreferredSize, sliderMaxSize]) => {
this.setState({
sliderMinSize,
sliderMaxSize,
sliderPreferredSize
});
this.props.onSizeChange({
minSize: roundNumber(Math.pow(sliderMinSize, 1.1)),
preferredSize: sliderPreferredSize === (slider.max - 3) ? null : roundNumber(Math.pow(sliderPreferredSize, 1.1)),
maxSize: sliderMaxSize === slider.max ? null : roundNumber(Math.pow(sliderMaxSize, 1.1))
});
2022-05-20 04:15:43 +00:00
};
onAfterSliderChange = () => {
const {
minSize,
maxSize,
preferredSize
} = this.props;
2018-01-13 02:01:27 +00:00
this.setState({
sliderMiSize: getSliderValue(minSize, slider.min),
sliderMaxSize: getSliderValue(maxSize, slider.max),
sliderPreferredSize: getSliderValue(preferredSize, (slider.max - 3)) // fix
});
2022-05-20 04:15:43 +00:00
};
2018-01-13 02:01:27 +00:00
onMinSizeChange = ({ value }) => {
const minSize = getValue(value);
this.setState({
sliderMinSize: getSliderValue(minSize, slider.min)
});
2018-01-13 02:01:27 +00:00
this.props.onSizeChange({
minSize,
maxSize: this.props.maxSize,
preferredSize: this.props.preferredSize
});
};
onPreferredSizeChange = ({ value }) => {
const preferredSize = value === (MAX - 3) ? null : getValue(value);
this.setState({
sliderPreferredSize: getSliderValue(preferredSize, slider.preferred)
});
this.props.onSizeChange({
minSize: this.props.minSize,
maxSize: this.props.maxSize,
preferredSize
2018-01-13 02:01:27 +00:00
});
2022-05-20 04:15:43 +00:00
};
2018-01-13 02:01:27 +00:00
onMaxSizeChange = ({ value }) => {
const maxSize = value === MAX ? null : getValue(value);
this.setState({
sliderMaxSize: getSliderValue(maxSize, slider.max)
});
2018-01-13 02:01:27 +00:00
this.props.onSizeChange({
minSize: this.props.minSize,
maxSize,
preferredSize: this.props.preferredSize
2018-01-13 02:01:27 +00:00
});
2022-05-20 04:15:43 +00:00
};
2018-01-13 02:01:27 +00:00
//
// Render
render() {
const {
id,
quality,
title,
minSize,
maxSize,
preferredSize,
2018-01-13 02:01:27 +00:00
advancedSettings,
onTitleChange
} = this.props;
const {
sliderMinSize,
sliderMaxSize,
sliderPreferredSize
} = this.state;
2018-01-13 02:01:27 +00:00
const minBytes = minSize * 1024 * 1024;
2023-07-22 19:14:33 +00:00
const minSixty = `${formatBytes(minBytes * 60)}/${translate('HourShorthand')}`;
2018-01-13 02:01:27 +00:00
const preferredBytes = preferredSize * 1024 * 1024;
2023-07-22 19:14:33 +00:00
const preferredSixty = preferredBytes ? `${formatBytes(preferredBytes * 60)}/${translate('HourShorthand')}` : translate('Unlimited');
2018-01-13 02:01:27 +00:00
const maxBytes = maxSize && maxSize * 1024 * 1024;
2023-07-22 19:14:33 +00:00
const maxSixty = maxBytes ? `${formatBytes(maxBytes * 60)}/${translate('HourShorthand')}` : translate('Unlimited');
2018-01-13 02:01:27 +00:00
return (
<div className={styles.qualityDefinition}>
<div className={styles.quality}>
{quality.name}
</div>
<div className={styles.title}>
<TextInput
name={`${id}.${title}`}
value={title}
onChange={onTitleChange}
/>
</div>
<div className={styles.sizeLimit}>
<ReactSlider
className={styles.slider}
2018-01-13 02:01:27 +00:00
min={slider.min}
max={slider.max}
step={slider.step}
minDistance={3}
value={[sliderMinSize, sliderPreferredSize, sliderMaxSize]}
withTracks={true}
allowCross={false}
2018-01-13 02:01:27 +00:00
snapDragDisabled={true}
renderThumb={this.thumbRenderer}
renderTrack={this.trackRenderer}
onChange={this.onSliderChange}
onAfterChange={this.onAfterSliderChange}
2018-01-13 02:01:27 +00:00
/>
<div className={styles.sizes}>
<div>
2019-04-24 06:48:43 +00:00
<Popover
anchor={
<Label kind={kinds.INFO}>{minSixty}</Label>
}
2023-07-22 19:14:33 +00:00
title={translate('MinimumLimits')}
2019-04-24 06:48:43 +00:00
body={
<QualityDefinitionLimits
bytes={minBytes}
2023-07-22 19:14:33 +00:00
message={translate('NoMinimumForAnyRuntime')}
2019-04-24 06:48:43 +00:00
/>
}
position={tooltipPositions.BOTTOM}
/>
2018-01-13 02:01:27 +00:00
</div>
<div>
<Popover
anchor={
<Label kind={kinds.SUCCESS}>{preferredSixty}</Label>
}
2023-07-22 19:14:33 +00:00
title={translate('PreferredSize')}
body={
<QualityDefinitionLimits
bytes={preferredBytes}
2023-07-22 19:14:33 +00:00
message={translate('NoLimitForAnyRuntime')}
/>
}
position={tooltipPositions.BOTTOM}
/>
</div>
2018-01-13 02:01:27 +00:00
<div>
2019-04-24 06:48:43 +00:00
<Popover
anchor={
<Label kind={kinds.WARNING}>{maxSixty}</Label>
}
2023-07-22 19:14:33 +00:00
title={translate('MaximumLimits')}
2019-04-24 06:48:43 +00:00
body={
<QualityDefinitionLimits
bytes={maxBytes}
2023-07-22 19:14:33 +00:00
message={translate('NoLimitForAnyRuntime')}
2019-04-24 06:48:43 +00:00
/>
}
position={tooltipPositions.BOTTOM}
/>
2018-01-13 02:01:27 +00:00
</div>
</div>
</div>
{
advancedSettings &&
<div className={styles.megabytesPerMinute}>
<div>
2023-07-22 19:14:33 +00:00
{translate('Min')}
2018-01-13 02:01:27 +00:00
<NumberInput
className={styles.sizeInput}
name={`${id}.min`}
value={minSize || MIN}
min={MIN}
max={preferredSize ? preferredSize - 5 : MAX - 5}
step={0.1}
2018-01-13 02:01:27 +00:00
isFloat={true}
onChange={this.onMinSizeChange}
/>
</div>
<div>
2023-07-22 19:14:33 +00:00
{translate('Preferred')}
<NumberInput
className={styles.sizeInput}
name={`${id}.min`}
value={preferredSize || MAX - 5}
min={MIN}
max={maxSize ? maxSize - 5 : MAX - 5}
step={0.1}
isFloat={true}
onChange={this.onPreferredSizeChange}
/>
</div>
2018-01-13 02:01:27 +00:00
<div>
2023-07-22 19:14:33 +00:00
{translate('Max')}
2018-01-13 02:01:27 +00:00
<NumberInput
className={styles.sizeInput}
name={`${id}.max`}
value={maxSize || MAX}
min={minSize + MIN_DISTANCE}
max={MAX}
step={0.1}
2018-01-13 02:01:27 +00:00
isFloat={true}
onChange={this.onMaxSizeChange}
/>
</div>
</div>
}
</div>
);
}
}
QualityDefinition.propTypes = {
id: PropTypes.number.isRequired,
quality: PropTypes.object.isRequired,
title: PropTypes.string.isRequired,
minSize: PropTypes.number,
maxSize: PropTypes.number,
preferredSize: PropTypes.number,
2018-01-13 02:01:27 +00:00
advancedSettings: PropTypes.bool.isRequired,
onTitleChange: PropTypes.func.isRequired,
onSizeChange: PropTypes.func.isRequired
};
export default QualityDefinition;