mirror of
https://github.com/Radarr/Radarr.git
synced 2024-10-26 22:52:40 +02:00
New: Add and Edit People Lists from Movie Details Page
This commit is contained in:
parent
8021381de2
commit
b3caa87b78
@ -8,7 +8,7 @@ import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
|
|||||||
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
|
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
|
||||||
import { fetchMovies } from 'Store/Actions/movieActions';
|
import { fetchMovies } from 'Store/Actions/movieActions';
|
||||||
import { fetchTags } from 'Store/Actions/tagActions';
|
import { fetchTags } from 'Store/Actions/tagActions';
|
||||||
import { fetchQualityProfiles, fetchUISettings, fetchLanguages } from 'Store/Actions/settingsActions';
|
import { fetchQualityProfiles, fetchUISettings, fetchLanguages, fetchNetImports } from 'Store/Actions/settingsActions';
|
||||||
import { fetchStatus } from 'Store/Actions/systemActions';
|
import { fetchStatus } from 'Store/Actions/systemActions';
|
||||||
import ErrorPage from './ErrorPage';
|
import ErrorPage from './ErrorPage';
|
||||||
import LoadingPage from './LoadingPage';
|
import LoadingPage from './LoadingPage';
|
||||||
@ -48,6 +48,7 @@ const selectIsPopulated = createSelector(
|
|||||||
(state) => state.settings.ui.isPopulated,
|
(state) => state.settings.ui.isPopulated,
|
||||||
(state) => state.settings.qualityProfiles.isPopulated,
|
(state) => state.settings.qualityProfiles.isPopulated,
|
||||||
(state) => state.settings.languages.isPopulated,
|
(state) => state.settings.languages.isPopulated,
|
||||||
|
(state) => state.settings.netImports.isPopulated,
|
||||||
(state) => state.system.status.isPopulated,
|
(state) => state.system.status.isPopulated,
|
||||||
(
|
(
|
||||||
customFiltersIsPopulated,
|
customFiltersIsPopulated,
|
||||||
@ -55,6 +56,7 @@ const selectIsPopulated = createSelector(
|
|||||||
uiSettingsIsPopulated,
|
uiSettingsIsPopulated,
|
||||||
qualityProfilesIsPopulated,
|
qualityProfilesIsPopulated,
|
||||||
languagesIsPopulated,
|
languagesIsPopulated,
|
||||||
|
netImportsIsPopulated,
|
||||||
systemStatusIsPopulated
|
systemStatusIsPopulated
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
@ -63,6 +65,7 @@ const selectIsPopulated = createSelector(
|
|||||||
uiSettingsIsPopulated &&
|
uiSettingsIsPopulated &&
|
||||||
qualityProfilesIsPopulated &&
|
qualityProfilesIsPopulated &&
|
||||||
languagesIsPopulated &&
|
languagesIsPopulated &&
|
||||||
|
netImportsIsPopulated &&
|
||||||
systemStatusIsPopulated
|
systemStatusIsPopulated
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -74,6 +77,7 @@ const selectErrors = createSelector(
|
|||||||
(state) => state.settings.ui.error,
|
(state) => state.settings.ui.error,
|
||||||
(state) => state.settings.qualityProfiles.error,
|
(state) => state.settings.qualityProfiles.error,
|
||||||
(state) => state.settings.languages.error,
|
(state) => state.settings.languages.error,
|
||||||
|
(state) => state.settings.netImports.error,
|
||||||
(state) => state.system.status.error,
|
(state) => state.system.status.error,
|
||||||
(
|
(
|
||||||
customFiltersError,
|
customFiltersError,
|
||||||
@ -81,6 +85,7 @@ const selectErrors = createSelector(
|
|||||||
uiSettingsError,
|
uiSettingsError,
|
||||||
qualityProfilesError,
|
qualityProfilesError,
|
||||||
languagesError,
|
languagesError,
|
||||||
|
netImportsError,
|
||||||
systemStatusError
|
systemStatusError
|
||||||
) => {
|
) => {
|
||||||
const hasError = !!(
|
const hasError = !!(
|
||||||
@ -89,6 +94,7 @@ const selectErrors = createSelector(
|
|||||||
uiSettingsError ||
|
uiSettingsError ||
|
||||||
qualityProfilesError ||
|
qualityProfilesError ||
|
||||||
languagesError ||
|
languagesError ||
|
||||||
|
netImportsError ||
|
||||||
systemStatusError
|
systemStatusError
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -99,6 +105,7 @@ const selectErrors = createSelector(
|
|||||||
uiSettingsError,
|
uiSettingsError,
|
||||||
qualityProfilesError,
|
qualityProfilesError,
|
||||||
languagesError,
|
languagesError,
|
||||||
|
netImportsError,
|
||||||
systemStatusError
|
systemStatusError
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -146,6 +153,9 @@ function createMapDispatchToProps(dispatch, props) {
|
|||||||
dispatchFetchLanguages() {
|
dispatchFetchLanguages() {
|
||||||
dispatch(fetchLanguages());
|
dispatch(fetchLanguages());
|
||||||
},
|
},
|
||||||
|
dispatchFetchNetImports() {
|
||||||
|
dispatch(fetchNetImports());
|
||||||
|
},
|
||||||
dispatchFetchUISettings() {
|
dispatchFetchUISettings() {
|
||||||
dispatch(fetchUISettings());
|
dispatch(fetchUISettings());
|
||||||
},
|
},
|
||||||
@ -181,6 +191,7 @@ class PageConnector extends Component {
|
|||||||
this.props.dispatchFetchTags();
|
this.props.dispatchFetchTags();
|
||||||
this.props.dispatchFetchQualityProfiles();
|
this.props.dispatchFetchQualityProfiles();
|
||||||
this.props.dispatchFetchLanguages();
|
this.props.dispatchFetchLanguages();
|
||||||
|
this.props.dispatchFetchNetImports();
|
||||||
this.props.dispatchFetchUISettings();
|
this.props.dispatchFetchUISettings();
|
||||||
this.props.dispatchFetchStatus();
|
this.props.dispatchFetchStatus();
|
||||||
}
|
}
|
||||||
@ -204,6 +215,7 @@ class PageConnector extends Component {
|
|||||||
dispatchFetchTags,
|
dispatchFetchTags,
|
||||||
dispatchFetchQualityProfiles,
|
dispatchFetchQualityProfiles,
|
||||||
dispatchFetchLanguages,
|
dispatchFetchLanguages,
|
||||||
|
dispatchFetchNetImports,
|
||||||
dispatchFetchUISettings,
|
dispatchFetchUISettings,
|
||||||
dispatchFetchStatus,
|
dispatchFetchStatus,
|
||||||
...otherProps
|
...otherProps
|
||||||
@ -242,6 +254,7 @@ PageConnector.propTypes = {
|
|||||||
dispatchFetchTags: PropTypes.func.isRequired,
|
dispatchFetchTags: PropTypes.func.isRequired,
|
||||||
dispatchFetchQualityProfiles: PropTypes.func.isRequired,
|
dispatchFetchQualityProfiles: PropTypes.func.isRequired,
|
||||||
dispatchFetchLanguages: PropTypes.func.isRequired,
|
dispatchFetchLanguages: PropTypes.func.isRequired,
|
||||||
|
dispatchFetchNetImports: PropTypes.func.isRequired,
|
||||||
dispatchFetchUISettings: PropTypes.func.isRequired,
|
dispatchFetchUISettings: PropTypes.func.isRequired,
|
||||||
dispatchFetchStatus: PropTypes.func.isRequired,
|
dispatchFetchStatus: PropTypes.func.isRequired,
|
||||||
onSidebarVisibleChange: PropTypes.func.isRequired
|
onSidebarVisibleChange: PropTypes.func.isRequired
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import MovieCastPosters from './MovieCastPosters';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.moviePeople.items,
|
|
||||||
(people) => {
|
|
||||||
const cast = _.reduce(people, (acc, person) => {
|
|
||||||
if (person.type === 'cast') {
|
|
||||||
acc.push(person);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
|
||||||
cast
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(MovieCastPosters);
|
|
@ -4,7 +4,8 @@ import { icons } from 'Helpers/Props';
|
|||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
import MovieHeadshot from 'Movie/MovieHeadshot';
|
import MovieHeadshot from 'Movie/MovieHeadshot';
|
||||||
import styles from './MovieCastPoster.css';
|
import EditNetImportModalConnector from 'Settings/NetImport/NetImport/EditNetImportModalConnector';
|
||||||
|
import styles from '../MovieCreditPoster.css';
|
||||||
|
|
||||||
class MovieCastPoster extends Component {
|
class MovieCastPoster extends Component {
|
||||||
|
|
||||||
@ -16,19 +17,24 @@ class MovieCastPoster extends Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
hasPosterError: false,
|
hasPosterError: false,
|
||||||
isEditMovieModalOpen: false
|
isEditNetImportModalOpen: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onEditMoviePress = () => {
|
onEditNetImportPress = () => {
|
||||||
this.setState({ isEditMovieModalOpen: true });
|
this.setState({ isEditNetImportModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onEditMovieModalClose = () => {
|
onAddNetImportPress = () => {
|
||||||
this.setState({ isEditMovieModalOpen: false });
|
this.props.onNetImportSelect();
|
||||||
|
this.setState({ isEditNetImportModalOpen: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditNetImportModalClose = () => {
|
||||||
|
this.setState({ isEditNetImportModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
onPosterLoad = () => {
|
onPosterLoad = () => {
|
||||||
@ -48,11 +54,12 @@ class MovieCastPoster extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
castName,
|
personName,
|
||||||
character,
|
character,
|
||||||
images,
|
images,
|
||||||
posterWidth,
|
posterWidth,
|
||||||
posterHeight
|
posterHeight,
|
||||||
|
netImportId
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -68,12 +75,21 @@ class MovieCastPoster extends Component {
|
|||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.posterContainer}>
|
<div className={styles.posterContainer}>
|
||||||
<Label className={styles.controls}>
|
<Label className={styles.controls}>
|
||||||
<IconButton
|
{
|
||||||
className={styles.action}
|
netImportId > 0 ?
|
||||||
name={icons.EDIT}
|
<IconButton
|
||||||
title="Edit movie"
|
className={styles.action}
|
||||||
onPress={this.onEditMoviePress}
|
name={icons.EDIT}
|
||||||
/>
|
title="Edit Person"
|
||||||
|
onPress={this.onEditNetImportPress}
|
||||||
|
/> :
|
||||||
|
<IconButton
|
||||||
|
className={styles.action}
|
||||||
|
name={icons.ADD}
|
||||||
|
title="Follow Person"
|
||||||
|
onPress={this.onAddNetImportPress}
|
||||||
|
/>
|
||||||
|
}
|
||||||
</Label>
|
</Label>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -94,30 +110,43 @@ class MovieCastPoster extends Component {
|
|||||||
{
|
{
|
||||||
hasPosterError &&
|
hasPosterError &&
|
||||||
<div className={styles.overlayTitle}>
|
<div className={styles.overlayTitle}>
|
||||||
{castName}
|
{personName}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
{castName}
|
{personName}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
{character}
|
{character}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<EditNetImportModalConnector
|
||||||
|
id={netImportId}
|
||||||
|
isOpen={this.state.isEditNetImportModalOpen}
|
||||||
|
onModalClose={this.onEditNetImportModalClose}
|
||||||
|
onDeleteNetImportPress={this.onDeleteNetImportPress}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieCastPoster.propTypes = {
|
MovieCastPoster.propTypes = {
|
||||||
castId: PropTypes.number.isRequired,
|
tmdbId: PropTypes.number.isRequired,
|
||||||
castName: PropTypes.string.isRequired,
|
personName: PropTypes.string.isRequired,
|
||||||
character: PropTypes.string.isRequired,
|
character: PropTypes.string.isRequired,
|
||||||
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
posterWidth: PropTypes.number.isRequired,
|
posterWidth: PropTypes.number.isRequired,
|
||||||
posterHeight: PropTypes.number.isRequired
|
posterHeight: PropTypes.number.isRequired,
|
||||||
|
netImportId: PropTypes.number.isRequired,
|
||||||
|
onNetImportSelect: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
MovieCastPoster.defaultProps = {
|
||||||
|
netImportId: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieCastPoster;
|
export default MovieCastPoster;
|
@ -0,0 +1,60 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import MovieCreditPosters from '../MovieCreditPosters';
|
||||||
|
import MovieCastPoster from './MovieCastPoster';
|
||||||
|
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state) => state.movieCredits.items,
|
||||||
|
(credits) => {
|
||||||
|
const cast = _.reduce(credits, (acc, credit) => {
|
||||||
|
if (credit.type === 'cast') {
|
||||||
|
acc.push(credit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: cast
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
fetchRootFolders
|
||||||
|
};
|
||||||
|
|
||||||
|
class MovieCastPostersConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.fetchRootFolders();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MovieCreditPosters
|
||||||
|
{...this.props}
|
||||||
|
itemComponent={MovieCastPoster}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MovieCastPostersConnector.propTypes = {
|
||||||
|
fetchRootFolders: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, mapDispatchToProps)(MovieCastPostersConnector);
|
@ -4,7 +4,8 @@ import { icons } from 'Helpers/Props';
|
|||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
import MovieHeadshot from 'Movie/MovieHeadshot';
|
import MovieHeadshot from 'Movie/MovieHeadshot';
|
||||||
import styles from './MovieCrewPoster.css';
|
import EditNetImportModalConnector from 'Settings/NetImport/NetImport/EditNetImportModalConnector';
|
||||||
|
import styles from '../MovieCreditPoster.css';
|
||||||
|
|
||||||
class MovieCrewPoster extends Component {
|
class MovieCrewPoster extends Component {
|
||||||
|
|
||||||
@ -16,19 +17,24 @@ class MovieCrewPoster extends Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
hasPosterError: false,
|
hasPosterError: false,
|
||||||
isEditMovieModalOpen: false
|
isEditNetImportModalOpen: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onEditMoviePress = () => {
|
onEditNetImportPress = () => {
|
||||||
this.setState({ isEditMovieModalOpen: true });
|
this.setState({ isEditNetImportModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onEditMovieModalClose = () => {
|
onAddNetImportPress = () => {
|
||||||
this.setState({ isEditMovieModalOpen: false });
|
this.props.onNetImportSelect();
|
||||||
|
this.setState({ isEditNetImportModalOpen: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditNetImportModalClose = () => {
|
||||||
|
this.setState({ isEditNetImportModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
onPosterLoad = () => {
|
onPosterLoad = () => {
|
||||||
@ -48,11 +54,12 @@ class MovieCrewPoster extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
crewName,
|
personName,
|
||||||
job,
|
job,
|
||||||
images,
|
images,
|
||||||
posterWidth,
|
posterWidth,
|
||||||
posterHeight
|
posterHeight,
|
||||||
|
netImportId
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -68,12 +75,21 @@ class MovieCrewPoster extends Component {
|
|||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.posterContainer}>
|
<div className={styles.posterContainer}>
|
||||||
<Label className={styles.controls}>
|
<Label className={styles.controls}>
|
||||||
<IconButton
|
{
|
||||||
className={styles.action}
|
netImportId > 0 ?
|
||||||
name={icons.EDIT}
|
<IconButton
|
||||||
title="Edit movie"
|
className={styles.action}
|
||||||
onPress={this.onEditMoviePress}
|
name={icons.EDIT}
|
||||||
/>
|
title="Edit Person"
|
||||||
|
onPress={this.onEditNetImportPress}
|
||||||
|
/> :
|
||||||
|
<IconButton
|
||||||
|
className={styles.action}
|
||||||
|
name={icons.ADD}
|
||||||
|
title="Follow Person"
|
||||||
|
onPress={this.onAddNetImportPress}
|
||||||
|
/>
|
||||||
|
}
|
||||||
</Label>
|
</Label>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -94,30 +110,43 @@ class MovieCrewPoster extends Component {
|
|||||||
{
|
{
|
||||||
hasPosterError &&
|
hasPosterError &&
|
||||||
<div className={styles.overlayTitle}>
|
<div className={styles.overlayTitle}>
|
||||||
{crewName}
|
{personName}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
{crewName}
|
{personName}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
{job}
|
{job}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<EditNetImportModalConnector
|
||||||
|
id={netImportId}
|
||||||
|
isOpen={this.state.isEditNetImportModalOpen}
|
||||||
|
onModalClose={this.onEditNetImportModalClose}
|
||||||
|
onDeleteNetImportPress={this.onDeleteNetImportPress}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieCrewPoster.propTypes = {
|
MovieCrewPoster.propTypes = {
|
||||||
crewId: PropTypes.number.isRequired,
|
tmdbId: PropTypes.number.isRequired,
|
||||||
crewName: PropTypes.string.isRequired,
|
personName: PropTypes.string.isRequired,
|
||||||
job: PropTypes.string.isRequired,
|
job: PropTypes.string.isRequired,
|
||||||
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
posterWidth: PropTypes.number.isRequired,
|
posterWidth: PropTypes.number.isRequired,
|
||||||
posterHeight: PropTypes.number.isRequired
|
posterHeight: PropTypes.number.isRequired,
|
||||||
|
netImportId: PropTypes.number.isRequired,
|
||||||
|
onNetImportSelect: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
MovieCrewPoster.defaultProps = {
|
||||||
|
netImportId: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieCrewPoster;
|
export default MovieCrewPoster;
|
@ -0,0 +1,60 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import MovieCreditPosters from '../MovieCreditPosters';
|
||||||
|
import MovieCrewPoster from './MovieCrewPoster';
|
||||||
|
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state) => state.movieCredits.items,
|
||||||
|
(credits) => {
|
||||||
|
const crew = _.reduce(credits, (acc, credit) => {
|
||||||
|
if (credit.type === 'crew') {
|
||||||
|
acc.push(credit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: crew
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
fetchRootFolders
|
||||||
|
};
|
||||||
|
|
||||||
|
class MovieCrewPostersConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.fetchRootFolders();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MovieCreditPosters
|
||||||
|
{...this.props}
|
||||||
|
itemComponent={MovieCrewPoster}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MovieCrewPostersConnector.propTypes = {
|
||||||
|
fetchRootFolders: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, mapDispatchToProps)(MovieCrewPostersConnector);
|
@ -0,0 +1,58 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import createMovieCreditListSelector from 'Store/Selectors/createMovieCreditListSelector';
|
||||||
|
import { selectNetImportSchema, setNetImportValue, setNetImportFieldValue } from 'Store/Actions/settingsActions';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createMovieCreditListSelector();
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
selectNetImportSchema,
|
||||||
|
setNetImportFieldValue,
|
||||||
|
setNetImportValue
|
||||||
|
};
|
||||||
|
|
||||||
|
class MovieCreditPosterConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onNetImportSelect = () => {
|
||||||
|
this.props.selectNetImportSchema({ implementation: 'TMDbPersonImport', presetName: undefined });
|
||||||
|
this.props.setNetImportFieldValue({ name: 'personId', value: this.props.tmdbId.toString() });
|
||||||
|
this.props.setNetImportValue({ name: 'name', value: `${this.props.personName} - ${this.props.tmdbId}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
tmdbId,
|
||||||
|
component: ItemComponent,
|
||||||
|
personName
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ItemComponent
|
||||||
|
{...this.props}
|
||||||
|
tmdbId={tmdbId}
|
||||||
|
personName={personName}
|
||||||
|
onNetImportSelect={this.onNetImportSelect}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MovieCreditPosterConnector.propTypes = {
|
||||||
|
tmdbId: PropTypes.number.isRequired,
|
||||||
|
personName: PropTypes.string.isRequired,
|
||||||
|
component: PropTypes.elementType.isRequired,
|
||||||
|
selectNetImportSchema: PropTypes.func.isRequired,
|
||||||
|
setNetImportFieldValue: PropTypes.func.isRequired,
|
||||||
|
setNetImportValue: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, mapDispatchToProps)(MovieCreditPosterConnector);
|
@ -4,8 +4,8 @@ import { Grid, WindowScroller } from 'react-virtualized';
|
|||||||
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
||||||
import dimensions from 'Styles/Variables/dimensions';
|
import dimensions from 'Styles/Variables/dimensions';
|
||||||
import Measure from 'Components/Measure';
|
import Measure from 'Components/Measure';
|
||||||
import MovieCastPoster from './MovieCastPoster';
|
import MovieCreditPosterConnector from './MovieCreditPosterConnector';
|
||||||
import styles from './MovieCastPosters.css';
|
import styles from './MovieCreditPosters.css';
|
||||||
|
|
||||||
// Poster container dimensions
|
// Poster container dimensions
|
||||||
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
|
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
|
||||||
@ -47,7 +47,7 @@ function calculatePosterHeight(posterWidth) {
|
|||||||
return Math.ceil((250 / 170) * posterWidth);
|
return Math.ceil((250 / 170) * posterWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MovieCastPosters extends Component {
|
class MovieCreditPosters extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
@ -70,7 +70,7 @@ class MovieCastPosters extends Component {
|
|||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
const {
|
const {
|
||||||
cast
|
items
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -85,7 +85,7 @@ class MovieCastPosters extends Component {
|
|||||||
prevState.columnWidth !== columnWidth ||
|
prevState.columnWidth !== columnWidth ||
|
||||||
prevState.columnCount !== columnCount ||
|
prevState.columnCount !== columnCount ||
|
||||||
prevState.rowHeight !== rowHeight ||
|
prevState.rowHeight !== rowHeight ||
|
||||||
hasDifferentItemsOrOrder(prevProps.cast, cast))) {
|
hasDifferentItemsOrOrder(prevProps.items, items))) {
|
||||||
// recomputeGridSize also forces Grid to discard its cache of rendered cells
|
// recomputeGridSize also forces Grid to discard its cache of rendered cells
|
||||||
this._grid.recomputeGridSize();
|
this._grid.recomputeGridSize();
|
||||||
}
|
}
|
||||||
@ -119,7 +119,8 @@ class MovieCastPosters extends Component {
|
|||||||
|
|
||||||
cellRenderer = ({ key, rowIndex, columnIndex, style }) => {
|
cellRenderer = ({ key, rowIndex, columnIndex, style }) => {
|
||||||
const {
|
const {
|
||||||
cast
|
items,
|
||||||
|
itemComponent
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -129,7 +130,7 @@ class MovieCastPosters extends Component {
|
|||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const movieIdx = rowIndex * columnCount + columnIndex;
|
const movieIdx = rowIndex * columnCount + columnIndex;
|
||||||
const movie = cast[movieIdx];
|
const movie = items[movieIdx];
|
||||||
|
|
||||||
if (!movie) {
|
if (!movie) {
|
||||||
return null;
|
return null;
|
||||||
@ -141,12 +142,14 @@ class MovieCastPosters extends Component {
|
|||||||
key={key}
|
key={key}
|
||||||
style={style}
|
style={style}
|
||||||
>
|
>
|
||||||
<MovieCastPoster
|
<MovieCreditPosterConnector
|
||||||
key={movie.order}
|
key={movie.order}
|
||||||
|
component={itemComponent}
|
||||||
posterWidth={posterWidth}
|
posterWidth={posterWidth}
|
||||||
posterHeight={posterHeight}
|
posterHeight={posterHeight}
|
||||||
castId={movie.tmdbId}
|
tmdbId={movie.personTmdbId}
|
||||||
castName={movie.name}
|
personName={movie.personName}
|
||||||
|
job={movie.job}
|
||||||
character={movie.character}
|
character={movie.character}
|
||||||
images={movie.images}
|
images={movie.images}
|
||||||
/>
|
/>
|
||||||
@ -166,7 +169,7 @@ class MovieCastPosters extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
cast
|
items
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -176,7 +179,7 @@ class MovieCastPosters extends Component {
|
|||||||
rowHeight
|
rowHeight
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const rowCount = Math.ceil(cast.length / columnCount);
|
const rowCount = Math.ceil(items.length / columnCount);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Measure
|
<Measure
|
||||||
@ -220,9 +223,10 @@ class MovieCastPosters extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MovieCastPosters.propTypes = {
|
MovieCreditPosters.propTypes = {
|
||||||
cast: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
itemComponent: PropTypes.elementType.isRequired,
|
||||||
isSmallScreen: PropTypes.bool.isRequired
|
isSmallScreen: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MovieCastPosters;
|
export default MovieCreditPosters;
|
@ -1,76 +0,0 @@
|
|||||||
$hoverScale: 1.05;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
transition: all 200ms ease-in;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
z-index: 2;
|
|
||||||
box-shadow: 0 0 12px $black;
|
|
||||||
transition: all 200ms ease-in;
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
opacity: 0.9;
|
|
||||||
transition: opacity 200ms linear 150ms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.posterContainer {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poster {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
background-color: $defaultColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlayTitle {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 5px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
color: $offWhite;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
@add-mixin truncate;
|
|
||||||
|
|
||||||
background-color: #fafbfc;
|
|
||||||
text-align: center;
|
|
||||||
font-size: $smallFontSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 10px;
|
|
||||||
left: 10px;
|
|
||||||
z-index: 3;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: #707070;
|
|
||||||
color: $white;
|
|
||||||
font-size: $smallFontSize;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action {
|
|
||||||
composes: button from '~Components/Link/IconButton.css';
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $radarrYellow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointSmall) {
|
|
||||||
.container {
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
.grid {
|
|
||||||
flex: 1 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
@ -1,228 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { Grid, WindowScroller } from 'react-virtualized';
|
|
||||||
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
|
||||||
import dimensions from 'Styles/Variables/dimensions';
|
|
||||||
import Measure from 'Components/Measure';
|
|
||||||
import MovieCrewPoster from './MovieCrewPoster';
|
|
||||||
import styles from './MovieCrewPosters.css';
|
|
||||||
|
|
||||||
// Poster container dimensions
|
|
||||||
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
|
|
||||||
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
|
|
||||||
|
|
||||||
const additionalColumnCount = {
|
|
||||||
small: 3,
|
|
||||||
medium: 2,
|
|
||||||
large: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
function calculateColumnWidth(width, posterSize, isSmallScreen) {
|
|
||||||
const maxiumColumnWidth = isSmallScreen ? 172 : 182;
|
|
||||||
const columns = Math.floor(width / maxiumColumnWidth);
|
|
||||||
const remainder = width % maxiumColumnWidth;
|
|
||||||
|
|
||||||
if (remainder === 0 && posterSize === 'large') {
|
|
||||||
return maxiumColumnWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.floor(width / (columns + additionalColumnCount[posterSize]));
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateRowHeight(posterHeight, isSmallScreen) {
|
|
||||||
const titleHeight = 19;
|
|
||||||
const characterHeight = 19;
|
|
||||||
|
|
||||||
const heights = [
|
|
||||||
posterHeight,
|
|
||||||
titleHeight,
|
|
||||||
characterHeight,
|
|
||||||
isSmallScreen ? columnPaddingSmallScreen : columnPadding
|
|
||||||
];
|
|
||||||
|
|
||||||
return heights.reduce((acc, height) => acc + height, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculatePosterHeight(posterWidth) {
|
|
||||||
return Math.ceil((250 / 170) * posterWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
class MovieCrewPosters extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
width: 0,
|
|
||||||
columnWidth: 182,
|
|
||||||
columnCount: 1,
|
|
||||||
posterWidth: 162,
|
|
||||||
posterHeight: 238,
|
|
||||||
rowHeight: calculateRowHeight(238, props.isSmallScreen)
|
|
||||||
};
|
|
||||||
|
|
||||||
this._isInitialized = false;
|
|
||||||
this._grid = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
const {
|
|
||||||
crew
|
|
||||||
} = 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.crew, crew))) {
|
|
||||||
// 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;
|
|
||||||
const columnWidth = calculateColumnWidth(width, 'small', isSmallScreen);
|
|
||||||
const columnCount = Math.max(Math.floor(width / columnWidth), 1);
|
|
||||||
const posterWidth = columnWidth - padding;
|
|
||||||
const posterHeight = calculatePosterHeight(posterWidth);
|
|
||||||
const rowHeight = calculateRowHeight(posterHeight, isSmallScreen);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
width,
|
|
||||||
columnWidth,
|
|
||||||
columnCount,
|
|
||||||
posterWidth,
|
|
||||||
posterHeight,
|
|
||||||
rowHeight
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cellRenderer = ({ key, rowIndex, columnIndex, style }) => {
|
|
||||||
const {
|
|
||||||
crew
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
posterWidth,
|
|
||||||
posterHeight,
|
|
||||||
columnCount
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const movieIdx = rowIndex * columnCount + columnIndex;
|
|
||||||
const movie = crew[movieIdx];
|
|
||||||
|
|
||||||
if (!movie) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={styles.container}
|
|
||||||
key={key}
|
|
||||||
style={style}
|
|
||||||
>
|
|
||||||
<MovieCrewPoster
|
|
||||||
key={movie.order}
|
|
||||||
posterWidth={posterWidth}
|
|
||||||
posterHeight={posterHeight}
|
|
||||||
crewId={movie.tmdbId}
|
|
||||||
crewName={movie.name}
|
|
||||||
job={movie.job}
|
|
||||||
images={movie.images}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onMeasure = ({ width }) => {
|
|
||||||
this.calculateGrid(width, this.props.isSmallScreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
crew
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
width,
|
|
||||||
columnWidth,
|
|
||||||
columnCount,
|
|
||||||
rowHeight
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const rowCount = Math.ceil(crew.length / columnCount);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Measure
|
|
||||||
whitelist={['width']}
|
|
||||||
onMeasure={this.onMeasure}
|
|
||||||
>
|
|
||||||
<WindowScroller
|
|
||||||
scrollElement={undefined}
|
|
||||||
>
|
|
||||||
{({ height, registerChild, onChildScroll, scrollTop }) => {
|
|
||||||
if (!height) {
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={registerChild}>
|
|
||||||
<Grid
|
|
||||||
ref={this.setGridRef}
|
|
||||||
className={styles.grid}
|
|
||||||
autoHeight={true}
|
|
||||||
height={height}
|
|
||||||
columnCount={columnCount}
|
|
||||||
columnWidth={columnWidth}
|
|
||||||
rowCount={rowCount}
|
|
||||||
rowHeight={rowHeight}
|
|
||||||
width={width}
|
|
||||||
onScroll={onChildScroll}
|
|
||||||
scrollTop={scrollTop}
|
|
||||||
overscanRowCount={2}
|
|
||||||
cellRenderer={this.cellRenderer}
|
|
||||||
scrollToAlignment={'start'}
|
|
||||||
isScrollingOptOut={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</WindowScroller>
|
|
||||||
</Measure>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieCrewPosters.propTypes = {
|
|
||||||
crew: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
isSmallScreen: PropTypes.bool.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieCrewPosters;
|
|
@ -1,25 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import MovieCrewPosters from './MovieCrewPosters';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.moviePeople.items,
|
|
||||||
(people) => {
|
|
||||||
const crew = _.reduce(people, (acc, person) => {
|
|
||||||
if (person.type === 'crew') {
|
|
||||||
acc.push(person);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
|
||||||
crew
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(MovieCrewPosters);
|
|
@ -31,8 +31,8 @@ import EditMovieModalConnector from 'Movie/Edit/EditMovieModalConnector';
|
|||||||
import DeleteMovieModal from 'Movie/Delete/DeleteMovieModal';
|
import DeleteMovieModal from 'Movie/Delete/DeleteMovieModal';
|
||||||
import MovieHistoryTable from 'Movie/History/MovieHistoryTable';
|
import MovieHistoryTable from 'Movie/History/MovieHistoryTable';
|
||||||
import MovieTitlesTable from './Titles/MovieTitlesTable';
|
import MovieTitlesTable from './Titles/MovieTitlesTable';
|
||||||
import MovieCastPostersConnector from './Cast/MovieCastPostersConnector';
|
import MovieCastPostersConnector from './Credits/Cast/MovieCastPostersConnector';
|
||||||
import MovieCrewPostersConnector from './Crew/MovieCrewPostersConnector';
|
import MovieCrewPostersConnector from './Credits/Crew/MovieCrewPostersConnector';
|
||||||
import MovieAlternateTitles from './MovieAlternateTitles';
|
import MovieAlternateTitles from './MovieAlternateTitles';
|
||||||
import MovieDetailsLinks from './MovieDetailsLinks';
|
import MovieDetailsLinks from './MovieDetailsLinks';
|
||||||
import InteractiveSearchTable from 'InteractiveSearch/InteractiveSearchTable';
|
import InteractiveSearchTable from 'InteractiveSearch/InteractiveSearchTable';
|
||||||
@ -181,7 +181,7 @@ class MovieDetails extends Component {
|
|||||||
isPopulated,
|
isPopulated,
|
||||||
isSmallScreen,
|
isSmallScreen,
|
||||||
movieFilesError,
|
movieFilesError,
|
||||||
moviePeopleError,
|
movieCreditsError,
|
||||||
hasMovieFiles,
|
hasMovieFiles,
|
||||||
previousMovie,
|
previousMovie,
|
||||||
nextMovie,
|
nextMovie,
|
||||||
@ -464,12 +464,12 @@ class MovieDetails extends Component {
|
|||||||
|
|
||||||
<div className={styles.contentContainer}>
|
<div className={styles.contentContainer}>
|
||||||
{
|
{
|
||||||
!isPopulated && !movieFilesError && !moviePeopleError &&
|
!isPopulated && !movieFilesError && !movieCreditsError &&
|
||||||
<LoadingIndicator />
|
<LoadingIndicator />
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && movieFilesError && !moviePeopleError &&
|
!isFetching && movieFilesError && !movieCreditsError &&
|
||||||
<div>Loading movie files failed</div>
|
<div>Loading movie files failed</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,7 +629,7 @@ MovieDetails.propTypes = {
|
|||||||
isPopulated: PropTypes.bool.isRequired,
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
isSmallScreen: PropTypes.bool.isRequired,
|
isSmallScreen: PropTypes.bool.isRequired,
|
||||||
movieFilesError: PropTypes.object,
|
movieFilesError: PropTypes.object,
|
||||||
moviePeopleError: PropTypes.object,
|
movieCreditsError: PropTypes.object,
|
||||||
hasMovieFiles: PropTypes.bool.isRequired,
|
hasMovieFiles: PropTypes.bool.isRequired,
|
||||||
previousMovie: PropTypes.object.isRequired,
|
previousMovie: PropTypes.object.isRequired,
|
||||||
nextMovie: PropTypes.object.isRequired,
|
nextMovie: PropTypes.object.isRequired,
|
||||||
|
@ -9,10 +9,11 @@ import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector';
|
|||||||
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
||||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||||
import { fetchMovieFiles, clearMovieFiles } from 'Store/Actions/movieFileActions';
|
import { fetchMovieFiles, clearMovieFiles } from 'Store/Actions/movieFileActions';
|
||||||
import { fetchMoviePeople, clearMoviePeople } from 'Store/Actions/moviePeopleActions';
|
import { fetchMovieCredits, clearMovieCredits } from 'Store/Actions/movieCreditsActions';
|
||||||
import { toggleMovieMonitored } from 'Store/Actions/movieActions';
|
import { toggleMovieMonitored } from 'Store/Actions/movieActions';
|
||||||
import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions';
|
import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions';
|
||||||
import { clearReleases, cancelFetchReleases } from 'Store/Actions/releaseActions';
|
import { clearReleases, cancelFetchReleases } from 'Store/Actions/releaseActions';
|
||||||
|
import { fetchNetImportSchema } from 'Store/Actions/settingsActions';
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
import { executeCommand } from 'Store/Actions/commandActions';
|
||||||
import * as commandNames from 'Commands/commandNames';
|
import * as commandNames from 'Commands/commandNames';
|
||||||
import MovieDetails from './MovieDetails';
|
import MovieDetails from './MovieDetails';
|
||||||
@ -41,19 +42,19 @@ const selectMovieFiles = createSelector(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectMoviePeople = createSelector(
|
const selectMovieCredits = createSelector(
|
||||||
(state) => state.moviePeople,
|
(state) => state.movieCredits,
|
||||||
(moviePeople) => {
|
(movieCredits) => {
|
||||||
const {
|
const {
|
||||||
isFetching,
|
isFetching,
|
||||||
isPopulated,
|
isPopulated,
|
||||||
error
|
error
|
||||||
} = moviePeople;
|
} = movieCredits;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isMoviePeopleFetching: isFetching,
|
isMovieCreditsFetching: isFetching,
|
||||||
isMoviePeoplePopulated: isPopulated,
|
isMovieCreditsPopulated: isPopulated,
|
||||||
moviePeopleError: error
|
movieCreditsError: error
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -62,11 +63,11 @@ function createMapStateToProps() {
|
|||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { titleSlug }) => titleSlug,
|
(state, { titleSlug }) => titleSlug,
|
||||||
selectMovieFiles,
|
selectMovieFiles,
|
||||||
selectMoviePeople,
|
selectMovieCredits,
|
||||||
createAllMoviesSelector(),
|
createAllMoviesSelector(),
|
||||||
createCommandsSelector(),
|
createCommandsSelector(),
|
||||||
createDimensionsSelector(),
|
createDimensionsSelector(),
|
||||||
(titleSlug, movieFiles, moviePeople, allMovies, commands, dimensions) => {
|
(titleSlug, movieFiles, movieCredits, allMovies, commands, dimensions) => {
|
||||||
const sortedMovies = _.orderBy(allMovies, 'sortTitle');
|
const sortedMovies = _.orderBy(allMovies, 'sortTitle');
|
||||||
const movieIndex = _.findIndex(sortedMovies, { titleSlug });
|
const movieIndex = _.findIndex(sortedMovies, { titleSlug });
|
||||||
const movie = sortedMovies[movieIndex];
|
const movie = sortedMovies[movieIndex];
|
||||||
@ -84,10 +85,10 @@ function createMapStateToProps() {
|
|||||||
} = movieFiles;
|
} = movieFiles;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isMoviePeopleFetching,
|
isMovieCreditsFetching,
|
||||||
isMoviePeoplePopulated,
|
isMovieCreditsPopulated,
|
||||||
moviePeopleError
|
movieCreditsError
|
||||||
} = moviePeople;
|
} = movieCredits;
|
||||||
|
|
||||||
const previousMovie = sortedMovies[movieIndex - 1] || _.last(sortedMovies);
|
const previousMovie = sortedMovies[movieIndex - 1] || _.last(sortedMovies);
|
||||||
const nextMovie = sortedMovies[movieIndex + 1] || _.first(sortedMovies);
|
const nextMovie = sortedMovies[movieIndex + 1] || _.first(sortedMovies);
|
||||||
@ -106,8 +107,8 @@ function createMapStateToProps() {
|
|||||||
isRenamingMovieCommand.body.movieIds.indexOf(movie.id) > -1
|
isRenamingMovieCommand.body.movieIds.indexOf(movie.id) > -1
|
||||||
);
|
);
|
||||||
|
|
||||||
const isFetching = isMovieFilesFetching && isMoviePeopleFetching;
|
const isFetching = isMovieFilesFetching && isMovieCreditsFetching;
|
||||||
const isPopulated = isMovieFilesPopulated && isMoviePeoplePopulated;
|
const isPopulated = isMovieFilesPopulated && isMovieCreditsPopulated;
|
||||||
const alternateTitles = _.reduce(movie.alternateTitles, (acc, alternateTitle) => {
|
const alternateTitles = _.reduce(movie.alternateTitles, (acc, alternateTitle) => {
|
||||||
acc.push(alternateTitle.title);
|
acc.push(alternateTitle.title);
|
||||||
return acc;
|
return acc;
|
||||||
@ -125,7 +126,7 @@ function createMapStateToProps() {
|
|||||||
isFetching,
|
isFetching,
|
||||||
isPopulated,
|
isPopulated,
|
||||||
movieFilesError,
|
movieFilesError,
|
||||||
moviePeopleError,
|
movieCreditsError,
|
||||||
hasMovieFiles,
|
hasMovieFiles,
|
||||||
sizeOnDisk,
|
sizeOnDisk,
|
||||||
previousMovie,
|
previousMovie,
|
||||||
@ -139,10 +140,11 @@ function createMapStateToProps() {
|
|||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchMovieFiles,
|
fetchMovieFiles,
|
||||||
clearMovieFiles,
|
clearMovieFiles,
|
||||||
fetchMoviePeople,
|
fetchMovieCredits,
|
||||||
clearMoviePeople,
|
clearMovieCredits,
|
||||||
clearReleases,
|
clearReleases,
|
||||||
cancelFetchReleases,
|
cancelFetchReleases,
|
||||||
|
fetchNetImportSchema,
|
||||||
toggleMovieMonitored,
|
toggleMovieMonitored,
|
||||||
fetchQueueDetails,
|
fetchQueueDetails,
|
||||||
clearQueueDetails,
|
clearQueueDetails,
|
||||||
@ -198,14 +200,15 @@ class MovieDetailsConnector extends Component {
|
|||||||
const movieId = this.props.id;
|
const movieId = this.props.id;
|
||||||
|
|
||||||
this.props.fetchMovieFiles({ movieId });
|
this.props.fetchMovieFiles({ movieId });
|
||||||
this.props.fetchMoviePeople({ movieId });
|
this.props.fetchMovieCredits({ movieId });
|
||||||
this.props.fetchQueueDetails({ movieId });
|
this.props.fetchQueueDetails({ movieId });
|
||||||
|
this.props.fetchNetImportSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
unpopulate = () => {
|
unpopulate = () => {
|
||||||
this.props.cancelFetchReleases();
|
this.props.cancelFetchReleases();
|
||||||
this.props.clearMovieFiles();
|
this.props.clearMovieFiles();
|
||||||
this.props.clearMoviePeople();
|
this.props.clearMovieCredits();
|
||||||
this.props.clearQueueDetails();
|
this.props.clearQueueDetails();
|
||||||
this.props.clearReleases();
|
this.props.clearReleases();
|
||||||
}
|
}
|
||||||
@ -260,13 +263,14 @@ MovieDetailsConnector.propTypes = {
|
|||||||
isSmallScreen: PropTypes.bool.isRequired,
|
isSmallScreen: PropTypes.bool.isRequired,
|
||||||
fetchMovieFiles: PropTypes.func.isRequired,
|
fetchMovieFiles: PropTypes.func.isRequired,
|
||||||
clearMovieFiles: PropTypes.func.isRequired,
|
clearMovieFiles: PropTypes.func.isRequired,
|
||||||
fetchMoviePeople: PropTypes.func.isRequired,
|
fetchMovieCredits: PropTypes.func.isRequired,
|
||||||
clearMoviePeople: PropTypes.func.isRequired,
|
clearMovieCredits: PropTypes.func.isRequired,
|
||||||
clearReleases: PropTypes.func.isRequired,
|
clearReleases: PropTypes.func.isRequired,
|
||||||
cancelFetchReleases: PropTypes.func.isRequired,
|
cancelFetchReleases: PropTypes.func.isRequired,
|
||||||
toggleMovieMonitored: PropTypes.func.isRequired,
|
toggleMovieMonitored: PropTypes.func.isRequired,
|
||||||
fetchQueueDetails: PropTypes.func.isRequired,
|
fetchQueueDetails: PropTypes.func.isRequired,
|
||||||
clearQueueDetails: PropTypes.func.isRequired,
|
clearQueueDetails: PropTypes.func.isRequired,
|
||||||
|
fetchNetImportSchema: PropTypes.func.isRequired,
|
||||||
executeCommand: PropTypes.func.isRequired
|
executeCommand: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
function createMovieCreditListSelector() {
|
||||||
|
return createSelector(
|
||||||
|
(state, { tmdbId }) => tmdbId,
|
||||||
|
(state) => state.settings.netImports.items,
|
||||||
|
(tmdbId, netImports) => {
|
||||||
|
const netImportIds = _.reduce(netImports, (acc, list) => {
|
||||||
|
if (list.implementation === 'TMDbPersonImport') {
|
||||||
|
const personIdField = list.fields.find((field) => {
|
||||||
|
return field.name === 'personId';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (personIdField && parseInt(personIdField.value) === tmdbId) {
|
||||||
|
acc.push(list);
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
let netImportId = 0;
|
||||||
|
|
||||||
|
if (netImportIds.length > 0) {
|
||||||
|
netImportId = netImportIds[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
netImportId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createMovieCreditListSelector;
|
Loading…
Reference in New Issue
Block a user