mirror of
https://github.com/fergalmoran/Readarr.git
synced 2025-12-22 09:29:59 +00:00
Renames in Frontend
This commit is contained in:
@@ -6,8 +6,8 @@ import IconButton from 'Components/Link/IconButton';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TrackQuality from 'Album/TrackQuality';
|
||||
import ArtistNameLink from 'Artist/ArtistNameLink';
|
||||
import BookQuality from 'Book/BookQuality';
|
||||
import AuthorNameLink from 'Author/AuthorNameLink';
|
||||
import BlacklistDetailsModal from './BlacklistDetailsModal';
|
||||
import styles from './BlacklistRow.css';
|
||||
|
||||
@@ -40,7 +40,7 @@ class BlacklistRow extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
artist,
|
||||
author,
|
||||
sourceTitle,
|
||||
quality,
|
||||
date,
|
||||
@@ -51,7 +51,7 @@ class BlacklistRow extends Component {
|
||||
onRemovePress
|
||||
} = this.props;
|
||||
|
||||
if (!artist) {
|
||||
if (!author) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -71,9 +71,9 @@ class BlacklistRow extends Component {
|
||||
if (name === 'authors.sortName') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<ArtistNameLink
|
||||
titleSlug={artist.titleSlug}
|
||||
artistName={artist.artistName}
|
||||
<AuthorNameLink
|
||||
titleSlug={author.titleSlug}
|
||||
authorName={author.authorName}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
@@ -93,7 +93,7 @@ class BlacklistRow extends Component {
|
||||
key={name}
|
||||
className={styles.quality}
|
||||
>
|
||||
<TrackQuality
|
||||
<BookQuality
|
||||
quality={quality}
|
||||
/>
|
||||
</TableRowCell>
|
||||
@@ -161,7 +161,7 @@ class BlacklistRow extends Component {
|
||||
|
||||
BlacklistRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
artist: PropTypes.object.isRequired,
|
||||
author: PropTypes.object.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
quality: PropTypes.object.isRequired,
|
||||
date: PropTypes.string.isRequired,
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import { removeFromBlacklist } from 'Store/Actions/blacklistActions';
|
||||
import BlacklistRow from './BlacklistRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
(artist) => {
|
||||
createAuthorSelector(),
|
||||
(author) => {
|
||||
return {
|
||||
artist
|
||||
author
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -170,7 +170,7 @@ function HistoryDetails(props) {
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'trackFileImported') {
|
||||
if (eventType === 'bookFileImported') {
|
||||
const {
|
||||
droppedPath,
|
||||
importedPath
|
||||
@@ -205,7 +205,7 @@ function HistoryDetails(props) {
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'trackFileDeleted') {
|
||||
if (eventType === 'bookFileDeleted') {
|
||||
const {
|
||||
reason
|
||||
} = data;
|
||||
@@ -241,7 +241,7 @@ function HistoryDetails(props) {
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'trackFileRenamed') {
|
||||
if (eventType === 'bookFileRenamed') {
|
||||
const {
|
||||
sourcePath,
|
||||
path
|
||||
@@ -262,7 +262,7 @@ function HistoryDetails(props) {
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'trackFileRetagged') {
|
||||
if (eventType === 'bookFileRetagged') {
|
||||
const {
|
||||
diff,
|
||||
tagsScrubbed
|
||||
@@ -293,7 +293,7 @@ function HistoryDetails(props) {
|
||||
);
|
||||
}
|
||||
|
||||
if (eventType === 'albumImportIncomplete') {
|
||||
if (eventType === 'bookImportIncomplete') {
|
||||
const {
|
||||
statusMessages
|
||||
} = data;
|
||||
|
||||
@@ -17,16 +17,16 @@ function getHeaderTitle(eventType) {
|
||||
return 'Grabbed';
|
||||
case 'downloadFailed':
|
||||
return 'Download Failed';
|
||||
case 'trackFileImported':
|
||||
return 'Track Imported';
|
||||
case 'trackFileDeleted':
|
||||
return 'Track File Deleted';
|
||||
case 'trackFileRenamed':
|
||||
return 'Track File Renamed';
|
||||
case 'trackFileRetagged':
|
||||
return 'Track File Tags Updated';
|
||||
case 'albumImportIncomplete':
|
||||
return 'Album Import Incomplete';
|
||||
case 'bookFileImported':
|
||||
return 'Book Imported';
|
||||
case 'bookFileDeleted':
|
||||
return 'Book File Deleted';
|
||||
case 'bookFileRenamed':
|
||||
return 'Book File Renamed';
|
||||
case 'bookFileRetagged':
|
||||
return 'Book File Tags Updated';
|
||||
case 'bookImportIncomplete':
|
||||
return 'Book Import Incomplete';
|
||||
case 'downloadImported':
|
||||
return 'Download Completed';
|
||||
case 'downloadIgnored':
|
||||
|
||||
@@ -22,7 +22,7 @@ class History extends Component {
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
// Don't update when fetching has completed if items have changed,
|
||||
// before albums start fetching or when albums start fetching.
|
||||
// before books start fetching or when books start fetching.
|
||||
|
||||
if (
|
||||
(
|
||||
@@ -30,7 +30,7 @@ class History extends Component {
|
||||
nextProps.isPopulated &&
|
||||
hasDifferentItems(this.props.items, nextProps.items)
|
||||
) ||
|
||||
(!this.props.isAlbumsFetching && nextProps.isAlbumsFetching)
|
||||
(!this.props.isBooksFetching && nextProps.isBooksFetching)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -51,17 +51,17 @@ class History extends Component {
|
||||
selectedFilterKey,
|
||||
filters,
|
||||
totalRecords,
|
||||
isAlbumsFetching,
|
||||
isAlbumsPopulated,
|
||||
albumsError,
|
||||
isBooksFetching,
|
||||
isBooksPopulated,
|
||||
booksError,
|
||||
onFilterSelect,
|
||||
onFirstPagePress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const isFetchingAny = isFetching || isAlbumsFetching;
|
||||
const isAllPopulated = isPopulated && (isAlbumsPopulated || !items.length);
|
||||
const hasError = error || albumsError;
|
||||
const isFetchingAny = isFetching || isBooksFetching;
|
||||
const isAllPopulated = isPopulated && (isBooksPopulated || !items.length);
|
||||
const hasError = error || booksError;
|
||||
|
||||
return (
|
||||
<PageContent title="History">
|
||||
@@ -109,7 +109,7 @@ class History extends Component {
|
||||
|
||||
{
|
||||
// If history isPopulated and it's empty show no history found and don't
|
||||
// wait for the albums to populate because they are never coming.
|
||||
// wait for the books to populate because they are never coming.
|
||||
|
||||
isPopulated && !hasError && !items.length &&
|
||||
<div>
|
||||
@@ -162,9 +162,9 @@ History.propTypes = {
|
||||
selectedFilterKey: PropTypes.string.isRequired,
|
||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
totalRecords: PropTypes.number,
|
||||
isAlbumsFetching: PropTypes.bool.isRequired,
|
||||
isAlbumsPopulated: PropTypes.bool.isRequired,
|
||||
albumsError: PropTypes.object,
|
||||
isBooksFetching: PropTypes.bool.isRequired,
|
||||
isBooksPopulated: PropTypes.bool.isRequired,
|
||||
booksError: PropTypes.object,
|
||||
onFilterSelect: PropTypes.func.isRequired,
|
||||
onFirstPagePress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -7,23 +7,18 @@ import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||
import selectUniqueIds from 'Utilities/Object/selectUniqueIds';
|
||||
import withCurrentPage from 'Components/withCurrentPage';
|
||||
import * as historyActions from 'Store/Actions/historyActions';
|
||||
import { fetchAlbums, clearAlbums } from 'Store/Actions/albumActions';
|
||||
import { fetchTracks, clearTracks } from 'Store/Actions/trackActions';
|
||||
import { fetchBooks, clearBooks } from 'Store/Actions/bookActions';
|
||||
import History from './History';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.history,
|
||||
(state) => state.albums,
|
||||
(state) => state.tracks,
|
||||
(history, albums, tracks) => {
|
||||
(state) => state.books,
|
||||
(history, books) => {
|
||||
return {
|
||||
isAlbumsFetching: albums.isFetching,
|
||||
isAlbumsPopulated: albums.isPopulated,
|
||||
albumsError: albums.error,
|
||||
isTracksFetching: tracks.isFetching,
|
||||
isTracksPopulated: tracks.isPopulated,
|
||||
tracksError: tracks.error,
|
||||
isBooksFetching: books.isFetching,
|
||||
isBooksPopulated: books.isPopulated,
|
||||
booksError: books.error,
|
||||
...history
|
||||
};
|
||||
}
|
||||
@@ -32,10 +27,8 @@ function createMapStateToProps() {
|
||||
|
||||
const mapDispatchToProps = {
|
||||
...historyActions,
|
||||
fetchAlbums,
|
||||
clearAlbums,
|
||||
fetchTracks,
|
||||
clearTracks
|
||||
fetchBooks,
|
||||
clearBooks
|
||||
};
|
||||
|
||||
class HistoryConnector extends Component {
|
||||
@@ -62,16 +55,10 @@ class HistoryConnector extends Component {
|
||||
componentDidUpdate(prevProps) {
|
||||
if (hasDifferentItems(prevProps.items, this.props.items)) {
|
||||
const bookIds = selectUniqueIds(this.props.items, 'bookId');
|
||||
const trackIds = selectUniqueIds(this.props.items, 'trackId');
|
||||
if (bookIds.length) {
|
||||
this.props.fetchAlbums({ bookIds });
|
||||
this.props.fetchBooks({ bookIds });
|
||||
} else {
|
||||
this.props.clearAlbums();
|
||||
}
|
||||
if (trackIds.length) {
|
||||
this.props.fetchTracks({ trackIds });
|
||||
} else {
|
||||
this.props.clearTracks();
|
||||
this.props.clearBooks();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,8 +66,7 @@ class HistoryConnector extends Component {
|
||||
componentWillUnmount() {
|
||||
unregisterPagePopulator(this.repopulate);
|
||||
this.props.clearHistory();
|
||||
this.props.clearAlbums();
|
||||
this.props.clearTracks();
|
||||
this.props.clearBooks();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -162,10 +148,8 @@ HistoryConnector.propTypes = {
|
||||
setHistoryFilter: PropTypes.func.isRequired,
|
||||
setHistoryTableOption: PropTypes.func.isRequired,
|
||||
clearHistory: PropTypes.func.isRequired,
|
||||
fetchAlbums: PropTypes.func.isRequired,
|
||||
clearAlbums: PropTypes.func.isRequired,
|
||||
fetchTracks: PropTypes.func.isRequired,
|
||||
clearTracks: PropTypes.func.isRequired
|
||||
fetchBooks: PropTypes.func.isRequired,
|
||||
clearBooks: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default withCurrentPage(
|
||||
|
||||
@@ -9,19 +9,19 @@ function getIconName(eventType) {
|
||||
switch (eventType) {
|
||||
case 'grabbed':
|
||||
return icons.DOWNLOADING;
|
||||
case 'artistFolderImported':
|
||||
case 'authorFolderImported':
|
||||
return icons.DRIVE;
|
||||
case 'trackFileImported':
|
||||
case 'bookFileImported':
|
||||
return icons.DOWNLOADED;
|
||||
case 'downloadFailed':
|
||||
return icons.DOWNLOADING;
|
||||
case 'trackFileDeleted':
|
||||
case 'bookFileDeleted':
|
||||
return icons.DELETE;
|
||||
case 'trackFileRenamed':
|
||||
case 'bookFileRenamed':
|
||||
return icons.ORGANIZE;
|
||||
case 'trackFileRetagged':
|
||||
case 'bookFileRetagged':
|
||||
return icons.RETAG;
|
||||
case 'albumImportIncomplete':
|
||||
case 'bookImportIncomplete':
|
||||
return icons.DOWNLOADED;
|
||||
case 'downloadImported':
|
||||
return icons.DOWNLOADED;
|
||||
@@ -36,7 +36,7 @@ function getIconKind(eventType) {
|
||||
switch (eventType) {
|
||||
case 'downloadFailed':
|
||||
return kinds.DANGER;
|
||||
case 'albumImportIncomplete':
|
||||
case 'bookImportIncomplete':
|
||||
return kinds.WARNING;
|
||||
default:
|
||||
return kinds.DEFAULT;
|
||||
@@ -46,25 +46,25 @@ function getIconKind(eventType) {
|
||||
function getTooltip(eventType, data) {
|
||||
switch (eventType) {
|
||||
case 'grabbed':
|
||||
return `Album grabbed from ${data.indexer} and sent to ${data.downloadClient}`;
|
||||
case 'artistFolderImported':
|
||||
return 'Track imported from artist folder';
|
||||
case 'trackFileImported':
|
||||
return 'Track downloaded successfully and picked up from download client';
|
||||
return `Book grabbed from ${data.indexer} and sent to ${data.downloadClient}`;
|
||||
case 'authorFolderImported':
|
||||
return 'Book imported from author folder';
|
||||
case 'bookFileImported':
|
||||
return 'Book downloaded successfully and picked up from download client';
|
||||
case 'downloadFailed':
|
||||
return 'Album download failed';
|
||||
case 'trackFileDeleted':
|
||||
return 'Track file deleted';
|
||||
case 'trackFileRenamed':
|
||||
return 'Track file renamed';
|
||||
case 'trackFileRetagged':
|
||||
return 'Track file tags updated';
|
||||
case 'albumImportIncomplete':
|
||||
return 'Book download failed';
|
||||
case 'bookFileDeleted':
|
||||
return 'Book file deleted';
|
||||
case 'bookFileRenamed':
|
||||
return 'Book file renamed';
|
||||
case 'bookFileRetagged':
|
||||
return 'Book file tags updated';
|
||||
case 'bookImportIncomplete':
|
||||
return 'Files downloaded but not all could be imported';
|
||||
case 'downloadImported':
|
||||
return 'Download completed and successfully imported';
|
||||
case 'downloadIgnored':
|
||||
return 'Album Download Ignored';
|
||||
return 'Book Download Ignored';
|
||||
default:
|
||||
return 'Unknown event';
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import IconButton from 'Components/Link/IconButton';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import AlbumTitleLink from 'Album/AlbumTitleLink';
|
||||
import TrackQuality from 'Album/TrackQuality';
|
||||
import ArtistNameLink from 'Artist/ArtistNameLink';
|
||||
import BookTitleLink from 'Book/BookTitleLink';
|
||||
import BookQuality from 'Book/BookQuality';
|
||||
import AuthorNameLink from 'Author/AuthorNameLink';
|
||||
import HistoryEventTypeCell from './HistoryEventTypeCell';
|
||||
import HistoryDetailsModal from './Details/HistoryDetailsModal';
|
||||
import styles from './HistoryRow.css';
|
||||
@@ -51,8 +51,8 @@ class HistoryRow extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
artist,
|
||||
album,
|
||||
author,
|
||||
book,
|
||||
quality,
|
||||
qualityCutoffNotMet,
|
||||
eventType,
|
||||
@@ -66,7 +66,7 @@ class HistoryRow extends Component {
|
||||
onMarkAsFailedPress
|
||||
} = this.props;
|
||||
|
||||
if (!artist || !album) {
|
||||
if (!author || !book) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -96,9 +96,9 @@ class HistoryRow extends Component {
|
||||
if (name === 'authors.sortName') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<ArtistNameLink
|
||||
titleSlug={artist.titleSlug}
|
||||
artistName={artist.artistName}
|
||||
<AuthorNameLink
|
||||
titleSlug={author.titleSlug}
|
||||
authorName={author.authorName}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
@@ -107,10 +107,10 @@ class HistoryRow extends Component {
|
||||
if (name === 'books.title') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<AlbumTitleLink
|
||||
titleSlug={album.titleSlug}
|
||||
title={album.title}
|
||||
disambiguation={album.disambiguation}
|
||||
<BookTitleLink
|
||||
titleSlug={book.titleSlug}
|
||||
title={book.title}
|
||||
disambiguation={book.disambiguation}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
@@ -119,7 +119,7 @@ class HistoryRow extends Component {
|
||||
if (name === 'quality') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<TrackQuality
|
||||
<BookQuality
|
||||
quality={quality}
|
||||
isCutoffMet={qualityCutoffNotMet}
|
||||
/>
|
||||
@@ -206,8 +206,8 @@ class HistoryRow extends Component {
|
||||
|
||||
HistoryRow.propTypes = {
|
||||
bookId: PropTypes.number,
|
||||
artist: PropTypes.object.isRequired,
|
||||
album: PropTypes.object,
|
||||
author: PropTypes.object.isRequired,
|
||||
book: PropTypes.object,
|
||||
quality: PropTypes.object.isRequired,
|
||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
||||
eventType: PropTypes.string.isRequired,
|
||||
|
||||
@@ -3,23 +3,20 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchHistory, markAsFailed } from 'Store/Actions/historyActions';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import createAlbumSelector from 'Store/Selectors/createAlbumSelector';
|
||||
import createTrackSelector from 'Store/Selectors/createTrackSelector';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import createBookSelector from 'Store/Selectors/createBookSelector';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import HistoryRow from './HistoryRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
createAlbumSelector(),
|
||||
createTrackSelector(),
|
||||
createAuthorSelector(),
|
||||
createBookSelector(),
|
||||
createUISettingsSelector(),
|
||||
(artist, album, track, uiSettings) => {
|
||||
(author, book, uiSettings) => {
|
||||
return {
|
||||
artist,
|
||||
album,
|
||||
track,
|
||||
author,
|
||||
book,
|
||||
shortDateFormat: uiSettings.shortDateFormat,
|
||||
timeFormat: uiSettings.timeFormat
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ class Queue extends Component {
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
// Don't update when fetching has completed if items have changed,
|
||||
// before albums start fetching or when albums start fetching.
|
||||
// before books start fetching or when books start fetching.
|
||||
|
||||
if (
|
||||
this.props.isFetching &&
|
||||
@@ -53,7 +53,7 @@ class Queue extends Component {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.props.isAlbumsFetching && nextProps.isAlbumsFetching) {
|
||||
if (!this.props.isBooksFetching && nextProps.isBooksFetching) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -125,9 +125,9 @@ class Queue extends Component {
|
||||
isPopulated,
|
||||
error,
|
||||
items,
|
||||
isAlbumsFetching,
|
||||
isAlbumsPopulated,
|
||||
albumsError,
|
||||
isBooksFetching,
|
||||
isBooksPopulated,
|
||||
booksError,
|
||||
columns,
|
||||
totalRecords,
|
||||
isGrabbing,
|
||||
@@ -145,9 +145,9 @@ class Queue extends Component {
|
||||
isPendingSelected
|
||||
} = this.state;
|
||||
|
||||
const isRefreshing = isFetching || isAlbumsFetching || isRefreshMonitoredDownloadsExecuting;
|
||||
const isAllPopulated = isPopulated && (isAlbumsPopulated || !items.length || items.every((e) => !e.bookId));
|
||||
const hasError = error || albumsError;
|
||||
const isRefreshing = isFetching || isBooksFetching || isRefreshMonitoredDownloadsExecuting;
|
||||
const isAllPopulated = isPopulated && (isBooksPopulated || !items.length || items.every((e) => !e.bookId));
|
||||
const hasError = error || booksError;
|
||||
const selectedIds = this.getSelectedIds();
|
||||
const selectedCount = selectedIds.length;
|
||||
const disableSelectedActions = selectedCount === 0;
|
||||
@@ -280,9 +280,9 @@ Queue.propTypes = {
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
isAlbumsFetching: PropTypes.bool.isRequired,
|
||||
isAlbumsPopulated: PropTypes.bool.isRequired,
|
||||
albumsError: PropTypes.object,
|
||||
isBooksFetching: PropTypes.bool.isRequired,
|
||||
isBooksPopulated: PropTypes.bool.isRequired,
|
||||
booksError: PropTypes.object,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
totalRecords: PropTypes.number,
|
||||
isGrabbing: PropTypes.bool.isRequired,
|
||||
|
||||
@@ -9,21 +9,21 @@ import withCurrentPage from 'Components/withCurrentPage';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import * as queueActions from 'Store/Actions/queueActions';
|
||||
import { fetchAlbums, clearAlbums } from 'Store/Actions/albumActions';
|
||||
import { fetchBooks, clearBooks } from 'Store/Actions/bookActions';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import Queue from './Queue';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.albums,
|
||||
(state) => state.books,
|
||||
(state) => state.queue.options,
|
||||
(state) => state.queue.paged,
|
||||
createCommandExecutingSelector(commandNames.REFRESH_MONITORED_DOWNLOADS),
|
||||
(albums, options, queue, isRefreshMonitoredDownloadsExecuting) => {
|
||||
(books, options, queue, isRefreshMonitoredDownloadsExecuting) => {
|
||||
return {
|
||||
isAlbumsFetching: albums.isFetching,
|
||||
isAlbumsPopulated: albums.isPopulated,
|
||||
albumsError: albums.error,
|
||||
isBooksFetching: books.isFetching,
|
||||
isBooksPopulated: books.isPopulated,
|
||||
booksError: books.error,
|
||||
isRefreshMonitoredDownloadsExecuting,
|
||||
...options,
|
||||
...queue
|
||||
@@ -34,8 +34,8 @@ function createMapStateToProps() {
|
||||
|
||||
const mapDispatchToProps = {
|
||||
...queueActions,
|
||||
fetchAlbums,
|
||||
clearAlbums,
|
||||
fetchBooks,
|
||||
clearBooks,
|
||||
executeCommand
|
||||
};
|
||||
|
||||
@@ -65,15 +65,15 @@ class QueueConnector extends Component {
|
||||
const bookIds = selectUniqueIds(this.props.items, 'bookId');
|
||||
|
||||
if (bookIds.length) {
|
||||
this.props.fetchAlbums({ bookIds });
|
||||
this.props.fetchBooks({ bookIds });
|
||||
} else {
|
||||
this.props.clearAlbums();
|
||||
this.props.clearBooks();
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.props.includeUnknownArtistItems !==
|
||||
prevProps.includeUnknownArtistItems
|
||||
this.props.includeUnknownAuthorItems !==
|
||||
prevProps.includeUnknownAuthorItems
|
||||
) {
|
||||
this.repopulate();
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class QueueConnector extends Component {
|
||||
componentWillUnmount() {
|
||||
unregisterPagePopulator(this.repopulate);
|
||||
this.props.clearQueue();
|
||||
this.props.clearAlbums();
|
||||
this.props.clearBooks();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -166,7 +166,7 @@ class QueueConnector extends Component {
|
||||
QueueConnector.propTypes = {
|
||||
useCurrentPage: PropTypes.bool.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
includeUnknownArtistItems: PropTypes.bool.isRequired,
|
||||
includeUnknownAuthorItems: PropTypes.bool.isRequired,
|
||||
fetchQueue: PropTypes.func.isRequired,
|
||||
gotoQueueFirstPage: PropTypes.func.isRequired,
|
||||
gotoQueuePreviousPage: PropTypes.func.isRequired,
|
||||
@@ -178,8 +178,8 @@ QueueConnector.propTypes = {
|
||||
clearQueue: PropTypes.func.isRequired,
|
||||
grabQueueItems: PropTypes.func.isRequired,
|
||||
removeQueueItems: PropTypes.func.isRequired,
|
||||
fetchAlbums: PropTypes.func.isRequired,
|
||||
clearAlbums: PropTypes.func.isRequired,
|
||||
fetchBooks: PropTypes.func.isRequired,
|
||||
clearBooks: PropTypes.func.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ function QueueDetails(props) {
|
||||
return (
|
||||
<Icon
|
||||
name={icons.DOWNLOADING}
|
||||
title={`Album is downloading - ${progress.toFixed(1)}% ${title}`}
|
||||
title={`Book is downloading - ${progress.toFixed(1)}% ${title}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,18 +14,18 @@ class QueueOptions extends Component {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
includeUnknownArtistItems: props.includeUnknownArtistItems
|
||||
includeUnknownAuthorItems: props.includeUnknownAuthorItems
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
includeUnknownArtistItems
|
||||
includeUnknownAuthorItems
|
||||
} = this.props;
|
||||
|
||||
if (includeUnknownArtistItems !== prevProps.includeUnknownArtistItems) {
|
||||
if (includeUnknownAuthorItems !== prevProps.includeUnknownAuthorItems) {
|
||||
this.setState({
|
||||
includeUnknownArtistItems
|
||||
includeUnknownAuthorItems
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -48,19 +48,19 @@ class QueueOptions extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
includeUnknownArtistItems
|
||||
includeUnknownAuthorItems
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<FormGroup>
|
||||
<FormLabel>Show Unknown Artist Items</FormLabel>
|
||||
<FormLabel>Show Unknown Author Items</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="includeUnknownArtistItems"
|
||||
value={includeUnknownArtistItems}
|
||||
helpText="Show items without a artist in the queue, this could include removed artists, movies or anything else in Readarr's category"
|
||||
name="includeUnknownAuthorItems"
|
||||
value={includeUnknownAuthorItems}
|
||||
helpText="Show items without a author in the queue, this could include removed authors, movies or anything else in Readarr's category"
|
||||
onChange={this.onOptionChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
@@ -70,7 +70,7 @@ class QueueOptions extends Component {
|
||||
}
|
||||
|
||||
QueueOptions.propTypes = {
|
||||
includeUnknownArtistItems: PropTypes.bool.isRequired,
|
||||
includeUnknownAuthorItems: PropTypes.bool.isRequired,
|
||||
onOptionChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import Icon from 'Components/Icon';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import ProtocolLabel from 'Activity/Queue/ProtocolLabel';
|
||||
import AlbumTitleLink from 'Album/AlbumTitleLink';
|
||||
import TrackQuality from 'Album/TrackQuality';
|
||||
import BookTitleLink from 'Book/BookTitleLink';
|
||||
import BookQuality from 'Book/BookQuality';
|
||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||
import ArtistNameLink from 'Artist/ArtistNameLink';
|
||||
import AuthorNameLink from 'Author/AuthorNameLink';
|
||||
import QueueStatusCell from './QueueStatusCell';
|
||||
import TimeleftCell from './TimeleftCell';
|
||||
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
||||
@@ -71,8 +71,8 @@ class QueueRow extends Component {
|
||||
trackedDownloadState,
|
||||
statusMessages,
|
||||
errorMessage,
|
||||
artist,
|
||||
album,
|
||||
author,
|
||||
book,
|
||||
quality,
|
||||
protocol,
|
||||
indexer,
|
||||
@@ -141,10 +141,10 @@ class QueueRow extends Component {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{
|
||||
artist ?
|
||||
<ArtistNameLink
|
||||
titleSlug={artist.titleSlug}
|
||||
artistName={artist.artistName}
|
||||
author ?
|
||||
<AuthorNameLink
|
||||
titleSlug={author.titleSlug}
|
||||
authorName={author.authorName}
|
||||
/> :
|
||||
title
|
||||
}
|
||||
@@ -156,11 +156,11 @@ class QueueRow extends Component {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
{
|
||||
album ?
|
||||
<AlbumTitleLink
|
||||
titleSlug={album.titleSlug}
|
||||
title={album.title}
|
||||
disambiguation={album.disambiguation}
|
||||
book ?
|
||||
<BookTitleLink
|
||||
titleSlug={book.titleSlug}
|
||||
title={book.title}
|
||||
disambiguation={book.disambiguation}
|
||||
/> :
|
||||
'-'
|
||||
}
|
||||
@@ -169,11 +169,11 @@ class QueueRow extends Component {
|
||||
}
|
||||
|
||||
if (name === 'books.releaseDate') {
|
||||
if (album) {
|
||||
if (book) {
|
||||
return (
|
||||
<RelativeDateCellConnector
|
||||
key={name}
|
||||
date={album.releaseDate}
|
||||
date={book.releaseDate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -188,7 +188,7 @@ class QueueRow extends Component {
|
||||
if (name === 'quality') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<TrackQuality
|
||||
<BookQuality
|
||||
quality={quality}
|
||||
/>
|
||||
</TableRowCell>
|
||||
@@ -335,7 +335,7 @@ class QueueRow extends Component {
|
||||
<RemoveQueueItemModal
|
||||
isOpen={isRemoveQueueItemModalOpen}
|
||||
sourceTitle={title}
|
||||
canIgnore={!!(artist && album)}
|
||||
canIgnore={!!(author && book)}
|
||||
onRemovePress={this.onRemoveQueueItemModalConfirmed}
|
||||
onModalClose={this.onRemoveQueueItemModalClose}
|
||||
/>
|
||||
@@ -354,8 +354,8 @@ QueueRow.propTypes = {
|
||||
trackedDownloadState: PropTypes.string,
|
||||
statusMessages: PropTypes.arrayOf(PropTypes.object),
|
||||
errorMessage: PropTypes.string,
|
||||
artist: PropTypes.object,
|
||||
album: PropTypes.object,
|
||||
author: PropTypes.object,
|
||||
book: PropTypes.object,
|
||||
quality: PropTypes.object.isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
indexer: PropTypes.string,
|
||||
|
||||
@@ -4,25 +4,25 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { grabQueueItem, removeQueueItem } from 'Store/Actions/queueActions';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import createAlbumSelector from 'Store/Selectors/createAlbumSelector';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import createBookSelector from 'Store/Selectors/createBookSelector';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import QueueRow from './QueueRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
createAlbumSelector(),
|
||||
createAuthorSelector(),
|
||||
createBookSelector(),
|
||||
createUISettingsSelector(),
|
||||
(artist, album, uiSettings) => {
|
||||
(author, book, uiSettings) => {
|
||||
const result = _.pick(uiSettings, [
|
||||
'showRelativeDates',
|
||||
'shortDateFormat',
|
||||
'timeFormat'
|
||||
]);
|
||||
|
||||
result.artist = artist;
|
||||
result.album = album;
|
||||
result.author = author;
|
||||
result.book = book;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -63,7 +63,7 @@ class QueueRowConnector extends Component {
|
||||
|
||||
QueueRowConnector.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
album: PropTypes.object,
|
||||
book: PropTypes.object,
|
||||
grabQueueItem: PropTypes.func.isRequired,
|
||||
removeQueueItem: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -9,8 +9,8 @@ function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.app,
|
||||
(state) => state.queue.status,
|
||||
(state) => state.queue.options.includeUnknownArtistItems,
|
||||
(app, status, includeUnknownArtistItems) => {
|
||||
(state) => state.queue.options.includeUnknownAuthorItems,
|
||||
(app, status, includeUnknownAuthorItems) => {
|
||||
const {
|
||||
errors,
|
||||
warnings,
|
||||
@@ -25,9 +25,9 @@ function createMapStateToProps() {
|
||||
isReconnecting: app.isReconnecting,
|
||||
isPopulated: status.isPopulated,
|
||||
...status.item,
|
||||
count: includeUnknownArtistItems ? totalCount : count,
|
||||
errors: includeUnknownArtistItems ? errors || unknownErrors : errors,
|
||||
warnings: includeUnknownArtistItems ? warnings || unknownWarnings : warnings
|
||||
count: includeUnknownAuthorItems ? totalCount : count,
|
||||
errors: includeUnknownAuthorItems ? errors || unknownErrors : errors,
|
||||
warnings: includeUnknownAuthorItems ? warnings || unknownWarnings : warnings
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
function ArtistMetadataProfilePopoverContent() {
|
||||
function AuthorMetadataProfilePopoverContent() {
|
||||
return (
|
||||
<div>
|
||||
Select 'None' to only include items manually added via search or that match files on disk
|
||||
@@ -8,4 +8,4 @@ function ArtistMetadataProfilePopoverContent() {
|
||||
);
|
||||
}
|
||||
|
||||
export default ArtistMetadataProfilePopoverContent;
|
||||
export default AuthorMetadataProfilePopoverContent;
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
|
||||
function ArtistMonitoringOptionsPopoverContent() {
|
||||
function AuthorMonitoringOptionsPopoverContent() {
|
||||
return (
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
@@ -43,4 +43,4 @@ function ArtistMonitoringOptionsPopoverContent() {
|
||||
);
|
||||
}
|
||||
|
||||
export default ArtistMonitoringOptionsPopoverContent;
|
||||
export default AuthorMonitoringOptionsPopoverContent;
|
||||
@@ -1,116 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createArtistClientSideCollectionItemsSelector from 'Store/Selectors/createArtistClientSideCollectionItemsSelector';
|
||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||
import { setAlbumStudioSort, setAlbumStudioFilter, saveAlbumStudio } from 'Store/Actions/albumStudioActions';
|
||||
import { fetchAlbums, clearAlbums } from 'Store/Actions/albumActions';
|
||||
import AlbumStudio from './AlbumStudio';
|
||||
|
||||
function createAlbumFetchStateSelector() {
|
||||
return createSelector(
|
||||
(state) => state.albums.items.length,
|
||||
(state) => state.albums.isFetching,
|
||||
(state) => state.albums.isPopulated,
|
||||
(length, isFetching, isPopulated) => {
|
||||
const albumCount = (!isFetching && isPopulated) ? length : 0;
|
||||
return {
|
||||
albumCount,
|
||||
isFetching,
|
||||
isPopulated
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createAlbumFetchStateSelector(),
|
||||
createArtistClientSideCollectionItemsSelector('albumStudio'),
|
||||
createDimensionsSelector(),
|
||||
(albums, artist, dimensionsState) => {
|
||||
const isPopulated = albums.isPopulated && artist.isPopulated;
|
||||
const isFetching = artist.isFetching || albums.isFetching;
|
||||
return {
|
||||
...artist,
|
||||
isPopulated,
|
||||
isFetching,
|
||||
albumCount: albums.albumCount,
|
||||
isSmallScreen: dimensionsState.isSmallScreen
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchAlbums,
|
||||
clearAlbums,
|
||||
setAlbumStudioSort,
|
||||
setAlbumStudioFilter,
|
||||
saveAlbumStudio
|
||||
};
|
||||
|
||||
class AlbumStudioConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.populate();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unpopulate();
|
||||
}
|
||||
|
||||
//
|
||||
// Control
|
||||
|
||||
populate = () => {
|
||||
this.props.fetchAlbums();
|
||||
}
|
||||
|
||||
unpopulate = () => {
|
||||
this.props.clearAlbums();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.setAlbumStudioSort({ sortKey });
|
||||
}
|
||||
|
||||
onFilterSelect = (selectedFilterKey) => {
|
||||
this.props.setAlbumStudioFilter({ selectedFilterKey });
|
||||
}
|
||||
|
||||
onUpdateSelectedPress = (payload) => {
|
||||
this.props.saveAlbumStudio(payload);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<AlbumStudio
|
||||
{...this.props}
|
||||
onSortPress={this.onSortPress}
|
||||
onFilterSelect={this.onFilterSelect}
|
||||
onUpdateSelectedPress={this.onUpdateSelectedPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AlbumStudioConnector.propTypes = {
|
||||
setAlbumStudioSort: PropTypes.func.isRequired,
|
||||
setAlbumStudioFilter: PropTypes.func.isRequired,
|
||||
fetchAlbums: PropTypes.func.isRequired,
|
||||
clearAlbums: PropTypes.func.isRequired,
|
||||
saveAlbumStudio: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AlbumStudioConnector);
|
||||
@@ -1,94 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import { toggleArtistMonitored } from 'Store/Actions/artistActions';
|
||||
import { toggleAlbumsMonitored } from 'Store/Actions/albumActions';
|
||||
import AlbumStudioRow from './AlbumStudioRow';
|
||||
|
||||
// Use a const to share the reselect cache between instances
|
||||
const getAlbumMap = createSelector(
|
||||
(state) => state.albums.items,
|
||||
(albums) => {
|
||||
return albums.reduce((acc, curr) => {
|
||||
(acc[curr.authorId] = acc[curr.authorId] || []).push(curr);
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
);
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
getAlbumMap,
|
||||
(artist, albumMap) => {
|
||||
const albumsInArtist = albumMap.hasOwnProperty(artist.id) ? albumMap[artist.id] : [];
|
||||
const sortedAlbums = _.orderBy(albumsInArtist, 'releaseDate', 'desc');
|
||||
|
||||
return {
|
||||
...artist,
|
||||
authorId: artist.id,
|
||||
artistName: artist.artistName,
|
||||
monitored: artist.monitored,
|
||||
status: artist.status,
|
||||
isSaving: artist.isSaving,
|
||||
albums: sortedAlbums
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
toggleArtistMonitored,
|
||||
toggleAlbumsMonitored
|
||||
};
|
||||
|
||||
class AlbumStudioRowConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onArtistMonitoredPress = () => {
|
||||
const {
|
||||
authorId,
|
||||
monitored
|
||||
} = this.props;
|
||||
|
||||
this.props.toggleArtistMonitored({
|
||||
authorId,
|
||||
monitored: !monitored
|
||||
});
|
||||
}
|
||||
|
||||
onAlbumMonitoredPress = (bookId, monitored) => {
|
||||
const bookIds = [bookId];
|
||||
this.props.toggleAlbumsMonitored({
|
||||
bookIds,
|
||||
monitored
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<AlbumStudioRow
|
||||
{...this.props}
|
||||
onArtistMonitoredPress={this.onArtistMonitoredPress}
|
||||
onAlbumMonitoredPress={this.onAlbumMonitoredPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AlbumStudioRowConnector.propTypes = {
|
||||
authorId: PropTypes.number.isRequired,
|
||||
monitored: PropTypes.bool.isRequired,
|
||||
toggleArtistMonitored: PropTypes.func.isRequired,
|
||||
toggleAlbumsMonitored: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AlbumStudioRowConnector);
|
||||
@@ -4,13 +4,13 @@ import { Route, Redirect } from 'react-router-dom';
|
||||
import getPathWithUrlBase from 'Utilities/getPathWithUrlBase';
|
||||
import NotFound from 'Components/NotFound';
|
||||
import Switch from 'Components/Router/Switch';
|
||||
import ArtistIndexConnector from 'Artist/Index/ArtistIndexConnector';
|
||||
import AuthorIndexConnector from 'Author/Index/AuthorIndexConnector';
|
||||
import AddNewItemConnector from 'Search/AddNewItemConnector';
|
||||
import ArtistEditorConnector from 'Artist/Editor/ArtistEditorConnector';
|
||||
import AlbumStudioConnector from 'AlbumStudio/AlbumStudioConnector';
|
||||
import AuthorEditorConnector from 'Author/Editor/AuthorEditorConnector';
|
||||
import BookshelfConnector from 'Bookshelf/BookshelfConnector';
|
||||
import UnmappedFilesTableConnector from 'UnmappedFiles/UnmappedFilesTableConnector';
|
||||
import ArtistDetailsPageConnector from 'Artist/Details/ArtistDetailsPageConnector';
|
||||
import AlbumDetailsPageConnector from 'Album/Details/AlbumDetailsPageConnector';
|
||||
import AuthorDetailsPageConnector from 'Author/Details/AuthorDetailsPageConnector';
|
||||
import BookDetailsPageConnector from 'Book/Details/BookDetailsPageConnector';
|
||||
import CalendarPageConnector from 'Calendar/CalendarPageConnector';
|
||||
import HistoryConnector from 'Activity/History/HistoryConnector';
|
||||
import QueueConnector from 'Activity/Queue/QueueConnector';
|
||||
@@ -44,13 +44,13 @@ function AppRoutes(props) {
|
||||
return (
|
||||
<Switch>
|
||||
{/*
|
||||
Artist
|
||||
Author
|
||||
*/}
|
||||
|
||||
<Route
|
||||
exact={true}
|
||||
path="/"
|
||||
component={ArtistIndexConnector}
|
||||
component={AuthorIndexConnector}
|
||||
/>
|
||||
|
||||
{
|
||||
@@ -76,13 +76,13 @@ function AppRoutes(props) {
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/artisteditor"
|
||||
component={ArtistEditorConnector}
|
||||
path="/authoreditor"
|
||||
component={AuthorEditorConnector}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/albumstudio"
|
||||
component={AlbumStudioConnector}
|
||||
path="/bookshelf"
|
||||
component={BookshelfConnector}
|
||||
/>
|
||||
|
||||
<Route
|
||||
@@ -92,12 +92,12 @@ function AppRoutes(props) {
|
||||
|
||||
<Route
|
||||
path="/author/:titleSlug"
|
||||
component={ArtistDetailsPageConnector}
|
||||
component={AuthorDetailsPageConnector}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/book/:titleSlug"
|
||||
component={AlbumDetailsPageConnector}
|
||||
component={BookDetailsPageConnector}
|
||||
/>
|
||||
|
||||
{/*
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/* eslint max-params: 0 */
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import createTrackFileSelector from 'Store/Selectors/createTrackFileSelector';
|
||||
import AlbumRow from './AlbumRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
createTrackFileSelector(),
|
||||
(artist = {}, trackFile) => {
|
||||
return {
|
||||
artistMonitored: artist.monitored,
|
||||
trackFilePath: trackFile ? trackFile.path : null
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
export default connect(createMapStateToProps)(AlbumRow);
|
||||
@@ -1,5 +0,0 @@
|
||||
.albumFolder {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 150px;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
import ArtistHistoryContentConnector from 'Artist/History/ArtistHistoryContentConnector';
|
||||
import ArtistHistoryTableContent from 'Artist/History/ArtistHistoryTableContent';
|
||||
|
||||
function ArtistHistoryTable(props) {
|
||||
const {
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<ArtistHistoryContentConnector
|
||||
component={ArtistHistoryTableContent}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
ArtistHistoryTable.propTypes = {
|
||||
};
|
||||
|
||||
export default ArtistHistoryTable;
|
||||
@@ -1,102 +0,0 @@
|
||||
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
|
||||
|
||||
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 {
|
||||
id,
|
||||
isRefreshingArtist,
|
||||
onRefreshArtistPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
isEditArtistModalOpen,
|
||||
isDeleteArtistModalOpen
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<VirtualTableRowCell
|
||||
{...otherProps}
|
||||
>
|
||||
<SpinnerIconButton
|
||||
name={icons.REFRESH}
|
||||
title="Refresh Artist"
|
||||
isSpinning={isRefreshingArtist}
|
||||
onPress={onRefreshArtistPress}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
name={icons.EDIT}
|
||||
title="Edit Artist"
|
||||
onPress={this.onEditArtistPress}
|
||||
/>
|
||||
|
||||
<EditArtistModalConnector
|
||||
isOpen={isEditArtistModalOpen}
|
||||
authorId={id}
|
||||
onModalClose={this.onEditArtistModalClose}
|
||||
onDeleteArtistPress={this.onDeleteArtistPress}
|
||||
/>
|
||||
|
||||
<DeleteArtistModal
|
||||
isOpen={isDeleteArtistModalOpen}
|
||||
authorId={id}
|
||||
onModalClose={this.onDeleteArtistModalClose}
|
||||
/>
|
||||
</VirtualTableRowCell>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ArtistIndexActionsCell.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
isRefreshingArtist: PropTypes.bool.isRequired,
|
||||
onRefreshArtistPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistIndexActionsCell;
|
||||
@@ -1,13 +0,0 @@
|
||||
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);
|
||||
@@ -1,12 +1,12 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ArtistImage from './ArtistImage';
|
||||
import AuthorImage from './AuthorImage';
|
||||
|
||||
const bannerPlaceholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+gAAAC5AgMAAADG9/24AAAADFBMVEUyMjI7Ozs1NTU4ODjgOsZvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QkRBgAc5PUQ8QAAB7tJREFUeNrtnb1rHEsMwMdjArtrUrpf3uMg2cOl+5QpXWRvjQkXlyHVK8MVYXFl3F+/GAzrNfdSuXkQnH9ie/Oqw32aFM6bkTQfd85rHrwi0qjJJql+pxlJo9FISv0XqX8mKkmSJEmSJEmSJEmSJEmSJEmS5NeVYrh7HDqJ5DeY23kQB64/u7zW91amzgXqvRqjfGYvarnfxqncuaQlf72Zxv4gSOluudOfjRy1Hzh1L+nPj+KU3jh0MWr3Sp87dDFq98An/msmg3zPW/aFR6/vRaBPglML6Mci0H0g92MYfrjvRgJ5TrDvhuFyGIZv9NdTOev93dDry1Z59mM56/2hU8WZcefFjZgVT/Z90SlEV9VKio3HeKYZLLSGII6WPP+oBv3ZwkL3iE4JG/ZRjUYb16mAripUO/c4Hl3bd/juCF19FuHeJn6nK90VBh0s3SjBvcFW/wTrvfBa118EbHY8qmMOtmgdupoKOLTvAmOH1k059LKAX+Qra/TncExH4hcBXV3Zf/+Dv5Vb43cfod/wt3PLsN5VF6HDiudt56IbB23Ql5/oZ8A7CfZWbgF61sap62XdlOZTZ2rF3c7ltNW1+f7LuDez/uc6uDfO8dwkbPXcKF8vPS9sds527pC2utH0uCb0Jmz2t8wNvN3q2jj4ntDJna94m3g4sb7HH8Gue0RH4Je8z61g4GGr79Wz1qHjbi94m/jcH1IOccsj+lt/sOFr4m0EPz+3XyNueURvSmfn+Ebx1rctMvOxU8foqOwVa+9mfdvHDH+DdYQOxAesvZslvc/wo4vQZy6eY+vdrCVrut/AyTW9CWvO3E1ra4L6i5Fxoka7MDan45vTOmx2MPGc0QH52Tb6kTPxXNGtW5/Tnl+oGP2N/dstY8eeO2M+bqM3zvVxRT+gS8Vdl5/z6CaCzfx/c41o3pP2+030UzrAHDNGv8d4FvMVAf1I4c07V/QlnNsyG9TN23ID/ZjObnO+6CZmydS+y8oG9Dfk2Gd80WcW3Rv44e5bZOLtD8EUHbRq0V0F/DAMn8fQ2UUv2UayEMxplaFvM0G7QR/ufqBcmH/gG85Z9BM8rMO5bdiUDu4ceaLvUjqWfJveQm8hpvnKFv1jOLxUTtkoBWf0nIK5Cd6wO207dAznTlmjH+K630KvuKPbOHYffJsOexykx0iWJ7pNRf+ttEXvW1U49F7ZbJ26RHSe6ehn5NRGMPBVQAfP12EQf8QZPTMxXac30M1RxtaWXLBFn3j0eRsHNIBuCycLtqfWCQZrBcZ0W+gVhXts0RtEXyit4zDOoE/N/5yNzNF3oVB0A93I6ise7bijr1XwbYiurySg7xjfpp+g375mjj5D9HYD3fr6YvkKcxU80Q8duvVt2+gjob/ljX7yFH1a80dvDfpia8GXqp3Wr1XJXevljvFt2QZ6a6tJ2Gvd2Pa8Xuu2iNB7o++r+lUJlQas93oOl04be73Ut7y1Ts4tn/0EfaxZOzcKaXIsqNiI4QtE5x7N7Z08RZ9CKpb38cWs9V26b4sDWURnr3W9foq+gpM8a3S4V+rhKUCcqrBXTufMUxU2QWUTkFtZGls3pjgnqJ4FdB1lZAd8/qMEZGRtAlJvJqONb2syzuj2CuK+1YU5reg2CzGNyqq6fpOpku8VRI7lchX+7SIy8NdQSaKn3K8bsWJOlZGBX2Ed0bUIdBXftJpzG1h2vjetWFqgHXrhczTWtx2h8llXVTytI4FHnaes0bGMqNhC1+jUXmFMx7iCaq6qy4HqxR7dFfMUtc34LQCVDPppcM13Kie5RmTGJYNUKBraDL57xEKalS0UzTgXiiqn1X3fRxR0bBf6TLEvD4aKkslTdO5F4fQUIHseo5ugfrShewbWjvsriHxT65WAByAHVCT7u0fvoKDC+rZClZyf/eSAngUTj1pfCXjshU/8smDiPTr7J374sDPDfI1Hd4cX1g874TmvRc+30U8V9+e88Ig7U6V26Ofk21rzg1ScH3Hj033bTXIZab2iGG6PdWOaQzLx7cShQ0FFfdxqlfFu2DAhxw4vf5zWV3hYZ96m47l7u0+DMAgdu1Dxbs4St+Rx6MbAwzIveLfk8Y2Y9J5HP1tij3DmjZii9lujQy9GXO/M22/5pmvUVdWiV3RkYd50zbfaI7Vb9GmDD0C4t9qLGiy+JPTVKT4A4d5gMY866N4i+p9KuUM767aavpkqLnmDDl10S8W/mSq20O3I3n8x6AXufP4tdKlxcpkZ4HN43FZ0mT3GCmicTO2ysW2uVXmFWp/yb5cdN0kHrb/ADwFN0uPW+ICOt+0SWuPjkW2tPTr+CjcSphjiGIzWodPQGwljMGj4Se/Q0bfJGH4Sj7zx6DJG3tCgo4HQoYiukDHoKB5vZdCtb9MrIUM7DyK169Zu+mEUMtQsjLJrtT7vWl2IGWXnBxjaFwFnraQBhmFs5dDqi66SNLYyGlY6XMgaVip4RG00mHh2LWwwcRhHPVsJG0cdhpDProQNIQ+j52e30kbPa19B5T64H9Wfqn0mTelB7Y04pWMFfCQf5JDTjYOTuSClu5QUSa9EyU0gf5BF7iaP2zxdq6TJjUydgxTD3aPvm5wkSZIkSZIkSZIkSZIkSZIk+Z9kv/534U2+Uyf0XwP9H83PZBlAqkdjAAAAAElFTkSuQmCC';
|
||||
|
||||
function ArtistBanner(props) {
|
||||
function AuthorBanner(props) {
|
||||
return (
|
||||
<ArtistImage
|
||||
<AuthorImage
|
||||
{...props}
|
||||
coverType="banner"
|
||||
placeholder={bannerPlaceholder}
|
||||
@@ -14,12 +14,12 @@ function ArtistBanner(props) {
|
||||
);
|
||||
}
|
||||
|
||||
ArtistBanner.propTypes = {
|
||||
AuthorBanner.propTypes = {
|
||||
size: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
ArtistBanner.defaultProps = {
|
||||
AuthorBanner.defaultProps = {
|
||||
size: 70
|
||||
};
|
||||
|
||||
export default ArtistBanner;
|
||||
export default AuthorBanner;
|
||||
@@ -17,7 +17,7 @@ function getUrl(image, coverType, size) {
|
||||
}
|
||||
}
|
||||
|
||||
class ArtistImage extends Component {
|
||||
class AuthorImage extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -179,7 +179,7 @@ class ArtistImage extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistImage.propTypes = {
|
||||
AuthorImage.propTypes = {
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
@@ -192,10 +192,10 @@ ArtistImage.propTypes = {
|
||||
onLoad: PropTypes.func
|
||||
};
|
||||
|
||||
ArtistImage.defaultProps = {
|
||||
AuthorImage.defaultProps = {
|
||||
size: 250,
|
||||
lazy: true,
|
||||
overflow: false
|
||||
};
|
||||
|
||||
export default ArtistImage;
|
||||
export default AuthorImage;
|
||||
@@ -2,19 +2,19 @@ import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Link from 'Components/Link/Link';
|
||||
|
||||
function ArtistNameLink({ titleSlug, artistName }) {
|
||||
function AuthorNameLink({ titleSlug, authorName }) {
|
||||
const link = `/author/${titleSlug}`;
|
||||
|
||||
return (
|
||||
<Link to={link}>
|
||||
{artistName}
|
||||
{authorName}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
ArtistNameLink.propTypes = {
|
||||
AuthorNameLink.propTypes = {
|
||||
titleSlug: PropTypes.string.isRequired,
|
||||
artistName: PropTypes.string.isRequired
|
||||
authorName: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default ArtistNameLink;
|
||||
export default AuthorNameLink;
|
||||
@@ -1,12 +1,12 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ArtistImage from './ArtistImage';
|
||||
import AuthorImage from './AuthorImage';
|
||||
|
||||
const posterPlaceholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPcAAAD3AQMAAAD7QlAQAAAABlBMVEUnJychISEIs8G4AAAEFklEQVRYw+2YMdPOQBDH95KQDEVSMKOj1FFQJx9BQc0nkeuUvoLS+AQ6MQqlRu8xCjpUgsjK7iXz2t1LMsbo8i/ey5vfc3e7m7tk9+DQoUOHDpGe3bu7hS8BwJ117BoAOLfOb/Hf62s4EY1VNrcPVvjNua1WuJ/b8xqoeR3sqFkllx8+AYAra9PniDg1ydr07cT7FQMy6k7ycQMKgJr5F4BrhvI9ZA3xCDU8fJggs9gBXJ35acX8lil74CPmO5w1xhwoIMVFMQcqKCfynH3soLLuEfkB4O5TBArDPZlH05ZkYMxBigyJDEyseylHFjjK4CzPyS4IE3gTgIxuAyulHzbG/as0PYsifM24X8/TA19Vxn2efjagNwFoHE2/GDAKpm86HE2AfMrmLQbqADnI2bzFQPv8y7NlM7naORU+uid+62X4xJg0V6PC1+KfvvSghWMgnh0cVIArCO694Ib+qWR4HQ257F9oRxu+L2FpzK3h7D5vPwqA5k1OPOwA4iaAOYWnZM4XPhPYT3eWDXriX4sHROjpskF7cC2eBHfUdVjeDw6/4Uk9oHqEz18DH9se8IvgCdQDBS/oLUxcPcB24mnAv+jfXvCMOdwI9jNXDxiJp9w9DCd4Afgdz96fF5GGk3xSCFBHw+gF4PAz9SQCwE7K5UGculJHGuTdKPun+IYHrafAUPfPKJdP4OhL7ErDuf9jfnXn6Gu6+Kj654EPKQIG7iu5PMLacGPO7Qf0EOMvx3LhhRh/5l+GOsahnPkw4Mw7sXzLedzxV+DvscsMZ8X51W0Olp/+5P7qIPlLPMEWP+3z5G94rXinuen/RWzAbe6g7hVvRX/DO8FdjMPB9+O3yD5fwf1fc72+/jcfN/cHRPZPJva/7q/27z9zlPyVfL9Abrgv/oW/Nvyx5vL9rbl5f78R/I3iTnP7fRH83QjVDpfCb4Kr71uxz1FzkN9nxfX32XKVHyj+BfweV/mJkM5Pdnkpsc6PfK64BynDM8lTiU1+l+LPP2iLUJj8sj5z3uaXgMPZFDY/rQDHs/rLTRxMfkwx4mX4hPLjaza/TgIfI/l1xvl5y/wT5+dSCd8rmXf8W2/qgx5S5rRYvAMlri+Ic2MKME9FCdQT/wJ8Ga1vSnzE+Z3l06REJi7qI1VfOXw0xusrCPVZ+6aP12dFqO/qN6d4fZeF+rB804X6sInXl/lrT1vBFtAu1KcuCfWpi9e33VLfJjZAS33ckvlZpH4uedu2nOcWhleiPr9peLFT32fyfGD7fMGBlf/jfCLZOd8oIrw6q4/o2jogzlc2z2fAW8w2nwvd3eqp0YXxCcdiS1HzRC8fw2ezJjvHVtn2tPbhqnOzTgNp1/kdv6pV7ig4RQOruuDBCax1+94dOHTo0KFDk34DoJynpPus3GIAAAAASUVORK5CYII=';
|
||||
|
||||
function ArtistPoster(props) {
|
||||
function AuthorPoster(props) {
|
||||
return (
|
||||
<ArtistImage
|
||||
<AuthorImage
|
||||
{...props}
|
||||
coverType="poster"
|
||||
placeholder={posterPlaceholder}
|
||||
@@ -14,12 +14,12 @@ function ArtistPoster(props) {
|
||||
);
|
||||
}
|
||||
|
||||
ArtistPoster.propTypes = {
|
||||
AuthorPoster.propTypes = {
|
||||
size: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
ArtistPoster.defaultProps = {
|
||||
AuthorPoster.defaultProps = {
|
||||
size: 250
|
||||
};
|
||||
|
||||
export default ArtistPoster;
|
||||
export default AuthorPoster;
|
||||
@@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import DeleteArtistModalContentConnector from './DeleteArtistModalContentConnector';
|
||||
import DeleteAuthorModalContentConnector from './DeleteAuthorModalContentConnector';
|
||||
|
||||
function DeleteArtistModal(props) {
|
||||
function DeleteAuthorModal(props) {
|
||||
const {
|
||||
isOpen,
|
||||
onModalClose,
|
||||
@@ -17,7 +17,7 @@ function DeleteArtistModal(props) {
|
||||
size={sizes.MEDIUM}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<DeleteArtistModalContentConnector
|
||||
<DeleteAuthorModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
@@ -25,9 +25,9 @@ function DeleteArtistModal(props) {
|
||||
);
|
||||
}
|
||||
|
||||
DeleteArtistModal.propTypes = {
|
||||
DeleteAuthorModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default DeleteArtistModal;
|
||||
export default DeleteAuthorModal;
|
||||
@@ -11,9 +11,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 styles from './DeleteArtistModalContent.css';
|
||||
import styles from './DeleteAuthorModalContent.css';
|
||||
|
||||
class DeleteArtistModalContent extends Component {
|
||||
class DeleteAuthorModalContent extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -38,7 +38,7 @@ class DeleteArtistModalContent extends Component {
|
||||
this.setState({ addImportListExclusion: value });
|
||||
}
|
||||
|
||||
onDeleteArtistConfirmed = () => {
|
||||
onDeleteAuthorConfirmed = () => {
|
||||
const deleteFiles = this.state.deleteFiles;
|
||||
const addImportListExclusion = this.state.addImportListExclusion;
|
||||
|
||||
@@ -52,26 +52,26 @@ class DeleteArtistModalContent extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
artistName,
|
||||
authorName,
|
||||
path,
|
||||
statistics,
|
||||
onModalClose
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
trackFileCount,
|
||||
bookFileCount,
|
||||
sizeOnDisk
|
||||
} = statistics;
|
||||
|
||||
const deleteFiles = this.state.deleteFiles;
|
||||
const addImportListExclusion = this.state.addImportListExclusion;
|
||||
|
||||
let deleteFilesLabel = `Delete ${trackFileCount} Track Files`;
|
||||
let deleteFilesHelpText = 'Delete the track files and artist folder';
|
||||
let deleteFilesLabel = `Delete ${bookFileCount} Book Files`;
|
||||
let deleteFilesHelpText = 'Delete the book files and author folder';
|
||||
|
||||
if (trackFileCount === 0) {
|
||||
deleteFilesLabel = 'Delete Artist Folder';
|
||||
deleteFilesHelpText = 'Delete the artist folder and its contents';
|
||||
if (bookFileCount === 0) {
|
||||
deleteFilesLabel = 'Delete Author Folder';
|
||||
deleteFilesHelpText = 'Delete the author folder and its contents';
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -79,7 +79,7 @@ class DeleteArtistModalContent extends Component {
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<ModalHeader>
|
||||
Delete - {artistName}
|
||||
Delete - {authorName}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
@@ -112,7 +112,7 @@ class DeleteArtistModalContent extends Component {
|
||||
type={inputTypes.CHECK}
|
||||
name="addImportListExclusion"
|
||||
value={addImportListExclusion}
|
||||
helpText="Prevent artist from being added to Readarr by Import lists"
|
||||
helpText="Prevent author from being added to Readarr by Import lists"
|
||||
kind={kinds.DANGER}
|
||||
onChange={this.onAddImportListExclusionChange}
|
||||
/>
|
||||
@@ -121,11 +121,11 @@ class DeleteArtistModalContent extends Component {
|
||||
{
|
||||
deleteFiles &&
|
||||
<div className={styles.deleteFilesMessage}>
|
||||
<div>The artist folder <strong>{path}</strong> and all of its content will be deleted.</div>
|
||||
<div>The author folder <strong>{path}</strong> and all of its content will be deleted.</div>
|
||||
|
||||
{
|
||||
!!trackFileCount &&
|
||||
<div>{trackFileCount} track files totaling {formatBytes(sizeOnDisk)}</div>
|
||||
!!bookFileCount &&
|
||||
<div>{bookFileCount} book files totaling {formatBytes(sizeOnDisk)}</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -139,7 +139,7 @@ class DeleteArtistModalContent extends Component {
|
||||
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
onPress={this.onDeleteArtistConfirmed}
|
||||
onPress={this.onDeleteAuthorConfirmed}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
@@ -149,18 +149,18 @@ class DeleteArtistModalContent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
DeleteArtistModalContent.propTypes = {
|
||||
artistName: PropTypes.string.isRequired,
|
||||
DeleteAuthorModalContent.propTypes = {
|
||||
authorName: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
statistics: PropTypes.object.isRequired,
|
||||
onDeletePress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
DeleteArtistModalContent.defaultProps = {
|
||||
DeleteAuthorModalContent.defaultProps = {
|
||||
statistics: {
|
||||
trackFileCount: 0
|
||||
bookFileCount: 0
|
||||
}
|
||||
};
|
||||
|
||||
export default DeleteArtistModalContent;
|
||||
export default DeleteAuthorModalContent;
|
||||
@@ -2,30 +2,30 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import { deleteArtist } from 'Store/Actions/artistActions';
|
||||
import DeleteArtistModalContent from './DeleteArtistModalContent';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import { deleteAuthor } from 'Store/Actions/authorActions';
|
||||
import DeleteAuthorModalContent from './DeleteAuthorModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
(artist) => {
|
||||
return artist;
|
||||
createAuthorSelector(),
|
||||
(author) => {
|
||||
return author;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
deleteArtist
|
||||
deleteAuthor
|
||||
};
|
||||
|
||||
class DeleteArtistModalContentConnector extends Component {
|
||||
class DeleteAuthorModalContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onDeletePress = (deleteFiles, addImportListExclusion) => {
|
||||
this.props.deleteArtist({
|
||||
this.props.deleteAuthor({
|
||||
id: this.props.authorId,
|
||||
deleteFiles,
|
||||
addImportListExclusion
|
||||
@@ -39,7 +39,7 @@ class DeleteArtistModalContentConnector extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<DeleteArtistModalContent
|
||||
<DeleteAuthorModalContent
|
||||
{...this.props}
|
||||
onDeletePress={this.onDeletePress}
|
||||
/>
|
||||
@@ -47,10 +47,10 @@ class DeleteArtistModalContentConnector extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
DeleteArtistModalContentConnector.propTypes = {
|
||||
DeleteAuthorModalContentConnector.propTypes = {
|
||||
authorId: PropTypes.number.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
deleteArtist: PropTypes.func.isRequired
|
||||
deleteAuthor: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(DeleteArtistModalContentConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(DeleteAuthorModalContentConnector);
|
||||
@@ -1,8 +1,8 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import styles from './ArtistAlternateTitles.css';
|
||||
import styles from './AuthorAlternateTitles.css';
|
||||
|
||||
function ArtistAlternateTitles({ alternateTitles }) {
|
||||
function AuthorAlternateTitles({ alternateTitles }) {
|
||||
return (
|
||||
<ul>
|
||||
{
|
||||
@@ -21,8 +21,8 @@ function ArtistAlternateTitles({ alternateTitles }) {
|
||||
);
|
||||
}
|
||||
|
||||
ArtistAlternateTitles.propTypes = {
|
||||
AuthorAlternateTitles.propTypes = {
|
||||
alternateTitles: PropTypes.arrayOf(PropTypes.string).isRequired
|
||||
};
|
||||
|
||||
export default ArtistAlternateTitles;
|
||||
export default AuthorAlternateTitles;
|
||||
@@ -99,11 +99,11 @@
|
||||
float: right;
|
||||
}
|
||||
|
||||
.artistNavigationButtons {
|
||||
.authorNavigationButtons {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.artistNavigationButton {
|
||||
.authorNavigationButton {
|
||||
composes: button from '~Components/Link/IconButton.css';
|
||||
|
||||
margin-left: 5px;
|
||||
@@ -1,7 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
|
||||
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
|
||||
import TextTruncate from 'react-text-truncate';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import selectAll from 'Utilities/Table/selectAll';
|
||||
@@ -22,20 +22,20 @@ import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import Tooltip from 'Components/Tooltip/Tooltip';
|
||||
import TrackFileEditorTable from 'TrackFile/Editor/TrackFileEditorTable';
|
||||
import BookFileEditorTable from 'BookFile/Editor/BookFileEditorTable';
|
||||
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
|
||||
import RetagPreviewModalConnector from 'Retag/RetagPreviewModalConnector';
|
||||
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
|
||||
import ArtistPoster from 'Artist/ArtistPoster';
|
||||
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
||||
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
||||
import ArtistHistoryTable from 'Artist/History/ArtistHistoryTable';
|
||||
import ArtistAlternateTitles from './ArtistAlternateTitles';
|
||||
import ArtistDetailsSeasonConnector from './ArtistDetailsSeasonConnector';
|
||||
import AuthorPoster from 'Author/AuthorPoster';
|
||||
import EditAuthorModalConnector from 'Author/Edit/EditAuthorModalConnector';
|
||||
import DeleteAuthorModal from 'Author/Delete/DeleteAuthorModal';
|
||||
import AuthorHistoryTable from 'Author/History/AuthorHistoryTable';
|
||||
import AuthorAlternateTitles from './AuthorAlternateTitles';
|
||||
import AuthorDetailsSeasonConnector from './AuthorDetailsSeasonConnector';
|
||||
import AuthorDetailsSeriesConnector from './AuthorDetailsSeriesConnector';
|
||||
import ArtistTagsConnector from './ArtistTagsConnector';
|
||||
import ArtistDetailsLinks from './ArtistDetailsLinks';
|
||||
import styles from './ArtistDetails.css';
|
||||
import AuthorTagsConnector from './AuthorTagsConnector';
|
||||
import AuthorDetailsLinks from './AuthorDetailsLinks';
|
||||
import styles from './AuthorDetails.css';
|
||||
import InteractiveSearchTable from 'InteractiveSearch/InteractiveSearchTable';
|
||||
import InteractiveSearchFilterMenuConnector from 'InteractiveSearch/InteractiveSearchFilterMenuConnector';
|
||||
import InteractiveImportModal from '../../InteractiveImport/InteractiveImportModal';
|
||||
@@ -60,7 +60,7 @@ function getExpandedState(newState) {
|
||||
};
|
||||
}
|
||||
|
||||
class ArtistDetails extends Component {
|
||||
class AuthorDetails extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -71,8 +71,8 @@ class ArtistDetails extends Component {
|
||||
this.state = {
|
||||
isOrganizeModalOpen: false,
|
||||
isRetagModalOpen: false,
|
||||
isEditArtistModalOpen: false,
|
||||
isDeleteArtistModalOpen: false,
|
||||
isEditAuthorModalOpen: false,
|
||||
isDeleteAuthorModalOpen: false,
|
||||
isInteractiveImportModalOpen: false,
|
||||
allExpanded: false,
|
||||
allCollapsed: false,
|
||||
@@ -108,23 +108,23 @@ class ArtistDetails extends Component {
|
||||
this.setState({ isInteractiveImportModalOpen: false });
|
||||
}
|
||||
|
||||
onEditArtistPress = () => {
|
||||
this.setState({ isEditArtistModalOpen: true });
|
||||
onEditAuthorPress = () => {
|
||||
this.setState({ isEditAuthorModalOpen: true });
|
||||
}
|
||||
|
||||
onEditArtistModalClose = () => {
|
||||
this.setState({ isEditArtistModalOpen: false });
|
||||
onEditAuthorModalClose = () => {
|
||||
this.setState({ isEditAuthorModalOpen: false });
|
||||
}
|
||||
|
||||
onDeleteArtistPress = () => {
|
||||
onDeleteAuthorPress = () => {
|
||||
this.setState({
|
||||
isEditArtistModalOpen: false,
|
||||
isDeleteArtistModalOpen: true
|
||||
isEditAuthorModalOpen: false,
|
||||
isDeleteAuthorModalOpen: true
|
||||
});
|
||||
}
|
||||
|
||||
onDeleteArtistModalClose = () => {
|
||||
this.setState({ isDeleteArtistModalOpen: false });
|
||||
onDeleteAuthorModalClose = () => {
|
||||
this.setState({ isDeleteAuthorModalOpen: false });
|
||||
}
|
||||
|
||||
onExpandAllPress = () => {
|
||||
@@ -156,7 +156,7 @@ class ArtistDetails extends Component {
|
||||
render() {
|
||||
const {
|
||||
id,
|
||||
artistName,
|
||||
authorName,
|
||||
ratings,
|
||||
path,
|
||||
statistics,
|
||||
@@ -166,7 +166,7 @@ class ArtistDetails extends Component {
|
||||
overview,
|
||||
links,
|
||||
images,
|
||||
artistType,
|
||||
authorType,
|
||||
alternateTitles,
|
||||
tags,
|
||||
isSaving,
|
||||
@@ -174,30 +174,30 @@ class ArtistDetails extends Component {
|
||||
isSearching,
|
||||
isFetching,
|
||||
isPopulated,
|
||||
albumsError,
|
||||
trackFilesError,
|
||||
hasAlbums,
|
||||
hasMonitoredAlbums,
|
||||
booksError,
|
||||
bookFilesError,
|
||||
hasBooks,
|
||||
hasMonitoredBooks,
|
||||
hasSeries,
|
||||
series,
|
||||
hasTrackFiles,
|
||||
previousArtist,
|
||||
nextArtist,
|
||||
hasBookFiles,
|
||||
previousAuthor,
|
||||
nextAuthor,
|
||||
onMonitorTogglePress,
|
||||
onRefreshPress,
|
||||
onSearchPress
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
trackFileCount,
|
||||
bookFileCount,
|
||||
sizeOnDisk
|
||||
} = statistics;
|
||||
|
||||
const {
|
||||
isOrganizeModalOpen,
|
||||
isRetagModalOpen,
|
||||
isEditArtistModalOpen,
|
||||
isDeleteArtistModalOpen,
|
||||
isEditAuthorModalOpen,
|
||||
isDeleteAuthorModalOpen,
|
||||
isInteractiveImportModalOpen,
|
||||
allExpanded,
|
||||
allCollapsed,
|
||||
@@ -206,14 +206,14 @@ class ArtistDetails extends Component {
|
||||
} = this.state;
|
||||
|
||||
const continuing = status === 'continuing';
|
||||
const endedString = artistType === 'Person' ? 'Deceased' : 'Ended';
|
||||
const endedString = authorType === 'Person' ? 'Deceased' : 'Ended';
|
||||
|
||||
let trackFilesCountMessage = 'No track files';
|
||||
let bookFilesCountMessage = 'No book files';
|
||||
|
||||
if (trackFileCount === 1) {
|
||||
trackFilesCountMessage = '1 track file';
|
||||
} else if (trackFileCount > 1) {
|
||||
trackFilesCountMessage = `${trackFileCount} track files`;
|
||||
if (bookFileCount === 1) {
|
||||
bookFilesCountMessage = '1 book file';
|
||||
} else if (bookFileCount > 1) {
|
||||
bookFilesCountMessage = `${bookFileCount} book files`;
|
||||
}
|
||||
|
||||
let expandIcon = icons.EXPAND_INDETERMINATE;
|
||||
@@ -225,7 +225,7 @@ class ArtistDetails extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<PageContent title={artistName}>
|
||||
<PageContent title={authorName}>
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
@@ -240,9 +240,9 @@ class ArtistDetails extends Component {
|
||||
<PageToolbarButton
|
||||
label="Search Monitored"
|
||||
iconName={icons.SEARCH}
|
||||
isDisabled={!monitored || !hasMonitoredAlbums || !hasAlbums}
|
||||
isDisabled={!monitored || !hasMonitoredBooks || !hasBooks}
|
||||
isSpinning={isSearching}
|
||||
title={hasMonitoredAlbums ? undefined : 'No monitored albums for this artist'}
|
||||
title={hasMonitoredBooks ? undefined : 'No monitored books for this author'}
|
||||
onPress={onSearchPress}
|
||||
/>
|
||||
|
||||
@@ -251,14 +251,14 @@ class ArtistDetails extends Component {
|
||||
<PageToolbarButton
|
||||
label="Preview Rename"
|
||||
iconName={icons.ORGANIZE}
|
||||
isDisabled={!hasTrackFiles}
|
||||
isDisabled={!hasBookFiles}
|
||||
onPress={this.onOrganizePress}
|
||||
/>
|
||||
|
||||
{/* <PageToolbarButton */}
|
||||
{/* label="Preview Retag" */}
|
||||
{/* iconName={icons.RETAG} */}
|
||||
{/* isDisabled={!hasTrackFiles} */}
|
||||
{/* isDisabled={!hasBookFiles} */}
|
||||
{/* onPress={this.onRetagPress} */}
|
||||
{/* /> */}
|
||||
|
||||
@@ -273,13 +273,13 @@ class ArtistDetails extends Component {
|
||||
<PageToolbarButton
|
||||
label="Edit"
|
||||
iconName={icons.EDIT}
|
||||
onPress={this.onEditArtistPress}
|
||||
onPress={this.onEditAuthorPress}
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label="Delete"
|
||||
iconName={icons.DELETE}
|
||||
onPress={this.onDeleteArtistPress}
|
||||
onPress={this.onDeleteAuthorPress}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
|
||||
@@ -304,7 +304,7 @@ class ArtistDetails extends Component {
|
||||
</div>
|
||||
|
||||
<div className={styles.headerContent}>
|
||||
<ArtistPoster
|
||||
<AuthorPoster
|
||||
className={styles.poster}
|
||||
images={images}
|
||||
size={250}
|
||||
@@ -325,7 +325,7 @@ class ArtistDetails extends Component {
|
||||
</div>
|
||||
|
||||
<div className={styles.title}>
|
||||
{artistName}
|
||||
{authorName}
|
||||
</div>
|
||||
|
||||
{
|
||||
@@ -339,36 +339,36 @@ class ArtistDetails extends Component {
|
||||
/>
|
||||
}
|
||||
title="Alternate Titles"
|
||||
body={<ArtistAlternateTitles alternateTitles={alternateTitles} />}
|
||||
body={<AuthorAlternateTitles alternateTitles={alternateTitles} />}
|
||||
position={tooltipPositions.BOTTOM}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className={styles.artistNavigationButtons}>
|
||||
<div className={styles.authorNavigationButtons}>
|
||||
<IconButton
|
||||
className={styles.artistNavigationButton}
|
||||
className={styles.authorNavigationButton}
|
||||
name={icons.ARROW_LEFT}
|
||||
size={30}
|
||||
title={`Go to ${previousArtist.artistName}`}
|
||||
to={`/author/${previousArtist.titleSlug}`}
|
||||
title={`Go to ${previousAuthor.authorName}`}
|
||||
to={`/author/${previousAuthor.titleSlug}`}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
className={styles.artistNavigationButton}
|
||||
className={styles.authorNavigationButton}
|
||||
name={icons.ARROW_UP}
|
||||
size={30}
|
||||
title={'Go to artist listing'}
|
||||
title={'Go to author listing'}
|
||||
to={'/'}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
className={styles.artistNavigationButton}
|
||||
className={styles.authorNavigationButton}
|
||||
name={icons.ARROW_RIGHT}
|
||||
size={30}
|
||||
title={`Go to ${nextArtist.artistName}`}
|
||||
to={`/author/${nextArtist.titleSlug}`}
|
||||
title={`Go to ${nextAuthor.authorName}`}
|
||||
to={`/author/${nextAuthor.titleSlug}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -399,7 +399,7 @@ class ArtistDetails extends Component {
|
||||
|
||||
<Label
|
||||
className={styles.detailsLabel}
|
||||
title={trackFilesCountMessage}
|
||||
title={bookFilesCountMessage}
|
||||
size={sizes.LARGE}
|
||||
>
|
||||
<Icon
|
||||
@@ -449,11 +449,11 @@ class ArtistDetails extends Component {
|
||||
|
||||
<Label
|
||||
className={styles.detailsLabel}
|
||||
title={continuing ? 'More albums are expected' : 'No additional albums are expected'}
|
||||
title={continuing ? 'More books are expected' : 'No additional books are expected'}
|
||||
size={sizes.LARGE}
|
||||
>
|
||||
<Icon
|
||||
name={continuing ? icons.ARTIST_CONTINUING : icons.ARTIST_ENDED}
|
||||
name={continuing ? icons.AUTHOR_CONTINUING : icons.AUTHOR_ENDED}
|
||||
size={17}
|
||||
/>
|
||||
|
||||
@@ -479,7 +479,7 @@ class ArtistDetails extends Component {
|
||||
</Label>
|
||||
}
|
||||
tooltip={
|
||||
<ArtistDetailsLinks
|
||||
<AuthorDetailsLinks
|
||||
links={links}
|
||||
/>
|
||||
}
|
||||
@@ -505,7 +505,7 @@ class ArtistDetails extends Component {
|
||||
</span>
|
||||
</Label>
|
||||
}
|
||||
tooltip={<ArtistTagsConnector authorId={id} />}
|
||||
tooltip={<AuthorTagsConnector authorId={id} />}
|
||||
kind={kinds.INVERSE}
|
||||
position={tooltipPositions.BOTTOM}
|
||||
/>
|
||||
@@ -524,18 +524,18 @@ class ArtistDetails extends Component {
|
||||
|
||||
<div className={styles.contentContainer}>
|
||||
{
|
||||
!isPopulated && !albumsError && !trackFilesError &&
|
||||
!isPopulated && !booksError && !bookFilesError &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && albumsError &&
|
||||
<div>Loading albums failed</div>
|
||||
!isFetching && booksError &&
|
||||
<div>Loading books failed</div>
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && trackFilesError &&
|
||||
<div>Loading track files failed</div>
|
||||
!isFetching && bookFilesError &&
|
||||
<div>Loading book files failed</div>
|
||||
}
|
||||
|
||||
{
|
||||
@@ -583,14 +583,14 @@ class ArtistDetails extends Component {
|
||||
selectedTabIndex === 3 &&
|
||||
<div className={styles.filterIcon}>
|
||||
<InteractiveSearchFilterMenuConnector
|
||||
type="artist"
|
||||
type="author"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</TabList>
|
||||
|
||||
<TabPanel>
|
||||
<ArtistDetailsSeasonConnector
|
||||
<AuthorDetailsSeasonConnector
|
||||
authorId={id}
|
||||
isExpanded={true}
|
||||
onExpandPress={this.onExpandPress}
|
||||
@@ -619,20 +619,20 @@ class ArtistDetails extends Component {
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<ArtistHistoryTable
|
||||
<AuthorHistoryTable
|
||||
authorId={id}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<InteractiveSearchTable
|
||||
type="artist"
|
||||
type="author"
|
||||
authorId={id}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<TrackFileEditorTable
|
||||
<BookFileEditorTable
|
||||
authorId={id}
|
||||
/>
|
||||
</TabPanel>
|
||||
@@ -645,7 +645,7 @@ class ArtistDetails extends Component {
|
||||
Missing or too many books? Modify or create a new
|
||||
<Link to='/settings/profiles'> Metadata Profile </Link>
|
||||
or manually
|
||||
<Link to={`/add/search?term=${encodeURIComponent(artistName)}`}> Search </Link>
|
||||
<Link to={`/add/search?term=${encodeURIComponent(authorName)}`}> Search </Link>
|
||||
for new items!
|
||||
</div>
|
||||
|
||||
@@ -661,23 +661,23 @@ class ArtistDetails extends Component {
|
||||
onModalClose={this.onRetagModalClose}
|
||||
/>
|
||||
|
||||
<EditArtistModalConnector
|
||||
isOpen={isEditArtistModalOpen}
|
||||
<EditAuthorModalConnector
|
||||
isOpen={isEditAuthorModalOpen}
|
||||
authorId={id}
|
||||
onModalClose={this.onEditArtistModalClose}
|
||||
onDeleteArtistPress={this.onDeleteArtistPress}
|
||||
onModalClose={this.onEditAuthorModalClose}
|
||||
onDeleteAuthorPress={this.onDeleteAuthorPress}
|
||||
/>
|
||||
|
||||
<DeleteArtistModal
|
||||
isOpen={isDeleteArtistModalOpen}
|
||||
<DeleteAuthorModal
|
||||
isOpen={isDeleteAuthorModalOpen}
|
||||
authorId={id}
|
||||
onModalClose={this.onDeleteArtistModalClose}
|
||||
onModalClose={this.onDeleteAuthorModalClose}
|
||||
/>
|
||||
|
||||
<InteractiveImportModal
|
||||
isOpen={isInteractiveImportModalOpen}
|
||||
folder={path}
|
||||
allowArtistChange={false}
|
||||
allowAuthorChange={false}
|
||||
showFilterExistingFiles={true}
|
||||
showImportMode={false}
|
||||
onModalClose={this.onInteractiveImportModalClose}
|
||||
@@ -688,15 +688,15 @@ class ArtistDetails extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistDetails.propTypes = {
|
||||
AuthorDetails.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
artistName: PropTypes.string.isRequired,
|
||||
authorName: PropTypes.string.isRequired,
|
||||
ratings: PropTypes.object.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
statistics: PropTypes.object.isRequired,
|
||||
qualityProfileId: PropTypes.number.isRequired,
|
||||
monitored: PropTypes.bool.isRequired,
|
||||
artistType: PropTypes.string,
|
||||
authorType: PropTypes.string,
|
||||
status: PropTypes.string.isRequired,
|
||||
overview: PropTypes.string.isRequired,
|
||||
links: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
@@ -708,24 +708,24 @@ ArtistDetails.propTypes = {
|
||||
isSearching: PropTypes.bool.isRequired,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
albumsError: PropTypes.object,
|
||||
trackFilesError: PropTypes.object,
|
||||
hasAlbums: PropTypes.bool.isRequired,
|
||||
hasMonitoredAlbums: PropTypes.bool.isRequired,
|
||||
booksError: PropTypes.object,
|
||||
bookFilesError: PropTypes.object,
|
||||
hasBooks: PropTypes.bool.isRequired,
|
||||
hasMonitoredBooks: PropTypes.bool.isRequired,
|
||||
hasSeries: PropTypes.bool.isRequired,
|
||||
series: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
hasTrackFiles: PropTypes.bool.isRequired,
|
||||
previousArtist: PropTypes.object.isRequired,
|
||||
nextArtist: PropTypes.object.isRequired,
|
||||
hasBookFiles: PropTypes.bool.isRequired,
|
||||
previousAuthor: PropTypes.object.isRequired,
|
||||
nextAuthor: PropTypes.object.isRequired,
|
||||
onMonitorTogglePress: PropTypes.func.isRequired,
|
||||
onRefreshPress: PropTypes.func.isRequired,
|
||||
onSearchPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
ArtistDetails.defaultProps = {
|
||||
AuthorDetails.defaultProps = {
|
||||
statistics: {},
|
||||
tags: [],
|
||||
isSaving: false
|
||||
};
|
||||
|
||||
export default ArtistDetails;
|
||||
export default AuthorDetails;
|
||||
@@ -7,37 +7,37 @@ import { createSelector } from 'reselect';
|
||||
import { findCommand, isCommandExecuting } from 'Utilities/Command';
|
||||
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
|
||||
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
||||
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||
import createAllAuthorSelector from 'Store/Selectors/createAllAuthorsSelector';
|
||||
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
||||
import { fetchAlbums, clearAlbums } from 'Store/Actions/albumActions';
|
||||
import { fetchSeries, clearSeries } from 'Store/Actions/seriesActions';
|
||||
import { fetchTrackFiles, clearTrackFiles } from 'Store/Actions/trackFileActions';
|
||||
import { toggleArtistMonitored } from 'Store/Actions/artistActions';
|
||||
import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions';
|
||||
import { clearReleases, cancelFetchReleases } from 'Store/Actions/releaseActions';
|
||||
import { clearBooks, fetchBooks } from 'Store/Actions/bookActions';
|
||||
import { clearSeries, fetchSeries } from 'Store/Actions/seriesActions';
|
||||
import { clearBookFiles, fetchBookFiles } from 'Store/Actions/bookFileActions';
|
||||
import { toggleAuthorMonitored } from 'Store/Actions/authorActions';
|
||||
import { clearQueueDetails, fetchQueueDetails } from 'Store/Actions/queueActions';
|
||||
import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import ArtistDetails from './ArtistDetails';
|
||||
import AuthorDetails from './AuthorDetails';
|
||||
|
||||
const selectAlbums = createSelector(
|
||||
(state) => state.albums,
|
||||
(albums) => {
|
||||
const selectBooks = createSelector(
|
||||
(state) => state.books,
|
||||
(books) => {
|
||||
const {
|
||||
items,
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error
|
||||
} = albums;
|
||||
} = books;
|
||||
|
||||
const hasAlbums = !!items.length;
|
||||
const hasMonitoredAlbums = items.some((e) => e.monitored);
|
||||
const hasBooks = !!items.length;
|
||||
const hasMonitoredBooks = items.some((e) => e.monitored);
|
||||
|
||||
return {
|
||||
isAlbumsFetching: isFetching,
|
||||
isAlbumsPopulated: isPopulated,
|
||||
albumsError: error,
|
||||
hasAlbums,
|
||||
hasMonitoredAlbums
|
||||
isBooksFetching: isFetching,
|
||||
isBooksPopulated: isPopulated,
|
||||
booksError: error,
|
||||
hasBooks,
|
||||
hasMonitoredBooks
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -65,23 +65,23 @@ const selectSeries = createSelector(
|
||||
}
|
||||
);
|
||||
|
||||
const selectTrackFiles = createSelector(
|
||||
(state) => state.trackFiles,
|
||||
(trackFiles) => {
|
||||
const selectBookFiles = createSelector(
|
||||
(state) => state.bookFiles,
|
||||
(bookFiles) => {
|
||||
const {
|
||||
items,
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error
|
||||
} = trackFiles;
|
||||
} = bookFiles;
|
||||
|
||||
const hasTrackFiles = !!items.length;
|
||||
const hasBookFiles = !!items.length;
|
||||
|
||||
return {
|
||||
isTrackFilesFetching: isFetching,
|
||||
isTrackFilesPopulated: isPopulated,
|
||||
trackFilesError: error,
|
||||
hasTrackFiles
|
||||
isBookFilesFetching: isFetching,
|
||||
isBookFilesPopulated: isPopulated,
|
||||
bookFilesError: error,
|
||||
hasBookFiles
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -89,27 +89,27 @@ const selectTrackFiles = createSelector(
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state, { titleSlug }) => titleSlug,
|
||||
selectAlbums,
|
||||
selectBooks,
|
||||
selectSeries,
|
||||
selectTrackFiles,
|
||||
createAllArtistSelector(),
|
||||
selectBookFiles,
|
||||
createAllAuthorSelector(),
|
||||
createCommandsSelector(),
|
||||
(titleSlug, albums, series, trackFiles, allArtists, commands) => {
|
||||
const sortedArtist = _.orderBy(allArtists, 'sortName');
|
||||
const artistIndex = _.findIndex(sortedArtist, { titleSlug });
|
||||
const artist = sortedArtist[artistIndex];
|
||||
(titleSlug, books, series, bookFiles, allAuthors, commands) => {
|
||||
const sortedAuthor = _.orderBy(allAuthors, 'sortName');
|
||||
const authorIndex = _.findIndex(sortedAuthor, { titleSlug });
|
||||
const author = sortedAuthor[authorIndex];
|
||||
|
||||
if (!artist) {
|
||||
if (!author) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const {
|
||||
isAlbumsFetching,
|
||||
isAlbumsPopulated,
|
||||
albumsError,
|
||||
hasAlbums,
|
||||
hasMonitoredAlbums
|
||||
} = albums;
|
||||
isBooksFetching,
|
||||
isBooksPopulated,
|
||||
booksError,
|
||||
hasBooks,
|
||||
hasMonitoredBooks
|
||||
} = books;
|
||||
|
||||
const {
|
||||
isSeriesFetching,
|
||||
@@ -120,34 +120,34 @@ function createMapStateToProps() {
|
||||
} = series;
|
||||
|
||||
const {
|
||||
isTrackFilesFetching,
|
||||
isTrackFilesPopulated,
|
||||
trackFilesError,
|
||||
hasTrackFiles
|
||||
} = trackFiles;
|
||||
isBookFilesFetching,
|
||||
isBookFilesPopulated,
|
||||
bookFilesError,
|
||||
hasBookFiles
|
||||
} = bookFiles;
|
||||
|
||||
const previousArtist = sortedArtist[artistIndex - 1] || _.last(sortedArtist);
|
||||
const nextArtist = sortedArtist[artistIndex + 1] || _.first(sortedArtist);
|
||||
const isArtistRefreshing = isCommandExecuting(findCommand(commands, { name: commandNames.REFRESH_ARTIST, authorId: artist.id }));
|
||||
const artistRefreshingCommand = findCommand(commands, { name: commandNames.REFRESH_ARTIST });
|
||||
const allArtistRefreshing = (
|
||||
isCommandExecuting(artistRefreshingCommand) &&
|
||||
!artistRefreshingCommand.body.authorId
|
||||
const previousAuthor = sortedAuthor[authorIndex - 1] || _.last(sortedAuthor);
|
||||
const nextAuthor = sortedAuthor[authorIndex + 1] || _.first(sortedAuthor);
|
||||
const isAuthorRefreshing = isCommandExecuting(findCommand(commands, { name: commandNames.REFRESH_AUTHOR, authorId: author.id }));
|
||||
const authorRefreshingCommand = findCommand(commands, { name: commandNames.REFRESH_AUTHOR });
|
||||
const allAuthorRefreshing = (
|
||||
isCommandExecuting(authorRefreshingCommand) &&
|
||||
!authorRefreshingCommand.body.authorId
|
||||
);
|
||||
const isRefreshing = isArtistRefreshing || allArtistRefreshing;
|
||||
const isSearching = isCommandExecuting(findCommand(commands, { name: commandNames.ARTIST_SEARCH, authorId: artist.id }));
|
||||
const isRenamingFiles = isCommandExecuting(findCommand(commands, { name: commandNames.RENAME_FILES, authorId: artist.id }));
|
||||
const isRefreshing = isAuthorRefreshing || allAuthorRefreshing;
|
||||
const isSearching = isCommandExecuting(findCommand(commands, { name: commandNames.AUTHOR_SEARCH, authorId: author.id }));
|
||||
const isRenamingFiles = isCommandExecuting(findCommand(commands, { name: commandNames.RENAME_FILES, authorId: author.id }));
|
||||
|
||||
const isRenamingArtistCommand = findCommand(commands, { name: commandNames.RENAME_ARTIST });
|
||||
const isRenamingArtist = (
|
||||
isCommandExecuting(isRenamingArtistCommand) &&
|
||||
isRenamingArtistCommand.body.authorIds.indexOf(artist.id) > -1
|
||||
const isRenamingAuthorCommand = findCommand(commands, { name: commandNames.RENAME_AUTHOR });
|
||||
const isRenamingAuthor = (
|
||||
isCommandExecuting(isRenamingAuthorCommand) &&
|
||||
isRenamingAuthorCommand.body.authorIds.indexOf(author.id) > -1
|
||||
);
|
||||
|
||||
const isFetching = isAlbumsFetching || isSeriesFetching || isTrackFilesFetching;
|
||||
const isPopulated = isAlbumsPopulated && isSeriesPopulated && isTrackFilesPopulated;
|
||||
const isFetching = isBooksFetching || isSeriesFetching || isBookFilesFetching;
|
||||
const isPopulated = isBooksPopulated && isSeriesPopulated && isBookFilesPopulated;
|
||||
|
||||
const alternateTitles = _.reduce(artist.alternateTitles, (acc, alternateTitle) => {
|
||||
const alternateTitles = _.reduce(author.alternateTitles, (acc, alternateTitle) => {
|
||||
if ((alternateTitle.seasonNumber === -1 || alternateTitle.seasonNumber === undefined) &&
|
||||
(alternateTitle.sceneSeasonNumber === -1 || alternateTitle.sceneSeasonNumber === undefined)) {
|
||||
acc.push(alternateTitle.title);
|
||||
@@ -157,39 +157,39 @@ function createMapStateToProps() {
|
||||
}, []);
|
||||
|
||||
return {
|
||||
...artist,
|
||||
...author,
|
||||
alternateTitles,
|
||||
isArtistRefreshing,
|
||||
allArtistRefreshing,
|
||||
isAuthorRefreshing,
|
||||
allAuthorRefreshing,
|
||||
isRefreshing,
|
||||
isSearching,
|
||||
isRenamingFiles,
|
||||
isRenamingArtist,
|
||||
isRenamingAuthor,
|
||||
isFetching,
|
||||
isPopulated,
|
||||
albumsError,
|
||||
booksError,
|
||||
seriesError,
|
||||
trackFilesError,
|
||||
hasAlbums,
|
||||
hasMonitoredAlbums,
|
||||
bookFilesError,
|
||||
hasBooks,
|
||||
hasMonitoredBooks,
|
||||
hasSeries,
|
||||
series: seriesItems,
|
||||
hasTrackFiles,
|
||||
previousArtist,
|
||||
nextArtist
|
||||
hasBookFiles,
|
||||
previousAuthor,
|
||||
nextAuthor
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchAlbums,
|
||||
clearAlbums,
|
||||
fetchBooks,
|
||||
clearBooks,
|
||||
fetchSeries,
|
||||
clearSeries,
|
||||
fetchTrackFiles,
|
||||
clearTrackFiles,
|
||||
toggleArtistMonitored,
|
||||
fetchBookFiles,
|
||||
clearBookFiles,
|
||||
toggleAuthorMonitored,
|
||||
fetchQueueDetails,
|
||||
clearQueueDetails,
|
||||
clearReleases,
|
||||
@@ -197,7 +197,7 @@ const mapDispatchToProps = {
|
||||
executeCommand
|
||||
};
|
||||
|
||||
class ArtistDetailsConnector extends Component {
|
||||
class AuthorDetailsConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -210,22 +210,22 @@ class ArtistDetailsConnector extends Component {
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
id,
|
||||
isArtistRefreshing,
|
||||
allArtistRefreshing,
|
||||
isAuthorRefreshing,
|
||||
allAuthorRefreshing,
|
||||
isRenamingFiles,
|
||||
isRenamingArtist
|
||||
isRenamingAuthor
|
||||
} = this.props;
|
||||
|
||||
if (
|
||||
(prevProps.isArtistRefreshing && !isArtistRefreshing) ||
|
||||
(prevProps.allArtistRefreshing && !allArtistRefreshing) ||
|
||||
(prevProps.isAuthorRefreshing && !isAuthorRefreshing) ||
|
||||
(prevProps.allAuthorRefreshing && !allAuthorRefreshing) ||
|
||||
(prevProps.isRenamingFiles && !isRenamingFiles) ||
|
||||
(prevProps.isRenamingArtist && !isRenamingArtist)
|
||||
(prevProps.isRenamingAuthor && !isRenamingAuthor)
|
||||
) {
|
||||
this.populate();
|
||||
}
|
||||
|
||||
// If the id has changed we need to clear the albums
|
||||
// If the id has changed we need to clear the books
|
||||
// files and fetch from the server.
|
||||
|
||||
if (prevProps.id !== id) {
|
||||
@@ -245,17 +245,17 @@ class ArtistDetailsConnector extends Component {
|
||||
populate = () => {
|
||||
const authorId = this.props.id;
|
||||
|
||||
this.props.fetchAlbums({ authorId });
|
||||
this.props.fetchBooks({ authorId });
|
||||
this.props.fetchSeries({ authorId });
|
||||
this.props.fetchTrackFiles({ authorId });
|
||||
this.props.fetchBookFiles({ authorId });
|
||||
this.props.fetchQueueDetails({ authorId });
|
||||
}
|
||||
|
||||
unpopulate = () => {
|
||||
this.props.cancelFetchReleases();
|
||||
this.props.clearAlbums();
|
||||
this.props.clearBooks();
|
||||
this.props.clearSeries();
|
||||
this.props.clearTrackFiles();
|
||||
this.props.clearBookFiles();
|
||||
this.props.clearQueueDetails();
|
||||
this.props.clearReleases();
|
||||
}
|
||||
@@ -264,7 +264,7 @@ class ArtistDetailsConnector extends Component {
|
||||
// Listeners
|
||||
|
||||
onMonitorTogglePress = (monitored) => {
|
||||
this.props.toggleArtistMonitored({
|
||||
this.props.toggleAuthorMonitored({
|
||||
authorId: this.props.id,
|
||||
monitored
|
||||
});
|
||||
@@ -272,14 +272,14 @@ class ArtistDetailsConnector extends Component {
|
||||
|
||||
onRefreshPress = () => {
|
||||
this.props.executeCommand({
|
||||
name: commandNames.REFRESH_ARTIST,
|
||||
name: commandNames.REFRESH_AUTHOR,
|
||||
authorId: this.props.id
|
||||
});
|
||||
}
|
||||
|
||||
onSearchPress = () => {
|
||||
this.props.executeCommand({
|
||||
name: commandNames.ARTIST_SEARCH,
|
||||
name: commandNames.AUTHOR_SEARCH,
|
||||
authorId: this.props.id
|
||||
});
|
||||
}
|
||||
@@ -289,7 +289,7 @@ class ArtistDetailsConnector extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ArtistDetails
|
||||
<AuthorDetails
|
||||
{...this.props}
|
||||
onMonitorTogglePress={this.onMonitorTogglePress}
|
||||
onRefreshPress={this.onRefreshPress}
|
||||
@@ -299,21 +299,21 @@ class ArtistDetailsConnector extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistDetailsConnector.propTypes = {
|
||||
AuthorDetailsConnector.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
titleSlug: PropTypes.string.isRequired,
|
||||
isArtistRefreshing: PropTypes.bool.isRequired,
|
||||
allArtistRefreshing: PropTypes.bool.isRequired,
|
||||
isAuthorRefreshing: PropTypes.bool.isRequired,
|
||||
allAuthorRefreshing: PropTypes.bool.isRequired,
|
||||
isRefreshing: PropTypes.bool.isRequired,
|
||||
isRenamingFiles: PropTypes.bool.isRequired,
|
||||
isRenamingArtist: PropTypes.bool.isRequired,
|
||||
fetchAlbums: PropTypes.func.isRequired,
|
||||
clearAlbums: PropTypes.func.isRequired,
|
||||
isRenamingAuthor: PropTypes.bool.isRequired,
|
||||
fetchBooks: PropTypes.func.isRequired,
|
||||
clearBooks: PropTypes.func.isRequired,
|
||||
fetchSeries: PropTypes.func.isRequired,
|
||||
clearSeries: PropTypes.func.isRequired,
|
||||
fetchTrackFiles: PropTypes.func.isRequired,
|
||||
clearTrackFiles: PropTypes.func.isRequired,
|
||||
toggleArtistMonitored: PropTypes.func.isRequired,
|
||||
fetchBookFiles: PropTypes.func.isRequired,
|
||||
clearBookFiles: PropTypes.func.isRequired,
|
||||
toggleAuthorMonitored: PropTypes.func.isRequired,
|
||||
fetchQueueDetails: PropTypes.func.isRequired,
|
||||
clearQueueDetails: PropTypes.func.isRequired,
|
||||
clearReleases: PropTypes.func.isRequired,
|
||||
@@ -321,4 +321,4 @@ ArtistDetailsConnector.propTypes = {
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(ArtistDetailsConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AuthorDetailsConnector);
|
||||
@@ -3,9 +3,9 @@ import React from 'react';
|
||||
import { kinds, sizes } from 'Helpers/Props';
|
||||
import Label from 'Components/Label';
|
||||
import Link from 'Components/Link/Link';
|
||||
import styles from './ArtistDetailsLinks.css';
|
||||
import styles from './AuthorDetailsLinks.css';
|
||||
|
||||
function ArtistDetailsLinks(props) {
|
||||
function AuthorDetailsLinks(props) {
|
||||
const {
|
||||
links
|
||||
} = props;
|
||||
@@ -41,8 +41,8 @@ function ArtistDetailsLinks(props) {
|
||||
);
|
||||
}
|
||||
|
||||
ArtistDetailsLinks.propTypes = {
|
||||
AuthorDetailsLinks.propTypes = {
|
||||
links: PropTypes.arrayOf(PropTypes.object).isRequired
|
||||
};
|
||||
|
||||
export default ArtistDetailsLinks;
|
||||
export default AuthorDetailsLinks;
|
||||
@@ -9,25 +9,25 @@ import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import NotFound from 'Components/NotFound';
|
||||
import ArtistDetailsConnector from './ArtistDetailsConnector';
|
||||
import styles from './ArtistDetails.css';
|
||||
import AuthorDetailsConnector from './AuthorDetailsConnector';
|
||||
import styles from './AuthorDetails.css';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state, { match }) => match,
|
||||
(state) => state.artist,
|
||||
(match, artist) => {
|
||||
(state) => state.authors,
|
||||
(match, authors) => {
|
||||
const titleSlug = match.params.titleSlug;
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
items
|
||||
} = artist;
|
||||
} = authors;
|
||||
|
||||
const artistIndex = _.findIndex(items, { titleSlug });
|
||||
const authorIndex = _.findIndex(items, { titleSlug });
|
||||
|
||||
if (artistIndex > -1) {
|
||||
if (authorIndex > -1) {
|
||||
return {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
@@ -48,7 +48,7 @@ const mapDispatchToProps = {
|
||||
push
|
||||
};
|
||||
|
||||
class ArtistDetailsPageConnector extends Component {
|
||||
class AuthorDetailsPageConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -98,14 +98,14 @@ class ArtistDetailsPageConnector extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<ArtistDetailsConnector
|
||||
<AuthorDetailsConnector
|
||||
titleSlug={titleSlug}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ArtistDetailsPageConnector.propTypes = {
|
||||
AuthorDetailsPageConnector.propTypes = {
|
||||
titleSlug: PropTypes.string,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
@@ -114,4 +114,4 @@ ArtistDetailsPageConnector.propTypes = {
|
||||
push: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(ArtistDetailsPageConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AuthorDetailsPageConnector);
|
||||
@@ -1,4 +1,4 @@
|
||||
.albumType {
|
||||
.bookType {
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid $borderColor;
|
||||
border-radius: 4px;
|
||||
@@ -18,12 +18,12 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.albumTypeLabel {
|
||||
.bookTypeLabel {
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.albumCount {
|
||||
.bookCount {
|
||||
color: #8895aa;
|
||||
font-style: italic;
|
||||
font-size: 18px;
|
||||
@@ -75,7 +75,7 @@
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.albums {
|
||||
.books {
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid $borderColor;
|
||||
}
|
||||
@@ -106,13 +106,13 @@
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
.noAlbums {
|
||||
.noBooks {
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointSmall) {
|
||||
.albumType {
|
||||
.bookType {
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
border-radius: 0;
|
||||
@@ -5,10 +5,10 @@ import getToggledRange from 'Utilities/Table/getToggledRange';
|
||||
import { sortDirections } from 'Helpers/Props';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import AlbumRowConnector from './AlbumRowConnector';
|
||||
import styles from './ArtistDetailsSeason.css';
|
||||
import BookRowConnector from './BookRowConnector';
|
||||
import styles from './AuthorDetailsSeason.css';
|
||||
|
||||
class ArtistDetailsSeason extends Component {
|
||||
class AuthorDetailsSeason extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -17,15 +17,15 @@ class ArtistDetailsSeason extends Component {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
lastToggledAlbum: null
|
||||
lastToggledBook: null
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onMonitorAlbumPress = (bookId, monitored, { shiftKey }) => {
|
||||
const lastToggled = this.state.lastToggledAlbum;
|
||||
onMonitorBookPress = (bookId, monitored, { shiftKey }) => {
|
||||
const lastToggled = this.state.lastToggledBook;
|
||||
const bookIds = [bookId];
|
||||
|
||||
if (shiftKey && lastToggled) {
|
||||
@@ -37,9 +37,9 @@ class ArtistDetailsSeason extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ lastToggledAlbum: bookId });
|
||||
this.setState({ lastToggledBook: bookId });
|
||||
|
||||
this.props.onMonitorAlbumPress(_.uniq(bookIds), monitored);
|
||||
this.props.onMonitorBookPress(_.uniq(bookIds), monitored);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -57,9 +57,9 @@ class ArtistDetailsSeason extends Component {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.albumType}
|
||||
className={styles.bookType}
|
||||
>
|
||||
<div className={styles.albums}>
|
||||
<div className={styles.books}>
|
||||
<Table
|
||||
columns={columns}
|
||||
sortKey={sortKey}
|
||||
@@ -71,11 +71,11 @@ class ArtistDetailsSeason extends Component {
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<AlbumRowConnector
|
||||
<BookRowConnector
|
||||
key={item.id}
|
||||
columns={columns}
|
||||
{...item}
|
||||
onMonitorAlbumPress={this.onMonitorAlbumPress}
|
||||
onMonitorBookPress={this.onMonitorBookPress}
|
||||
/>
|
||||
);
|
||||
})
|
||||
@@ -88,7 +88,7 @@ class ArtistDetailsSeason extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistDetailsSeason.propTypes = {
|
||||
AuthorDetailsSeason.propTypes = {
|
||||
sortKey: PropTypes.string,
|
||||
sortDirection: PropTypes.oneOf(sortDirections.all),
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
@@ -96,8 +96,8 @@ ArtistDetailsSeason.propTypes = {
|
||||
onTableOptionChange: PropTypes.func.isRequired,
|
||||
onExpandPress: PropTypes.func.isRequired,
|
||||
onSortPress: PropTypes.func.isRequired,
|
||||
onMonitorAlbumPress: PropTypes.func.isRequired,
|
||||
onMonitorBookPress: PropTypes.func.isRequired,
|
||||
uiSettings: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default ArtistDetailsSeason;
|
||||
export default AuthorDetailsSeason;
|
||||
@@ -5,40 +5,40 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { toggleAlbumsMonitored, setAlbumsTableOption, setAlbumsSort } from 'Store/Actions/albumActions';
|
||||
import { setBooksSort, setBooksTableOption, toggleBooksMonitored } from 'Store/Actions/bookActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import ArtistDetailsSeason from './ArtistDetailsSeason';
|
||||
import AuthorDetailsSeason from './AuthorDetailsSeason';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state, { label }) => label,
|
||||
createClientSideCollectionSelector('albums'),
|
||||
createArtistSelector(),
|
||||
createClientSideCollectionSelector('books'),
|
||||
createAuthorSelector(),
|
||||
createCommandsSelector(),
|
||||
createDimensionsSelector(),
|
||||
createUISettingsSelector(),
|
||||
(label, albums, artist, commands, dimensions, uiSettings) => {
|
||||
(label, books, author, commands, dimensions, uiSettings) => {
|
||||
|
||||
const albumsInGroup = albums.items;
|
||||
const booksInGroup = books.items;
|
||||
|
||||
let sortDir = 'asc';
|
||||
|
||||
if (albums.sortDirection === 'descending') {
|
||||
if (books.sortDirection === 'descending') {
|
||||
sortDir = 'desc';
|
||||
}
|
||||
|
||||
const sortedAlbums = _.orderBy(albumsInGroup, albums.sortKey, sortDir);
|
||||
const sortedBooks = _.orderBy(booksInGroup, books.sortKey, sortDir);
|
||||
|
||||
return {
|
||||
items: sortedAlbums,
|
||||
columns: albums.columns,
|
||||
sortKey: albums.sortKey,
|
||||
sortDirection: albums.sortDirection,
|
||||
artistMonitored: artist.monitored,
|
||||
items: sortedBooks,
|
||||
columns: books.columns,
|
||||
sortKey: books.sortKey,
|
||||
sortDirection: books.sortDirection,
|
||||
authorMonitored: author.monitored,
|
||||
isSmallScreen: dimensions.isSmallScreen,
|
||||
uiSettings
|
||||
};
|
||||
@@ -47,27 +47,27 @@ function createMapStateToProps() {
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
toggleAlbumsMonitored,
|
||||
setAlbumsTableOption,
|
||||
dispatchSetAlbumSort: setAlbumsSort,
|
||||
toggleBooksMonitored,
|
||||
setBooksTableOption,
|
||||
dispatchSetBookSort: setBooksSort,
|
||||
executeCommand
|
||||
};
|
||||
|
||||
class ArtistDetailsSeasonConnector extends Component {
|
||||
class AuthorDetailsSeasonConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onTableOptionChange = (payload) => {
|
||||
this.props.setAlbumsTableOption(payload);
|
||||
this.props.setBooksTableOption(payload);
|
||||
}
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.dispatchSetAlbumSort({ sortKey });
|
||||
this.props.dispatchSetBookSort({ sortKey });
|
||||
}
|
||||
|
||||
onMonitorAlbumPress = (bookIds, monitored) => {
|
||||
this.props.toggleAlbumsMonitored({
|
||||
onMonitorBookPress = (bookIds, monitored) => {
|
||||
this.props.toggleBooksMonitored({
|
||||
bookIds,
|
||||
monitored
|
||||
});
|
||||
@@ -78,22 +78,22 @@ class ArtistDetailsSeasonConnector extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ArtistDetailsSeason
|
||||
<AuthorDetailsSeason
|
||||
{...this.props}
|
||||
onSortPress={this.onSortPress}
|
||||
onTableOptionChange={this.onTableOptionChange}
|
||||
onMonitorAlbumPress={this.onMonitorAlbumPress}
|
||||
onMonitorBookPress={this.onMonitorBookPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ArtistDetailsSeasonConnector.propTypes = {
|
||||
AuthorDetailsSeasonConnector.propTypes = {
|
||||
authorId: PropTypes.number.isRequired,
|
||||
toggleAlbumsMonitored: PropTypes.func.isRequired,
|
||||
setAlbumsTableOption: PropTypes.func.isRequired,
|
||||
dispatchSetAlbumSort: PropTypes.func.isRequired,
|
||||
toggleBooksMonitored: PropTypes.func.isRequired,
|
||||
setBooksTableOption: PropTypes.func.isRequired,
|
||||
dispatchSetBookSort: PropTypes.func.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(ArtistDetailsSeasonConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AuthorDetailsSeasonConnector);
|
||||
@@ -1,4 +1,4 @@
|
||||
.albumType {
|
||||
.bookType {
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid $borderColor;
|
||||
border-radius: 4px;
|
||||
@@ -18,12 +18,12 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.albumTypeLabel {
|
||||
.bookTypeLabel {
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.albumCount {
|
||||
.bookCount {
|
||||
color: #8895aa;
|
||||
font-style: italic;
|
||||
font-size: 18px;
|
||||
@@ -75,7 +75,7 @@
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.albums {
|
||||
.books {
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid $borderColor;
|
||||
}
|
||||
@@ -108,13 +108,13 @@
|
||||
/* margin-left: -15px; */
|
||||
}
|
||||
|
||||
.noAlbums {
|
||||
.noBooks {
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointSmall) {
|
||||
.albumType {
|
||||
.bookType {
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
border-radius: 0;
|
||||
@@ -8,7 +8,7 @@ import IconButton from 'Components/Link/IconButton';
|
||||
import Link from 'Components/Link/Link';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import AlbumRowConnector from './AlbumRowConnector';
|
||||
import BookRowConnector from './BookRowConnector';
|
||||
import styles from './AuthorDetailsSeries.css';
|
||||
|
||||
class AuthorDetailsSeries extends Component {
|
||||
@@ -21,8 +21,8 @@ class AuthorDetailsSeries extends Component {
|
||||
|
||||
this.state = {
|
||||
isOrganizeModalOpen: false,
|
||||
isManageTracksOpen: false,
|
||||
lastToggledAlbum: null
|
||||
isManageBooksOpen: false,
|
||||
lastToggledBook: null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,22 +65,22 @@ class AuthorDetailsSeries extends Component {
|
||||
this.props.onExpandPress(id, !isExpanded);
|
||||
}
|
||||
|
||||
onMonitorAlbumPress = (albumId, monitored, { shiftKey }) => {
|
||||
const lastToggled = this.state.lastToggledAlbum;
|
||||
const albumIds = [albumId];
|
||||
onMonitorBookPress = (bookId, monitored, { shiftKey }) => {
|
||||
const lastToggled = this.state.lastToggledBook;
|
||||
const bookIds = [bookId];
|
||||
|
||||
if (shiftKey && lastToggled) {
|
||||
const { lower, upper } = getToggledRange(this.props.items, albumId, lastToggled);
|
||||
const { lower, upper } = getToggledRange(this.props.items, bookId, lastToggled);
|
||||
const items = this.props.items;
|
||||
|
||||
for (let i = lower; i < upper; i++) {
|
||||
albumIds.push(items[i].id);
|
||||
bookIds.push(items[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ lastToggledAlbum: albumId });
|
||||
this.setState({ lastToggledBook: bookId });
|
||||
|
||||
this.props.onMonitorAlbumPress(_.uniq(albumIds), monitored);
|
||||
this.props.onMonitorBookPress(_.uniq(bookIds), monitored);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -102,7 +102,7 @@ class AuthorDetailsSeries extends Component {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.albumType}
|
||||
className={styles.bookType}
|
||||
>
|
||||
<Link
|
||||
className={styles.expandButton}
|
||||
@@ -112,11 +112,11 @@ class AuthorDetailsSeries extends Component {
|
||||
<div className={styles.left}>
|
||||
{
|
||||
<div>
|
||||
<span className={styles.albumTypeLabel}>
|
||||
<span className={styles.bookTypeLabel}>
|
||||
{label}
|
||||
</span>
|
||||
|
||||
<span className={styles.albumCount}>
|
||||
<span className={styles.bookCount}>
|
||||
({items.length} Books)
|
||||
</span>
|
||||
</div>
|
||||
@@ -142,7 +142,7 @@ class AuthorDetailsSeries extends Component {
|
||||
<div>
|
||||
{
|
||||
isExpanded &&
|
||||
<div className={styles.albums}>
|
||||
<div className={styles.books}>
|
||||
<Table
|
||||
columns={columns}
|
||||
sortKey={sortKey}
|
||||
@@ -154,12 +154,12 @@ class AuthorDetailsSeries extends Component {
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<AlbumRowConnector
|
||||
<BookRowConnector
|
||||
key={item.id}
|
||||
columns={columns}
|
||||
{...item}
|
||||
position={positionMap[item.id]}
|
||||
onMonitorAlbumPress={this.onMonitorAlbumPress}
|
||||
onMonitorBookPress={this.onMonitorBookPress}
|
||||
/>
|
||||
);
|
||||
})
|
||||
@@ -198,7 +198,7 @@ AuthorDetailsSeries.propTypes = {
|
||||
onTableOptionChange: PropTypes.func.isRequired,
|
||||
onExpandPress: PropTypes.func.isRequired,
|
||||
onSortPress: PropTypes.func.isRequired,
|
||||
onMonitorAlbumPress: PropTypes.func.isRequired,
|
||||
onMonitorBookPress: PropTypes.func.isRequired,
|
||||
uiSettings: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
@@ -5,11 +5,11 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
||||
// import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
import { toggleAlbumsMonitored, setAlbumsTableOption } from 'Store/Actions/albumActions';
|
||||
import { setBooksTableOption, toggleBooksMonitored } from 'Store/Actions/bookActions';
|
||||
import { setSeriesSort } from 'Store/Actions/seriesActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import AuthorDetailsSeries from './AuthorDetailsSeries';
|
||||
@@ -17,8 +17,8 @@ import AuthorDetailsSeries from './AuthorDetailsSeries';
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state, { seriesId }) => seriesId,
|
||||
(state) => state.albums,
|
||||
createArtistSelector(),
|
||||
(state) => state.books,
|
||||
createAuthorSelector(),
|
||||
(state) => state.series,
|
||||
createCommandsSelector(),
|
||||
createDimensionsSelector(),
|
||||
@@ -60,7 +60,7 @@ function createMapStateToProps() {
|
||||
columns: series.columns,
|
||||
sortKey: series.sortKey,
|
||||
sortDirection: series.sortDirection,
|
||||
artistMonitored: author.monitored,
|
||||
authorMonitored: author.monitored,
|
||||
isSmallScreen: dimensions.isSmallScreen,
|
||||
uiSettings
|
||||
};
|
||||
@@ -69,27 +69,27 @@ function createMapStateToProps() {
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
toggleAlbumsMonitored,
|
||||
setAlbumsTableOption,
|
||||
toggleBooksMonitored,
|
||||
setBooksTableOption,
|
||||
dispatchSetSeriesSort: setSeriesSort,
|
||||
executeCommand
|
||||
};
|
||||
|
||||
class ArtistDetailsSeasonConnector extends Component {
|
||||
class AuthorDetailsSeasonConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onTableOptionChange = (payload) => {
|
||||
this.props.setAlbumsTableOption(payload);
|
||||
this.props.setBooksTableOption(payload);
|
||||
}
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.dispatchSetSeriesSort({ sortKey });
|
||||
}
|
||||
|
||||
onMonitorAlbumPress = (bookIds, monitored) => {
|
||||
this.props.toggleAlbumsMonitored({
|
||||
onMonitorBookPress = (bookIds, monitored) => {
|
||||
this.props.toggleBooksMonitored({
|
||||
bookIds,
|
||||
monitored
|
||||
});
|
||||
@@ -104,18 +104,18 @@ class ArtistDetailsSeasonConnector extends Component {
|
||||
{...this.props}
|
||||
onSortPress={this.onSortPress}
|
||||
onTableOptionChange={this.onTableOptionChange}
|
||||
onMonitorAlbumPress={this.onMonitorAlbumPress}
|
||||
onMonitorBookPress={this.onMonitorBookPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ArtistDetailsSeasonConnector.propTypes = {
|
||||
AuthorDetailsSeasonConnector.propTypes = {
|
||||
authorId: PropTypes.number.isRequired,
|
||||
toggleAlbumsMonitored: PropTypes.func.isRequired,
|
||||
setAlbumsTableOption: PropTypes.func.isRequired,
|
||||
toggleBooksMonitored: PropTypes.func.isRequired,
|
||||
setBooksTableOption: PropTypes.func.isRequired,
|
||||
dispatchSetSeriesSort: PropTypes.func.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(ArtistDetailsSeasonConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AuthorDetailsSeasonConnector);
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
import { kinds, sizes } from 'Helpers/Props';
|
||||
import Label from 'Components/Label';
|
||||
|
||||
function ArtistTags({ tags }) {
|
||||
function AuthorTags({ tags }) {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
@@ -23,8 +23,8 @@ function ArtistTags({ tags }) {
|
||||
);
|
||||
}
|
||||
|
||||
ArtistTags.propTypes = {
|
||||
AuthorTags.propTypes = {
|
||||
tags: PropTypes.arrayOf(PropTypes.string).isRequired
|
||||
};
|
||||
|
||||
export default ArtistTags;
|
||||
export default AuthorTags;
|
||||
@@ -1,16 +1,16 @@
|
||||
import _ from 'lodash';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||
import ArtistTags from './ArtistTags';
|
||||
import AuthorTags from './AuthorTags';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
createAuthorSelector(),
|
||||
createTagsSelector(),
|
||||
(artist, tagList) => {
|
||||
const tags = _.reduce(artist.tags, (acc, tag) => {
|
||||
(author, tagList) => {
|
||||
const tags = _.reduce(author.tags, (acc, tag) => {
|
||||
const matchingTag = _.find(tagList, { id: tag });
|
||||
|
||||
if (matchingTag) {
|
||||
@@ -27,4 +27,4 @@ function createMapStateToProps() {
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(createMapStateToProps)(ArtistTags);
|
||||
export default connect(createMapStateToProps)(AuthorTags);
|
||||
@@ -6,13 +6,13 @@ import { kinds, sizes } from 'Helpers/Props';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import Label from 'Components/Label';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import AlbumSearchCellConnector from 'Album/AlbumSearchCellConnector';
|
||||
import AlbumTitleLink from 'Album/AlbumTitleLink';
|
||||
import BookSearchCellConnector from 'Book/BookSearchCellConnector';
|
||||
import BookTitleLink from 'Book/BookTitleLink';
|
||||
import StarRating from 'Components/StarRating';
|
||||
import styles from './AlbumRow.css';
|
||||
import styles from './BookRow.css';
|
||||
|
||||
function getTrackCountKind(monitored, trackFileCount, trackCount) {
|
||||
if (trackFileCount === trackCount && trackCount > 0) {
|
||||
function getBookCountKind(monitored, bookFileCount, bookCount) {
|
||||
if (bookFileCount === bookCount && bookCount > 0) {
|
||||
return kinds.SUCCESS;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ function getTrackCountKind(monitored, trackFileCount, trackCount) {
|
||||
return kinds.DANGER;
|
||||
}
|
||||
|
||||
class AlbumRow extends Component {
|
||||
class BookRow extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -33,7 +33,7 @@ class AlbumRow extends Component {
|
||||
|
||||
this.state = {
|
||||
isDetailsModalOpen: false,
|
||||
isEditAlbumModalOpen: false
|
||||
isEditBookModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,16 +48,16 @@ class AlbumRow extends Component {
|
||||
this.setState({ isDetailsModalOpen: false });
|
||||
}
|
||||
|
||||
onEditAlbumPress = () => {
|
||||
this.setState({ isEditAlbumModalOpen: true });
|
||||
onEditBookPress = () => {
|
||||
this.setState({ isEditBookModalOpen: true });
|
||||
}
|
||||
|
||||
onEditAlbumModalClose = () => {
|
||||
this.setState({ isEditAlbumModalOpen: false });
|
||||
onEditBookModalClose = () => {
|
||||
this.setState({ isEditBookModalOpen: false });
|
||||
}
|
||||
|
||||
onMonitorAlbumPress = (monitored, options) => {
|
||||
this.props.onMonitorAlbumPress(this.props.id, monitored, options);
|
||||
onMonitorBookPress = (monitored, options) => {
|
||||
this.props.onMonitorBookPress(this.props.id, monitored, options);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -75,15 +75,15 @@ class AlbumRow extends Component {
|
||||
ratings,
|
||||
disambiguation,
|
||||
isSaving,
|
||||
artistMonitored,
|
||||
authorMonitored,
|
||||
titleSlug,
|
||||
columns
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
trackCount,
|
||||
trackFileCount,
|
||||
totalTrackCount
|
||||
bookCount,
|
||||
bookFileCount,
|
||||
totalBookCount
|
||||
} = statistics;
|
||||
|
||||
return (
|
||||
@@ -107,9 +107,9 @@ class AlbumRow extends Component {
|
||||
>
|
||||
<MonitorToggleButton
|
||||
monitored={monitored}
|
||||
isDisabled={!artistMonitored}
|
||||
isDisabled={!authorMonitored}
|
||||
isSaving={isSaving}
|
||||
onPress={this.onMonitorAlbumPress}
|
||||
onPress={this.onMonitorBookPress}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
@@ -121,7 +121,7 @@ class AlbumRow extends Component {
|
||||
key={name}
|
||||
className={styles.title}
|
||||
>
|
||||
<AlbumTitleLink
|
||||
<BookTitleLink
|
||||
titleSlug={titleSlug}
|
||||
title={title}
|
||||
disambiguation={disambiguation}
|
||||
@@ -170,12 +170,12 @@ class AlbumRow extends Component {
|
||||
className={styles.status}
|
||||
>
|
||||
<Label
|
||||
title={`${totalTrackCount} tracks total. ${trackFileCount} tracks with files.`}
|
||||
kind={getTrackCountKind(monitored, trackFileCount, trackCount)}
|
||||
title={`${totalBookCount} books total. ${bookFileCount} books with files.`}
|
||||
kind={getBookCountKind(monitored, bookFileCount, bookCount)}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
{
|
||||
<span>{trackFileCount} / {trackCount}</span>
|
||||
<span>{bookFileCount} / {bookCount}</span>
|
||||
}
|
||||
</Label>
|
||||
</TableRowCell>
|
||||
@@ -184,11 +184,11 @@ class AlbumRow extends Component {
|
||||
|
||||
if (name === 'actions') {
|
||||
return (
|
||||
<AlbumSearchCellConnector
|
||||
<BookSearchCellConnector
|
||||
key={name}
|
||||
bookId={id}
|
||||
authorId={authorId}
|
||||
albumTitle={title}
|
||||
bookTitle={title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -200,7 +200,7 @@ class AlbumRow extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
AlbumRow.propTypes = {
|
||||
BookRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
authorId: PropTypes.number.isRequired,
|
||||
monitored: PropTypes.bool.isRequired,
|
||||
@@ -211,17 +211,17 @@ AlbumRow.propTypes = {
|
||||
disambiguation: PropTypes.string,
|
||||
titleSlug: PropTypes.string.isRequired,
|
||||
isSaving: PropTypes.bool,
|
||||
artistMonitored: PropTypes.bool.isRequired,
|
||||
authorMonitored: PropTypes.bool.isRequired,
|
||||
statistics: PropTypes.object.isRequired,
|
||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onMonitorAlbumPress: PropTypes.func.isRequired
|
||||
onMonitorBookPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
AlbumRow.defaultProps = {
|
||||
BookRow.defaultProps = {
|
||||
statistics: {
|
||||
trackCount: 0,
|
||||
trackFileCount: 0
|
||||
bookCount: 0,
|
||||
bookFileCount: 0
|
||||
}
|
||||
};
|
||||
|
||||
export default AlbumRow;
|
||||
export default BookRow;
|
||||
20
frontend/src/Author/Details/BookRowConnector.js
Normal file
20
frontend/src/Author/Details/BookRowConnector.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/* eslint max-params: 0 */
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import createBookFileSelector from 'Store/Selectors/createBookFileSelector';
|
||||
import BookRow from './BookRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createAuthorSelector(),
|
||||
createBookFileSelector(),
|
||||
(author = {}, bookFile) => {
|
||||
return {
|
||||
authorMonitored: author.monitored,
|
||||
bookFilePath: bookFile ? bookFile.path : null
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
export default connect(createMapStateToProps)(BookRow);
|
||||
@@ -1,15 +1,15 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import EditArtistModalContentConnector from './EditArtistModalContentConnector';
|
||||
import EditAuthorModalContentConnector from './EditAuthorModalContentConnector';
|
||||
|
||||
function EditArtistModal({ isOpen, onModalClose, ...otherProps }) {
|
||||
function EditAuthorModal({ isOpen, onModalClose, ...otherProps }) {
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<EditArtistModalContentConnector
|
||||
<EditAuthorModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
@@ -17,9 +17,9 @@ function EditArtistModal({ isOpen, onModalClose, ...otherProps }) {
|
||||
);
|
||||
}
|
||||
|
||||
EditArtistModal.propTypes = {
|
||||
EditAuthorModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default EditArtistModal;
|
||||
export default EditAuthorModal;
|
||||
@@ -2,19 +2,19 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||
import EditArtistModal from './EditArtistModal';
|
||||
import EditAuthorModal from './EditAuthorModal';
|
||||
|
||||
const mapDispatchToProps = {
|
||||
clearPendingChanges
|
||||
};
|
||||
|
||||
class EditArtistModalConnector extends Component {
|
||||
class EditAuthorModalConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onModalClose = () => {
|
||||
this.props.clearPendingChanges({ section: 'artist' });
|
||||
this.props.clearPendingChanges({ section: 'author' });
|
||||
this.props.onModalClose();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class EditArtistModalConnector extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EditArtistModal
|
||||
<EditAuthorModal
|
||||
{...this.props}
|
||||
onModalClose={this.onModalClose}
|
||||
/>
|
||||
@@ -31,9 +31,9 @@ class EditArtistModalConnector extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
EditArtistModalConnector.propTypes = {
|
||||
EditAuthorModalConnector.propTypes = {
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
clearPendingChanges: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(undefined, mapDispatchToProps)(EditArtistModalConnector);
|
||||
export default connect(undefined, mapDispatchToProps)(EditAuthorModalConnector);
|
||||
@@ -13,11 +13,11 @@ import FormLabel from 'Components/Form/FormLabel';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import Icon from 'Components/Icon';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import MoveArtistModal from 'Artist/MoveArtist/MoveArtistModal';
|
||||
import ArtistMetadataProfilePopoverContent from 'AddArtist/ArtistMetadataProfilePopoverContent';
|
||||
import styles from './EditArtistModalContent.css';
|
||||
import MoveAuthorModal from 'Author/MoveAuthor/MoveAuthorModal';
|
||||
import AuthorMetadataProfilePopoverContent from 'AddAuthor/AuthorMetadataProfilePopoverContent';
|
||||
import styles from './EditAuthorModalContent.css';
|
||||
|
||||
class EditArtistModalContent extends Component {
|
||||
class EditAuthorModalContent extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -48,7 +48,7 @@ class EditArtistModalContent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
onMoveArtistPress = () => {
|
||||
onMoveAuthorPress = () => {
|
||||
this.setState({ isConfirmMoveModalOpen: false });
|
||||
|
||||
this.props.onSavePress(true);
|
||||
@@ -59,14 +59,14 @@ class EditArtistModalContent extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
artistName,
|
||||
authorName,
|
||||
item,
|
||||
isSaving,
|
||||
showMetadataProfile,
|
||||
originalPath,
|
||||
onInputChange,
|
||||
onModalClose,
|
||||
onDeleteArtistPress,
|
||||
onDeleteAuthorPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
@@ -81,7 +81,7 @@ class EditArtistModalContent extends Component {
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Edit - {artistName}
|
||||
Edit - {authorName}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
@@ -92,7 +92,7 @@ class EditArtistModalContent extends Component {
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="monitored"
|
||||
helpText="Download monitored albums from this artist"
|
||||
helpText="Download monitored books from this author"
|
||||
{...monitored}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
@@ -123,7 +123,7 @@ class EditArtistModalContent extends Component {
|
||||
/>
|
||||
}
|
||||
title="Metadata Profile"
|
||||
body={<ArtistMetadataProfilePopoverContent />}
|
||||
body={<AuthorMetadataProfilePopoverContent />}
|
||||
position={tooltipPositions.RIGHT}
|
||||
/>
|
||||
|
||||
@@ -132,7 +132,7 @@ class EditArtistModalContent extends Component {
|
||||
<FormInputGroup
|
||||
type={inputTypes.METADATA_PROFILE_SELECT}
|
||||
name="metadataProfileId"
|
||||
helpText="Changes will take place on next artist refresh"
|
||||
helpText="Changes will take place on next author refresh"
|
||||
includeNone={true}
|
||||
{...metadataProfileId}
|
||||
onChange={onInputChange}
|
||||
@@ -167,7 +167,7 @@ class EditArtistModalContent extends Component {
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
kind={kinds.DANGER}
|
||||
onPress={onDeleteArtistPress}
|
||||
onPress={onDeleteAuthorPress}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
@@ -186,12 +186,12 @@ class EditArtistModalContent extends Component {
|
||||
</SpinnerButton>
|
||||
</ModalFooter>
|
||||
|
||||
<MoveArtistModal
|
||||
<MoveAuthorModal
|
||||
originalPath={originalPath}
|
||||
destinationPath={path.value}
|
||||
isOpen={this.state.isConfirmMoveModalOpen}
|
||||
onSavePress={this.onSavePress}
|
||||
onMoveArtistPress={this.onMoveArtistPress}
|
||||
onMoveAuthorPress={this.onMoveAuthorPress}
|
||||
/>
|
||||
|
||||
</ModalContent>
|
||||
@@ -199,9 +199,9 @@ class EditArtistModalContent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
EditArtistModalContent.propTypes = {
|
||||
EditAuthorModalContent.propTypes = {
|
||||
authorId: PropTypes.number.isRequired,
|
||||
artistName: PropTypes.string.isRequired,
|
||||
authorName: PropTypes.string.isRequired,
|
||||
item: PropTypes.object.isRequired,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
showMetadataProfile: PropTypes.bool.isRequired,
|
||||
@@ -210,7 +210,7 @@ EditArtistModalContent.propTypes = {
|
||||
onInputChange: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onDeleteArtistPress: PropTypes.func.isRequired
|
||||
onDeleteAuthorPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default EditArtistModalContent;
|
||||
export default EditAuthorModalContent;
|
||||
@@ -4,56 +4,55 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import selectSettings from 'Store/Selectors/selectSettings';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import { setArtistValue, saveArtist } from 'Store/Actions/artistActions';
|
||||
import EditArtistModalContent from './EditArtistModalContent';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import { saveAuthor, setAuthorValue } from 'Store/Actions/authorActions';
|
||||
import EditAuthorModalContent from './EditAuthorModalContent';
|
||||
|
||||
function createIsPathChangingSelector() {
|
||||
return createSelector(
|
||||
(state) => state.artist.pendingChanges,
|
||||
createArtistSelector(),
|
||||
(pendingChanges, artist) => {
|
||||
(state) => state.authors.pendingChanges,
|
||||
createAuthorSelector(),
|
||||
(pendingChanges, author) => {
|
||||
const path = pendingChanges.path;
|
||||
|
||||
if (path == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return artist.path !== path;
|
||||
return author.path !== path;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.artist,
|
||||
(state) => state.authors,
|
||||
(state) => state.settings.metadataProfiles,
|
||||
createArtistSelector(),
|
||||
createAuthorSelector(),
|
||||
createIsPathChangingSelector(),
|
||||
(artistState, metadataProfiles, artist, isPathChanging) => {
|
||||
(authorsState, metadataProfiles, author, isPathChanging) => {
|
||||
const {
|
||||
isSaving,
|
||||
saveError,
|
||||
pendingChanges
|
||||
} = artistState;
|
||||
} = authorsState;
|
||||
|
||||
const artistSettings = _.pick(artist, [
|
||||
const authorSettings = _.pick(author, [
|
||||
'monitored',
|
||||
'albumFolder',
|
||||
'qualityProfileId',
|
||||
'metadataProfileId',
|
||||
'path',
|
||||
'tags'
|
||||
]);
|
||||
|
||||
const settings = selectSettings(artistSettings, pendingChanges, saveError);
|
||||
const settings = selectSettings(authorSettings, pendingChanges, saveError);
|
||||
|
||||
return {
|
||||
artistName: artist.artistName,
|
||||
authorName: author.authorName,
|
||||
isSaving,
|
||||
saveError,
|
||||
isPathChanging,
|
||||
originalPath: artist.path,
|
||||
originalPath: author.path,
|
||||
item: settings.settings,
|
||||
showMetadataProfile: metadataProfiles.items.length > 1,
|
||||
...settings
|
||||
@@ -63,11 +62,11 @@ function createMapStateToProps() {
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
dispatchSetArtistValue: setArtistValue,
|
||||
dispatchSaveArtist: saveArtist
|
||||
dispatchSetAuthorValue: setAuthorValue,
|
||||
dispatchSaveAuthor: saveAuthor
|
||||
};
|
||||
|
||||
class EditArtistModalContentConnector extends Component {
|
||||
class EditAuthorModalContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -82,11 +81,11 @@ class EditArtistModalContentConnector extends Component {
|
||||
// Listeners
|
||||
|
||||
onInputChange = ({ name, value }) => {
|
||||
this.props.dispatchSetArtistValue({ name, value });
|
||||
this.props.dispatchSetAuthorValue({ name, value });
|
||||
}
|
||||
|
||||
onSavePress = (moveFiles) => {
|
||||
this.props.dispatchSaveArtist({
|
||||
this.props.dispatchSaveAuthor({
|
||||
id: this.props.authorId,
|
||||
moveFiles
|
||||
});
|
||||
@@ -97,23 +96,23 @@ class EditArtistModalContentConnector extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EditArtistModalContent
|
||||
<EditAuthorModalContent
|
||||
{...this.props}
|
||||
onInputChange={this.onInputChange}
|
||||
onSavePress={this.onSavePress}
|
||||
onMoveArtistPress={this.onMoveArtistPress}
|
||||
onMoveAuthorPress={this.onMoveAuthorPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EditArtistModalContentConnector.propTypes = {
|
||||
EditAuthorModalContentConnector.propTypes = {
|
||||
authorId: PropTypes.number,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
saveError: PropTypes.object,
|
||||
dispatchSetArtistValue: PropTypes.func.isRequired,
|
||||
dispatchSaveArtist: PropTypes.func.isRequired,
|
||||
dispatchSetAuthorValue: PropTypes.func.isRequired,
|
||||
dispatchSaveAuthor: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(EditArtistModalContentConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(EditAuthorModalContentConnector);
|
||||
@@ -1,9 +1,9 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import RetagArtistModalContentConnector from './RetagArtistModalContentConnector';
|
||||
import RetagAuthorModalContentConnector from './RetagAuthorModalContentConnector';
|
||||
|
||||
function RetagArtistModal(props) {
|
||||
function RetagAuthorModal(props) {
|
||||
const {
|
||||
isOpen,
|
||||
onModalClose,
|
||||
@@ -15,7 +15,7 @@ function RetagArtistModal(props) {
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<RetagArtistModalContentConnector
|
||||
<RetagAuthorModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
@@ -23,9 +23,9 @@ function RetagArtistModal(props) {
|
||||
);
|
||||
}
|
||||
|
||||
RetagArtistModal.propTypes = {
|
||||
RetagAuthorModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default RetagArtistModal;
|
||||
export default RetagAuthorModal;
|
||||
@@ -8,24 +8,24 @@ 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 styles from './RetagArtistModalContent.css';
|
||||
import styles from './RetagAuthorModalContent.css';
|
||||
|
||||
function RetagArtistModalContent(props) {
|
||||
function RetagAuthorModalContent(props) {
|
||||
const {
|
||||
artistNames,
|
||||
authorNames,
|
||||
onModalClose,
|
||||
onRetagArtistPress
|
||||
onRetagAuthorPress
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Retag Selected Artist
|
||||
Retag Selected Author
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<Alert>
|
||||
Tip: To preview the tags that will be written... select "Cancel" then click any artist name and use the
|
||||
Tip: To preview the tags that will be written... select "Cancel" then click any author name and use the
|
||||
<Icon
|
||||
className={styles.retagIcon}
|
||||
name={icons.RETAG}
|
||||
@@ -33,14 +33,14 @@ function RetagArtistModalContent(props) {
|
||||
</Alert>
|
||||
|
||||
<div className={styles.message}>
|
||||
Are you sure you want to re-tag all files in the {artistNames.length} selected artist?
|
||||
Are you sure you want to re-tag all files in the {authorNames.length} selected author?
|
||||
</div>
|
||||
<ul>
|
||||
{
|
||||
artistNames.map((artistName) => {
|
||||
authorNames.map((authorName) => {
|
||||
return (
|
||||
<li key={artistName}>
|
||||
{artistName}
|
||||
<li key={authorName}>
|
||||
{authorName}
|
||||
</li>
|
||||
);
|
||||
})
|
||||
@@ -55,7 +55,7 @@ function RetagArtistModalContent(props) {
|
||||
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
onPress={onRetagArtistPress}
|
||||
onPress={onRetagAuthorPress}
|
||||
>
|
||||
Retag
|
||||
</Button>
|
||||
@@ -64,10 +64,10 @@ function RetagArtistModalContent(props) {
|
||||
);
|
||||
}
|
||||
|
||||
RetagArtistModalContent.propTypes = {
|
||||
artistNames: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
RetagAuthorModalContent.propTypes = {
|
||||
authorNames: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onRetagArtistPress: PropTypes.func.isRequired
|
||||
onRetagAuthorPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default RetagArtistModalContent;
|
||||
export default RetagAuthorModalContent;
|
||||
@@ -3,25 +3,25 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||
import createAllAuthorSelector from 'Store/Selectors/createAllAuthorsSelector';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import RetagArtistModalContent from './RetagArtistModalContent';
|
||||
import RetagAuthorModalContent from './RetagAuthorModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state, { authorIds }) => authorIds,
|
||||
createAllArtistSelector(),
|
||||
(authorIds, allArtists) => {
|
||||
const artist = _.intersectionWith(allArtists, authorIds, (s, id) => {
|
||||
createAllAuthorSelector(),
|
||||
(authorIds, allAuthors) => {
|
||||
const author = _.intersectionWith(allAuthors, authorIds, (s, id) => {
|
||||
return s.id === id;
|
||||
});
|
||||
|
||||
const sortedArtist = _.orderBy(artist, 'sortName');
|
||||
const artistNames = _.map(sortedArtist, 'artistName');
|
||||
const sortedAuthor = _.orderBy(author, 'sortName');
|
||||
const authorNames = _.map(sortedAuthor, 'authorName');
|
||||
|
||||
return {
|
||||
artistNames
|
||||
authorNames
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -31,14 +31,14 @@ const mapDispatchToProps = {
|
||||
executeCommand
|
||||
};
|
||||
|
||||
class RetagArtistModalContentConnector extends Component {
|
||||
class RetagAuthorModalContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onRetagArtistPress = () => {
|
||||
onRetagAuthorPress = () => {
|
||||
this.props.executeCommand({
|
||||
name: commandNames.RETAG_ARTIST,
|
||||
name: commandNames.RETAG_AUTHOR,
|
||||
authorIds: this.props.authorIds
|
||||
});
|
||||
|
||||
@@ -50,18 +50,18 @@ class RetagArtistModalContentConnector extends Component {
|
||||
|
||||
render(props) {
|
||||
return (
|
||||
<RetagArtistModalContent
|
||||
<RetagAuthorModalContent
|
||||
{...this.props}
|
||||
onRetagArtistPress={this.onRetagArtistPress}
|
||||
onRetagAuthorPress={this.onRetagAuthorPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RetagArtistModalContentConnector.propTypes = {
|
||||
RetagAuthorModalContentConnector.propTypes = {
|
||||
authorIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(RetagArtistModalContentConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(RetagAuthorModalContentConnector);
|
||||
@@ -13,12 +13,12 @@ import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import NoArtist from 'Artist/NoArtist';
|
||||
import OrganizeArtistModal from './Organize/OrganizeArtistModal';
|
||||
import RetagArtistModal from './AudioTags/RetagArtistModal';
|
||||
import ArtistEditorRowConnector from './ArtistEditorRowConnector';
|
||||
import ArtistEditorFooter from './ArtistEditorFooter';
|
||||
import ArtistEditorFilterModalConnector from './ArtistEditorFilterModalConnector';
|
||||
import NoAuthor from 'Author/NoAuthor';
|
||||
import OrganizeAuthorModal from './Organize/OrganizeAuthorModal';
|
||||
import RetagAuthorModal from './AudioTags/RetagAuthorModal';
|
||||
import AuthorEditorRowConnector from './AuthorEditorRowConnector';
|
||||
import AuthorEditorFooter from './AuthorEditorFooter';
|
||||
import AuthorEditorFilterModalConnector from './AuthorEditorFilterModalConnector';
|
||||
|
||||
function getColumns(showMetadataProfile) {
|
||||
return [
|
||||
@@ -60,7 +60,7 @@ function getColumns(showMetadataProfile) {
|
||||
];
|
||||
}
|
||||
|
||||
class ArtistEditor extends Component {
|
||||
class AuthorEditor extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -73,8 +73,8 @@ class ArtistEditor extends Component {
|
||||
allUnselected: false,
|
||||
lastToggled: null,
|
||||
selectedState: {},
|
||||
isOrganizingArtistModalOpen: false,
|
||||
isRetaggingArtistModalOpen: false,
|
||||
isOrganizingAuthorModalOpen: false,
|
||||
isRetaggingAuthorModalOpen: false,
|
||||
columns: getColumns(props.showMetadataProfile)
|
||||
};
|
||||
}
|
||||
@@ -121,24 +121,24 @@ class ArtistEditor extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
onOrganizeArtistPress = () => {
|
||||
this.setState({ isOrganizingArtistModalOpen: true });
|
||||
onOrganizeAuthorPress = () => {
|
||||
this.setState({ isOrganizingAuthorModalOpen: true });
|
||||
}
|
||||
|
||||
onOrganizeArtistModalClose = (organized) => {
|
||||
this.setState({ isOrganizingArtistModalOpen: false });
|
||||
onOrganizeAuthorModalClose = (organized) => {
|
||||
this.setState({ isOrganizingAuthorModalOpen: false });
|
||||
|
||||
if (organized === true) {
|
||||
this.onSelectAllChange({ value: false });
|
||||
}
|
||||
}
|
||||
|
||||
onRetagArtistPress = () => {
|
||||
this.setState({ isRetaggingArtistModalOpen: true });
|
||||
onRetagAuthorPress = () => {
|
||||
this.setState({ isRetaggingAuthorModalOpen: true });
|
||||
}
|
||||
|
||||
onRetagArtistModalClose = (organized) => {
|
||||
this.setState({ isRetaggingArtistModalOpen: false });
|
||||
onRetagAuthorModalClose = (organized) => {
|
||||
this.setState({ isRetaggingAuthorModalOpen: false });
|
||||
|
||||
if (organized === true) {
|
||||
this.onSelectAllChange({ value: false });
|
||||
@@ -164,8 +164,8 @@ class ArtistEditor extends Component {
|
||||
saveError,
|
||||
isDeleting,
|
||||
deleteError,
|
||||
isOrganizingArtist,
|
||||
isRetaggingArtist,
|
||||
isOrganizingAuthor,
|
||||
isRetaggingAuthor,
|
||||
showMetadataProfile,
|
||||
onSortPress,
|
||||
onFilterSelect
|
||||
@@ -181,7 +181,7 @@ class ArtistEditor extends Component {
|
||||
const selectedAuthorIds = this.getSelectedIds();
|
||||
|
||||
return (
|
||||
<PageContent title="Artist Editor">
|
||||
<PageContent title="Author Editor">
|
||||
<PageToolbar>
|
||||
<PageToolbarSection />
|
||||
<PageToolbarSection alignContent={align.RIGHT}>
|
||||
@@ -190,7 +190,7 @@ class ArtistEditor extends Component {
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
filters={filters}
|
||||
customFilters={customFilters}
|
||||
filterModalConnectorComponent={ArtistEditorFilterModalConnector}
|
||||
filterModalConnectorComponent={AuthorEditorFilterModalConnector}
|
||||
onFilterSelect={onFilterSelect}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
@@ -204,7 +204,7 @@ class ArtistEditor extends Component {
|
||||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div>{getErrorMessage(error, 'Failed to load artist from API')}</div>
|
||||
<div>{getErrorMessage(error, 'Failed to load author from API')}</div>
|
||||
}
|
||||
|
||||
{
|
||||
@@ -224,7 +224,7 @@ class ArtistEditor extends Component {
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<ArtistEditorRowConnector
|
||||
<AuthorEditorRowConnector
|
||||
key={item.id}
|
||||
{...item}
|
||||
columns={columns}
|
||||
@@ -241,35 +241,35 @@ class ArtistEditor extends Component {
|
||||
|
||||
{
|
||||
!error && isPopulated && !items.length &&
|
||||
<NoArtist totalItems={totalItems} />
|
||||
<NoAuthor totalItems={totalItems} />
|
||||
}
|
||||
</PageContentBodyConnector>
|
||||
|
||||
<ArtistEditorFooter
|
||||
<AuthorEditorFooter
|
||||
authorIds={selectedAuthorIds}
|
||||
selectedCount={selectedAuthorIds.length}
|
||||
isSaving={isSaving}
|
||||
saveError={saveError}
|
||||
isDeleting={isDeleting}
|
||||
deleteError={deleteError}
|
||||
isOrganizingArtist={isOrganizingArtist}
|
||||
isRetaggingArtist={isRetaggingArtist}
|
||||
isOrganizingAuthor={isOrganizingAuthor}
|
||||
isRetaggingAuthor={isRetaggingAuthor}
|
||||
showMetadataProfile={showMetadataProfile}
|
||||
onSaveSelected={this.onSaveSelected}
|
||||
onOrganizeArtistPress={this.onOrganizeArtistPress}
|
||||
onRetagArtistPress={this.onRetagArtistPress}
|
||||
onOrganizeAuthorPress={this.onOrganizeAuthorPress}
|
||||
onRetagAuthorPress={this.onRetagAuthorPress}
|
||||
/>
|
||||
|
||||
<OrganizeArtistModal
|
||||
isOpen={this.state.isOrganizingArtistModalOpen}
|
||||
<OrganizeAuthorModal
|
||||
isOpen={this.state.isOrganizingAuthorModalOpen}
|
||||
authorIds={selectedAuthorIds}
|
||||
onModalClose={this.onOrganizeArtistModalClose}
|
||||
onModalClose={this.onOrganizeAuthorModalClose}
|
||||
/>
|
||||
|
||||
<RetagArtistModal
|
||||
isOpen={this.state.isRetaggingArtistModalOpen}
|
||||
<RetagAuthorModal
|
||||
isOpen={this.state.isRetaggingAuthorModalOpen}
|
||||
authorIds={selectedAuthorIds}
|
||||
onModalClose={this.onRetagArtistModalClose}
|
||||
onModalClose={this.onRetagAuthorModalClose}
|
||||
/>
|
||||
|
||||
</PageContent>
|
||||
@@ -277,7 +277,7 @@ class ArtistEditor extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistEditor.propTypes = {
|
||||
AuthorEditor.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
@@ -292,12 +292,12 @@ ArtistEditor.propTypes = {
|
||||
saveError: PropTypes.object,
|
||||
isDeleting: PropTypes.bool.isRequired,
|
||||
deleteError: PropTypes.object,
|
||||
isOrganizingArtist: PropTypes.bool.isRequired,
|
||||
isRetaggingArtist: PropTypes.bool.isRequired,
|
||||
isOrganizingAuthor: PropTypes.bool.isRequired,
|
||||
isRetaggingAuthor: PropTypes.bool.isRequired,
|
||||
showMetadataProfile: PropTypes.bool.isRequired,
|
||||
onSortPress: PropTypes.func.isRequired,
|
||||
onFilterSelect: PropTypes.func.isRequired,
|
||||
onSaveSelected: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistEditor;
|
||||
export default AuthorEditor;
|
||||
@@ -4,38 +4,38 @@ import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import { setArtistEditorSort, setArtistEditorFilter, saveArtistEditor } from 'Store/Actions/artistEditorActions';
|
||||
import { saveAuthorEditor, setAuthorEditorFilter, setAuthorEditorSort } from 'Store/Actions/authorEditorActions';
|
||||
import { fetchRootFolders } from 'Store/Actions/settingsActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import ArtistEditor from './ArtistEditor';
|
||||
import AuthorEditor from './AuthorEditor';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.settings.metadataProfiles,
|
||||
createClientSideCollectionSelector('artist', 'artistEditor'),
|
||||
createCommandExecutingSelector(commandNames.RENAME_ARTIST),
|
||||
createCommandExecutingSelector(commandNames.RETAG_ARTIST),
|
||||
(metadataProfiles, artist, isOrganizingArtist, isRetaggingArtist) => {
|
||||
createClientSideCollectionSelector('authors', 'authorEditor'),
|
||||
createCommandExecutingSelector(commandNames.RENAME_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.RETAG_AUTHOR),
|
||||
(metadataProfiles, author, isOrganizingAuthor, isRetaggingAuthor) => {
|
||||
return {
|
||||
isOrganizingArtist,
|
||||
isRetaggingArtist,
|
||||
isOrganizingAuthor,
|
||||
isRetaggingAuthor,
|
||||
showMetadataProfile: metadataProfiles.items.length > 1,
|
||||
...artist
|
||||
...author
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
dispatchSetArtistEditorSort: setArtistEditorSort,
|
||||
dispatchSetArtistEditorFilter: setArtistEditorFilter,
|
||||
dispatchSaveArtistEditor: saveArtistEditor,
|
||||
dispatchSetAuthorEditorSort: setAuthorEditorSort,
|
||||
dispatchSetAuthorEditorFilter: setAuthorEditorFilter,
|
||||
dispatchSaveAuthorEditor: saveAuthorEditor,
|
||||
dispatchFetchRootFolders: fetchRootFolders,
|
||||
dispatchExecuteCommand: executeCommand
|
||||
};
|
||||
|
||||
class ArtistEditorConnector extends Component {
|
||||
class AuthorEditorConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -48,20 +48,20 @@ class ArtistEditorConnector extends Component {
|
||||
// Listeners
|
||||
|
||||
onSortPress = (sortKey) => {
|
||||
this.props.dispatchSetArtistEditorSort({ sortKey });
|
||||
this.props.dispatchSetAuthorEditorSort({ sortKey });
|
||||
}
|
||||
|
||||
onFilterSelect = (selectedFilterKey) => {
|
||||
this.props.dispatchSetArtistEditorFilter({ selectedFilterKey });
|
||||
this.props.dispatchSetAuthorEditorFilter({ selectedFilterKey });
|
||||
}
|
||||
|
||||
onSaveSelected = (payload) => {
|
||||
this.props.dispatchSaveArtistEditor(payload);
|
||||
this.props.dispatchSaveAuthorEditor(payload);
|
||||
}
|
||||
|
||||
onMoveSelected = (payload) => {
|
||||
this.props.dispatchExecuteCommand({
|
||||
name: commandNames.MOVE_ARTIST,
|
||||
name: commandNames.MOVE_AUTHOR,
|
||||
...payload
|
||||
});
|
||||
}
|
||||
@@ -71,7 +71,7 @@ class ArtistEditorConnector extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ArtistEditor
|
||||
<AuthorEditor
|
||||
{...this.props}
|
||||
onSortPress={this.onSortPress}
|
||||
onFilterSelect={this.onFilterSelect}
|
||||
@@ -81,12 +81,12 @@ class ArtistEditorConnector extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistEditorConnector.propTypes = {
|
||||
dispatchSetArtistEditorSort: PropTypes.func.isRequired,
|
||||
dispatchSetArtistEditorFilter: PropTypes.func.isRequired,
|
||||
dispatchSaveArtistEditor: PropTypes.func.isRequired,
|
||||
AuthorEditorConnector.propTypes = {
|
||||
dispatchSetAuthorEditorSort: PropTypes.func.isRequired,
|
||||
dispatchSetAuthorEditorFilter: PropTypes.func.isRequired,
|
||||
dispatchSaveAuthorEditor: PropTypes.func.isRequired,
|
||||
dispatchFetchRootFolders: PropTypes.func.isRequired,
|
||||
dispatchExecuteCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(ArtistEditorConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AuthorEditorConnector);
|
||||
@@ -1,24 +1,24 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { setArtistEditorFilter } from 'Store/Actions/artistEditorActions';
|
||||
import { setAuthorEditorFilter } from 'Store/Actions/authorEditorActions';
|
||||
import FilterModal from 'Components/Filter/FilterModal';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.artist.items,
|
||||
(state) => state.artistEditor.filterBuilderProps,
|
||||
(state) => state.authors.items,
|
||||
(state) => state.authorEditor.filterBuilderProps,
|
||||
(sectionItems, filterBuilderProps) => {
|
||||
return {
|
||||
sectionItems,
|
||||
filterBuilderProps,
|
||||
customFilterType: 'artistEditor'
|
||||
customFilterType: 'authorEditor'
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
dispatchSetFilter: setArtistEditorFilter
|
||||
dispatchSetFilter: setAuthorEditorFilter
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(FilterModal);
|
||||
@@ -64,7 +64,7 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.selectedArtistLabel {
|
||||
.selectedAuthorLabel {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
@@ -7,15 +7,15 @@ import QualityProfileSelectInputConnector from 'Components/Form/QualityProfileSe
|
||||
import RootFolderSelectInputConnector from 'Components/Form/RootFolderSelectInputConnector';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import PageContentFooter from 'Components/Page/PageContentFooter';
|
||||
import MoveArtistModal from 'Artist/MoveArtist/MoveArtistModal';
|
||||
import MoveAuthorModal from 'Author/MoveAuthor/MoveAuthorModal';
|
||||
import TagsModal from './Tags/TagsModal';
|
||||
import DeleteArtistModal from './Delete/DeleteArtistModal';
|
||||
import ArtistEditorFooterLabel from './ArtistEditorFooterLabel';
|
||||
import styles from './ArtistEditorFooter.css';
|
||||
import DeleteAuthorModal from './Delete/DeleteAuthorModal';
|
||||
import AuthorEditorFooterLabel from './AuthorEditorFooterLabel';
|
||||
import styles from './AuthorEditorFooter.css';
|
||||
|
||||
const NO_CHANGE = 'noChange';
|
||||
|
||||
class ArtistEditorFooter extends Component {
|
||||
class AuthorEditorFooter extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -27,10 +27,9 @@ class ArtistEditorFooter extends Component {
|
||||
monitored: NO_CHANGE,
|
||||
qualityProfileId: NO_CHANGE,
|
||||
metadataProfileId: NO_CHANGE,
|
||||
albumFolder: NO_CHANGE,
|
||||
rootFolderPath: NO_CHANGE,
|
||||
savingTags: false,
|
||||
isDeleteArtistModalOpen: false,
|
||||
isDeleteAuthorModalOpen: false,
|
||||
isTagsModalOpen: false,
|
||||
isConfirmMoveModalOpen: false,
|
||||
destinationRootFolder: null
|
||||
@@ -48,7 +47,6 @@ class ArtistEditorFooter extends Component {
|
||||
monitored: NO_CHANGE,
|
||||
qualityProfileId: NO_CHANGE,
|
||||
metadataProfileId: NO_CHANGE,
|
||||
albumFolder: NO_CHANGE,
|
||||
rootFolderPath: NO_CHANGE,
|
||||
savingTags: false
|
||||
});
|
||||
@@ -75,9 +73,6 @@ class ArtistEditorFooter extends Component {
|
||||
case 'monitored':
|
||||
this.props.onSaveSelected({ [name]: value === 'monitored' });
|
||||
break;
|
||||
case 'albumFolder':
|
||||
this.props.onSaveSelected({ [name]: value === 'yes' });
|
||||
break;
|
||||
default:
|
||||
this.props.onSaveSelected({ [name]: value });
|
||||
}
|
||||
@@ -96,11 +91,11 @@ class ArtistEditorFooter extends Component {
|
||||
}
|
||||
|
||||
onDeleteSelectedPress = () => {
|
||||
this.setState({ isDeleteArtistModalOpen: true });
|
||||
this.setState({ isDeleteAuthorModalOpen: true });
|
||||
}
|
||||
|
||||
onDeleteArtistModalClose = () => {
|
||||
this.setState({ isDeleteArtistModalOpen: false });
|
||||
onDeleteAuthorModalClose = () => {
|
||||
this.setState({ isDeleteAuthorModalOpen: false });
|
||||
}
|
||||
|
||||
onTagsPress = () => {
|
||||
@@ -120,7 +115,7 @@ class ArtistEditorFooter extends Component {
|
||||
this.props.onSaveSelected({ rootFolderPath: this.state.destinationRootFolder });
|
||||
}
|
||||
|
||||
onMoveArtistPress = () => {
|
||||
onMoveAuthorPress = () => {
|
||||
this.setState({
|
||||
isConfirmMoveModalOpen: false,
|
||||
destinationRootFolder: null
|
||||
@@ -141,22 +136,21 @@ class ArtistEditorFooter extends Component {
|
||||
selectedCount,
|
||||
isSaving,
|
||||
isDeleting,
|
||||
isOrganizingArtist,
|
||||
isRetaggingArtist,
|
||||
isOrganizingAuthor,
|
||||
isRetaggingAuthor,
|
||||
showMetadataProfile,
|
||||
onOrganizeArtistPress,
|
||||
onRetagArtistPress
|
||||
onOrganizeAuthorPress,
|
||||
onRetagAuthorPress
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
monitored,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
albumFolder,
|
||||
rootFolderPath,
|
||||
savingTags,
|
||||
isTagsModalOpen,
|
||||
isDeleteArtistModalOpen,
|
||||
isDeleteAuthorModalOpen,
|
||||
isConfirmMoveModalOpen,
|
||||
destinationRootFolder
|
||||
} = this.state;
|
||||
@@ -167,17 +161,11 @@ class ArtistEditorFooter extends Component {
|
||||
{ key: 'unmonitored', value: 'Unmonitored' }
|
||||
];
|
||||
|
||||
const albumFolderOptions = [
|
||||
{ key: NO_CHANGE, value: 'No Change', disabled: true },
|
||||
{ key: 'yes', value: 'Yes' },
|
||||
{ key: 'no', value: 'No' }
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContentFooter>
|
||||
<div className={styles.inputContainer}>
|
||||
<ArtistEditorFooterLabel
|
||||
label="Monitor Artist"
|
||||
<AuthorEditorFooterLabel
|
||||
label="Monitor Author"
|
||||
isSaving={isSaving && monitored !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
@@ -191,7 +179,7 @@ class ArtistEditorFooter extends Component {
|
||||
</div>
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
<ArtistEditorFooterLabel
|
||||
<AuthorEditorFooterLabel
|
||||
label="Quality Profile"
|
||||
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
|
||||
/>
|
||||
@@ -208,7 +196,7 @@ class ArtistEditorFooter extends Component {
|
||||
{
|
||||
showMetadataProfile &&
|
||||
<div className={styles.inputContainer}>
|
||||
<ArtistEditorFooterLabel
|
||||
<AuthorEditorFooterLabel
|
||||
label="Metadata Profile"
|
||||
isSaving={isSaving && metadataProfileId !== NO_CHANGE}
|
||||
/>
|
||||
@@ -225,22 +213,7 @@ class ArtistEditorFooter extends Component {
|
||||
}
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
<ArtistEditorFooterLabel
|
||||
label="Album Folder"
|
||||
isSaving={isSaving && albumFolder !== NO_CHANGE}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
name="albumFolder"
|
||||
value={albumFolder}
|
||||
values={albumFolderOptions}
|
||||
isDisabled={!selectedCount}
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
<ArtistEditorFooterLabel
|
||||
<AuthorEditorFooterLabel
|
||||
label="Root Folder"
|
||||
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
|
||||
/>
|
||||
@@ -257,8 +230,8 @@ class ArtistEditorFooter extends Component {
|
||||
|
||||
<div className={styles.buttonContainer}>
|
||||
<div className={styles.buttonContainerContent}>
|
||||
<ArtistEditorFooterLabel
|
||||
label={`${selectedCount} Artist(s) Selected`}
|
||||
<AuthorEditorFooterLabel
|
||||
label={`${selectedCount} Author(s) Selected`}
|
||||
isSaving={false}
|
||||
/>
|
||||
|
||||
@@ -267,9 +240,9 @@ class ArtistEditorFooter extends Component {
|
||||
<SpinnerButton
|
||||
className={styles.organizeSelectedButton}
|
||||
kind={kinds.WARNING}
|
||||
isSpinning={isOrganizingArtist}
|
||||
isDisabled={!selectedCount || isOrganizingArtist || isRetaggingArtist}
|
||||
onPress={onOrganizeArtistPress}
|
||||
isSpinning={isOrganizingAuthor}
|
||||
isDisabled={!selectedCount || isOrganizingAuthor || isRetaggingAuthor}
|
||||
onPress={onOrganizeAuthorPress}
|
||||
>
|
||||
Rename Files
|
||||
</SpinnerButton>
|
||||
@@ -277,9 +250,9 @@ class ArtistEditorFooter extends Component {
|
||||
<SpinnerButton
|
||||
className={styles.organizeSelectedButton}
|
||||
kind={kinds.WARNING}
|
||||
isSpinning={isRetaggingArtist}
|
||||
isDisabled={!selectedCount || isOrganizingArtist || isRetaggingArtist}
|
||||
onPress={onRetagArtistPress}
|
||||
isSpinning={isRetaggingAuthor}
|
||||
isDisabled={!selectedCount || isOrganizingAuthor || isRetaggingAuthor}
|
||||
onPress={onRetagAuthorPress}
|
||||
>
|
||||
Write Metadata Tags
|
||||
</SpinnerButton>
|
||||
@@ -287,7 +260,7 @@ class ArtistEditorFooter extends Component {
|
||||
<SpinnerButton
|
||||
className={styles.tagsButton}
|
||||
isSpinning={isSaving && savingTags}
|
||||
isDisabled={!selectedCount || isOrganizingArtist || isRetaggingArtist}
|
||||
isDisabled={!selectedCount || isOrganizingAuthor || isRetaggingAuthor}
|
||||
onPress={this.onTagsPress}
|
||||
>
|
||||
Set Readarr Tags
|
||||
@@ -314,17 +287,17 @@ class ArtistEditorFooter extends Component {
|
||||
onModalClose={this.onTagsModalClose}
|
||||
/>
|
||||
|
||||
<DeleteArtistModal
|
||||
isOpen={isDeleteArtistModalOpen}
|
||||
<DeleteAuthorModal
|
||||
isOpen={isDeleteAuthorModalOpen}
|
||||
authorIds={authorIds}
|
||||
onModalClose={this.onDeleteArtistModalClose}
|
||||
onModalClose={this.onDeleteAuthorModalClose}
|
||||
/>
|
||||
|
||||
<MoveArtistModal
|
||||
<MoveAuthorModal
|
||||
destinationRootFolder={destinationRootFolder}
|
||||
isOpen={isConfirmMoveModalOpen}
|
||||
onSavePress={this.onSaveRootFolderPress}
|
||||
onMoveArtistPress={this.onMoveArtistPress}
|
||||
onMoveAuthorPress={this.onMoveAuthorPress}
|
||||
/>
|
||||
|
||||
</PageContentFooter>
|
||||
@@ -332,19 +305,19 @@ class ArtistEditorFooter extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistEditorFooter.propTypes = {
|
||||
AuthorEditorFooter.propTypes = {
|
||||
authorIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
selectedCount: PropTypes.number.isRequired,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
saveError: PropTypes.object,
|
||||
isDeleting: PropTypes.bool.isRequired,
|
||||
deleteError: PropTypes.object,
|
||||
isOrganizingArtist: PropTypes.bool.isRequired,
|
||||
isRetaggingArtist: PropTypes.bool.isRequired,
|
||||
isOrganizingAuthor: PropTypes.bool.isRequired,
|
||||
isRetaggingAuthor: PropTypes.bool.isRequired,
|
||||
showMetadataProfile: PropTypes.bool.isRequired,
|
||||
onSaveSelected: PropTypes.func.isRequired,
|
||||
onOrganizeArtistPress: PropTypes.func.isRequired,
|
||||
onRetagArtistPress: PropTypes.func.isRequired
|
||||
onOrganizeAuthorPress: PropTypes.func.isRequired,
|
||||
onRetagAuthorPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistEditorFooter;
|
||||
export default AuthorEditorFooter;
|
||||
@@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import SpinnerIcon from 'Components/SpinnerIcon';
|
||||
import styles from './ArtistEditorFooterLabel.css';
|
||||
import styles from './AuthorEditorFooterLabel.css';
|
||||
|
||||
function ArtistEditorFooterLabel(props) {
|
||||
function AuthorEditorFooterLabel(props) {
|
||||
const {
|
||||
className,
|
||||
label,
|
||||
@@ -27,14 +27,14 @@ function ArtistEditorFooterLabel(props) {
|
||||
);
|
||||
}
|
||||
|
||||
ArtistEditorFooterLabel.propTypes = {
|
||||
AuthorEditorFooterLabel.propTypes = {
|
||||
className: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
isSaving: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
ArtistEditorFooterLabel.defaultProps = {
|
||||
AuthorEditorFooterLabel.defaultProps = {
|
||||
className: styles.label
|
||||
};
|
||||
|
||||
export default ArtistEditorFooterLabel;
|
||||
export default AuthorEditorFooterLabel;
|
||||
@@ -5,16 +5,15 @@ import TagListConnector from 'Components/TagListConnector';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import ArtistNameLink from 'Artist/ArtistNameLink';
|
||||
import ArtistStatusCell from 'Artist/Index/Table/ArtistStatusCell';
|
||||
import styles from './ArtistEditorRow.css';
|
||||
import AuthorNameLink from 'Author/AuthorNameLink';
|
||||
import AuthorStatusCell from 'Author/Index/Table/AuthorStatusCell';
|
||||
|
||||
class ArtistEditorRow extends Component {
|
||||
class AuthorEditorRow extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onAlbumFolderChange = () => {
|
||||
onBookFolderChange = () => {
|
||||
// Mock handler to satisfy `onChange` being required for `CheckInput`.
|
||||
//
|
||||
}
|
||||
@@ -27,8 +26,8 @@ class ArtistEditorRow extends Component {
|
||||
id,
|
||||
status,
|
||||
titleSlug,
|
||||
artistName,
|
||||
artistType,
|
||||
authorName,
|
||||
authorType,
|
||||
monitored,
|
||||
metadataProfile,
|
||||
qualityProfile,
|
||||
@@ -47,16 +46,16 @@ class ArtistEditorRow extends Component {
|
||||
onSelectedChange={onSelectedChange}
|
||||
/>
|
||||
|
||||
<ArtistStatusCell
|
||||
artistType={artistType}
|
||||
<AuthorStatusCell
|
||||
authorType={authorType}
|
||||
monitored={monitored}
|
||||
status={status}
|
||||
/>
|
||||
|
||||
<TableRowCell className={styles.title}>
|
||||
<ArtistNameLink
|
||||
<TableRowCell>
|
||||
<AuthorNameLink
|
||||
titleSlug={titleSlug}
|
||||
artistName={artistName}
|
||||
authorName={authorName}
|
||||
/>
|
||||
</TableRowCell>
|
||||
|
||||
@@ -85,12 +84,12 @@ class ArtistEditorRow extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistEditorRow.propTypes = {
|
||||
AuthorEditorRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
status: PropTypes.string.isRequired,
|
||||
titleSlug: PropTypes.string.isRequired,
|
||||
artistName: PropTypes.string.isRequired,
|
||||
artistType: PropTypes.string,
|
||||
authorName: PropTypes.string.isRequired,
|
||||
authorType: PropTypes.string,
|
||||
monitored: PropTypes.bool.isRequired,
|
||||
metadataProfile: PropTypes.object.isRequired,
|
||||
qualityProfile: PropTypes.object.isRequired,
|
||||
@@ -101,8 +100,8 @@ ArtistEditorRow.propTypes = {
|
||||
onSelectedChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
ArtistEditorRow.defaultProps = {
|
||||
AuthorEditorRow.defaultProps = {
|
||||
tags: []
|
||||
};
|
||||
|
||||
export default ArtistEditorRow;
|
||||
export default AuthorEditorRow;
|
||||
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createMetadataProfileSelector from 'Store/Selectors/createMetadataProfileSelector';
|
||||
import createQualityProfileSelector from 'Store/Selectors/createQualityProfileSelector';
|
||||
import ArtistEditorRow from './ArtistEditorRow';
|
||||
import AuthorEditorRow from './AuthorEditorRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
@@ -19,16 +19,16 @@ function createMapStateToProps() {
|
||||
);
|
||||
}
|
||||
|
||||
function ArtistEditorRowConnector(props) {
|
||||
function AuthorEditorRowConnector(props) {
|
||||
return (
|
||||
<ArtistEditorRow
|
||||
<AuthorEditorRow
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
ArtistEditorRowConnector.propTypes = {
|
||||
AuthorEditorRowConnector.propTypes = {
|
||||
qualityProfileId: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps)(ArtistEditorRowConnector);
|
||||
export default connect(createMapStateToProps)(AuthorEditorRowConnector);
|
||||
@@ -1,9 +1,9 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import AddNewArtistModalContentConnector from './AddNewArtistModalContentConnector';
|
||||
import DeleteAuthorModalContentConnector from './DeleteAuthorModalContentConnector';
|
||||
|
||||
function AddNewArtistModal(props) {
|
||||
function DeleteAuthorModal(props) {
|
||||
const {
|
||||
isOpen,
|
||||
onModalClose,
|
||||
@@ -15,7 +15,7 @@ function AddNewArtistModal(props) {
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<AddNewArtistModalContentConnector
|
||||
<DeleteAuthorModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
@@ -23,9 +23,9 @@ function AddNewArtistModal(props) {
|
||||
);
|
||||
}
|
||||
|
||||
AddNewArtistModal.propTypes = {
|
||||
DeleteAuthorModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default AddNewArtistModal;
|
||||
export default DeleteAuthorModal;
|
||||
@@ -9,9 +9,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 styles from './DeleteArtistModalContent.css';
|
||||
import styles from './DeleteAuthorModalContent.css';
|
||||
|
||||
class DeleteArtistModalContent extends Component {
|
||||
class DeleteAuthorModalContent extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -31,7 +31,7 @@ class DeleteArtistModalContent extends Component {
|
||||
this.setState({ deleteFiles: value });
|
||||
}
|
||||
|
||||
onDeleteArtistConfirmed = () => {
|
||||
onDeleteAuthorConfirmed = () => {
|
||||
const deleteFiles = this.state.deleteFiles;
|
||||
|
||||
this.setState({ deleteFiles: false });
|
||||
@@ -43,7 +43,7 @@ class DeleteArtistModalContent extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
artist,
|
||||
author,
|
||||
onModalClose
|
||||
} = this.props;
|
||||
const deleteFiles = this.state.deleteFiles;
|
||||
@@ -51,19 +51,19 @@ class DeleteArtistModalContent extends Component {
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Delete Selected Artist
|
||||
Delete Selected Author
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div>
|
||||
<FormGroup>
|
||||
<FormLabel>{`Delete Artist Folder${artist.length > 1 ? 's' : ''}`}</FormLabel>
|
||||
<FormLabel>{`Delete Author Folder${author.length > 1 ? 's' : ''}`}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="deleteFiles"
|
||||
value={deleteFiles}
|
||||
helpText={`Delete Artist Folder${artist.length > 1 ? 's' : ''} and all contents`}
|
||||
helpText={`Delete Author Folder${author.length > 1 ? 's' : ''} and all contents`}
|
||||
kind={kinds.DANGER}
|
||||
onChange={this.onDeleteFilesChange}
|
||||
/>
|
||||
@@ -71,15 +71,15 @@ class DeleteArtistModalContent extends Component {
|
||||
</div>
|
||||
|
||||
<div className={styles.message}>
|
||||
{`Are you sure you want to delete ${artist.length} selected artist${artist.length > 1 ? 's' : ''}${deleteFiles ? ' and all contents' : ''}?`}
|
||||
{`Are you sure you want to delete ${author.length} selected author${author.length > 1 ? 's' : ''}${deleteFiles ? ' and all contents' : ''}?`}
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
{
|
||||
artist.map((s) => {
|
||||
author.map((s) => {
|
||||
return (
|
||||
<li key={s.artistName}>
|
||||
<span>{s.artistName}</span>
|
||||
<li key={s.authorName}>
|
||||
<span>{s.authorName}</span>
|
||||
|
||||
{
|
||||
deleteFiles &&
|
||||
@@ -104,7 +104,7 @@ class DeleteArtistModalContent extends Component {
|
||||
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
onPress={this.onDeleteArtistConfirmed}
|
||||
onPress={this.onDeleteAuthorConfirmed}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
@@ -114,10 +114,10 @@ class DeleteArtistModalContent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
DeleteArtistModalContent.propTypes = {
|
||||
artist: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
DeleteAuthorModalContent.propTypes = {
|
||||
author: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onDeleteSelectedPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default DeleteArtistModalContent;
|
||||
export default DeleteAuthorModalContent;
|
||||
@@ -1,29 +1,29 @@
|
||||
import _ from 'lodash';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||
import { bulkDeleteArtist } from 'Store/Actions/artistEditorActions';
|
||||
import DeleteArtistModalContent from './DeleteArtistModalContent';
|
||||
import createAllAuthorSelector from 'Store/Selectors/createAllAuthorsSelector';
|
||||
import { bulkDeleteAuthor } from 'Store/Actions/authorEditorActions';
|
||||
import DeleteAuthorModalContent from './DeleteAuthorModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state, { authorIds }) => authorIds,
|
||||
createAllArtistSelector(),
|
||||
(authorIds, allArtists) => {
|
||||
const selectedArtist = _.intersectionWith(allArtists, authorIds, (s, id) => {
|
||||
createAllAuthorSelector(),
|
||||
(authorIds, allAuthors) => {
|
||||
const selectedAuthor = _.intersectionWith(allAuthors, authorIds, (s, id) => {
|
||||
return s.id === id;
|
||||
});
|
||||
|
||||
const sortedArtist = _.orderBy(selectedArtist, 'sortName');
|
||||
const artist = _.map(sortedArtist, (s) => {
|
||||
const sortedAuthor = _.orderBy(selectedAuthor, 'sortName');
|
||||
const author = _.map(sortedAuthor, (s) => {
|
||||
return {
|
||||
artistName: s.artistName,
|
||||
authorName: s.authorName,
|
||||
path: s.path
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
artist
|
||||
author
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -32,7 +32,7 @@ function createMapStateToProps() {
|
||||
function createMapDispatchToProps(dispatch, props) {
|
||||
return {
|
||||
onDeleteSelectedPress(deleteFiles) {
|
||||
dispatch(bulkDeleteArtist({
|
||||
dispatch(bulkDeleteAuthor({
|
||||
authorIds: props.authorIds,
|
||||
deleteFiles
|
||||
}));
|
||||
@@ -42,4 +42,4 @@ function createMapDispatchToProps(dispatch, props) {
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(createMapStateToProps, createMapDispatchToProps)(DeleteArtistModalContent);
|
||||
export default connect(createMapStateToProps, createMapDispatchToProps)(DeleteAuthorModalContent);
|
||||
@@ -1,9 +1,9 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import OrganizeArtistModalContentConnector from './OrganizeArtistModalContentConnector';
|
||||
import OrganizeAuthorModalContentConnector from './OrganizeAuthorModalContentConnector';
|
||||
|
||||
function OrganizeArtistModal(props) {
|
||||
function OrganizeAuthorModal(props) {
|
||||
const {
|
||||
isOpen,
|
||||
onModalClose,
|
||||
@@ -15,7 +15,7 @@ function OrganizeArtistModal(props) {
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<OrganizeArtistModalContentConnector
|
||||
<OrganizeAuthorModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
@@ -23,9 +23,9 @@ function OrganizeArtistModal(props) {
|
||||
);
|
||||
}
|
||||
|
||||
OrganizeArtistModal.propTypes = {
|
||||
OrganizeAuthorModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default OrganizeArtistModal;
|
||||
export default OrganizeAuthorModal;
|
||||
@@ -8,24 +8,24 @@ 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 styles from './OrganizeArtistModalContent.css';
|
||||
import styles from './OrganizeAuthorModalContent.css';
|
||||
|
||||
function OrganizeArtistModalContent(props) {
|
||||
function OrganizeAuthorModalContent(props) {
|
||||
const {
|
||||
artistNames,
|
||||
authorNames,
|
||||
onModalClose,
|
||||
onOrganizeArtistPress
|
||||
onOrganizeAuthorPress
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Organize Selected Artist
|
||||
Organize Selected Author
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<Alert>
|
||||
Tip: To preview a rename... select "Cancel" then click any artist name and use the
|
||||
Tip: To preview a rename... select "Cancel" then click any author name and use the
|
||||
<Icon
|
||||
className={styles.renameIcon}
|
||||
name={icons.ORGANIZE}
|
||||
@@ -33,15 +33,15 @@ function OrganizeArtistModalContent(props) {
|
||||
</Alert>
|
||||
|
||||
<div className={styles.message}>
|
||||
Are you sure you want to organize all files in the {artistNames.length} selected artist?
|
||||
Are you sure you want to organize all files in the {authorNames.length} selected author?
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
{
|
||||
artistNames.map((artistName) => {
|
||||
authorNames.map((authorName) => {
|
||||
return (
|
||||
<li key={artistName}>
|
||||
{artistName}
|
||||
<li key={authorName}>
|
||||
{authorName}
|
||||
</li>
|
||||
);
|
||||
})
|
||||
@@ -56,7 +56,7 @@ function OrganizeArtistModalContent(props) {
|
||||
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
onPress={onOrganizeArtistPress}
|
||||
onPress={onOrganizeAuthorPress}
|
||||
>
|
||||
Organize
|
||||
</Button>
|
||||
@@ -65,10 +65,10 @@ function OrganizeArtistModalContent(props) {
|
||||
);
|
||||
}
|
||||
|
||||
OrganizeArtistModalContent.propTypes = {
|
||||
artistNames: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
OrganizeAuthorModalContent.propTypes = {
|
||||
authorNames: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onOrganizeArtistPress: PropTypes.func.isRequired
|
||||
onOrganizeAuthorPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default OrganizeArtistModalContent;
|
||||
export default OrganizeAuthorModalContent;
|
||||
@@ -3,25 +3,25 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||
import createAllAuthorSelector from 'Store/Selectors/createAllAuthorsSelector';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import OrganizeArtistModalContent from './OrganizeArtistModalContent';
|
||||
import OrganizeAuthorModalContent from './OrganizeAuthorModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state, { authorIds }) => authorIds,
|
||||
createAllArtistSelector(),
|
||||
(authorIds, allArtists) => {
|
||||
const artist = _.intersectionWith(allArtists, authorIds, (s, id) => {
|
||||
createAllAuthorSelector(),
|
||||
(authorIds, allAuthors) => {
|
||||
const author = _.intersectionWith(allAuthors, authorIds, (s, id) => {
|
||||
return s.id === id;
|
||||
});
|
||||
|
||||
const sortedArtist = _.orderBy(artist, 'sortName');
|
||||
const artistNames = _.map(sortedArtist, 'artistName');
|
||||
const sortedAuthor = _.orderBy(author, 'sortName');
|
||||
const authorNames = _.map(sortedAuthor, 'authorName');
|
||||
|
||||
return {
|
||||
artistNames
|
||||
authorNames
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -31,14 +31,14 @@ const mapDispatchToProps = {
|
||||
executeCommand
|
||||
};
|
||||
|
||||
class OrganizeArtistModalContentConnector extends Component {
|
||||
class OrganizeAuthorModalContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onOrganizeArtistPress = () => {
|
||||
onOrganizeAuthorPress = () => {
|
||||
this.props.executeCommand({
|
||||
name: commandNames.RENAME_ARTIST,
|
||||
name: commandNames.RENAME_AUTHOR,
|
||||
authorIds: this.props.authorIds
|
||||
});
|
||||
|
||||
@@ -50,18 +50,18 @@ class OrganizeArtistModalContentConnector extends Component {
|
||||
|
||||
render(props) {
|
||||
return (
|
||||
<OrganizeArtistModalContent
|
||||
<OrganizeAuthorModalContent
|
||||
{...this.props}
|
||||
onOrganizeArtistPress={this.onOrganizeArtistPress}
|
||||
onOrganizeAuthorPress={this.onOrganizeAuthorPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
OrganizeArtistModalContentConnector.propTypes = {
|
||||
OrganizeAuthorModalContentConnector.propTypes = {
|
||||
authorIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
executeCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(OrganizeArtistModalContentConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(OrganizeAuthorModalContentConnector);
|
||||
@@ -49,7 +49,7 @@ class TagsModalContent extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
artistTags,
|
||||
authorTags,
|
||||
tagList,
|
||||
onModalClose
|
||||
} = this.props;
|
||||
@@ -93,7 +93,7 @@ class TagsModalContent extends Component {
|
||||
value={applyTags}
|
||||
values={applyTagsOptions}
|
||||
helpTexts={[
|
||||
'How to apply tags to the selected artist',
|
||||
'How to apply tags to the selected author',
|
||||
'Add: Add the tags the existing list of tags',
|
||||
'Remove: Remove the entered tags',
|
||||
'Replace: Replace the tags with the entered tags (enter no tags to clear all tags)'
|
||||
@@ -107,7 +107,7 @@ class TagsModalContent extends Component {
|
||||
|
||||
<div className={styles.result}>
|
||||
{
|
||||
artistTags.map((t) => {
|
||||
authorTags.map((t) => {
|
||||
const tag = _.find(tagList, { id: t });
|
||||
|
||||
if (!tag) {
|
||||
@@ -139,7 +139,7 @@ class TagsModalContent extends Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (artistTags.indexOf(t) > -1) {
|
||||
if (authorTags.indexOf(t) > -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ class TagsModalContent extends Component {
|
||||
}
|
||||
|
||||
TagsModalContent.propTypes = {
|
||||
artistTags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
authorTags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onApplyTagsPress: PropTypes.func.isRequired
|
||||
@@ -1,24 +1,24 @@
|
||||
import _ from 'lodash';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||
import createAllAuthorSelector from 'Store/Selectors/createAllAuthorsSelector';
|
||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||
import TagsModalContent from './TagsModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state, { authorIds }) => authorIds,
|
||||
createAllArtistSelector(),
|
||||
createAllAuthorSelector(),
|
||||
createTagsSelector(),
|
||||
(authorIds, allArtists, tagList) => {
|
||||
const artist = _.intersectionWith(allArtists, authorIds, (s, id) => {
|
||||
(authorIds, allAuthors, tagList) => {
|
||||
const author = _.intersectionWith(allAuthors, authorIds, (s, id) => {
|
||||
return s.id === id;
|
||||
});
|
||||
|
||||
const artistTags = _.uniq(_.concat(..._.map(artist, 'tags')));
|
||||
const authorTags = _.uniq(_.concat(..._.map(author, 'tags')));
|
||||
|
||||
return {
|
||||
artistTags,
|
||||
authorTags,
|
||||
tagList
|
||||
};
|
||||
}
|
||||
@@ -2,24 +2,24 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchArtistHistory, clearArtistHistory, artistHistoryMarkAsFailed } from 'Store/Actions/artistHistoryActions';
|
||||
import { authorHistoryMarkAsFailed, clearAuthorHistory, fetchAuthorHistory } from 'Store/Actions/authorHistoryActions';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.artistHistory,
|
||||
(artistHistory) => {
|
||||
return artistHistory;
|
||||
(state) => state.authorHistory,
|
||||
(authorHistory) => {
|
||||
return authorHistory;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchArtistHistory,
|
||||
clearArtistHistory,
|
||||
artistHistoryMarkAsFailed
|
||||
fetchAuthorHistory,
|
||||
clearAuthorHistory,
|
||||
authorHistoryMarkAsFailed
|
||||
};
|
||||
|
||||
class ArtistHistoryContentConnector extends Component {
|
||||
class AuthorHistoryContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -30,14 +30,14 @@ class ArtistHistoryContentConnector extends Component {
|
||||
bookId
|
||||
} = this.props;
|
||||
|
||||
this.props.fetchArtistHistory({
|
||||
this.props.fetchAuthorHistory({
|
||||
authorId,
|
||||
bookId
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.clearArtistHistory();
|
||||
this.props.clearAuthorHistory();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -49,7 +49,7 @@ class ArtistHistoryContentConnector extends Component {
|
||||
bookId
|
||||
} = this.props;
|
||||
|
||||
this.props.artistHistoryMarkAsFailed({
|
||||
this.props.authorHistoryMarkAsFailed({
|
||||
historyId,
|
||||
authorId,
|
||||
bookId
|
||||
@@ -74,13 +74,13 @@ class ArtistHistoryContentConnector extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistHistoryContentConnector.propTypes = {
|
||||
AuthorHistoryContentConnector.propTypes = {
|
||||
component: PropTypes.elementType.isRequired,
|
||||
authorId: PropTypes.number.isRequired,
|
||||
bookId: PropTypes.number,
|
||||
fetchArtistHistory: PropTypes.func.isRequired,
|
||||
clearArtistHistory: PropTypes.func.isRequired,
|
||||
artistHistoryMarkAsFailed: PropTypes.func.isRequired
|
||||
fetchAuthorHistory: PropTypes.func.isRequired,
|
||||
clearAuthorHistory: PropTypes.func.isRequired,
|
||||
authorHistoryMarkAsFailed: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(ArtistHistoryContentConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AuthorHistoryContentConnector);
|
||||
@@ -1,10 +1,10 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ArtistHistoryContentConnector from './ArtistHistoryContentConnector';
|
||||
import ArtistHistoryModalContent from './ArtistHistoryModalContent';
|
||||
import AuthorHistoryContentConnector from './AuthorHistoryContentConnector';
|
||||
import AuthorHistoryModalContent from './AuthorHistoryModalContent';
|
||||
|
||||
function ArtistHistoryModal(props) {
|
||||
function AuthorHistoryModal(props) {
|
||||
const {
|
||||
isOpen,
|
||||
onModalClose,
|
||||
@@ -16,8 +16,8 @@ function ArtistHistoryModal(props) {
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<ArtistHistoryContentConnector
|
||||
component={ArtistHistoryModalContent}
|
||||
<AuthorHistoryContentConnector
|
||||
component={AuthorHistoryModalContent}
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
@@ -25,9 +25,9 @@ function ArtistHistoryModal(props) {
|
||||
);
|
||||
}
|
||||
|
||||
ArtistHistoryModal.propTypes = {
|
||||
AuthorHistoryModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistHistoryModal;
|
||||
export default AuthorHistoryModal;
|
||||
@@ -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 ArtistHistoryTableContent from './ArtistHistoryTableContent';
|
||||
import AuthorHistoryTableContent from './AuthorHistoryTableContent';
|
||||
|
||||
class ArtistHistoryModalContent extends Component {
|
||||
class AuthorHistoryModalContent extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -24,7 +24,7 @@ class ArtistHistoryModalContent extends Component {
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<ArtistHistoryTableContent
|
||||
<AuthorHistoryTableContent
|
||||
{...this.props}
|
||||
/>
|
||||
</ModalBody>
|
||||
@@ -39,8 +39,8 @@ class ArtistHistoryModalContent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistHistoryModalContent.propTypes = {
|
||||
AuthorHistoryModalContent.propTypes = {
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistHistoryModalContent;
|
||||
export default AuthorHistoryModalContent;
|
||||
@@ -8,10 +8,10 @@ import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellCo
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import TrackQuality from 'Album/TrackQuality';
|
||||
import BookQuality from 'Book/BookQuality';
|
||||
import HistoryDetailsConnector from 'Activity/History/Details/HistoryDetailsConnector';
|
||||
import HistoryEventTypeCell from 'Activity/History/HistoryEventTypeCell';
|
||||
import styles from './ArtistHistoryRow.css';
|
||||
import styles from './AuthorHistoryRow.css';
|
||||
|
||||
function getTitle(eventType) {
|
||||
switch (eventType) {
|
||||
@@ -19,24 +19,24 @@ function getTitle(eventType) {
|
||||
return 'Grabbed';
|
||||
case 'downloadImported':
|
||||
return 'Download Completed';
|
||||
case 'trackFileImported':
|
||||
return 'Track Imported';
|
||||
case 'bookFileImported':
|
||||
return 'Book Imported';
|
||||
case 'downloadFailed':
|
||||
return 'Download Failed';
|
||||
case 'trackFileDeleted':
|
||||
return 'Track File Deleted';
|
||||
case 'trackFileRenamed':
|
||||
return 'Track File Renamed';
|
||||
case 'trackFileRetagged':
|
||||
return 'Track File Tags Updated';
|
||||
case 'albumImportIncomplete':
|
||||
return 'Album Import Incomplete';
|
||||
case 'bookFileDeleted':
|
||||
return 'Book File Deleted';
|
||||
case 'bookFileRenamed':
|
||||
return 'Book File Renamed';
|
||||
case 'bookFileRetagged':
|
||||
return 'Book File Tags Updated';
|
||||
case 'bookImportIncomplete':
|
||||
return 'Book Import Incomplete';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
class ArtistHistoryRow extends Component {
|
||||
class AuthorHistoryRow extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -76,7 +76,7 @@ class ArtistHistoryRow extends Component {
|
||||
qualityCutoffNotMet,
|
||||
date,
|
||||
data,
|
||||
album
|
||||
book
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
@@ -91,7 +91,7 @@ class ArtistHistoryRow extends Component {
|
||||
/>
|
||||
|
||||
<TableRowCell key={name}>
|
||||
{album.title}
|
||||
{book.title}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell>
|
||||
@@ -99,7 +99,7 @@ class ArtistHistoryRow extends Component {
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell>
|
||||
<TrackQuality
|
||||
<BookQuality
|
||||
quality={quality}
|
||||
isCutoffNotMet={qualityCutoffNotMet}
|
||||
/>
|
||||
@@ -153,7 +153,7 @@ class ArtistHistoryRow extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistHistoryRow.propTypes = {
|
||||
AuthorHistoryRow.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
eventType: PropTypes.string.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
@@ -161,10 +161,10 @@ ArtistHistoryRow.propTypes = {
|
||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
||||
date: PropTypes.string.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
fullArtist: PropTypes.bool.isRequired,
|
||||
artist: PropTypes.object.isRequired,
|
||||
album: PropTypes.object.isRequired,
|
||||
fullAuthor: PropTypes.bool.isRequired,
|
||||
author: PropTypes.object.isRequired,
|
||||
book: PropTypes.object.isRequired,
|
||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistHistoryRow;
|
||||
export default AuthorHistoryRow;
|
||||
@@ -1,18 +1,18 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchHistory, markAsFailed } from 'Store/Actions/historyActions';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import createAlbumSelector from 'Store/Selectors/createAlbumSelector';
|
||||
import ArtistHistoryRow from './ArtistHistoryRow';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import createBookSelector from 'Store/Selectors/createBookSelector';
|
||||
import AuthorHistoryRow from './AuthorHistoryRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
createAlbumSelector(),
|
||||
(artist, album) => {
|
||||
createAuthorSelector(),
|
||||
createBookSelector(),
|
||||
(author, book) => {
|
||||
return {
|
||||
artist,
|
||||
album
|
||||
author,
|
||||
book
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -23,4 +23,4 @@ const mapDispatchToProps = {
|
||||
markAsFailed
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(ArtistHistoryRow);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AuthorHistoryRow);
|
||||
21
frontend/src/Author/History/AuthorHistoryTable.js
Normal file
21
frontend/src/Author/History/AuthorHistoryTable.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import AuthorHistoryContentConnector from 'Author/History/AuthorHistoryContentConnector';
|
||||
import AuthorHistoryTableContent from 'Author/History/AuthorHistoryTableContent';
|
||||
|
||||
function AuthorHistoryTable(props) {
|
||||
const {
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<AuthorHistoryContentConnector
|
||||
component={AuthorHistoryTableContent}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
AuthorHistoryTable.propTypes = {
|
||||
};
|
||||
|
||||
export default AuthorHistoryTable;
|
||||
@@ -3,7 +3,7 @@ import React, { Component } from 'react';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import ArtistHistoryRowConnector from './ArtistHistoryRowConnector';
|
||||
import AuthorHistoryRowConnector from './AuthorHistoryRowConnector';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@@ -11,8 +11,8 @@ const columns = [
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'album',
|
||||
label: 'Album',
|
||||
name: 'book',
|
||||
label: 'Book',
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
@@ -42,7 +42,7 @@ const columns = [
|
||||
}
|
||||
];
|
||||
|
||||
class ArtistHistoryTableContent extends Component {
|
||||
class AuthorHistoryTableContent extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
@@ -57,7 +57,7 @@ class ArtistHistoryTableContent extends Component {
|
||||
onMarkAsFailedPress
|
||||
} = this.props;
|
||||
|
||||
const fullArtist = bookId == null;
|
||||
const fullAuthor = bookId == null;
|
||||
const hasItems = !!items.length;
|
||||
|
||||
return (
|
||||
@@ -84,9 +84,9 @@ class ArtistHistoryTableContent extends Component {
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<ArtistHistoryRowConnector
|
||||
<AuthorHistoryRowConnector
|
||||
key={item.id}
|
||||
fullArtist={fullArtist}
|
||||
fullAuthor={fullAuthor}
|
||||
{...item}
|
||||
onMarkAsFailedPress={onMarkAsFailedPress}
|
||||
/>
|
||||
@@ -101,7 +101,7 @@ class ArtistHistoryTableContent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistHistoryTableContent.propTypes = {
|
||||
AuthorHistoryTableContent.propTypes = {
|
||||
bookId: PropTypes.number,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
@@ -110,4 +110,4 @@ ArtistHistoryTableContent.propTypes = {
|
||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistHistoryTableContent;
|
||||
export default AuthorHistoryTableContent;
|
||||
@@ -13,38 +13,38 @@ import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import NoArtist from 'Artist/NoArtist';
|
||||
import ArtistIndexTableConnector from './Table/ArtistIndexTableConnector';
|
||||
import ArtistIndexTableOptionsConnector from './Table/ArtistIndexTableOptionsConnector';
|
||||
import ArtistIndexPosterOptionsModal from './Posters/Options/ArtistIndexPosterOptionsModal';
|
||||
import ArtistIndexPostersConnector from './Posters/ArtistIndexPostersConnector';
|
||||
import ArtistIndexBannerOptionsModal from './Banners/Options/ArtistIndexBannerOptionsModal';
|
||||
import ArtistIndexBannersConnector from './Banners/ArtistIndexBannersConnector';
|
||||
import ArtistIndexOverviewOptionsModal from './Overview/Options/ArtistIndexOverviewOptionsModal';
|
||||
import ArtistIndexOverviewsConnector from './Overview/ArtistIndexOverviewsConnector';
|
||||
import ArtistIndexFooterConnector from './ArtistIndexFooterConnector';
|
||||
import ArtistIndexFilterMenu from './Menus/ArtistIndexFilterMenu';
|
||||
import ArtistIndexSortMenu from './Menus/ArtistIndexSortMenu';
|
||||
import ArtistIndexViewMenu from './Menus/ArtistIndexViewMenu';
|
||||
import styles from './ArtistIndex.css';
|
||||
import NoAuthor from 'Author/NoAuthor';
|
||||
import AuthorIndexTableConnector from './Table/AuthorIndexTableConnector';
|
||||
import AuthorIndexTableOptionsConnector from './Table/AuthorIndexTableOptionsConnector';
|
||||
import AuthorIndexPosterOptionsModal from './Posters/Options/AuthorIndexPosterOptionsModal';
|
||||
import AuthorIndexPostersConnector from './Posters/AuthorIndexPostersConnector';
|
||||
import AuthorIndexBannerOptionsModal from './Banners/Options/AuthorIndexBannerOptionsModal';
|
||||
import AuthorIndexBannersConnector from './Banners/AuthorIndexBannersConnector';
|
||||
import AuthorIndexOverviewOptionsModal from './Overview/Options/AuthorIndexOverviewOptionsModal';
|
||||
import AuthorIndexOverviewsConnector from './Overview/AuthorIndexOverviewsConnector';
|
||||
import AuthorIndexFooterConnector from './AuthorIndexFooterConnector';
|
||||
import AuthorIndexFilterMenu from './Menus/AuthorIndexFilterMenu';
|
||||
import AuthorIndexSortMenu from './Menus/AuthorIndexSortMenu';
|
||||
import AuthorIndexViewMenu from './Menus/AuthorIndexViewMenu';
|
||||
import styles from './AuthorIndex.css';
|
||||
|
||||
function getViewComponent(view) {
|
||||
if (view === 'posters') {
|
||||
return ArtistIndexPostersConnector;
|
||||
return AuthorIndexPostersConnector;
|
||||
}
|
||||
|
||||
if (view === 'banners') {
|
||||
return ArtistIndexBannersConnector;
|
||||
return AuthorIndexBannersConnector;
|
||||
}
|
||||
|
||||
if (view === 'overview') {
|
||||
return ArtistIndexOverviewsConnector;
|
||||
return AuthorIndexOverviewsConnector;
|
||||
}
|
||||
|
||||
return ArtistIndexTableConnector;
|
||||
return AuthorIndexTableConnector;
|
||||
}
|
||||
|
||||
class ArtistIndex extends Component {
|
||||
class AuthorIndex extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
@@ -184,13 +184,13 @@ class ArtistIndex extends Component {
|
||||
sortKey,
|
||||
sortDirection,
|
||||
view,
|
||||
isRefreshingArtist,
|
||||
isRefreshingAuthor,
|
||||
isRssSyncExecuting,
|
||||
onScroll,
|
||||
onSortSelect,
|
||||
onFilterSelect,
|
||||
onViewSelect,
|
||||
onRefreshArtistPress,
|
||||
onRefreshAuthorPress,
|
||||
onRssSyncPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
@@ -206,7 +206,7 @@ class ArtistIndex extends Component {
|
||||
|
||||
const ViewComponent = getViewComponent(view);
|
||||
const isLoaded = !!(!error && isPopulated && items.length && scroller);
|
||||
const hasNoArtist = !totalItems;
|
||||
const hasNoAuthor = !totalItems;
|
||||
|
||||
return (
|
||||
<PageContent>
|
||||
@@ -216,15 +216,15 @@ class ArtistIndex extends Component {
|
||||
label="Update all"
|
||||
iconName={icons.REFRESH}
|
||||
spinningName={icons.REFRESH}
|
||||
isSpinning={isRefreshingArtist}
|
||||
onPress={onRefreshArtistPress}
|
||||
isSpinning={isRefreshingAuthor}
|
||||
onPress={onRefreshAuthorPress}
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label="RSS Sync"
|
||||
iconName={icons.RSS}
|
||||
isSpinning={isRssSyncExecuting}
|
||||
isDisabled={hasNoArtist}
|
||||
isDisabled={hasNoAuthor}
|
||||
onPress={onRssSyncPress}
|
||||
/>
|
||||
|
||||
@@ -239,7 +239,7 @@ class ArtistIndex extends Component {
|
||||
<TableOptionsModalWrapper
|
||||
{...otherProps}
|
||||
columns={columns}
|
||||
optionsComponent={ArtistIndexTableOptionsConnector}
|
||||
optionsComponent={AuthorIndexTableOptionsConnector}
|
||||
>
|
||||
<PageToolbarButton
|
||||
label="Options"
|
||||
@@ -254,7 +254,7 @@ class ArtistIndex extends Component {
|
||||
<PageToolbarButton
|
||||
label="Options"
|
||||
iconName={icons.POSTER}
|
||||
isDisabled={hasNoArtist}
|
||||
isDisabled={hasNoAuthor}
|
||||
onPress={this.onPosterOptionsPress}
|
||||
/> :
|
||||
null
|
||||
@@ -265,7 +265,7 @@ class ArtistIndex extends Component {
|
||||
<PageToolbarButton
|
||||
label="Options"
|
||||
iconName={icons.POSTER}
|
||||
isDisabled={hasNoArtist}
|
||||
isDisabled={hasNoAuthor}
|
||||
onPress={this.onBannerOptionsPress}
|
||||
/> :
|
||||
null
|
||||
@@ -276,7 +276,7 @@ class ArtistIndex extends Component {
|
||||
<PageToolbarButton
|
||||
label="Options"
|
||||
iconName={icons.OVERVIEW}
|
||||
isDisabled={hasNoArtist}
|
||||
isDisabled={hasNoAuthor}
|
||||
onPress={this.onOverviewOptionsPress}
|
||||
/> :
|
||||
null
|
||||
@@ -288,24 +288,24 @@ class ArtistIndex extends Component {
|
||||
<PageToolbarSeparator />
|
||||
}
|
||||
|
||||
<ArtistIndexViewMenu
|
||||
<AuthorIndexViewMenu
|
||||
view={view}
|
||||
isDisabled={hasNoArtist}
|
||||
isDisabled={hasNoAuthor}
|
||||
onViewSelect={onViewSelect}
|
||||
/>
|
||||
|
||||
<ArtistIndexSortMenu
|
||||
<AuthorIndexSortMenu
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
isDisabled={hasNoArtist}
|
||||
isDisabled={hasNoAuthor}
|
||||
onSortSelect={onSortSelect}
|
||||
/>
|
||||
|
||||
<ArtistIndexFilterMenu
|
||||
<AuthorIndexFilterMenu
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
filters={filters}
|
||||
customFilters={customFilters}
|
||||
isDisabled={hasNoArtist}
|
||||
isDisabled={hasNoAuthor}
|
||||
onFilterSelect={onFilterSelect}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
@@ -326,7 +326,7 @@ class ArtistIndex extends Component {
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div className={styles.errorMessage}>
|
||||
{getErrorMessage(error, 'Failed to load artist from API')}
|
||||
{getErrorMessage(error, 'Failed to load author from API')}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -343,13 +343,13 @@ class ArtistIndex extends Component {
|
||||
{...otherProps}
|
||||
/>
|
||||
|
||||
<ArtistIndexFooterConnector />
|
||||
<AuthorIndexFooterConnector />
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!error && isPopulated && !items.length &&
|
||||
<NoArtist totalItems={totalItems} />
|
||||
<NoAuthor totalItems={totalItems} />
|
||||
}
|
||||
</PageContentBodyConnector>
|
||||
|
||||
@@ -362,18 +362,18 @@ class ArtistIndex extends Component {
|
||||
}
|
||||
</div>
|
||||
|
||||
<ArtistIndexPosterOptionsModal
|
||||
<AuthorIndexPosterOptionsModal
|
||||
isOpen={isPosterOptionsModalOpen}
|
||||
onModalClose={this.onPosterOptionsModalClose}
|
||||
/>
|
||||
|
||||
<ArtistIndexBannerOptionsModal
|
||||
<AuthorIndexBannerOptionsModal
|
||||
isOpen={isBannerOptionsModalOpen}
|
||||
onModalClose={this.onBannerOptionsModalClose}
|
||||
|
||||
/>
|
||||
|
||||
<ArtistIndexOverviewOptionsModal
|
||||
<AuthorIndexOverviewOptionsModal
|
||||
isOpen={isOverviewOptionsModalOpen}
|
||||
onModalClose={this.onOverviewOptionsModalClose}
|
||||
|
||||
@@ -383,7 +383,7 @@ class ArtistIndex extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistIndex.propTypes = {
|
||||
AuthorIndex.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
@@ -396,15 +396,15 @@ ArtistIndex.propTypes = {
|
||||
sortKey: PropTypes.string,
|
||||
sortDirection: PropTypes.oneOf(sortDirections.all),
|
||||
view: PropTypes.string.isRequired,
|
||||
isRefreshingArtist: PropTypes.bool.isRequired,
|
||||
isRefreshingAuthor: PropTypes.bool.isRequired,
|
||||
isRssSyncExecuting: PropTypes.bool.isRequired,
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
onSortSelect: PropTypes.func.isRequired,
|
||||
onFilterSelect: PropTypes.func.isRequired,
|
||||
onViewSelect: PropTypes.func.isRequired,
|
||||
onRefreshArtistPress: PropTypes.func.isRequired,
|
||||
onRefreshAuthorPress: PropTypes.func.isRequired,
|
||||
onRssSyncPress: PropTypes.func.isRequired,
|
||||
onScroll: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ArtistIndex;
|
||||
export default AuthorIndex;
|
||||
@@ -3,31 +3,32 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createArtistClientSideCollectionItemsSelector from 'Store/Selectors/createArtistClientSideCollectionItemsSelector';
|
||||
import createAuthorClientSideCollectionItemsSelector
|
||||
from 'Store/Selectors/createAuthorClientSideCollectionItemsSelector';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||
import scrollPositions from 'Store/scrollPositions';
|
||||
import { setArtistSort, setArtistFilter, setArtistView, setArtistTableOption } from 'Store/Actions/artistIndexActions';
|
||||
import { setAuthorFilter, setAuthorSort, setAuthorTableOption, setAuthorView } from 'Store/Actions/authorIndexActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import withScrollPosition from 'Components/withScrollPosition';
|
||||
import ArtistIndex from './ArtistIndex';
|
||||
import AuthorIndex from './AuthorIndex';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistClientSideCollectionItemsSelector('artistIndex'),
|
||||
createCommandExecutingSelector(commandNames.REFRESH_ARTIST),
|
||||
createAuthorClientSideCollectionItemsSelector('authorIndex'),
|
||||
createCommandExecutingSelector(commandNames.REFRESH_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.RSS_SYNC),
|
||||
createDimensionsSelector(),
|
||||
(
|
||||
artist,
|
||||
isRefreshingArtist,
|
||||
author,
|
||||
isRefreshingAuthor,
|
||||
isRssSyncExecuting,
|
||||
dimensionsState
|
||||
) => {
|
||||
return {
|
||||
...artist,
|
||||
isRefreshingArtist,
|
||||
...author,
|
||||
isRefreshingAuthor,
|
||||
isRssSyncExecuting,
|
||||
isSmallScreen: dimensionsState.isSmallScreen
|
||||
};
|
||||
@@ -38,24 +39,24 @@ function createMapStateToProps() {
|
||||
function createMapDispatchToProps(dispatch, props) {
|
||||
return {
|
||||
onTableOptionChange(payload) {
|
||||
dispatch(setArtistTableOption(payload));
|
||||
dispatch(setAuthorTableOption(payload));
|
||||
},
|
||||
|
||||
onSortSelect(sortKey) {
|
||||
dispatch(setArtistSort({ sortKey }));
|
||||
dispatch(setAuthorSort({ sortKey }));
|
||||
},
|
||||
|
||||
onFilterSelect(selectedFilterKey) {
|
||||
dispatch(setArtistFilter({ selectedFilterKey }));
|
||||
dispatch(setAuthorFilter({ selectedFilterKey }));
|
||||
},
|
||||
|
||||
dispatchSetArtistView(view) {
|
||||
dispatch(setArtistView({ view }));
|
||||
dispatchSetAuthorView(view) {
|
||||
dispatch(setAuthorView({ view }));
|
||||
},
|
||||
|
||||
onRefreshArtistPress() {
|
||||
onRefreshAuthorPress() {
|
||||
dispatch(executeCommand({
|
||||
name: commandNames.REFRESH_ARTIST
|
||||
name: commandNames.REFRESH_AUTHOR
|
||||
}));
|
||||
},
|
||||
|
||||
@@ -67,17 +68,17 @@ function createMapDispatchToProps(dispatch, props) {
|
||||
};
|
||||
}
|
||||
|
||||
class ArtistIndexConnector extends Component {
|
||||
class AuthorIndexConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onViewSelect = (view) => {
|
||||
this.props.dispatchSetArtistView(view);
|
||||
this.props.dispatchSetAuthorView(view);
|
||||
}
|
||||
|
||||
onScroll = ({ scrollTop }) => {
|
||||
scrollPositions.artistIndex = scrollTop;
|
||||
scrollPositions.authorIndex = scrollTop;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -85,7 +86,7 @@ class ArtistIndexConnector extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ArtistIndex
|
||||
<AuthorIndex
|
||||
{...this.props}
|
||||
onViewSelect={this.onViewSelect}
|
||||
onScroll={this.onScroll}
|
||||
@@ -94,13 +95,13 @@ class ArtistIndexConnector extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistIndexConnector.propTypes = {
|
||||
AuthorIndexConnector.propTypes = {
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
view: PropTypes.string.isRequired,
|
||||
dispatchSetArtistView: PropTypes.func.isRequired
|
||||
dispatchSetAuthorView: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default withScrollPosition(
|
||||
connect(createMapStateToProps, createMapDispatchToProps)(ArtistIndexConnector),
|
||||
'artistIndex'
|
||||
connect(createMapStateToProps, createMapDispatchToProps)(AuthorIndexConnector),
|
||||
'authorIndex'
|
||||
);
|
||||
@@ -1,24 +1,24 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { setArtistFilter } from 'Store/Actions/artistIndexActions';
|
||||
import { setAuthorFilter } from 'Store/Actions/authorIndexActions';
|
||||
import FilterModal from 'Components/Filter/FilterModal';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.artist.items,
|
||||
(state) => state.artistIndex.filterBuilderProps,
|
||||
(state) => state.authors.items,
|
||||
(state) => state.authorIndex.filterBuilderProps,
|
||||
(sectionItems, filterBuilderProps) => {
|
||||
return {
|
||||
sectionItems,
|
||||
filterBuilderProps,
|
||||
customFilterType: 'artistIndex'
|
||||
customFilterType: 'authorIndex'
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
dispatchSetFilter: setArtistFilter
|
||||
dispatchSetFilter: setAuthorFilter
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(FilterModal);
|
||||
@@ -5,34 +5,34 @@ import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import { ColorImpairedConsumer } from 'App/ColorImpairedContext';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
import styles from './ArtistIndexFooter.css';
|
||||
import styles from './AuthorIndexFooter.css';
|
||||
|
||||
class ArtistIndexFooter extends PureComponent {
|
||||
class AuthorIndexFooter extends PureComponent {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const { artist } = this.props;
|
||||
const count = artist.length;
|
||||
let tracks = 0;
|
||||
let trackFiles = 0;
|
||||
const { author } = this.props;
|
||||
const count = author.length;
|
||||
let books = 0;
|
||||
let bookFiles = 0;
|
||||
let ended = 0;
|
||||
let continuing = 0;
|
||||
let monitored = 0;
|
||||
let totalFileSize = 0;
|
||||
|
||||
artist.forEach((s) => {
|
||||
author.forEach((s) => {
|
||||
const { statistics = {} } = s;
|
||||
|
||||
const {
|
||||
trackCount = 0,
|
||||
trackFileCount = 0,
|
||||
bookCount = 0,
|
||||
bookFileCount = 0,
|
||||
sizeOnDisk = 0
|
||||
} = statistics;
|
||||
|
||||
tracks += trackCount;
|
||||
trackFiles += trackFileCount;
|
||||
books += bookCount;
|
||||
bookFiles += bookFileCount;
|
||||
|
||||
if (s.status === 'ended') {
|
||||
ended++;
|
||||
@@ -127,12 +127,12 @@ class ArtistIndexFooter extends PureComponent {
|
||||
<DescriptionList>
|
||||
<DescriptionListItem
|
||||
title="Books"
|
||||
data={tracks}
|
||||
data={books}
|
||||
/>
|
||||
|
||||
<DescriptionListItem
|
||||
title="Files"
|
||||
data={trackFiles}
|
||||
data={bookFiles}
|
||||
/>
|
||||
</DescriptionList>
|
||||
|
||||
@@ -151,8 +151,8 @@ class ArtistIndexFooter extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
ArtistIndexFooter.propTypes = {
|
||||
artist: PropTypes.arrayOf(PropTypes.object).isRequired
|
||||
AuthorIndexFooter.propTypes = {
|
||||
author: PropTypes.arrayOf(PropTypes.object).isRequired
|
||||
};
|
||||
|
||||
export default ArtistIndexFooter;
|
||||
export default AuthorIndexFooter;
|
||||
@@ -2,13 +2,13 @@ import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createDeepEqualSelector from 'Store/Selectors/createDeepEqualSelector';
|
||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import ArtistIndexFooter from './ArtistIndexFooter';
|
||||
import AuthorIndexFooter from './AuthorIndexFooter';
|
||||
|
||||
function createUnoptimizedSelector() {
|
||||
return createSelector(
|
||||
createClientSideCollectionSelector('artist', 'artistIndex'),
|
||||
(artist) => {
|
||||
return artist.items.map((s) => {
|
||||
createClientSideCollectionSelector('authors', 'authorIndex'),
|
||||
(authors) => {
|
||||
return authors.items.map((s) => {
|
||||
const {
|
||||
monitored,
|
||||
status,
|
||||
@@ -25,22 +25,22 @@ function createUnoptimizedSelector() {
|
||||
);
|
||||
}
|
||||
|
||||
function createArtistSelector() {
|
||||
function createAuthorSelector() {
|
||||
return createDeepEqualSelector(
|
||||
createUnoptimizedSelector(),
|
||||
(artist) => artist
|
||||
(author) => author
|
||||
);
|
||||
}
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
(artist) => {
|
||||
createAuthorSelector(),
|
||||
(author) => {
|
||||
return {
|
||||
artist
|
||||
author
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(createMapStateToProps)(ArtistIndexFooter);
|
||||
export default connect(createMapStateToProps)(AuthorIndexFooter);
|
||||
@@ -4,28 +4,28 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
|
||||
import createExecutingCommandsSelector from 'Store/Selectors/createExecutingCommandsSelector';
|
||||
import createArtistQualityProfileSelector from 'Store/Selectors/createArtistQualityProfileSelector';
|
||||
import createArtistMetadataProfileSelector from 'Store/Selectors/createArtistMetadataProfileSelector';
|
||||
import createAuthorQualityProfileSelector from 'Store/Selectors/createAuthorQualityProfileSelector';
|
||||
import createAuthorMetadataProfileSelector from 'Store/Selectors/createAuthorMetadataProfileSelector';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
|
||||
function selectShowSearchAction() {
|
||||
return createSelector(
|
||||
(state) => state.artistIndex,
|
||||
(artistIndex) => {
|
||||
const view = artistIndex.view;
|
||||
(state) => state.authorIndex,
|
||||
(authorIndex) => {
|
||||
const view = authorIndex.view;
|
||||
|
||||
switch (view) {
|
||||
case 'posters':
|
||||
return artistIndex.posterOptions.showSearchAction;
|
||||
return authorIndex.posterOptions.showSearchAction;
|
||||
case 'banners':
|
||||
return artistIndex.bannerOptions.showSearchAction;
|
||||
return authorIndex.bannerOptions.showSearchAction;
|
||||
case 'overview':
|
||||
return artistIndex.overviewOptions.showSearchAction;
|
||||
return authorIndex.overviewOptions.showSearchAction;
|
||||
default:
|
||||
return artistIndex.tableOptions.showSearchAction;
|
||||
return authorIndex.tableOptions.showSearchAction;
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -33,52 +33,52 @@ function selectShowSearchAction() {
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createArtistSelector(),
|
||||
createArtistQualityProfileSelector(),
|
||||
createArtistMetadataProfileSelector(),
|
||||
createAuthorSelector(),
|
||||
createAuthorQualityProfileSelector(),
|
||||
createAuthorMetadataProfileSelector(),
|
||||
selectShowSearchAction(),
|
||||
createExecutingCommandsSelector(),
|
||||
(
|
||||
artist,
|
||||
author,
|
||||
qualityProfile,
|
||||
metadataProfile,
|
||||
showSearchAction,
|
||||
executingCommands
|
||||
) => {
|
||||
|
||||
// If an artist is deleted this selector may fire before the parent
|
||||
// selectors, which will result in an undefined artist, if that happens
|
||||
// If an author is deleted this selector may fire before the parent
|
||||
// selectors, which will result in an undefined author, if that happens
|
||||
// we want to return early here and again in the render function to avoid
|
||||
// trying to show an artist that has no information available.
|
||||
// trying to show an author that has no information available.
|
||||
|
||||
if (!artist) {
|
||||
if (!author) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const isRefreshingArtist = executingCommands.some((command) => {
|
||||
const isRefreshingAuthor = executingCommands.some((command) => {
|
||||
return (
|
||||
command.name === commandNames.REFRESH_ARTIST &&
|
||||
command.body.authorId === artist.id
|
||||
command.name === commandNames.REFRESH_AUTHOR &&
|
||||
command.body.authorId === author.id
|
||||
);
|
||||
});
|
||||
|
||||
const isSearchingArtist = executingCommands.some((command) => {
|
||||
const isSearchingAuthor = executingCommands.some((command) => {
|
||||
return (
|
||||
command.name === commandNames.ARTIST_SEARCH &&
|
||||
command.body.authorId === artist.id
|
||||
command.name === commandNames.AUTHOR_SEARCH &&
|
||||
command.body.authorId === author.id
|
||||
);
|
||||
});
|
||||
|
||||
const latestAlbum = _.maxBy(artist.albums, (album) => album.releaseDate);
|
||||
const latestBook = _.maxBy(author.books, (book) => book.releaseDate);
|
||||
|
||||
return {
|
||||
...artist,
|
||||
...author,
|
||||
qualityProfile,
|
||||
metadataProfile,
|
||||
latestAlbum,
|
||||
latestBook,
|
||||
showSearchAction,
|
||||
isRefreshingArtist,
|
||||
isSearchingArtist
|
||||
isRefreshingAuthor,
|
||||
isSearchingAuthor
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -88,21 +88,21 @@ const mapDispatchToProps = {
|
||||
dispatchExecuteCommand: executeCommand
|
||||
};
|
||||
|
||||
class ArtistIndexItemConnector extends Component {
|
||||
class AuthorIndexItemConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onRefreshArtistPress = () => {
|
||||
onRefreshAuthorPress = () => {
|
||||
this.props.dispatchExecuteCommand({
|
||||
name: commandNames.REFRESH_ARTIST,
|
||||
name: commandNames.REFRESH_AUTHOR,
|
||||
authorId: this.props.id
|
||||
});
|
||||
}
|
||||
|
||||
onSearchPress = () => {
|
||||
this.props.dispatchExecuteCommand({
|
||||
name: commandNames.ARTIST_SEARCH,
|
||||
name: commandNames.AUTHOR_SEARCH,
|
||||
authorId: this.props.id
|
||||
});
|
||||
}
|
||||
@@ -125,17 +125,17 @@ class ArtistIndexItemConnector extends Component {
|
||||
<ItemComponent
|
||||
{...otherProps}
|
||||
id={id}
|
||||
onRefreshArtistPress={this.onRefreshArtistPress}
|
||||
onRefreshAuthorPress={this.onRefreshAuthorPress}
|
||||
onSearchPress={this.onSearchPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ArtistIndexItemConnector.propTypes = {
|
||||
AuthorIndexItemConnector.propTypes = {
|
||||
id: PropTypes.number,
|
||||
component: PropTypes.elementType.isRequired,
|
||||
dispatchExecuteCommand: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(ArtistIndexItemConnector);
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AuthorIndexItemConnector);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user