Convert Add Indexer Modal to Typescript

pull/2184/head
Bogdan 6 months ago
parent 5464b23329
commit f1fdec6822

@ -1,31 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import Modal from 'Components/Modal/Modal';
import { sizes } from 'Helpers/Props';
import AddIndexerModalContentConnector from './AddIndexerModalContentConnector';
import styles from './AddIndexerModal.css';
function AddIndexerModal({ isOpen, onModalClose, onSelectIndexer, ...otherProps }) {
return (
<Modal
isOpen={isOpen}
size={sizes.EXTRA_LARGE}
onModalClose={onModalClose}
className={styles.modal}
>
<AddIndexerModalContentConnector
{...otherProps}
onModalClose={onModalClose}
onSelectIndexer={onSelectIndexer}
/>
</Modal>
);
}
AddIndexerModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired,
onSelectIndexer: PropTypes.func.isRequired
};
export default AddIndexerModal;

@ -0,0 +1,51 @@
import PropTypes from 'prop-types';
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import Modal from 'Components/Modal/Modal';
import { sizes } from 'Helpers/Props';
import { clearIndexerSchema } from 'Store/Actions/indexerActions';
import AddIndexerModalContent from './AddIndexerModalContent';
import styles from './AddIndexerModal.css';
interface AddIndexerModalProps {
isOpen: boolean;
onSelectIndexer(): void;
onModalClose(): void;
}
function AddIndexerModal({
isOpen,
onSelectIndexer,
onModalClose,
...otherProps
}: AddIndexerModalProps) {
const dispatch = useDispatch();
const onModalClosePress = useCallback(() => {
dispatch(clearIndexerSchema());
onModalClose();
}, [dispatch, onModalClose]);
return (
<Modal
isOpen={isOpen}
size={sizes.EXTRA_LARGE}
onModalClose={onModalClosePress}
className={styles.modal}
>
<AddIndexerModalContent
{...otherProps}
onSelectIndexer={onSelectIndexer}
onModalClose={onModalClosePress}
/>
</Modal>
);
}
AddIndexerModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired,
onSelectIndexer: PropTypes.func.isRequired,
};
export default AddIndexerModal;

