diff --git a/frontend/src/App/AppRoutes.js b/frontend/src/App/AppRoutes.js index 58ce900ee..17aa034e0 100644 --- a/frontend/src/App/AppRoutes.js +++ b/frontend/src/App/AppRoutes.js @@ -9,7 +9,6 @@ import StatsConnector from 'Indexer/Stats/StatsConnector'; import SearchIndexConnector from 'Search/SearchIndexConnector'; import ApplicationSettings from 'Settings/Applications/ApplicationSettings'; import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector'; -import IndexerSettingsConnector from 'Settings/Indexers/IndexerSettingsConnector'; import NotificationSettings from 'Settings/Notifications/NotificationSettings'; import Settings from 'Settings/Settings'; import TagSettings from 'Settings/Tags/TagSettings'; @@ -94,11 +93,6 @@ function AppRoutes(props) { component={ApplicationSettings} /> - - { - this._saveCallback = saveCallback; - } - - onChildStateChange = (payload) => { - this.setState(payload); - } - - onSavePress = () => { - if (this._saveCallback) { - this._saveCallback(); - } - } - - // - // Render - - render() { - const { - isTestingAll, - dispatchTestAllIndexers - } = this.props; - - const { - isSaving, - hasPendingChanges - } = this.state; - - return ( - - - - - - - } - onSavePress={this.onSavePress} - /> - - - - - - - - ); - } -} - -IndexerSettings.propTypes = { - isTestingAll: PropTypes.bool.isRequired, - dispatchTestAllIndexers: PropTypes.func.isRequired -}; - -export default IndexerSettings; diff --git a/frontend/src/Settings/Indexers/IndexerSettingsConnector.js b/frontend/src/Settings/Indexers/IndexerSettingsConnector.js deleted file mode 100644 index 9767b7b04..000000000 --- a/frontend/src/Settings/Indexers/IndexerSettingsConnector.js +++ /dev/null @@ -1,21 +0,0 @@ -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { testAllIndexers } from 'Store/Actions/indexerActions'; -import IndexerSettings from './IndexerSettings'; - -function createMapStateToProps() { - return createSelector( - (state) => state.indexers.isTestingAll, - (isTestingAll) => { - return { - isTestingAll - }; - } - ); -} - -const mapDispatchToProps = { - dispatchTestAllIndexers: testAllIndexers -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(IndexerSettings); diff --git a/frontend/src/Settings/Indexers/Indexers/AddIndexerItem.css b/frontend/src/Settings/Indexers/Indexers/AddIndexerItem.css deleted file mode 100644 index 1010221e1..000000000 --- a/frontend/src/Settings/Indexers/Indexers/AddIndexerItem.css +++ /dev/null @@ -1,44 +0,0 @@ -.indexer { - composes: card from '~Components/Card.css'; - - position: relative; - width: 300px; - height: 100px; -} - -.underlay { - @add-mixin cover; -} - -.overlay { - @add-mixin linkOverlay; - - padding: 10px; -} - -.name { - text-align: center; - font-weight: lighter; - font-size: 24px; -} - -.actions { - margin-top: 20px; - text-align: right; -} - -.presetsMenu { - composes: menu from '~Components/Menu/Menu.css'; - - display: inline-block; - margin: 0 5px; -} - -.presetsMenuButton { - composes: button from '~Components/Link/Button.css'; - - &::after { - margin-left: 5px; - content: '\25BE'; - } -} diff --git a/frontend/src/Settings/Indexers/Indexers/AddIndexerItem.js b/frontend/src/Settings/Indexers/Indexers/AddIndexerItem.js deleted file mode 100644 index 2dadcffb2..000000000 --- a/frontend/src/Settings/Indexers/Indexers/AddIndexerItem.js +++ /dev/null @@ -1,112 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import Button from 'Components/Link/Button'; -import Link from 'Components/Link/Link'; -import Menu from 'Components/Menu/Menu'; -import MenuContent from 'Components/Menu/MenuContent'; -import { sizes } from 'Helpers/Props'; -import translate from 'Utilities/String/translate'; -import AddIndexerPresetMenuItem from './AddIndexerPresetMenuItem'; -import styles from './AddIndexerItem.css'; - -class AddIndexerItem extends Component { - - // - // Listeners - - onIndexerSelect = () => { - const { - implementation, - name - } = this.props; - - this.props.onIndexerSelect({ implementation, name }); - } - - // - // Render - - render() { - const { - name, - implementation, - infoLink, - presets, - onIndexerSelect - } = this.props; - - const hasPresets = !!presets && !!presets.length; - - return ( - - - - - - {name} - - - - { - hasPresets && - - - Custom - - - - - Presets - - - - { - presets.map((preset) => { - return ( - - ); - }) - } - - - - } - - - {translate('MoreInfo')} - - - - - ); - } -} - -AddIndexerItem.propTypes = { - name: PropTypes.string.isRequired, - implementation: PropTypes.string.isRequired, - infoLink: PropTypes.string.isRequired, - presets: PropTypes.arrayOf(PropTypes.object), - onIndexerSelect: PropTypes.func.isRequired -}; - -export default AddIndexerItem; diff --git a/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.css b/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.css index 946305dff..9e175cacf 100644 --- a/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.css +++ b/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.css @@ -3,3 +3,28 @@ justify-content: center; flex-wrap: wrap; } + +.modalBody { + composes: modalBody from '~Components/Modal/ModalBody.css'; + + display: flex; + flex: 1 1 auto; + flex-direction: column; +} + +.filterInput { + composes: input from '~Components/Form/TextInput.css'; + + flex: 0 0 auto; + margin-bottom: 20px; +} + +.alert { + composes: alert from '~Components/Alert.css'; + + margin-bottom: 20px; +} + +.scroller { + flex: 1 1 auto; +} diff --git a/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.js b/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.js index 6b6702912..3243729f9 100644 --- a/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.js +++ b/frontend/src/Settings/Indexers/Indexers/AddIndexerModalContent.js @@ -1,102 +1,152 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import Alert from 'Components/Alert'; -import FieldSet from 'Components/FieldSet'; +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 { kinds } from 'Helpers/Props'; +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 getErrorMessage from 'Utilities/Object/getErrorMessage'; import translate from 'Utilities/String/translate'; -import AddIndexerItem from './AddIndexerItem'; +import SelectIndexerRow from './SelectIndexerRow'; import styles from './AddIndexerModalContent.css'; +const columns = [ + { + name: 'protocol', + label: 'Protocol', + isSortable: true, + isVisible: true + }, + { + name: 'name', + label: 'Name', + isSortable: true, + isVisible: true + }, + { + name: 'privacy', + label: 'Privacy', + isSortable: true, + isVisible: true + } +]; + class AddIndexerModalContent extends Component { + // + // Lifecycle + + constructor(props, context) { + super(props, context); + + this.state = { + filter: '' + }; + } + + // + // Listeners + + onFilterChange = ({ value }) => { + this.setState({ filter: value }); + } + // // Render render() { const { - isSchemaFetching, - isSchemaPopulated, - schemaError, - usenetIndexers, - torrentIndexers, + indexers, onIndexerSelect, + sortKey, + sortDirection, + isFetching, + isPopulated, + error, + onSortPress, onModalClose } = this.props; + const filter = this.state.filter; + const filterLower = filter.toLowerCase(); + + const errorMessage = getErrorMessage(error, 'Unable to load indexers'); + return ( Add Indexer - - { - isSchemaFetching && - - } + + - { - !isSchemaFetching && !!schemaError && - - {translate('UnableToAddANewIndexerPleaseTryAgain')} - - } + + + {translate('ProwlarrSupportsAnyIndexer')} + + - { - isSchemaPopulated && !schemaError && - - - - - {translate('ProwlarrSupportsAnyIndexer')} - - - {translate('ForMoreInformationOnTheIndividualIndexers')} - - - - - + + { + isFetching ? : null + } + { + error ? {errorMessage} : null + } + { + isPopulated && !!indexers.length ? + + { - usenetIndexers.map((indexer) => { - return ( - - ); + indexers.map((indexer) => { + return indexer.name.toLowerCase().includes(filterLower) ? + ( + + ) : + null; }) } - - - - - - { - torrentIndexers.map((indexer) => { - return ( - - ); - }) - } - - - - } + + : + null + } + + state.indexers, + createClientSideCollectionSelector('indexers.schema'), (indexers) => { const { - isSchemaFetching, - isSchemaPopulated, - schemaError, - schema + isFetching, + isPopulated, + error, + items, + sortDirection, + sortKey } = indexers; - const usenetIndexers = _.filter(schema, { protocol: 'usenet' }); - const torrentIndexers = _.filter(schema, { protocol: 'torrent' }); - return { - isSchemaFetching, - isSchemaPopulated, - schemaError, - usenetIndexers, - torrentIndexers + isFetching, + isPopulated, + error, + indexers: items, + sortKey, + sortDirection }; } ); @@ -33,7 +33,8 @@ function createMapStateToProps() { const mapDispatchToProps = { fetchIndexerSchema, - selectIndexerSchema + selectIndexerSchema, + setIndexerSchemaSort }; class AddIndexerModalContentConnector extends Component { @@ -53,6 +54,10 @@ class AddIndexerModalContentConnector extends Component { this.props.onModalClose({ indexerSelected: true }); } + onSortPress = (sortKey, sortDirection) => { + this.props.setIndexerSchemaSort({ sortKey, sortDirection }); + } + // // Render @@ -60,6 +65,7 @@ class AddIndexerModalContentConnector extends Component { return ( ); @@ -69,6 +75,7 @@ class AddIndexerModalContentConnector extends Component { AddIndexerModalContentConnector.propTypes = { fetchIndexerSchema: PropTypes.func.isRequired, selectIndexerSchema: PropTypes.func.isRequired, + setIndexerSchemaSort: PropTypes.func.isRequired, onModalClose: PropTypes.func.isRequired }; diff --git a/frontend/src/Settings/Indexers/Indexers/EditIndexerModalContentConnector.js b/frontend/src/Settings/Indexers/Indexers/EditIndexerModalContentConnector.js index 2d6fe7d59..517804feb 100644 --- a/frontend/src/Settings/Indexers/Indexers/EditIndexerModalContentConnector.js +++ b/frontend/src/Settings/Indexers/Indexers/EditIndexerModalContentConnector.js @@ -3,13 +3,13 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { saveIndexer, setIndexerFieldValue, setIndexerValue, testIndexer } from 'Store/Actions/indexerActions'; -import createProviderSelector from 'Store/Selectors/createProviderSelector'; +import createIndexerSchemaSelector from 'Store/Selectors/createIndexerSchemaSelector'; import EditIndexerModalContent from './EditIndexerModalContent'; function createMapStateToProps() { return createSelector( (state) => state.settings.advancedSettings, - createProviderSelector('indexers'), + createIndexerSchemaSelector(), (advancedSettings, indexer) => { return { advancedSettings, diff --git a/frontend/src/Settings/Indexers/Indexers/SelectIndexerRow.css b/frontend/src/Settings/Indexers/Indexers/SelectIndexerRow.css new file mode 100644 index 000000000..b5e99d910 --- /dev/null +++ b/frontend/src/Settings/Indexers/Indexers/SelectIndexerRow.css @@ -0,0 +1,5 @@ +.protocol { + composes: cell from '~Components/Table/Cells/TableRowCell.css'; + + width: 32px; +} diff --git a/frontend/src/Settings/Indexers/Indexers/SelectIndexerRow.js b/frontend/src/Settings/Indexers/Indexers/SelectIndexerRow.js new file mode 100644 index 000000000..bd679ee86 --- /dev/null +++ b/frontend/src/Settings/Indexers/Indexers/SelectIndexerRow.js @@ -0,0 +1,60 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import TableRowCell from 'Components/Table/Cells/TableRowCell'; +import TableRowButton from 'Components/Table/TableRowButton'; +import ProtocolLabel from 'Indexer/Index/Table/ProtocolLabel'; +import styles from './SelectIndexerRow.css'; + +class SelectIndexerRow extends Component { + + // + // Listeners + + onPress = () => { + const { + implementation, + name + } = this.props; + + this.props.onIndexerSelect({ implementation, name }); + } + + // + // Render + + render() { + const { + protocol, + privacy, + name + } = this.props; + + return ( + + + + + + + {name} + + + + {privacy} + + + ); + } +} + +SelectIndexerRow.propTypes = { + name: PropTypes.string.isRequired, + protocol: PropTypes.string.isRequired, + privacy: PropTypes.string.isRequired, + implementation: PropTypes.string.isRequired, + onIndexerSelect: PropTypes.func.isRequired +}; + +export default SelectIndexerRow; diff --git a/frontend/src/Settings/Indexers/Options/IndexerOptions.js b/frontend/src/Settings/Indexers/Options/IndexerOptions.js deleted file mode 100644 index 47adfe959..000000000 --- a/frontend/src/Settings/Indexers/Options/IndexerOptions.js +++ /dev/null @@ -1,171 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import FieldSet from 'Components/FieldSet'; -import Form from 'Components/Form/Form'; -import FormGroup from 'Components/Form/FormGroup'; -import FormInputGroup from 'Components/Form/FormInputGroup'; -import FormLabel from 'Components/Form/FormLabel'; -import LoadingIndicator from 'Components/Loading/LoadingIndicator'; -import { inputTypes } from 'Helpers/Props'; -import translate from 'Utilities/String/translate'; - -function IndexerOptions(props) { - const { - advancedSettings, - isFetching, - error, - settings, - hasSettings, - onInputChange - } = props; - - return ( - - { - isFetching && - - } - - { - !isFetching && error && - - {translate('UnableToLoadIndexerOptions')} - - } - - { - hasSettings && !isFetching && !error && - - - {translate('MinimumAge')} - - - - - - {translate('Retention')} - - - - - - {translate('MaximumSize')} - - - - - - {translate('PreferIndexerFlags')} - - - - - - {translate('AvailabilityDelay')} - - - - - - {translate('RSSSyncInterval')} - - - - - - {translate('WhitelistedSubtitleTags')} - - - - - - {translate('AllowHardcodedSubs')} - - - - - } - - ); -} - -IndexerOptions.propTypes = { - advancedSettings: PropTypes.bool.isRequired, - isFetching: PropTypes.bool.isRequired, - error: PropTypes.object, - settings: PropTypes.object.isRequired, - hasSettings: PropTypes.bool.isRequired, - onInputChange: PropTypes.func.isRequired -}; - -export default IndexerOptions; diff --git a/frontend/src/Settings/Indexers/Options/IndexerOptionsConnector.js b/frontend/src/Settings/Indexers/Options/IndexerOptionsConnector.js deleted file mode 100644 index 4882d4927..000000000 --- a/frontend/src/Settings/Indexers/Options/IndexerOptionsConnector.js +++ /dev/null @@ -1,101 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { clearPendingChanges } from 'Store/Actions/baseActions'; -import { fetchIndexerOptions, saveIndexerOptions, setIndexerOptionsValue } from 'Store/Actions/settingsActions'; -import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector'; -import IndexerOptions from './IndexerOptions'; - -const SECTION = 'indexerOptions'; - -function createMapStateToProps() { - return createSelector( - (state) => state.settings.advancedSettings, - createSettingsSectionSelector(SECTION), - (advancedSettings, sectionSettings) => { - return { - advancedSettings, - ...sectionSettings - }; - } - ); -} - -const mapDispatchToProps = { - dispatchFetchIndexerOptions: fetchIndexerOptions, - dispatchSetIndexerOptionsValue: setIndexerOptionsValue, - dispatchSaveIndexerOptions: saveIndexerOptions, - dispatchClearPendingChanges: clearPendingChanges -}; - -class IndexerOptionsConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - const { - dispatchFetchIndexerOptions, - dispatchSaveIndexerOptions, - onChildMounted - } = this.props; - - dispatchFetchIndexerOptions(); - onChildMounted(dispatchSaveIndexerOptions); - } - - componentDidUpdate(prevProps) { - const { - hasPendingChanges, - isSaving, - onChildStateChange - } = this.props; - - if ( - prevProps.isSaving !== isSaving || - prevProps.hasPendingChanges !== hasPendingChanges - ) { - onChildStateChange({ - isSaving, - hasPendingChanges - }); - } - } - - componentWillUnmount() { - this.props.dispatchClearPendingChanges({ section: `settings.${SECTION}` }); - } - - // - // Listeners - - onInputChange = ({ name, value }) => { - this.props.dispatchSetIndexerOptionsValue({ name, value }); - } - - // - // Render - - render() { - return ( - - ); - } -} - -IndexerOptionsConnector.propTypes = { - isSaving: PropTypes.bool.isRequired, - hasPendingChanges: PropTypes.bool.isRequired, - dispatchFetchIndexerOptions: PropTypes.func.isRequired, - dispatchSetIndexerOptionsValue: PropTypes.func.isRequired, - dispatchSaveIndexerOptions: PropTypes.func.isRequired, - dispatchClearPendingChanges: PropTypes.func.isRequired, - onChildMounted: PropTypes.func.isRequired, - onChildStateChange: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(IndexerOptionsConnector); diff --git a/frontend/src/Store/Actions/Settings/indexerOptions.js b/frontend/src/Store/Actions/Settings/indexerOptions.js deleted file mode 100644 index bafc2735d..000000000 --- a/frontend/src/Store/Actions/Settings/indexerOptions.js +++ /dev/null @@ -1,64 +0,0 @@ -import { createAction } from 'redux-actions'; -import createFetchHandler from 'Store/Actions/Creators/createFetchHandler'; -import createSaveHandler from 'Store/Actions/Creators/createSaveHandler'; -import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer'; -import { createThunk } from 'Store/thunks'; - -// -// Variables - -const section = 'settings.indexerOptions'; - -// -// Actions Types - -export const FETCH_INDEXER_OPTIONS = 'settings/indexerOptions/fetchIndexerOptions'; -export const SAVE_INDEXER_OPTIONS = 'settings/indexerOptions/saveIndexerOptions'; -export const SET_INDEXER_OPTIONS_VALUE = 'settings/indexerOptions/setIndexerOptionsValue'; - -// -// Action Creators - -export const fetchIndexerOptions = createThunk(FETCH_INDEXER_OPTIONS); -export const saveIndexerOptions = createThunk(SAVE_INDEXER_OPTIONS); -export const setIndexerOptionsValue = createAction(SET_INDEXER_OPTIONS_VALUE, (payload) => { - return { - section, - ...payload - }; -}); - -// -// Details - -export default { - - // - // State - - defaultState: { - isFetching: false, - isPopulated: false, - error: null, - pendingChanges: {}, - isSaving: false, - saveError: null, - item: {} - }, - - // - // Action Handlers - - actionHandlers: { - [FETCH_INDEXER_OPTIONS]: createFetchHandler(section, '/config/indexer'), - [SAVE_INDEXER_OPTIONS]: createSaveHandler(section, '/config/indexer') - }, - - // - // Reducers - - reducers: { - [SET_INDEXER_OPTIONS_VALUE]: createSetSettingValueReducer(section) - } - -}; diff --git a/frontend/src/Store/Actions/indexerActions.js b/frontend/src/Store/Actions/indexerActions.js index f7801f847..b18904673 100644 --- a/frontend/src/Store/Actions/indexerActions.js +++ b/frontend/src/Store/Actions/indexerActions.js @@ -1,7 +1,7 @@ import _ from 'lodash'; import { createAction } from 'redux-actions'; +import { sortDirections } from 'Helpers/Props'; import createFetchHandler from 'Store/Actions/Creators/createFetchHandler'; -import createFetchSchemaHandler from 'Store/Actions/Creators/createFetchSchemaHandler'; import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler'; import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler'; import createTestAllProvidersHandler from 'Store/Actions/Creators/createTestAllProvidersHandler'; @@ -12,11 +12,13 @@ import { createThunk, handleThunks } from 'Store/thunks'; import getSectionState from 'Utilities/State/getSectionState'; import updateSectionState from 'Utilities/State/updateSectionState'; import createHandleActions from './Creators/createHandleActions'; +import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer'; // // Variables export const section = 'indexers'; +const schemaSection = `${section}.schema`; // // State @@ -25,17 +27,22 @@ export const defaultState = { isFetching: false, isPopulated: false, error: null, - isSchemaFetching: false, - isSchemaPopulated: false, - schemaError: null, - schema: [], selectedSchema: {}, isSaving: false, saveError: null, isTesting: false, isTestingAll: false, items: [], - pendingChanges: {} + pendingChanges: {}, + + schema: { + isFetching: false, + isPopulated: false, + error: null, + sortKey: 'name', + sortDirection: sortDirections.ASCENDING, + items: [] + } }; // @@ -44,6 +51,7 @@ export const defaultState = { 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 CLONE_INDEXER = 'indexers/cloneIndexer'; export const SET_INDEXER_VALUE = 'indexers/setIndexerValue'; export const SET_INDEXER_FIELD_VALUE = 'indexers/setIndexerFieldValue'; @@ -60,6 +68,7 @@ export const TEST_ALL_INDEXERS = 'indexers/testAllIndexers'; 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 cloneIndexer = createAction(CLONE_INDEXER); export const saveIndexer = createThunk(SAVE_INDEXER); @@ -104,7 +113,7 @@ function selectSchema(state, payload, schemaDefaults) { name } = payload; - const selectedImplementation = _.find(newState.schema, { implementation, name }); + const selectedImplementation = _.find(newState.schema.items, { implementation, name }); newState.selectedSchema = applySchemaDefaults(_.cloneDeep(selectedImplementation), schemaDefaults); @@ -113,7 +122,7 @@ function selectSchema(state, payload, schemaDefaults) { export const actionHandlers = handleThunks({ [FETCH_INDEXERS]: createFetchHandler(section, '/indexer'), - [FETCH_INDEXER_SCHEMA]: createFetchSchemaHandler(section, '/indexer/schema'), + [FETCH_INDEXER_SCHEMA]: createFetchHandler(schemaSection, '/indexer/schema'), [SAVE_INDEXER]: createSaveProviderHandler(section, '/indexer'), [CANCEL_SAVE_INDEXER]: createCancelSaveProviderHandler(section), @@ -129,6 +138,7 @@ export const actionHandlers = handleThunks({ export const reducers = createHandleActions({ [SET_INDEXER_VALUE]: createSetSettingValueReducer(section), [SET_INDEXER_FIELD_VALUE]: createSetProviderFieldValueReducer(section), + [SET_INDEXER_SCHEMA_SORT]: createSetClientSideCollectionSortReducer(schemaSection), [SELECT_INDEXER_SCHEMA]: (state, { payload }) => { return selectSchema(state, payload, (selectedSchema) => { diff --git a/frontend/src/Store/Actions/settingsActions.js b/frontend/src/Store/Actions/settingsActions.js index 47c3f573b..330d42f10 100644 --- a/frontend/src/Store/Actions/settingsActions.js +++ b/frontend/src/Store/Actions/settingsActions.js @@ -5,7 +5,6 @@ import applications from './Settings/applications'; import general from './Settings/general'; import indexerCategories from './Settings/indexerCategories'; import indexerFlags from './Settings/indexerFlags'; -import indexerOptions from './Settings/indexerOptions'; import languages from './Settings/languages'; import notifications from './Settings/notifications'; import ui from './Settings/ui'; @@ -13,7 +12,6 @@ import ui from './Settings/ui'; export * from './Settings/general'; export * from './Settings/indexerCategories'; export * from './Settings/indexerFlags'; -export * from './Settings/indexerOptions'; export * from './Settings/languages'; export * from './Settings/notifications'; export * from './Settings/applications'; @@ -33,7 +31,6 @@ export const defaultState = { general: general.defaultState, indexerCategories: indexerCategories.defaultState, indexerFlags: indexerFlags.defaultState, - indexerOptions: indexerOptions.defaultState, languages: languages.defaultState, notifications: notifications.defaultState, applications: applications.defaultState, @@ -61,7 +58,6 @@ export const actionHandlers = handleThunks({ ...general.actionHandlers, ...indexerCategories.actionHandlers, ...indexerFlags.actionHandlers, - ...indexerOptions.actionHandlers, ...languages.actionHandlers, ...notifications.actionHandlers, ...applications.actionHandlers, @@ -80,7 +76,6 @@ export const reducers = createHandleActions({ ...general.reducers, ...indexerCategories.reducers, ...indexerFlags.reducers, - ...indexerOptions.reducers, ...languages.reducers, ...notifications.reducers, ...applications.reducers, diff --git a/frontend/src/Store/Selectors/createIndexerSchemaSelector.js b/frontend/src/Store/Selectors/createIndexerSchemaSelector.js new file mode 100644 index 000000000..626b72bf3 --- /dev/null +++ b/frontend/src/Store/Selectors/createIndexerSchemaSelector.js @@ -0,0 +1,66 @@ +import _ from 'lodash'; +import { createSelector } from 'reselect'; +import selectSettings from 'Store/Selectors/selectSettings'; + +function createIndexerSchemaSelector() { + return createSelector( + (state, { id }) => id, + (state) => state.indexers, + (id, section) => { + if (!id) { + const item = _.isArray(section.schema.items) ? section.selectedSchema : section.schema.items; + const settings = selectSettings(Object.assign({ name: '' }, item), section.pendingChanges, section.saveError); + + const { + isSaving, + saveError, + isTesting, + pendingChanges + } = section; + + const { + isFetching, + isPopulated, + error + } = section.schema; + + return { + isFetching, + isPopulated, + error, + isSaving, + saveError, + isTesting, + pendingChanges, + ...settings, + item: settings.settings + }; + } + + const { + isFetching, + isPopulated, + error, + isSaving, + saveError, + isTesting, + pendingChanges + } = section; + + const settings = selectSettings(_.find(section.items, { id }), pendingChanges, saveError); + + return { + isFetching, + isPopulated, + error, + isSaving, + saveError, + isTesting, + ...settings, + item: settings.settings + }; + } + ); +} + +export default createIndexerSchemaSelector; diff --git a/frontend/src/Utilities/State/getProviderState.js b/frontend/src/Utilities/State/getProviderState.js index 4159905b8..8046211cd 100644 --- a/frontend/src/Utilities/State/getProviderState.js +++ b/frontend/src/Utilities/State/getProviderState.js @@ -12,7 +12,7 @@ function getProviderState(payload, getState, section, keyValueOnly=true) { const pendingFields = state.pendingChanges.fields || {}; delete pendingChanges.fields; - const item = id ? _.find(state.items, { id }) : state.selectedSchema || state.schema || {}; + const item = id ? _.find(state.items, { id }) : state.selectedSchema || state.schema || state.schema.items || {}; if (item.fields) { pendingChanges.fields = _.reduce(item.fields, (result, field) => { diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 095d9c12b..5715a8fbf 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -256,7 +256,7 @@ "Files": "Files", "Filesize": "Filesize", "Filter": "Filter", - "FilterPlaceHolder": "Search movies", + "FilterPlaceHolder": "Search indexers", "FirstDayOfWeek": "First Day of Week", "Fixed": "Fixed", "FocusSearchBox": "Focus Search Box",