diff --git a/frontend/src/Author/Details/BookRow.js b/frontend/src/Author/Details/BookRow.js index 043a6798e..292232bf2 100644 --- a/frontend/src/Author/Details/BookRow.js +++ b/frontend/src/Author/Details/BookRow.js @@ -2,28 +2,14 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import BookSearchCellConnector from 'Book/BookSearchCellConnector'; import BookTitleLink from 'Book/BookTitleLink'; -import Label from 'Components/Label'; import MonitorToggleButton from 'Components/MonitorToggleButton'; import StarRating from 'Components/StarRating'; import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector'; import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRow from 'Components/Table/TableRow'; -import { kinds, sizes } from 'Helpers/Props'; -import translate from 'Utilities/String/translate'; +import BookStatus from './BookStatus'; import styles from './BookRow.css'; -function getBookCountKind(monitored, bookFileCount, bookCount) { - if (bookFileCount === bookCount && bookCount > 0) { - return kinds.SUCCESS; - } - - if (!monitored) { - return kinds.WARNING; - } - - return kinds.DANGER; -} - class BookRow extends Component { // @@ -69,7 +55,6 @@ class BookRow extends Component { id, authorId, monitored, - statistics, releaseDate, title, seriesTitle, @@ -79,14 +64,12 @@ class BookRow extends Component { isSaving, authorMonitored, titleSlug, + bookFiles, columns } = this.props; - const { - bookCount, - bookFileCount, - totalBookCount - } = statistics; + const bookFile = bookFiles[0]; + const isAvailable = Date.parse(releaseDate) < new Date(); return ( @@ -196,15 +179,11 @@ class BookRow extends Component { key={name} className={styles.status} > - + ); } @@ -240,16 +219,9 @@ BookRow.propTypes = { titleSlug: PropTypes.string.isRequired, isSaving: PropTypes.bool, authorMonitored: PropTypes.bool.isRequired, - statistics: PropTypes.object.isRequired, + bookFiles: PropTypes.arrayOf(PropTypes.object).isRequired, columns: PropTypes.arrayOf(PropTypes.object).isRequired, onMonitorBookPress: PropTypes.func.isRequired }; -BookRow.defaultProps = { - statistics: { - bookCount: 0, - bookFileCount: 0 - } -}; - export default BookRow; diff --git a/frontend/src/Author/Details/BookRowConnector.js b/frontend/src/Author/Details/BookRowConnector.js index c0c7d0087..e6ab2f9dd 100644 --- a/frontend/src/Author/Details/BookRowConnector.js +++ b/frontend/src/Author/Details/BookRowConnector.js @@ -2,17 +2,38 @@ 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'; +const selectBookFiles = createSelector( + (state) => state.bookFiles, + (bookFiles) => { + const { + items + } = bookFiles; + + const bookFileDict = items.reduce((acc, file) => { + const bookId = file.bookId; + if (!acc.hasOwnProperty(bookId)) { + acc[bookId] = []; + } + + acc[bookId].push(file); + return acc; + }, {}); + + return bookFileDict; + } +); + function createMapStateToProps() { return createSelector( createAuthorSelector(), - createBookFileSelector(), - (author = {}, bookFile) => { + selectBookFiles, + (state, { id }) => id, + (author = {}, bookFiles, bookId) => { return { authorMonitored: author.monitored, - bookFilePath: bookFile ? bookFile.path : null + bookFiles: bookFiles[bookId] ?? [] }; } ); diff --git a/frontend/src/Author/Details/BookStatus.css b/frontend/src/Author/Details/BookStatus.css new file mode 100644 index 000000000..2ed71140a --- /dev/null +++ b/frontend/src/Author/Details/BookStatus.css @@ -0,0 +1,5 @@ + +.center { + display: flex; + justify-content: center; +} diff --git a/frontend/src/Author/Details/BookStatus.js b/frontend/src/Author/Details/BookStatus.js new file mode 100644 index 000000000..42432c70d --- /dev/null +++ b/frontend/src/Author/Details/BookStatus.js @@ -0,0 +1,78 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import BookQuality from 'Book/BookQuality'; +import Label from 'Components/Label'; +import { kinds } from 'Helpers/Props'; +import translate from 'Utilities/String/translate'; +import styles from './BookStatus.css'; + +function BookStatus(props) { + const { + isAvailable, + monitored, + bookFile + } = props; + + const hasBookFile = !!bookFile; + + if (hasBookFile) { + const quality = bookFile.quality; + + return ( +
+ +
+ ); + } + + if (!monitored) { + return ( +
+ +
+ ); + } + + if (isAvailable) { + return ( +
+ +
+ ); + } + + return ( +
+ +
+ ); +} + +BookStatus.propTypes = { + isAvailable: PropTypes.bool, + monitored: PropTypes.bool.isRequired, + bookFile: PropTypes.object +}; + +export default BookStatus; diff --git a/frontend/src/Book/BookQuality.js b/frontend/src/Book/BookQuality.js index 8048f544d..b19fe2a8f 100644 --- a/frontend/src/Book/BookQuality.js +++ b/frontend/src/Book/BookQuality.js @@ -4,11 +4,7 @@ import Label from 'Components/Label'; import { kinds } from 'Helpers/Props'; import formatBytes from 'Utilities/Number/formatBytes'; -function getTooltip(title, quality, size) { - if (!title) { - return; - } - +function getTooltip(title, quality, size, isMonitored, isCutoffNotMet) { const revision = quality.revision; if (revision.real && revision.real > 0) { @@ -23,6 +19,12 @@ function getTooltip(title, quality, size) { title += ` - ${formatBytes(size)}`; } + if (!isMonitored) { + title += ' [Not Monitored]'; + } else if (isCutoffNotMet) { + title += ' [Cutoff Not Met]'; + } + return title; } @@ -32,14 +34,26 @@ function BookQuality(props) { title, quality, size, + isMonitored, isCutoffNotMet } = props; + let kind = kinds.DEFAULT; + if (!isMonitored) { + kind = kinds.DISABLED; + } else if (isCutoffNotMet) { + kind = kinds.INVERSE; + } + + if (!quality) { + return null; + } + return ( @@ -51,11 +65,13 @@ BookQuality.propTypes = { title: PropTypes.string, quality: PropTypes.object.isRequired, size: PropTypes.number, + isMonitored: PropTypes.bool, isCutoffNotMet: PropTypes.bool }; BookQuality.defaultProps = { - title: '' + title: '', + isMonitored: true }; export default BookQuality; diff --git a/frontend/src/Store/Actions/bookActions.js b/frontend/src/Store/Actions/bookActions.js index 5d5cfc1d7..63549ef37 100644 --- a/frontend/src/Store/Actions/bookActions.js +++ b/frontend/src/Store/Actions/bookActions.js @@ -105,20 +105,6 @@ export const filterPredicates = { }; export const sortPredicates = { - status: function(item) { - let result = 0; - - if (item.monitored) { - result += 2; - } - - if (item.status === 'continuing') { - result++; - } - - return result; - }, - sizeOnDisk: function(item) { const { statistics = {} } = item; diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index cdd8c45df..bc4171d8d 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -64,6 +64,7 @@ "BlacklistHelpText": "Prevents Readarr from automatically grabbing these files again", "BlacklistRelease": "Blacklist Release", "Book": "Book", + "BookAvailableButMissing": "Book Available, but Missing", "BookDownloaded": "Book Downloaded", "BookFileCountBookCountTotalTotalBookCountInterp": "{0} / {1} (Total: {2})", "BookFileCounttotalBookCountBooksDownloadedInterp": "{0}/{1} books downloaded", @@ -402,6 +403,8 @@ "NoMinimumForAnyRuntime": "No minimum for any runtime", "NoName": "Do not show name", "None": "None", + "NotAvailable": "Not Available", + "NotMonitored": "Not Monitored", "NoTagsHaveBeenAddedYet": "No tags have been added yet. Add tags to link authors with delay profiles, restrictions, or notifications. Click {0} to find out more about tags in Readarr.", "NotificationTriggers": "Notification Triggers", "NoUpdatesAreAvailable": "No updates are available",