parent
f6fc78d927
commit
566ac1a9d3
@ -1,45 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import getProgressBarKind from 'Utilities/Series/getProgressBarKind';
|
|
||||||
import { sizes } from 'Helpers/Props';
|
|
||||||
import ProgressBar from 'Components/ProgressBar';
|
|
||||||
import styles from './ArtistIndexBannerProgressBar.css';
|
|
||||||
|
|
||||||
function ArtistIndexBannerProgressBar(props) {
|
|
||||||
const {
|
|
||||||
monitored,
|
|
||||||
status,
|
|
||||||
trackCount,
|
|
||||||
trackFileCount,
|
|
||||||
bannerWidth,
|
|
||||||
detailedProgressBar
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const progress = trackCount ? trackFileCount / trackCount * 100 : 100;
|
|
||||||
const text = `${trackFileCount} / ${trackCount}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ProgressBar
|
|
||||||
className={styles.progressBar}
|
|
||||||
containerClassName={styles.progress}
|
|
||||||
progress={progress}
|
|
||||||
kind={getProgressBarKind(status, monitored, progress)}
|
|
||||||
size={detailedProgressBar ? sizes.MEDIUM : sizes.SMALL}
|
|
||||||
showText={detailedProgressBar}
|
|
||||||
text={text}
|
|
||||||
title={detailedProgressBar ? null : text}
|
|
||||||
width={bannerWidth}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArtistIndexBannerProgressBar.propTypes = {
|
|
||||||
monitored: PropTypes.bool.isRequired,
|
|
||||||
status: PropTypes.string.isRequired,
|
|
||||||
trackCount: PropTypes.number.isRequired,
|
|
||||||
trackFileCount: PropTypes.number.isRequired,
|
|
||||||
bannerWidth: PropTypes.number.isRequired,
|
|
||||||
detailedProgressBar: PropTypes.bool.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ArtistIndexBannerProgressBar;
|
|
@ -0,0 +1,82 @@
|
|||||||
|
$hoverScale: 1.05;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.posterContainer {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
composes: link from 'Components/Link/Link.css';
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
color: $defaultColor;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $defaultColor;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ended {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-width: 0 25px 25px 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent $dangerColor transparent transparent;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
flex: 1 0 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleRow {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@add-mixin truncate;
|
||||||
|
composes: link;
|
||||||
|
|
||||||
|
flex: 1 0 1px;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview {
|
||||||
|
composes: link;
|
||||||
|
|
||||||
|
flex: 0 1 1000px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: $breakpointSmall) {
|
||||||
|
.overview {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,250 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import Truncate from 'react-truncate';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import dimensions from 'Styles/Variables/dimensions';
|
||||||
|
import fonts from 'Styles/Variables/fonts';
|
||||||
|
import IconButton from 'Components/Link/IconButton';
|
||||||
|
import Link from 'Components/Link/Link';
|
||||||
|
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||||
|
import ArtistPoster from 'Artist/ArtistPoster';
|
||||||
|
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
||||||
|
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
||||||
|
import ArtistIndexProgressBar from 'Artist/Index/ProgressBar/ArtistIndexProgressBar';
|
||||||
|
import ArtistIndexOverviewInfo from './ArtistIndexOverviewInfo';
|
||||||
|
import styles from './ArtistIndexOverview.css';
|
||||||
|
|
||||||
|
const columnPadding = parseInt(dimensions.artistIndexColumnPadding);
|
||||||
|
const columnPaddingSmallScreen = parseInt(dimensions.artistIndexColumnPaddingSmallScreen);
|
||||||
|
const defaultFontSize = parseInt(fonts.defaultFontSize);
|
||||||
|
const lineHeight = parseFloat(fonts.lineHeight);
|
||||||
|
|
||||||
|
function calculateHeight(rowHeight, isSmallScreen) {
|
||||||
|
let height = rowHeight - 45;
|
||||||
|
|
||||||
|
if (isSmallScreen) {
|
||||||
|
height -= columnPaddingSmallScreen;
|
||||||
|
} else {
|
||||||
|
height -= columnPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArtistIndexOverview extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isEditArtistModalOpen: false,
|
||||||
|
isDeleteArtistModalOpen: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onEditArtistPress = () => {
|
||||||
|
this.setState({ isEditArtistModalOpen: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditArtistModalClose = () => {
|
||||||
|
this.setState({ isEditArtistModalOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteArtistPress = () => {
|
||||||
|
this.setState({
|
||||||
|
isEditArtistModalOpen: false,
|
||||||
|
isDeleteArtistModalOpen: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteArtistModalClose = () => {
|
||||||
|
this.setState({ isDeleteArtistModalOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
style,
|
||||||
|
id,
|
||||||
|
artistName,
|
||||||
|
overview,
|
||||||
|
monitored,
|
||||||
|
status,
|
||||||
|
nameSlug,
|
||||||
|
nextAiring,
|
||||||
|
trackCount,
|
||||||
|
trackFileCount,
|
||||||
|
images,
|
||||||
|
posterWidth,
|
||||||
|
posterHeight,
|
||||||
|
qualityProfile,
|
||||||
|
overviewOptions,
|
||||||
|
showRelativeDates,
|
||||||
|
shortDateFormat,
|
||||||
|
timeFormat,
|
||||||
|
rowHeight,
|
||||||
|
isSmallScreen,
|
||||||
|
isRefreshingArtist,
|
||||||
|
onRefreshArtistPress,
|
||||||
|
...otherProps
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
isEditArtistModalOpen,
|
||||||
|
isDeleteArtistModalOpen
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
const link = `/artist/${nameSlug}`;
|
||||||
|
|
||||||
|
const elementStyle = {
|
||||||
|
width: `${posterWidth}px`,
|
||||||
|
height: `${posterHeight}px`
|
||||||
|
};
|
||||||
|
|
||||||
|
const height = calculateHeight(rowHeight, isSmallScreen);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container} style={style}>
|
||||||
|
<div className={styles.poster} style={elementStyle}>
|
||||||
|
<div className={styles.posterContainer}>
|
||||||
|
{
|
||||||
|
status === 'ended' &&
|
||||||
|
<div
|
||||||
|
className={styles.ended}
|
||||||
|
title="Ended"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
<Link
|
||||||
|
className={styles.link}
|
||||||
|
style={elementStyle}
|
||||||
|
to={link}
|
||||||
|
>
|
||||||
|
<ArtistPoster
|
||||||
|
className={styles.poster}
|
||||||
|
style={elementStyle}
|
||||||
|
images={images}
|
||||||
|
size={250}
|
||||||
|
lazy={false}
|
||||||
|
overflow={true}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ArtistIndexProgressBar
|
||||||
|
monitored={monitored}
|
||||||
|
status={status}
|
||||||
|
trackCount={trackCount}
|
||||||
|
trackFileCount={trackFileCount}
|
||||||
|
posterWidth={posterWidth}
|
||||||
|
detailedProgressBar={overviewOptions.detailedProgressBar}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.info}>
|
||||||
|
<div className={styles.titleRow}>
|
||||||
|
<Link
|
||||||
|
className={styles.title}
|
||||||
|
to={link}
|
||||||
|
>
|
||||||
|
{artistName}
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className={styles.actions}>
|
||||||
|
<SpinnerIconButton
|
||||||
|
name={icons.REFRESH}
|
||||||
|
title="Refresh artist"
|
||||||
|
isSpinning={isRefreshingArtist}
|
||||||
|
onPress={onRefreshArtistPress}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
name={icons.EDIT}
|
||||||
|
title="Edit Artist"
|
||||||
|
onPress={this.onEditArtistPress}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.details}>
|
||||||
|
<Link
|
||||||
|
className={styles.overview}
|
||||||
|
style={{
|
||||||
|
maxHeight: `${height}px`
|
||||||
|
}}
|
||||||
|
to={link}
|
||||||
|
>
|
||||||
|
<Truncate lines={Math.floor(height / (defaultFontSize * lineHeight))}>
|
||||||
|
{overview}
|
||||||
|
</Truncate>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<ArtistIndexOverviewInfo
|
||||||
|
height={height}
|
||||||
|
nextAiring={nextAiring}
|
||||||
|
qualityProfile={qualityProfile}
|
||||||
|
showRelativeDates={showRelativeDates}
|
||||||
|
shortDateFormat={shortDateFormat}
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
{...overviewOptions}
|
||||||
|
{...otherProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EditArtistModalConnector
|
||||||
|
isOpen={isEditArtistModalOpen}
|
||||||
|
artistId={id}
|
||||||
|
onModalClose={this.onEditArtistModalClose}
|
||||||
|
onDeleteArtistPress={this.onDeleteArtistPress}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DeleteArtistModal
|
||||||
|
isOpen={isDeleteArtistModalOpen}
|
||||||
|
artistId={id}
|
||||||
|
onModalClose={this.onDeleteArtistModalClose}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistIndexOverview.propTypes = {
|
||||||
|
style: PropTypes.object.isRequired,
|
||||||
|
id: PropTypes.number.isRequired,
|
||||||
|
artistName: PropTypes.string.isRequired,
|
||||||
|
overview: PropTypes.string.isRequired,
|
||||||
|
monitored: PropTypes.bool.isRequired,
|
||||||
|
status: PropTypes.string.isRequired,
|
||||||
|
nameSlug: PropTypes.string.isRequired,
|
||||||
|
nextAiring: PropTypes.string,
|
||||||
|
trackCount: PropTypes.number,
|
||||||
|
trackFileCount: PropTypes.number,
|
||||||
|
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
posterWidth: PropTypes.number.isRequired,
|
||||||
|
posterHeight: PropTypes.number.isRequired,
|
||||||
|
rowHeight: PropTypes.number.isRequired,
|
||||||
|
qualityProfile: PropTypes.object.isRequired,
|
||||||
|
overviewOptions: PropTypes.object.isRequired,
|
||||||
|
showRelativeDates: PropTypes.bool.isRequired,
|
||||||
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
|
timeFormat: PropTypes.string.isRequired,
|
||||||
|
isSmallScreen: PropTypes.bool.isRequired,
|
||||||
|
isRefreshingArtist: PropTypes.bool.isRequired,
|
||||||
|
onRefreshArtistPress: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
ArtistIndexOverview.defaultProps = {
|
||||||
|
trackCount: 0,
|
||||||
|
trackFileCount: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArtistIndexOverview;
|
@ -0,0 +1,23 @@
|
|||||||
|
.infos {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 250px;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
flex: 0 0 $artistIndexOverviewInfoRowHeight;
|
||||||
|
margin: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
width: 25px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: $breakpointSmall) {
|
||||||
|
.infos {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||||
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import dimensions from 'Styles/Variables/dimensions';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import styles from './ArtistIndexOverviewInfo.css';
|
||||||
|
|
||||||
|
const infoRowHeight = parseInt(dimensions.artistIndexOverviewInfoRowHeight);
|
||||||
|
|
||||||
|
function isVisible(name, show, value, sortKey, index) {
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return show || sortKey === name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ArtistIndexOverviewInfo(props) {
|
||||||
|
const {
|
||||||
|
height,
|
||||||
|
showQualityProfile,
|
||||||
|
showAdded,
|
||||||
|
showAlbumCount,
|
||||||
|
showPath,
|
||||||
|
showSizeOnDisk,
|
||||||
|
nextAiring,
|
||||||
|
qualityProfile,
|
||||||
|
added,
|
||||||
|
albumCount,
|
||||||
|
path,
|
||||||
|
sizeOnDisk,
|
||||||
|
sortKey,
|
||||||
|
showRelativeDates,
|
||||||
|
shortDateFormat,
|
||||||
|
timeFormat
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
let albums = '1 album';
|
||||||
|
|
||||||
|
if (albumCount === 0) {
|
||||||
|
albums = 'No albums';
|
||||||
|
} else if (albumCount > 1) {
|
||||||
|
albums = `${albumCount} albums`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxRows = Math.floor(height / (infoRowHeight + 4));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.infos}>
|
||||||
|
{
|
||||||
|
!!nextAiring &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Next Airing"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.SCHEDULED}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
getRelativeDate(
|
||||||
|
nextAiring,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates,
|
||||||
|
{
|
||||||
|
timeFormat,
|
||||||
|
timeForToday: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isVisible('qualityProfileId', showQualityProfile, qualityProfile, sortKey) && maxRows > 1 &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Quality Profile"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.PROFILE}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{qualityProfile.name}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isVisible('added', showAdded, added, sortKey) && maxRows > 2 &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Date Added"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.ADD}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
getRelativeDate(
|
||||||
|
added,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates,
|
||||||
|
{
|
||||||
|
timeFormat,
|
||||||
|
timeForToday: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isVisible('albumCount', showAlbumCount, albumCount, sortKey) && maxRows > 3 &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Album Count"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.CIRCLE}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{albums}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isVisible('path', showPath, path, sortKey) && maxRows > 4 &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Path"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.FOLDER}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{path}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isVisible('sizeOnDisk', showSizeOnDisk, sizeOnDisk, sortKey) && maxRows > 5 &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Size on Disk"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.DRIVE}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{formatBytes(sizeOnDisk)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistIndexOverviewInfo.propTypes = {
|
||||||
|
height: PropTypes.number.isRequired,
|
||||||
|
showNetwork: PropTypes.bool.isRequired,
|
||||||
|
showQualityProfile: PropTypes.bool.isRequired,
|
||||||
|
showAdded: PropTypes.bool.isRequired,
|
||||||
|
showAlbumCount: PropTypes.bool.isRequired,
|
||||||
|
showPath: PropTypes.bool.isRequired,
|
||||||
|
showSizeOnDisk: PropTypes.bool.isRequired,
|
||||||
|
nextAiring: PropTypes.string,
|
||||||
|
qualityProfile: PropTypes.object.isRequired,
|
||||||
|
previousAiring: PropTypes.string,
|
||||||
|
added: PropTypes.string,
|
||||||
|
albumCount: PropTypes.number.isRequired,
|
||||||
|
path: PropTypes.string.isRequired,
|
||||||
|
sizeOnDisk: PropTypes.number,
|
||||||
|
sortKey: PropTypes.string.isRequired,
|
||||||
|
showRelativeDates: PropTypes.bool.isRequired,
|
||||||
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
|
timeFormat: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArtistIndexOverviewInfo;
|
@ -0,0 +1,3 @@
|
|||||||
|
.grid {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
@ -0,0 +1,278 @@
|
|||||||
|
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}
|
||||||
|
{...artist}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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;
|
@ -0,0 +1,33 @@
|
|||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import connectSection from 'Store/connectSection';
|
||||||
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
|
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||||
|
import ArtistIndexOverviews from './ArtistIndexOverviews';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state) => state.artistIndex.overviewOptions,
|
||||||
|
createClientSideCollectionSelector(),
|
||||||
|
createUISettingsSelector(),
|
||||||
|
createDimensionsSelector(),
|
||||||
|
(overviewOptions, artist, uiSettings, dimensions) => {
|
||||||
|
return {
|
||||||
|
overviewOptions,
|
||||||
|
showRelativeDates: uiSettings.showRelativeDates,
|
||||||
|
shortDateFormat: uiSettings.shortDateFormat,
|
||||||
|
timeFormat: uiSettings.timeFormat,
|
||||||
|
isSmallScreen: dimensions.isSmallScreen,
|
||||||
|
...artist
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connectSection(
|
||||||
|
createMapStateToProps,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{ withRef: true },
|
||||||
|
{ section: 'artist', uiSection: 'artistIndex' }
|
||||||
|
)(ArtistIndexOverviews);
|
@ -0,0 +1,25 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import ArtistIndexOverviewOptionsModalContentConnector from './ArtistIndexOverviewOptionsModalContentConnector';
|
||||||
|
|
||||||
|
function ArtistIndexOverviewOptionsModal({ isOpen, onModalClose, ...otherProps }) {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
>
|
||||||
|
<ArtistIndexOverviewOptionsModalContentConnector
|
||||||
|
{...otherProps}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistIndexOverviewOptionsModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArtistIndexOverviewOptionsModal;
|
@ -0,0 +1,247 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { inputTypes } from 'Helpers/Props';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import Form from 'Components/Form/Form';
|
||||||
|
import FormGroup from 'Components/Form/FormGroup';
|
||||||
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
|
||||||
|
const posterSizeOptions = [
|
||||||
|
{ key: 'small', value: 'Small' },
|
||||||
|
{ key: 'medium', value: 'Medium' },
|
||||||
|
{ key: 'large', value: 'Large' }
|
||||||
|
];
|
||||||
|
|
||||||
|
class ArtistIndexOverviewOptionsModalContent extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
detailedProgressBar: props.detailedProgressBar,
|
||||||
|
size: props.size,
|
||||||
|
showQualityProfile: props.showQualityProfile,
|
||||||
|
showPreviousAiring: props.showPreviousAiring,
|
||||||
|
showAdded: props.showAdded,
|
||||||
|
showAlbumCount: props.showAlbumCount,
|
||||||
|
showPath: props.showPath,
|
||||||
|
showSizeOnDisk: props.showSizeOnDisk
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
const {
|
||||||
|
detailedProgressBar,
|
||||||
|
size,
|
||||||
|
showQualityProfile,
|
||||||
|
showPreviousAiring,
|
||||||
|
showAdded,
|
||||||
|
showAlbumCount,
|
||||||
|
showPath,
|
||||||
|
showSizeOnDisk
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const state = {};
|
||||||
|
|
||||||
|
if (detailedProgressBar !== prevProps.detailedProgressBar) {
|
||||||
|
state.detailedProgressBar = detailedProgressBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size !== prevProps.size) {
|
||||||
|
state.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showQualityProfile !== prevProps.showQualityProfile) {
|
||||||
|
state.showQualityProfile = showQualityProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showPreviousAiring !== prevProps.showPreviousAiring) {
|
||||||
|
state.showPreviousAiring = showPreviousAiring;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showAdded !== prevProps.showAdded) {
|
||||||
|
state.showAdded = showAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showAlbumCount !== prevProps.showAlbumCount) {
|
||||||
|
state.showAlbumCount = showAlbumCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showPath !== prevProps.showPath) {
|
||||||
|
state.showPath = showPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showSizeOnDisk !== prevProps.showSizeOnDisk) {
|
||||||
|
state.showSizeOnDisk = showSizeOnDisk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_.isEmpty(state)) {
|
||||||
|
this.setState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onChangeOverviewOption = ({ name, value }) => {
|
||||||
|
this.setState({
|
||||||
|
[name]: value
|
||||||
|
}, () => {
|
||||||
|
this.props.onChangeOverviewOption({ [name]: value });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
onModalClose
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
detailedProgressBar,
|
||||||
|
size,
|
||||||
|
showQualityProfile,
|
||||||
|
showPreviousAiring,
|
||||||
|
showAdded,
|
||||||
|
showAlbumCount,
|
||||||
|
showPath,
|
||||||
|
showSizeOnDisk
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
Overview Options
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<Form>
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Poster Size</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.SELECT}
|
||||||
|
name="size"
|
||||||
|
value={size}
|
||||||
|
values={posterSizeOptions}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Detailed Progress Bar</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="detailedProgressBar"
|
||||||
|
value={detailedProgressBar}
|
||||||
|
helpText="Show text on progess bar"
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Quality Profile</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showQualityProfile"
|
||||||
|
value={showQualityProfile}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Previous Airing</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showPreviousAiring"
|
||||||
|
value={showPreviousAiring}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Date Added</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showAdded"
|
||||||
|
value={showAdded}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Season Count</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showAlbumCount"
|
||||||
|
value={showAlbumCount}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Path</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showPath"
|
||||||
|
value={showPath}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Size on Disk</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showSizeOnDisk"
|
||||||
|
value={showSizeOnDisk}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Form>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button
|
||||||
|
onPress={onModalClose}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistIndexOverviewOptionsModalContent.propTypes = {
|
||||||
|
size: PropTypes.string.isRequired,
|
||||||
|
showQualityProfile: PropTypes.bool.isRequired,
|
||||||
|
showPreviousAiring: PropTypes.bool.isRequired,
|
||||||
|
showAdded: PropTypes.bool.isRequired,
|
||||||
|
showAlbumCount: PropTypes.bool.isRequired,
|
||||||
|
showPath: PropTypes.bool.isRequired,
|
||||||
|
showSizeOnDisk: PropTypes.bool.isRequired,
|
||||||
|
detailedProgressBar: PropTypes.bool.isRequired,
|
||||||
|
onChangeOverviewOption: PropTypes.func.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArtistIndexOverviewOptionsModalContent;
|
@ -0,0 +1,23 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { setArtistOverviewOption } from 'Store/Actions/artistIndexActions';
|
||||||
|
import ArtistIndexOverviewOptionsModalContent from './ArtistIndexOverviewOptionsModalContent';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state) => state.artistIndex,
|
||||||
|
(artistIndex) => {
|
||||||
|
return artistIndex.overviewOptions;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
|
return {
|
||||||
|
onChangeOverviewOption(payload) {
|
||||||
|
dispatch(setArtistOverviewOption(payload));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, createMapDispatchToProps)(ArtistIndexOverviewOptionsModalContent);
|
@ -1,14 +0,0 @@
|
|||||||
.progress {
|
|
||||||
composes: container from 'Components/ProgressBar.css';
|
|
||||||
|
|
||||||
border-radius: 0;
|
|
||||||
background-color: #5b5b5b;
|
|
||||||
color: $white;
|
|
||||||
transition: width 200ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progressBar {
|
|
||||||
composes: progressBar from 'Components/ProgressBar.css';
|
|
||||||
|
|
||||||
transition: width 200ms ease;
|
|
||||||
}
|
|
Loading…
Reference in new issue