diff --git a/frontend/src/Settings/Indexers/Indexers/Indexer.css b/frontend/src/Settings/Indexers/Indexers/Indexer.css
index 6715d2a0a..693f94f87 100644
--- a/frontend/src/Settings/Indexers/Indexers/Indexer.css
+++ b/frontend/src/Settings/Indexers/Indexers/Indexer.css
@@ -4,6 +4,11 @@
width: 290px;
}
+.nameContainer {
+ display: flex;
+ justify-content: space-between;
+}
+
.name {
@add-mixin truncate;
@@ -12,6 +17,12 @@
font-size: 24px;
}
+.cloneButton {
+ composes: button from '~Components/Link/IconButton.css';
+
+ height: 36px;
+}
+
.enabled {
display: flex;
flex-wrap: wrap;
diff --git a/frontend/src/Settings/Indexers/Indexers/Indexer.js b/frontend/src/Settings/Indexers/Indexers/Indexer.js
index 6f13f65d5..a3a850a8b 100644
--- a/frontend/src/Settings/Indexers/Indexers/Indexer.js
+++ b/frontend/src/Settings/Indexers/Indexers/Indexer.js
@@ -2,8 +2,9 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Card from 'Components/Card';
import Label from 'Components/Label';
+import IconButton from 'Components/Link/IconButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
-import { kinds } from 'Helpers/Props';
+import { icons, kinds } from 'Helpers/Props';
import EditIndexerModalConnector from './EditIndexerModalConnector';
import styles from './Indexer.css';
@@ -47,6 +48,15 @@ class Indexer extends Component {
this.props.onConfirmDeleteIndexer(this.props.id);
}
+ onCloneIndexerPress = () => {
+ const {
+ id,
+ onCloneIndexerPress
+ } = this.props;
+
+ onCloneIndexerPress(id);
+ }
+
//
// Render
@@ -67,8 +77,17 @@ class Indexer extends Component {
overlayContent={true}
onPress={this.onEditIndexerPress}
>
-
- {name}
+
@@ -134,6 +153,7 @@ Indexer.propTypes = {
enableInteractiveSearch: PropTypes.bool.isRequired,
supportsRss: PropTypes.bool.isRequired,
supportsSearch: PropTypes.bool.isRequired,
+ onCloneIndexerPress: PropTypes.func.isRequired,
onConfirmDeleteIndexer: PropTypes.func.isRequired
};
diff --git a/frontend/src/Settings/Indexers/Indexers/Indexers.js b/frontend/src/Settings/Indexers/Indexers/Indexers.js
index 6615ba43d..f7db235ef 100644
--- a/frontend/src/Settings/Indexers/Indexers/Indexers.js
+++ b/frontend/src/Settings/Indexers/Indexers/Indexers.js
@@ -31,6 +31,11 @@ class Indexers extends Component {
this.setState({ isAddIndexerModalOpen: true });
}
+ onCloneIndexerPress = (id) => {
+ this.props.dispatchCloneIndexer({ id });
+ this.setState({ isEditIndexerModalOpen: true });
+ }
+
onAddIndexerModalClose = ({ indexerSelected = false } = {}) => {
this.setState({
isAddIndexerModalOpen: false,
@@ -48,6 +53,7 @@ class Indexers extends Component {
render() {
const {
items,
+ dispatchCloneIndexer,
onConfirmDeleteIndexer,
...otherProps
} = this.props;
@@ -70,6 +76,7 @@ class Indexers extends Component {
);
@@ -108,6 +115,7 @@ Indexers.propTypes = {
isFetching: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
+ dispatchCloneIndexer: PropTypes.func.isRequired,
onConfirmDeleteIndexer: PropTypes.func.isRequired
};
diff --git a/frontend/src/Settings/Indexers/Indexers/IndexersConnector.js b/frontend/src/Settings/Indexers/Indexers/IndexersConnector.js
index 8747179b3..9acb86f22 100644
--- a/frontend/src/Settings/Indexers/Indexers/IndexersConnector.js
+++ b/frontend/src/Settings/Indexers/Indexers/IndexersConnector.js
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
-import { deleteIndexer, fetchIndexers } from 'Store/Actions/settingsActions';
+import { cloneIndexer, deleteIndexer, fetchIndexers } from 'Store/Actions/settingsActions';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
import sortByName from 'Utilities/Array/sortByName';
import Indexers from './Indexers';
@@ -15,8 +15,9 @@ function createMapStateToProps() {
}
const mapDispatchToProps = {
- fetchIndexers,
- deleteIndexer
+ dispatchFetchIndexers: fetchIndexers,
+ dispatchDeleteIndexer: deleteIndexer,
+ dispatchCloneIndexer: cloneIndexer
};
class IndexersConnector extends Component {
@@ -25,14 +26,14 @@ class IndexersConnector extends Component {
// Lifecycle
componentDidMount() {
- this.props.fetchIndexers();
+ this.props.dispatchFetchIndexers();
}
//
// Listeners
onConfirmDeleteIndexer = (id) => {
- this.props.deleteIndexer({ id });
+ this.props.dispatchDeleteIndexer({ id });
}
//
@@ -49,8 +50,9 @@ class IndexersConnector extends Component {
}
IndexersConnector.propTypes = {
- fetchIndexers: PropTypes.func.isRequired,
- deleteIndexer: PropTypes.func.isRequired
+ dispatchFetchIndexers: PropTypes.func.isRequired,
+ dispatchDeleteIndexer: PropTypes.func.isRequired,
+ dispatchCloneIndexer: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(IndexersConnector);
diff --git a/frontend/src/Store/Actions/Settings/indexers.js b/frontend/src/Store/Actions/Settings/indexers.js
index 7bf4c10a1..e2e85cbb5 100644
--- a/frontend/src/Store/Actions/Settings/indexers.js
+++ b/frontend/src/Store/Actions/Settings/indexers.js
@@ -8,7 +8,9 @@ import createTestProviderHandler, { createCancelTestProviderHandler } from 'Stor
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
import { createThunk } from 'Store/thunks';
+import getSectionState from 'Utilities/State/getSectionState';
import selectProviderSchema from 'Utilities/State/selectProviderSchema';
+import updateSectionState from 'Utilities/State/updateSectionState';
//
// Variables
@@ -21,6 +23,7 @@ const section = 'settings.indexers';
export const FETCH_INDEXERS = 'settings/indexers/fetchIndexers';
export const FETCH_INDEXER_SCHEMA = 'settings/indexers/fetchIndexerSchema';
export const SELECT_INDEXER_SCHEMA = 'settings/indexers/selectIndexerSchema';
+export const CLONE_INDEXER = 'settings/indexers/cloneIndexer';
export const SET_INDEXER_VALUE = 'settings/indexers/setIndexerValue';
export const SET_INDEXER_FIELD_VALUE = 'settings/indexers/setIndexerFieldValue';
export const SAVE_INDEXER = 'settings/indexers/saveIndexer';
@@ -36,6 +39,7 @@ export const TEST_ALL_INDEXERS = 'settings/indexers/testAllIndexers';
export const fetchIndexers = createThunk(FETCH_INDEXERS);
export const fetchIndexerSchema = createThunk(FETCH_INDEXER_SCHEMA);
export const selectIndexerSchema = createAction(SELECT_INDEXER_SCHEMA);
+export const cloneIndexer = createAction(CLONE_INDEXER);
export const saveIndexer = createThunk(SAVE_INDEXER);
export const cancelSaveIndexer = createThunk(CANCEL_SAVE_INDEXER);
@@ -113,6 +117,30 @@ export default {
return selectedSchema;
});
+ },
+
+ [CLONE_INDEXER]: function(state, { payload }) {
+ const id = payload.id;
+ const newState = getSectionState(state, section);
+ const item = newState.items.find((i) => i.id === id);
+
+ // Use selectedSchema so `createProviderSettingsSelector` works properly
+ const selectedSchema = { ...item };
+ delete selectedSchema.id;
+ delete selectedSchema.name;
+
+ selectedSchema.fields = selectedSchema.fields.map((field) => {
+ return { ...field };
+ });
+
+ newState.selectedSchema = selectedSchema;
+
+ // Set the name in pendingChanges
+ newState.pendingChanges = {
+ name: `${item.name} - Copy`
+ };
+
+ return updateSectionState(state, section, newState);
}
}