From f1fdec6822f7f5d3baeddb6d8b8201eb41a4eaf9 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Fri, 26 Jul 2024 06:25:37 +0300 Subject: [PATCH] Convert Add Indexer Modal to Typescript --- frontend/src/Indexer/Add/AddIndexerModal.js | 31 -- frontend/src/Indexer/Add/AddIndexerModal.tsx | 51 +++ .../Indexer/Add/AddIndexerModalContent.css | 8 +- .../Add/AddIndexerModalContent.css.d.ts | 1 + .../src/Indexer/Add/AddIndexerModalContent.js | 325 -------------- .../Indexer/Add/AddIndexerModalContent.tsx | 414 ++++++++++++++++++ .../Add/AddIndexerModalContentConnector.js | 94 ---- frontend/src/Indexer/Indexer.ts | 1 + frontend/src/Store/Actions/indexerActions.js | 18 +- 9 files changed, 486 insertions(+), 457 deletions(-) delete mode 100644 frontend/src/Indexer/Add/AddIndexerModal.js create mode 100644 frontend/src/Indexer/Add/AddIndexerModal.tsx delete mode 100644 frontend/src/Indexer/Add/AddIndexerModalContent.js create mode 100644 frontend/src/Indexer/Add/AddIndexerModalContent.tsx delete mode 100644 frontend/src/Indexer/Add/AddIndexerModalContentConnector.js diff --git a/frontend/src/Indexer/Add/AddIndexerModal.js b/frontend/src/Indexer/Add/AddIndexerModal.js deleted file mode 100644 index 9344c8130..000000000 --- a/frontend/src/Indexer/Add/AddIndexerModal.js +++ /dev/null @@ -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 ( - - - - ); -} - -AddIndexerModal.propTypes = { - isOpen: PropTypes.bool.isRequired, - onModalClose: PropTypes.func.isRequired, - onSelectIndexer: PropTypes.func.isRequired -}; - -export default AddIndexerModal; diff --git a/frontend/src/Indexer/Add/AddIndexerModal.tsx b/frontend/src/Indexer/Add/AddIndexerModal.tsx new file mode 100644 index 000000000..d3c568022 --- /dev/null +++ b/frontend/src/Indexer/Add/AddIndexerModal.tsx @@ -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 ( + + + + ); +} + +AddIndexerModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onModalClose: PropTypes.func.isRequired, + onSelectIndexer: PropTypes.func.isRequired, +}; + +export default AddIndexerModal; diff --git a/frontend/src/Indexer/Add/AddIndexerModalContent.css b/frontend/src/Indexer/Add/AddIndexerModalContent.css index a58eccfbc..e824c5475 100644 --- a/frontend/src/Indexer/Add/AddIndexerModalContent.css +++ b/frontend/src/Indexer/Add/AddIndexerModalContent.css @@ -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; } diff --git a/frontend/src/Indexer/Add/AddIndexerModalContent.css.d.ts b/frontend/src/Indexer/Add/AddIndexerModalContent.css.d.ts index cbedc72a4..5978832e4 100644 --- a/frontend/src/Indexer/Add/AddIndexerModalContent.css.d.ts +++ b/frontend/src/Indexer/Add/AddIndexerModalContent.css.d.ts @@ -10,6 +10,7 @@ interface CssExports { 'indexers': string; 'modalBody': string; 'modalFooter': string; + 'notice': string; 'scroller': string; } export const cssExports: CssExports; diff --git a/frontend/src/Indexer/Add/AddIndexerModalContent.js b/frontend/src/Indexer/Add/AddIndexerModalContent.js deleted file mode 100644 index 642abe095..000000000 --- a/frontend/src/Indexer/Add/AddIndexerModalContent.js +++ /dev/null @@ -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 ( - - - {translate('AddIndexer')} - - - - - -
-
- - this.setState({ filterProtocols: value })} - /> -
- -
- - this.setState({ filterLanguages: value })} - /> -
- -
- - this.setState({ filterPrivacyLevels: value })} - /> -
- -
- - this.setState({ filterCategories: value })} - /> -
-
- - -
- {translate('ProwlarrSupportsAnyIndexer')} -
-
- - - { - isFetching ? : null - } - { - error ? {errorMessage} : null - } - { - isPopulated && !!indexers.length ? - - - { - filteredIndexers.map((indexer) => ( - - )) - } - -
: - null - } - { - isPopulated && !!indexers.length && !filteredIndexers.length ? - - {translate('NoIndexersFound')} - : - null - } -
-
- - -
- { - isPopulated ? - translate('CountIndexersAvailable', { count: filteredIndexers.length }) : - null - } -
- -
- -
-
-
- ); - } -} - -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; diff --git a/frontend/src/Indexer/Add/AddIndexerModalContent.tsx b/frontend/src/Indexer/Add/AddIndexerModalContent.tsx new file mode 100644 index 000000000..66af61a90 --- /dev/null +++ b/frontend/src/Indexer/Add/AddIndexerModalContent.tsx @@ -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([]); + const [filterLanguages, setFilterLanguages] = useState([]); + const [filterPrivacyLevels, setFilterPrivacyLevels] = useState([]); + const [filterCategories, setFilterCategories] = useState([]); + + 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( + (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 ( + + {translate('AddIndexer')} + + + + +
+
+ + + + setFilterProtocols(value) + } + /> +
+ +
+ + + + setFilterLanguages(value) + } + /> +
+ +
+ + + setFilterPrivacyLevels(value) + } + /> +
+ +
+ + + + setFilterCategories(value) + } + /> +
+
+ + +
{translate('ProwlarrSupportsAnyIndexer')}
+
+ + + {isFetching ? : null} + + {error ? ( + + {errorMessage} + + ) : null} + + {isPopulated && !!indexers.length ? ( + + + {filteredIndexers.map((indexer) => ( + + ))} + +
+ ) : null} + + {isPopulated && !!indexers.length && !filteredIndexers.length ? ( + + {translate('NoIndexersFound')} + + ) : null} +
+
+ + +
+ {isPopulated + ? translate('CountIndexersAvailable', { + count: filteredIndexers.length, + }) + : null} +
+ +
+ +
+
+
+ ); +} + +export default AddIndexerModalContent; diff --git a/frontend/src/Indexer/Add/AddIndexerModalContentConnector.js b/frontend/src/Indexer/Add/AddIndexerModalContentConnector.js deleted file mode 100644 index a422e0a03..000000000 --- a/frontend/src/Indexer/Add/AddIndexerModalContentConnector.js +++ /dev/null @@ -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 ( - - ); - } -} - -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); diff --git a/frontend/src/Indexer/Indexer.ts b/frontend/src/Indexer/Indexer.ts index b0e739a11..c2a63aa62 100644 --- a/frontend/src/Indexer/Indexer.ts +++ b/frontend/src/Indexer/Indexer.ts @@ -38,6 +38,7 @@ export interface IndexerField extends ModelBase { interface Indexer extends ModelBase { name: string; + definitionName: string; description: string; encoding: string; language: string; diff --git a/frontend/src/Store/Actions/indexerActions.js b/frontend/src/Store/Actions/indexerActions.js index 2aae11b36..c8cde2ee3 100644 --- a/frontend/src/Store/Actions/indexerActions.js +++ b/frontend/src/Store/Actions/indexerActions.js @@ -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);