Fixed: Removed unnecessary author data from book endpoint

This commit is contained in:
ta264
2022-05-26 21:51:37 +01:00
parent ce58e6ecdb
commit a59706ceb4
16 changed files with 190 additions and 49 deletions

View File

@@ -8,6 +8,7 @@ import * as commandNames from 'Commands/commandNames';
import { toggleBooksMonitored } from 'Store/Actions/bookActions';
import { clearBookFiles, fetchBookFiles } from 'Store/Actions/bookFileActions';
import { executeCommand } from 'Store/Actions/commandActions';
import { clearEditions, fetchEditions } from 'Store/Actions/editionActions';
import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions';
import createAllAuthorSelector from 'Store/Selectors/createAllAuthorsSelector';
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
@@ -43,11 +44,12 @@ function createMapStateToProps() {
(state, { titleSlug }) => titleSlug,
selectBookFiles,
(state) => state.books,
(state) => state.editions,
createAllAuthorSelector(),
createCommandsSelector(),
createUISettingsSelector(),
createDimensionsSelector(),
(titleSlug, bookFiles, books, authors, commands, uiSettings, dimensions) => {
(titleSlug, bookFiles, books, editions, authors, commands, uiSettings, dimensions) => {
const book = books.items.find((b) => b.titleSlug === titleSlug);
const author = authors.find((a) => a.id === book.authorId);
const sortedBooks = books.items.filter((b) => b.authorId === book.authorId);
@@ -79,8 +81,8 @@ function createMapStateToProps() {
isRefreshingCommand.body.bookId === book.id
);
const isFetching = isBookFilesFetching;
const isPopulated = isBookFilesPopulated;
const isFetching = isBookFilesFetching || editions.isFetching;
const isPopulated = isBookFilesPopulated && editions.isPopulated;
return {
...book,
@@ -104,6 +106,8 @@ const mapDispatchToProps = {
executeCommand,
fetchBookFiles,
clearBookFiles,
fetchEditions,
clearEditions,
clearReleases,
cancelFetchReleases,
toggleBooksMonitored
@@ -121,7 +125,8 @@ class BookDetailsConnector extends Component {
}
componentDidUpdate(prevProps) {
if (!_.isEqual(getMonitoredEditions(prevProps), getMonitoredEditions(this.props)) ||
if (prevProps.id !== this.props.id ||
!_.isEqual(getMonitoredEditions(prevProps), getMonitoredEditions(this.props)) ||
(prevProps.anyReleaseOk === false && this.props.anyReleaseOk === true)) {
this.unpopulate();
this.populate();
@@ -140,12 +145,14 @@ class BookDetailsConnector extends Component {
const bookId = this.props.id;
this.props.fetchBookFiles({ bookId });
this.props.fetchEditions({ bookId });
}
unpopulate = () => {
this.props.cancelFetchReleases();
this.props.clearReleases();
this.props.clearBookFiles();
this.props.clearEditions();
}
//
@@ -195,6 +202,8 @@ BookDetailsConnector.propTypes = {
titleSlug: PropTypes.string.isRequired,
fetchBookFiles: PropTypes.func.isRequired,
clearBookFiles: PropTypes.func.isRequired,
fetchEditions: PropTypes.func.isRequired,
clearEditions: PropTypes.func.isRequired,
clearReleases: PropTypes.func.isRequired,
cancelFetchReleases: PropTypes.func.isRequired,
toggleBooksMonitored: PropTypes.func.isRequired,

View File

@@ -8,15 +8,25 @@ import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import BookDetailsHeader from './BookDetailsHeader';
const selectOverview = createSelector(
(state) => state.editions,
(editions) => {
const monitored = editions.items.find((e) => e.monitored === true);
return monitored?.overview;
}
);
function createMapStateToProps() {
return createSelector(
createBookSelector(),
selectOverview,
createUISettingsSelector(),
createDimensionsSelector(),
(book, uiSettings, dimensions) => {
(book, overview, uiSettings, dimensions) => {
return {
...book,
overview,
shortDateFormat: uiSettings.shortDateFormat,
isSmallScreen: dimensions.isSmallScreen
};

View File

@@ -53,7 +53,7 @@ class EditBookModalContent extends Component {
editions
} = item;
const hasFile = statistics ? statistics.bookFileCount : 0;
const hasFile = statistics ? statistics.bookFileCount > 0 : false;
const errorMessage = getErrorMessage(error, 'Unable to load editions');
return (

View File

@@ -4,7 +4,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { saveBook, setBookValue } from 'Store/Actions/bookActions';
import { clearEditions, fetchEditions } from 'Store/Actions/editionActions';
import { saveEditions } from 'Store/Actions/editionActions';
import createAuthorSelector from 'Store/Selectors/createAuthorSelector';
import createBookSelector from 'Store/Selectors/createBookSelector';
import selectSettings from 'Store/Selectors/selectSettings';
@@ -26,17 +26,14 @@ function createMapStateToProps() {
const {
isFetching,
isPopulated,
error,
items
error
} = editionState;
book.editions = items;
const bookSettings = _.pick(book, [
'monitored',
'anyEditionOk',
'editions'
'anyEditionOk'
]);
bookSettings.editions = editionState.items;
const settings = selectSettings(bookSettings, pendingChanges, saveError);
@@ -58,10 +55,9 @@ function createMapStateToProps() {
}
const mapDispatchToProps = {
dispatchFetchEditions: fetchEditions,
dispatchClearEditions: clearEditions,
dispatchSetBookValue: setBookValue,
dispatchSaveBook: saveBook
dispatchSaveBook: saveBook,
dispatchSaveEditions: saveEditions
};
class EditBookModalContentConnector extends Component {
@@ -69,20 +65,12 @@ class EditBookModalContentConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.dispatchFetchEditions({ bookId: this.props.bookId });
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
this.props.onModalClose();
}
}
componentWillUnmount() {
this.props.dispatchClearEditions();
}
//
// Listeners
@@ -94,6 +82,9 @@ class EditBookModalContentConnector extends Component {
this.props.dispatchSaveBook({
id: this.props.bookId
});
this.props.dispatchSaveEditions({
id: this.props.bookId
});
}
//
@@ -114,10 +105,9 @@ EditBookModalContentConnector.propTypes = {
bookId: PropTypes.number,
isSaving: PropTypes.bool.isRequired,
saveError: PropTypes.object,
dispatchFetchEditions: PropTypes.func.isRequired,
dispatchClearEditions: PropTypes.func.isRequired,
dispatchSetBookValue: PropTypes.func.isRequired,
dispatchSaveBook: PropTypes.func.isRequired,
dispatchSaveEditions: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};

View File

@@ -5,6 +5,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import * as commandNames from 'Commands/commandNames';
import { executeCommand } from 'Store/Actions/commandActions';
import createBookAuthorSelector from 'Store/Selectors/createBookAuthorSelector';
import createBookQualityProfileSelector from 'Store/Selectors/createBookQualityProfileSelector';
import createBookSelector from 'Store/Selectors/createBookSelector';
import createExecutingCommandsSelector from 'Store/Selectors/createExecutingCommandsSelector';
@@ -32,11 +33,13 @@ function selectShowSearchAction() {
function createMapStateToProps() {
return createSelector(
createBookSelector(),
createBookAuthorSelector(),
createBookQualityProfileSelector(),
selectShowSearchAction(),
createExecutingCommandsSelector(),
(
book,
author,
qualityProfile,
showSearchAction,
executingCommands
@@ -54,7 +57,7 @@ function createMapStateToProps() {
const isRefreshingBook = executingCommands.some((command) => {
return (
(command.name === commandNames.REFRESH_AUTHOR &&
command.body.authorId === book.author.id) ||
command.body.authorId === book.authorId) ||
(command.name === commandNames.REFRESH_BOOK &&
command.body.bookId === book.id)
);
@@ -63,7 +66,7 @@ function createMapStateToProps() {
const isSearchingBook = executingCommands.some((command) => {
return (
(command.name === commandNames.AUTHOR_SEARCH &&
command.body.authorId === book.author.id) ||
command.body.authorId === book.authorId) ||
(command.name === commandNames.BOOK_SEARCH &&
command.body.bookIds.includes(book.id))
);
@@ -71,6 +74,7 @@ function createMapStateToProps() {
return {
...book,
author,
qualityProfile,
showSearchAction,
isRefreshingBook,

View File

@@ -12,6 +12,7 @@ import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
import { icons } from 'Helpers/Props';
import dimensions from 'Styles/Variables/dimensions';
import fonts from 'Styles/Variables/fonts';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import stripHtml from 'Utilities/String/stripHtml';
import translate from 'Utilities/String/translate';
import BookIndexOverviewInfo from './BookIndexOverviewInfo';
@@ -42,10 +43,26 @@ class BookIndexOverview extends Component {
this.state = {
isEditAuthorModalOpen: false,
isDeleteAuthorModalOpen: false
isDeleteAuthorModalOpen: false,
overview: ''
};
}
componentDidMount() {
const { id } = this.props;
// Note that this component is lazy loaded by the virtualised view.
// We want to avoid storing overviews for *all* books which is
// why it's not put into the redux store
const promise = createAjaxRequest({
url: `/book/${id}/overview`
}).request;
promise.done((data) => {
this.setState({ overview: data.overview });
});
}
//
// Listeners
@@ -84,7 +101,6 @@ class BookIndexOverview extends Component {
const {
id,
title,
overview,
monitored,
titleSlug,
nextAiring,
@@ -118,6 +134,7 @@ class BookIndexOverview extends Component {
} = statistics;
const {
overview,
isEditAuthorModalOpen,
isDeleteAuthorModalOpen
} = this.state;
@@ -267,7 +284,6 @@ class BookIndexOverview extends Component {
BookIndexOverview.propTypes = {
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
overview: PropTypes.string.isRequired,
monitored: PropTypes.bool.isRequired,
titleSlug: PropTypes.string.isRequired,
nextAiring: PropTypes.string,

View File

@@ -1,5 +1,8 @@
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import { createThunk, handleThunks } from 'Store/thunks';
import getProviderState from 'Utilities/State/getProviderState';
import { updateItem } from './baseActions';
import createFetchHandler from './Creators/createFetchHandler';
import createHandleActions from './Creators/createHandleActions';
import createClearReducer from './Creators/Reducers/createClearReducer';
@@ -25,18 +28,39 @@ export const defaultState = {
export const FETCH_EDITIONS = 'editions/fetchEditions';
export const CLEAR_EDITIONS = 'editions/clearEditions';
export const SAVE_EDITIONS = 'editions/saveEditions';
//
// Action Creators
export const fetchEditions = createThunk(FETCH_EDITIONS);
export const clearEditions = createAction(CLEAR_EDITIONS);
export const saveEditions = createThunk(SAVE_EDITIONS);
//
// Action Handlers
export const actionHandlers = handleThunks({
[FETCH_EDITIONS]: createFetchHandler(section, '/edition')
[FETCH_EDITIONS]: createFetchHandler(section, '/edition'),
[SAVE_EDITIONS]: function(getState, payload, dispatch) {
const {
id,
...otherPayload
} = payload;
const saveData = getProviderState({ id, ...otherPayload }, getState, 'books');
dispatch(batchActions([
...saveData.editions.map((edition) => {
return updateItem({
id: edition.id,
section: 'editions',
...edition
});
})
]));
}
});
//

View File

@@ -0,0 +1,15 @@
import { createSelector } from 'reselect';
import createBookSelector from './createBookSelector';
function createBookAuthorSelector() {
return createSelector(
createBookSelector(),
(state) => state.authors.itemMap,
(state) => state.authors.items,
(book, authorMap, allAuthors) => {
return allAuthors[authorMap[book.authorId]];
}
);
}
export default createBookAuthorSelector;

View File

@@ -1,18 +1,16 @@
import { createSelector } from 'reselect';
import createBookSelector from './createBookSelector';
import createBookAuthorSelector from './createBookAuthorSelector';
function createBookQualityProfileSelector() {
return createSelector(
(state) => state.settings.qualityProfiles.items,
createBookSelector(),
(qualityProfiles, book) => {
if (!book) {
createBookAuthorSelector(),
(qualityProfiles, author) => {
if (!author) {
return {};
}
return qualityProfiles.find((profile) => {
return profile.id === book.author.qualityProfileId;
});
return qualityProfiles.find((profile) => profile.id === author.qualityProfileId);
}
);
}