mirror of
https://github.com/fergalmoran/Readarr.git
synced 2025-12-22 09:29:59 +00:00
Fixed: Better book status column in author details
This commit is contained in:
@@ -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 (
|
||||
<TableRow>
|
||||
@@ -196,15 +179,11 @@ class BookRow extends Component {
|
||||
key={name}
|
||||
className={styles.status}
|
||||
>
|
||||
<Label
|
||||
title={translate('TotalBookCountBooksTotalBookFileCountBooksWithFilesInterp', [totalBookCount, bookFileCount])}
|
||||
kind={getBookCountKind(monitored, bookFileCount, bookCount)}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
{
|
||||
<span>{bookFileCount} / {bookCount}</span>
|
||||
}
|
||||
</Label>
|
||||
<BookStatus
|
||||
isAvailable={isAvailable}
|
||||
monitored={monitored}
|
||||
bookFile={bookFile}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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] ?? []
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
5
frontend/src/Author/Details/BookStatus.css
Normal file
5
frontend/src/Author/Details/BookStatus.css
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
78
frontend/src/Author/Details/BookStatus.js
Normal file
78
frontend/src/Author/Details/BookStatus.js
Normal file
@@ -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 (
|
||||
<div className={styles.center}>
|
||||
<BookQuality
|
||||
title={quality.quality.name}
|
||||
size={bookFile.size}
|
||||
quality={quality}
|
||||
isMonitored={monitored}
|
||||
isCutoffNotMet={bookFile.qualityCutoffNotMet}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!monitored) {
|
||||
return (
|
||||
<div className={styles.center}>
|
||||
<Label
|
||||
title={translate('NotMonitored')}
|
||||
kind={kinds.WARNING}
|
||||
>
|
||||
{translate('NotMonitored')}
|
||||
</Label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isAvailable) {
|
||||
return (
|
||||
<div className={styles.center}>
|
||||
<Label
|
||||
title={translate('BookAvailableButMissing')}
|
||||
kind={kinds.DANGER}
|
||||
>
|
||||
{translate('Missing')}
|
||||
</Label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.center}>
|
||||
<Label
|
||||
title={translate('NotAvailable')}
|
||||
kind={kinds.INFO}
|
||||
>
|
||||
{translate('NotAvailable')}
|
||||
</Label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
BookStatus.propTypes = {
|
||||
isAvailable: PropTypes.bool,
|
||||
monitored: PropTypes.bool.isRequired,
|
||||
bookFile: PropTypes.object
|
||||
};
|
||||
|
||||
export default BookStatus;
|
||||
@@ -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 (
|
||||
<Label
|
||||
className={className}
|
||||
kind={isCutoffNotMet ? kinds.INVERSE : kinds.DEFAULT}
|
||||
title={getTooltip(title, quality, size)}
|
||||
kind={kind}
|
||||
title={getTooltip(title, quality, size, isMonitored, isCutoffNotMet)}
|
||||
>
|
||||
{quality.quality.name}
|
||||
</Label>
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user