From e6f5d535e9bdd903a0b7f96a9181965d117b2f0b Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 1 Mar 2020 12:56:58 -0800 Subject: [PATCH] New: Clone indexer button Closes #3546 --- .../Settings/Indexers/Indexers/Indexer.css | 11 ++++++++ .../src/Settings/Indexers/Indexers/Indexer.js | 26 +++++++++++++++-- .../Settings/Indexers/Indexers/Indexers.js | 8 ++++++ .../Indexers/Indexers/IndexersConnector.js | 16 ++++++----- .../src/Store/Actions/Settings/indexers.js | 28 +++++++++++++++++++ 5 files changed, 79 insertions(+), 10 deletions(-) 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 8d7e5719d..8ffbe571d 100644 --- a/frontend/src/Settings/Indexers/Indexers/Indexer.js +++ b/frontend/src/Settings/Indexers/Indexers/Indexer.js @@ -1,8 +1,9 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { kinds } from 'Helpers/Props'; +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 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} +
+
+ {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 3f020d7ac..734700d2d 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 f26907d6d..7a7a6abb3 100644 --- a/frontend/src/Settings/Indexers/Indexers/IndexersConnector.js +++ b/frontend/src/Settings/Indexers/Indexers/IndexersConnector.js @@ -4,7 +4,7 @@ import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import sortByName from 'Utilities/Array/sortByName'; import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector'; -import { fetchIndexers, deleteIndexer } from 'Store/Actions/settingsActions'; +import { fetchIndexers, deleteIndexer, cloneIndexer } from 'Store/Actions/settingsActions'; import Indexers from './Indexers'; function createMapStateToProps() { @@ -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 5c1b0a508..79541ba88 100644 --- a/frontend/src/Store/Actions/Settings/indexers.js +++ b/frontend/src/Store/Actions/Settings/indexers.js @@ -1,5 +1,7 @@ import { createAction } from 'redux-actions'; import { createThunk } from 'Store/thunks'; +import getSectionState from 'Utilities/State/getSectionState'; +import updateSectionState from 'Utilities/State/updateSectionState'; import selectProviderSchema from 'Utilities/State/selectProviderSchema'; import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer'; import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer'; @@ -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); @@ -112,6 +116,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); } }