-
+
+ 0 ? this.onEditImportListPress : this.onAddImportListPress}
+ />
+
{
+ return {
+ importList
+ };
+ }
+ );
}
const mapDispatchToProps = {
@@ -20,7 +28,7 @@ class MovieCreditPosterConnector extends Component {
// Listeners
onImportListSelect = () => {
- this.props.selectImportListSchema({ implementation: 'TMDbPersonImport', presetName: undefined });
+ this.props.selectImportListSchema({ implementation: 'TMDbPersonImport', implementationName: 'TMDb Person', presetName: undefined });
this.props.setImportListFieldValue({ name: 'personId', value: this.props.tmdbId.toString() });
this.props.setImportListValue({ name: 'name', value: `${this.props.personName} - ${this.props.tmdbId}` });
};
diff --git a/frontend/src/Movie/Details/Credits/MovieCreditPosters.css b/frontend/src/Movie/Details/Credits/MovieCreditPosters.css
index d80f951a0d..2bd05a5e01 100644
--- a/frontend/src/Movie/Details/Credits/MovieCreditPosters.css
+++ b/frontend/src/Movie/Details/Credits/MovieCreditPosters.css
@@ -2,6 +2,10 @@
flex: 1 0 auto;
}
+.movie {
+ padding: 10px;
+}
+
.container {
padding: 10px;
}
diff --git a/frontend/src/Movie/Details/Credits/MovieCreditPosters.css.d.ts b/frontend/src/Movie/Details/Credits/MovieCreditPosters.css.d.ts
index bf4143415a..339e1f48d4 100644
--- a/frontend/src/Movie/Details/Credits/MovieCreditPosters.css.d.ts
+++ b/frontend/src/Movie/Details/Credits/MovieCreditPosters.css.d.ts
@@ -3,6 +3,7 @@
interface CssExports {
'container': string;
'grid': string;
+ 'movie': string;
}
export const cssExports: CssExports;
export default cssExports;
diff --git a/frontend/src/Movie/Details/Credits/MovieCreditPosters.js b/frontend/src/Movie/Details/Credits/MovieCreditPosters.js
index 7815da3ca9..b0e5c83e76 100644
--- a/frontend/src/Movie/Details/Credits/MovieCreditPosters.js
+++ b/frontend/src/Movie/Details/Credits/MovieCreditPosters.js
@@ -1,12 +1,15 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
-import { Grid, WindowScroller } from 'react-virtualized';
-import Measure from 'Components/Measure';
+import { Navigation } from 'swiper';
+import { Swiper, SwiperSlide } from 'swiper/react';
import dimensions from 'Styles/Variables/dimensions';
-import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
import MovieCreditPosterConnector from './MovieCreditPosterConnector';
import styles from './MovieCreditPosters.css';
+// Import Swiper styles
+import 'swiper/css';
+import 'swiper/css/navigation';
+
// Poster container dimensions
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
@@ -65,39 +68,11 @@ class MovieCreditPosters extends Component {
};
this._isInitialized = false;
- this._grid = null;
- }
-
- componentDidUpdate(prevProps, prevState) {
- const {
- items
- } = this.props;
-
- const {
- width,
- columnWidth,
- columnCount,
- rowHeight
- } = this.state;
-
- if (this._grid &&
- (prevState.width !== width ||
- prevState.columnWidth !== columnWidth ||
- prevState.columnCount !== columnCount ||
- prevState.rowHeight !== rowHeight ||
- hasDifferentItemsOrOrder(prevProps.items, items))) {
- // recomputeGridSize also forces Grid to discard its cache of rendered cells
- this._grid.recomputeGridSize();
- }
}
//
// Control
- setGridRef = (ref) => {
- this._grid = ref;
- };
-
calculateGrid = (width = this.state.width, isSmallScreen) => {
const padding = isSmallScreen ? columnPaddingSmallScreen : columnPadding;
@@ -117,7 +92,10 @@ class MovieCreditPosters extends Component {
});
};
- cellRenderer = ({ key, rowIndex, columnIndex, style }) => {
+ //
+ // Render
+
+ render() {
const {
items,
itemComponent
@@ -126,99 +104,44 @@ class MovieCreditPosters extends Component {
const {
posterWidth,
posterHeight,
- columnCount
- } = this.state;
-
- const movieIdx = rowIndex * columnCount + columnIndex;
- const movie = items[movieIdx];
-
- if (!movie) {
- return null;
- }
-
- return (
-
-
-
- );
- };
-
- //
- // Listeners
-
- onMeasure = ({ width }) => {
- this.calculateGrid(width, this.props.isSmallScreen);
- };
-
- //
- // Render
-
- render() {
- const {
- items
- } = this.props;
-
- const {
- width,
- columnWidth,
- columnCount,
rowHeight
} = this.state;
- const rowCount = Math.ceil(items.length / columnCount);
-
return (
-
-
- {({ height, registerChild, onChildScroll, scrollTop }) => {
- if (!height) {
- return ;
- }
- return (
-
-
-
- );
- }
- }
-
-
+
+ {
+ swiper.params.navigation.prevEl = this._swiperPrevRef;
+ swiper.params.navigation.nextEl = this._swiperNextRef;
+ swiper.navigation.init();
+ swiper.navigation.update();
+ }}
+ >
+ {items.map((credit) => (
+
+
+
+ ))}
+
+
);
}
}
diff --git a/frontend/src/Movie/Details/MovieAlternateTitles.css b/frontend/src/Movie/Details/MovieAlternateTitles.css
deleted file mode 100644
index 1af1ae68b0..0000000000
--- a/frontend/src/Movie/Details/MovieAlternateTitles.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.alternateTitle {
- white-space: nowrap;
-}
diff --git a/frontend/src/Movie/Details/MovieAlternateTitles.js b/frontend/src/Movie/Details/MovieAlternateTitles.js
deleted file mode 100644
index 5b0fdaeaab..0000000000
--- a/frontend/src/Movie/Details/MovieAlternateTitles.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import styles from './MovieAlternateTitles.css';
-
-function MovieAlternateTitles({ alternateTitles }) {
- return (
-
- {
- alternateTitles.filter((x, i, a) => a.indexOf(x) === i).map((alternateTitle) => {
- return (
- -
- {alternateTitle}
-
- );
- })
- }
-
- );
-}
-
-MovieAlternateTitles.propTypes = {
- alternateTitles: PropTypes.arrayOf(PropTypes.string).isRequired
-};
-
-export default MovieAlternateTitles;
diff --git a/frontend/src/Movie/Details/MovieDetails.css b/frontend/src/Movie/Details/MovieDetails.css
index 3bcf0e2629..60be33a4c4 100644
--- a/frontend/src/Movie/Details/MovieDetails.css
+++ b/frontend/src/Movie/Details/MovieDetails.css
@@ -5,7 +5,7 @@
.header {
position: relative;
width: 100%;
- height: 375px;
+ height: 425px;
}
.errorMessage {
@@ -39,10 +39,11 @@
}
.poster {
+ z-index: 2;
flex-shrink: 0;
margin-right: 35px;
- width: 217px;
- height: 319px;
+ width: 250px;
+ height: 368px;
}
.info {
diff --git a/frontend/src/Movie/Details/MovieDetails.js b/frontend/src/Movie/Details/MovieDetails.js
index 730e581f7a..db4abf4dd0 100644
--- a/frontend/src/Movie/Details/MovieDetails.js
+++ b/frontend/src/Movie/Details/MovieDetails.js
@@ -1,9 +1,9 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
-import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import TextTruncate from 'react-text-truncate';
import Alert from 'Components/Alert';
+import FieldSet from 'Components/FieldSet';
import Icon from 'Components/Icon';
import ImdbRating from 'Components/ImdbRating';
import InfoLabel from 'Components/InfoLabel';
@@ -25,7 +25,7 @@ import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
import DeleteMovieModal from 'Movie/Delete/DeleteMovieModal';
import EditMovieModalConnector from 'Movie/Edit/EditMovieModalConnector';
-import MovieHistoryTable from 'Movie/History/MovieHistoryTable';
+import MovieHistoryModal from 'Movie/History/MovieHistoryModal';
import MoviePoster from 'Movie/MoviePoster';
import MovieInteractiveSearchModalConnector from 'Movie/Search/MovieInteractiveSearchModalConnector';
import MovieFileEditorTable from 'MovieFile/Editor/MovieFileEditorTable';
@@ -78,10 +78,10 @@ class MovieDetails extends Component {
isDeleteMovieModalOpen: false,
isInteractiveImportModalOpen: false,
isInteractiveSearchModalOpen: false,
+ isMovieHistoryModalOpen: false,
allExpanded: false,
allCollapsed: false,
expandedState: {},
- selectedTabIndex: 0,
overviewHeight: 0,
titleWidth: 0
};
@@ -153,6 +153,14 @@ class MovieDetails extends Component {
this.setState({ isDeleteMovieModalOpen: false });
};
+ onMovieHistoryPress = () => {
+ this.setState({ isMovieHistoryModalOpen: true });
+ };
+
+ onMovieHistoryModalClose = () => {
+ this.setState({ isMovieHistoryModalOpen: false });
+ };
+
onExpandAllPress = () => {
const {
allExpanded,
@@ -304,9 +312,9 @@ class MovieDetails extends Component {
isDeleteMovieModalOpen,
isInteractiveImportModalOpen,
isInteractiveSearchModalOpen,
+ isMovieHistoryModalOpen,
overviewHeight,
- titleWidth,
- selectedTabIndex
+ titleWidth
} = this.state;
const fanartUrl = getFanartUrl(images);
@@ -356,6 +364,13 @@ class MovieDetails extends Component {
onPress={this.onInteractiveImportPress}
/>
+
+
-
-
- {translate('History')}
-
+
-
- {translate('Titles')}
-
+
-
- {translate('Cast')}
-
-
-
- {translate('Crew')}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
);
}
diff --git a/frontend/src/Movie/History/MovieHistoryModal.js b/frontend/src/Movie/History/MovieHistoryModal.js
new file mode 100644
index 0000000000..86c8bda731
--- /dev/null
+++ b/frontend/src/Movie/History/MovieHistoryModal.js
@@ -0,0 +1,33 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import Modal from 'Components/Modal/Modal';
+import { sizes } from 'Helpers/Props';
+import MovieHistoryModalContentConnector from './MovieHistoryModalContentConnector';
+
+function MovieHistoryModal(props) {
+ const {
+ isOpen,
+ onModalClose,
+ ...otherProps
+ } = props;
+
+ return (
+
+
+
+ );
+}
+
+MovieHistoryModal.propTypes = {
+ isOpen: PropTypes.bool.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default MovieHistoryModal;
diff --git a/frontend/src/Movie/History/MovieHistoryTableContent.css b/frontend/src/Movie/History/MovieHistoryModalContent.css
similarity index 100%
rename from frontend/src/Movie/History/MovieHistoryTableContent.css
rename to frontend/src/Movie/History/MovieHistoryModalContent.css
diff --git a/frontend/src/Movie/History/MovieHistoryTableContent.css.d.ts b/frontend/src/Movie/History/MovieHistoryModalContent.css.d.ts
similarity index 100%
rename from frontend/src/Movie/History/MovieHistoryTableContent.css.d.ts
rename to frontend/src/Movie/History/MovieHistoryModalContent.css.d.ts
diff --git a/frontend/src/Movie/History/MovieHistoryModalContent.js b/frontend/src/Movie/History/MovieHistoryModalContent.js
new file mode 100644
index 0000000000..d1d1ee5100
--- /dev/null
+++ b/frontend/src/Movie/History/MovieHistoryModalContent.js
@@ -0,0 +1,142 @@
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import Alert from 'Components/Alert';
+import Icon from 'Components/Icon';
+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 Table from 'Components/Table/Table';
+import TableBody from 'Components/Table/TableBody';
+import { icons, kinds } from 'Helpers/Props';
+import translate from 'Utilities/String/translate';
+import MovieHistoryRowConnector from './MovieHistoryRowConnector';
+
+const columns = [
+ {
+ name: 'eventType',
+ isVisible: true
+ },
+ {
+ name: 'sourceTitle',
+ label: () => translate('SourceTitle'),
+ isVisible: true
+ },
+ {
+ name: 'languages',
+ label: () => translate('Languages'),
+ isVisible: true
+ },
+ {
+ name: 'quality',
+ label: () => translate('Quality'),
+ isVisible: true
+ },
+ {
+ name: 'customFormats',
+ label: () => translate('CustomFormats'),
+ isSortable: false,
+ isVisible: true
+ },
+ {
+ name: 'customFormatScore',
+ label: React.createElement(Icon, {
+ name: icons.SCORE,
+ title: 'Custom format score'
+ }),
+ isSortable: true,
+ isVisible: true
+ },
+ {
+ name: 'date',
+ label: () => translate('Date'),
+ isVisible: true
+ },
+ {
+ name: 'actions',
+ label: () => translate('Actions'),
+ isVisible: true
+ }
+];
+
+class MovieHistoryModalContent extends Component {
+
+ //
+ // Render
+
+ render() {
+ const {
+ isFetching,
+ isPopulated,
+ error,
+ items,
+ onMarkAsFailedPress,
+ onModalClose
+ } = this.props;
+
+ const hasItems = !!items.length;
+
+ return (
+
+
+ {translate('History')}
+
+
+
+ {
+ isFetching &&
+
+ }
+
+ {
+ !isFetching && !!error &&
+ {translate('HistoryLoadError')}
+ }
+
+ {
+ isPopulated && !hasItems && !error &&
+ {translate('NoHistory')}
+ }
+
+ {
+ isPopulated && hasItems && !error &&
+
+
+ {
+ items.map((item) => {
+ return (
+
+ );
+ })
+ }
+
+
+ }
+
+
+
+
+
+
+ );
+ }
+}
+
+MovieHistoryModalContent.propTypes = {
+ isFetching: PropTypes.bool.isRequired,
+ isPopulated: PropTypes.bool.isRequired,
+ error: PropTypes.object,
+ items: PropTypes.arrayOf(PropTypes.object).isRequired,
+ onMarkAsFailedPress: PropTypes.func.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default MovieHistoryModalContent;
diff --git a/frontend/src/Movie/History/MovieHistoryTableContentConnector.js b/frontend/src/Movie/History/MovieHistoryModalContentConnector.js
similarity index 55%
rename from frontend/src/Movie/History/MovieHistoryTableContentConnector.js
rename to frontend/src/Movie/History/MovieHistoryModalContentConnector.js
index 76802e0687..ee2dbe20ec 100644
--- a/frontend/src/Movie/History/MovieHistoryTableContentConnector.js
+++ b/frontend/src/Movie/History/MovieHistoryModalContentConnector.js
@@ -2,8 +2,8 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
-import { movieHistoryMarkAsFailed } from 'Store/Actions/movieHistoryActions';
-import MovieHistoryTableContent from './MovieHistoryTableContent';
+import { clearMovieHistory, fetchMovieHistory, movieHistoryMarkAsFailed } from 'Store/Actions/movieHistoryActions';
+import MovieHistoryModalContent from './MovieHistoryModalContent';
function createMapStateToProps() {
return createSelector(
@@ -15,10 +15,29 @@ function createMapStateToProps() {
}
const mapDispatchToProps = {
+ fetchMovieHistory,
+ clearMovieHistory,
movieHistoryMarkAsFailed
};
-class MovieHistoryTableContentConnector extends Component {
+class MovieHistoryModalContentConnector extends Component {
+
+ //
+ // Lifecycle
+
+ componentDidMount() {
+ const {
+ movieId
+ } = this.props;
+
+ this.props.fetchMovieHistory({
+ movieId
+ });
+ }
+
+ componentWillUnmount() {
+ this.props.clearMovieHistory();
+ }
//
// Listeners
@@ -39,7 +58,7 @@ class MovieHistoryTableContentConnector extends Component {
render() {
return (
-
@@ -47,9 +66,11 @@ class MovieHistoryTableContentConnector extends Component {
}
}
-MovieHistoryTableContentConnector.propTypes = {
+MovieHistoryModalContentConnector.propTypes = {
movieId: PropTypes.number.isRequired,
+ fetchMovieHistory: PropTypes.func.isRequired,
+ clearMovieHistory: PropTypes.func.isRequired,
movieHistoryMarkAsFailed: PropTypes.func.isRequired
};
-export default connect(createMapStateToProps, mapDispatchToProps)(MovieHistoryTableContentConnector);
+export default connect(createMapStateToProps, mapDispatchToProps)(MovieHistoryModalContentConnector);
diff --git a/frontend/src/Movie/History/MovieHistoryTable.js b/frontend/src/Movie/History/MovieHistoryTable.js
deleted file mode 100644
index e07bfa5619..0000000000
--- a/frontend/src/Movie/History/MovieHistoryTable.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import MovieHistoryTableContentConnector from './MovieHistoryTableContentConnector';
-import styles from './MovieHistoryTable.css';
-
-function MovieHistoryTable(props) {
- const {
- ...otherProps
- } = props;
-
- return (
-
-
-
- );
-}
-
-MovieHistoryTable.propTypes = {
-};
-
-export default MovieHistoryTable;
diff --git a/frontend/src/Movie/History/MovieHistoryTableContent.js b/frontend/src/Movie/History/MovieHistoryTableContent.js
deleted file mode 100644
index 8b8a3cb6b8..0000000000
--- a/frontend/src/Movie/History/MovieHistoryTableContent.js
+++ /dev/null
@@ -1,128 +0,0 @@
-import PropTypes from 'prop-types';
-import React, { Component } from 'react';
-import Icon from 'Components/Icon';
-import IconButton from 'Components/Link/IconButton';
-import LoadingIndicator from 'Components/Loading/LoadingIndicator';
-import Table from 'Components/Table/Table';
-import TableBody from 'Components/Table/TableBody';
-import { icons } from 'Helpers/Props';
-import translate from 'Utilities/String/translate';
-import MovieHistoryRowConnector from './MovieHistoryRowConnector';
-import styles from './MovieHistoryTableContent.css';
-
-const columns = [
- {
- name: 'eventType',
- isVisible: true
- },
- {
- name: 'sourceTitle',
- label: () => translate('SourceTitle'),
- isVisible: true
- },
- {
- name: 'languages',
- label: () => translate('Languages'),
- isVisible: true
- },
- {
- name: 'quality',
- label: () => translate('Quality'),
- isVisible: true
- },
- {
- name: 'customFormats',
- label: () => translate('CustomFormats'),
- isSortable: false,
- isVisible: true
- },
- {
- name: 'customFormatScore',
- label: React.createElement(Icon, {
- name: icons.SCORE,
- title: 'Custom format score'
- }),
- isSortable: true,
- isVisible: true
- },
- {
- name: 'date',
- label: () => translate('Date'),
- isVisible: true
- },
- {
- name: 'actions',
- label: React.createElement(IconButton, { name: icons.ADVANCED_SETTINGS }),
- isVisible: true
- }
-];
-
-class MovieHistoryTableContent extends Component {
-
- //
- // Render
-
- render() {
- const {
- isFetching,
- isPopulated,
- error,
- items,
- onMarkAsFailedPress
- } = this.props;
-
- const hasItems = !!items.length;
-
- return (
-
- {
- isFetching &&
-
- }
-
- {
- !isFetching && !!error &&
-
- {translate('UnableToLoadHistory')}
-
- }
-
- {
- isPopulated && !hasItems && !error &&
-
- {translate('NoHistory')}
-
- }
-
- {
- isPopulated && hasItems && !error &&
-
-
- {
- items.map((item) => {
- return (
-
- );
- })
- }
-
-
- }
-
- );
- }
-}
-
-MovieHistoryTableContent.propTypes = {
- isFetching: PropTypes.bool.isRequired,
- isPopulated: PropTypes.bool.isRequired,
- error: PropTypes.object,
- items: PropTypes.arrayOf(PropTypes.object).isRequired,
- onMarkAsFailedPress: PropTypes.func.isRequired
-};
-
-export default MovieHistoryTableContent;
diff --git a/frontend/src/Movie/Search/MovieInteractiveSearchModal.js b/frontend/src/Movie/Search/MovieInteractiveSearchModal.js
index ec8987dfa4..071c2b67dd 100644
--- a/frontend/src/Movie/Search/MovieInteractiveSearchModal.js
+++ b/frontend/src/Movie/Search/MovieInteractiveSearchModal.js
@@ -16,7 +16,7 @@ function MovieInteractiveSearchModal(props) {
isOpen={isOpen}
closeOnBackgroundClick={false}
onModalClose={onModalClose}
- size={sizes.EXTRA_LARGE}
+ size={sizes.EXTRA_EXTRA_LARGE}
>
tmdbId,
- (state) => state.settings.importLists.items,
- (tmdbId, importLists) => {
- const importListIds = _.reduce(importLists, (acc, list) => {
- if (list.implementation === 'TMDbCollectionImport') {
- const collectionIdField = list.fields.find((field) => {
- return field.name === 'collectionId';
- });
-
- if (collectionIdField && parseInt(collectionIdField.value) === tmdbId) {
- acc.push(list);
- return acc;
- }
- }
-
- return acc;
- }, []);
-
- if (importListIds.length === 0) {
- return undefined;
- }
-
- return importListIds[0];
- }
- );
-}
-
-export default createMovieCollectionListSelector;
diff --git a/frontend/src/Store/Selectors/createMovieCreditListSelector.js b/frontend/src/Store/Selectors/createMovieCreditListSelector.js
index 51764b3795..017485b93c 100644
--- a/frontend/src/Store/Selectors/createMovieCreditListSelector.js
+++ b/frontend/src/Store/Selectors/createMovieCreditListSelector.js
@@ -21,15 +21,11 @@ function createMovieCreditListSelector() {
return acc;
}, []);
- let importListId = 0;
-
- if (importListIds.length > 0) {
- importListId = importListIds[0].id;
+ if (importListIds.length === 0) {
+ return undefined;
}
- return {
- importListId
- };
+ return importListIds[0];
}
);
}