New: User defined scores for each Custom Format

Brings it more into line with Sonarr preferred words
This commit is contained in:
ta264
2020-02-25 22:10:52 +00:00
parent da80793204
commit 50d6c5e61e
49 changed files with 547 additions and 791 deletions

View File

@@ -94,10 +94,18 @@ class CustomFormat extends Component {
return null;
}
let kind = kinds.DEFAULT;
if (item.required) {
kind = kinds.SUCCESS;
}
if (item.negate) {
kind = kinds.DANGER;
}
return (
<Label
key={index}
kind={item.required ? kinds.DANGER : kinds.DEFAULT}
kind={kind}
>
{item.name}
</Label>

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { kinds } from 'Helpers/Props';
import Alert from 'Components/Alert';
import Link from 'Components/Link/Link';
import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ModalContent from 'Components/Modal/ModalContent';
@@ -48,8 +49,8 @@ class AddSpecificationModalContent extends Component {
<div>
<Alert kind={kinds.INFO}>
<div>Radarr supports custom conditions against the following release properties</div>
<div>Visit github for more details</div>
<div>Radarr supports custom conditions against the release properties below.</div>
<div>Visit <Link to='https://github.com/Radarr/Radarr/wiki/Custom-Formats-Aphrodite'>GitHub</Link> for more details.</div>
</Alert>
<div className={styles.specifications}>

View File

@@ -90,14 +90,14 @@ class Specification extends Component {
{
negate &&
<Label kind={kinds.INVERSE}>
<Label kind={kinds.DANGER}>
{'Negated'}
</Label>
}
{
required &&
<Label kind={kinds.DANGER}>
<Label kind={kinds.SUCCESS}>
{'Required'}
</Label>
}

View File

@@ -0,0 +1,6 @@
.addCustomFormatMessage {
color: $helpTextColor;
text-align: center;
font-weight: 300;
font-size: 20px;
}

View File

@@ -3,9 +3,11 @@ import { DndProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import PageContent from 'Components/Page/PageContent';
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
import Link from 'Components/Link/Link';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import QualityProfilesConnector from './Quality/QualityProfilesConnector';
import DelayProfilesConnector from './Delay/DelayProfilesConnector';
import styles from './Profiles.css';
// Only a single DragDrop Context can exist so it's done here to allow editing
// quality profiles and reordering delay profiles to work.
@@ -25,6 +27,11 @@ class Profiles extends Component {
<DndProvider backend={HTML5Backend}>
<QualityProfilesConnector />
<DelayProfilesConnector />
<div className={styles.addCustomFormatMessage}>
Looking for Release Profiles? Try
<Link to='/settings/customformats'> Custom Formats </Link>
instead.
</div>
</DndProvider>
</PageContentBodyConnector>
</PageContent>

View File

@@ -99,7 +99,6 @@ class EditQualityProfileModalContent extends Component {
isInUse,
onInputChange,
onCutoffChange,
onFormatCutoffChange,
onLanguageChange,
onSavePress,
onModalClose,
@@ -112,7 +111,8 @@ class EditQualityProfileModalContent extends Component {
name,
upgradeAllowed,
cutoff,
formatCutoff,
minFormatScore,
cutoffFormatScore,
language,
items,
formatItems
@@ -201,19 +201,35 @@ class EditQualityProfileModalContent extends Component {
}
{
upgradeAllowed.value &&
formatItems.value.length > 0 &&
<FormGroup size={sizes.EXTRA_SMALL}>
<FormLabel size={sizes.SMALL}>
Upgrade Until Format
Minimum Custom Format Score
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="formatCutoff"
{...formatCutoff}
values={customFormats}
helpText="Once this custom format is reached Radarr will no longer download movies"
onChange={onFormatCutoffChange}
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 Radarr will no longer download movies"
onChange={onInputChange}
/>
</FormGroup>
}
@@ -320,7 +336,6 @@ EditQualityProfileModalContent.propTypes = {
isInUse: PropTypes.bool.isRequired,
onInputChange: PropTypes.func.isRequired,
onCutoffChange: PropTypes.func.isRequired,
onFormatCutoffChange: PropTypes.func.isRequired,
onLanguageChange: PropTypes.func.isRequired,
onSavePress: PropTypes.func.isRequired,
onContentHeightChange: PropTypes.func.isRequired,

View File

@@ -70,19 +70,19 @@ function createFormatsSelector() {
return [];
}
return _.reduceRight(items.value, (result, { allowed, id, name, format }) => {
if (allowed) {
if (id) {
result.push({
key: id,
value: name
});
} else {
result.push({
key: format,
value: name
});
}
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;
@@ -193,30 +193,6 @@ class EditQualityProfileModalContentConnector extends Component {
}
}
ensureFormatCutoff = (qualityProfile) => {
const cutoff = qualityProfile.formatCutoff.value;
const cutoffItem = _.find(qualityProfile.formatItems.value, (i) => {
if (!cutoff) {
return false;
}
return i.id === cutoff || (i.format === cutoff);
});
// If the cutoff isn't allowed anymore or there isn't a cutoff set one
if (!cutoff || !cutoffItem || !cutoffItem.allowed) {
const firstAllowed = _.find(qualityProfile.formatItems.value, { allowed: true });
let cutoffId = null;
if (firstAllowed) {
cutoffId = firstAllowed.format;
}
this.props.setQualityProfileValue({ name: 'formatCutoff', value: cutoffId });
}
}
//
// Listeners
@@ -239,13 +215,6 @@ class EditQualityProfileModalContentConnector extends Component {
this.props.setQualityProfileValue({ name, value: cutoffId });
}
onFormatCutoffChange = ({ name, value }) => {
const id = parseInt(value);
const cutoffId = _.find(this.props.item.formatItems.value, (i) => i.format === id).format;
this.props.setQualityProfileValue({ name, value: cutoffId });
}
onLanguageChange = ({ name, value }) => {
const id = parseInt(value);
@@ -274,19 +243,17 @@ class EditQualityProfileModalContentConnector extends Component {
this.ensureCutoff(qualityProfile);
}
onQualityProfileFormatItemAllowedChange = (id, allowed) => {
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.allowed = allowed;
item.score = score;
this.props.setQualityProfileValue({
name: 'formatItems',
value: formatItems
});
this.ensureFormatCutoff(qualityProfile);
}
onItemGroupAllowedChange = (id, allowed) => {
@@ -505,39 +472,6 @@ class EditQualityProfileModalContentConnector extends Component {
});
}
onQualityProfileFormatItemDragMove = (dragIndex, dropIndex) => {
if (this.state.dragIndex !== dragIndex || this.state.dropIndex !== dropIndex) {
this.setState({
dragIndex,
dropIndex
});
}
}
onQualityProfileFormatItemDragEnd = ({ id }, didDrop) => {
const {
dragIndex,
dropIndex
} = this.state;
if (didDrop && dropIndex !== null) {
const qualityProfile = _.cloneDeep(this.props.item);
const formats = qualityProfile.formatItems.value.splice(dragIndex, 1);
qualityProfile.formatItems.value.splice(dropIndex, 0, formats[0]);
this.props.setQualityProfileValue({
name: 'formatItems',
value: qualityProfile.formatItems.value
});
}
this.setState({
dragIndex: null,
dropIndex: null
});
}
onToggleEditGroupsMode = () => {
this.setState({ editGroups: !this.state.editGroups });
}
@@ -557,18 +491,15 @@ class EditQualityProfileModalContentConnector extends Component {
onSavePress={this.onSavePress}
onInputChange={this.onInputChange}
onCutoffChange={this.onCutoffChange}
onFormatCutoffChange={this.onFormatCutoffChange}
onLanguageChange={this.onLanguageChange}
onCreateGroupPress={this.onCreateGroupPress}
onDeleteGroupPress={this.onDeleteGroupPress}
onQualityProfileItemAllowedChange={this.onQualityProfileItemAllowedChange}
onQualityProfileFormatItemAllowedChange={this.onQualityProfileFormatItemAllowedChange}
onItemGroupAllowedChange={this.onItemGroupAllowedChange}
onItemGroupNameChange={this.onItemGroupNameChange}
onQualityProfileItemDragMove={this.onQualityProfileItemDragMove}
onQualityProfileItemDragEnd={this.onQualityProfileItemDragEnd}
onQualityProfileFormatItemDragMove={this.onQualityProfileFormatItemDragMove}
onQualityProfileFormatItemDragEnd={this.onQualityProfileFormatItemDragEnd}
onQualityProfileFormatItemScoreChange={this.onQualityProfileFormatItemScoreChange}
onToggleEditGroupsMode={this.onToggleEditGroupsMode}
/>
);

View File

@@ -1,3 +1,9 @@
.qualityProfileFormatItemContainer {
display: flex;
padding: $qualityProfileItemDragSourcePadding 0;
width: 100%;
}
.qualityProfileFormatItem {
display: flex;
align-items: stretch;
@@ -7,38 +13,33 @@
background: #fafafa;
}
.checkContainer {
position: relative;
margin-right: 4px;
margin-bottom: 7px;
margin-left: 8px;
.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;
margin-bottom: 0;
margin-left: 2px;
font-weight: normal;
line-height: 36px;
cursor: pointer;
}
.dragHandle {
.scoreContainer {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-left: auto;
width: $dragHandleWidth;
text-align: center;
cursor: grab;
flex-grow: 0;
}
.dragIcon {
top: 0;
}
.scoreInput {
composes: input from '~Components/Form/Input.css';
.isDragging {
opacity: 0.25;
width: 100px;
height: 30px;
border: unset;
border-radius: unset;
background-color: unset;
}

View File

@@ -1,9 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import classNames from 'classnames';
import { icons } from 'Helpers/Props';
import Icon from 'Components/Icon';
import CheckInput from 'Components/Form/CheckInput';
import NumberInput from 'Components/Form/NumberInput';
import styles from './QualityProfileFormatItem.css';
class QualityProfileFormatItem extends Component {
@@ -11,13 +8,12 @@ class QualityProfileFormatItem extends Component {
//
// Listeners
onAllowedChange = ({ value }) => {
onScoreChange = ({ value }) => {
const {
formatId,
onQualityProfileFormatItemAllowedChange
formatId
} = this.props;
onQualityProfileFormatItemAllowedChange(formatId, value);
this.props.onScoreChange(formatId, value);
}
//
@@ -26,40 +22,32 @@ class QualityProfileFormatItem extends Component {
render() {
const {
name,
allowed,
isDragging,
connectDragSource
score
} = this.props;
return (
<div
className={classNames(
styles.qualityProfileFormatItem,
isDragging && styles.isDragging
)}
className={styles.qualityProfileFormatItemContainer}
>
<label
className={styles.formatName}
<div
className={styles.qualityProfileFormatItem}
>
<CheckInput
containerClassName={styles.checkContainer}
name={name}
value={allowed}
onChange={this.onAllowedChange}
/>
{name}
</label>
{
connectDragSource(
<div className={styles.dragHandle}>
<Icon
className={styles.dragIcon}
name={icons.REORDER}
/>
<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>
);
}
@@ -68,16 +56,13 @@ class QualityProfileFormatItem extends Component {
QualityProfileFormatItem.propTypes = {
formatId: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
allowed: PropTypes.bool.isRequired,
sortIndex: PropTypes.number.isRequired,
isDragging: PropTypes.bool.isRequired,
connectDragSource: PropTypes.func,
onQualityProfileFormatItemAllowedChange: PropTypes.func
score: PropTypes.number.isRequired,
onScoreChange: PropTypes.func
};
QualityProfileFormatItem.defaultProps = {
// The drag preview will not connect the drag handle.
connectDragSource: (node) => node
// To handle the case score is deleted during edit
score: 0
};
export default QualityProfileFormatItem;

View File

@@ -1,4 +0,0 @@
.dragPreview {
width: 380px;
opacity: 0.75;
}

View File

@@ -1,88 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { DragLayer } from 'react-dnd';
import dimensions from 'Styles/Variables/dimensions.js';
import { QUALITY_PROFILE_FORMAT_ITEM } from 'Helpers/dragTypes';
import DragPreviewLayer from 'Components/DragPreviewLayer';
import QualityProfileFormatItem from './QualityProfileFormatItem';
import styles from './QualityProfileFormatItemDragPreview.css';
const formGroupSmallWidth = parseInt(dimensions.formGroupSmallWidth);
const formLabelLargeWidth = parseInt(dimensions.formLabelLargeWidth);
const formLabelRightMarginWidth = parseInt(dimensions.formLabelRightMarginWidth);
const dragHandleWidth = parseInt(dimensions.dragHandleWidth);
function collectDragLayer(monitor) {
return {
item: monitor.getItem(),
itemType: monitor.getItemType(),
currentOffset: monitor.getSourceClientOffset()
};
}
class QualityProfileFormatItemDragPreview extends Component {
//
// Render
render() {
const {
item,
itemType,
currentOffset
} = this.props;
if (!currentOffset || itemType !== QUALITY_PROFILE_FORMAT_ITEM) {
return null;
}
// The offset is shifted because the drag handle is on the right edge of the
// list item and the preview is wider than the drag handle.
const { x, y } = currentOffset;
const handleOffset = formGroupSmallWidth - formLabelLargeWidth - formLabelRightMarginWidth - dragHandleWidth;
const transform = `translate3d(${x - handleOffset}px, ${y}px, 0)`;
const style = {
position: 'absolute',
WebkitTransform: transform,
msTransform: transform,
transform
};
const {
formatId,
name,
allowed,
sortIndex
} = item;
return (
<DragPreviewLayer>
<div
className={styles.dragPreview}
style={style}
>
<QualityProfileFormatItem
formatId={formatId}
name={name}
allowed={allowed}
sortIndex={sortIndex}
isDragging={false}
/>
</div>
</DragPreviewLayer>
);
}
}
QualityProfileFormatItemDragPreview.propTypes = {
item: PropTypes.object,
itemType: PropTypes.string,
currentOffset: PropTypes.shape({
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired
})
};
export default DragLayer(collectDragLayer)(QualityProfileFormatItemDragPreview);

View File

@@ -1,18 +0,0 @@
.qualityProfileFormatItemDragSource {
padding: 4px 0;
}
.qualityProfileFormatItemPlaceholder {
width: 100%;
height: 36px;
border: 1px dotted #aaa;
border-radius: 4px;
}
.qualityProfileFormatItemPlaceholderBefore {
margin-bottom: 8px;
}
.qualityProfileFormatItemPlaceholderAfter {
margin-top: 8px;
}

View File

@@ -1,157 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import { DragSource, DropTarget } from 'react-dnd';
import classNames from 'classnames';
import { QUALITY_PROFILE_FORMAT_ITEM } from 'Helpers/dragTypes';
import QualityProfileFormatItem from './QualityProfileFormatItem';
import styles from './QualityProfileFormatItemDragSource.css';
const qualityProfileFormatItemDragSource = {
beginDrag({ formatId, name, allowed, sortIndex }) {
return {
formatId,
name,
allowed,
sortIndex
};
},
endDrag(props, monitor, component) {
props.onQualityProfileFormatItemDragEnd(monitor.getItem(), monitor.didDrop());
}
};
const qualityProfileFormatItemDropTarget = {
hover(props, monitor, component) {
const dragIndex = monitor.getItem().sortIndex;
const hoverIndex = props.sortIndex;
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
const clientOffset = monitor.getClientOffset();
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
// Moving up, only trigger if drag position is above 50%
if (dragIndex < hoverIndex && hoverClientY > hoverMiddleY) {
return;
}
// Moving down, only trigger if drag position is below 50%
if (dragIndex > hoverIndex && hoverClientY < hoverMiddleY) {
return;
}
props.onQualityProfileFormatItemDragMove(dragIndex, hoverIndex);
}
};
function collectDragSource(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
};
}
function collectDropTarget(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver()
};
}
class QualityProfileFormatItemDragSource extends Component {
//
// Render
render() {
const {
formatId,
name,
allowed,
sortIndex,
isDragging,
isDraggingUp,
isDraggingDown,
isOver,
connectDragSource,
connectDropTarget,
onQualityProfileFormatItemAllowedChange
} = this.props;
const isBefore = !isDragging && isDraggingUp && isOver;
const isAfter = !isDragging && isDraggingDown && isOver;
// if (isDragging && !isOver) {
// return null;
// }
return connectDropTarget(
<div
className={classNames(
styles.qualityProfileFormatItemDragSource,
isBefore && styles.isDraggingUp,
isAfter && styles.isDraggingDown
)}
>
{
isBefore &&
<div
className={classNames(
styles.qualityProfileFormatItemPlaceholder,
styles.qualityProfileFormatItemPlaceholderBefore
)}
/>
}
<QualityProfileFormatItem
formatId={formatId}
name={name}
allowed={allowed}
sortIndex={sortIndex}
isDragging={isDragging}
isOver={isOver}
connectDragSource={connectDragSource}
onQualityProfileFormatItemAllowedChange={onQualityProfileFormatItemAllowedChange}
/>
{
isAfter &&
<div
className={classNames(
styles.qualityProfileFormatItemPlaceholder,
styles.qualityProfileFormatItemPlaceholderAfter
)}
/>
}
</div>
);
}
}
QualityProfileFormatItemDragSource.propTypes = {
formatId: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
allowed: PropTypes.bool.isRequired,
sortIndex: PropTypes.number.isRequired,
isDragging: PropTypes.bool,
isDraggingUp: PropTypes.bool,
isDraggingDown: PropTypes.bool,
isOver: PropTypes.bool,
connectDragSource: PropTypes.func,
connectDropTarget: PropTypes.func,
onQualityProfileFormatItemAllowedChange: PropTypes.func.isRequired,
onQualityProfileFormatItemDragMove: PropTypes.func.isRequired,
onQualityProfileFormatItemDragEnd: PropTypes.func.isRequired
};
export default DropTarget(
QUALITY_PROFILE_FORMAT_ITEM,
qualityProfileFormatItemDropTarget,
collectDropTarget
)(DragSource(
QUALITY_PROFILE_FORMAT_ITEM,
qualityProfileFormatItemDragSource,
collectDragSource
)(QualityProfileFormatItemDragSource));

View File

@@ -1,6 +1,31 @@
.formats {
margin-top: 10px;
/* TODO: This should consider the number of languages in the list */
min-height: 550px;
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: $helpTextColor;
text-align: center;
font-weight: 300;
font-size: 20px;
}

View File

@@ -1,37 +1,87 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { sizes } from 'Helpers/Props';
import FormGroup from 'Components/Form/FormGroup';
import FormLabel from 'Components/Form/FormLabel';
import FormInputHelpText from 'Components/Form/FormInputHelpText';
import QualityProfileFormatItemDragSource from './QualityProfileFormatItemDragSource';
import QualityProfileFormatItemDragPreview from './QualityProfileFormatItemDragPreview';
import Link from 'Components/Link/Link';
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 {
dragIndex,
dropIndex,
profileFormatItems,
errors,
warnings,
...otherProps
warnings
} = this.props;
const isDragging = dropIndex !== null;
const isDraggingUp = isDragging && dropIndex > dragIndex;
const isDraggingDown = isDragging && dropIndex < dragIndex;
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>
<FormLabel>Custom Formats</FormLabel>
<FormGroup size={sizes.EXTRA_SMALL}>
<FormLabel size={sizes.SMALL}>
Custom Formats
</FormLabel>
<div>
<FormInputHelpText
text="Custom Formats higher in the list are more preferred. Only checked custom formats are wanted"
text='Radarr 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 Radarr will grab it.'
/>
{
@@ -61,25 +111,32 @@ class QualityProfileFormatItems extends Component {
}
<div className={styles.formats}>
<div className={styles.headerContainer}>
<div className={styles.headerTitle}>
Custom Format
</div>
<div className={styles.headerScore}>
Score
</div>
</div>
{
profileFormatItems.map(({ allowed, format, name }, index) => {
order.map((index) => {
const {
format,
name,
score
} = profileFormatItems[index];
return (
<QualityProfileFormatItemDragSource
<QualityProfileFormatItem
key={format}
formatId={format}
name={name}
allowed={allowed}
sortIndex={index}
isDragging={isDragging}
isDraggingUp={isDraggingUp}
isDraggingDown={isDraggingDown}
{...otherProps}
score={score}
onScoreChange={this.onScoreChange}
/>
);
}).reverse()
})
}
<QualityProfileFormatItemDragPreview />
</div>
</div>
</FormGroup>
@@ -88,11 +145,10 @@ class QualityProfileFormatItems extends Component {
}
QualityProfileFormatItems.propTypes = {
dragIndex: PropTypes.number,
dropIndex: PropTypes.number,
profileFormatItems: PropTypes.arrayOf(PropTypes.object).isRequired,
errors: PropTypes.arrayOf(PropTypes.object),
warnings: PropTypes.arrayOf(PropTypes.object)
warnings: PropTypes.arrayOf(PropTypes.object),
onQualityProfileFormatItemScoreChange: PropTypes.func
};
QualityProfileFormatItems.defaultProps = {