New: Added Series Monitoring Toggle to Series Details

Closes #3991
pull/4164/head
Robin Dadswell 4 years ago committed by GitHub
parent 39589b8c02
commit 0ff889c3be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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 {
<PageToolbarSeparator />
<PageToolbarButton
label="Series Monitoring"
iconName={icons.MONITORED}
onPress={this.onMonitorOptionsPress}
/>
<PageToolbarButton
label="Edit"
iconName={icons.EDIT}
@ -299,6 +316,7 @@ class SeriesDetails extends Component {
iconName={icons.DELETE}
onPress={this.onDeleteSeriesPress}
/>
</PageToolbarSection>
<PageToolbarSection alignContent={align.RIGHT}>
@ -646,6 +664,12 @@ class SeriesDetails extends Component {
showImportMode={false}
onModalClose={this.onInteractiveImportModalClose}
/>
<MonitoringOptionsModal
isOpen={isMonitorOptionsModalOpen}
seriesId={id}
onModalClose={this.onMonitorOptionsClose}
/>
</PageContentBody>
</PageContent>
);
@ -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,

@ -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 (
<MonitoringOptionsModal
{...this.props}
onModalClose={this.onModalClose}
/>
);
}
}
MonitoringOptionsModalConnector.propTypes = {
onModalClose: PropTypes.func.isRequired,
clearPendingChanges: PropTypes.func.isRequired
};
export default connect(undefined, mapDispatchToProps)(MonitoringOptionsModalConnector);

@ -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 (
<Modal
isOpen={isOpen}
onModalClose={onModalClose}
>
<MonitoringOptionsModalContentConnector
{...otherProps}
onModalClose={onModalClose}
/>
</Modal>
);
}
MonitoringOptionsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default MonitoringOptionsModal;

@ -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 (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Monitor Series
</ModalHeader>
<ModalBody>
<Form {...otherProps}>
<FormGroup>
<FormLabel>Monitoring</FormLabel>
<FormInputGroup
type={inputTypes.MONITOR_EPISODES_SELECT}
name="monitor"
value={monitor}
includeNoChange={true}
onChange={this.onInputChange}
/>
</FormGroup>
</Form>
</ModalBody>
<ModalFooter>
<Button
onPress={onModalClose}
>
Cancel
</Button>
<SpinnerButton
isSpinning={isSaving}
onPress={this.onSavePress}
>
Save
</SpinnerButton>
</ModalFooter>
</ModalContent>
);
}
}
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;

@ -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 (
<MonitoringOptionsModalContent
{...this.props}
onInputChange={this.onInputChange}
onSavePress={this.onSavePress}
/>
);
}
}
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);

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

Loading…
Cancel
Save