mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-27 04:21:27 +02:00
Fixed: Quality Groups and Profiles
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import React, { Component } from 'react';
|
||||
import { DragDropContext } from 'react-dnd';
|
||||
import HTML5Backend from 'react-dnd-html5-backend';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
|
||||
import CustomFormatsConnector from './CustomFormats/CustomFormatsConnector';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
|
||||
class CustomFormatSettingsConnector extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<PageContent title="Custom Formats Settings">
|
||||
<SettingsToolbarConnector
|
||||
showSave={false}
|
||||
/>
|
||||
|
||||
<PageContentBodyConnector>
|
||||
<CustomFormatsConnector />
|
||||
</PageContentBodyConnector>
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DragDropContext(HTML5Backend)(CustomFormatSettingsConnector);
|
||||
|
@@ -0,0 +1,38 @@
|
||||
.customFormat {
|
||||
composes: card from '~Components/Card.css';
|
||||
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.nameContainer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.name {
|
||||
@add-mixin truncate;
|
||||
|
||||
margin-bottom: 20px;
|
||||
font-weight: 300;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.cloneButton {
|
||||
composes: button from '~Components/Link/IconButton.css';
|
||||
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.formats {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 5px;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.tooltipLabel {
|
||||
composes: label from '~Components/Label.css';
|
||||
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import Card from 'Components/Card';
|
||||
import Label from 'Components/Label';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
// import EditCustomFormatModalConnector from './EditCustomFormatModalConnector';
|
||||
import styles from './CustomFormat.css';
|
||||
|
||||
class CustomFormat extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isEditCustomFormatModalOpen: false,
|
||||
isDeleteCustomFormatModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onEditCustomFormatPress = () => {
|
||||
this.setState({ isEditCustomFormatModalOpen: true });
|
||||
}
|
||||
|
||||
onEditCustomFormatModalClose = () => {
|
||||
this.setState({ isEditCustomFormatModalOpen: false });
|
||||
}
|
||||
|
||||
onDeleteCustomFormatPress = () => {
|
||||
this.setState({
|
||||
isEditCustomFormatModalOpen: false,
|
||||
isDeleteCustomFormatModalOpen: true
|
||||
});
|
||||
}
|
||||
|
||||
onDeleteCustomFormatModalClose = () => {
|
||||
this.setState({ isDeleteCustomFormatModalOpen: false });
|
||||
}
|
||||
|
||||
onConfirmDeleteCustomFormat = () => {
|
||||
this.props.onConfirmDeleteCustomFormat(this.props.id);
|
||||
}
|
||||
|
||||
onCloneCustomFormatPress = () => {
|
||||
const {
|
||||
id,
|
||||
onCloneCustomFormatPress
|
||||
} = this.props;
|
||||
|
||||
onCloneCustomFormatPress(id);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
// id,
|
||||
name,
|
||||
items,
|
||||
isDeleting
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={styles.CustomFormat}
|
||||
overlayContent={true}
|
||||
onPress={this.onEditCustomFormatPress}
|
||||
>
|
||||
<div className={styles.nameContainer}>
|
||||
<div className={styles.name}>
|
||||
{name}
|
||||
</div>
|
||||
|
||||
<IconButton
|
||||
className={styles.cloneButton}
|
||||
title="Clone Profile"
|
||||
name={icons.CLONE}
|
||||
onPress={this.onCloneCustomFormatPress}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.formats}>
|
||||
{
|
||||
items.map((item) => {
|
||||
if (!item.allowed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Label
|
||||
key={item.quality.id}
|
||||
kind={kinds.default}
|
||||
title={null}
|
||||
>
|
||||
{item.quality.name}
|
||||
</Label>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
{/* <EditCustomFormatModalConnector
|
||||
id={id}
|
||||
isOpen={this.state.isEditCustomFormatModalOpen}
|
||||
onModalClose={this.onEditCustomFormatModalClose}
|
||||
onDeleteCustomFormatPress={this.onDeleteCustomFormatPress}
|
||||
/> */}
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={this.state.isDeleteCustomFormatModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Custom Format"
|
||||
message={`Are you sure you want to delete the custom format '${name}'?`}
|
||||
confirmLabel="Delete"
|
||||
isSpinning={isDeleting}
|
||||
onConfirm={this.onConfirmDeleteCustomFormat}
|
||||
onCancel={this.onDeleteCustomFormatModalClose}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CustomFormat.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
isDeleting: PropTypes.bool.isRequired,
|
||||
onConfirmDeleteCustomFormat: PropTypes.func.isRequired,
|
||||
onCloneCustomFormatPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default CustomFormat;
|
@@ -0,0 +1,21 @@
|
||||
.customFormats {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.addCustomFormat {
|
||||
composes: customFormat from '~./CustomFormat.css';
|
||||
|
||||
background-color: $cardAlternateBackgroundColor;
|
||||
color: $gray;
|
||||
text-align: center;
|
||||
font-size: 45px;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: inline-block;
|
||||
padding: 5px 20px 0;
|
||||
border: 1px solid $borderColor;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import sortByName from 'Utilities/Array/sortByName';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import Card from 'Components/Card';
|
||||
import Icon from 'Components/Icon';
|
||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||
import CustomFormat from './CustomFormat';
|
||||
// import EditCustomFormatModalConnector from './EditCustomFormatModalConnector';
|
||||
import styles from './CustomFormats.css';
|
||||
|
||||
class CustomFormats extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isCustomFormatModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onCloneCustomFormatPress = (id) => {
|
||||
this.props.onCloneCustomFormatPress(id);
|
||||
this.setState({ isCustomFormatModalOpen: true });
|
||||
}
|
||||
|
||||
onEditCustomFormatPress = () => {
|
||||
this.setState({ isCustomFormatModalOpen: true });
|
||||
}
|
||||
|
||||
onModalClose = () => {
|
||||
this.setState({ isCustomFormatModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
isDeleting,
|
||||
onConfirmDeleteCustomFormat,
|
||||
onCloneCustomFormatPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldSet legend="Custom Formats">
|
||||
<PageSectionContent
|
||||
errorMessage="Unable to load Custom Formats"
|
||||
{...otherProps}c={true}
|
||||
>
|
||||
<div className={styles.CustomFormats}>
|
||||
{
|
||||
items.sort(sortByName).map((item) => {
|
||||
return (
|
||||
<CustomFormat
|
||||
key={item.id}
|
||||
{...item}
|
||||
isDeleting={isDeleting}
|
||||
onConfirmDeleteCustomFormat={onConfirmDeleteCustomFormat}
|
||||
onCloneCustomFormatPress={this.onCloneCustomFormatPress}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
<Card
|
||||
className={styles.addCustomFormat}
|
||||
onPress={this.onEditCustomFormatPress}
|
||||
>
|
||||
<div className={styles.center}>
|
||||
<Icon
|
||||
name={icons.ADD}
|
||||
size={45}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/*
|
||||
<EditCustomFormatModalConnector
|
||||
isOpen={this.state.isCustomFormatModalOpen}
|
||||
onModalClose={this.onModalClose}
|
||||
/> */}
|
||||
|
||||
</PageSectionContent>
|
||||
</FieldSet>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CustomFormats.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
isDeleting: PropTypes.bool.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onConfirmDeleteCustomFormat: PropTypes.func.isRequired,
|
||||
onCloneCustomFormatPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default CustomFormats;
|
@@ -0,0 +1,65 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchCustomFormats, deleteCustomFormat, cloneCustomFormat } from 'Store/Actions/settingsActions';
|
||||
import CustomFormats from './CustomFormats';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.settings.customFormats,
|
||||
(customFormats) => {
|
||||
return {
|
||||
...customFormats
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
dispatchFetchCustomFormats: fetchCustomFormats,
|
||||
dispatchDeleteCustomFormat: deleteCustomFormat,
|
||||
dispatchCloneCustomFormat: cloneCustomFormat
|
||||
};
|
||||
|
||||
class CustomFormatsConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.dispatchFetchCustomFormats();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onConfirmDeleteCustomFormat = (id) => {
|
||||
this.props.dispatchDeleteCustomFormat({ id });
|
||||
}
|
||||
|
||||
onCloneCustomFormatPress = (id) => {
|
||||
this.props.dispatchCloneCustomFormat({ id });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<CustomFormats
|
||||
onConfirmDeleteCustomFormat={this.onConfirmDeleteCustomFormat}
|
||||
onCloneCustomFormatPress={this.onCloneCustomFormatPress}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CustomFormatsConnector.propTypes = {
|
||||
dispatchFetchCustomFormats: PropTypes.func.isRequired,
|
||||
dispatchDeleteCustomFormat: PropTypes.func.isRequired,
|
||||
dispatchCloneCustomFormat: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(CustomFormatsConnector);
|
@@ -1,17 +0,0 @@
|
||||
import React from 'react';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
|
||||
function CustomFormatsConnector() {
|
||||
return (
|
||||
<PageContent title="Custom Formats Settings">
|
||||
<SettingsToolbarConnector
|
||||
showSave={false}
|
||||
/>
|
||||
|
||||
</PageContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomFormatsConnector;
|
||||
|
@@ -121,7 +121,7 @@ class EditQualityProfileModalContentConnector extends Component {
|
||||
return false;
|
||||
}
|
||||
|
||||
return i.id === cutoff.id || (i.quality && i.quality.id === cutoff.id);
|
||||
return i.id === cutoff || (i.quality && i.quality.id === cutoff);
|
||||
});
|
||||
|
||||
// If the cutoff isn't allowed anymore or there isn't a cutoff set one
|
||||
|
@@ -97,20 +97,20 @@ class QualityProfile extends Component {
|
||||
}
|
||||
|
||||
if (item.quality) {
|
||||
const isCutoff = item.quality.id === cutoff.id;
|
||||
const isCutoff = item.quality.id === cutoff;
|
||||
|
||||
return (
|
||||
<Label
|
||||
key={item.quality.id}
|
||||
kind={isCutoff ? kinds.INFO : kinds.default}
|
||||
title={isCutoff ? 'Cutoff' : null}
|
||||
title={isCutoff ? 'Upgrade until this quality is met or exceeded' : null}
|
||||
>
|
||||
{item.quality.name}
|
||||
</Label>
|
||||
);
|
||||
}
|
||||
|
||||
const isCutoff = item.id === cutoff.id;
|
||||
const isCutoff = item.id === cutoff;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
@@ -174,7 +174,7 @@ class QualityProfile extends Component {
|
||||
QualityProfile.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
cutoff: PropTypes.object.isRequired,
|
||||
cutoff: PropTypes.number.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
isDeleting: PropTypes.bool.isRequired,
|
||||
onConfirmDeleteQualityProfile: PropTypes.func.isRequired,
|
||||
|
Reference in New Issue
Block a user