parent
8021381de2
commit
b3caa87b78
@ -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);
|
@ -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);
|
@ -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);
|
@ -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);
|
@ -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