New: Sort episodes on series details page

Closes #  4558
This commit is contained in:
Mark McDowall 2023-10-06 12:38:10 -07:00
parent db15a03c1e
commit 113b0864b8
4 changed files with 46 additions and 20 deletions

View File

@ -69,8 +69,6 @@ interface SelectEpisodeModalContentProps {
seasonNumber?: number; seasonNumber?: number;
selectedDetails?: string; selectedDetails?: string;
isAnime: boolean; isAnime: boolean;
sortKey?: string;
sortDirection?: string;
modalTitle: string; modalTitle: string;
onEpisodesSelect(selectedEpisodes: SelectedEpisode[]): unknown; onEpisodesSelect(selectedEpisodes: SelectedEpisode[]): unknown;
onModalClose(): unknown; onModalClose(): unknown;
@ -86,8 +84,6 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
seasonNumber, seasonNumber,
selectedDetails, selectedDetails,
isAnime, isAnime,
sortKey,
sortDirection,
modalTitle, modalTitle,
onEpisodesSelect, onEpisodesSelect,
onModalClose, onModalClose,
@ -97,9 +93,8 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
const [selectState, setSelectState] = useSelectState(); const [selectState, setSelectState] = useSelectState();
const { allSelected, allUnselected, selectedState } = selectState; const { allSelected, allUnselected, selectedState } = selectState;
const { isFetching, isPopulated, items, error } = useSelector( const { isFetching, isPopulated, items, error, sortKey, sortDirection } =
episodesSelector() useSelector(episodesSelector());
);
const dispatch = useDispatch(); const dispatch = useDispatch();
const filterEpisodeNumber = parseInt(filter); const filterEpisodeNumber = parseInt(filter);

View File

@ -210,12 +210,15 @@ class SeriesDetailsSeason extends Component {
seasonNumber, seasonNumber,
items, items,
columns, columns,
sortKey,
sortDirection,
statistics, statistics,
isSaving, isSaving,
isExpanded, isExpanded,
isSearching, isSearching,
seriesMonitored, seriesMonitored,
isSmallScreen, isSmallScreen,
onSortPress,
onTableOptionChange, onTableOptionChange,
onMonitorSeasonPress, onMonitorSeasonPress,
onSearchPress onSearchPress
@ -447,6 +450,9 @@ class SeriesDetailsSeason extends Component {
items.length ? items.length ?
<Table <Table
columns={columns} columns={columns}
sortKey={sortKey}
sortDirection={sortDirection}
onSortPress={onSortPress}
onTableOptionChange={onTableOptionChange} onTableOptionChange={onTableOptionChange}
> >
<TableBody> <TableBody>
@ -530,6 +536,8 @@ SeriesDetailsSeason.propTypes = {
seasonNumber: PropTypes.number.isRequired, seasonNumber: PropTypes.number.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired, columns: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string.isRequired,
sortDirection: PropTypes.oneOf(sortDirections.all),
statistics: PropTypes.object.isRequired, statistics: PropTypes.object.isRequired,
isSaving: PropTypes.bool, isSaving: PropTypes.bool,
isExpanded: PropTypes.bool, isExpanded: PropTypes.bool,
@ -537,6 +545,7 @@ SeriesDetailsSeason.propTypes = {
seriesMonitored: PropTypes.bool.isRequired, seriesMonitored: PropTypes.bool.isRequired,
isSmallScreen: PropTypes.bool.isRequired, isSmallScreen: PropTypes.bool.isRequired,
onTableOptionChange: PropTypes.func.isRequired, onTableOptionChange: PropTypes.func.isRequired,
onSortPress: PropTypes.func.isRequired,
onMonitorSeasonPress: PropTypes.func.isRequired, onMonitorSeasonPress: PropTypes.func.isRequired,
onExpandPress: PropTypes.func.isRequired, onExpandPress: PropTypes.func.isRequired,
onMonitorEpisodePress: PropTypes.func.isRequired, onMonitorEpisodePress: PropTypes.func.isRequired,

View File

@ -4,8 +4,9 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import * as commandNames from 'Commands/commandNames'; import * as commandNames from 'Commands/commandNames';
import { executeCommand } from 'Store/Actions/commandActions'; import { executeCommand } from 'Store/Actions/commandActions';
import { setEpisodesTableOption, toggleEpisodesMonitored } from 'Store/Actions/episodeActions'; import { setEpisodesSort, setEpisodesTableOption, toggleEpisodesMonitored } from 'Store/Actions/episodeActions';
import { toggleSeasonMonitored } from 'Store/Actions/seriesActions'; import { toggleSeasonMonitored } from 'Store/Actions/seriesActions';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createSeriesSelector from 'Store/Selectors/createSeriesSelector'; import createSeriesSelector from 'Store/Selectors/createSeriesSelector';
@ -15,7 +16,7 @@ import SeriesDetailsSeason from './SeriesDetailsSeason';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state, { seasonNumber }) => seasonNumber, (state, { seasonNumber }) => seasonNumber,
(state) => state.episodes, createClientSideCollectionSelector('episodes'),
createSeriesSelector(), createSeriesSelector(),
createCommandsSelector(), createCommandsSelector(),
createDimensionsSelector(), createDimensionsSelector(),
@ -27,11 +28,12 @@ function createMapStateToProps() {
})); }));
const episodesInSeason = episodes.items.filter((episode) => episode.seasonNumber === seasonNumber); const episodesInSeason = episodes.items.filter((episode) => episode.seasonNumber === seasonNumber);
const sortedEpisodes = episodesInSeason.sort((a, b) => b.episodeNumber - a.episodeNumber);
return { return {
items: sortedEpisodes, items: episodesInSeason,
columns: episodes.columns, columns: episodes.columns,
sortKey: episodes.sortKey,
sortDirection: episodes.sortDirection,
isSearching, isSearching,
seriesMonitored: series.monitored, seriesMonitored: series.monitored,
path: series.path, path: series.path,
@ -45,6 +47,7 @@ const mapDispatchToProps = {
toggleSeasonMonitored, toggleSeasonMonitored,
toggleEpisodesMonitored, toggleEpisodesMonitored,
setEpisodesTableOption, setEpisodesTableOption,
setEpisodesSort,
executeCommand executeCommand
}; };
@ -90,6 +93,13 @@ class SeriesDetailsSeasonConnector extends Component {
}); });
}; };
onSortPress = (sortKey, sortDirection) => {
this.props.setEpisodesSort({
sortKey,
sortDirection
});
};
// //
// Render // Render
@ -98,6 +108,7 @@ class SeriesDetailsSeasonConnector extends Component {
<SeriesDetailsSeason <SeriesDetailsSeason
{...this.props} {...this.props}
onTableOptionChange={this.onTableOptionChange} onTableOptionChange={this.onTableOptionChange}
onSortPress={this.onSortPress}
onMonitorSeasonPress={this.onMonitorSeasonPress} onMonitorSeasonPress={this.onMonitorSeasonPress}
onSearchPress={this.onSearchPress} onSearchPress={this.onSearchPress}
onMonitorEpisodePress={this.onMonitorEpisodePress} onMonitorEpisodePress={this.onMonitorEpisodePress}
@ -112,6 +123,7 @@ SeriesDetailsSeasonConnector.propTypes = {
toggleSeasonMonitored: PropTypes.func.isRequired, toggleSeasonMonitored: PropTypes.func.isRequired,
toggleEpisodesMonitored: PropTypes.func.isRequired, toggleEpisodesMonitored: PropTypes.func.isRequired,
setEpisodesTableOption: PropTypes.func.isRequired, setEpisodesTableOption: PropTypes.func.isRequired,
setEpisodesSort: PropTypes.func.isRequired,
executeCommand: PropTypes.func.isRequired executeCommand: PropTypes.func.isRequired
}; };

