New: Readarr 0.1

This commit is contained in:
ta264
2020-05-06 21:14:11 +01:00
parent 476f2d6047
commit 08496c82af
911 changed files with 14837 additions and 24442 deletions
@@ -12,9 +12,6 @@ import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormLabel from 'Components/Form/FormLabel';
import FormInputGroup from 'Components/Form/FormInputGroup';
import PrimaryTypeItems from './PrimaryTypeItems';
import SecondaryTypeItems from './SecondaryTypeItems';
import ReleaseStatusItems from './ReleaseStatusItems';
import styles from './EditMetadataProfileModalContent.css';
function EditMetadataProfileModalContent(props) {
@@ -23,8 +20,6 @@ function EditMetadataProfileModalContent(props) {
error,
isSaving,
saveError,
primaryAlbumTypes,
secondaryAlbumTypes,
item,
isInUse,
onInputChange,
@@ -37,9 +32,13 @@ function EditMetadataProfileModalContent(props) {
const {
id,
name,
primaryAlbumTypes: itemPrimaryAlbumTypes,
secondaryAlbumTypes: itemSecondaryAlbumTypes,
releaseStatuses: itemReleaseStatuses
minRating,
minRatingCount,
skipMissingDate,
skipMissingIsbn,
skipPartsAndSets,
skipSeriesSecondary,
allowedLanguages
} = item;
return (
@@ -73,29 +72,86 @@ function EditMetadataProfileModalContent(props) {
/>
</FormGroup>
<PrimaryTypeItems
metadataProfileItems={itemPrimaryAlbumTypes.value}
errors={itemPrimaryAlbumTypes.errors}
warnings={itemPrimaryAlbumTypes.warnings}
formLabel="Primary Album Types"
{...otherProps}
/>
<FormGroup>
<FormLabel>Minimum Rating</FormLabel>
<SecondaryTypeItems
metadataProfileItems={itemSecondaryAlbumTypes.value}
errors={itemSecondaryAlbumTypes.errors}
warnings={itemSecondaryAlbumTypes.warnings}
formLabel="Secondary Album Types"
{...otherProps}
/>
<FormInputGroup
type={inputTypes.NUMBER}
name="minRating"
{...minRating}
isFloat={true}
min={0}
max={5}
onChange={onInputChange}
/>
</FormGroup>
<ReleaseStatusItems
metadataProfileItems={itemReleaseStatuses.value}
errors={itemReleaseStatuses.errors}
warnings={itemReleaseStatuses.warnings}
formLabel="Release Statuses"
{...otherProps}
/>
<FormGroup>
<FormLabel>Minimum Number of Ratings</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="minRatingCount"
{...minRatingCount}
min={0}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Skip books with missing release date</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="skipMissingDate"
{...skipMissingDate}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Skip books with no ISBN or ASIN</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="skipMissingIsbn"
{...skipMissingIsbn}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Skip part books and sets</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="skipPartsAndSets"
{...skipPartsAndSets}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Skip secondary series books</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="skipSeriesSecondary"
{...skipSeriesSecondary}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Allowed Languages</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="allowedLanguages"
{...allowedLanguages}
onChange={onInputChange}
/>
</FormGroup>
</Form>
}
@@ -140,9 +196,6 @@ EditMetadataProfileModalContent.propTypes = {
error: PropTypes.object,
isSaving: PropTypes.bool.isRequired,
saveError: PropTypes.object,
primaryAlbumTypes: PropTypes.arrayOf(PropTypes.object).isRequired,
secondaryAlbumTypes: PropTypes.arrayOf(PropTypes.object).isRequired,
releaseStatuses: PropTypes.arrayOf(PropTypes.object).isRequired,
item: PropTypes.object.isRequired,
isInUse: PropTypes.bool.isRequired,
onInputChange: PropTypes.func.isRequired,
@@ -1,4 +1,3 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@@ -8,87 +7,12 @@ import createProviderSettingsSelector from 'Store/Selectors/createProviderSettin
import { fetchMetadataProfileSchema, setMetadataProfileValue, saveMetadataProfile } from 'Store/Actions/settingsActions';
import EditMetadataProfileModalContent from './EditMetadataProfileModalContent';
function createPrimaryAlbumTypesSelector() {
return createSelector(
createProviderSettingsSelector('metadataProfiles'),
(metadataProfile) => {
const primaryAlbumTypes = metadataProfile.item.primaryAlbumTypes;
if (!primaryAlbumTypes || !primaryAlbumTypes.value) {
return [];
}
return _.reduceRight(primaryAlbumTypes.value, (result, { allowed, albumType }) => {
if (allowed) {
result.push({
key: albumType.id,
value: albumType.name
});
}
return result;
}, []);
}
);
}
function createSecondaryAlbumTypesSelector() {
return createSelector(
createProviderSettingsSelector('metadataProfiles'),
(metadataProfile) => {
const secondaryAlbumTypes = metadataProfile.item.secondaryAlbumTypes;
if (!secondaryAlbumTypes || !secondaryAlbumTypes.value) {
return [];
}
return _.reduceRight(secondaryAlbumTypes.value, (result, { allowed, albumType }) => {
if (allowed) {
result.push({
key: albumType.id,
value: albumType.name
});
}
return result;
}, []);
}
);
}
function createReleaseStatusesSelector() {
return createSelector(
createProviderSettingsSelector('metadataProfiles'),
(metadataProfile) => {
const releaseStatuses = metadataProfile.item.releaseStatuses;
if (!releaseStatuses || !releaseStatuses.value) {
return [];
}
return _.reduceRight(releaseStatuses.value, (result, { allowed, releaseStatus }) => {
if (allowed) {
result.push({
key: releaseStatus.id,
value: releaseStatus.name
});
}
return result;
}, []);
}
);
}
function createMapStateToProps() {
return createSelector(
createProviderSettingsSelector('metadataProfiles'),
createPrimaryAlbumTypesSelector(),
createSecondaryAlbumTypesSelector(),
createReleaseStatusesSelector(),
createProfileInUseSelector('metadataProfileId'),
(metadataProfile, primaryAlbumTypes, secondaryAlbumTypes, releaseStatuses, isInUse) => {
(metadataProfile, isInUse) => {
return {
primaryAlbumTypes,
secondaryAlbumTypes,
releaseStatuses,
...metadataProfile,
isInUse
};
@@ -139,59 +63,16 @@ class EditMetadataProfileModalContentConnector extends Component {
this.props.saveMetadataProfile({ id: this.props.id });
}
onMetadataPrimaryTypeItemAllowedChange = (id, allowed) => {
const metadataProfile = _.cloneDeep(this.props.item);
const item = _.find(metadataProfile.primaryAlbumTypes.value, (i) => i.albumType.id === id);
item.allowed = allowed;
this.props.setMetadataProfileValue({
name: 'primaryAlbumTypes',
value: metadataProfile.primaryAlbumTypes.value
});
}
onMetadataSecondaryTypeItemAllowedChange = (id, allowed) => {
const metadataProfile = _.cloneDeep(this.props.item);
const item = _.find(metadataProfile.secondaryAlbumTypes.value, (i) => i.albumType.id === id);
item.allowed = allowed;
this.props.setMetadataProfileValue({
name: 'secondaryAlbumTypes',
value: metadataProfile.secondaryAlbumTypes.value
});
}
onMetadataReleaseStatusItemAllowedChange = (id, allowed) => {
const metadataProfile = _.cloneDeep(this.props.item);
const item = _.find(metadataProfile.releaseStatuses.value, (i) => i.releaseStatus.id === id);
item.allowed = allowed;
this.props.setMetadataProfileValue({
name: 'releaseStatuses',
value: metadataProfile.releaseStatuses.value
});
}
//
// Render
render() {
if (_.isEmpty(this.props.item.primaryAlbumTypes) && !this.props.isFetching) {
return null;
}
return (
<EditMetadataProfileModalContent
{...this.state}
{...this.props}
onSavePress={this.onSavePress}
onInputChange={this.onInputChange}
onMetadataPrimaryTypeItemAllowedChange={this.onMetadataPrimaryTypeItemAllowedChange}
onMetadataSecondaryTypeItemAllowedChange={this.onMetadataSecondaryTypeItemAllowedChange}
onMetadataReleaseStatusItemAllowedChange={this.onMetadataReleaseStatusItemAllowedChange}
/>
);
}
@@ -2,7 +2,6 @@ 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 EditMetadataProfileModalConnector from './EditMetadataProfileModalConnector';
@@ -64,8 +63,6 @@ class MetadataProfile extends Component {
const {
id,
name,
primaryAlbumTypes,
secondaryAlbumTypes,
isDeleting
} = this.props;
@@ -88,46 +85,6 @@ class MetadataProfile extends Component {
/>
</div>
<div className={styles.albumTypes}>
{
primaryAlbumTypes.map((item) => {
if (!item.allowed) {
return null;
}
return (
<Label
key={item.albumType.id}
kind={kinds.default}
title={null}
>
{item.albumType.name}
</Label>
);
})
}
</div>
<div className={styles.albumTypes}>
{
secondaryAlbumTypes.map((item) => {
if (!item.allowed) {
return null;
}
return (
<Label
key={item.albumType.id}
kind={kinds.INFO}
title={null}
>
{item.albumType.name}
</Label>
);
})
}
</div>
<EditMetadataProfileModalConnector
id={id}
isOpen={this.state.isEditMetadataProfileModalOpen}
@@ -153,8 +110,6 @@ class MetadataProfile extends Component {
MetadataProfile.propTypes = {
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
primaryAlbumTypes: PropTypes.arrayOf(PropTypes.object).isRequired,
secondaryAlbumTypes: PropTypes.arrayOf(PropTypes.object).isRequired,
isDeleting: PropTypes.bool.isRequired,
onConfirmDeleteMetadataProfile: PropTypes.func.isRequired,
onCloneMetadataProfilePress: PropTypes.func.isRequired
@@ -1,60 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import classNames from 'classnames';
import CheckInput from 'Components/Form/CheckInput';
import styles from './TypeItem.css';
class PrimaryTypeItem extends Component {
//
// Listeners
onAllowedChange = ({ value }) => {
const {
albumTypeId,
onMetadataPrimaryTypeItemAllowedChange
} = this.props;
onMetadataPrimaryTypeItemAllowedChange(albumTypeId, value);
}
//
// Render
render() {
const {
name,
allowed
} = this.props;
return (
<div
className={classNames(
styles.metadataProfileItem
)}
>
<label
className={styles.albumTypeName}
>
<CheckInput
containerClassName={styles.checkContainer}
name={name}
value={allowed}
onChange={this.onAllowedChange}
/>
{name}
</label>
</div>
);
}
}
PrimaryTypeItem.propTypes = {
albumTypeId: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
allowed: PropTypes.bool.isRequired,
sortIndex: PropTypes.number.isRequired,
onMetadataPrimaryTypeItemAllowedChange: PropTypes.func
};
export default PrimaryTypeItem;
@@ -1,87 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import FormGroup from 'Components/Form/FormGroup';
import FormLabel from 'Components/Form/FormLabel';
import FormInputHelpText from 'Components/Form/FormInputHelpText';
import PrimaryTypeItem from './PrimaryTypeItem';
import styles from './TypeItems.css';
class PrimaryTypeItems extends Component {
//
// Render
render() {
const {
metadataProfileItems,
errors,
warnings,
...otherProps
} = this.props;
return (
<FormGroup>
<FormLabel>Primary Types</FormLabel>
<div>
{
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.albumTypes}>
{
metadataProfileItems.map(({ allowed, albumType }, index) => {
return (
<PrimaryTypeItem
key={albumType.id}
albumTypeId={albumType.id}
name={albumType.name}
allowed={allowed}
sortIndex={index}
{...otherProps}
/>
);
}).reverse()
}
</div>
</div>
</FormGroup>
);
}
}
PrimaryTypeItems.propTypes = {
metadataProfileItems: PropTypes.arrayOf(PropTypes.object).isRequired,
errors: PropTypes.arrayOf(PropTypes.object),
warnings: PropTypes.arrayOf(PropTypes.object),
formLabel: PropTypes.string
};
PrimaryTypeItems.defaultProps = {
errors: [],
warnings: []
};
export default PrimaryTypeItems;
@@ -1,60 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import classNames from 'classnames';
import CheckInput from 'Components/Form/CheckInput';
import styles from './TypeItem.css';
class ReleaseStatusItem extends Component {
//
// Listeners
onAllowedChange = ({ value }) => {
const {
albumTypeId,
onMetadataReleaseStatusItemAllowedChange
} = this.props;
onMetadataReleaseStatusItemAllowedChange(albumTypeId, value);
}
//
// Render
render() {
const {
name,
allowed
} = this.props;
return (
<div
className={classNames(
styles.metadataProfileItem
)}
>
<label
className={styles.albumTypeName}
>
<CheckInput
containerClassName={styles.checkContainer}
name={name}
value={allowed}
onChange={this.onAllowedChange}
/>
{name}
</label>
</div>
);
}
}
ReleaseStatusItem.propTypes = {
albumTypeId: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
allowed: PropTypes.bool.isRequired,
sortIndex: PropTypes.number.isRequired,
onMetadataReleaseStatusItemAllowedChange: PropTypes.func
};
export default ReleaseStatusItem;
@@ -1,87 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import FormGroup from 'Components/Form/FormGroup';
import FormLabel from 'Components/Form/FormLabel';
import FormInputHelpText from 'Components/Form/FormInputHelpText';
import ReleaseStatusItem from './ReleaseStatusItem';
import styles from './TypeItems.css';
class ReleaseStatusItems extends Component {
//
// Render
render() {
const {
metadataProfileItems,
errors,
warnings,
...otherProps
} = this.props;
return (
<FormGroup>
<FormLabel>Release Statuses</FormLabel>
<div>
{
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.albumTypes}>
{
metadataProfileItems.map(({ allowed, releaseStatus }, index) => {
return (
<ReleaseStatusItem
key={releaseStatus.id}
albumTypeId={releaseStatus.id}
name={releaseStatus.name}
allowed={allowed}
sortIndex={index}
{...otherProps}
/>
);
})
}
</div>
</div>
</FormGroup>
);
}
}
ReleaseStatusItems.propTypes = {
metadataProfileItems: PropTypes.arrayOf(PropTypes.object).isRequired,
errors: PropTypes.arrayOf(PropTypes.object),
warnings: PropTypes.arrayOf(PropTypes.object),
formLabel: PropTypes.string
};
ReleaseStatusItems.defaultProps = {
errors: [],
warnings: []
};
export default ReleaseStatusItems;
@@ -1,60 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import classNames from 'classnames';
import CheckInput from 'Components/Form/CheckInput';
import styles from './TypeItem.css';
class SecondaryTypeItem extends Component {
//
// Listeners
onAllowedChange = ({ value }) => {
const {
albumTypeId,
onMetadataSecondaryTypeItemAllowedChange
} = this.props;
onMetadataSecondaryTypeItemAllowedChange(albumTypeId, value);
}
//
// Render
render() {
const {
name,
allowed
} = this.props;
return (
<div
className={classNames(
styles.metadataProfileItem
)}
>
<label
className={styles.albumTypeName}
>
<CheckInput
containerClassName={styles.checkContainer}
name={name}
value={allowed}
onChange={this.onAllowedChange}
/>
{name}
</label>
</div>
);
}
}
SecondaryTypeItem.propTypes = {
albumTypeId: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
allowed: PropTypes.bool.isRequired,
sortIndex: PropTypes.number.isRequired,
onMetadataSecondaryTypeItemAllowedChange: PropTypes.func
};
export default SecondaryTypeItem;
@@ -1,87 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import FormGroup from 'Components/Form/FormGroup';
import FormLabel from 'Components/Form/FormLabel';
import FormInputHelpText from 'Components/Form/FormInputHelpText';
import SecondaryTypeItem from './SecondaryTypeItem';
import styles from './TypeItems.css';
class SecondaryTypeItems extends Component {
//
// Render
render() {
const {
metadataProfileItems,
errors,
warnings,
...otherProps
} = this.props;
return (
<FormGroup>
<FormLabel>Secondary Types</FormLabel>
<div>
{
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.albumTypes}>
{
metadataProfileItems.map(({ allowed, albumType }, index) => {
return (
<SecondaryTypeItem
key={albumType.id}
albumTypeId={albumType.id}
name={albumType.name}
allowed={allowed}
sortIndex={index}
{...otherProps}
/>
);
})
}
</div>
</div>
</FormGroup>
);
}
}
SecondaryTypeItems.propTypes = {
metadataProfileItems: PropTypes.arrayOf(PropTypes.object).isRequired,
errors: PropTypes.arrayOf(PropTypes.object),
warnings: PropTypes.arrayOf(PropTypes.object),
formLabel: PropTypes.string
};
SecondaryTypeItems.defaultProps = {
errors: [],
warnings: []
};
export default SecondaryTypeItems;
@@ -1,25 +0,0 @@
.metadataProfileItem {
display: flex;
align-items: stretch;
width: 100%;
}
.checkContainer {
position: relative;
margin-right: 4px;
margin-bottom: 7px;
margin-left: 8px;
}
.albumTypeName {
display: flex;
flex-grow: 1;
margin-bottom: 0;
margin-left: 2px;
font-weight: normal;
line-height: 36px;
}
.isDragging {
opacity: 0.25;
}
@@ -1,6 +0,0 @@
.albumTypes {
margin-top: 10px;
/* TODO: This should consider the number of types in the list */
min-height: 200px;
user-select: none;
}