|
|
|
import _ from 'lodash';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import React, { Component } from 'react';
|
|
|
|
import ReactDOM from 'react-dom';
|
|
|
|
import Measure from 'react-measure';
|
|
|
|
import { Grid, WindowScroller } from 'react-virtualized';
|
|
|
|
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
|
|
|
import dimensions from 'Styles/Variables/dimensions';
|
|
|
|
import { sortDirections } from 'Helpers/Props';
|
|
|
|
import ArtistIndexItemConnector from 'Artist/Index/ArtistIndexItemConnector';
|
|
|
|
import ArtistIndexOverview from './ArtistIndexOverview';
|
|
|
|
import styles from './ArtistIndexOverviews.css';
|
|
|
|
|
|
|
|
// Poster container dimensions
|
|
|
|
const columnPadding = parseInt(dimensions.artistIndexColumnPadding);
|
|
|
|
const columnPaddingSmallScreen = parseInt(dimensions.artistIndexColumnPaddingSmallScreen);
|
|
|
|
const progressBarHeight = parseInt(dimensions.progressBarSmallHeight);
|
|
|
|
const detailedProgressBarHeight = parseInt(dimensions.progressBarMediumHeight);
|
|
|
|
|
|
|
|
function calculatePosterWidth(posterSize, isSmallScreen) {
|
|
|
|
const maxiumPosterWidth = isSmallScreen ? 192 : 202;
|
|
|
|
|
|
|
|
if (posterSize === 'large') {
|
|
|
|
return maxiumPosterWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (posterSize === 'medium') {
|
|
|
|
return Math.floor(maxiumPosterWidth * 0.75);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Math.floor(maxiumPosterWidth * 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
function calculateRowHeight(posterHeight, sortKey, isSmallScreen, overviewOptions) {
|
|
|
|
const {
|
|
|
|
detailedProgressBar
|
|
|
|
} = overviewOptions;
|
|
|
|
|
|
|
|
const heights = [
|
|
|
|
posterHeight,
|
|
|
|
detailedProgressBar ? detailedProgressBarHeight : progressBarHeight,
|
|
|
|
isSmallScreen ? columnPaddingSmallScreen : columnPadding
|
|
|
|
];
|
|
|
|
|
|
|
|
return heights.reduce((acc, height) => acc + height, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
function calculatePosterHeight(posterWidth) {
|
|
|
|
return posterWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
class ArtistIndexOverviews extends Component {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Lifecycle
|
|
|
|
|
|
|
|
constructor(props, context) {
|
|
|
|
super(props, context);
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
width: 0,
|
|
|
|
columnCount: 1,
|
|
|
|
posterWidth: 238,
|
|
|
|
posterHeight: 238,
|
|
|
|
rowHeight: calculateRowHeight(238, null, props.isSmallScreen, {})
|
|
|
|
};
|
|
|
|
|
|
|
|
this._isInitialized = false;
|
|
|
|
this._grid = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
this._contentBodyNode = ReactDOM.findDOMNode(this.props.contentBody);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidUpdate(prevProps) {
|
|
|
|
const {
|
|
|
|
items,
|
|
|
|
filterKey,
|
|
|
|
filterValue,
|
|
|
|
sortKey,
|
|
|
|
sortDirection,
|
|
|
|
overviewOptions
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
const itemsChanged = hasDifferentItems(prevProps.items, items);
|
|
|
|
const overviewOptionsChanged = !_.isMatch(prevProps.overviewOptions, overviewOptions);
|
|
|
|
|
|
|
|
if (
|
|
|
|
prevProps.sortKey !== sortKey ||
|
|
|
|
prevProps.overviewOptions !== overviewOptions ||
|
|
|
|
itemsChanged
|
|
|
|
) {
|
|
|
|
this.calculateGrid();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
prevProps.filterKey !== filterKey ||
|
|
|
|
prevProps.filterValue !== filterValue ||
|
|
|
|
prevProps.sortKey !== sortKey ||
|
|
|
|
prevProps.sortDirection !== sortDirection ||
|
|
|
|
itemsChanged ||
|
|
|
|
overviewOptionsChanged
|
|
|
|
) {
|
|
|
|
this._grid.recomputeGridSize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Control
|
|
|
|
|
|
|
|
scrollToFirstCharacter(character) {
|
|
|
|
const items = this.props.items;
|
|
|
|
const {
|
|
|
|
rowHeight
|
|
|
|
} = this.state;
|
|
|
|
|
|
|
|
const index = _.findIndex(items, (item) => {
|
|
|
|
const firstCharacter = item.sortTitle.charAt(0);
|
|
|
|
|
|
|
|
if (character === '#') {
|
|
|
|
return !isNaN(firstCharacter);
|
|
|
|
}
|
|
|
|
|
|
|
|
return firstCharacter === character;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (index != null) {
|
|
|
|
const scrollTop = rowHeight * index;
|
|
|
|
|
|
|
|
this.props.onScroll({ scrollTop });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setGridRef = (ref) => {
|
|
|
|
this._grid = ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
calculateGrid = (width = this.state.width, isSmallScreen) => {
|
|
|
|
const {
|
|
|
|
sortKey,
|
|
|
|
overviewOptions
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
const posterWidth = calculatePosterWidth(overviewOptions.size, isSmallScreen);
|
|
|
|
const posterHeight = calculatePosterHeight(posterWidth);
|
|
|
|
const rowHeight = calculateRowHeight(posterHeight, sortKey, isSmallScreen, overviewOptions);
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
width,
|
|
|
|
posterWidth,
|
|
|
|
posterHeight,
|
|
|
|
rowHeight
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
cellRenderer = ({ key, rowIndex, style }) => {
|
|
|
|
const {
|
|
|
|
items,
|
|
|
|
sortKey,
|
|
|
|
overviewOptions,
|
|
|
|
showRelativeDates,
|
|
|
|
shortDateFormat,
|
|
|
|
timeFormat,
|
|
|
|
isSmallScreen
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
const {
|
|
|
|
posterWidth,
|
|
|
|
posterHeight,
|
|
|
|
rowHeight
|
|
|
|
} = this.state;
|
|
|
|
|
|
|
|
const artist = items[rowIndex];
|
|
|
|
|
|
|
|
if (!artist) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<ArtistIndexItemConnector
|
|
|
|
key={key}
|
|
|
|
component={ArtistIndexOverview}
|
|
|
|
sortKey={sortKey}
|
|
|
|
posterWidth={posterWidth}
|
|
|
|
posterHeight={posterHeight}
|
|
|
|
rowHeight={rowHeight}
|
|
|
|
overviewOptions={overviewOptions}
|
|
|
|
showRelativeDates={showRelativeDates}
|
|
|
|
shortDateFormat={shortDateFormat}
|
|
|
|
timeFormat={timeFormat}
|
|
|
|
isSmallScreen={isSmallScreen}
|
|
|
|
style={style}
|
|
|
|
artistId={artist.id}
|
|
|
|
languageProfileId={artist.languageProfileId}
|
|
|
|
qualityProfileId={artist.qualityProfileId}
|
|
|
|
metadataProfileId={artist.metadataProfileId}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Listeners
|
|
|
|
|
|
|
|
onMeasure = ({ width }) => {
|
|
|
|
this.calculateGrid(width, this.props.isSmallScreen);
|
|
|
|
}
|
|
|
|
|
|
|
|
onSectionRendered = () => {
|
|
|
|
if (!this._isInitialized && this._contentBodyNode) {
|
|
|
|
this.props.onRender();
|
|
|
|
this._isInitialized = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Render
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const {
|
|
|
|
items,
|
|
|
|
scrollTop,
|
|
|
|
isSmallScreen,
|
|
|
|
onScroll
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
const {
|
|
|
|
width,
|
|
|
|
rowHeight
|
|
|
|
} = this.state;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Measure onMeasure={this.onMeasure}>
|
|
|
|
<WindowScroller
|
|
|
|
scrollElement={isSmallScreen ? null : this._contentBodyNode}
|
|
|
|
onScroll={onScroll}
|
|
|
|
>
|
|
|
|
{({ height, isScrolling }) => {
|
|
|
|
return (
|
|
|
|
<Grid
|
|
|
|
ref={this.setGridRef}
|
|
|
|
className={styles.grid}
|
|
|
|
autoHeight={true}
|
|
|
|
height={height}
|
|
|
|
columnCount={1}
|
|
|
|
columnWidth={width}
|
|
|
|
rowCount={items.length}
|
|
|
|
rowHeight={rowHeight}
|
|
|
|
width={width}
|
|
|
|
scrollTop={scrollTop}
|
|
|
|
overscanRowCount={2}
|
|
|
|
cellRenderer={this.cellRenderer}
|
|
|
|
onSectionRendered={this.onSectionRendered}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</WindowScroller>
|
|
|
|
</Measure>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ArtistIndexOverviews.propTypes = {
|
|
|
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
|
|
filterKey: PropTypes.string,
|
|
|
|
filterValue: PropTypes.oneOfType([PropTypes.bool, PropTypes.number, PropTypes.string]),
|
|
|
|
sortKey: PropTypes.string,
|
|
|
|
sortDirection: PropTypes.oneOf(sortDirections.all),
|
|
|
|
overviewOptions: PropTypes.object.isRequired,
|
|
|
|
scrollTop: PropTypes.number.isRequired,
|
|
|
|
contentBody: PropTypes.object.isRequired,
|
|
|
|
showRelativeDates: PropTypes.bool.isRequired,
|
|
|
|
shortDateFormat: PropTypes.string.isRequired,
|
|
|
|
isSmallScreen: PropTypes.bool.isRequired,
|
|
|
|
timeFormat: PropTypes.string.isRequired,
|
|
|
|
onRender: PropTypes.func.isRequired,
|
|
|
|
onScroll: PropTypes.func.isRequired
|
|
|
|
};
|
|
|
|
|
|
|
|
export default ArtistIndexOverviews;
|