@@ -95,6 +93,7 @@ class PageHeader extends Component {
PageHeader.propTypes = {
onSidebarToggle: PropTypes.func.isRequired,
+ isSmallScreen: PropTypes.bool.isRequired,
bindShortcut: PropTypes.func.isRequired
};
diff --git a/frontend/src/Components/Page/Page.js b/frontend/src/Components/Page/Page.js
index 4f871f864..2bb59c532 100644
--- a/frontend/src/Components/Page/Page.js
+++ b/frontend/src/Components/Page/Page.js
@@ -86,6 +86,7 @@ class Page extends Component {
diff --git a/frontend/src/Components/Page/PageConnector.js b/frontend/src/Components/Page/PageConnector.js
index 340663da0..516548b75 100644
--- a/frontend/src/Components/Page/PageConnector.js
+++ b/frontend/src/Components/Page/PageConnector.js
@@ -8,7 +8,7 @@ import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
import { fetchMovies } from 'Store/Actions/movieActions';
import { fetchTags } from 'Store/Actions/tagActions';
-import { fetchQualityProfiles, fetchUISettings } from 'Store/Actions/settingsActions';
+import { fetchQualityProfiles, fetchUISettings, fetchLanguages } from 'Store/Actions/settingsActions';
import { fetchStatus } from 'Store/Actions/systemActions';
import ErrorPage from './ErrorPage';
import LoadingPage from './LoadingPage';
@@ -48,6 +48,7 @@ const selectIsPopulated = createSelector(
(state) => state.tags.isPopulated,
(state) => state.settings.ui.isPopulated,
(state) => state.settings.qualityProfiles.isPopulated,
+ (state) => state.settings.languages.isPopulated,
(state) => state.system.status.isPopulated,
(
moviesIsPopulated,
@@ -55,6 +56,7 @@ const selectIsPopulated = createSelector(
tagsIsPopulated,
uiSettingsIsPopulated,
qualityProfilesIsPopulated,
+ languagesIsPopulated,
systemStatusIsPopulated
) => {
return (
@@ -63,6 +65,7 @@ const selectIsPopulated = createSelector(
tagsIsPopulated &&
uiSettingsIsPopulated &&
qualityProfilesIsPopulated &&
+ languagesIsPopulated &&
systemStatusIsPopulated
);
}
@@ -74,6 +77,7 @@ const selectErrors = createSelector(
(state) => state.tags.error,
(state) => state.settings.ui.error,
(state) => state.settings.qualityProfiles.error,
+ (state) => state.settings.languages.error,
(state) => state.system.status.error,
(
moviesError,
@@ -81,6 +85,7 @@ const selectErrors = createSelector(
tagsError,
uiSettingsError,
qualityProfilesError,
+ languagesError,
systemStatusError
) => {
const hasError = !!(
@@ -89,6 +94,7 @@ const selectErrors = createSelector(
tagsError ||
uiSettingsError ||
qualityProfilesError ||
+ languagesError ||
systemStatusError
);
@@ -99,6 +105,7 @@ const selectErrors = createSelector(
tagsError,
uiSettingsError,
qualityProfilesError,
+ languagesError,
systemStatusError
};
}
@@ -143,6 +150,9 @@ function createMapDispatchToProps(dispatch, props) {
dispatchFetchQualityProfiles() {
dispatch(fetchQualityProfiles());
},
+ dispatchFetchLanguages() {
+ dispatch(fetchLanguages());
+ },
dispatchFetchUISettings() {
dispatch(fetchUISettings());
},
@@ -177,6 +187,7 @@ class PageConnector extends Component {
this.props.dispatchFetchCustomFilters();
this.props.dispatchFetchTags();
this.props.dispatchFetchQualityProfiles();
+ this.props.dispatchFetchLanguages();
this.props.dispatchFetchUISettings();
this.props.dispatchFetchStatus();
}
@@ -199,6 +210,7 @@ class PageConnector extends Component {
dispatchFetchMovies,
dispatchFetchTags,
dispatchFetchQualityProfiles,
+ dispatchFetchLanguages,
dispatchFetchUISettings,
dispatchFetchStatus,
...otherProps
@@ -236,6 +248,7 @@ PageConnector.propTypes = {
dispatchFetchCustomFilters: PropTypes.func.isRequired,
dispatchFetchTags: PropTypes.func.isRequired,
dispatchFetchQualityProfiles: PropTypes.func.isRequired,
+ dispatchFetchLanguages: PropTypes.func.isRequired,
dispatchFetchUISettings: PropTypes.func.isRequired,
dispatchFetchStatus: PropTypes.func.isRequired,
onSidebarVisibleChange: PropTypes.func.isRequired
diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.css b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.css
index e6a0351af..d50f3a261 100644
--- a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.css
+++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.css
@@ -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;
}
diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js
index fe8ce2ecc..71363f1f0 100644
--- a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js
+++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js
@@ -20,7 +20,9 @@ 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 SelectSeriesModal from 'InteractiveImport/Series/SelectSeriesModal';
+import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
+import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
+import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
import InteractiveImportRow from './InteractiveImportRow';
import styles from './InteractiveImportModalContent.css';
@@ -32,8 +34,8 @@ const columns = [
isVisible: true
},
{
- name: 'series',
- label: 'Series',
+ name: 'movie',
+ label: 'Movie',
isSortable: true,
isVisible: true
},
@@ -69,6 +71,16 @@ const filterExistingFilesOptions = {
NEW: 'new'
};
+const importModeOptions = [
+ { key: 'move', value: 'Move Files' },
+ { key: 'copy', value: 'Copy Files' }
+];
+
+const SELECT = 'select';
+const MOVIE = 'movie';
+const LANGUAGE = 'language';
+const QUALITY = 'quality';
+
class InteractiveImportModalContent extends Component {
//
@@ -83,7 +95,7 @@ class InteractiveImportModalContent extends Component {
lastToggled: null,
selectedState: {},
invalidRowsSelected: [],
- isSelectSeriesModalOpen: false
+ selectModalOpen: null
};
}
@@ -122,9 +134,17 @@ class InteractiveImportModalContent extends Component {
}
onImportSelectedPress = () => {
+ const {
+ downloadId,
+ showImportMode,
+ importMode,
+ onImportSelectedPress
+ } = this.props;
+
const selected = this.getSelectedIds();
+ const finalImportMode = downloadId || !showImportMode ? 'auto' : importMode;
- this.props.onImportSelectedPress(selected, this.props.importMode);
+ onImportSelectedPress(selected, finalImportMode);
}
onFilterExistingFilesChange = (value) => {
@@ -135,12 +155,12 @@ class InteractiveImportModalContent extends Component {
this.props.onImportModeChange(value);
}
- onSelectSeriesPress = () => {
- this.setState({ isSelectSeriesModalOpen: true });
+ onSelectModalSelect = ({ value }) => {
+ this.setState({ selectModalOpen: value });
}
- onSelectSeriesModalClose = () => {
- this.setState({ isSelectSeriesModalOpen: false });
+ onSelectModalClose = () => {
+ this.setState({ selectModalOpen: null });
}
//
@@ -149,7 +169,7 @@ class InteractiveImportModalContent extends Component {
render() {
const {
downloadId,
- allowSeriesChange,
+ allowMovieChange,
showFilterExistingFiles,
showImportMode,
filterExistingFiles,
@@ -172,17 +192,25 @@ class InteractiveImportModalContent extends Component {
allUnselected,
selectedState,
invalidRowsSelected,
- isSelectSeriesModalOpen
+ selectModalOpen
} = this.state;
const selectedIds = this.getSelectedIds();
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: LANGUAGE, value: 'Select Language' },
+ { key: QUALITY, value: 'Select Quality' }
];
+ if (allowMovieChange) {
+ bulkSelectOptions.splice(1, 0, {
+ key: MOVIE,
+ value: 'Select Movie'
+ });
+ }
+
return (
@@ -258,7 +286,7 @@ class InteractiveImportModalContent extends Component {
key={item.id}
isSelected={selectedState[item.id]}
{...item}
- allowSeriesChange={allowSeriesChange}
+ allowMovieChange={allowMovieChange}
onSelectedChange={this.onSelectedChange}
onValidRowChange={this.onValidRowChange}
/>
@@ -278,24 +306,25 @@ class InteractiveImportModalContent extends Component {
{
- !downloadId && showImportMode &&
+ !downloadId && showImportMode ?
+ /> :
+ null
}
-
-
- {
- allowSeriesChange &&
-
- }
+
@@ -318,10 +347,26 @@ class InteractiveImportModalContent extends Component {
-
+
+
+
+
);
@@ -330,7 +375,7 @@ class InteractiveImportModalContent extends Component {
InteractiveImportModalContent.propTypes = {
downloadId: PropTypes.string,
- allowSeriesChange: PropTypes.bool.isRequired,
+ allowMovieChange: PropTypes.bool.isRequired,
showImportMode: PropTypes.bool.isRequired,
showFilterExistingFiles: PropTypes.bool.isRequired,
filterExistingFiles: PropTypes.bool.isRequired,
@@ -352,7 +397,7 @@ InteractiveImportModalContent.propTypes = {
};
InteractiveImportModalContent.defaultProps = {
- allowSeriesChange: true,
+ allowMovieChange: true,
showFilterExistingFiles: false,
showImportMode: true,
importMode: 'move'
diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContentConnector.js b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContentConnector.js
index 3a7b03fb6..a28919e29 100644
--- a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContentConnector.js
+++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContentConnector.js
@@ -19,11 +19,11 @@ function createMapStateToProps() {
}
const mapDispatchToProps = {
- fetchInteractiveImportItems,
- setInteractiveImportSort,
- setInteractiveImportMode,
- clearInteractiveImport,
- executeCommand
+ dispatchFetchInteractiveImportItems: fetchInteractiveImportItems,
+ dispatchSetInteractiveImportSort: setInteractiveImportSort,
+ dispatchSetInteractiveImportMode: setInteractiveImportMode,
+ dispatchClearInteractiveImport: clearInteractiveImport,
+ dispatchExecuteCommand: executeCommand
};
class InteractiveImportModalContentConnector extends Component {
@@ -50,7 +50,7 @@ class InteractiveImportModalContentConnector extends Component {
filterExistingFiles
} = this.state;
- this.props.fetchInteractiveImportItems({
+ this.props.dispatchFetchInteractiveImportItems({
downloadId,
folder,
filterExistingFiles
@@ -68,7 +68,7 @@ class InteractiveImportModalContentConnector extends Component {
folder
} = this.props;
- this.props.fetchInteractiveImportItems({
+ this.props.dispatchFetchInteractiveImportItems({
downloadId,
folder,
filterExistingFiles
@@ -77,14 +77,14 @@ class InteractiveImportModalContentConnector extends Component {
}
componentWillUnmount() {
- this.props.clearInteractiveImport();
+ this.props.dispatchClearInteractiveImport();
}
//
// Listeners
onSortPress = (sortKey, sortDirection) => {
- this.props.setInteractiveImportSort({ sortKey, sortDirection });
+ this.props.dispatchSetInteractiveImportSort({ sortKey, sortDirection });
}
onFilterExistingFilesChange = (filterExistingFiles) => {
@@ -92,7 +92,7 @@ class InteractiveImportModalContentConnector extends Component {
}
onImportModeChange = (importMode) => {
- this.props.setInteractiveImportMode({ importMode });
+ this.props.dispatchSetInteractiveImportMode({ importMode });
}
onImportSelectedPress = (selected, importMode) => {
@@ -103,25 +103,13 @@ class InteractiveImportModalContentConnector extends Component {
if (isSelected) {
const {
- series,
- seasonNumber,
- episodes,
+ movie,
quality,
language
} = item;
- if (!series) {
- this.setState({ interactiveImportErrorMessage: 'Series must be chosen for each selected file' });
- return false;
- }
-
- if (isNaN(seasonNumber)) {
- this.setState({ interactiveImportErrorMessage: 'Season must be chosen for each selected file' });
- return false;
- }
-
- if (!episodes || !episodes.length) {
- this.setState({ interactiveImportErrorMessage: 'One or more episodes must be chosen for each selected file' });
+ if (!movie) {
+ this.setState({ interactiveImportErrorMessage: 'Movie must be chosen for each selected file' });
return false;
}
@@ -138,8 +126,7 @@ class InteractiveImportModalContentConnector extends Component {
files.push({
path: item.path,
folderName: item.folderName,
- seriesId: series.id,
- episodeIds: _.map(episodes, 'id'),
+ movieId: movie.id,
quality,
language,
downloadId: this.props.downloadId
@@ -151,7 +138,7 @@ class InteractiveImportModalContentConnector extends Component {
return;
}
- this.props.executeCommand({
+ this.props.dispatchExecuteCommand({
name: commandNames.INTERACTIVE_IMPORT,
files,
importMode
@@ -188,11 +175,11 @@ InteractiveImportModalContentConnector.propTypes = {
folder: PropTypes.string,
filterExistingFiles: PropTypes.bool.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
- fetchInteractiveImportItems: PropTypes.func.isRequired,
- setInteractiveImportSort: PropTypes.func.isRequired,
- clearInteractiveImport: PropTypes.func.isRequired,
- setInteractiveImportMode: PropTypes.func.isRequired,
- executeCommand: PropTypes.func.isRequired,
+ dispatchFetchInteractiveImportItems: PropTypes.func.isRequired,
+ dispatchSetInteractiveImportSort: PropTypes.func.isRequired,
+ dispatchSetInteractiveImportMode: PropTypes.func.isRequired,
+ dispatchClearInteractiveImport: PropTypes.func.isRequired,
+ dispatchExecuteCommand: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js
index 0f84b9a8c..090ef1fd9 100644
--- a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js
+++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js
@@ -9,9 +9,10 @@ import TableRowCellButton from 'Components/Table/Cells/TableRowCellButton';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import Popover from 'Components/Tooltip/Popover';
import MovieQuality from 'Movie/MovieQuality';
-// import EpisodeLanguage from 'Episode/EpisodeLanguage';
-import SelectSeriesModal from 'InteractiveImport/Series/SelectSeriesModal';
+// import MovieLanguage from 'Movie/MovieLanguage';
+import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
+import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
import InteractiveImportRowCellPlaceholder from './InteractiveImportRowCellPlaceholder';
import styles from './InteractiveImportRow.css';
@@ -24,21 +25,24 @@ class InteractiveImportRow extends Component {
super(props, context);
this.state = {
- isSelectSeriesModalOpen: false,
- isSelectQualityModalOpen: false
+ isSelectMovieModalOpen: false,
+ isSelectQualityModalOpen: false,
+ isSelectLanguageModalOpen: false
};
}
componentDidMount() {
const {
id,
- series,
- quality
+ movie,
+ quality,
+ language
} = this.props;
if (
- series &&
- quality
+ movie &&
+ quality &&
+ language
) {
this.props.onSelectedChange({ id, value: true });
}
@@ -47,23 +51,26 @@ class InteractiveImportRow extends Component {
componentDidUpdate(prevProps) {
const {
id,
- series,
+ movie,
quality,
+ language,
isSelected,
onValidRowChange
} = this.props;
if (
- prevProps.series === series &&
+ prevProps.movie === movie &&
prevProps.quality === quality &&
+ prevProps.language === language &&
prevProps.isSelected === isSelected
) {
return;
}
const isValid = !!(
- series &&
- quality
+ movie &&
+ quality &&
+ language
);
if (isSelected && !isValid) {
@@ -90,16 +97,20 @@ class InteractiveImportRow extends Component {
//
// Listeners
- onSelectSeriesPress = () => {
- this.setState({ isSelectSeriesModalOpen: true });
+ onSelectMoviePress = () => {
+ this.setState({ isSelectMovieModalOpen: true });
}
onSelectQualityPress = () => {
this.setState({ isSelectQualityModalOpen: true });
}
- onSelectSeriesModalClose = (changed) => {
- this.setState({ isSelectSeriesModalOpen: false });
+ onSelectLanguagePress = () => {
+ this.setState({ isSelectLanguageModalOpen: true });
+ }
+
+ onSelectMovieModalClose = (changed) => {
+ this.setState({ isSelectMovieModalOpen: false });
this.selectRowAfterChange(changed);
}
@@ -108,16 +119,22 @@ class InteractiveImportRow extends Component {
this.selectRowAfterChange(changed);
}
+ onSelectLanguageModalClose = (changed) => {
+ this.setState({ isSelectLanguageModalOpen: false });
+ this.selectRowAfterChange(changed);
+ }
+
//
// Render
render() {
const {
id,
- allowSeriesChange,
+ allowMovieChange,
relativePath,
- series,
+ movie,
quality,
+ language,
size,
rejections,
isSelected,
@@ -125,14 +142,16 @@ class InteractiveImportRow extends Component {
} = this.props;
const {
- isSelectSeriesModalOpen,
- isSelectQualityModalOpen
+ isSelectMovieModalOpen,
+ isSelectQualityModalOpen,
+ isSelectLanguageModalOpen
} = this.state;
- const seriesTitle = series ? series.title : '';
+ const movieTitle = movie ? movie.title : '';
- const showSeriesPlaceholder = isSelected && !series;
+ const showMoviePlaceholder = isSelected && !movie;
const showQualityPlaceholder = isSelected && !quality;
+ const showLanguagePlaceholder = isSelected && !language;
return (
@@ -150,16 +169,18 @@ class InteractiveImportRow extends Component {
{
- showSeriesPlaceholder ? : seriesTitle
+ showMoviePlaceholder ? : movieTitle
}
{
@@ -176,6 +197,25 @@ class InteractiveImportRow extends Component {
}
+
+ {
+ showLanguagePlaceholder &&
+
+ }
+
+ {/* {
+ !showLanguagePlaceholder && !!language &&
+
+ } */}
+
+
{formatBytes(size)}
@@ -209,20 +249,27 @@ class InteractiveImportRow extends Component {
}
-
1 : false}
real={quality ? quality.revision.real > 0 : false}
onModalClose={this.onSelectQualityModalClose}
/>
+
+
);
}
@@ -231,12 +278,11 @@ class InteractiveImportRow extends Component {
InteractiveImportRow.propTypes = {
id: PropTypes.number.isRequired,
- allowSeriesChange: PropTypes.bool.isRequired,
+ allowMovieChange: PropTypes.bool.isRequired,
relativePath: PropTypes.string.isRequired,
- series: PropTypes.object,
- seasonNumber: PropTypes.number,
- episodes: PropTypes.arrayOf(PropTypes.object).isRequired,
+ movie: PropTypes.object,
quality: PropTypes.object,
+ language: PropTypes.object,
size: PropTypes.number.isRequired,
rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
isSelected: PropTypes.bool,
@@ -244,8 +290,4 @@ InteractiveImportRow.propTypes = {
onValidRowChange: PropTypes.func.isRequired
};
-InteractiveImportRow.defaultProps = {
- episodes: []
-};
-
export default InteractiveImportRow;
diff --git a/frontend/src/InteractiveImport/Language/SelectLanguageModal.js b/frontend/src/InteractiveImport/Language/SelectLanguageModal.js
new file mode 100644
index 000000000..938d26a6d
--- /dev/null
+++ b/frontend/src/InteractiveImport/Language/SelectLanguageModal.js
@@ -0,0 +1,37 @@
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import Modal from 'Components/Modal/Modal';
+import SelectLanguageModalContentConnector from './SelectLanguageModalContentConnector';
+
+class SelectLanguageModal extends Component {
+
+ //
+ // Render
+
+ render() {
+ const {
+ isOpen,
+ onModalClose,
+ ...otherProps
+ } = this.props;
+
+ return (
+
+
+
+ );
+ }
+}
+
+SelectLanguageModal.propTypes = {
+ isOpen: PropTypes.bool.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default SelectLanguageModal;
diff --git a/frontend/src/InteractiveImport/Language/SelectLanguageModalContent.js b/frontend/src/InteractiveImport/Language/SelectLanguageModalContent.js
new file mode 100644
index 000000000..2f16d417b
--- /dev/null
+++ b/frontend/src/InteractiveImport/Language/SelectLanguageModalContent.js
@@ -0,0 +1,87 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { inputTypes } from 'Helpers/Props';
+import Button from 'Components/Link/Button';
+import LoadingIndicator from 'Components/Loading/LoadingIndicator';
+import Form from 'Components/Form/Form';
+import FormGroup from 'Components/Form/FormGroup';
+import FormLabel from 'Components/Form/FormLabel';
+import FormInputGroup from 'Components/Form/FormInputGroup';
+import ModalContent from 'Components/Modal/ModalContent';
+import ModalHeader from 'Components/Modal/ModalHeader';
+import ModalBody from 'Components/Modal/ModalBody';
+import ModalFooter from 'Components/Modal/ModalFooter';
+
+function SelectLanguageModalContent(props) {
+ const {
+ languageId,
+ isFetching,
+ isPopulated,
+ error,
+ items,
+ onModalClose,
+ onLanguageSelect
+ } = props;
+
+ const languageOptions = items.map(( language ) => {
+ return {
+ key: language.id,
+ value: language.name
+ };
+ });
+
+ return (
+
+
+ Manual Import - Select Language
+
+
+
+ {
+ isFetching &&
+
+ }
+
+ {
+ !isFetching && !!error &&
+ Unable to load languages
+ }
+
+ {
+ isPopulated && !error &&
+
+ }
+
+
+
+
+
+
+ );
+}
+
+SelectLanguageModalContent.propTypes = {
+ languageId: PropTypes.number.isRequired,
+ isFetching: PropTypes.bool.isRequired,
+ isPopulated: PropTypes.bool.isRequired,
+ error: PropTypes.object,
+ items: PropTypes.arrayOf(PropTypes.object).isRequired,
+ onLanguageSelect: PropTypes.func.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default SelectLanguageModalContent;
diff --git a/frontend/src/InteractiveImport/Language/SelectLanguageModalContentConnector.js b/frontend/src/InteractiveImport/Language/SelectLanguageModalContentConnector.js
new file mode 100644
index 000000000..846c6acc9
--- /dev/null
+++ b/frontend/src/InteractiveImport/Language/SelectLanguageModalContentConnector.js
@@ -0,0 +1,88 @@
+import _ from 'lodash';
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { createSelector } from 'reselect';
+import { fetchLanguages } from 'Store/Actions/settingsActions';
+import { updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
+import SelectLanguageModalContent from './SelectLanguageModalContent';
+
+function createMapStateToProps() {
+ return createSelector(
+ (state) => state.settings.languages,
+ (languages) => {
+ const {
+ isFetching,
+ isPopulated,
+ error,
+ items
+ } = languages;
+
+ return {
+ isFetching,
+ isPopulated,
+ error,
+ items
+ };
+ }
+ );
+}
+
+const mapDispatchToProps = {
+ dispatchFetchLanguages: fetchLanguages,
+ dispatchUpdateInteractiveImportItems: updateInteractiveImportItems
+};
+
+class SelectLanguageModalContentConnector extends Component {
+
+ //
+ // Lifecycle
+
+ componentDidMount = () => {
+ if (!this.props.isPopulated) {
+ this.props.dispatchFetchLanguages();
+ }
+ }
+
+ //
+ // Listeners
+
+ onLanguageSelect = ({ value }) => {
+ const languageId = parseInt(value);
+
+ const language = _.find(this.props.items,
+ (item) => item.id === languageId);
+
+ this.props.dispatchUpdateInteractiveImportItems({
+ ids: this.props.ids,
+ language
+ });
+
+ this.props.onModalClose(true);
+ }
+
+ //
+ // Render
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+SelectLanguageModalContentConnector.propTypes = {
+ ids: PropTypes.arrayOf(PropTypes.number).isRequired,
+ isFetching: PropTypes.bool.isRequired,
+ isPopulated: PropTypes.bool.isRequired,
+ error: PropTypes.object,
+ items: PropTypes.arrayOf(PropTypes.object).isRequired,
+ dispatchFetchLanguages: PropTypes.func.isRequired,
+ dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default connect(createMapStateToProps, mapDispatchToProps)(SelectLanguageModalContentConnector);
diff --git a/frontend/src/InteractiveImport/Series/SelectSeriesModal.js b/frontend/src/InteractiveImport/Movie/SelectMovieModal.js
similarity index 68%
rename from frontend/src/InteractiveImport/Series/SelectSeriesModal.js
rename to frontend/src/InteractiveImport/Movie/SelectMovieModal.js
index 1a1ceffca..a2ef71718 100644
--- a/frontend/src/InteractiveImport/Series/SelectSeriesModal.js
+++ b/frontend/src/InteractiveImport/Movie/SelectMovieModal.js
@@ -1,9 +1,9 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Modal from 'Components/Modal/Modal';
-import SelectSeriesModalContentConnector from './SelectSeriesModalContentConnector';
+import SelectMovieModalContentConnector from './SelectMovieModalContentConnector';
-class SelectSeriesModal extends Component {
+class SelectMovieModal extends Component {
//
// Render
@@ -20,7 +20,7 @@ class SelectSeriesModal extends Component {
isOpen={isOpen}
onModalClose={onModalClose}
>
-
@@ -29,9 +29,9 @@ class SelectSeriesModal extends Component {
}
}
-SelectSeriesModal.propTypes = {
+SelectMovieModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired
};
-export default SelectSeriesModal;
+export default SelectMovieModal;
diff --git a/frontend/src/InteractiveImport/Series/SelectSeriesModalContent.css b/frontend/src/InteractiveImport/Movie/SelectMovieModalContent.css
similarity index 100%
rename from frontend/src/InteractiveImport/Series/SelectSeriesModalContent.css
rename to frontend/src/InteractiveImport/Movie/SelectMovieModalContent.css
diff --git a/frontend/src/InteractiveImport/Series/SelectSeriesModalContent.js b/frontend/src/InteractiveImport/Movie/SelectMovieModalContent.js
similarity index 85%
rename from frontend/src/InteractiveImport/Series/SelectSeriesModalContent.js
rename to frontend/src/InteractiveImport/Movie/SelectMovieModalContent.js
index 6c4c75255..2bae22307 100644
--- a/frontend/src/InteractiveImport/Series/SelectSeriesModalContent.js
+++ b/frontend/src/InteractiveImport/Movie/SelectMovieModalContent.js
@@ -8,10 +8,10 @@ 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 SelectSeriesRow from './SelectSeriesRow';
-import styles from './SelectSeriesModalContent.css';
+import SelectMovieRow from './SelectMovieRow';
+import styles from './SelectMovieModalContent.css';
-class SelectSeriesModalContent extends Component {
+class SelectMovieModalContent extends Component {
//
// Lifecycle
@@ -46,7 +46,7 @@ class SelectSeriesModalContent extends Component {
return (
- Manual Import - Select Series
+ Manual Import - Select Movie
{
return item.title.toLowerCase().includes(filter) ?
(
- {
- const series = _.find(this.props.items, { id: seriesId });
+ onMovieSelect = (movieId) => {
+ const {
+ ids,
+ items,
+ dispatchUpdateInteractiveImportItem,
+ onModalClose
+ } = this.props;
- this.props.ids.forEach((id) => {
- this.props.updateInteractiveImportItem({
+ const movie = items.find((s) => s.id === movieId);
+
+ ids.forEach((id) => {
+ dispatchUpdateInteractiveImportItem({
id,
- series,
- seasonNumber: undefined,
- episodes: []
+ movie
});
});
- this.props.onModalClose(true);
+ onModalClose(true);
}
//
@@ -57,7 +61,7 @@ class SelectSeriesModalContentConnector extends Component {
render() {
return (
-
@@ -65,11 +69,11 @@ class SelectSeriesModalContentConnector extends Component {
}
}
-SelectSeriesModalContentConnector.propTypes = {
+SelectMovieModalContentConnector.propTypes = {
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
- updateInteractiveImportItem: PropTypes.func.isRequired,
+ dispatchUpdateInteractiveImportItem: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
-export default connect(createMapStateToProps, mapDispatchToProps)(SelectSeriesModalContentConnector);
+export default connect(createMapStateToProps, mapDispatchToProps)(SelectMovieModalContentConnector);
diff --git a/frontend/src/InteractiveImport/Series/SelectSeriesRow.css b/frontend/src/InteractiveImport/Movie/SelectMovieRow.css
similarity index 85%
rename from frontend/src/InteractiveImport/Series/SelectSeriesRow.css
rename to frontend/src/InteractiveImport/Movie/SelectMovieRow.css
index f2573d585..f84778bb5 100644
--- a/frontend/src/InteractiveImport/Series/SelectSeriesRow.css
+++ b/frontend/src/InteractiveImport/Movie/SelectMovieRow.css
@@ -1,4 +1,4 @@
-.series {
+.movie {
padding: 8px;
border-bottom: 1px solid $borderColor;
}
diff --git a/frontend/src/InteractiveImport/Series/SelectSeriesRow.js b/frontend/src/InteractiveImport/Movie/SelectMovieRow.js
similarity index 73%
rename from frontend/src/InteractiveImport/Series/SelectSeriesRow.js
rename to frontend/src/InteractiveImport/Movie/SelectMovieRow.js
index 48ba77094..b48ec7992 100644
--- a/frontend/src/InteractiveImport/Series/SelectSeriesRow.js
+++ b/frontend/src/InteractiveImport/Movie/SelectMovieRow.js
@@ -1,9 +1,9 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Link from 'Components/Link/Link';
-import styles from './SelectSeriesRow.css';
+import styles from './SelectMovieRow.css';
-class SelectSeriesRow extends Component {
+class SelectMovieRow extends Component {
//
// Listeners
@@ -18,7 +18,7 @@ class SelectSeriesRow extends Component {
render() {
return (
@@ -28,10 +28,10 @@ class SelectSeriesRow extends Component {
}
}
-SelectSeriesRow.propTypes = {
+SelectMovieRow.propTypes = {
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
onMovieSelect: PropTypes.func.isRequired
};
-export default SelectSeriesRow;
+export default SelectMovieRow;
diff --git a/frontend/src/InteractiveImport/Quality/SelectQualityModalContentConnector.js b/frontend/src/InteractiveImport/Quality/SelectQualityModalContentConnector.js
index 20a49c768..1cf55cde6 100644
--- a/frontend/src/InteractiveImport/Quality/SelectQualityModalContentConnector.js
+++ b/frontend/src/InteractiveImport/Quality/SelectQualityModalContentConnector.js
@@ -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
};
diff --git a/frontend/src/Movie/Index/MovieIndex.js b/frontend/src/Movie/Index/MovieIndex.js
index 7e82c807e..7e714db51 100644
--- a/frontend/src/Movie/Index/MovieIndex.js
+++ b/frontend/src/Movie/Index/MovieIndex.js
@@ -21,6 +21,7 @@ import MovieIndexFilterMenu from './Menus/MovieIndexFilterMenu';
import MovieIndexSortMenu from './Menus/MovieIndexSortMenu';
import MovieIndexViewMenu from './Menus/MovieIndexViewMenu';
import MovieIndexFooterConnector from './MovieIndexFooterConnector';
+import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
import styles from './MovieIndex.css';
function getViewComponent(view) {
@@ -49,6 +50,7 @@ class MovieIndex extends Component {
jumpToCharacter: null,
isPosterOptionsModalOpen: false,
isOverviewOptionsModalOpen: false,
+ isInteractiveImportModalOpen: false,
isRendered: false
};
}
@@ -137,6 +139,14 @@ class MovieIndex extends Component {
this.setState({ isOverviewOptionsModalOpen: false });
}
+ onInteractiveImportPress = () => {
+ this.setState({ isInteractiveImportModalOpen: true });
+ }
+
+ onInteractiveImportModalClose = () => {
+ this.setState({ isInteractiveImportModalOpen: false });
+ }
+
onJumpBarItemPress = (jumpToCharacter) => {
this.setState({ jumpToCharacter });
}
@@ -195,6 +205,7 @@ class MovieIndex extends Component {
jumpToCharacter,
isPosterOptionsModalOpen,
isOverviewOptionsModalOpen,
+ isInteractiveImportModalOpen,
isRendered
} = this.state;
@@ -223,6 +234,28 @@ class MovieIndex extends Component {
onPress={onRssSyncPress}
/>
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/frontend/src/Movie/Index/Table/MovieIndexRow.css b/frontend/src/Movie/Index/Table/MovieIndexRow.css
index 571bb65e5..a75849059 100644
--- a/frontend/src/Movie/Index/Table/MovieIndexRow.css
+++ b/frontend/src/Movie/Index/Table/MovieIndexRow.css
@@ -24,6 +24,7 @@
.added,
.inCinemas,
+.physicalRelease,
.genres {
composes: cell from '~Components/Table/Cells/VirtualTableRowCell.css';
diff --git a/frontend/src/Settings/Profiles/Quality/EditQualityProfileModalContent.js b/frontend/src/Settings/Profiles/Quality/EditQualityProfileModalContent.js
index e390f251d..9b2d4b6d1 100644
--- a/frontend/src/Settings/Profiles/Quality/EditQualityProfileModalContent.js
+++ b/frontend/src/Settings/Profiles/Quality/EditQualityProfileModalContent.js
@@ -92,10 +92,12 @@ class EditQualityProfileModalContent extends Component {
isSaving,
saveError,
qualities,
+ languages,
item,
isInUse,
onInputChange,
onCutoffChange,
+ onLanguageChange,
onSavePress,
onModalClose,
onDeleteQualityProfilePress,
@@ -105,10 +107,14 @@ class EditQualityProfileModalContent extends Component {
const {
id,
name,
+ upgradeAllowed,
cutoff,
+ language,
items
} = item;
+ const languageId = language.value.id;
+
return (
- Cutoff
+ Upgrades Allowed
+
+
+
+
+
+ {
+ upgradeAllowed.value &&
+
+
+ Upgrade Until
+
+
+
+
+ }
+
+
+
+ Language
@@ -197,10 +235,10 @@ class EditQualityProfileModalContent extends Component {
>