mirror of
https://github.com/Sonarr/Sonarr
synced 2024-12-25 09:18:34 +00:00
New: Bulk select language and quality in Manual Import
This commit is contained in:
parent
8339f7fdb3
commit
11164ab838
7 changed files with 106 additions and 70 deletions
|
@ -1,5 +1,5 @@
|
|||
.description {
|
||||
composes: title from 'Components/DescriptionList/DescriptionListItemDescription.css';
|
||||
composes: description from 'Components/DescriptionList/DescriptionListItemDescription.css';
|
||||
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
|
|
@ -16,24 +16,21 @@
|
|||
}
|
||||
|
||||
.leftButtons,
|
||||
.centerButtons,
|
||||
.rightButtons {
|
||||
display: flex;
|
||||
flex: 1 0 33%;
|
||||
flex: 1 0 50%;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.centerButtons {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.rightButtons {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.importMode {
|
||||
.importMode,
|
||||
.bulkSelect {
|
||||
composes: select from 'Components/Form/SelectInput.css';
|
||||
|
||||
margin-right: 10px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
|
@ -44,7 +41,6 @@
|
|||
@media only screen and (max-width: $breakpointSmall) {
|
||||
.footer {
|
||||
.leftButtons,
|
||||
.centerButtons,
|
||||
.rightButtons {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
@ -53,10 +49,6 @@
|
|||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.centerButtons {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.rightButtons {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import ModalBody from 'Components/Modal/ModalBody';
|
|||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
|
||||
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
||||
import SelectSeriesModal from 'InteractiveImport/Series/SelectSeriesModal';
|
||||
import SelectSeasonModal from 'InteractiveImport/Season/SelectSeasonModal';
|
||||
import InteractiveImportRow from './InteractiveImportRow';
|
||||
|
@ -80,6 +82,17 @@ const filterExistingFilesOptions = {
|
|||
NEW: 'new'
|
||||
};
|
||||
|
||||
const importModeOptions = [
|
||||
{ key: 'move', value: 'Move Files' },
|
||||
{ key: 'copy', value: 'Copy Files' }
|
||||
];
|
||||
|
||||
const SELECT = 'select';
|
||||
const SERIES = 'series';
|
||||
const SEASON = 'season';
|
||||
const LANGUAGE = 'language';
|
||||
const QUALITY = 'quality';
|
||||
|
||||
class InteractiveImportModalContent extends Component {
|
||||
|
||||
//
|
||||
|
@ -94,8 +107,7 @@ class InteractiveImportModalContent extends Component {
|
|||
lastToggled: null,
|
||||
selectedState: {},
|
||||
invalidRowsSelected: [],
|
||||
isSelectSeriesModalOpen: false,
|
||||
isSelectSeasonModalOpen: false
|
||||
selectModalOpen: null
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -155,20 +167,12 @@ class InteractiveImportModalContent extends Component {
|
|||
this.props.onImportModeChange(value);
|
||||
}
|
||||
|
||||
onSelectSeriesPress = () => {
|
||||
this.setState({ isSelectSeriesModalOpen: true });
|
||||
onSelectModalSelect = ({ value }) => {
|
||||
this.setState({ selectModalOpen: value });
|
||||
}
|
||||
|
||||
onSelectSeasonPress = () => {
|
||||
this.setState({ isSelectSeasonModalOpen: true });
|
||||
}
|
||||
|
||||
onSelectSeriesModalClose = () => {
|
||||
this.setState({ isSelectSeriesModalOpen: false });
|
||||
}
|
||||
|
||||
onSelectSeasonModalClose = () => {
|
||||
this.setState({ isSelectSeasonModalOpen: false });
|
||||
onSelectModalClose = () => {
|
||||
this.setState({ selectModalOpen: null });
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -200,19 +204,27 @@ class InteractiveImportModalContent extends Component {
|
|||
allUnselected,
|
||||
selectedState,
|
||||
invalidRowsSelected,
|
||||
isSelectSeriesModalOpen,
|
||||
isSelectSeasonModalOpen
|
||||
selectModalOpen
|
||||
} = this.state;
|
||||
|
||||
const selectedIds = this.getSelectedIds();
|
||||
const selectedItem = selectedIds.length ? _.find(items, { id: selectedIds[0] }) : null;
|
||||
const errorMessage = getErrorMessage(error, 'Unable to load manual import items');
|
||||
|
||||
const importModeOptions = [
|
||||
{ key: 'move', value: 'Move Files' },
|
||||
{ key: 'copy', value: 'Copy Files' }
|
||||
const bulkSelectOptions = [
|
||||
{ key: SELECT, value: 'Select...', disabled: true },
|
||||
{ key: SEASON, value: 'Select Season' },
|
||||
{ key: LANGUAGE, value: 'Select Language' },
|
||||
{ key: QUALITY, value: 'Select Qualty' }
|
||||
];
|
||||
|
||||
if (allowSeriesChange) {
|
||||
bulkSelectOptions.splice(1, 0, {
|
||||
key: SERIES,
|
||||
value: 'Select Series'
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
|
@ -308,28 +320,25 @@ class InteractiveImportModalContent extends Component {
|
|||
<ModalFooter className={styles.footer}>
|
||||
<div className={styles.leftButtons}>
|
||||
{
|
||||
!downloadId && showImportMode &&
|
||||
!downloadId && showImportMode ?
|
||||
<SelectInput
|
||||
className={styles.importMode}
|
||||
name="importMode"
|
||||
value={importMode}
|
||||
values={importModeOptions}
|
||||
onChange={this.onImportModeChange}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className={styles.centerButtons}>
|
||||
{
|
||||
allowSeriesChange &&
|
||||
<Button onPress={this.onSelectSeriesPress}>
|
||||
Select Series
|
||||
</Button>
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
<Button onPress={this.onSelectSeasonPress}>
|
||||
Select Season
|
||||
</Button>
|
||||
<SelectInput
|
||||
className={styles.bulkSelect}
|
||||
name="select"
|
||||
value={SELECT}
|
||||
values={bulkSelectOptions}
|
||||
isDisabled={!selectedIds.length}
|
||||
onChange={this.onSelectModalSelect}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.rightButtons}>
|
||||
|
@ -353,16 +362,32 @@ class InteractiveImportModalContent extends Component {
|
|||
</ModalFooter>
|
||||
|
||||
<SelectSeriesModal
|
||||
isOpen={isSelectSeriesModalOpen}
|
||||
isOpen={selectModalOpen === SERIES}
|
||||
ids={selectedIds}
|
||||
onModalClose={this.onSelectSeriesModalClose}
|
||||
onModalClose={this.onSelectModalClose}
|
||||
/>
|
||||
|
||||
<SelectSeasonModal
|
||||
isOpen={isSelectSeasonModalOpen}
|
||||
isOpen={selectModalOpen === SEASON}
|
||||
ids={selectedIds}
|
||||
seriesId={selectedItem && selectedItem.series && selectedItem.series.id}
|
||||
onModalClose={this.onSelectSeasonModalClose}
|
||||
onModalClose={this.onSelectModalClose}
|
||||
/>
|
||||
|
||||
<SelectLanguageModal
|
||||
isOpen={selectModalOpen === LANGUAGE}
|
||||
ids={selectedIds}
|
||||
languageId={0}
|
||||
onModalClose={this.onSelectModalClose}
|
||||
/>
|
||||
|
||||
<SelectQualityModal
|
||||
isOpen={selectModalOpen === QUALITY}
|
||||
ids={selectedIds}
|
||||
qualityId={0}
|
||||
proper={false}
|
||||
real={false}
|
||||
onModalClose={this.onSelectModalClose}
|
||||
/>
|
||||
</ModalContent>
|
||||
);
|
||||
|
|
|
@ -328,7 +328,7 @@ class InteractiveImportRow extends Component {
|
|||
|
||||
<SelectQualityModal
|
||||
isOpen={isSelectQualityModalOpen}
|
||||
id={id}
|
||||
ids={[id]}
|
||||
qualityId={quality ? quality.quality.id : 0}
|
||||
proper={quality ? quality.revision.version > 1 : false}
|
||||
real={quality ? quality.revision.real > 0 : false}
|
||||
|
@ -337,7 +337,7 @@ class InteractiveImportRow extends Component {
|
|||
|
||||
<SelectLanguageModal
|
||||
isOpen={isSelectLanguageModalOpen}
|
||||
id={id}
|
||||
ids={[id]}
|
||||
languageId={language ? language.id : 0}
|
||||
onModalClose={this.onSelectLanguageModalClose}
|
||||
/>
|
||||
|
|
|
@ -4,7 +4,7 @@ import React, { Component } from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchLanguageProfileSchema } from 'Store/Actions/settingsActions';
|
||||
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||
import { updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
|
||||
import SelectLanguageModalContent from './SelectLanguageModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
|
@ -29,8 +29,8 @@ function createMapStateToProps() {
|
|||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchLanguageProfileSchema,
|
||||
updateInteractiveImportItem
|
||||
dispatchFetchLanguageProfileSchema: fetchLanguageProfileSchema,
|
||||
dispatchUpdateInteractiveImportItems: updateInteractiveImportItems
|
||||
};
|
||||
|
||||
class SelectLanguageModalContentConnector extends Component {
|
||||
|
@ -40,7 +40,7 @@ class SelectLanguageModalContentConnector extends Component {
|
|||
|
||||
componentDidMount = () => {
|
||||
if (!this.props.isPopulated) {
|
||||
this.props.fetchLanguageProfileSchema();
|
||||
this.props.dispatchFetchLanguageProfileSchema();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,8 +52,8 @@ class SelectLanguageModalContentConnector extends Component {
|
|||
const language = _.find(this.props.items,
|
||||
(item) => item.language.id === languageId).language;
|
||||
|
||||
this.props.updateInteractiveImportItem({
|
||||
id: this.props.id,
|
||||
this.props.dispatchUpdateInteractiveImportItems({
|
||||
ids: this.props.ids,
|
||||
language
|
||||
});
|
||||
|
||||
|
@ -74,13 +74,13 @@ class SelectLanguageModalContentConnector extends Component {
|
|||
}
|
||||
|
||||
SelectLanguageModalContentConnector.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
fetchLanguageProfileSchema: PropTypes.func.isRequired,
|
||||
updateInteractiveImportItem: PropTypes.func.isRequired,
|
||||
dispatchFetchLanguageProfileSchema: PropTypes.func.isRequired,
|
||||
dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { connect } from 'react-redux';
|
|||
import { createSelector } from 'reselect';
|
||||
import getQualities from 'Utilities/Quality/getQualities';
|
||||
import { fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
|
||||
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||
import { updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
|
||||
import SelectQualityModalContent from './SelectQualityModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
|
@ -30,8 +30,8 @@ function createMapStateToProps() {
|
|||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchQualityProfileSchema,
|
||||
updateInteractiveImportItem
|
||||
dispatchFetchQualityProfileSchema: fetchQualityProfileSchema,
|
||||
dispatchUpdateInteractiveImportItems: updateInteractiveImportItems
|
||||
};
|
||||
|
||||
class SelectQualityModalContentConnector extends Component {
|
||||
|
@ -41,7 +41,7 @@ class SelectQualityModalContentConnector extends Component {
|
|||
|
||||
componentDidMount = () => {
|
||||
if (!this.props.isPopulated) {
|
||||
this.props.fetchQualityProfileSchema();
|
||||
this.props.dispatchFetchQualityProfileSchema();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,8 +57,8 @@ class SelectQualityModalContentConnector extends Component {
|
|||
real: real ? 1 : 0
|
||||
};
|
||||
|
||||
this.props.updateInteractiveImportItem({
|
||||
id: this.props.id,
|
||||
this.props.dispatchUpdateInteractiveImportItems({
|
||||
ids: this.props.ids,
|
||||
quality: {
|
||||
quality,
|
||||
revision
|
||||
|
@ -82,13 +82,13 @@ class SelectQualityModalContentConnector extends Component {
|
|||
}
|
||||
|
||||
SelectQualityModalContentConnector.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
fetchQualityProfileSchema: PropTypes.func.isRequired,
|
||||
updateInteractiveImportItem: PropTypes.func.isRequired,
|
||||
dispatchFetchQualityProfileSchema: PropTypes.func.isRequired,
|
||||
dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -68,6 +68,7 @@ export const persistState = [
|
|||
export const FETCH_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/fetchInteractiveImportItems';
|
||||
export const SET_INTERACTIVE_IMPORT_SORT = 'interactiveImport/setInteractiveImportSort';
|
||||
export const UPDATE_INTERACTIVE_IMPORT_ITEM = 'interactiveImport/updateInteractiveImportItem';
|
||||
export const UPDATE_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/updateInteractiveImportItems';
|
||||
export const CLEAR_INTERACTIVE_IMPORT = 'interactiveImport/clearInteractiveImport';
|
||||
export const ADD_RECENT_FOLDER = 'interactiveImport/addRecentFolder';
|
||||
export const REMOVE_RECENT_FOLDER = 'interactiveImport/removeRecentFolder';
|
||||
|
@ -83,6 +84,7 @@ export const CLEAR_INTERACTIVE_IMPORT_EPISODES = 'interactiveImport/clearInterac
|
|||
export const fetchInteractiveImportItems = createThunk(FETCH_INTERACTIVE_IMPORT_ITEMS);
|
||||
export const setInteractiveImportSort = createAction(SET_INTERACTIVE_IMPORT_SORT);
|
||||
export const updateInteractiveImportItem = createAction(UPDATE_INTERACTIVE_IMPORT_ITEM);
|
||||
export const updateInteractiveImportItems = createAction(UPDATE_INTERACTIVE_IMPORT_ITEMS);
|
||||
export const clearInteractiveImport = createAction(CLEAR_INTERACTIVE_IMPORT);
|
||||
export const addRecentFolder = createAction(ADD_RECENT_FOLDER);
|
||||
export const removeRecentFolder = createAction(REMOVE_RECENT_FOLDER);
|
||||
|
@ -152,6 +154,23 @@ export const reducers = createHandleActions({
|
|||
return newState;
|
||||
},
|
||||
|
||||
[UPDATE_INTERACTIVE_IMPORT_ITEMS]: (state, { payload }) => {
|
||||
const ids = payload.ids;
|
||||
const newState = Object.assign({}, state);
|
||||
const items = [...newState.items];
|
||||
|
||||
ids.forEach((id) => {
|
||||
const index = items.findIndex((item) => item.id === id);
|
||||
const item = Object.assign({}, items[index], payload);
|
||||
|
||||
items.splice(index, 1, item);
|
||||
});
|
||||
|
||||
newState.items = items;
|
||||
|
||||
return newState;
|
||||
},
|
||||
|
||||
[ADD_RECENT_FOLDER]: function(state, { payload }) {
|
||||
const folder = payload.folder;
|
||||
const recentFolder = { folder, lastUsed: moment().toISOString() };
|
||||
|
|
Loading…
Reference in a new issue