mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-25 22:36:59 -04:00
Initial Commit Rework
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
||||
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
||||
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
||||
|
||||
class ArtistIndexActionsCell extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isEditArtistModalOpen: false,
|
||||
isDeleteArtistModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onEditSeriesPress = () => {
|
||||
this.setState({ isEditArtistModalOpen: true });
|
||||
}
|
||||
|
||||
onEditSeriesModalClose = () => {
|
||||
this.setState({ isEditArtistModalOpen: false });
|
||||
}
|
||||
|
||||
onDeleteSeriesPress = () => {
|
||||
this.setState({
|
||||
isEditArtistModalOpen: false,
|
||||
isDeleteArtistModalOpen: true
|
||||
});
|
||||
}
|
||||
|
||||
onDeleteSeriesModalClose = () => {
|
||||
this.setState({ isDeleteArtistModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
id,
|
||||
isRefreshingSeries,
|
||||
onRefreshSeriesPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
isEditArtistModalOpen,
|
||||
isDeleteArtistModalOpen
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
{...otherProps}
|
||||
>
|
||||
<SpinnerIconButton
|
||||
name={icons.REFRESH}
|
||||
title="Refresh Artist"
|
||||
isSpinning={isRefreshingSeries}
|
||||
onPress={onRefreshSeriesPress}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
name={icons.EDIT}
|
||||
title="Edit Artist"
|
||||
onPress={this.onEditSeriesPress}
|
||||
/>
|
||||
|
||||
<EditArtistModalConnector
|
||||
isOpen={isEditArtistModalOpen}
|
||||
artistId={id}
|
||||
onModalClose={this.onEditSeriesModalClose}
|
||||
onDeleteSeriesPress={this.onDeleteSeriesPress}
|
||||
/>
|
||||
|
||||
<DeleteArtistModal
|
||||
isOpen={isDeleteArtistModalOpen}
|
||||
artistId={id}
|
||||
onModalClose={this.onDeleteSeriesModalClose}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ArtistIndexActionsCell.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
isRefreshingSeries: PropTypes.bool.isRequired,
|
||||
onRefreshSeriesPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistIndexActionsCell;
|
||||
@@ -0,0 +1,81 @@
|
||||
.status {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 0 60px;
|
||||
}
|
||||
|
||||
.sortName {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 4 0 110px;
|
||||
}
|
||||
|
||||
.network {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 2 0 90px;
|
||||
}
|
||||
|
||||
.qualityProfileId,
|
||||
.languageProfileId {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 1 0 125px;
|
||||
}
|
||||
|
||||
.nextAiring,
|
||||
.previousAiring,
|
||||
.added {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 0 180px;
|
||||
}
|
||||
|
||||
.albumCount {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 0 100px;
|
||||
}
|
||||
|
||||
.trackProgress,
|
||||
.latestSeason {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 0 150px;
|
||||
}
|
||||
|
||||
.trackCount {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 0 120px;
|
||||
}
|
||||
|
||||
.path {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 1 0 150px;
|
||||
}
|
||||
|
||||
.sizeOnDisk {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 0 110px;
|
||||
}
|
||||
|
||||
.tags {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 1 0 60px;
|
||||
}
|
||||
|
||||
.useSceneNumbering {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 0 145px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 0 70px;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import VirtualTableHeader from 'Components/Table/VirtualTableHeader';
|
||||
import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell';
|
||||
import TableOptionsModal from 'Components/Table/TableOptions/TableOptionsModal';
|
||||
import styles from './ArtistIndexHeader.css';
|
||||
|
||||
class ArtistIndexHeader extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isTableOptionsModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onTableOptionsPress = () => {
|
||||
this.setState({ isTableOptionsModalOpen: true });
|
||||
}
|
||||
|
||||
onTableOptionsModalClose = () => {
|
||||
this.setState({ isTableOptionsModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
columns,
|
||||
onTableOptionChange,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<VirtualTableHeader>
|
||||
{
|
||||
columns.map((column) => {
|
||||
const {
|
||||
name,
|
||||
label,
|
||||
isSortable,
|
||||
isVisible
|
||||
} = column;
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name === 'actions') {
|
||||
return (
|
||||
<VirtualTableHeaderCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
name={name}
|
||||
isSortable={false}
|
||||
{...otherProps}
|
||||
>
|
||||
<IconButton
|
||||
name={icons.ADVANCED_SETTINGS}
|
||||
onPress={this.onTableOptionsPress}
|
||||
/>
|
||||
</VirtualTableHeaderCell>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<VirtualTableHeaderCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
name={name}
|
||||
isSortable={isSortable}
|
||||
{...otherProps}
|
||||
>
|
||||
{label}
|
||||
</VirtualTableHeaderCell>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
<TableOptionsModal
|
||||
isOpen={this.state.isTableOptionsModalOpen}
|
||||
columns={columns}
|
||||
onTableOptionChange={onTableOptionChange}
|
||||
onModalClose={this.onTableOptionsModalClose}
|
||||
/>
|
||||
</VirtualTableHeader>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ArtistIndexHeader.propTypes = {
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onTableOptionChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistIndexHeader;
|
||||
@@ -0,0 +1,13 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { setArtistTableOption } from 'Store/Actions/artistIndexActions';
|
||||
import ArtistIndexHeader from './ArtistIndexHeader';
|
||||
|
||||
function createMapDispatchToProps(dispatch, props) {
|
||||
return {
|
||||
onTableOptionChange(payload) {
|
||||
dispatch(setArtistTableOption(payload));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(undefined, createMapDispatchToProps)(ArtistIndexHeader);
|
||||
@@ -0,0 +1,90 @@
|
||||
.status {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 0 0 60px;
|
||||
}
|
||||
|
||||
.sortName {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 4 0 110px;
|
||||
}
|
||||
|
||||
.network {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 2 0 90px;
|
||||
}
|
||||
|
||||
.qualityProfileId,
|
||||
.languageProfileId {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 1 0 125px;
|
||||
}
|
||||
|
||||
.nextAiring,
|
||||
.previousAiring,
|
||||
.added {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 0 0 180px;
|
||||
}
|
||||
|
||||
.albumCount {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 0 0 100px;
|
||||
}
|
||||
|
||||
.trackProgress,
|
||||
.latestSeason {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex: 0 0 150px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.trackCount {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 0 0 120px;
|
||||
}
|
||||
|
||||
.path {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 1 0 150px;
|
||||
}
|
||||
|
||||
.sizeOnDisk {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 0 0 110px;
|
||||
}
|
||||
|
||||
.tags {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 1 0 60px;
|
||||
}
|
||||
|
||||
.useSceneNumbering {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 0 0 145px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 0 0 70px;
|
||||
}
|
||||
|
||||
.checkInput {
|
||||
composes: input from 'Components/Form/CheckInput.css';
|
||||
|
||||
margin-top: 0;
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import getProgressBarKind from 'Utilities/Series/getProgressBarKind';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||
import ProgressBar from 'Components/ProgressBar';
|
||||
import TagListConnector from 'Components/TagListConnector';
|
||||
// import CheckInput from 'Components/Form/CheckInput';
|
||||
import VirtualTableRow from 'Components/Table/VirtualTableRow';
|
||||
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import ArtistNameLink from 'Artist/ArtistNameLink';
|
||||
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
||||
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
||||
import ArtistStatusCell from './ArtistStatusCell';
|
||||
import styles from './ArtistIndexRow.css';
|
||||
|
||||
class ArtistIndexRow extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isEditArtistModalOpen: false,
|
||||
isDeleteArtistModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
onEditSeriesPress = () => {
|
||||
this.setState({ isEditArtistModalOpen: true });
|
||||
}
|
||||
|
||||
onEditSeriesModalClose = () => {
|
||||
this.setState({ isEditArtistModalOpen: false });
|
||||
}
|
||||
|
||||
onDeleteSeriesPress = () => {
|
||||
this.setState({
|
||||
isEditArtistModalOpen: false,
|
||||
isDeleteArtistModalOpen: true
|
||||
});
|
||||
}
|
||||
|
||||
onDeleteSeriesModalClose = () => {
|
||||
this.setState({ isDeleteArtistModalOpen: false });
|
||||
}
|
||||
|
||||
onUseSceneNumberingChange = () => {
|
||||
// Mock handler to satisfy `onChange` being required for `CheckInput`.
|
||||
//
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
style,
|
||||
id,
|
||||
monitored,
|
||||
status,
|
||||
artistName,
|
||||
nameSlug,
|
||||
network,
|
||||
qualityProfile,
|
||||
languageProfile,
|
||||
nextAiring,
|
||||
previousAiring,
|
||||
added,
|
||||
albumCount,
|
||||
trackCount,
|
||||
trackFileCount,
|
||||
totalTrackCount,
|
||||
latestSeason,
|
||||
path,
|
||||
sizeOnDisk,
|
||||
tags,
|
||||
// useSceneNumbering,
|
||||
columns,
|
||||
isRefreshingSeries,
|
||||
onRefreshSeriesPress
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
isEditArtistModalOpen,
|
||||
isDeleteArtistModalOpen
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<VirtualTableRow style={style}>
|
||||
{
|
||||
columns.map((column) => {
|
||||
const {
|
||||
name,
|
||||
isVisible
|
||||
} = column;
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name === 'status') {
|
||||
return (
|
||||
<ArtistStatusCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
monitored={monitored}
|
||||
status={status}
|
||||
component={VirtualTableRowCell}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'sortName') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
<ArtistNameLink
|
||||
nameSlug={nameSlug}
|
||||
artistName={artistName}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'network') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
{network}
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'qualityProfileId') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
{qualityProfile.name}
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'languageProfileId') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
{languageProfile.name}
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'nextAiring') {
|
||||
return (
|
||||
<RelativeDateCellConnector
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
date={nextAiring}
|
||||
component={VirtualTableRowCell}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'previousAiring') {
|
||||
return (
|
||||
<RelativeDateCellConnector
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
date={previousAiring}
|
||||
component={VirtualTableRowCell}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'added') {
|
||||
return (
|
||||
<RelativeDateCellConnector
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
date={added}
|
||||
component={VirtualTableRowCell}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'albumCount') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
{albumCount}
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'trackProgress') {
|
||||
const progress = trackCount ? trackFileCount / trackCount * 100 : 100;
|
||||
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
<ProgressBar
|
||||
progress={progress}
|
||||
kind={getProgressBarKind(status, monitored, progress)}
|
||||
showText={true}
|
||||
text={`${trackFileCount} / ${trackCount}`}
|
||||
title={`${trackFileCount} / ${trackCount} (Total: ${totalTrackCount})`}
|
||||
width={125}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'latestSeason') {
|
||||
const seasonStatistics = latestSeason.statistics;
|
||||
const progress = seasonStatistics.episodeCount ? seasonStatistics.episodeFileCount / seasonStatistics.episodeCount * 100 : 100;
|
||||
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
<ProgressBar
|
||||
progress={progress}
|
||||
kind={getProgressBarKind(status, monitored, progress)}
|
||||
showText={true}
|
||||
text={`${seasonStatistics.episodeFileCount} / ${seasonStatistics.episodeCount}`}
|
||||
title={`${seasonStatistics.episodeFileCount} / ${seasonStatistics.episodeCount} (Total: ${seasonStatistics.totalEpisodeCount})`}
|
||||
width={125}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'trackCount') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
{totalTrackCount}
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'path') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
{path}
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'sizeOnDisk') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
{formatBytes(sizeOnDisk)}
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'tags') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
<TagListConnector
|
||||
tags={tags}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
// if (name === 'useSceneNumbering') {
|
||||
// return (
|
||||
// <VirtualTableRowCell
|
||||
// key={name}
|
||||
// className={styles[name]}
|
||||
// >
|
||||
// <CheckInput
|
||||
// className={styles.checkInput}
|
||||
// name="useSceneNumbering"
|
||||
// value={useSceneNumbering}
|
||||
// isDisabled={true}
|
||||
// onChange={this.onUseSceneNumberingChange}
|
||||
// />
|
||||
// </VirtualTableRowCell>
|
||||
// );
|
||||
// }
|
||||
|
||||
if (name === 'actions') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={name}
|
||||
className={styles[name]}
|
||||
>
|
||||
<SpinnerIconButton
|
||||
name={icons.REFRESH}
|
||||
title="Refresh Artist"
|
||||
isSpinning={isRefreshingSeries}
|
||||
onPress={onRefreshSeriesPress}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
name={icons.EDIT}
|
||||
title="Edit Artist"
|
||||
onPress={this.onEditSeriesPress}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
}
|
||||
|
||||
<EditArtistModalConnector
|
||||
isOpen={isEditArtistModalOpen}
|
||||
artistId={id}
|
||||
onModalClose={this.onEditSeriesModalClose}
|
||||
onDeleteSeriesPress={this.onDeleteSeriesPress}
|
||||
/>
|
||||
|
||||
<DeleteArtistModal
|
||||
isOpen={isDeleteArtistModalOpen}
|
||||
artistId={id}
|
||||
onModalClose={this.onDeleteSeriesModalClose}
|
||||
/>
|
||||
</VirtualTableRow>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ArtistIndexRow.propTypes = {
|
||||
style: PropTypes.object.isRequired,
|
||||
id: PropTypes.number.isRequired,
|
||||
monitored: PropTypes.bool.isRequired,
|
||||
status: PropTypes.string.isRequired,
|
||||
artistName: PropTypes.string.isRequired,
|
||||
nameSlug: PropTypes.string.isRequired,
|
||||
network: PropTypes.string,
|
||||
qualityProfile: PropTypes.object.isRequired,
|
||||
languageProfile: PropTypes.object.isRequired,
|
||||
nextAiring: PropTypes.string,
|
||||
previousAiring: PropTypes.string,
|
||||
added: PropTypes.string,
|
||||
albumCount: PropTypes.number.isRequired,
|
||||
trackCount: PropTypes.number,
|
||||
trackFileCount: PropTypes.number,
|
||||
totalTrackCount: PropTypes.number,
|
||||
latestSeason: PropTypes.object,
|
||||
path: PropTypes.string.isRequired,
|
||||
sizeOnDisk: PropTypes.number,
|
||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
// useSceneNumbering: PropTypes.bool.isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
isRefreshingSeries: PropTypes.bool.isRequired,
|
||||
onRefreshSeriesPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
ArtistIndexRow.defaultProps = {
|
||||
trackCount: 0,
|
||||
trackFileCount: 0
|
||||
};
|
||||
|
||||
export default ArtistIndexRow;
|
||||
@@ -0,0 +1,5 @@
|
||||
.tableContainer {
|
||||
composes: tableContainer from 'Components/Table/VirtualTable.css';
|
||||
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { sortDirections } from 'Helpers/Props';
|
||||
import VirtualTable from 'Components/Table/VirtualTable';
|
||||
import ArtistIndexItemConnector from 'Artist/Index/ArtistIndexItemConnector';
|
||||
import ArtistIndexHeaderConnector from './ArtistIndexHeaderConnector';
|
||||
import ArtistIndexRow from './ArtistIndexRow';
|
||||
import styles from './ArtistIndexTable.css';
|
||||
|
||||
class ArtistIndexTable extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this._table = null;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
columns,
|
||||
filterKey,
|
||||
filterValue,
|
||||
sortKey,
|
||||
sortDirection
|
||||
} = this.props;
|
||||
|
||||
if (prevProps.columns !== columns ||
|
||||
prevProps.filterKey !== filterKey ||
|
||||
prevProps.filterValue !== filterValue ||
|
||||
prevProps.sortKey !== sortKey ||
|
||||
prevProps.sortDirection !== sortDirection
|
||||
) {
|
||||
this._table.forceUpdateGrid();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
scrollToFirstCharacter(character) {
|
||||
const items = this.props.items;
|
||||
|
||||
const row = _.findIndex(items, (item) => {
|
||||
const firstCharacter = item.sortTitle.charAt(0);
|
||||
|
||||
if (character === '#') {
|
||||
return !isNaN(firstCharacter);
|
||||
}
|
||||
|
||||
return firstCharacter === character;
|
||||
});
|
||||
|
||||
if (row != null) {
|
||||
this._table.scrollToRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
setTableRef = (ref) => {
|
||||
this._table = ref;
|
||||
}
|
||||
|
||||
rowRenderer = ({ key, rowIndex, style }) => {
|
||||
const {
|
||||
items,
|
||||
columns
|
||||
} = this.props;
|
||||
|
||||
const series = items[rowIndex];
|
||||
|
||||
return (
|
||||
<ArtistIndexItemConnector
|
||||
key={key}
|
||||
component={ArtistIndexRow}
|
||||
style={style}
|
||||
columns={columns}
|
||||
{...series}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
columns,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
isSmallScreen,
|
||||
scrollTop,
|
||||
contentBody,
|
||||
onSortPress,
|
||||
onRender,
|
||||
onScroll
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<VirtualTable
|
||||
ref={this.setTableRef}
|
||||
className={styles.tableContainer}
|
||||
items={items}
|
||||
scrollTop={scrollTop}
|
||||
contentBody={contentBody}
|
||||
isSmallScreen={isSmallScreen}
|
||||
rowHeight={38}
|
||||
overscanRowCount={2}
|
||||
rowRenderer={this.rowRenderer}
|
||||
header={
|
||||
<ArtistIndexHeaderConnector
|
||||
columns={columns}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
onSortPress={onSortPress}
|
||||
/>
|
||||
}
|
||||
onRender={onRender}
|
||||
onScroll={onScroll}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ArtistIndexTable.propTypes = {
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
columns: 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),
|
||||
scrollTop: PropTypes.number.isRequired,
|
||||
contentBody: PropTypes.object.isRequired,
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
onSortPress: PropTypes.func.isRequired,
|
||||
onRender: PropTypes.func.isRequired,
|
||||
onScroll: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistIndexTable;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import connectSection from 'Store/connectSection';
|
||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import { setArtistSort } from 'Store/Actions/artistIndexActions';
|
||||
import ArtistIndexTable from './ArtistIndexTable';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.app.dimensions,
|
||||
createClientSideCollectionSelector(),
|
||||
(dimensions, series) => {
|
||||
return {
|
||||
isSmallScreen: dimensions.isSmallScreen,
|
||||
...series
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createMapDispatchToProps(dispatch, props) {
|
||||
return {
|
||||
onSortPress(sortKey) {
|
||||
dispatch(setArtistSort({ sortKey }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default connectSection(
|
||||
createMapStateToProps,
|
||||
createMapDispatchToProps,
|
||||
undefined,
|
||||
{ withRef: true },
|
||||
{ section: 'series', uiSection: 'seriesIndex' }
|
||||
)(ArtistIndexTable);
|
||||
@@ -0,0 +1,9 @@
|
||||
.status {
|
||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.statusIcon {
|
||||
width: 20px;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import Icon from 'Components/Icon';
|
||||
import VirtualTableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import styles from './ArtistStatusCell.css';
|
||||
|
||||
function ArtistStatusCell(props) {
|
||||
const {
|
||||
className,
|
||||
monitored,
|
||||
status,
|
||||
component: Component,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Component
|
||||
className={className}
|
||||
{...otherProps}
|
||||
>
|
||||
<Icon
|
||||
className={styles.statusIcon}
|
||||
name={monitored ? icons.MONITORED : icons.UNMONITORED}
|
||||
title={monitored ? 'Artist is monitored' : 'Artist is unmonitored'}
|
||||
/>
|
||||
|
||||
<Icon
|
||||
className={styles.statusIcon}
|
||||
name={status === 'ended' ? icons.SERIES_ENDED : icons.SERIES_CONTINUING}
|
||||
title={status === 'ended' ? 'Ended' : 'Continuing'}
|
||||
|
||||
/>
|
||||
</Component>
|
||||
);
|
||||
}
|
||||
|
||||
ArtistStatusCell.propTypes = {
|
||||
className: PropTypes.string.isRequired,
|
||||
monitored: PropTypes.bool.isRequired,
|
||||
status: PropTypes.string.isRequired,
|
||||
component: PropTypes.func
|
||||
};
|
||||
|
||||
ArtistStatusCell.defaultProps = {
|
||||
className: styles.status,
|
||||
component: VirtualTableRowCell
|
||||
};
|
||||
|
||||
export default ArtistStatusCell;
|
||||
@@ -0,0 +1,138 @@
|
||||
import React from 'react';
|
||||
import getProgressBarKind from 'Utilities/Series/getProgressBarKind';
|
||||
import ProgressBar from 'Components/ProgressBar';
|
||||
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
|
||||
import ArtistNameLink from 'Artist/ArtistNameLink';
|
||||
import ArtistIndexItemConnector from 'Artist/Index/ArtistIndexItemConnector';
|
||||
import ArtistIndexActionsCell from './ArtistIndexActionsCell';
|
||||
import ArtistStatusCell from './ArtistStatusCell';
|
||||
|
||||
export default function artistIndexCellRenderers(cellProps) {
|
||||
const {
|
||||
cellKey,
|
||||
dataKey,
|
||||
rowData,
|
||||
...otherProps
|
||||
} = cellProps;
|
||||
|
||||
const {
|
||||
id,
|
||||
monitored,
|
||||
status,
|
||||
name,
|
||||
nameSlug,
|
||||
network,
|
||||
qualityProfileId,
|
||||
nextAiring,
|
||||
previousAiring,
|
||||
albumCount,
|
||||
trackCount,
|
||||
trackFileCount
|
||||
} = rowData;
|
||||
|
||||
const progress = trackCount ? trackFileCount / trackCount * 100 : 100;
|
||||
|
||||
if (dataKey === 'status') {
|
||||
return (
|
||||
<ArtistStatusCell
|
||||
key={cellKey}
|
||||
monitored={monitored}
|
||||
status={status}
|
||||
component={VirtualTableRowCell}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (dataKey === 'sortTitle') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={cellKey}
|
||||
{...otherProps}
|
||||
>
|
||||
<ArtistNameLink
|
||||
nameSlug={nameSlug}
|
||||
name={name}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
if (dataKey === 'network') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={cellKey}
|
||||
{...otherProps}
|
||||
>
|
||||
{network}
|
||||
</VirtualTableRowCell>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
if (dataKey === 'qualityProfileId') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={cellKey}
|
||||
{...otherProps}
|
||||
>
|
||||
<QualityProfileNameConnector
|
||||
qualityProfileId={qualityProfileId}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (dataKey === 'nextAiring') {
|
||||
return (
|
||||
<RelativeDateCellConnector
|
||||
key={cellKey}
|
||||
date={nextAiring}
|
||||
component={VirtualTableRowCell}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (dataKey === 'seasonCount') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={cellKey}
|
||||
{...otherProps}
|
||||
>
|
||||
{albumCount}
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (dataKey === 'episodeProgress') {
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
key={cellKey}
|
||||
{...otherProps}
|
||||
>
|
||||
<ProgressBar
|
||||
progress={progress}
|
||||
kind={getProgressBarKind(status, monitored, progress)}
|
||||
showText={true}
|
||||
text={`${trackFileCount} / ${trackCount}`}
|
||||
width={125}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (dataKey === 'actions') {
|
||||
return (
|
||||
<ArtistIndexItemConnector
|
||||
key={cellKey}
|
||||
component={ArtistIndexActionsCell}
|
||||
id={id}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user