@ -19,12 +19,18 @@
margin-bottom: 16px;
}
.alert {
.notice {
composes: alert from '~Components/Alert.css';
margin-bottom: 20px;
}
.alert {
composes: alert from '~Components/Alert.css';
text-align: center;
}
.scroller {
flex: 1 1 auto;
}

@ -10,6 +10,7 @@ interface CssExports {
'indexers': string;
'modalBody': string;
'modalFooter': string;
'notice': string;
'scroller': string;
}
export const cssExports: CssExports;

@ -1,325 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import EnhancedSelectInput from 'Components/Form/EnhancedSelectInput';
import NewznabCategorySelectInputConnector from 'Components/Form/NewznabCategorySelectInputConnector';
import TextInput from 'Components/Form/TextInput';
import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import Scroller from 'Components/Scroller/Scroller';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { kinds, scrollDirections } from 'Helpers/Props';
import sortByProp from 'Utilities/Array/sortByProp';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import SelectIndexerRow from './SelectIndexerRow';
import styles from './AddIndexerModalContent.css';
const columns = [
{
name: 'protocol',
label: () => translate('Protocol'),
isSortable: true,
isVisible: true
},
{
name: 'sortName',
label: () => translate('Name'),
isSortable: true,
isVisible: true
},
{
name: 'language',
label: () => translate('Language'),
isSortable: true,
isVisible: true
},
{
name: 'description',
label: () => translate('Description'),
isSortable: false,
isVisible: true
},
{
name: 'privacy',
label: () => translate('Privacy'),
isSortable: true,
isVisible: true
},
{
name: 'categories',
label: () => translate('Categories'),
isSortable: false,
isVisible: true
}
];
const protocols = [
{
key: 'torrent',
value: 'torrent'
},
{
key: 'usenet',
value: 'nzb'
}
];
const privacyLevels = [
{
key: 'private',
get value() {
return translate('Private');
}
},
{
key: 'semiPrivate',
get value() {
return translate('SemiPrivate');
}
},
{
key: 'public',
get value() {
return translate('Public');
}
}
];
class AddIndexerModalContent extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
filter: '',
filterProtocols: [],
filterLanguages: [],
filterPrivacyLevels: [],
filterCategories: []
};
}
//
// Listeners
onFilterChange = ({ value }) => {
this.setState({ filter: value });
};
//
// Render
render() {
const {
indexers,
onIndexerSelect,
sortKey,
sortDirection,
isFetching,
isPopulated,
error,
onSortPress,
onModalClose
} = this.props;
const languages = Array.from(new Set(indexers.map(({ language }) => language)))
.map((language) => ({ key: language, value: language }))
.sort(sortByProp('value'));
const filteredIndexers = indexers.filter((indexer) => {
const {
filter,
filterProtocols,
filterLanguages,
filterPrivacyLevels,
filterCategories
} = this.state;
if (!indexer.name.toLowerCase().includes(filter.toLocaleLowerCase()) && !indexer.description.toLowerCase().includes(filter.toLocaleLowerCase())) {
return false;
}
if (filterProtocols.length && !filterProtocols.includes(indexer.protocol)) {
return false;
}
if (filterLanguages.length && !filterLanguages.includes(indexer.language)) {
return false;
}
if (filterPrivacyLevels.length && !filterPrivacyLevels.includes(indexer.privacy)) {
return false;
}
if (filterCategories.length) {
const { categories = [] } = indexer.capabilities || {};
const flat = ({ id, subCategories = [] }) => [id, ...subCategories.flatMap(flat)];
const flatCategories = categories
.filter((item) => item.id < 100000)
.flatMap(flat);
if (!filterCategories.every((item) => flatCategories.includes(item))) {
return false;
}
}
return true;
});
const errorMessage = getErrorMessage(error, translate('UnableToLoadIndexers'));
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{translate('AddIndexer')}
</ModalHeader>
<ModalBody
className={styles.modalBody}
scrollDirection={scrollDirections.NONE}
>
<TextInput
className={styles.filterInput}
placeholder={translate('FilterPlaceHolder')}
name="filter"
value={this.state.filter}
autoFocus={true}
onChange={this.onFilterChange}
/>
<div className={styles.filterRow}>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>{translate('Protocol')}</label>
<EnhancedSelectInput
name="indexerProtocols"
value={this.state.filterProtocols}
values={protocols}
onChange={({ value }) => this.setState({ filterProtocols: value })}
/>
</div>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>{translate('Language')}</label>
<EnhancedSelectInput
name="indexerLanguages"
value={this.state.filterLanguages}
values={languages}
onChange={({ value }) => this.setState({ filterLanguages: value })}
/>
</div>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>{translate('Privacy')}</label>
<EnhancedSelectInput
name="indexerPrivacyLevels"
value={this.state.filterPrivacyLevels}
values={privacyLevels}
onChange={({ value }) => this.setState({ filterPrivacyLevels: value })}
/>
</div>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>{translate('Categories')}</label>
<NewznabCategorySelectInputConnector
name="indexerCategories"
value={this.state.filterCategories}
onChange={({ value }) => this.setState({ filterCategories: value })}
/>
</div>
</div>
<Alert
kind={kinds.INFO}
className={styles.alert}
>
<div>
{translate('ProwlarrSupportsAnyIndexer')}
</div>
</Alert>
<Scroller
className={styles.scroller}
autoFocus={false}
>
{
isFetching ? <LoadingIndicator /> : null
}
{
error ? <Alert kind={kinds.DANGER}>{errorMessage}</Alert> : null
}
{
isPopulated && !!indexers.length ?
<Table
columns={columns}
sortKey={sortKey}
sortDirection={sortDirection}
onSortPress={onSortPress}
>
<TableBody>
{
filteredIndexers.map((indexer) => (
<SelectIndexerRow
key={`${indexer.implementation}-${indexer.name}`}
implementation={indexer.implementation}
implementationName={indexer.implementationName}
{...indexer}
onIndexerSelect={onIndexerSelect}
/>
))
}
</TableBody>
</Table> :
null
}
{
isPopulated && !!indexers.length && !filteredIndexers.length ?
<Alert
kind={kinds.WARNING}
>
{translate('NoIndexersFound')}
</Alert> :
null
}
</Scroller>
</ModalBody>
<ModalFooter className={styles.modalFooter}>
<div className={styles.available}>
{
isPopulated ?
translate('CountIndexersAvailable', { count: filteredIndexers.length }) :
null
}
</div>
<div>
<Button onPress={onModalClose}>{translate('Close')}</Button>
</div>
</ModalFooter>
</ModalContent>
);
}
}
AddIndexerModalContent.propTypes = {
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
sortKey: PropTypes.string,
sortDirection: PropTypes.string,
onSortPress: PropTypes.func.isRequired,
indexers: PropTypes.arrayOf(PropTypes.object).isRequired,
onIndexerSelect: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default AddIndexerModalContent;

@ -0,0 +1,414 @@
import { some } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import IndexerAppState from 'App/State/IndexerAppState';
import Alert from 'Components/Alert';
import EnhancedSelectInput from 'Components/Form/EnhancedSelectInput';
import NewznabCategorySelectInputConnector from 'Components/Form/NewznabCategorySelectInputConnector';
import TextInput from 'Components/Form/TextInput';
import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import Scroller from 'Components/Scroller/Scroller';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { kinds, scrollDirections } from 'Helpers/Props';
import Indexer, { IndexerCategory } from 'Indexer/Indexer';
import {
fetchIndexerSchema,
selectIndexerSchema,
setIndexerSchemaSort,
} from 'Store/Actions/indexerActions';
import createAllIndexersSelector from 'Store/Selectors/createAllIndexersSelector';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import { SortCallback } from 'typings/callbacks';
import sortByProp from 'Utilities/Array/sortByProp';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import SelectIndexerRow from './SelectIndexerRow';
import styles from './AddIndexerModalContent.css';
const COLUMNS = [
{
name: 'protocol',
label: () => translate('Protocol'),
isSortable: true,
isVisible: true,
},
{
name: 'sortName',
label: () => translate('Name'),
isSortable: true,
isVisible: true,
},
{
name: 'language',
label: () => translate('Language'),
isSortable: true,
isVisible: true,
},
{
name: 'description',
label: () => translate('Description'),
isSortable: false,
isVisible: true,
},
{
name: 'privacy',
label: () => translate('Privacy'),
isSortable: true,
isVisible: true,
},
{
name: 'categories',
label: () => translate('Categories'),
isSortable: false,
isVisible: true,
},
];
const PROTOCOLS = [
{
key: 'torrent',
value: 'torrent',
},
{
key: 'usenet',
value: 'nzb',
},
];
const PRIVACY_LEVELS = [
{
key: 'private',
get value() {
return translate('Private');
},
},
{
key: 'semiPrivate',
get value() {
return translate('SemiPrivate');
},
},
{
key: 'public',
get value() {
return translate('Public');
},
},
];
interface IndexerSchema extends Indexer {
isExistingIndexer: boolean;
}
function createAddIndexersSelector() {
return createSelector(
createClientSideCollectionSelector('indexers.schema'),
createAllIndexersSelector(),
(indexers: IndexerAppState, allIndexers) => {
const { isFetching, isPopulated, error, items, sortDirection, sortKey } =
indexers;
const indexerList: IndexerSchema[] = items.map((item) => {
const { definitionName } = item;
return {
...item,
isExistingIndexer: some(allIndexers, { definitionName }),
};
});
return {
isFetching,
isPopulated,
error,
indexers: indexerList,
sortKey,
sortDirection,
};
}
);
}
interface AddIndexerModalContentProps {
onSelectIndexer(): void;
onModalClose(): void;
}
function AddIndexerModalContent(props: AddIndexerModalContentProps) {
const { onSelectIndexer, onModalClose } = props;
const { isFetching, isPopulated, error, indexers, sortKey, sortDirection } =
useSelector(createAddIndexersSelector());
const dispatch = useDispatch();
const [filter, setFilter] = useState('');
const [filterProtocols, setFilterProtocols] = useState<string[]>([]);
const [filterLanguages, setFilterLanguages] = useState<string[]>([]);
const [filterPrivacyLevels, setFilterPrivacyLevels] = useState<string[]>([]);
const [filterCategories, setFilterCategories] = useState<number[]>([]);
useEffect(
() => {
dispatch(fetchIndexerSchema());
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
const onFilterChange = useCallback(
({ value }: { value: string }) => {
setFilter(value);
},
[setFilter]
);
const onIndexerSelect = useCallback(
({
implementation,
implementationName,
name,
}: {
implementation: string;
implementationName: string;
name: string;
}) => {
dispatch(
selectIndexerSchema({
implementation,
implementationName,
name,
})
);
onSelectIndexer();
},
[dispatch, onSelectIndexer]
);
const onSortPress = useCallback<SortCallback>(
(sortKey, sortDirection) => {
dispatch(setIndexerSchemaSort({ sortKey, sortDirection }));
},
[dispatch]
);
const languages = useMemo(
() =>
Array.from(new Set(indexers.map(({ language }) => language)))
.map((language) => ({ key: language, value: language }))
.sort(sortByProp('value')),
[indexers]
);
const filteredIndexers = useMemo(() => {
const flat = ({
id,
subCategories = [],
}: {
id: number;
subCategories: IndexerCategory[];
}): number[] => [id, ...subCategories.flatMap(flat)];
return indexers.filter((indexer) => {
if (
filter.length &&
!indexer.name.toLowerCase().includes(filter.toLocaleLowerCase()) &&
!indexer.description.toLowerCase().includes(filter.toLocaleLowerCase())
) {
return false;
}
if (
filterProtocols.length &&
!filterProtocols.includes(indexer.protocol)
) {
return false;
}
if (
filterLanguages.length &&
!filterLanguages.includes(indexer.language)
) {
return false;
}
if (
filterPrivacyLevels.length &&
!filterPrivacyLevels.includes(indexer.privacy)
) {
return false;
}
if (filterCategories.length) {
const { categories = [] } = indexer.capabilities || {};
const flatCategories = categories
.filter((item) => item.id < 100000)
.flatMap(flat);
if (
!filterCategories.every((categoryId) =>
flatCategories.includes(categoryId)
)
) {
return false;
}
}
return true;
});
}, [
indexers,
filter,
filterProtocols,
filterLanguages,
filterPrivacyLevels,
filterCategories,
]);
const errorMessage = getErrorMessage(
error,
translate('UnableToLoadIndexers')
);
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{translate('AddIndexer')}</ModalHeader>
<ModalBody
className={styles.modalBody}
scrollDirection={scrollDirections.NONE}
>
<TextInput
className={styles.filterInput}
placeholder={translate('FilterPlaceHolder')}
name="filter"
value={filter}
autoFocus={true}
onChange={onFilterChange}
/>
<div className={styles.filterRow}>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>
{translate('Protocol')}
</label>
<EnhancedSelectInput
name="indexerProtocols"
value={filterProtocols}
values={PROTOCOLS}
onChange={({ value }: { value: string[] }) =>
setFilterProtocols(value)
}
/>
</div>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>
{translate('Language')}
</label>
<EnhancedSelectInput
name="indexerLanguages"
value={filterLanguages}
values={languages}
onChange={({ value }: { value: string[] }) =>
setFilterLanguages(value)
}
/>
</div>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>{translate('Privacy')}</label>
<EnhancedSelectInput
name="indexerPrivacyLevels"
value={filterPrivacyLevels}
values={PRIVACY_LEVELS}
onChange={({ value }: { value: string[] }) =>
setFilterPrivacyLevels(value)
}
/>
</div>
<div className={styles.filterContainer}>
<label className={styles.filterLabel}>
{translate('Categories')}
</label>
<NewznabCategorySelectInputConnector
name="indexerCategories"
value={filterCategories}
onChange={({ value }: { value: number[] }) =>
setFilterCategories(value)
}
/>
</div>
</div>
<Alert kind={kinds.INFO} className={styles.notice}>
<div>{translate('ProwlarrSupportsAnyIndexer')}</div>
</Alert>
<Scroller className={styles.scroller} autoFocus={false}>
{isFetching ? <LoadingIndicator /> : null}
{error ? (
<Alert kind={kinds.DANGER} className={styles.alert}>
{errorMessage}
</Alert>
) : null}
{isPopulated && !!indexers.length ? (
<Table
columns={COLUMNS}
sortKey={sortKey}
sortDirection={sortDirection}
onSortPress={onSortPress}
>
<TableBody>
{filteredIndexers.map((indexer) => (
<SelectIndexerRow
{...indexer}
key={`${indexer.implementation}-${indexer.name}`}
implementation={indexer.implementation}
implementationName={indexer.implementationName}
onIndexerSelect={onIndexerSelect}
/>
))}
</TableBody>
</Table>
) : null}
{isPopulated && !!indexers.length && !filteredIndexers.length ? (
<Alert kind={kinds.WARNING} className={styles.alert}>
{translate('NoIndexersFound')}
</Alert>
) : null}
</Scroller>
</ModalBody>
<ModalFooter className={styles.modalFooter}>
<div className={styles.available}>
{isPopulated
? translate('CountIndexersAvailable', {
count: filteredIndexers.length,
})
: null}
</div>
<div>
<Button onPress={onModalClose}>{translate('Close')}</Button>
</div>
</ModalFooter>
</ModalContent>
);
}
export default AddIndexerModalContent;

@ -1,94 +0,0 @@
import { some } from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchIndexerSchema, selectIndexerSchema, setIndexerSchemaSort } from 'Store/Actions/indexerActions';
import createAllIndexersSelector from 'Store/Selectors/createAllIndexersSelector';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import AddIndexerModalContent from './AddIndexerModalContent';
function createMapStateToProps() {
return createSelector(
createClientSideCollectionSelector('indexers.schema'),
createAllIndexersSelector(),
(indexers, allIndexers) => {
const {
isFetching,
isPopulated,
error,
items,
sortDirection,
sortKey
} = indexers;
const indexerList = items.map((item) => {
const { definitionName } = item;
return {
...item,
isExistingIndexer: some(allIndexers, { definitionName })
};
});
return {
isFetching,
isPopulated,
error,
indexers: indexerList,
sortKey,
sortDirection
};
}
);
}
const mapDispatchToProps = {
fetchIndexerSchema,
selectIndexerSchema,
setIndexerSchemaSort
};
class AddIndexerModalContentConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.fetchIndexerSchema();
}
//
// Listeners
onIndexerSelect = ({ implementation, implementationName, name }) => {
this.props.selectIndexerSchema({ implementation, implementationName, name });
this.props.onSelectIndexer();
};
onSortPress = (sortKey, sortDirection) => {
this.props.setIndexerSchemaSort({ sortKey, sortDirection });
};
//
// Render
render() {
return (
<AddIndexerModalContent
{...this.props}
onSortPress={this.onSortPress}
onIndexerSelect={this.onIndexerSelect}
/>
);
}
}
AddIndexerModalContentConnector.propTypes = {
fetchIndexerSchema: PropTypes.func.isRequired,
selectIndexerSchema: PropTypes.func.isRequired,
setIndexerSchemaSort: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired,
onSelectIndexer: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(AddIndexerModalContentConnector);

@ -38,6 +38,7 @@ export interface IndexerField extends ModelBase {
interface Indexer extends ModelBase {
name: string;
definitionName: string;
description: string;
encoding: string;
language: string;

@ -3,9 +3,13 @@ import { createAction } from 'redux-actions';
import { filterTypePredicates, sortDirections } from 'Helpers/Props';
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler';
import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler';
import createSaveProviderHandler, {
createCancelSaveProviderHandler
} from 'Store/Actions/Creators/createSaveProviderHandler';
import createTestAllProvidersHandler from 'Store/Actions/Creators/createTestAllProvidersHandler';
import createTestProviderHandler, { createCancelTestProviderHandler } from 'Store/Actions/Creators/createTestProviderHandler';
import createTestProviderHandler, {
createCancelTestProviderHandler
} from 'Store/Actions/Creators/createTestProviderHandler';
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
import { createThunk, handleThunks } from 'Store/thunks';
@ -16,6 +20,7 @@ import translate from 'Utilities/String/translate';
import createBulkEditItemHandler from './Creators/createBulkEditItemHandler';
import createBulkRemoveItemHandler from './Creators/createBulkRemoveItemHandler';
import createHandleActions from './Creators/createHandleActions';
import createClearReducer from './Creators/Reducers/createClearReducer';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
//
@ -96,10 +101,7 @@ export const filterPredicates = {
export const sortPredicates = {
vipExpiration: function(item) {
const vipExpiration =
item.fields.find((field) => field.name === 'vipExpiration')?.value ?? '';
return vipExpiration;
return item.fields.find((field) => field.name === 'vipExpiration')?.value ?? '';
}
};
@ -110,6 +112,7 @@ export const FETCH_INDEXERS = 'indexers/fetchIndexers';
export const FETCH_INDEXER_SCHEMA = 'indexers/fetchIndexerSchema';
export const SELECT_INDEXER_SCHEMA = 'indexers/selectIndexerSchema';
export const SET_INDEXER_SCHEMA_SORT = 'indexers/setIndexerSchemaSort';
export const CLEAR_INDEXER_SCHEMA = 'indexers/clearIndexerSchema';
export const CLONE_INDEXER = 'indexers/cloneIndexer';
export const SET_INDEXER_VALUE = 'indexers/setIndexerValue';
export const SET_INDEXER_FIELD_VALUE = 'indexers/setIndexerFieldValue';
@ -129,6 +132,7 @@ export const fetchIndexers = createThunk(FETCH_INDEXERS);
export const fetchIndexerSchema = createThunk(FETCH_INDEXER_SCHEMA);
export const selectIndexerSchema = createAction(SELECT_INDEXER_SCHEMA);
export const setIndexerSchemaSort = createAction(SET_INDEXER_SCHEMA_SORT);
export const clearIndexerSchema = createAction(CLEAR_INDEXER_SCHEMA);
export const cloneIndexer = createAction(CLONE_INDEXER);
export const saveIndexer = createThunk(SAVE_INDEXER);
@ -214,6 +218,8 @@ export const reducers = createHandleActions({
});
},
[CLEAR_INDEXER_SCHEMA]: createClearReducer(schemaSection, defaultState),
[CLONE_INDEXER]: function(state, { payload }) {
const id = payload.id;
const newState = getSectionState(state, section);

Loading…
Cancel
Save