mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-22 22:14:44 -04:00
New: Custom Formats
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
This commit is contained in:
@@ -40,6 +40,9 @@ function EditDelayProfileModalContent(props) {
|
||||
enableTorrent,
|
||||
usenetDelay,
|
||||
torrentDelay,
|
||||
bypassIfHighestQuality,
|
||||
bypassIfAboveCustomFormatScore,
|
||||
minimumCustomFormatScore,
|
||||
tags
|
||||
} = item;
|
||||
|
||||
@@ -81,7 +84,7 @@ function EditDelayProfileModalContent(props) {
|
||||
</FormGroup>
|
||||
|
||||
{
|
||||
enableUsenet.value &&
|
||||
enableUsenet.value ?
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('UsenetDelay')}
|
||||
@@ -95,11 +98,12 @@ function EditDelayProfileModalContent(props) {
|
||||
helpText={translate('UsenetDelayHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FormGroup> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
enableTorrent.value &&
|
||||
enableTorrent.value ?
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('TorrentDelay')}
|
||||
@@ -113,7 +117,48 @@ function EditDelayProfileModalContent(props) {
|
||||
helpText={translate('TorrentDelayHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FormGroup> :
|
||||
null
|
||||
}
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('BypassIfHighestQuality')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="bypassIfHighestQuality"
|
||||
{...bypassIfHighestQuality}
|
||||
helpText={translate('BypassIfHighestQualityHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('BypassIfAboveCustomFormatScore')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="bypassIfAboveCustomFormatScore"
|
||||
{...bypassIfAboveCustomFormatScore}
|
||||
helpText={translate('BypassIfAboveCustomFormatScoreHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{
|
||||
bypassIfAboveCustomFormatScore.value ?
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('MinimumCustomFormatScore')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="minimumCustomFormatScore"
|
||||
{...minimumCustomFormatScore}
|
||||
helpText={translate('MinimumCustomFormatScoreHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.formGroupWrapper {
|
||||
.formGroupWrapper,
|
||||
.formatItemLarge {
|
||||
flex: 0 0 calc($formGroupSmallWidth - 100px);
|
||||
}
|
||||
|
||||
@@ -11,8 +12,20 @@
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointLarge) {
|
||||
.formatItemSmall {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: calc($breakpointLarge + 100px)) {
|
||||
.formGroupsContainer {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.formatItemSmall {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.formatItemLarge {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,23 @@ import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import dimensions from 'Styles/Variables/dimensions';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import QualityProfileFormatItems from './QualityProfileFormatItems';
|
||||
import QualityProfileItems from './QualityProfileItems';
|
||||
import styles from './EditQualityProfileModalContent.css';
|
||||
|
||||
const MODAL_BODY_PADDING = parseInt(dimensions.modalBodyPadding);
|
||||
|
||||
function getCustomFormatRender(formatItems, otherProps) {
|
||||
return (
|
||||
<QualityProfileFormatItems
|
||||
profileFormatItems={formatItems.value}
|
||||
errors={formatItems.errors}
|
||||
warnings={formatItems.warnings}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
class EditQualityProfileModalContent extends Component {
|
||||
|
||||
//
|
||||
@@ -93,6 +105,7 @@ class EditQualityProfileModalContent extends Component {
|
||||
isSaving,
|
||||
saveError,
|
||||
qualities,
|
||||
customFormats,
|
||||
item,
|
||||
isInUse,
|
||||
onInputChange,
|
||||
@@ -108,7 +121,10 @@ class EditQualityProfileModalContent extends Component {
|
||||
name,
|
||||
upgradeAllowed,
|
||||
cutoff,
|
||||
items
|
||||
minFormatScore,
|
||||
cutoffFormatScore,
|
||||
items,
|
||||
formatItems
|
||||
} = item;
|
||||
|
||||
return (
|
||||
@@ -157,7 +173,7 @@ class EditQualityProfileModalContent extends Component {
|
||||
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Upgrades Allowed
|
||||
{translate('UpgradesAllowed')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
@@ -186,6 +202,44 @@ class EditQualityProfileModalContent extends Component {
|
||||
/>
|
||||
</FormGroup>
|
||||
}
|
||||
|
||||
{
|
||||
formatItems.value.length > 0 &&
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Minimum Custom Format Score
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="minFormatScore"
|
||||
{...minFormatScore}
|
||||
helpText="Minimum custom format score allowed to download"
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
}
|
||||
|
||||
{
|
||||
upgradeAllowed.value && formatItems.value.length > 0 &&
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Upgrade Until Custom Format Score
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="cutoffFormatScore"
|
||||
{...cutoffFormatScore}
|
||||
helpText="Once this custom format score is reached Readarr will no longer grab book releases"
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
}
|
||||
|
||||
<div className={styles.formatItemLarge}>
|
||||
{getCustomFormatRender(formatItems, ...otherProps)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.formGroupWrapper}>
|
||||
@@ -197,9 +251,12 @@ class EditQualityProfileModalContent extends Component {
|
||||
{...otherProps}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.formatItemSmall}>
|
||||
{getCustomFormatRender(formatItems, otherProps)}
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
}
|
||||
</Measure>
|
||||
</ModalBody>
|
||||
@@ -209,7 +266,7 @@ class EditQualityProfileModalContent extends Component {
|
||||
>
|
||||
<ModalFooter>
|
||||
{
|
||||
id &&
|
||||
id ?
|
||||
<div
|
||||
className={styles.deleteButtonContainer}
|
||||
title={isInUse ? translate('IsInUseCantDeleteAQualityProfileThatIsAttachedToAnAuthorOrImportList') : undefined}
|
||||
@@ -221,7 +278,8 @@ class EditQualityProfileModalContent extends Component {
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
||||
<Button
|
||||
@@ -251,6 +309,7 @@ EditQualityProfileModalContent.propTypes = {
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
saveError: PropTypes.object,
|
||||
qualities: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
item: PropTypes.object.isRequired,
|
||||
isInUse: PropTypes.bool.isRequired,
|
||||
onInputChange: PropTypes.func.isRequired,
|
||||
|
||||
@@ -61,14 +61,46 @@ function createQualitiesSelector() {
|
||||
);
|
||||
}
|
||||
|
||||
function createFormatsSelector() {
|
||||
return createSelector(
|
||||
createProviderSettingsSelector('qualityProfiles'),
|
||||
(customFormat) => {
|
||||
const items = customFormat.item.formatItems;
|
||||
if (!items || !items.value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return _.reduceRight(items.value, (result, { id, name, format, score }) => {
|
||||
if (id) {
|
||||
result.push({
|
||||
key: id,
|
||||
value: name,
|
||||
score
|
||||
});
|
||||
} else {
|
||||
result.push({
|
||||
key: format,
|
||||
value: name,
|
||||
score
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createProviderSettingsSelector('qualityProfiles'),
|
||||
createQualitiesSelector(),
|
||||
createFormatsSelector(),
|
||||
createProfileInUseSelector('qualityProfileId'),
|
||||
(qualityProfile, qualities, isInUse) => {
|
||||
(qualityProfile, qualities, customFormats, isInUse) => {
|
||||
return {
|
||||
qualities,
|
||||
customFormats,
|
||||
...qualityProfile,
|
||||
isInUse
|
||||
};
|
||||
@@ -178,6 +210,19 @@ class EditQualityProfileModalContentConnector extends Component {
|
||||
this.ensureCutoff(qualityProfile);
|
||||
};
|
||||
|
||||
onQualityProfileFormatItemScoreChange = (id, score) => {
|
||||
const qualityProfile = _.cloneDeep(this.props.item);
|
||||
const formatItems = qualityProfile.formatItems.value;
|
||||
const item = _.find(qualityProfile.formatItems.value, (i) => i.format === id);
|
||||
|
||||
item.score = score;
|
||||
|
||||
this.props.setQualityProfileValue({
|
||||
name: 'formatItems',
|
||||
value: formatItems
|
||||
});
|
||||
};
|
||||
|
||||
onItemGroupAllowedChange = (id, allowed) => {
|
||||
const qualityProfile = _.cloneDeep(this.props.item);
|
||||
const items = qualityProfile.items.value;
|
||||
@@ -420,6 +465,7 @@ class EditQualityProfileModalContentConnector extends Component {
|
||||
onItemGroupNameChange={this.onItemGroupNameChange}
|
||||
onQualityProfileItemDragMove={this.onQualityProfileItemDragMove}
|
||||
onQualityProfileItemDragEnd={this.onQualityProfileItemDragEnd}
|
||||
onQualityProfileFormatItemScoreChange={this.onQualityProfileFormatItemScoreChange}
|
||||
onToggleEditGroupsMode={this.onToggleEditGroupsMode}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
.qualityProfileFormatItemContainer {
|
||||
display: flex;
|
||||
padding: $qualityProfileItemDragSourcePadding 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.qualityProfileFormatItem {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 4px;
|
||||
background: var(--inputBackgroundColor);
|
||||
}
|
||||
|
||||
.formatNameContainer {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
margin-bottom: 0;
|
||||
margin-left: 14px;
|
||||
width: 100%;
|
||||
font-weight: normal;
|
||||
line-height: $qualityProfileItemHeight;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.formatName {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.scoreContainer {
|
||||
display: flex;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.scoreInput {
|
||||
composes: input from '~Components/Form/Input.css';
|
||||
|
||||
width: 100px;
|
||||
height: 30px;
|
||||
border: unset;
|
||||
border-radius: unset;
|
||||
background-color: unset;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import NumberInput from 'Components/Form/NumberInput';
|
||||
import styles from './QualityProfileFormatItem.css';
|
||||
|
||||
class QualityProfileFormatItem extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onScoreChange = ({ value }) => {
|
||||
const {
|
||||
formatId
|
||||
} = this.props;
|
||||
|
||||
this.props.onScoreChange(formatId, value);
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
name,
|
||||
score
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.qualityProfileFormatItemContainer}
|
||||
>
|
||||
<div
|
||||
className={styles.qualityProfileFormatItem}
|
||||
>
|
||||
<label
|
||||
className={styles.formatNameContainer}
|
||||
>
|
||||
<div className={styles.formatName}>
|
||||
{name}
|
||||
</div>
|
||||
<NumberInput
|
||||
containerClassName={styles.scoreContainer}
|
||||
className={styles.scoreInput}
|
||||
name={name}
|
||||
value={score}
|
||||
onChange={this.onScoreChange}
|
||||
/>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QualityProfileFormatItem.propTypes = {
|
||||
formatId: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
score: PropTypes.number.isRequired,
|
||||
onScoreChange: PropTypes.func
|
||||
};
|
||||
|
||||
QualityProfileFormatItem.defaultProps = {
|
||||
// To handle the case score is deleted during edit
|
||||
score: 0
|
||||
};
|
||||
|
||||
export default QualityProfileFormatItem;
|
||||
@@ -0,0 +1,31 @@
|
||||
.formats {
|
||||
margin-top: 10px;
|
||||
/* TODO: This should consider the number of languages in the list */
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.headerContainer {
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
.headerTitle {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.headerScore {
|
||||
display: flex;
|
||||
flex-grow: 0;
|
||||
padding-left: 16px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.addCustomFormatMessage {
|
||||
max-width: $formGroupExtraSmallWidth;
|
||||
color: var(--helpTextColor);
|
||||
text-align: center;
|
||||
font-weight: 300;
|
||||
font-size: 20px;
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputHelpText from 'Components/Form/FormInputHelpText';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import Link from 'Components/Link/Link';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import QualityProfileFormatItem from './QualityProfileFormatItem';
|
||||
import styles from './QualityProfileFormatItems.css';
|
||||
|
||||
function calcOrder(profileFormatItems) {
|
||||
const items = profileFormatItems.reduce((acc, cur, index) => {
|
||||
acc[cur.format] = index;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return [...profileFormatItems].sort((a, b) => {
|
||||
if (b.score !== a.score) {
|
||||
return b.score - a.score;
|
||||
}
|
||||
return a.name > b.name ? 1 : -1;
|
||||
}).map((x) => items[x.format]);
|
||||
}
|
||||
|
||||
class QualityProfileFormatItems extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
order: calcOrder(this.props.profileFormatItems)
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onScoreChange = (formatId, value) => {
|
||||
const {
|
||||
onQualityProfileFormatItemScoreChange
|
||||
} = this.props;
|
||||
|
||||
onQualityProfileFormatItemScoreChange(formatId, value);
|
||||
this.reorderItems();
|
||||
};
|
||||
|
||||
reorderItems = _.debounce(() => this.setState({ order: calcOrder(this.props.profileFormatItems) }), 1000);
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
profileFormatItems,
|
||||
errors,
|
||||
warnings
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
order
|
||||
} = this.state;
|
||||
|
||||
if (profileFormatItems.length < 1) {
|
||||
return (
|
||||
<div className={styles.addCustomFormatMessage}>
|
||||
{'Want more control over which downloads are preferred? Add a'}
|
||||
<Link to='/settings/customformats'> Custom Format </Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormGroup size={sizes.EXTRA_SMALL}>
|
||||
<FormLabel size={sizes.SMALL}>
|
||||
Custom Formats
|
||||
</FormLabel>
|
||||
|
||||
<div>
|
||||
<FormInputHelpText
|
||||
text="Readarr scores each release using the sum of scores for matching custom formats. If a new release would improve the score, at the same or better quality, then Readarr will grab it."
|
||||
/>
|
||||
|
||||
{
|
||||
errors.map((error, index) => {
|
||||
return (
|
||||
<FormInputHelpText
|
||||
key={index}
|
||||
text={error.message}
|
||||
isError={true}
|
||||
isCheckInput={false}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
warnings.map((warning, index) => {
|
||||
return (
|
||||
<FormInputHelpText
|
||||
key={index}
|
||||
text={warning.message}
|
||||
isWarning={true}
|
||||
isCheckInput={false}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
<div className={styles.formats}>
|
||||
<div className={styles.headerContainer}>
|
||||
<div className={styles.headerTitle}>
|
||||
Custom Format
|
||||
</div>
|
||||
<div className={styles.headerScore}>
|
||||
Score
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
order.map((index) => {
|
||||
const {
|
||||
format,
|
||||
name,
|
||||
score
|
||||
} = profileFormatItems[index];
|
||||
return (
|
||||
<QualityProfileFormatItem
|
||||
key={format}
|
||||
formatId={format}
|
||||
name={name}
|
||||
score={score}
|
||||
onScoreChange={this.onScoreChange}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QualityProfileFormatItems.propTypes = {
|
||||
profileFormatItems: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
errors: PropTypes.arrayOf(PropTypes.object),
|
||||
warnings: PropTypes.arrayOf(PropTypes.object),
|
||||
onQualityProfileFormatItemScoreChange: PropTypes.func
|
||||
};
|
||||
|
||||
QualityProfileFormatItems.defaultProps = {
|
||||
errors: [],
|
||||
warnings: []
|
||||
};
|
||||
|
||||
export default QualityProfileFormatItems;
|
||||
@@ -14,8 +14,7 @@ import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './EditReleaseProfileModalContent.css';
|
||||
|
||||
// Tab, enter, and comma
|
||||
const tagInputDelimiters = [9, 13, 188];
|
||||
const tagInputDelimiters = ['Tab', 'Enter'];
|
||||
|
||||
function EditReleaseProfileModalContent(props) {
|
||||
const {
|
||||
@@ -34,8 +33,6 @@ function EditReleaseProfileModalContent(props) {
|
||||
enabled,
|
||||
required,
|
||||
ignored,
|
||||
preferred,
|
||||
includePreferredWhenRenaming,
|
||||
tags,
|
||||
indexerId
|
||||
} = item;
|
||||
@@ -96,41 +93,6 @@ function EditReleaseProfileModalContent(props) {
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('Preferred')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.KEY_VALUE_LIST}
|
||||
name="preferred"
|
||||
helpTexts={[
|
||||
translate('PreferredHelpTexts1'),
|
||||
translate('PreferredHelpTexts2'),
|
||||
translate('PreferredHelpTexts3')
|
||||
]}
|
||||
{...preferred}
|
||||
keyPlaceholder={translate('Term')}
|
||||
valuePlaceholder={translate('Score')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('IncludePreferredWhenRenaming')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="includePreferredWhenRenaming"
|
||||
helpText={indexerId.value === 0 ? translate('IndexerIdvalue0IncludeInPreferredWordsRenamingFormat') : translate('IndexerIdvalue0OnlySupportedWhenIndexerIsSetToAll')}
|
||||
{...includePreferredWhenRenaming}
|
||||
onChange={onInputChange}
|
||||
isDisabled={indexerId.value !== 0}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('Indexer')}
|
||||
|
||||
@@ -9,9 +9,8 @@ import EditReleaseProfileModalContent from './EditReleaseProfileModalContent';
|
||||
|
||||
const newReleaseProfile = {
|
||||
enabled: true,
|
||||
required: '',
|
||||
ignored: '',
|
||||
preferred: [],
|
||||
required: [],
|
||||
ignored: [],
|
||||
includePreferredWhenRenaming: false,
|
||||
tags: [],
|
||||
indexerId: 0
|
||||
|
||||
@@ -9,3 +9,9 @@
|
||||
flex-wrap: wrap;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.label {
|
||||
composes: label from '~Components/Label.css';
|
||||
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import MiddleTruncate from 'react-middle-truncate';
|
||||
import Card from 'Components/Card';
|
||||
import Label from 'Components/Label';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import TagList from 'Components/TagList';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import split from 'Utilities/String/split';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditReleaseProfileModalConnector from './EditReleaseProfileModalConnector';
|
||||
import styles from './ReleaseProfile.css';
|
||||
@@ -60,7 +60,6 @@ class ReleaseProfile extends Component {
|
||||
enabled,
|
||||
required,
|
||||
ignored,
|
||||
preferred,
|
||||
tags,
|
||||
indexerId,
|
||||
tagList,
|
||||
@@ -82,17 +81,22 @@ class ReleaseProfile extends Component {
|
||||
>
|
||||
<div>
|
||||
{
|
||||
split(required).map((item) => {
|
||||
required.map((item) => {
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Label
|
||||
className={styles.label}
|
||||
key={item}
|
||||
kind={kinds.SUCCESS}
|
||||
>
|
||||
{item}
|
||||
<MiddleTruncate
|
||||
text={item}
|
||||
start={10}
|
||||
end={10}
|
||||
/>
|
||||
</Label>
|
||||
);
|
||||
})
|
||||
@@ -101,34 +105,22 @@ class ReleaseProfile extends Component {
|
||||
|
||||
<div>
|
||||
{
|
||||
preferred.map((item) => {
|
||||
const isPreferred = item.value >= 0;
|
||||
|
||||
return (
|
||||
<Label
|
||||
key={item.key}
|
||||
kind={isPreferred ? kinds.DEFAULT : kinds.WARNING}
|
||||
>
|
||||
{item.key} {isPreferred && '+'}{item.value}
|
||||
</Label>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{
|
||||
split(ignored).map((item) => {
|
||||
ignored.map((item) => {
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Label
|
||||
className={styles.label}
|
||||
key={item}
|
||||
kind={kinds.DANGER}
|
||||
>
|
||||
{item}
|
||||
<MiddleTruncate
|
||||
text={item}
|
||||
start={10}
|
||||
end={10}
|
||||
/>
|
||||
</Label>
|
||||
);
|
||||
})
|
||||
@@ -186,9 +178,8 @@ class ReleaseProfile extends Component {
|
||||
ReleaseProfile.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
enabled: PropTypes.bool.isRequired,
|
||||
required: PropTypes.string.isRequired,
|
||||
ignored: PropTypes.string.isRequired,
|
||||
preferred: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
required: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
ignored: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
indexerId: PropTypes.number.isRequired,
|
||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
@@ -198,9 +189,8 @@ ReleaseProfile.propTypes = {
|
||||
|
||||
ReleaseProfile.defaultProps = {
|
||||
enabled: true,
|
||||
required: '',
|
||||
ignored: '',
|
||||
preferred: [],
|
||||
required: [],
|
||||
ignored: [],
|
||||
indexerId: 0
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user