mirror of
https://github.com/Radarr/Radarr.git
synced 2025-06-28 09:17:54 -04:00
New: Ability to clone Import Lists
(cherry picked from commit 2314d0b506e30d3a965497a052bc5e54fa0beb81) Closes #10948
This commit is contained in:
parent
b99c536306
commit
15c34a61de
6 changed files with 91 additions and 8 deletions
|
@ -4,6 +4,11 @@
|
||||||
width: 290px;
|
width: 290px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nameContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
@add-mixin truncate;
|
@add-mixin truncate;
|
||||||
|
|
||||||
|
@ -12,6 +17,12 @@
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cloneButton {
|
||||||
|
composes: button from '~Components/Link/IconButton.css';
|
||||||
|
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
.enabled {
|
.enabled {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
// This file is automatically generated.
|
// This file is automatically generated.
|
||||||
// Please do not change this file!
|
// Please do not change this file!
|
||||||
interface CssExports {
|
interface CssExports {
|
||||||
|
'cloneButton': string;
|
||||||
'enabled': string;
|
'enabled': string;
|
||||||
'list': string;
|
'list': string;
|
||||||
'name': string;
|
'name': string;
|
||||||
|
'nameContainer': string;
|
||||||
}
|
}
|
||||||
export const cssExports: CssExports;
|
export const cssExports: CssExports;
|
||||||
export default cssExports;
|
export default cssExports;
|
||||||
|
|
|
@ -2,9 +2,10 @@ import React, { useCallback, useState } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import Card from 'Components/Card';
|
import Card from 'Components/Card';
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||||
import TagList from 'Components/TagList';
|
import TagList from 'Components/TagList';
|
||||||
import { kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import { deleteImportList } from 'Store/Actions/settingsActions';
|
import { deleteImportList } from 'Store/Actions/settingsActions';
|
||||||
import useTags from 'Tags/useTags';
|
import useTags from 'Tags/useTags';
|
||||||
import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan';
|
import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan';
|
||||||
|
@ -19,6 +20,7 @@ interface ImportListProps {
|
||||||
enableAuto: boolean;
|
enableAuto: boolean;
|
||||||
tags: number[];
|
tags: number[];
|
||||||
minRefreshInterval: string;
|
minRefreshInterval: string;
|
||||||
|
onCloneImportListPress: (id: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImportList({
|
function ImportList({
|
||||||
|
@ -28,6 +30,7 @@ function ImportList({
|
||||||
enableAuto,
|
enableAuto,
|
||||||
tags,
|
tags,
|
||||||
minRefreshInterval,
|
minRefreshInterval,
|
||||||
|
onCloneImportListPress,
|
||||||
}: ImportListProps) {
|
}: ImportListProps) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const tagList = useTags();
|
const tagList = useTags();
|
||||||
|
@ -59,13 +62,26 @@ function ImportList({
|
||||||
dispatch(deleteImportList({ id }));
|
dispatch(deleteImportList({ id }));
|
||||||
}, [id, dispatch]);
|
}, [id, dispatch]);
|
||||||
|
|
||||||
|
const handleCloneImportListPress = useCallback(() => {
|
||||||
|
onCloneImportListPress(id);
|
||||||
|
}, [id, onCloneImportListPress]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className={styles.list}
|
className={styles.list}
|
||||||
overlayContent={true}
|
overlayContent={true}
|
||||||
onPress={handleEditImportListPress}
|
onPress={handleEditImportListPress}
|
||||||
>
|
>
|
||||||
<div className={styles.name}>{name}</div>
|
<div className={styles.nameContainer}>
|
||||||
|
<div className={styles.name}>{name}</div>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
className={styles.cloneButton}
|
||||||
|
title={translate('CloneImportList')}
|
||||||
|
name={icons.CLONE}
|
||||||
|
onPress={handleCloneImportListPress}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={styles.enabled}>
|
<div className={styles.enabled}>
|
||||||
{enabled ? (
|
{enabled ? (
|
||||||
|
|
|
@ -7,7 +7,10 @@ import Icon from 'Components/Icon';
|
||||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||||
import { fetchImportLists } from 'Store/Actions/settingsActions';
|
import {
|
||||||
|
cloneImportList,
|
||||||
|
fetchImportLists,
|
||||||
|
} from 'Store/Actions/settingsActions';
|
||||||
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
||||||
import ImportListModel from 'typings/ImportList';
|
import ImportListModel from 'typings/ImportList';
|
||||||
import sortByProp from 'Utilities/Array/sortByProp';
|
import sortByProp from 'Utilities/Array/sortByProp';
|
||||||
|
@ -49,6 +52,14 @@ function ImportLists() {
|
||||||
setIsEditImportListModalOpen(false);
|
setIsEditImportListModalOpen(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleCloneImportListPress = useCallback(
|
||||||
|
(id: number) => {
|
||||||
|
dispatch(cloneImportList({ id }));
|
||||||
|
setIsEditImportListModalOpen(true);
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchImportLists());
|
dispatch(fetchImportLists());
|
||||||
dispatch(fetchRootFolders());
|
dispatch(fetchRootFolders());
|
||||||
|
@ -64,7 +75,13 @@ function ImportLists() {
|
||||||
>
|
>
|
||||||
<div className={styles.lists}>
|
<div className={styles.lists}>
|
||||||
{items.map((item) => {
|
{items.map((item) => {
|
||||||
return <ImportList key={item.id} {...item} />;
|
return (
|
||||||
|
<ImportList
|
||||||
|
key={item.id}
|
||||||
|
{...item}
|
||||||
|
onCloneImportListPress={handleCloneImportListPress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<Card className={styles.addList} onPress={handleAddImportListPress}>
|
<Card className={styles.addList} onPress={handleAddImportListPress}>
|
||||||
|
|
|
@ -10,7 +10,10 @@ import createTestProviderHandler, { createCancelTestProviderHandler } from 'Stor
|
||||||
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
|
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
|
||||||
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
||||||
import { createThunk } from 'Store/thunks';
|
import { createThunk } from 'Store/thunks';
|
||||||
|
import getSectionState from 'Utilities/State/getSectionState';
|
||||||
import selectProviderSchema from 'Utilities/State/selectProviderSchema';
|
import selectProviderSchema from 'Utilities/State/selectProviderSchema';
|
||||||
|
import updateSectionState from 'Utilities/State/updateSectionState';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
//
|
//
|
||||||
// Variables
|
// Variables
|
||||||
|
@ -31,9 +34,9 @@ export const DELETE_IMPORT_LIST = 'settings/importLists/deleteImportList';
|
||||||
export const TEST_IMPORT_LIST = 'settings/importLists/testImportList';
|
export const TEST_IMPORT_LIST = 'settings/importLists/testImportList';
|
||||||
export const CANCEL_TEST_IMPORT_LIST = 'settings/importLists/cancelTestImportList';
|
export const CANCEL_TEST_IMPORT_LIST = 'settings/importLists/cancelTestImportList';
|
||||||
export const TEST_ALL_IMPORT_LISTS = 'settings/importLists/testAllImportLists';
|
export const TEST_ALL_IMPORT_LISTS = 'settings/importLists/testAllImportLists';
|
||||||
|
|
||||||
export const BULK_DELETE_IMPORT_LISTS = 'settings/importLists/bulkDeleteImportLists';
|
|
||||||
export const BULK_EDIT_IMPORT_LISTS = 'settings/importLists/bulkEditImportLists';
|
export const BULK_EDIT_IMPORT_LISTS = 'settings/importLists/bulkEditImportLists';
|
||||||
|
export const BULK_DELETE_IMPORT_LISTS = 'settings/importLists/bulkDeleteImportLists';
|
||||||
|
export const CLONE_IMPORT_LIST = 'settings/importLists/cloneImportList';
|
||||||
|
|
||||||
//
|
//
|
||||||
// Action Creators
|
// Action Creators
|
||||||
|
@ -48,9 +51,8 @@ export const deleteImportList = createThunk(DELETE_IMPORT_LIST);
|
||||||
export const testImportList = createThunk(TEST_IMPORT_LIST);
|
export const testImportList = createThunk(TEST_IMPORT_LIST);
|
||||||
export const cancelTestImportList = createThunk(CANCEL_TEST_IMPORT_LIST);
|
export const cancelTestImportList = createThunk(CANCEL_TEST_IMPORT_LIST);
|
||||||
export const testAllImportLists = createThunk(TEST_ALL_IMPORT_LISTS);
|
export const testAllImportLists = createThunk(TEST_ALL_IMPORT_LISTS);
|
||||||
|
|
||||||
export const bulkDeleteImportLists = createThunk(BULK_DELETE_IMPORT_LISTS);
|
|
||||||
export const bulkEditImportLists = createThunk(BULK_EDIT_IMPORT_LISTS);
|
export const bulkEditImportLists = createThunk(BULK_EDIT_IMPORT_LISTS);
|
||||||
|
export const bulkDeleteImportLists = createThunk(BULK_DELETE_IMPORT_LISTS);
|
||||||
|
|
||||||
export const setImportListValue = createAction(SET_IMPORT_LIST_VALUE, (payload) => {
|
export const setImportListValue = createAction(SET_IMPORT_LIST_VALUE, (payload) => {
|
||||||
return {
|
return {
|
||||||
|
@ -66,6 +68,8 @@ export const setImportListFieldValue = createAction(SET_IMPORT_LIST_FIELD_VALUE,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const cloneImportList = createAction(CLONE_IMPORT_LIST);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Details
|
// Details
|
||||||
|
|
||||||
|
@ -128,6 +132,37 @@ export default {
|
||||||
|
|
||||||
return selectedSchema;
|
return selectedSchema;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
[CLONE_IMPORT_LIST]: (state, { payload }) => {
|
||||||
|
const id = payload.id;
|
||||||
|
const newState = getSectionState(state, section);
|
||||||
|
const item = newState.items.find((i) => i.id === id);
|
||||||
|
|
||||||
|
const selectedSchema = { ...item };
|
||||||
|
delete selectedSchema.id;
|
||||||
|
delete selectedSchema.name;
|
||||||
|
|
||||||
|
// Use selectedSchema so `createProviderSettingsSelector` works properly
|
||||||
|
selectedSchema.fields = selectedSchema.fields.map((field) => {
|
||||||
|
const newField = { ...field };
|
||||||
|
|
||||||
|
if (newField.privacy === 'apiKey' || newField.privacy === 'password') {
|
||||||
|
newField.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return newField;
|
||||||
|
});
|
||||||
|
|
||||||
|
newState.selectedSchema = selectedSchema;
|
||||||
|
|
||||||
|
const pendingChanges = { ...item, id: 0 };
|
||||||
|
delete pendingChanges.id;
|
||||||
|
|
||||||
|
pendingChanges.name = translate('DefaultNameCopiedImportList', { name: pendingChanges.name });
|
||||||
|
newState.pendingChanges = pendingChanges;
|
||||||
|
|
||||||
|
return updateSectionState(state, section, newState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -222,6 +222,7 @@
|
||||||
"CloneAutoTag": "Clone Auto Tag",
|
"CloneAutoTag": "Clone Auto Tag",
|
||||||
"CloneCondition": "Clone Condition",
|
"CloneCondition": "Clone Condition",
|
||||||
"CloneCustomFormat": "Clone Custom Format",
|
"CloneCustomFormat": "Clone Custom Format",
|
||||||
|
"CloneImportList": "Clone Import List",
|
||||||
"CloneIndexer": "Clone Indexer",
|
"CloneIndexer": "Clone Indexer",
|
||||||
"CloneProfile": "Clone Profile",
|
"CloneProfile": "Clone Profile",
|
||||||
"Close": "Close",
|
"Close": "Close",
|
||||||
|
@ -311,6 +312,7 @@
|
||||||
"Default": "Default",
|
"Default": "Default",
|
||||||
"DefaultCase": "Default Case",
|
"DefaultCase": "Default Case",
|
||||||
"DefaultDelayProfileMovie": "This is the default profile. It applies to all movies that don't have an explicit profile.",
|
"DefaultDelayProfileMovie": "This is the default profile. It applies to all movies that don't have an explicit profile.",
|
||||||
|
"DefaultNameCopiedImportList": "{name} - Copy",
|
||||||
"DefaultNameCopiedProfile": "{name} - Copy",
|
"DefaultNameCopiedProfile": "{name} - Copy",
|
||||||
"DefaultNameCopiedSpecification": "{name} - Copy",
|
"DefaultNameCopiedSpecification": "{name} - Copy",
|
||||||
"DefaultNotFoundMessage": "You must be lost, nothing to see here.",
|
"DefaultNotFoundMessage": "You must be lost, nothing to see here.",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue