mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-20 21:43:33 -07:00
[UI Work] Interactive Import, More Artist Detail
This commit is contained in:
parent
0054226307
commit
f05332cf6e
42 changed files with 486 additions and 549 deletions
|
@ -71,7 +71,7 @@ class ImportArtistRowConnector extends Component {
|
|||
<ImportArtistRow
|
||||
{...this.props}
|
||||
onInputChange={this.onInputChange}
|
||||
onSeriesSelect={this.onSeriesSelect}
|
||||
onArtistSelect={this.onArtistSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -99,10 +99,10 @@ class ImportArtistSelectArtist extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
onSeriesSelect = (foreignArtistId) => {
|
||||
onArtistSelect = (foreignArtistId) => {
|
||||
this.setState({ isOpen: false });
|
||||
|
||||
this.props.onSeriesSelect(foreignArtistId);
|
||||
this.props.onArtistSelect(foreignArtistId);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -117,7 +117,7 @@ class ImportArtistSelectArtist extends Component {
|
|||
error,
|
||||
items,
|
||||
queued,
|
||||
onSeriesSelect
|
||||
onArtistSelect
|
||||
} = this.props;
|
||||
|
||||
const errorMessage = error &&
|
||||
|
@ -233,7 +233,7 @@ class ImportArtistSelectArtist extends Component {
|
|||
overview={item.overview}
|
||||
// year={item.year}
|
||||
// network={item.network}
|
||||
onPress={this.onSeriesSelect}
|
||||
onPress={this.onArtistSelect}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
@ -257,7 +257,7 @@ ImportArtistSelectArtist.propTypes = {
|
|||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
queued: PropTypes.bool.isRequired,
|
||||
onSearchInputChange: PropTypes.func.isRequired,
|
||||
onSeriesSelect: PropTypes.func.isRequired
|
||||
onArtistSelect: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
ImportArtistSelectArtist.defaultProps = {
|
||||
|
|
|
@ -33,7 +33,7 @@ class ImportArtistSelectArtistConnector extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
onSeriesSelect = (foreignArtistId) => {
|
||||
onArtistSelect = (foreignArtistId) => {
|
||||
const {
|
||||
id,
|
||||
items
|
||||
|
@ -53,7 +53,7 @@ class ImportArtistSelectArtistConnector extends Component {
|
|||
<ImportArtistSelectArtist
|
||||
{...this.props}
|
||||
onSearchInputChange={this.onSearchInputChange}
|
||||
onSeriesSelect={this.onSeriesSelect}
|
||||
onArtistSelect={this.onArtistSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ function App({ store, history }) {
|
|||
<PageConnector>
|
||||
<Switch>
|
||||
{/*
|
||||
Series
|
||||
Artist
|
||||
*/}
|
||||
|
||||
<Route
|
||||
|
|
|
@ -112,7 +112,7 @@ class SeriesDetails extends Component {
|
|||
this.setState(getExpandedState(selectAll(expandedState, !allExpanded)));
|
||||
}
|
||||
|
||||
onExpandPress = (seasonNumber, isExpanded) => {
|
||||
onExpandPress = (albumId, isExpanded) => {
|
||||
this.setState((state) => {
|
||||
const convertedState = {
|
||||
allSelected: state.allExpanded,
|
||||
|
@ -120,7 +120,7 @@ class SeriesDetails extends Component {
|
|||
selectedState: state.expandedState
|
||||
};
|
||||
|
||||
const newState = toggleSelected(convertedState, [], seasonNumber, isExpanded, false);
|
||||
const newState = toggleSelected(convertedState, [], albumId, isExpanded, false);
|
||||
|
||||
return getExpandedState(newState);
|
||||
});
|
||||
|
@ -136,10 +136,9 @@ class SeriesDetails extends Component {
|
|||
tvMazeId,
|
||||
imdbId,
|
||||
artistName,
|
||||
runtime,
|
||||
ratings,
|
||||
sizeOnDisk,
|
||||
episodeFileCount,
|
||||
trackFileCount,
|
||||
qualityProfileId,
|
||||
monitored,
|
||||
status,
|
||||
|
@ -175,10 +174,10 @@ class SeriesDetails extends Component {
|
|||
|
||||
let episodeFilesCountMessage = 'No episode files';
|
||||
|
||||
if (episodeFileCount === 1) {
|
||||
if (trackFileCount === 1) {
|
||||
episodeFilesCountMessage = '1 episode file';
|
||||
} else if (episodeFileCount > 1) {
|
||||
episodeFilesCountMessage = `${episodeFileCount} episode files`;
|
||||
} else if (trackFileCount > 1) {
|
||||
episodeFilesCountMessage = `${trackFileCount} episode files`;
|
||||
}
|
||||
|
||||
let expandIcon = icons.EXPAND_INDETERMINATE;
|
||||
|
@ -310,13 +309,6 @@ class SeriesDetails extends Component {
|
|||
|
||||
<div className={styles.details}>
|
||||
<div>
|
||||
{
|
||||
!!runtime &&
|
||||
<span className={styles.runtime}>
|
||||
{runtime} Minutes
|
||||
</span>
|
||||
}
|
||||
|
||||
<HeartRating
|
||||
rating={ratings.value}
|
||||
iconSize={20}
|
||||
|
@ -491,10 +483,12 @@ class SeriesDetails extends Component {
|
|||
albums.slice(0).reverse().map((season) => {
|
||||
return (
|
||||
<SeriesDetailsSeasonConnector
|
||||
key={season.seasonNumber}
|
||||
key={season.id}
|
||||
artistId={id}
|
||||
albumId={season.id}
|
||||
statistics={season.statistics}
|
||||
{...season}
|
||||
isExpanded={expandedState[season.seasonNumber]}
|
||||
isExpanded={expandedState[season.id]}
|
||||
onExpandPress={this.onExpandPress}
|
||||
/>
|
||||
);
|
||||
|
@ -548,10 +542,9 @@ SeriesDetails.propTypes = {
|
|||
tvMazeId: PropTypes.number,
|
||||
imdbId: PropTypes.string,
|
||||
artistName: PropTypes.string.isRequired,
|
||||
runtime: PropTypes.number.isRequired,
|
||||
ratings: PropTypes.object.isRequired,
|
||||
sizeOnDisk: PropTypes.number.isRequired,
|
||||
episodeFileCount: PropTypes.number,
|
||||
trackFileCount: PropTypes.number,
|
||||
qualityProfileId: PropTypes.number.isRequired,
|
||||
monitored: PropTypes.bool.isRequired,
|
||||
status: PropTypes.string.isRequired,
|
||||
|
|
|
@ -16,30 +16,16 @@ function SeriesDetailsLinks(props) {
|
|||
<div className={styles.links}>
|
||||
<Link
|
||||
className={styles.link}
|
||||
to={`http://www.thetvdb.com/?tab=series&id=${foreignArtistId}`}
|
||||
to={`https://musicbrainz.org/artist/${foreignArtistId}`}
|
||||
>
|
||||
<Label
|
||||
className={styles.linkLabel}
|
||||
kind={kinds.INFO}
|
||||
size={sizes.LARGE}
|
||||
>
|
||||
The TVDB
|
||||
Musicbrainz
|
||||
</Label>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
className={styles.link}
|
||||
to={`http://trakt.tv/search/tvdb/${foreignArtistId}?id_type=show`}
|
||||
>
|
||||
<Label
|
||||
className={styles.linkLabel}
|
||||
kind={kinds.INFO}
|
||||
size={sizes.LARGE}
|
||||
>
|
||||
Trakt
|
||||
</Label>
|
||||
</Link>
|
||||
|
||||
{
|
||||
!!tvMazeId &&
|
||||
<Link
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 1 300px;
|
||||
flex: 0 1 600px;
|
||||
}
|
||||
|
||||
.left,
|
||||
|
|
|
@ -23,30 +23,6 @@ import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnecto
|
|||
import EpisodeRowConnector from './EpisodeRowConnector';
|
||||
import styles from './SeriesDetailsSeason.css';
|
||||
|
||||
function getSeasonStatistics(episodes) {
|
||||
let episodeCount = 0;
|
||||
let episodeFileCount = 0;
|
||||
let totalEpisodeCount = 0;
|
||||
|
||||
episodes.forEach((episode) => {
|
||||
if (episode.episodeFileId || (episode.monitored && isBefore(episode.airDateUtc))) {
|
||||
episodeCount++;
|
||||
}
|
||||
|
||||
if (episode.episodeFileId) {
|
||||
episodeFileCount++;
|
||||
}
|
||||
|
||||
totalEpisodeCount++;
|
||||
});
|
||||
|
||||
return {
|
||||
episodeCount,
|
||||
episodeFileCount,
|
||||
totalEpisodeCount
|
||||
};
|
||||
}
|
||||
|
||||
function getEpisodeCountKind(monitored, episodeFileCount, episodeCount) {
|
||||
if (episodeFileCount === episodeCount && episodeCount > 0) {
|
||||
return kinds.SUCCESS;
|
||||
|
@ -89,7 +65,7 @@ class SeriesDetailsSeason extends Component {
|
|||
|
||||
_expandByDefault() {
|
||||
const {
|
||||
seasonNumber,
|
||||
albumId,
|
||||
onExpandPress,
|
||||
items
|
||||
} = this.props;
|
||||
|
@ -99,7 +75,7 @@ class SeriesDetailsSeason extends Component {
|
|||
isAfter(item.airDateUtc, { days: -30 });
|
||||
});
|
||||
|
||||
onExpandPress(seasonNumber, expand && seasonNumber > 0);
|
||||
onExpandPress(albumId, expand && albumId > 0);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -123,11 +99,11 @@ class SeriesDetailsSeason extends Component {
|
|||
|
||||
onExpandPress = () => {
|
||||
const {
|
||||
seasonNumber,
|
||||
albumId,
|
||||
isExpanded
|
||||
} = this.props;
|
||||
|
||||
this.props.onExpandPress(seasonNumber, !isExpanded);
|
||||
this.props.onExpandPress(albumId, !isExpanded);
|
||||
}
|
||||
|
||||
onMonitorEpisodePress = (episodeId, monitored, { shiftKey }) => {
|
||||
|
@ -155,7 +131,10 @@ class SeriesDetailsSeason extends Component {
|
|||
const {
|
||||
artistId,
|
||||
monitored,
|
||||
seasonNumber,
|
||||
title,
|
||||
releaseDate,
|
||||
albumId,
|
||||
statistics,
|
||||
items,
|
||||
columns,
|
||||
isSaving,
|
||||
|
@ -169,10 +148,10 @@ class SeriesDetailsSeason extends Component {
|
|||
} = this.props;
|
||||
|
||||
const {
|
||||
episodeCount,
|
||||
episodeFileCount,
|
||||
totalEpisodeCount
|
||||
} = getSeasonStatistics(items);
|
||||
trackCount,
|
||||
trackFileCount,
|
||||
totalTrackCount
|
||||
} = statistics;
|
||||
|
||||
const {
|
||||
isOrganizeModalOpen,
|
||||
|
@ -194,22 +173,22 @@ class SeriesDetailsSeason extends Component {
|
|||
/>
|
||||
|
||||
{
|
||||
seasonNumber === 0 ?
|
||||
albumId === 0 ?
|
||||
<span className={styles.seasonNumber}>
|
||||
Specials
|
||||
</span> :
|
||||
<span className={styles.seasonNumber}>
|
||||
Season {seasonNumber}
|
||||
{title}
|
||||
</span>
|
||||
}
|
||||
|
||||
<Label
|
||||
title={`${totalEpisodeCount} episodes total. ${episodeFileCount} episodes with files.`}
|
||||
kind={getEpisodeCountKind(monitored, episodeFileCount, episodeCount)}
|
||||
title={`${totalTrackCount} tracks total. ${trackFileCount} tracks with files.`}
|
||||
kind={getEpisodeCountKind(monitored, trackFileCount, trackCount)}
|
||||
size={sizes.LARGE}
|
||||
>
|
||||
{
|
||||
<span>{episodeFileCount} / {episodeCount}</span>
|
||||
<span>{trackFileCount} / {trackCount}</span>
|
||||
}
|
||||
</Label>
|
||||
</div>
|
||||
|
@ -218,12 +197,7 @@ class SeriesDetailsSeason extends Component {
|
|||
className={styles.expandButton}
|
||||
onPress={this.onExpandPress}
|
||||
>
|
||||
<Icon
|
||||
className={styles.expandButtonIcon}
|
||||
name={isExpanded ? icons.COLLAPSE : icons.EXPAND}
|
||||
title={isExpanded ? 'Hide episodes' : 'Show episodes'}
|
||||
size={24}
|
||||
/>
|
||||
|
||||
{
|
||||
!isSmallScreen &&
|
||||
<span> </span>
|
||||
|
@ -277,7 +251,7 @@ class SeriesDetailsSeason extends Component {
|
|||
name={icons.EPISODE_FILE}
|
||||
/>
|
||||
|
||||
Manage Episodes
|
||||
Manage Tracks
|
||||
</MenuItem>
|
||||
</MenuContent>
|
||||
</Menu> :
|
||||
|
@ -286,7 +260,7 @@ class SeriesDetailsSeason extends Component {
|
|||
<SpinnerIconButton
|
||||
className={styles.actionButton}
|
||||
name={icons.SEARCH}
|
||||
title="Search for monitored episodes in this seasons"
|
||||
title="Search for album"
|
||||
size={24}
|
||||
isSpinning={isSearching}
|
||||
onPress={onSearchPress}
|
||||
|
@ -295,7 +269,7 @@ class SeriesDetailsSeason extends Component {
|
|||
<IconButton
|
||||
className={styles.actionButton}
|
||||
name={icons.ORGANIZE}
|
||||
title="Preview rename for this season"
|
||||
title="Preview rename for this album"
|
||||
size={24}
|
||||
onPress={this.onOrganizePress}
|
||||
/>
|
||||
|
@ -303,7 +277,7 @@ class SeriesDetailsSeason extends Component {
|
|||
<IconButton
|
||||
className={styles.actionButton}
|
||||
name={icons.EPISODE_FILE}
|
||||
title="Manage episode files in this series"
|
||||
title="Manage track files in this artist"
|
||||
size={24}
|
||||
onPress={this.onManageEpisodesPress}
|
||||
/>
|
||||
|
@ -312,59 +286,17 @@ class SeriesDetailsSeason extends Component {
|
|||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{
|
||||
isExpanded &&
|
||||
<div className={styles.episodes}>
|
||||
{
|
||||
items.length ?
|
||||
<Table
|
||||
columns={columns}
|
||||
onTableOptionChange={onTableOptionChange}
|
||||
>
|
||||
<TableBody>
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<EpisodeRowConnector
|
||||
key={item.id}
|
||||
columns={columns}
|
||||
{...item}
|
||||
onMonitorEpisodePress={this.onMonitorEpisodePress}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</TableBody>
|
||||
</Table> :
|
||||
|
||||
<div className={styles.noEpisodes}>
|
||||
No episodes in this season
|
||||
</div>
|
||||
}
|
||||
<div className={styles.collapseButtonContainer}>
|
||||
<IconButton
|
||||
name={icons.COLLAPSE}
|
||||
size={20}
|
||||
title="Hide episodes"
|
||||
onPress={this.onExpandPress}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<OrganizePreviewModalConnector
|
||||
isOpen={isOrganizeModalOpen}
|
||||
artistId={artistId}
|
||||
seasonNumber={seasonNumber}
|
||||
albumId={albumId}
|
||||
onModalClose={this.onOrganizeModalClose}
|
||||
/>
|
||||
|
||||
<EpisodeFileEditorModal
|
||||
isOpen={isManageEpisodesOpen}
|
||||
artistId={artistId}
|
||||
seasonNumber={seasonNumber}
|
||||
albumId={albumId}
|
||||
onModalClose={this.onManageEpisodesModalClose}
|
||||
/>
|
||||
</div>
|
||||
|
@ -375,7 +307,10 @@ class SeriesDetailsSeason extends Component {
|
|||
SeriesDetailsSeason.propTypes = {
|
||||
artistId: PropTypes.number.isRequired,
|
||||
monitored: PropTypes.bool.isRequired,
|
||||
seasonNumber: PropTypes.number.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
releaseDate: PropTypes.string.isRequired,
|
||||
albumId: PropTypes.number.isRequired,
|
||||
statistics: PropTypes.object.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
isSaving: PropTypes.bool,
|
||||
|
@ -390,4 +325,12 @@ SeriesDetailsSeason.propTypes = {
|
|||
onSearchPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
SeriesDetailsSeason.defaultProps = {
|
||||
statistics: {
|
||||
trackFileCount: 0,
|
||||
totalTrackCount: 0,
|
||||
percentOfTracks: 0
|
||||
}
|
||||
};
|
||||
|
||||
export default SeriesDetailsSeason;
|
||||
|
|
|
@ -60,12 +60,12 @@ class SeriesDetailsSeasonConnector extends Component {
|
|||
onMonitorSeasonPress = (monitored) => {
|
||||
const {
|
||||
artistId,
|
||||
seasonNumber
|
||||
albumId
|
||||
} = this.props;
|
||||
|
||||
this.props.toggleSeasonMonitored({
|
||||
artistId,
|
||||
seasonNumber,
|
||||
albumId,
|
||||
monitored
|
||||
});
|
||||
}
|
||||
|
@ -73,13 +73,13 @@ class SeriesDetailsSeasonConnector extends Component {
|
|||
onSearchPress = () => {
|
||||
const {
|
||||
artistId,
|
||||
seasonNumber
|
||||
albumId
|
||||
} = this.props;
|
||||
|
||||
this.props.executeCommand({
|
||||
name: commandNames.SEASON_SEARCH,
|
||||
artistId,
|
||||
seasonNumber
|
||||
albumIds: [albumId]
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ class SeriesDetailsSeasonConnector extends Component {
|
|||
|
||||
SeriesDetailsSeasonConnector.propTypes = {
|
||||
artistId: PropTypes.number.isRequired,
|
||||
seasonNumber: PropTypes.number.isRequired,
|
||||
albumId: PropTypes.number.isRequired,
|
||||
toggleSeasonMonitored: PropTypes.func.isRequired,
|
||||
toggleEpisodesMonitored: PropTypes.func.isRequired,
|
||||
setEpisodesTableOption: PropTypes.func.isRequired,
|
||||
|
|
|
@ -15,5 +15,5 @@ export const RENAME_FILES = 'RenameFiles';
|
|||
export const RENAME_ARTIST = 'RenameArtist';
|
||||
export const RESET_API_KEY = 'ResetApiKey';
|
||||
export const RSS_SYNC = 'RssSync';
|
||||
export const SEASON_SEARCH = 'SeasonSearch';
|
||||
export const SEASON_SEARCH = 'AlbumSearch';
|
||||
export const ARTIST_SEARCH = 'ArtistSearch';
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import SelectSeasonModalContentConnector from './SelectSeasonModalContentConnector';
|
||||
import SelectAlbumModalContentConnector from './SelectAlbumModalContentConnector';
|
||||
|
||||
class SelectSeasonModal extends Component {
|
||||
class SelectAlbumModal extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
@ -20,7 +20,7 @@ class SelectSeasonModal extends Component {
|
|||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<SelectSeasonModalContentConnector
|
||||
<SelectAlbumModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
|
@ -29,9 +29,9 @@ class SelectSeasonModal extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
SelectSeasonModal.propTypes = {
|
||||
SelectAlbumModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SelectSeasonModal;
|
||||
export default SelectAlbumModal;
|
|
@ -5,9 +5,9 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import SelectSeasonRow from './SelectSeasonRow';
|
||||
import SelectAlbumRow from './SelectAlbumRow';
|
||||
|
||||
class SelectSeasonModalContent extends Component {
|
||||
class SelectAlbumModalContent extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
@ -15,24 +15,25 @@ class SelectSeasonModalContent extends Component {
|
|||
render() {
|
||||
const {
|
||||
items,
|
||||
onSeasonSelect,
|
||||
onAlbumSelect,
|
||||
onModalClose
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Manual Import - Select Season
|
||||
Manual Import - Select Album
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<SelectSeasonRow
|
||||
key={item.seasonNumber}
|
||||
seasonNumber={item.seasonNumber}
|
||||
onSeasonSelect={onSeasonSelect}
|
||||
<SelectAlbumRow
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
title={item.title}
|
||||
onAlbumSelect={onAlbumSelect}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
@ -49,10 +50,10 @@ class SelectSeasonModalContent extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
SelectSeasonModalContent.propTypes = {
|
||||
SelectAlbumModalContent.propTypes = {
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onSeasonSelect: PropTypes.func.isRequired,
|
||||
onAlbumSelect: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SelectSeasonModalContent;
|
||||
export default SelectAlbumModalContent;
|
|
@ -1,17 +1,18 @@
|
|||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import SelectSeasonModalContent from './SelectSeasonModalContent';
|
||||
import SelectAlbumModalContent from './SelectAlbumModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
(series) => {
|
||||
return {
|
||||
items: series.seasons
|
||||
items: series.albums
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -21,16 +22,18 @@ const mapDispatchToProps = {
|
|||
updateInteractiveImportItem
|
||||
};
|
||||
|
||||
class SelectSeasonModalContentConnector extends Component {
|
||||
class SelectAlbumModalContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onSeasonSelect = (seasonNumber) => {
|
||||
onAlbumSelect = (albumId) => {
|
||||
const album = _.find(this.props.items, { id: albumId });
|
||||
|
||||
this.props.ids.forEach((id) => {
|
||||
this.props.updateInteractiveImportItem({
|
||||
id,
|
||||
seasonNumber,
|
||||
album,
|
||||
episodes: []
|
||||
});
|
||||
});
|
||||
|
@ -43,15 +46,15 @@ class SelectSeasonModalContentConnector extends Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<SelectSeasonModalContent
|
||||
<SelectAlbumModalContent
|
||||
{...this.props}
|
||||
onSeasonSelect={this.onSeasonSelect}
|
||||
onAlbumSelect={this.onAlbumSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SelectSeasonModalContentConnector.propTypes = {
|
||||
SelectAlbumModalContentConnector.propTypes = {
|
||||
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
artistId: PropTypes.number.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
|
@ -59,4 +62,4 @@ SelectSeasonModalContentConnector.propTypes = {
|
|||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(SelectSeasonModalContentConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(SelectAlbumModalContentConnector);
|
37
frontend/src/InteractiveImport/Album/SelectAlbumRow.js
Normal file
37
frontend/src/InteractiveImport/Album/SelectAlbumRow.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Link from 'Components/Link/Link';
|
||||
import styles from './SelectAlbumRow.css';
|
||||
|
||||
class SelectAlbumRow extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onPress = () => {
|
||||
this.props.onAlbumSelect(this.props.id);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Link
|
||||
className={styles.season}
|
||||
component="div"
|
||||
onPress={this.onPress}
|
||||
>
|
||||
{this.props.title}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SelectAlbumRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
onAlbumSelect: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SelectAlbumRow;
|
|
@ -37,7 +37,7 @@ class SelectArtistModalContent extends Component {
|
|||
render() {
|
||||
const {
|
||||
items,
|
||||
onSeriesSelect,
|
||||
onArtistSelect,
|
||||
onModalClose
|
||||
} = this.props;
|
||||
|
||||
|
@ -46,7 +46,7 @@ class SelectArtistModalContent extends Component {
|
|||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Manual Import - Select Series
|
||||
Manual Import - Select Artist
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody
|
||||
|
@ -55,7 +55,7 @@ class SelectArtistModalContent extends Component {
|
|||
>
|
||||
<TextInput
|
||||
className={styles.filterInput}
|
||||
placeholder="Filter series"
|
||||
placeholder="Filter artist"
|
||||
name="filter"
|
||||
value={filter}
|
||||
autoFocus={true}
|
||||
|
@ -65,13 +65,13 @@ class SelectArtistModalContent extends Component {
|
|||
<Scroller className={styles.scroller}>
|
||||
{
|
||||
items.map((item) => {
|
||||
return item.title.toLowerCase().includes(filter) ?
|
||||
return item.artistName.toLowerCase().includes(filter) ?
|
||||
(
|
||||
<SelectArtistRow
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
title={item.title}
|
||||
onSeriesSelect={onSeriesSelect}
|
||||
artistName={item.artistName}
|
||||
onArtistSelect={onArtistSelect}
|
||||
/>
|
||||
) :
|
||||
null;
|
||||
|
@ -92,7 +92,7 @@ class SelectArtistModalContent extends Component {
|
|||
|
||||
SelectArtistModalContent.propTypes = {
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onSeriesSelect: PropTypes.func.isRequired,
|
||||
onArtistSelect: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
|
@ -27,15 +27,15 @@ class SelectArtistModalContentConnector extends Component {
|
|||
//
|
||||
// Listeners
|
||||
|
||||
onSeriesSelect = (artistId) => {
|
||||
const series = _.find(this.props.items, { id: artistId });
|
||||
onArtistSelect = (artistId) => {
|
||||
const artist = _.find(this.props.items, { id: artistId });
|
||||
|
||||
this.props.ids.forEach((id) => {
|
||||
this.props.updateInteractiveImportItem({
|
||||
id,
|
||||
series,
|
||||
seasonNumber: undefined,
|
||||
episodes: []
|
||||
artist,
|
||||
album: undefined,
|
||||
tracks: []
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -49,7 +49,7 @@ class SelectArtistModalContentConnector extends Component {
|
|||
return (
|
||||
<SelectArtistModalContent
|
||||
{...this.props}
|
||||
onSeriesSelect={this.onSeriesSelect}
|
||||
onArtistSelect={this.onArtistSelect}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.series {
|
||||
.artist {
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid $borderColor;
|
||||
}
|
|
@ -9,7 +9,7 @@ class SelectArtistRow extends Component {
|
|||
// Listeners
|
||||
|
||||
onPress = () => {
|
||||
this.props.onSeriesSelect(this.props.id);
|
||||
this.props.onArtistSelect(this.props.id);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -18,11 +18,11 @@ class SelectArtistRow extends Component {
|
|||
render() {
|
||||
return (
|
||||
<Link
|
||||
className={styles.series}
|
||||
className={styles.artist}
|
||||
component="div"
|
||||
onPress={this.onPress}
|
||||
>
|
||||
{this.props.title}
|
||||
{this.props.artistName}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ class SelectArtistRow extends Component {
|
|||
|
||||
SelectArtistRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
onSeriesSelect: PropTypes.func.isRequired
|
||||
artistName: PropTypes.string.isRequired,
|
||||
onArtistSelect: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SelectArtistRow;
|
|
@ -15,8 +15,8 @@ import ModalBody from 'Components/Modal/ModalBody';
|
|||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import SelectArtistModal from 'InteractiveImport/Series/SelectArtistModal';
|
||||
import SelectSeasonModal from 'InteractiveImport/Season/SelectSeasonModal';
|
||||
import SelectArtistModal from 'InteractiveImport/Artist/SelectArtistModal';
|
||||
import SelectAlbumModal from 'InteractiveImport/Album/SelectAlbumModal';
|
||||
import InteractiveImportRow from './InteractiveImportRow';
|
||||
import styles from './InteractiveImportModalContent.css';
|
||||
|
||||
|
@ -28,19 +28,19 @@ const columns = [
|
|||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'series',
|
||||
label: 'Series',
|
||||
name: 'artist',
|
||||
label: 'Artist',
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'season',
|
||||
label: 'Season',
|
||||
name: 'album',
|
||||
label: 'Album',
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'episodes',
|
||||
label: 'Episode(s)',
|
||||
name: 'tracks',
|
||||
label: 'Track(s)',
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
|
@ -79,7 +79,7 @@ class InteractiveImportModalContent extends Component {
|
|||
selectedState: {},
|
||||
invalidRowsSelected: [],
|
||||
isSelectArtistModalOpen: false,
|
||||
isSelectSeasonModalOpen: false
|
||||
isSelectAlbumModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -131,16 +131,16 @@ class InteractiveImportModalContent extends Component {
|
|||
this.setState({ isSelectArtistModalOpen: true });
|
||||
}
|
||||
|
||||
onSelectSeasonPress = () => {
|
||||
this.setState({ isSelectSeasonModalOpen: true });
|
||||
onSelectAlbumPress = () => {
|
||||
this.setState({ isSelectAlbumModalOpen: true });
|
||||
}
|
||||
|
||||
onSelectArtistModalClose = () => {
|
||||
this.setState({ isSelectArtistModalOpen: false });
|
||||
}
|
||||
|
||||
onSelectSeasonModalClose = () => {
|
||||
this.setState({ isSelectSeasonModalOpen: false });
|
||||
onSelectAlbumModalClose = () => {
|
||||
this.setState({ isSelectAlbumModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -169,7 +169,7 @@ class InteractiveImportModalContent extends Component {
|
|||
selectedState,
|
||||
invalidRowsSelected,
|
||||
isSelectArtistModalOpen,
|
||||
isSelectSeasonModalOpen
|
||||
isSelectAlbumModalOpen
|
||||
} = this.state;
|
||||
|
||||
const selectedIds = this.getSelectedIds();
|
||||
|
@ -250,11 +250,11 @@ class InteractiveImportModalContent extends Component {
|
|||
|
||||
<div className={downloadId ? styles.leftButtons : styles.centerButtons}>
|
||||
<Button onPress={this.onSelectArtistPress}>
|
||||
Select Series
|
||||
Select Artist
|
||||
</Button>
|
||||
|
||||
<Button onPress={this.onSelectSeasonPress}>
|
||||
Select Season
|
||||
<Button onPress={this.onSelectAlbumPress}>
|
||||
Select Album
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
@ -284,11 +284,11 @@ class InteractiveImportModalContent extends Component {
|
|||
onModalClose={this.onSelectArtistModalClose}
|
||||
/>
|
||||
|
||||
<SelectSeasonModal
|
||||
isOpen={isSelectSeasonModalOpen}
|
||||
<SelectAlbumModal
|
||||
isOpen={isSelectAlbumModalOpen}
|
||||
ids={selectedIds}
|
||||
artistId={selectedItem && selectedItem.series && selectedItem.series.id}
|
||||
onModalClose={this.onSelectSeasonModalClose}
|
||||
onModalClose={this.onSelectAlbumModalClose}
|
||||
/>
|
||||
</ModalContent>
|
||||
);
|
||||
|
|
|
@ -71,31 +71,32 @@ class InteractiveImportModalContentConnector extends Component {
|
|||
|
||||
if (isSelected) {
|
||||
const {
|
||||
series,
|
||||
seasonNumber,
|
||||
episodes,
|
||||
artist,
|
||||
album,
|
||||
tracks,
|
||||
quality
|
||||
} = item;
|
||||
|
||||
if (!series) {
|
||||
this.setState({ interactiveImportErrorMessage: 'Series must be chosen for each selected file' });
|
||||
if (!artist) {
|
||||
this.setState({ interactiveImportErrorMessage: 'Artist must be chosen for each selected file' });
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isNaN(seasonNumber)) {
|
||||
this.setState({ interactiveImportErrorMessage: 'Season must be chosen for each selected file' });
|
||||
if (!album) {
|
||||
this.setState({ interactiveImportErrorMessage: 'Album must be chosen for each selected file' });
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!episodes || !episodes.length) {
|
||||
this.setState({ interactiveImportErrorMessage: 'One or more episodes must be chosen for each selected file' });
|
||||
if (!tracks || !tracks.length) {
|
||||
this.setState({ interactiveImportErrorMessage: 'One or more tracks must be chosen for each selected file' });
|
||||
return false;
|
||||
}
|
||||
|
||||
files.push({
|
||||
path: item.path,
|
||||
artistId: series.id,
|
||||
episodeIds: _.map(episodes, 'id'),
|
||||
artistId: artist.id,
|
||||
albumId: album.id,
|
||||
trackIds: _.map(tracks, 'id'),
|
||||
quality,
|
||||
downloadId: this.props.downloadId
|
||||
});
|
||||
|
|
|
@ -9,9 +9,9 @@ import TableRowCellButton from 'Components/Table/Cells/TableRowCellButton';
|
|||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import EpisodeQuality from 'Episode/EpisodeQuality';
|
||||
import SelectArtistModal from 'InteractiveImport/Series/SelectArtistModal';
|
||||
import SelectSeasonModal from 'InteractiveImport/Season/SelectSeasonModal';
|
||||
import SelectEpisodeModal from 'InteractiveImport/Episode/SelectEpisodeModal';
|
||||
import SelectArtistModal from 'InteractiveImport/Artist/SelectArtistModal';
|
||||
import SelectAlbumModal from 'InteractiveImport/Album/SelectAlbumModal';
|
||||
import SelectTrackModal from 'InteractiveImport/Track/SelectTrackModal';
|
||||
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
||||
import InteractiveImportRowCellPlaceholder from './InteractiveImportRowCellPlaceholder';
|
||||
import styles from './InteractiveImportRow.css';
|
||||
|
@ -26,8 +26,8 @@ class InteractiveImportRow extends Component {
|
|||
|
||||
this.state = {
|
||||
isSelectArtistModalOpen: false,
|
||||
isSelectSeasonModalOpen: false,
|
||||
isSelectEpisodeModalOpen: false,
|
||||
isSelectAlbumModalOpen: false,
|
||||
isSelectTrackModalOpen: false,
|
||||
isSelectQualityModalOpen: false
|
||||
};
|
||||
}
|
||||
|
@ -35,13 +35,13 @@ class InteractiveImportRow extends Component {
|
|||
componentDidMount() {
|
||||
const {
|
||||
id,
|
||||
series,
|
||||
seasonNumber,
|
||||
episodes,
|
||||
artist,
|
||||
album,
|
||||
tracks,
|
||||
quality
|
||||
} = this.props;
|
||||
|
||||
if (series && seasonNumber !== undefined && episodes.length && quality) {
|
||||
if (artist && album !== undefined && tracks.length && quality) {
|
||||
this.props.onSelectedChange({ id, value: true });
|
||||
}
|
||||
}
|
||||
|
@ -49,9 +49,9 @@ class InteractiveImportRow extends Component {
|
|||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
id,
|
||||
series,
|
||||
seasonNumber,
|
||||
episodes,
|
||||
artist,
|
||||
album,
|
||||
tracks,
|
||||
quality,
|
||||
isSelected,
|
||||
onValidRowChange
|
||||
|
@ -61,7 +61,7 @@ class InteractiveImportRow extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
const isValid = !!(series && seasonNumber != null && episodes.length && quality);
|
||||
const isValid = !!(artist && album != null && tracks.length && quality);
|
||||
|
||||
if (isSelected && !isValid) {
|
||||
onValidRowChange(id, false);
|
||||
|
@ -91,12 +91,12 @@ class InteractiveImportRow extends Component {
|
|||
this.setState({ isSelectArtistModalOpen: true });
|
||||
}
|
||||
|
||||
onSelectSeasonPress = () => {
|
||||
this.setState({ isSelectSeasonModalOpen: true });
|
||||
onSelectAlbumPress = () => {
|
||||
this.setState({ isSelectAlbumModalOpen: true });
|
||||
}
|
||||
|
||||
onSelectEpisodePress = () => {
|
||||
this.setState({ isSelectEpisodeModalOpen: true });
|
||||
onSelectTrackPress = () => {
|
||||
this.setState({ isSelectTrackModalOpen: true });
|
||||
}
|
||||
|
||||
onSelectQualityPress = () => {
|
||||
|
@ -108,13 +108,13 @@ class InteractiveImportRow extends Component {
|
|||
this.selectRowAfterChange(changed);
|
||||
}
|
||||
|
||||
onSelectSeasonModalClose = (changed) => {
|
||||
this.setState({ isSelectSeasonModalOpen: false });
|
||||
onSelectAlbumModalClose = (changed) => {
|
||||
this.setState({ isSelectAlbumModalOpen: false });
|
||||
this.selectRowAfterChange(changed);
|
||||
}
|
||||
|
||||
onSelectEpisodeModalClose = (changed) => {
|
||||
this.setState({ isSelectEpisodeModalOpen: false });
|
||||
onSelectTrackModalClose = (changed) => {
|
||||
this.setState({ isSelectTrackModalOpen: false });
|
||||
this.selectRowAfterChange(changed);
|
||||
}
|
||||
|
||||
|
@ -130,9 +130,9 @@ class InteractiveImportRow extends Component {
|
|||
const {
|
||||
id,
|
||||
relativePath,
|
||||
series,
|
||||
seasonNumber,
|
||||
episodes,
|
||||
artist,
|
||||
album,
|
||||
tracks,
|
||||
quality,
|
||||
size,
|
||||
rejections,
|
||||
|
@ -142,18 +142,19 @@ class InteractiveImportRow extends Component {
|
|||
|
||||
const {
|
||||
isSelectArtistModalOpen,
|
||||
isSelectSeasonModalOpen,
|
||||
isSelectEpisodeModalOpen,
|
||||
isSelectAlbumModalOpen,
|
||||
isSelectTrackModalOpen,
|
||||
isSelectQualityModalOpen
|
||||
} = this.state;
|
||||
|
||||
const seriesTitle = series ? series.title : '';
|
||||
const episodeNumbers = episodes.map((episode) => episode.episodeNumber)
|
||||
const seriesTitle = artist ? artist.artistName : '';
|
||||
const albumTitle = album ? album.title : '';
|
||||
const trackNumbers = tracks.map((episode) => episode.trackNumber)
|
||||
.join(', ');
|
||||
|
||||
const showSeriesPlaceholder = isSelected && !series;
|
||||
const showSeasonNumberPlaceholder = isSelected && !!series && isNaN(seasonNumber);
|
||||
const showEpisodeNumbersPlaceholder = isSelected && Number.isInteger(seasonNumber) && !episodes.length;
|
||||
const showSeriesPlaceholder = isSelected && !artist;
|
||||
const showSeasonNumberPlaceholder = isSelected && !!artist && !album;
|
||||
const showEpisodeNumbersPlaceholder = isSelected && !!album && !tracks.length;
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
|
@ -179,20 +180,20 @@ class InteractiveImportRow extends Component {
|
|||
</TableRowCellButton>
|
||||
|
||||
<TableRowCellButton
|
||||
isDisabled={!series}
|
||||
onPress={this.onSelectSeasonPress}
|
||||
isDisabled={!artist}
|
||||
onPress={this.onSelectAlbumPress}
|
||||
>
|
||||
{
|
||||
showSeasonNumberPlaceholder ? <InteractiveImportRowCellPlaceholder /> : seasonNumber
|
||||
showSeasonNumberPlaceholder ? <InteractiveImportRowCellPlaceholder /> : albumTitle
|
||||
}
|
||||
</TableRowCellButton>
|
||||
|
||||
<TableRowCellButton
|
||||
isDisabled={!series || isNaN(seasonNumber)}
|
||||
onPress={this.onSelectEpisodePress}
|
||||
isDisabled={!artist || !album}
|
||||
onPress={this.onSelectTrackPress}
|
||||
>
|
||||
{
|
||||
showEpisodeNumbersPlaceholder ? <InteractiveImportRowCellPlaceholder /> : episodeNumbers
|
||||
showEpisodeNumbersPlaceholder ? <InteractiveImportRowCellPlaceholder /> : trackNumbers
|
||||
}
|
||||
</TableRowCellButton>
|
||||
|
||||
|
@ -244,19 +245,19 @@ class InteractiveImportRow extends Component {
|
|||
onModalClose={this.onSelectArtistModalClose}
|
||||
/>
|
||||
|
||||
<SelectSeasonModal
|
||||
isOpen={isSelectSeasonModalOpen}
|
||||
<SelectAlbumModal
|
||||
isOpen={isSelectAlbumModalOpen}
|
||||
ids={[id]}
|
||||
artistId={series && series.id}
|
||||
onModalClose={this.onSelectSeasonModalClose}
|
||||
artistId={artist && artist.id}
|
||||
onModalClose={this.onSelectAlbumModalClose}
|
||||
/>
|
||||
|
||||
<SelectEpisodeModal
|
||||
isOpen={isSelectEpisodeModalOpen}
|
||||
<SelectTrackModal
|
||||
isOpen={isSelectTrackModalOpen}
|
||||
id={id}
|
||||
artistId={series && series.id}
|
||||
seasonNumber={seasonNumber}
|
||||
onModalClose={this.onSelectEpisodeModalClose}
|
||||
artistId={artist && artist.id}
|
||||
albumId={album && album.id}
|
||||
onModalClose={this.onSelectTrackModalClose}
|
||||
/>
|
||||
|
||||
<SelectQualityModal
|
||||
|
@ -276,9 +277,9 @@ class InteractiveImportRow extends Component {
|
|||
InteractiveImportRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
relativePath: PropTypes.string.isRequired,
|
||||
series: PropTypes.object,
|
||||
seasonNumber: PropTypes.number,
|
||||
episodes: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
artist: PropTypes.object,
|
||||
album: PropTypes.object,
|
||||
tracks: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
quality: PropTypes.object,
|
||||
size: PropTypes.number.isRequired,
|
||||
rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
|
@ -288,7 +289,7 @@ InteractiveImportRow.propTypes = {
|
|||
};
|
||||
|
||||
InteractiveImportRow.defaultProps = {
|
||||
episodes: []
|
||||
tracks: []
|
||||
};
|
||||
|
||||
export default InteractiveImportRow;
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Link from 'Components/Link/Link';
|
||||
import styles from './SelectSeasonRow.css';
|
||||
|
||||
class SelectSeasonRow extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onPress = () => {
|
||||
this.props.onSeasonSelect(this.props.seasonNumber);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const seasonNumber = this.props.seasonNumber;
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={styles.season}
|
||||
component="div"
|
||||
onPress={this.onPress}
|
||||
>
|
||||
{
|
||||
seasonNumber === 0 ? 'Specials' : `Season ${seasonNumber}`
|
||||
}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SelectSeasonRow.propTypes = {
|
||||
seasonNumber: PropTypes.number.isRequired,
|
||||
onSeasonSelect: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SelectSeasonRow;
|
|
@ -1,9 +1,9 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import SelectEpisodeModalContentConnector from './SelectEpisodeModalContentConnector';
|
||||
import SelectTrackModalContentConnector from './SelectTrackModalContentConnector';
|
||||
|
||||
class SelectEpisodeModal extends Component {
|
||||
class SelectTrackModal extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
@ -20,7 +20,7 @@ class SelectEpisodeModal extends Component {
|
|||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<SelectEpisodeModalContentConnector
|
||||
<SelectTrackModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
|
@ -29,9 +29,9 @@ class SelectEpisodeModal extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
SelectEpisodeModal.propTypes = {
|
||||
SelectTrackModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SelectEpisodeModal;
|
||||
export default SelectTrackModal;
|
|
@ -12,11 +12,11 @@ import ModalBody from 'Components/Modal/ModalBody';
|
|||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import SelectEpisodeRow from './SelectEpisodeRow';
|
||||
import SelectTrackRow from './SelectTrackRow';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'episodeNumber',
|
||||
name: 'trackNumber',
|
||||
label: '#',
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
|
@ -25,15 +25,10 @@ const columns = [
|
|||
name: 'title',
|
||||
label: 'Title',
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'airDate',
|
||||
label: 'Air Date',
|
||||
isVisible: true
|
||||
}
|
||||
];
|
||||
|
||||
class SelectEpisodeModalContent extends Component {
|
||||
class SelectTrackModalContent extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
@ -69,8 +64,8 @@ class SelectEpisodeModalContent extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
onEpisodesSelect = () => {
|
||||
this.props.onEpisodesSelect(this.getSelectedIds());
|
||||
onTracksSelect = () => {
|
||||
this.props.onTracksSelect(this.getSelectedIds());
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -94,12 +89,12 @@ class SelectEpisodeModalContent extends Component {
|
|||
selectedState
|
||||
} = this.state;
|
||||
|
||||
const errorMessage = error && error.message || 'Unable to load episodes';
|
||||
const errorMessage = error && error.message || 'Unable to load tracks';
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Manual Import - Select Episode(s)
|
||||
Manual Import - Select Track(s)
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
|
@ -129,12 +124,11 @@ class SelectEpisodeModalContent extends Component {
|
|||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<SelectEpisodeRow
|
||||
<SelectTrackRow
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
episodeNumber={item.episodeNumber}
|
||||
trackNumber={item.trackNumber}
|
||||
title={item.title}
|
||||
airDate={item.airDate}
|
||||
isSelected={selectedState[item.id]}
|
||||
onSelectedChange={this.onSelectedChange}
|
||||
/>
|
||||
|
@ -147,7 +141,7 @@ class SelectEpisodeModalContent extends Component {
|
|||
|
||||
{
|
||||
isPopulated && !items.length &&
|
||||
'No episodes were found for the selected season'
|
||||
'No tracks were found for the selected album'
|
||||
}
|
||||
</ModalBody>
|
||||
|
||||
|
@ -158,9 +152,9 @@ class SelectEpisodeModalContent extends Component {
|
|||
|
||||
<Button
|
||||
kind={kinds.SUCCESS}
|
||||
onPress={this.onEpisodesSelect}
|
||||
onPress={this.onTracksSelect}
|
||||
>
|
||||
Select Episodes
|
||||
Select Tracks
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
@ -168,7 +162,7 @@ class SelectEpisodeModalContent extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
SelectEpisodeModalContent.propTypes = {
|
||||
SelectTrackModalContent.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
|
@ -176,8 +170,8 @@ SelectEpisodeModalContent.propTypes = {
|
|||
sortKey: PropTypes.string,
|
||||
sortDirection: PropTypes.string,
|
||||
onSortPress: PropTypes.func.isRequired,
|
||||
onEpisodesSelect: PropTypes.func.isRequired,
|
||||
onTracksSelect: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SelectEpisodeModalContent;
|
||||
export default SelectTrackModalContent;
|
|
@ -6,7 +6,7 @@ import connectSection from 'Store/connectSection';
|
|||
import { fetchEpisodes, setEpisodesSort, clearEpisodes } from 'Store/Actions/episodeActions';
|
||||
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import SelectEpisodeModalContent from './SelectEpisodeModalContent';
|
||||
import SelectTrackModalContent from './SelectTrackModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
|
@ -24,7 +24,7 @@ const mapDispatchToProps = {
|
|||
updateInteractiveImportItem
|
||||
};
|
||||
|
||||
class SelectEpisodeModalContentConnector extends Component {
|
||||
class SelectTrackModalContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
@ -32,10 +32,10 @@ class SelectEpisodeModalContentConnector extends Component {
|
|||
componentDidMount() {
|
||||
const {
|
||||
artistId,
|
||||
seasonNumber
|
||||
albumId
|
||||
} = this.props;
|
||||
|
||||
this.props.fetchEpisodes({ artistId, seasonNumber });
|
||||
this.props.fetchEpisodes({ artistId, albumId });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -51,8 +51,8 @@ class SelectEpisodeModalContentConnector extends Component {
|
|||
this.props.setEpisodesSort({ sortKey, sortDirection });
|
||||
}
|
||||
|
||||
onEpisodesSelect = (episodeIds) => {
|
||||
const episodes = _.reduce(this.props.items, (acc, item) => {
|
||||
onTracksSelect = (episodeIds) => {
|
||||
const tracks = _.reduce(this.props.items, (acc, item) => {
|
||||
if (episodeIds.indexOf(item.id) > -1) {
|
||||
acc.push(item);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ class SelectEpisodeModalContentConnector extends Component {
|
|||
|
||||
this.props.updateInteractiveImportItem({
|
||||
id: this.props.id,
|
||||
episodes: _.sortBy(episodes, 'episodeNumber')
|
||||
tracks: _.sortBy(tracks, 'trackNumber')
|
||||
});
|
||||
|
||||
this.props.onModalClose(true);
|
||||
|
@ -73,19 +73,19 @@ class SelectEpisodeModalContentConnector extends Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<SelectEpisodeModalContent
|
||||
<SelectTrackModalContent
|
||||
{...this.props}
|
||||
onSortPress={this.onSortPress}
|
||||
onEpisodesSelect={this.onEpisodesSelect}
|
||||
onTracksSelect={this.onTracksSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SelectEpisodeModalContentConnector.propTypes = {
|
||||
SelectTrackModalContentConnector.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
artistId: PropTypes.number.isRequired,
|
||||
seasonNumber: PropTypes.number.isRequired,
|
||||
albumId: PropTypes.number.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
fetchEpisodes: PropTypes.func.isRequired,
|
||||
setEpisodesSort: PropTypes.func.isRequired,
|
||||
|
@ -100,4 +100,4 @@ export default connectSection(
|
|||
undefined,
|
||||
undefined,
|
||||
{ section: 'episodes' }
|
||||
)(SelectEpisodeModalContentConnector);
|
||||
)(SelectTrackModalContentConnector);
|
|
@ -4,7 +4,7 @@ import TableRowButton from 'Components/Table/TableRowButton';
|
|||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
|
||||
class SelectEpisodeRow extends Component {
|
||||
class SelectTrackRow extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
@ -24,9 +24,8 @@ class SelectEpisodeRow extends Component {
|
|||
render() {
|
||||
const {
|
||||
id,
|
||||
episodeNumber,
|
||||
trackNumber,
|
||||
title,
|
||||
airDate,
|
||||
isSelected,
|
||||
onSelectedChange
|
||||
} = this.props;
|
||||
|
@ -40,28 +39,24 @@ class SelectEpisodeRow extends Component {
|
|||
/>
|
||||
|
||||
<TableRowCell>
|
||||
{episodeNumber}
|
||||
{trackNumber}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell>
|
||||
{title}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell>
|
||||
{airDate}
|
||||
</TableRowCell>
|
||||
</TableRowButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SelectEpisodeRow.propTypes = {
|
||||
SelectTrackRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
episodeNumber: PropTypes.number.isRequired,
|
||||
trackNumber: PropTypes.number.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
airDate: PropTypes.string.isRequired,
|
||||
isSelected: PropTypes.bool,
|
||||
onSelectedChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default SelectEpisodeRow;
|
||||
export default SelectTrackRow;
|
|
@ -9,7 +9,7 @@ import { updateItem } from './baseActions';
|
|||
const section = 'episodes';
|
||||
|
||||
const episodeActionHandlers = {
|
||||
[types.FETCH_EPISODES]: createFetchHandler(section, '/episode'),
|
||||
[types.FETCH_EPISODES]: createFetchHandler(section, '/track'),
|
||||
|
||||
[types.TOGGLE_EPISODE_MONITORED]: function(payload) {
|
||||
return function(dispatch, getState) {
|
||||
|
|
|
@ -8,10 +8,10 @@ import * as types from './actionTypes';
|
|||
import { set, removeItem, updateItem } from './baseActions';
|
||||
|
||||
const section = 'episodeFiles';
|
||||
const deleteEpisodeFile = createRemoveItemHandler(section, '/episodeFile');
|
||||
const deleteEpisodeFile = createRemoveItemHandler(section, '/trackFile');
|
||||
|
||||
const episodeFileActionHandlers = {
|
||||
[types.FETCH_EPISODE_FILES]: createFetchHandler(section, '/episodeFile'),
|
||||
[types.FETCH_EPISODE_FILES]: createFetchHandler(section, '/trackFile'),
|
||||
|
||||
[types.DELETE_EPISODE_FILE]: function(payload) {
|
||||
return function(dispatch, getState) {
|
||||
|
|
|
@ -13,8 +13,8 @@ export default function(history) {
|
|||
isProduction
|
||||
} = window.Sonarr;
|
||||
|
||||
const dsn = isProduction ? 'https://b80ca60625b443c38b242e0d21681eb7@sentry.sonarr.tv/13' :
|
||||
'https://8dbaacdfe2ff4caf97dc7945aecf9ace@sentry.sonarr.tv/12';
|
||||
const dsn = isProduction ? 'https://c3a5b33e08de4e18b7d0505e942dbc95:e35e6d535b034995a6896022c6bfed04@sentry.io/216290' :
|
||||
'https://c3a5b33e08de4e18b7d0505e942dbc95:e35e6d535b034995a6896022c6bfed04@sentry.io/216290';
|
||||
|
||||
const middlewares = [];
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue