diff --git a/frontend/src/Series/Details/SeriesDetails.js b/frontend/src/Series/Details/SeriesDetails.js index d31bd9a25..7b0856ccb 100644 --- a/frontend/src/Series/Details/SeriesDetails.js +++ b/frontend/src/Series/Details/SeriesDetails.js @@ -34,6 +34,7 @@ import SeriesAlternateTitles from './SeriesAlternateTitles'; import SeriesDetailsSeasonConnector from './SeriesDetailsSeasonConnector'; import SeriesTagsConnector from './SeriesTagsConnector'; import SeriesDetailsLinks from './SeriesDetailsLinks'; +import MonitoringOptionsModal from 'Series/MonitoringOptions/MonitoringOptionsModal'; import { getSeriesStatusDetails } from 'Series/SeriesStatus'; import styles from './SeriesDetails.css'; @@ -71,6 +72,7 @@ class SeriesDetails extends Component { isDeleteSeriesModalOpen: false, isSeriesHistoryModalOpen: false, isInteractiveImportModalOpen: false, + isMonitorOptionsModalOpen: false, allExpanded: false, allCollapsed: false, expandedState: {}, @@ -132,6 +134,14 @@ class SeriesDetails extends Component { this.setState({ isSeriesHistoryModalOpen: false }); } + onMonitorOptionsPress = () => { + this.setState({ isMonitorOptionsModalOpen: true }); + } + + onMonitorOptionsClose = () => { + this.setState({ isMonitorOptionsModalOpen: false }); + } + onExpandAllPress = () => { const { allExpanded, @@ -211,6 +221,7 @@ class SeriesDetails extends Component { isDeleteSeriesModalOpen, isSeriesHistoryModalOpen, isInteractiveImportModalOpen, + isMonitorOptionsModalOpen, allExpanded, allCollapsed, expandedState, @@ -288,6 +299,12 @@ class SeriesDetails extends Component { + + + @@ -646,6 +664,12 @@ class SeriesDetails extends Component { showImportMode={false} onModalClose={this.onInteractiveImportModalClose} /> + + ); @@ -664,6 +688,7 @@ SeriesDetails.propTypes = { statistics: PropTypes.object.isRequired, qualityProfileId: PropTypes.number.isRequired, monitored: PropTypes.bool.isRequired, + monitor: PropTypes.string, status: PropTypes.string.isRequired, network: PropTypes.string, overview: PropTypes.string.isRequired, @@ -672,6 +697,7 @@ SeriesDetails.propTypes = { alternateTitles: PropTypes.arrayOf(PropTypes.string).isRequired, tags: PropTypes.arrayOf(PropTypes.number).isRequired, isSaving: PropTypes.bool.isRequired, + saveError: PropTypes.object, isRefreshing: PropTypes.bool.isRequired, isSearching: PropTypes.bool.isRequired, isFetching: PropTypes.bool.isRequired, diff --git a/frontend/src/Series/MonitoringOptions/MonitoringOptionModalConnector.js b/frontend/src/Series/MonitoringOptions/MonitoringOptionModalConnector.js new file mode 100644 index 000000000..250945f40 --- /dev/null +++ b/frontend/src/Series/MonitoringOptions/MonitoringOptionModalConnector.js @@ -0,0 +1,39 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { clearPendingChanges } from 'Store/Actions/baseActions'; +import MonitoringOptionsModal from './EditSeriesModal'; + +const mapDispatchToProps = { + clearPendingChanges +}; + +class MonitoringOptionsModalConnector extends Component { + + // + // Listeners + + onModalClose = () => { + this.props.clearPendingChanges({ section: 'series' }); + this.props.onModalClose(); + } + + // + // Render + + render() { + return ( + + ); + } +} + +MonitoringOptionsModalConnector.propTypes = { + onModalClose: PropTypes.func.isRequired, + clearPendingChanges: PropTypes.func.isRequired +}; + +export default connect(undefined, mapDispatchToProps)(MonitoringOptionsModalConnector); diff --git a/frontend/src/Series/MonitoringOptions/MonitoringOptionsModal.js b/frontend/src/Series/MonitoringOptions/MonitoringOptionsModal.js new file mode 100644 index 000000000..4071e7f9a --- /dev/null +++ b/frontend/src/Series/MonitoringOptions/MonitoringOptionsModal.js @@ -0,0 +1,25 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import Modal from 'Components/Modal/Modal'; +import MonitoringOptionsModalContentConnector from './MonitoringOptionsModalContentConnector'; + +function MonitoringOptionsModal({ isOpen, onModalClose, ...otherProps }) { + return ( + + + + ); +} + +MonitoringOptionsModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onModalClose: PropTypes.func.isRequired +}; + +export default MonitoringOptionsModal; diff --git a/frontend/src/Series/MonitoringOptions/MonitoringOptionsModalContent.js b/frontend/src/Series/MonitoringOptions/MonitoringOptionsModalContent.js new file mode 100644 index 000000000..a284dd5c4 --- /dev/null +++ b/frontend/src/Series/MonitoringOptions/MonitoringOptionsModalContent.js @@ -0,0 +1,132 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { inputTypes } from 'Helpers/Props'; +import Button from 'Components/Link/Button'; +import SpinnerButton from 'Components/Link/SpinnerButton'; +import ModalContent from 'Components/Modal/ModalContent'; +import ModalHeader from 'Components/Modal/ModalHeader'; +import ModalBody from 'Components/Modal/ModalBody'; +import ModalFooter from 'Components/Modal/ModalFooter'; +import Form from 'Components/Form/Form'; +import FormGroup from 'Components/Form/FormGroup'; +import FormLabel from 'Components/Form/FormLabel'; +import FormInputGroup from 'Components/Form/FormInputGroup'; + +const NO_CHANGE = 'noChange'; + +class MonitoringOptionsModalContent extends Component { + + // + // Lifecycle + + constructor(props, context) { + super(props, context); + + this.state = { + monitor: NO_CHANGE + }; + } + + componentDidUpdate(prevProps) { + const { + isSaving, + saveError + } = prevProps; + + if (prevProps.isSaving && !isSaving && !saveError) { + this.setState({ + monitor: NO_CHANGE + }); + } + } + + onInputChange = ({ name, value }) => { + this.setState({ [name]: value }); + } + + // + // Listeners + + onSavePress = () => { + const { + onSavePress + } = this.props; + const { + monitor + } = this.state; + + if (monitor !== NO_CHANGE) { + onSavePress({ monitor }); + } + } + + // + // Render + + render() { + const { + isSaving, + onInputChange, + onModalClose, + ...otherProps + } = this.props; + + const { + monitor + } = this.state; + + return ( + + + Monitor Series + + + +
+ + Monitoring + + + +
+
+ + + + + + Save + + +
+ ); + } +} + +MonitoringOptionsModalContent.propTypes = { + seriesId: PropTypes.number.isRequired, + saveError: PropTypes.object, + isSaving: PropTypes.bool.isRequired, + onInputChange: PropTypes.func.isRequired, + onSavePress: PropTypes.func.isRequired, + onModalClose: PropTypes.func.isRequired +}; + +MonitoringOptionsModalContent.defaultProps = { + isSaving: false +}; + +export default MonitoringOptionsModalContent; diff --git a/frontend/src/Series/MonitoringOptions/MonitoringOptionsModalContentConnector.js b/frontend/src/Series/MonitoringOptions/MonitoringOptionsModalContentConnector.js new file mode 100644 index 000000000..39208cbc2 --- /dev/null +++ b/frontend/src/Series/MonitoringOptions/MonitoringOptionsModalContentConnector.js @@ -0,0 +1,77 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; +import { updateSeriesMonitor } from 'Store/Actions/seriesActions'; +import MonitoringOptionsModalContent from './MonitoringOptionsModalContent'; + +function createMapStateToProps() { + return createSelector( + (state) => state.series, + (seriesState) => { + const { + isSaving, + saveError + } = seriesState; + + return { + isSaving, + saveError + }; + } + ); +} + +const mapDispatchToProps = { + dispatchUpdateMonitoringOptions: updateSeriesMonitor +}; + +class MonitoringOptionsModalContentConnector extends Component { + + // + // Lifecycle + + componentDidUpdate(prevProps, prevState) { + if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) { + this.props.onModalClose(true); + } + } + + // + // Listeners + + onInputChange = ({ name, value }) => { + this.setState({ name, value }); + } + + onSavePress = ({ monitor }) => { + this.props.dispatchUpdateMonitoringOptions({ + id: this.props.seriesId, + monitor + }); + } + + // + // Render + + render() { + return ( + + ); + } +} + +MonitoringOptionsModalContentConnector.propTypes = { + seriesId: PropTypes.number.isRequired, + isSaving: PropTypes.bool.isRequired, + saveError: PropTypes.object, + dispatchUpdateMonitoringOptions: PropTypes.func.isRequired, + onModalClose: PropTypes.func.isRequired, + onSavePress: PropTypes.func.isRequired +}; + +export default connect(createMapStateToProps, mapDispatchToProps)(MonitoringOptionsModalContentConnector); diff --git a/frontend/src/Store/Actions/seriesActions.js b/frontend/src/Store/Actions/seriesActions.js index 45f361f79..b26701a3c 100644 --- a/frontend/src/Store/Actions/seriesActions.js +++ b/frontend/src/Store/Actions/seriesActions.js @@ -10,7 +10,8 @@ import createFetchHandler from './Creators/createFetchHandler'; import createSaveProviderHandler from './Creators/createSaveProviderHandler'; import createRemoveItemHandler from './Creators/createRemoveItemHandler'; import createHandleActions from './Creators/createHandleActions'; -import { updateItem } from './baseActions'; +import { updateItem, set } from './baseActions'; +import { fetchEpisodes } from './episodeActions'; // // Local @@ -176,6 +177,7 @@ export const DELETE_SERIES = 'series/deleteSeries'; export const TOGGLE_SERIES_MONITORED = 'series/toggleSeriesMonitored'; export const TOGGLE_SEASON_MONITORED = 'series/toggleSeasonMonitored'; +export const UPDATE_SERIES_MONITOR = 'series/updateSeriesMonitor'; // // Action Creators @@ -209,6 +211,7 @@ export const deleteSeries = createThunk(DELETE_SERIES, (payload) => { export const toggleSeriesMonitored = createThunk(TOGGLE_SERIES_MONITORED); export const toggleSeasonMonitored = createThunk(TOGGLE_SEASON_MONITORED); +export const updateSeriesMonitor = createThunk(UPDATE_SERIES_MONITOR); export const setSeriesValue = createAction(SET_SERIES_VALUE, (payload) => { return { @@ -377,8 +380,54 @@ export const actionHandlers = handleThunks({ }); }); }, MONITOR_TIMEOUT); - } + }, + + [UPDATE_SERIES_MONITOR]: function(getState, payload, dispatch) { + const { + id, + monitor + } = payload; + + const seriesToUpdate = { id }; + + if (monitor !== 'None') { + seriesToUpdate.monitored = true; + } + + dispatch(set({ + section, + isSaving: true + })); + const promise = createAjaxRequest({ + url: '/seasonPass', + method: 'POST', + data: JSON.stringify({ + series: [ + seriesToUpdate + ], + monitoringOptions: { monitor } + }), + dataType: 'json' + }).request; + promise.done((data) => { + dispatch(fetchEpisodes({ seriesId: id })); + + dispatch(set({ + section, + isSaving: false, + saveError: null + })); + }); + + promise.fail((xhr) => { + dispatch(set({ + section, + isSaving: false, + saveError: xhr + })); + }); + } }); //