New: Display stats for all series in index footer

This commit is contained in:
Bogdan 2024-06-15 19:18:16 +03:00
parent d2509798e9
commit 8601f35f6e
1 changed files with 121 additions and 41 deletions

View File

@ -1,18 +1,40 @@
import classNames from 'classnames'; import classNames from 'classnames';
import React from 'react'; import React, { useMemo } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { ColorImpairedConsumer } from 'App/ColorImpairedContext'; import { ColorImpairedConsumer } from 'App/ColorImpairedContext';
import SeriesAppState from 'App/State/SeriesAppState'; import SeriesAppState from 'App/State/SeriesAppState';
import DescriptionList from 'Components/DescriptionList/DescriptionList'; import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem'; import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import { Statistics } from 'Series/Series';
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector'; import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import createDeepEqualSelector from 'Store/Selectors/createDeepEqualSelector'; import createDeepEqualSelector from 'Store/Selectors/createDeepEqualSelector';
import formatBytes from 'Utilities/Number/formatBytes'; import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from './SeriesIndexFooter.css'; import styles from './SeriesIndexFooter.css';
function createUnoptimizedSelector() { interface SeriesStatistics {
monitored: boolean;
status: string;
statistics: Statistics;
}
function createAllSeriesStatisticsSelector() {
return createSelector(createAllSeriesSelector(), (series) => {
return series.map((s) => {
const { monitored, status, statistics } = s;
return {
monitored,
status,
statistics,
};
});
});
}
function createUnoptimizedSeriesSelector() {
return createSelector( return createSelector(
createClientSideCollectionSelector('series', 'seriesIndex'), createClientSideCollectionSelector('series', 'seriesIndex'),
(series: SeriesAppState) => { (series: SeriesAppState) => {
@ -31,47 +53,75 @@ function createUnoptimizedSelector() {
function createSeriesSelector() { function createSeriesSelector() {
return createDeepEqualSelector( return createDeepEqualSelector(
createUnoptimizedSelector(), createUnoptimizedSeriesSelector(),
(series) => series (series) => series
); );
} }
function calculateSeriesStatistics(series: SeriesStatistics[]) {
return series.reduce(
(acc, s) => {
const { statistics = {} as Statistics } = s;
const {
episodeCount = 0,
episodeFileCount = 0,
sizeOnDisk = 0,
} = statistics;
acc.episodes += episodeCount;
acc.episodeFiles += episodeFileCount;
acc.totalFileSize += sizeOnDisk;
if (s.status === 'ended') {
acc.ended++;
} else {
acc.continuing++;
}
if (s.monitored) {
acc.monitored++;
}
return acc;
},
{
episodes: 0,
episodeFiles: 0,
totalFileSize: 0,
ended: 0,
continuing: 0,
monitored: 0,
}
);
}
export default function SeriesIndexFooter() { export default function SeriesIndexFooter() {
const allSeries = useSelector(createAllSeriesStatisticsSelector());
const series = useSelector(createSeriesSelector()); const series = useSelector(createSeriesSelector());
const allCount = allSeries.length;
const count = series.length; const count = series.length;
let episodes = 0;
let episodeFiles = 0;
let ended = 0;
let continuing = 0;
let monitored = 0;
let totalFileSize = 0;
series.forEach((s) => { const {
const { episodes: allEpisodes,
statistics = { episodeCount: 0, episodeFileCount: 0, sizeOnDisk: 0 }, episodeFiles: allEpisodeFiles,
} = s; ended: allEnded,
continuing: allContinuing,
monitored: allMonitored,
totalFileSize: allTotalFileSize,
} = useMemo(() => calculateSeriesStatistics(allSeries), [allSeries]);
const { const {
episodeCount = 0, episodes,
episodeFileCount = 0, episodeFiles,
sizeOnDisk = 0, ended,
} = statistics; continuing,
monitored,
totalFileSize,
} = useMemo(() => calculateSeriesStatistics(series), [series]);
episodes += episodeCount; const unmonitored = count - monitored;
episodeFiles += episodeFileCount; const allUnmonitored = allCount - allMonitored;
if (s.status === 'ended') {
ended++;
} else {
continuing++;
}
if (s.monitored) {
monitored++;
}
totalFileSize += sizeOnDisk;
});
return ( return (
<ColorImpairedConsumer> <ColorImpairedConsumer>
@ -132,44 +182,74 @@ export default function SeriesIndexFooter() {
<div className={styles.statistics}> <div className={styles.statistics}>
<DescriptionList> <DescriptionList>
<DescriptionListItem title={translate('Series')} data={count} /> <DescriptionListItem
title={translate('Series')}
data={count === allCount ? count : `${count}/${allCount}`}
/>
<DescriptionListItem title={translate('Ended')} data={ended} /> <DescriptionListItem
title={translate('Ended')}
data={count === allCount ? ended : `${ended}/${allEnded}`}
/>
<DescriptionListItem <DescriptionListItem
title={translate('Continuing')} title={translate('Continuing')}
data={continuing} data={
count === allCount
? continuing
: `${continuing}/${allContinuing}`
}
/> />
</DescriptionList> </DescriptionList>
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
title={translate('Monitored')} title={translate('Monitored')}
data={monitored} data={
count === allCount
? monitored
: `${monitored}/${allMonitored}`
}
/> />
<DescriptionListItem <DescriptionListItem
title={translate('Unmonitored')} title={translate('Unmonitored')}
data={count - monitored} data={
count === allCount
? unmonitored
: `${unmonitored}/${allUnmonitored}`
}
/> />
</DescriptionList> </DescriptionList>
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
title={translate('Episodes')} title={translate('Episodes')}
data={episodes} data={
count === allCount ? episodes : `${episodes}/${allEpisodes}`
}
/> />
<DescriptionListItem <DescriptionListItem
title={translate('Files')} title={translate('Files')}
data={episodeFiles} data={
count === allCount
? episodeFiles
: `${episodeFiles}/${allEpisodeFiles}`
}
/> />
</DescriptionList> </DescriptionList>
<DescriptionList> <DescriptionList>
<DescriptionListItem <DescriptionListItem
title={translate('TotalFileSize')} title={translate('TotalFileSize')}
data={formatBytes(totalFileSize)} data={
count === allCount
? formatBytes(totalFileSize)
: `${formatBytes(totalFileSize)}/${formatBytes(
allTotalFileSize
)}`
}
/> />
</DescriptionList> </DescriptionList>
</div> </div>