View File

@ -40,32 +40,38 @@ export const defaultState = {
{ {
name: 'episodeNumber', name: 'episodeNumber',
label: '#', label: '#',
isVisible: true isVisible: true,
isSortable: true
}, },
{ {
name: 'title', name: 'title',
label: () => translate('Title'), label: () => translate('Title'),
isVisible: true isVisible: true,
isSortable: true
}, },
{ {
name: 'path', name: 'path',
label: () => translate('Path'), label: () => translate('Path'),
isVisible: false isVisible: false,
isSortable: true
}, },
{ {
name: 'relativePath', name: 'relativePath',
label: () => translate('RelativePath'), label: () => translate('RelativePath'),
isVisible: false isVisible: false,
isSortable: true
}, },
{ {
name: 'airDateUtc', name: 'airDateUtc',
label: () => translate('AirDate'), label: () => translate('AirDate'),
isVisible: true isVisible: true,
isSortable: true
}, },
{ {
name: 'runtime', name: 'runtime',
label: () => translate('Runtime'), label: () => translate('Runtime'),
isVisible: false isVisible: false,
isSortable: true
}, },
{ {
name: 'languages', name: 'languages',
@ -100,7 +106,8 @@ export const defaultState = {
{ {
name: 'size', name: 'size',
label: () => translate('Size'), label: () => translate('Size'),
isVisible: false isVisible: false,
isSortable: true
}, },
{ {
name: 'releaseGroup', name: 'releaseGroup',
@ -119,7 +126,8 @@ export const defaultState = {
name: icons.SCORE, name: icons.SCORE,
title: () => translate('CustomFormatScore') title: () => translate('CustomFormatScore')
}), }),
isVisible: false isVisible: false,
isSortable: true
}, },
{ {
name: 'status', name: 'status',
@ -136,7 +144,9 @@ export const defaultState = {
}; };
export const persistState = [ export const persistState = [
'episodes.columns' 'episodes.columns',
'episodes.sortDirection',
'episodes.sortKey'
]; ];
// //