mirror of
https://github.com/Radarr/Radarr.git
synced 2024-10-05 15:47:20 +02:00
Fixed: Minor improvements and translations for managing bulk indexers, lists and clients
This commit is contained in:
parent
7dde88387a
commit
48b9c1e8b9
@ -86,7 +86,7 @@ class DownloadClientSettings extends Component {
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label="Manage Clients"
|
||||
label={translate('ManageClients')}
|
||||
iconName={icons.MANAGE}
|
||||
onPress={this.onManageDownloadClientsPress}
|
||||
/>
|
||||
|
@ -27,9 +27,9 @@ interface ManageDownloadClientsEditModalContentProps {
|
||||
const NO_CHANGE = 'noChange';
|
||||
|
||||
const enableOptions = [
|
||||
{ key: NO_CHANGE, value: 'No Change', disabled: true },
|
||||
{ key: 'enabled', value: 'Enabled' },
|
||||
{ key: 'disabled', value: 'Disabled' },
|
||||
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
|
||||
{ key: 'enabled', value: translate('Enabled') },
|
||||
{ key: 'disabled', value: translate('Disabled') },
|
||||
];
|
||||
|
||||
function ManageDownloadClientsEditModalContent(
|
||||
@ -97,7 +97,9 @@ function ManageDownloadClientsEditModalContent(
|
||||
setRemoveFailedDownloads(value);
|
||||
break;
|
||||
default:
|
||||
console.warn('EditDownloadClientsModalContent Unknown Input');
|
||||
console.warn(
|
||||
`EditDownloadClientsModalContent Unknown Input: '${name}'`
|
||||
);
|
||||
}
|
||||
},
|
||||
[]
|
||||
@ -162,9 +164,7 @@ function ManageDownloadClientsEditModalContent(
|
||||
|
||||
<ModalFooter className={styles.modalFooter}>
|
||||
<div className={styles.selected}>
|
||||
{translate('{count} download clients selected', {
|
||||
count: selectedCount,
|
||||
})}
|
||||
{translate('CountDownloadClientsSelected', [selectedCount])}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { DownloadClientAppState } from 'App/State/SettingsAppState';
|
||||
import Alert from 'Components/Alert';
|
||||
import Button from 'Components/Link/Button';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
@ -20,6 +21,7 @@ import {
|
||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import { SelectStateInputProps } from 'typings/props';
|
||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
import ManageDownloadClientsEditModal from './Edit/ManageDownloadClientsEditModal';
|
||||
import ManageDownloadClientsModalRow from './ManageDownloadClientsModalRow';
|
||||
@ -33,37 +35,37 @@ type OnSelectedChangeCallback = React.ComponentProps<
|
||||
const COLUMNS = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
label: translate('Name'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'implementation',
|
||||
label: 'Implementation',
|
||||
label: translate('Implementation'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'enable',
|
||||
label: 'Enabled',
|
||||
label: translate('Enabled'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'priority',
|
||||
label: 'Priority',
|
||||
label: translate('Priority'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'removeCompletedDownloads',
|
||||
label: 'Remove Completed',
|
||||
label: translate('RemoveCompleted'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'removeFailedDownloads',
|
||||
label: 'Remove Failed',
|
||||
label: translate('RemoveFailed'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
@ -158,17 +160,24 @@ function ManageDownloadClientsModalContent(
|
||||
[items, setSelectState]
|
||||
);
|
||||
|
||||
const errorMessage = getErrorMessage(error, 'Unable to load import lists.');
|
||||
const errorMessage = getErrorMessage(
|
||||
error,
|
||||
'Unable to load download clients.'
|
||||
);
|
||||
const anySelected = selectedCount > 0;
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>Manage Import Lists</ModalHeader>
|
||||
<ModalHeader>{translate('ManageDownloadClients')}</ModalHeader>
|
||||
<ModalBody>
|
||||
{isFetching ? <LoadingIndicator /> : null}
|
||||
|
||||
{error ? <div>{errorMessage}</div> : null}
|
||||
|
||||
{isPopulated && !error && !items.length && (
|
||||
<Alert kind={kinds.INFO}>{translate('NoDownloadClientsFound')}</Alert>
|
||||
)}
|
||||
|
||||
{isPopulated && !!items.length && !isFetching && !isFetching ? (
|
||||
<Table
|
||||
columns={COLUMNS}
|
||||
@ -203,7 +212,7 @@ function ManageDownloadClientsModalContent(
|
||||
isDisabled={!anySelected}
|
||||
onPress={onDeletePress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</SpinnerButton>
|
||||
|
||||
<SpinnerButton
|
||||
@ -211,11 +220,11 @@ function ManageDownloadClientsModalContent(
|
||||
isDisabled={!anySelected}
|
||||
onPress={onEditPress}
|
||||
>
|
||||
Edit
|
||||
{translate('Edit')}
|
||||
</SpinnerButton>
|
||||
</div>
|
||||
|
||||
<Button onPress={onModalClose}>Close</Button>
|
||||
<Button onPress={onModalClose}>{translate('Close')}</Button>
|
||||
</ModalFooter>
|
||||
|
||||
<ManageDownloadClientsEditModal
|
||||
@ -228,9 +237,11 @@ function ManageDownloadClientsModalContent(
|
||||
<ConfirmModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Download Clients(s)"
|
||||
message={`Are you sure you want to delete ${selectedIds.length} download clients(s)?`}
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteSelectedDownloadClients')}
|
||||
message={translate('DeleteSelectedDownloadClientsMessageText', [
|
||||
selectedIds.length,
|
||||
])}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={onDeleteModalClose}
|
||||
/>
|
||||
|
@ -1,9 +1,12 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import Label from 'Components/Label';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import Column from 'Components/Table/Column';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import { SelectStateInputProps } from 'typings/props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ManageDownloadClientsModalRow.css';
|
||||
|
||||
interface ManageDownloadClientsModalRowProps {
|
||||
@ -58,17 +61,19 @@ function ManageDownloadClientsModalRow(
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.enable}>
|
||||
{enable ? 'Yes' : 'No'}
|
||||
<Label kind={enable ? kinds.SUCCESS : kinds.DISABLED} outline={!enable}>
|
||||
{enable ? translate('Yes') : translate('No')}
|
||||
</Label>
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.priority}>{priority}</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.removeCompletedDownloads}>
|
||||
{removeCompletedDownloads ? 'Yes' : 'No'}
|
||||
{removeCompletedDownloads ? translate('Yes') : translate('No')}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.removeFailedDownloads}>
|
||||
{removeFailedDownloads ? 'Yes' : 'No'}
|
||||
{removeFailedDownloads ? translate('Yes') : translate('No')}
|
||||
</TableRowCell>
|
||||
</TableRow>
|
||||
);
|
||||
|
@ -86,7 +86,7 @@ class ImportListSettings extends Component {
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label="Manage Lists"
|
||||
label={translate('ManageLists')}
|
||||
iconName={icons.MANAGE}
|
||||
onPress={this.onManageImportListsPress}
|
||||
/>
|
||||
|
@ -26,9 +26,9 @@ interface ManageImportListsEditModalContentProps {
|
||||
const NO_CHANGE = 'noChange';
|
||||
|
||||
const autoAddOptions = [
|
||||
{ key: NO_CHANGE, value: 'No Change', disabled: true },
|
||||
{ key: 'enabled', value: 'Enabled' },
|
||||
{ key: 'disabled', value: 'Disabled' },
|
||||
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
|
||||
{ key: 'enabled', value: translate('Enabled') },
|
||||
{ key: 'disabled', value: translate('Disabled') },
|
||||
];
|
||||
|
||||
function ManageImportListsEditModalContent(
|
||||
@ -36,7 +36,7 @@ function ManageImportListsEditModalContent(
|
||||
) {
|
||||
const { importListIds, onSavePress, onModalClose } = props;
|
||||
|
||||
const [enableAuto, setenableAuto] = useState(NO_CHANGE);
|
||||
const [enableAuto, setEnableAuto] = useState(NO_CHANGE);
|
||||
const [qualityProfileId, setQualityProfileId] = useState<string | number>(
|
||||
NO_CHANGE
|
||||
);
|
||||
@ -72,7 +72,7 @@ function ManageImportListsEditModalContent(
|
||||
({ name, value }: { name: string; value: string }) => {
|
||||
switch (name) {
|
||||
case 'enableAuto':
|
||||
setenableAuto(value);
|
||||
setEnableAuto(value);
|
||||
break;
|
||||
case 'qualityProfileId':
|
||||
setQualityProfileId(value);
|
||||
@ -81,7 +81,7 @@ function ManageImportListsEditModalContent(
|
||||
setRootFolderPath(value);
|
||||
break;
|
||||
default:
|
||||
console.warn('EditImportListModalContent Unknown Input');
|
||||
console.warn(`EditImportListModalContent Unknown Input: '${name}'`);
|
||||
}
|
||||
},
|
||||
[]
|
||||
@ -136,7 +136,7 @@ function ManageImportListsEditModalContent(
|
||||
|
||||
<ModalFooter className={styles.modalFooter}>
|
||||
<div className={styles.selected}>
|
||||
{translate('{count} import lists selected', { count: selectedCount })}
|
||||
{translate('CountImportListsSelected', [selectedCount])}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { ImportListAppState } from 'App/State/SettingsAppState';
|
||||
import Alert from 'Components/Alert';
|
||||
import Button from 'Components/Link/Button';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
@ -20,6 +21,7 @@ import {
|
||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import { SelectStateInputProps } from 'typings/props';
|
||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
import ManageImportListsEditModal from './Edit/ManageImportListsEditModal';
|
||||
import ManageImportListsModalRow from './ManageImportListsModalRow';
|
||||
@ -34,37 +36,37 @@ type OnSelectedChangeCallback = React.ComponentProps<
|
||||
const COLUMNS = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
label: translate('Name'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'implementation',
|
||||
label: 'Implementation',
|
||||
label: translate('Implementation'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'qualityProfileId',
|
||||
label: 'Quality Profile',
|
||||
label: translate('QualityProfile'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'rootFolderPath',
|
||||
label: 'Root Folder',
|
||||
label: translate('RootFolder'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'enableAuto',
|
||||
label: 'Auto Add',
|
||||
label: translate('AutomaticAdd'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'tags',
|
||||
label: 'Tags',
|
||||
label: translate('Tags'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
@ -190,12 +192,16 @@ function ManageImportListsModalContent(
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>Manage Import Lists</ModalHeader>
|
||||
<ModalHeader>{translate('ManageImportLists')}</ModalHeader>
|
||||
<ModalBody>
|
||||
{isFetching ? <LoadingIndicator /> : null}
|
||||
|
||||
{error ? <div>{errorMessage}</div> : null}
|
||||
|
||||
{isPopulated && !error && !items.length && (
|
||||
<Alert kind={kinds.INFO}>{translate('NoImportListsFound')}</Alert>
|
||||
)}
|
||||
|
||||
{isPopulated && !!items.length && !isFetching && !isFetching ? (
|
||||
<Table
|
||||
columns={COLUMNS}
|
||||
@ -230,7 +236,7 @@ function ManageImportListsModalContent(
|
||||
isDisabled={!anySelected}
|
||||
onPress={onDeletePress}
|
||||
>
|
||||
Delete
|
||||
{translate('Delete')}
|
||||
</SpinnerButton>
|
||||
|
||||
<SpinnerButton
|
||||
@ -238,7 +244,7 @@ function ManageImportListsModalContent(
|
||||
isDisabled={!anySelected}
|
||||
onPress={onEditPress}
|
||||
>
|
||||
Edit
|
||||
{translate('Edit')}
|
||||
</SpinnerButton>
|
||||
|
||||
<SpinnerButton
|
||||
@ -246,11 +252,11 @@ function ManageImportListsModalContent(
|
||||
isDisabled={!anySelected}
|
||||
onPress={onTagsPress}
|
||||
>
|
||||
Set Tags
|
||||
{translate('SetTags')}
|
||||
</SpinnerButton>
|
||||
</div>
|
||||
|
||||
<Button onPress={onModalClose}>Close</Button>
|
||||
<Button onPress={onModalClose}>{translate('Close')}</Button>
|
||||
</ModalFooter>
|
||||
|
||||
<ManageImportListsEditModal
|
||||
@ -270,9 +276,11 @@ function ManageImportListsModalContent(
|
||||
<ConfirmModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title="Delete Import List(s)"
|
||||
message={`Are you sure you want to delete ${selectedIds.length} import list(s)?`}
|
||||
confirmLabel="Delete"
|
||||
title={translate('DeleteSelectedImportLists')}
|
||||
message={translate('DeleteSelectedImportListsMessageText', [
|
||||
selectedIds.length,
|
||||
])}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={onDeleteModalClose}
|
||||
/>
|
||||
|
@ -17,6 +17,7 @@ import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||
import ImportList from 'typings/ImportList';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './TagsModalContent.css';
|
||||
|
||||
interface TagsModalContentProps {
|
||||
@ -36,7 +37,7 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
const [tags, setTags] = useState<number[]>([]);
|
||||
const [applyTags, setApplyTags] = useState('add');
|
||||
|
||||
const seriesTags = useMemo(() => {
|
||||
const importListsTags = useMemo(() => {
|
||||
const tags = ids.reduce((acc: number[], id) => {
|
||||
const s = allImportLists.items.find((s: ImportList) => s.id === id);
|
||||
|
||||
@ -69,19 +70,19 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
}, [tags, applyTags, onApplyTagsPress]);
|
||||
|
||||
const applyTagsOptions = [
|
||||
{ key: 'add', value: 'Add' },
|
||||
{ key: 'remove', value: 'Remove' },
|
||||
{ key: 'replace', value: 'Replace' },
|
||||
{ key: 'add', value: translate('Add') },
|
||||
{ key: 'remove', value: translate('Remove') },
|
||||
{ key: 'replace', value: translate('Replace') },
|
||||
];
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>Tags</ModalHeader>
|
||||
<ModalHeader>{translate('Tags')}</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<Form>
|
||||
<FormGroup>
|
||||
<FormLabel>Tags</FormLabel>
|
||||
<FormLabel>{translate('Tags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
@ -92,7 +93,7 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Apply Tags</FormLabel>
|
||||
<FormLabel>{translate('ApplyTags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
@ -100,20 +101,20 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
value={applyTags}
|
||||
values={applyTagsOptions}
|
||||
helpTexts={[
|
||||
'How to apply tags to the selected list',
|
||||
'Add: Add the tags the existing list of tags',
|
||||
'Remove: Remove the entered tags',
|
||||
'Replace: Replace the tags with the entered tags (enter no tags to clear all tags)',
|
||||
translate('ApplyTagsHelpTexts1'),
|
||||
translate('ApplyTagsHelpTexts2'),
|
||||
translate('ApplyTagsHelpTexts3'),
|
||||
translate('ApplyTagsHelpTexts4'),
|
||||
]}
|
||||
onChange={onApplyTagsChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Result</FormLabel>
|
||||
<FormLabel>{translate('Result')}</FormLabel>
|
||||
|
||||
<div className={styles.result}>
|
||||
{seriesTags.map((id) => {
|
||||
{importListsTags.map((id) => {
|
||||
const tag = tagList.find((t) => t.id === id);
|
||||
|
||||
if (!tag) {
|
||||
@ -127,7 +128,11 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
return (
|
||||
<Label
|
||||
key={tag.id}
|
||||
title={removeTag ? 'Removing tag' : 'Existing tag'}
|
||||
title={
|
||||
removeTag
|
||||
? translate('RemovingTag')
|
||||
: translate('ExistingTag')
|
||||
}
|
||||
kind={removeTag ? kinds.INVERSE : kinds.INFO}
|
||||
size={sizes.LARGE}
|
||||
>
|
||||
@ -144,14 +149,14 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (seriesTags.indexOf(id) > -1) {
|
||||
if (importListsTags.indexOf(id) > -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Label
|
||||
key={tag.id}
|
||||
title={'Adding tag'}
|
||||
title={translate('AddingTag')}
|
||||
kind={kinds.SUCCESS}
|
||||
size={sizes.LARGE}
|
||||
>
|
||||
@ -165,10 +170,10 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={onModalClose}>Cancel</Button>
|
||||
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
|
||||
|
||||
<Button kind={kinds.PRIMARY} onPress={onApplyPress}>
|
||||
Apply
|
||||
{translate('Apply')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
@ -86,7 +86,7 @@ class IndexerSettings extends Component {
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label="Manage Indexers"
|
||||
label={translate('ManageIndexers')}
|
||||
iconName={icons.MANAGE}
|
||||
onPress={this.onManageIndexersPress}
|
||||
/>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { IndexerAppState } from 'App/State/SettingsAppState';
|
||||
import Alert from 'Components/Alert';
|
||||
import Button from 'Components/Link/Button';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
@ -201,6 +202,10 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
|
||||
|
||||
{error ? <div>{errorMessage}</div> : null}
|
||||
|
||||
{isPopulated && !error && !items.length && (
|
||||
<Alert kind={kinds.INFO}>{translate('NoIndexersFound')}</Alert>
|
||||
)}
|
||||
|
||||
{isPopulated && !!items.length && !isFetching && !isFetching ? (
|
||||
<Table
|
||||
columns={COLUMNS}
|
||||
@ -275,8 +280,10 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
|
||||
<ConfirmModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteIndexers')}
|
||||
message={translate('DeleteIndexersMessageText', [selectedIds.length])}
|
||||
title={translate('DeleteSelectedIndexers')}
|
||||
message={translate('DeleteSelectedIndexersMessageText', [
|
||||
selectedIds.length,
|
||||
])}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={onDeleteModalClose}
|
||||
|
@ -1,9 +1,11 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import Label from 'Components/Label';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import Column from 'Components/Table/Column';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import TagListConnector from 'Components/TagListConnector';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import { SelectStateInputProps } from 'typings/props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ManageIndexersModalRow.css';
|
||||
@ -60,15 +62,30 @@ function ManageIndexersModalRow(props: ManageIndexersModalRowProps) {
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.enableRss}>
|
||||
{enableRss ? translate('Yes') : translate('No')}
|
||||
<Label
|
||||
kind={enableRss ? kinds.SUCCESS : kinds.DISABLED}
|
||||
outline={!enableRss}
|
||||
>
|
||||
{enableRss ? translate('Yes') : translate('No')}
|
||||
</Label>
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.enableAutomaticSearch}>
|
||||
{enableAutomaticSearch ? translate('Yes') : translate('No')}
|
||||
<Label
|
||||
kind={enableAutomaticSearch ? kinds.SUCCESS : kinds.DISABLED}
|
||||
outline={!enableAutomaticSearch}
|
||||
>
|
||||
{enableAutomaticSearch ? translate('Yes') : translate('No')}
|
||||
</Label>
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.enableInteractiveSearch}>
|
||||
{enableInteractiveSearch ? translate('Yes') : translate('No')}
|
||||
<Label
|
||||
kind={enableInteractiveSearch ? kinds.SUCCESS : kinds.DISABLED}
|
||||
outline={!enableInteractiveSearch}
|
||||
>
|
||||
{enableInteractiveSearch ? translate('Yes') : translate('No')}
|
||||
</Label>
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.priority}>{priority}</TableRowCell>
|
||||
|
@ -37,7 +37,7 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
const [tags, setTags] = useState<number[]>([]);
|
||||
const [applyTags, setApplyTags] = useState('add');
|
||||
|
||||
const seriesTags = useMemo(() => {
|
||||
const indexersTags = useMemo(() => {
|
||||
const tags = ids.reduce((acc: number[], id) => {
|
||||
const s = allIndexers.items.find((s: Indexer) => s.id === id);
|
||||
|
||||
@ -101,10 +101,10 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
value={applyTags}
|
||||
values={applyTagsOptions}
|
||||
helpTexts={[
|
||||
'How to apply tags to the selected indexer(s)',
|
||||
'Add: Add the tags the existing list of tags',
|
||||
'Remove: Remove the entered tags',
|
||||
'Replace: Replace the tags with the entered tags (enter no tags to clear all tags)',
|
||||
translate('ApplyTagsHelpTexts1'),
|
||||
translate('ApplyTagsHelpTexts2'),
|
||||
translate('ApplyTagsHelpTexts3'),
|
||||
translate('ApplyTagsHelpTexts4'),
|
||||
]}
|
||||
onChange={onApplyTagsChange}
|
||||
/>
|
||||
@ -114,7 +114,7 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
<FormLabel>{translate('Result')}</FormLabel>
|
||||
|
||||
<div className={styles.result}>
|
||||
{seriesTags.map((id) => {
|
||||
{indexersTags.map((id) => {
|
||||
const tag = tagList.find((t) => t.id === id);
|
||||
|
||||
if (!tag) {
|
||||
@ -149,7 +149,7 @@ function TagsModalContent(props: TagsModalContentProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (seriesTags.indexOf(id) > -1) {
|
||||
if (indexersTags.indexOf(id) > -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,7 @@
|
||||
"AutoRedownloadFailedHelpText": "Automatically search for and attempt to download a different release",
|
||||
"AutoUnmonitorPreviouslyDownloadedMoviesHelpText": "Movies deleted from the disk are automatically unmonitored in Radarr",
|
||||
"Automatic": "Automatic",
|
||||
"AutomaticAdd": "Automatic Add",
|
||||
"AutomaticSearch": "Automatic Search",
|
||||
"AvailabilityDelay": "Availability Delay",
|
||||
"AvailabilityDelayHelpText": "Amount of time before or after available date to search for Movie",
|
||||
@ -161,7 +162,9 @@
|
||||
"CopyUsingHardlinksHelpTextWarning": "Occasionally, file locks may prevent renaming files that are being seeded. You may temporarily disable seeding and use Radarr's rename function as a work around.",
|
||||
"CouldNotConnectSignalR": "Could not connect to SignalR, UI won't update",
|
||||
"CouldNotFindResults": "Couldn't find any results for '{0}'",
|
||||
"CountIndexersSelected": "{0} indexers selected",
|
||||
"CountDownloadClientsSelected": "{0} download client(s) selected",
|
||||
"CountImportListsSelected": "{0} import list(s) selected",
|
||||
"CountIndexersSelected": "{0} indexer(s) selected",
|
||||
"CreateEmptyMovieFolders": "Create empty movie folders",
|
||||
"CreateEmptyMovieFoldersHelpText": "Create missing movie folders during disk scan",
|
||||
"CreateGroup": "Create group",
|
||||
@ -230,6 +233,12 @@
|
||||
"DeleteRemotePathMappingMessageText": "Are you sure you want to delete this remote path mapping?",
|
||||
"DeleteRestriction": "Delete Restriction",
|
||||
"DeleteRestrictionHelpText": "Are you sure you want to delete this restriction?",
|
||||
"DeleteSelectedDownloadClients": "Delete Download Client(s)",
|
||||
"DeleteSelectedDownloadClientsMessageText": "Are you sure you want to delete {0} selected download client(s)?",
|
||||
"DeleteSelectedImportLists": "Delete Import List(s)",
|
||||
"DeleteSelectedImportListsMessageText": "Are you sure you want to delete {0} selected import list(s)?",
|
||||
"DeleteSelectedIndexers": "Delete Indexer(s)",
|
||||
"DeleteSelectedIndexersMessageText": "Are you sure you want to delete {0} selected indexer(s)?",
|
||||
"DeleteSelectedMovie": "Delete Selected Movie(s)",
|
||||
"DeleteSelectedMovieFiles": "Delete Selected Movie Files",
|
||||
"DeleteSelectedMovieFilesMessage": "Are you sure you want to delete the selected movie files?",
|
||||
@ -295,6 +304,8 @@
|
||||
"EditQualityProfile": "Edit Quality Profile",
|
||||
"EditRemotePathMapping": "Edit Remote Path Mapping",
|
||||
"EditRestriction": "Edit Restriction",
|
||||
"EditSelectedDownloadClients": "Edit Selected Download Clients",
|
||||
"EditSelectedImportLists": "Edit Selected Import Lists",
|
||||
"EditSelectedIndexers": "Edit Selected Indexers",
|
||||
"EditSelectedMovies": "Edit Selected Movies",
|
||||
"Edition": "Edition",
|
||||
@ -406,6 +417,7 @@
|
||||
"Images": "Images",
|
||||
"ImdbRating": "IMDb Rating",
|
||||
"ImdbVotes": "IMDb Votes",
|
||||
"Implementation": "Implementation",
|
||||
"Import": "Import",
|
||||
"ImportCustomFormat": "Import Custom Format",
|
||||
"ImportErrors": "Import Errors",
|
||||
@ -514,7 +526,11 @@
|
||||
"LowerCase": "Lowercase",
|
||||
"MIA": "MIA",
|
||||
"MaintenanceRelease": "Maintenance Release: bug fixes and other improvements. See Github Commit History for more details",
|
||||
"ManageClients": "Manage Clients",
|
||||
"ManageDownloadClients": "Manage Download Clients",
|
||||
"ManageImportLists": "Manage Import Lists",
|
||||
"ManageIndexers": "Manage Indexers",
|
||||
"ManageLists": "Manage Lists",
|
||||
"Manual": "Manual",
|
||||
"ManualImport": "Manual Import",
|
||||
"ManualImportSelectLanguage": "Manual Import - Select Language",
|
||||
@ -632,9 +648,12 @@
|
||||
"NoChange": "No Change",
|
||||
"NoChanges": "No Changes",
|
||||
"NoCollections": "No collections found, to get started you'll want to add a new movie, or import some existing ones",
|
||||
"NoDownloadClientsFound": "No download clients found",
|
||||
"NoEventsFound": "No events found",
|
||||
"NoHistory": "No history",
|
||||
"NoHistoryBlocklist": "No history blocklist",
|
||||
"NoImportListsFound": "No import lists found",
|
||||
"NoIndexersFound": "No indexers found",
|
||||
"NoLeaveIt": "No, Leave It",
|
||||
"NoLimitForAnyRuntime": "No limit for any runtime",
|
||||
"NoLinks": "No Links",
|
||||
|
@ -6,8 +6,8 @@ namespace Radarr.Api.V3.DownloadClient
|
||||
[V3ApiController]
|
||||
public class DownloadClientController : ProviderControllerBase<DownloadClientResource, DownloadClientBulkResource, IDownloadClient, DownloadClientDefinition>
|
||||
{
|
||||
public static readonly DownloadClientResourceMapper ResourceMapper = new DownloadClientResourceMapper();
|
||||
public static readonly DownloadClientBulkResourceMapper BulkResourceMapper = new DownloadClientBulkResourceMapper();
|
||||
public static readonly DownloadClientResourceMapper ResourceMapper = new ();
|
||||
public static readonly DownloadClientBulkResourceMapper BulkResourceMapper = new ();
|
||||
|
||||
public DownloadClientController(IDownloadClientFactory downloadClientFactory)
|
||||
: base(downloadClientFactory, "downloadclient", ResourceMapper, BulkResourceMapper)
|
||||
|
@ -9,8 +9,8 @@ namespace Radarr.Api.V3.ImportLists
|
||||
[V3ApiController]
|
||||
public class ImportListController : ProviderControllerBase<ImportListResource, ImportListBulkResource, IImportList, ImportListDefinition>
|
||||
{
|
||||
public static readonly ImportListResourceMapper ResourceMapper = new ImportListResourceMapper();
|
||||
public static readonly ImportListBulkResourceMapper BulkResourceMapper = new ImportListBulkResourceMapper();
|
||||
public static readonly ImportListResourceMapper ResourceMapper = new ();
|
||||
public static readonly ImportListBulkResourceMapper BulkResourceMapper = new ();
|
||||
|
||||
public ImportListController(IImportListFactory importListFactory, ProfileExistsValidator profileExistsValidator)
|
||||
: base(importListFactory, "importlist", ResourceMapper, BulkResourceMapper)
|
||||
|
@ -6,8 +6,8 @@ namespace Radarr.Api.V3.Indexers
|
||||
[V3ApiController]
|
||||
public class IndexerController : ProviderControllerBase<IndexerResource, IndexerBulkResource, IIndexer, IndexerDefinition>
|
||||
{
|
||||
public static readonly IndexerResourceMapper ResourceMapper = new IndexerResourceMapper();
|
||||
public static readonly IndexerBulkResourceMapper BulkResourceMapper = new IndexerBulkResourceMapper();
|
||||
public static readonly IndexerResourceMapper ResourceMapper = new ();
|
||||
public static readonly IndexerBulkResourceMapper BulkResourceMapper = new ();
|
||||
|
||||
public IndexerController(IndexerFactory indexerFactory)
|
||||
: base(indexerFactory, "indexer", ResourceMapper, BulkResourceMapper)
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Extras.Metadata;
|
||||
using Radarr.Http;
|
||||
|
||||
@ -6,12 +8,24 @@ namespace Radarr.Api.V3.Metadata
|
||||
[V3ApiController]
|
||||
public class MetadataController : ProviderControllerBase<MetadataResource, MetadataBulkResource, IMetadata, MetadataDefinition>
|
||||
{
|
||||
public static readonly MetadataResourceMapper ResourceMapper = new MetadataResourceMapper();
|
||||
public static readonly MetadataBulkResourceMapper BulkResourceMapper = new MetadataBulkResourceMapper();
|
||||
public static readonly MetadataResourceMapper ResourceMapper = new ();
|
||||
public static readonly MetadataBulkResourceMapper BulkResourceMapper = new ();
|
||||
|
||||
public MetadataController(IMetadataFactory metadataFactory)
|
||||
: base(metadataFactory, "metadata", ResourceMapper, BulkResourceMapper)
|
||||
{
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public override ActionResult<MetadataResource> UpdateProvider([FromBody] MetadataBulkResource providerResource)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public override object DeleteProviders([FromBody] MetadataBulkResource resource)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,4 @@ public class MovieEditorResource
|
||||
public bool DeleteFiles { get; set; }
|
||||
public bool AddImportExclusion { get; set; }
|
||||
}
|
||||
|
||||
public enum ApplyTags
|
||||
{
|
||||
Add,
|
||||
Remove,
|
||||
Replace
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Notifications;
|
||||
using Radarr.Http;
|
||||
|
||||
@ -6,12 +8,24 @@ namespace Radarr.Api.V3.Notifications
|
||||
[V3ApiController]
|
||||
public class NotificationController : ProviderControllerBase<NotificationResource, NotificationBulkResource, INotification, NotificationDefinition>
|
||||
{
|
||||
public static readonly NotificationResourceMapper ResourceMapper = new NotificationResourceMapper();
|
||||
public static readonly NotificationBulkResourceMapper BulkResourceMapper = new NotificationBulkResourceMapper();
|
||||
public static readonly NotificationResourceMapper ResourceMapper = new ();
|
||||
public static readonly NotificationBulkResourceMapper BulkResourceMapper = new ();
|
||||
|
||||
public NotificationController(NotificationFactory notificationFactory)
|
||||
: base(notificationFactory, "notification", ResourceMapper, BulkResourceMapper)
|
||||
{
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public override ActionResult<NotificationResource> UpdateProvider([FromBody] NotificationBulkResource providerResource)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public override object DeleteProviders([FromBody] NotificationBulkResource resource)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using Radarr.Api.V3.Movies;
|
||||
|
||||
namespace Radarr.Api.V3
|
||||
{
|
||||
@ -9,6 +8,18 @@ public class ProviderBulkResource<T>
|
||||
public List<int> Ids { get; set; }
|
||||
public List<int> Tags { get; set; }
|
||||
public ApplyTags ApplyTags { get; set; }
|
||||
|
||||
public ProviderBulkResource()
|
||||
{
|
||||
Ids = new List<int>();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ApplyTags
|
||||
{
|
||||
Add,
|
||||
Remove,
|
||||
Replace
|
||||
}
|
||||
|
||||
public class ProviderBulkResourceMapper<TProviderBulkResource, TProviderDefinition>
|
||||
|
@ -6,7 +6,6 @@
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
using Radarr.Api.V3.Movies;
|
||||
using Radarr.Http.REST;
|
||||
using Radarr.Http.REST.Attributes;
|
||||
|
||||
@ -103,8 +102,13 @@ public ActionResult<TProviderResource> UpdateProvider([FromBody] TProviderResour
|
||||
[HttpPut("bulk")]
|
||||
[Consumes("application/json")]
|
||||
[Produces("application/json")]
|
||||
public ActionResult<TProviderResource> UpdateProvider([FromBody] TBulkProviderResource providerResource)
|
||||
public virtual ActionResult<TProviderResource> UpdateProvider([FromBody] TBulkProviderResource providerResource)
|
||||
{
|
||||
if (!providerResource.Ids.Any())
|
||||
{
|
||||
throw new BadRequestException("ids must be provided");
|
||||
}
|
||||
|
||||
var definitionsToUpdate = _providerFactory.Get(providerResource.Ids).ToList();
|
||||
|
||||
foreach (var definition in definitionsToUpdate)
|
||||
@ -157,7 +161,7 @@ public object DeleteProvider(int id)
|
||||
|
||||
[HttpDelete("bulk")]
|
||||
[Consumes("application/json")]
|
||||
public object DeleteProviders([FromBody] TBulkProviderResource resource)
|
||||
public virtual object DeleteProviders([FromBody] TBulkProviderResource resource)
|
||||
{
|
||||
_providerFactory.Delete(resource.Ids);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user