New: Add and Edit People Lists from Movie Details Page

pull/4072/head
Qstick 4 years ago
parent 8021381de2
commit b3caa87b78

@ -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, fetchLanguages } from 'Store/Actions/settingsActions';
import { fetchQualityProfiles, fetchUISettings, fetchLanguages, fetchNetImports } 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.settings.ui.isPopulated,
(state) => state.settings.qualityProfiles.isPopulated,
(state) => state.settings.languages.isPopulated,
(state) => state.settings.netImports.isPopulated,
(state) => state.system.status.isPopulated,
(
customFiltersIsPopulated,
@ -55,6 +56,7 @@ const selectIsPopulated = createSelector(
uiSettingsIsPopulated,
qualityProfilesIsPopulated,
languagesIsPopulated,
netImportsIsPopulated,
systemStatusIsPopulated
) => {
return (
@ -63,6 +65,7 @@ const selectIsPopulated = createSelector(
uiSettingsIsPopulated &&
qualityProfilesIsPopulated &&
languagesIsPopulated &&
netImportsIsPopulated &&
systemStatusIsPopulated
);
}
@ -74,6 +77,7 @@ const selectErrors = createSelector(
(state) => state.settings.ui.error,
(state) => state.settings.qualityProfiles.error,
(state) => state.settings.languages.error,
(state) => state.settings.netImports.error,
(state) => state.system.status.error,
(
customFiltersError,
@ -81,6 +85,7 @@ const selectErrors = createSelector(
uiSettingsError,
qualityProfilesError,
languagesError,
netImportsError,
systemStatusError
) => {
const hasError = !!(
@ -89,6 +94,7 @@ const selectErrors = createSelector(
uiSettingsError ||
qualityProfilesError ||
languagesError ||
netImportsError ||
systemStatusError
);
@ -99,6 +105,7 @@ const selectErrors = createSelector(
uiSettingsError,
qualityProfilesError,
languagesError,
netImportsError,
systemStatusError
};
}
@ -146,6 +153,9 @@ function createMapDispatchToProps(dispatch, props) {
dispatchFetchLanguages() {
dispatch(fetchLanguages());
},
dispatchFetchNetImports() {
dispatch(fetchNetImports());
},
dispatchFetchUISettings() {
dispatch(fetchUISettings());
},
@ -181,6 +191,7 @@ class PageConnector extends Component {
this.props.dispatchFetchTags();
this.props.dispatchFetchQualityProfiles();
this.props.dispatchFetchLanguages();
this.props.dispatchFetchNetImports();
this.props.dispatchFetchUISettings();
this.props.dispatchFetchStatus();
}
@ -204,6 +215,7 @@ class PageConnector extends Component {
dispatchFetchTags,
dispatchFetchQualityProfiles,
dispatchFetchLanguages,
dispatchFetchNetImports,
dispatchFetchUISettings,
dispatchFetchStatus,
...otherProps
@ -242,6 +254,7 @@ PageConnector.propTypes = {
dispatchFetchTags: PropTypes.func.isRequired,
dispatchFetchQualityProfiles: PropTypes.func.isRequired,
dispatchFetchLanguages: PropTypes.func.isRequired,
dispatchFetchNetImports: PropTypes.func.isRequired,
dispatchFetchUISettings: PropTypes.func.isRequired,
dispatchFetchStatus: 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 Label from 'Components/Label';
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 {
@ -16,19 +17,24 @@ class MovieCastPoster extends Component {
this.state = {
hasPosterError: false,
isEditMovieModalOpen: false
isEditNetImportModalOpen: false
};
}
//
// Listeners
onEditMoviePress = () => {
this.setState({ isEditMovieModalOpen: true });
onEditNetImportPress = () => {
this.setState({ isEditNetImportModalOpen: true });
}
onEditMovieModalClose = () => {
this.setState({ isEditMovieModalOpen: false });
onAddNetImportPress = () => {
this.props.onNetImportSelect();
this.setState({ isEditNetImportModalOpen: true });
}
onEditNetImportModalClose = () => {
this.setState({ isEditNetImportModalOpen: false });
}
onPosterLoad = () => {
@ -48,11 +54,12 @@ class MovieCastPoster extends Component {
render() {
const {
castName,
personName,
character,
images,
posterWidth,
posterHeight
posterHeight,
netImportId
} = this.props;
const {
@ -68,12 +75,21 @@ class MovieCastPoster extends Component {
<div className={styles.content}>
<div className={styles.posterContainer}>
<Label className={styles.controls}>
<IconButton
className={styles.action}
name={icons.EDIT}
title="Edit movie"
onPress={this.onEditMoviePress}
/>
{
netImportId > 0 ?
<IconButton
className={styles.action}
name={icons.EDIT}
title="Edit Person"
onPress={this.onEditNetImportPress}
/> :
<IconButton
className={styles.action}
name={icons.ADD}
title="Follow Person"
onPress={this.onAddNetImportPress}
/>
}
</Label>
<div
@ -94,30 +110,43 @@ class MovieCastPoster extends Component {
{
hasPosterError &&
<div className={styles.overlayTitle}>
{castName}
{personName}
</div>
}
</div>
</div>
<div className={styles.title}>
{castName}
{personName}
</div>
<div className={styles.title}>
{character}
</div>
<EditNetImportModalConnector
id={netImportId}
isOpen={this.state.isEditNetImportModalOpen}
onModalClose={this.onEditNetImportModalClose}
onDeleteNetImportPress={this.onDeleteNetImportPress}
/>
</div>
);
}
}
MovieCastPoster.propTypes = {
castId: PropTypes.number.isRequired,
castName: PropTypes.string.isRequired,
tmdbId: PropTypes.number.isRequired,
personName: PropTypes.string.isRequired,
character: PropTypes.string.isRequired,
images: PropTypes.arrayOf(PropTypes.object).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;

@ -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 Label from 'Components/Label';
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 {
@ -16,19 +17,24 @@ class MovieCrewPoster extends Component {
this.state = {
hasPosterError: false,
isEditMovieModalOpen: false
isEditNetImportModalOpen: false
};
}
//
// Listeners
onEditMoviePress = () => {
this.setState({ isEditMovieModalOpen: true });
onEditNetImportPress = () => {
this.setState({ isEditNetImportModalOpen: true });
}
onEditMovieModalClose = () => {
this.setState({ isEditMovieModalOpen: false });
onAddNetImportPress = () => {
this.props.onNetImportSelect();
this.setState({ isEditNetImportModalOpen: true });
}
onEditNetImportModalClose = () => {
this.setState({ isEditNetImportModalOpen: false });
}
onPosterLoad = () => {
@ -48,11 +54,12 @@ class MovieCrewPoster extends Component {
render() {
const {
crewName,
personName,
job,
images,
posterWidth,
posterHeight
posterHeight,
netImportId
} = this.props;
const {
@ -68,12 +75,21 @@ class MovieCrewPoster extends Component {
<div className={styles.content}>
<div className={styles.posterContainer}>
<Label className={styles.controls}>
<IconButton
className={styles.action}
name={icons.EDIT}
title="Edit movie"
onPress={this.onEditMoviePress}
/>
{
netImportId > 0 ?
<IconButton
className={styles.action}
name={icons.EDIT}
title="Edit Person"
onPress={this.onEditNetImportPress}
/> :
<IconButton
className={styles.action}
name={icons.ADD}
title="Follow Person"
onPress={this.onAddNetImportPress}
/>
}
</Label>
<div
@ -94,30 +110,43 @@ class MovieCrewPoster extends Component {
{
hasPosterError &&
<div className={styles.overlayTitle}>
{crewName}
{personName}
</div>
}
</div>
</div>
<div className={styles.title}>
{crewName}
{personName}
</div>
<div className={styles.title}>
{job}
</div>
<EditNetImportModalConnector
id={netImportId}
isOpen={this.state.isEditNetImportModalOpen}
onModalClose={this.onEditNetImportModalClose}
onDeleteNetImportPress={this.onDeleteNetImportPress}
/>
</div>
);
}
}
MovieCrewPoster.propTypes = {
crewId: PropTypes.number.isRequired,
crewName: PropTypes.string.isRequired,
tmdbId: PropTypes.number.isRequired,
personName: PropTypes.string.isRequired,
job: PropTypes.string.isRequired,
images: PropTypes.arrayOf(PropTypes.object).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;

@ -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 dimensions from 'Styles/Variables/dimensions';
import Measure from 'Components/Measure';
import MovieCastPoster from './MovieCastPoster';
import styles from './MovieCastPosters.css';
import MovieCreditPosterConnector from './MovieCreditPosterConnector';
import styles from './MovieCreditPosters.css';
// Poster container dimensions
const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
@ -47,7 +47,7 @@ function calculatePosterHeight(posterWidth) {
return Math.ceil((250 / 170) * posterWidth);
}
class MovieCastPosters extends Component {
class MovieCreditPosters extends Component {
//
// Lifecycle
@ -70,7 +70,7 @@ class MovieCastPosters extends Component {
componentDidUpdate(prevProps, prevState) {
const {
cast
items
} = this.props;
const {
@ -85,7 +85,7 @@ class MovieCastPosters extends Component {
prevState.columnWidth !== columnWidth ||
prevState.columnCount !== columnCount ||
prevState.rowHeight !== rowHeight ||
hasDifferentItemsOrOrder(prevProps.cast, cast))) {
hasDifferentItemsOrOrder(prevProps.items, items))) {
// recomputeGridSize also forces Grid to discard its cache of rendered cells
this._grid.recomputeGridSize();
}
@ -119,7 +119,8 @@ class MovieCastPosters extends Component {
cellRenderer = ({ key, rowIndex, columnIndex, style }) => {
const {
cast
items,
itemComponent
} = this.props;
const {
@ -129,7 +130,7 @@ class MovieCastPosters extends Component {
} = this.state;
const movieIdx = rowIndex * columnCount + columnIndex;
const movie = cast[movieIdx];
const movie = items[movieIdx];
if (!movie) {
return null;
@ -141,12 +142,14 @@ class MovieCastPosters extends Component {
key={key}
style={style}
>
<MovieCastPoster
<MovieCreditPosterConnector
key={movie.order}
component={itemComponent}
posterWidth={posterWidth}
posterHeight={posterHeight}
castId={movie.tmdbId}
castName={movie.name}
tmdbId={movie.personTmdbId}
personName={movie.personName}
job={movie.job}
character={movie.character}
images={movie.images}
/>
@ -166,7 +169,7 @@ class MovieCastPosters extends Component {
render() {
const {
cast
items
} = this.props;
const {
@ -176,7 +179,7 @@ class MovieCastPosters extends Component {
rowHeight
} = this.state;
const rowCount = Math.ceil(cast.length / columnCount);
const rowCount = Math.ceil(items.length / columnCount);
return (
<Measure
@ -220,9 +223,10 @@ class MovieCastPosters extends Component {
}
}
MovieCastPosters.propTypes = {
cast: PropTypes.arrayOf(PropTypes.object).isRequired,
MovieCreditPosters.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired,
itemComponent: PropTypes.elementType.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 MovieHistoryTable from 'Movie/History/MovieHistoryTable';
import MovieTitlesTable from './Titles/MovieTitlesTable';
import MovieCastPostersConnector from './Cast/MovieCastPostersConnector';
import MovieCrewPostersConnector from './Crew/MovieCrewPostersConnector';
import MovieCastPostersConnector from './Credits/Cast/MovieCastPostersConnector';
import MovieCrewPostersConnector from './Credits/Crew/MovieCrewPostersConnector';
import MovieAlternateTitles from './MovieAlternateTitles';
import MovieDetailsLinks from './MovieDetailsLinks';
import InteractiveSearchTable from 'InteractiveSearch/InteractiveSearchTable';
@ -181,7 +181,7 @@ class MovieDetails extends Component {
isPopulated,
isSmallScreen,
movieFilesError,
moviePeopleError,
movieCreditsError,
hasMovieFiles,
previousMovie,
nextMovie,
@ -464,12 +464,12 @@ class MovieDetails extends Component {
<div className={styles.contentContainer}>
{
!isPopulated && !movieFilesError && !moviePeopleError &&
!isPopulated && !movieFilesError && !movieCreditsError &&
<LoadingIndicator />
}
{
!isFetching && movieFilesError && !moviePeopleError &&
!isFetching && movieFilesError && !movieCreditsError &&
<div>Loading movie files failed</div>
}
@ -629,7 +629,7 @@ MovieDetails.propTypes = {
isPopulated: PropTypes.bool.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
movieFilesError: PropTypes.object,
moviePeopleError: PropTypes.object,
movieCreditsError: PropTypes.object,
hasMovieFiles: PropTypes.bool.isRequired,
previousMovie: PropTypes.object.isRequired,
nextMovie: PropTypes.object.isRequired,

@ -9,10 +9,11 @@ import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector';
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
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 { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions';
import { clearReleases, cancelFetchReleases } from 'Store/Actions/releaseActions';
import { fetchNetImportSchema } from 'Store/Actions/settingsActions';
import { executeCommand } from 'Store/Actions/commandActions';
import * as commandNames from 'Commands/commandNames';
import MovieDetails from './MovieDetails';
@ -41,19 +42,19 @@ const selectMovieFiles = createSelector(
}
);
const selectMoviePeople = createSelector(
(state) => state.moviePeople,
(moviePeople) => {
const selectMovieCredits = createSelector(
(state) => state.movieCredits,
(movieCredits) => {
const {
isFetching,
isPopulated,
error
} = moviePeople;
} = movieCredits;
return {
isMoviePeopleFetching: isFetching,
isMoviePeoplePopulated: isPopulated,
moviePeopleError: error
isMovieCreditsFetching: isFetching,
isMovieCreditsPopulated: isPopulated,
movieCreditsError: error
};
}
);
@ -62,11 +63,11 @@ function createMapStateToProps() {
return createSelector(
(state, { titleSlug }) => titleSlug,
selectMovieFiles,
selectMoviePeople,
selectMovieCredits,
createAllMoviesSelector(),
createCommandsSelector(),
createDimensionsSelector(),
(titleSlug, movieFiles, moviePeople, allMovies, commands, dimensions) => {
(titleSlug, movieFiles, movieCredits, allMovies, commands, dimensions) => {
const sortedMovies = _.orderBy(allMovies, 'sortTitle');
const movieIndex = _.findIndex(sortedMovies, { titleSlug });
const movie = sortedMovies[movieIndex];
@ -84,10 +85,10 @@ function createMapStateToProps() {
} = movieFiles;
const {
isMoviePeopleFetching,
isMoviePeoplePopulated,
moviePeopleError
} = moviePeople;
isMovieCreditsFetching,
isMovieCreditsPopulated,
movieCreditsError
} = movieCredits;
const previousMovie = sortedMovies[movieIndex - 1] || _.last(sortedMovies);
const nextMovie = sortedMovies[movieIndex + 1] || _.first(sortedMovies);
@ -106,8 +107,8 @@ function createMapStateToProps() {
isRenamingMovieCommand.body.movieIds.indexOf(movie.id) > -1
);
const isFetching = isMovieFilesFetching && isMoviePeopleFetching;
const isPopulated = isMovieFilesPopulated && isMoviePeoplePopulated;
const isFetching = isMovieFilesFetching && isMovieCreditsFetching;
const isPopulated = isMovieFilesPopulated && isMovieCreditsPopulated;
const alternateTitles = _.reduce(movie.alternateTitles, (acc, alternateTitle) => {
acc.push(alternateTitle.title);
return acc;
@ -125,7 +126,7 @@ function createMapStateToProps() {
isFetching,
isPopulated,
movieFilesError,
moviePeopleError,
movieCreditsError,
hasMovieFiles,
sizeOnDisk,
previousMovie,
@ -139,10 +140,11 @@ function createMapStateToProps() {
const mapDispatchToProps = {
fetchMovieFiles,
clearMovieFiles,
fetchMoviePeople,
clearMoviePeople,
fetchMovieCredits,
clearMovieCredits,
clearReleases,
cancelFetchReleases,
fetchNetImportSchema,
toggleMovieMonitored,
fetchQueueDetails,
clearQueueDetails,
@ -198,14 +200,15 @@ class MovieDetailsConnector extends Component {
const movieId = this.props.id;
this.props.fetchMovieFiles({ movieId });
this.props.fetchMoviePeople({ movieId });
this.props.fetchMovieCredits({ movieId });
this.props.fetchQueueDetails({ movieId });
this.props.fetchNetImportSchema();
}
unpopulate = () => {
this.props.cancelFetchReleases();
this.props.clearMovieFiles();
this.props.clearMoviePeople();
this.props.clearMovieCredits();
this.props.clearQueueDetails();
this.props.clearReleases();
}
@ -260,13 +263,14 @@ MovieDetailsConnector.propTypes = {
isSmallScreen: PropTypes.bool.isRequired,
fetchMovieFiles: PropTypes.func.isRequired,
clearMovieFiles: PropTypes.func.isRequired,
fetchMoviePeople: PropTypes.func.isRequired,
clearMoviePeople: PropTypes.func.isRequired,
fetchMovieCredits: PropTypes.func.isRequired,
clearMovieCredits: PropTypes.func.isRequired,
clearReleases: PropTypes.func.isRequired,
cancelFetchReleases: PropTypes.func.isRequired,
toggleMovieMonitored: PropTypes.func.isRequired,
fetchQueueDetails: PropTypes.func.isRequired,
clearQueueDetails: PropTypes.func.isRequired,
fetchNetImportSchema: 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…
Cancel
Save