New: Display stats for all series in index footer
This commit is contained in:
parent
d2509798e9
commit
8601f35f6e
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SeriesIndexFooter() {
|
function calculateSeriesStatistics(series: SeriesStatistics[]) {
|
||||||
const series = useSelector(createSeriesSelector());
|
return series.reduce(
|
||||||
const count = series.length;
|
(acc, s) => {
|
||||||
let episodes = 0;
|
const { statistics = {} as Statistics } = s;
|
||||||
let episodeFiles = 0;
|
|
||||||
let ended = 0;
|
|
||||||
let continuing = 0;
|
|
||||||
let monitored = 0;
|
|
||||||
let totalFileSize = 0;
|
|
||||||
|
|
||||||
series.forEach((s) => {
|
|
||||||
const {
|
|
||||||
statistics = { episodeCount: 0, episodeFileCount: 0, sizeOnDisk: 0 },
|
|
||||||
} = s;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
episodeCount = 0,
|
episodeCount = 0,
|
||||||
episodeFileCount = 0,
|
episodeFileCount = 0,
|
||||||
sizeOnDisk = 0,
|
sizeOnDisk = 0,
|
||||||
} = statistics;
|
} = statistics;
|
||||||
|
|
||||||
episodes += episodeCount;
|
acc.episodes += episodeCount;
|
||||||
episodeFiles += episodeFileCount;
|
acc.episodeFiles += episodeFileCount;
|
||||||
|
acc.totalFileSize += sizeOnDisk;
|
||||||
|
|
||||||
if (s.status === 'ended') {
|
if (s.status === 'ended') {
|
||||||
ended++;
|
acc.ended++;
|
||||||
} else {
|
} else {
|
||||||
continuing++;
|
acc.continuing++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.monitored) {
|
if (s.monitored) {
|
||||||
monitored++;
|
acc.monitored++;
|
||||||
}
|
}
|
||||||
|
|
||||||
totalFileSize += sizeOnDisk;
|
return acc;
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
episodes: 0,
|
||||||
|
episodeFiles: 0,
|
||||||
|
totalFileSize: 0,
|
||||||
|
ended: 0,
|
||||||
|
continuing: 0,
|
||||||
|
monitored: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SeriesIndexFooter() {
|
||||||
|
const allSeries = useSelector(createAllSeriesStatisticsSelector());
|
||||||
|
const series = useSelector(createSeriesSelector());
|
||||||
|
|
||||||
|
const allCount = allSeries.length;
|
||||||
|
const count = series.length;
|
||||||
|
|
||||||
|
const {
|
||||||
|
episodes: allEpisodes,
|
||||||
|
episodeFiles: allEpisodeFiles,
|
||||||
|
ended: allEnded,
|
||||||
|
continuing: allContinuing,
|
||||||
|
monitored: allMonitored,
|
||||||
|
totalFileSize: allTotalFileSize,
|
||||||
|
} = useMemo(() => calculateSeriesStatistics(allSeries), [allSeries]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
episodes,
|
||||||
|
episodeFiles,
|
||||||
|
ended,
|
||||||
|
continuing,
|
||||||
|
monitored,
|
||||||
|
totalFileSize,
|
||||||
|
} = useMemo(() => calculateSeriesStatistics(series), [series]);
|
||||||
|
|
||||||
|
const unmonitored = count - monitored;
|
||||||
|
const allUnmonitored = allCount - allMonitored;
|
||||||
|
|
||||||
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>
|
||||||
|
|
Loading…
Reference in New Issue