New: Clone indexer button

Closes #3546
This commit is contained in:
Mark McDowall 2020-03-01 12:56:58 -08:00
parent b8ce274fa5
commit be11789a86
5 changed files with 79 additions and 10 deletions

View File

@ -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;

View File

@ -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}
>
<div className={styles.name}>
{name}
<div className={styles.nameContainer}>
<div className={styles.name}>
{name}
</div>
<IconButton
className={styles.cloneButton}
title="Clone Profile"
name={icons.CLONE}
onPress={this.onCloneIndexerPress}
/>
</div>
<div className={styles.enabled}>
@ -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
};

View File

@ -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 {
<Indexer
key={item.id}
{...item}
onCloneIndexerPress={this.onCloneIndexerPress}
onConfirmDeleteIndexer={onConfirmDeleteIndexer}
/>
);
@ -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
};

View File

@ -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);

View File

@ -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);
@ -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);
}
}