mirror of https://github.com/Radarr/Radarr
Sort movie files on movie details page
(cherry picked from commit 113b0864b8e92b7b768acc8341bdf4c9e2e1a47f)
This commit is contained in:
parent
4a9c0b2240
commit
b08981dee0
|
@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Table from 'Components/Table/Table';
|
import Table from 'Components/Table/Table';
|
||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
|
import { sortDirections } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import MovieFileEditorRow from './MovieFileEditorRow';
|
import MovieFileEditorRow from './MovieFileEditorRow';
|
||||||
import styles from './MovieFileEditorTableContent.css';
|
import styles from './MovieFileEditorTableContent.css';
|
||||||
|
@ -15,6 +16,9 @@ class MovieFileEditorTableContent extends Component {
|
||||||
const {
|
const {
|
||||||
items,
|
items,
|
||||||
columns,
|
columns,
|
||||||
|
sortKey,
|
||||||
|
sortDirection,
|
||||||
|
onSortPress,
|
||||||
onTableOptionChange
|
onTableOptionChange
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -31,6 +35,9 @@ class MovieFileEditorTableContent extends Component {
|
||||||
!!items.length &&
|
!!items.length &&
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
sortKey={sortKey}
|
||||||
|
sortDirection={sortDirection}
|
||||||
|
onSortPress={onSortPress}
|
||||||
onTableOptionChange={onTableOptionChange}
|
onTableOptionChange={onTableOptionChange}
|
||||||
>
|
>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
|
@ -60,7 +67,10 @@ MovieFileEditorTableContent.propTypes = {
|
||||||
isDeleting: PropTypes.bool.isRequired,
|
isDeleting: PropTypes.bool.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),
|
||||||
onTableOptionChange: PropTypes.func.isRequired,
|
onTableOptionChange: PropTypes.func.isRequired,
|
||||||
|
onSortPress: PropTypes.func.isRequired,
|
||||||
onDeletePress: PropTypes.func.isRequired
|
onDeletePress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,9 @@ import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { deleteMovieFile, setMovieFilesTableOption, updateMovieFiles } from 'Store/Actions/movieFileActions';
|
import { deleteMovieFile, setMovieFilesSort, setMovieFilesTableOption } from 'Store/Actions/movieFileActions';
|
||||||
import { fetchLanguages, fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
|
import { fetchLanguages, fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
|
||||||
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
import createMovieSelector from 'Store/Selectors/createMovieSelector';
|
import createMovieSelector from 'Store/Selectors/createMovieSelector';
|
||||||
import getQualities from 'Utilities/Quality/getQualities';
|
import getQualities from 'Utilities/Quality/getQualities';
|
||||||
import MovieFileEditorTableContent from './MovieFileEditorTableContent';
|
import MovieFileEditorTableContent from './MovieFileEditorTableContent';
|
||||||
|
@ -11,7 +12,7 @@ import MovieFileEditorTableContent from './MovieFileEditorTableContent';
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { movieId }) => movieId,
|
(state, { movieId }) => movieId,
|
||||||
(state) => state.movieFiles,
|
createClientSideCollectionSelector('movieFiles'),
|
||||||
(state) => state.settings.languages,
|
(state) => state.settings.languages,
|
||||||
(state) => state.settings.qualityProfiles,
|
(state) => state.settings.qualityProfiles,
|
||||||
createMovieSelector(),
|
createMovieSelector(),
|
||||||
|
@ -28,6 +29,8 @@ function createMapStateToProps() {
|
||||||
return {
|
return {
|
||||||
items: filesForMovie,
|
items: filesForMovie,
|
||||||
columns: movieFiles.columns,
|
columns: movieFiles.columns,
|
||||||
|
sortKey: movieFiles.sortKey,
|
||||||
|
sortDirection: movieFiles.sortDirection,
|
||||||
isDeleting: movieFiles.isDeleting,
|
isDeleting: movieFiles.isDeleting,
|
||||||
isSaving: movieFiles.isSaving,
|
isSaving: movieFiles.isSaving,
|
||||||
error: null,
|
error: null,
|
||||||
|
@ -38,31 +41,13 @@ function createMapStateToProps() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMapDispatchToProps(dispatch, props) {
|
const mapDispatchToProps = {
|
||||||
return {
|
fetchQualityProfileSchema,
|
||||||
dispatchFetchQualityProfileSchema() {
|
fetchLanguages,
|
||||||
dispatch(fetchQualityProfileSchema());
|
deleteMovieFile,
|
||||||
},
|
setMovieFilesTableOption,
|
||||||
|
setMovieFilesSort
|
||||||
dispatchFetchLanguages() {
|
};
|
||||||
dispatch(fetchLanguages());
|
|
||||||
},
|
|
||||||
|
|
||||||
dispatchUpdateMovieFiles(updateProps) {
|
|
||||||
dispatch(updateMovieFiles(updateProps));
|
|
||||||
},
|
|
||||||
|
|
||||||
onTableOptionChange(payload) {
|
|
||||||
dispatch(setMovieFilesTableOption(payload));
|
|
||||||
},
|
|
||||||
|
|
||||||
onDeletePress(movieFileId) {
|
|
||||||
dispatch(deleteMovieFile({
|
|
||||||
id: movieFileId
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class MovieFileEditorTableContentConnector extends Component {
|
class MovieFileEditorTableContentConnector extends Component {
|
||||||
|
|
||||||
|
@ -70,24 +55,40 @@ class MovieFileEditorTableContentConnector extends Component {
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.dispatchFetchLanguages();
|
this.props.fetchLanguages();
|
||||||
this.props.dispatchFetchQualityProfileSchema();
|
this.props.fetchQualityProfileSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onDeletePress = (movieFileId) => {
|
||||||
|
this.props.deleteMovieFile({
|
||||||
|
id: movieFileId
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onTableOptionChange = (payload) => {
|
||||||
|
this.props.setMovieFilesTableOption(payload);
|
||||||
|
};
|
||||||
|
|
||||||
|
onSortPress = (sortKey, sortDirection) => {
|
||||||
|
this.props.setMovieFilesSort({
|
||||||
|
sortKey,
|
||||||
|
sortDirection
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
|
||||||
dispatchFetchLanguages,
|
|
||||||
dispatchFetchQualityProfileSchema,
|
|
||||||
dispatchUpdateMovieFiles,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MovieFileEditorTableContent
|
<MovieFileEditorTableContent
|
||||||
{...otherProps}
|
{...this.props}
|
||||||
|
onDeletePress={this.onDeletePress}
|
||||||
|
onTableOptionChange={this.onTableOptionChange}
|
||||||
|
onSortPress={this.onSortPress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -97,9 +98,11 @@ MovieFileEditorTableContentConnector.propTypes = {
|
||||||
movieId: PropTypes.number.isRequired,
|
movieId: PropTypes.number.isRequired,
|
||||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
qualities: PropTypes.arrayOf(PropTypes.object).isRequired,
|
qualities: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
dispatchFetchLanguages: PropTypes.func.isRequired,
|
fetchLanguages: PropTypes.func.isRequired,
|
||||||
dispatchFetchQualityProfileSchema: PropTypes.func.isRequired,
|
fetchQualityProfileSchema: PropTypes.func.isRequired,
|
||||||
dispatchUpdateMovieFiles: PropTypes.func.isRequired
|
deleteMovieFile: PropTypes.func.isRequired,
|
||||||
|
setMovieFilesTableOption: PropTypes.func.isRequired,
|
||||||
|
setMovieFilesSort: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, createMapDispatchToProps)(MovieFileEditorTableContentConnector);
|
export default connect(createMapStateToProps, mapDispatchToProps)(MovieFileEditorTableContentConnector);
|
||||||
|
|
|
@ -4,8 +4,9 @@ import { createAction } from 'redux-actions';
|
||||||
import { batchActions } from 'redux-batched-actions';
|
import { batchActions } from 'redux-batched-actions';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons, sortDirections } from 'Helpers/Props';
|
||||||
import movieEntities from 'Movie/movieEntities';
|
import movieEntities from 'Movie/movieEntities';
|
||||||
|
import createSetClientSideCollectionSortReducer from 'Store/Actions/Creators/Reducers/createSetClientSideCollectionSortReducer';
|
||||||
import createSetTableOptionReducer from 'Store/Actions/Creators/Reducers/createSetTableOptionReducer';
|
import createSetTableOptionReducer from 'Store/Actions/Creators/Reducers/createSetTableOptionReducer';
|
||||||
import { createThunk, handleThunks } from 'Store/thunks';
|
import { createThunk, handleThunks } from 'Store/thunks';
|
||||||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
||||||
|
@ -31,13 +32,16 @@ export const defaultState = {
|
||||||
deleteError: null,
|
deleteError: null,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
saveError: null,
|
saveError: null,
|
||||||
|
sortKey: 'relativePath',
|
||||||
|
sortDirection: sortDirections.ASCENDING,
|
||||||
items: [],
|
items: [],
|
||||||
|
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
name: 'relativePath',
|
name: 'relativePath',
|
||||||
label: () => translate('RelativePath'),
|
label: () => translate('RelativePath'),
|
||||||
isVisible: true
|
isVisible: true,
|
||||||
|
isSortable: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'videoCodec',
|
name: 'videoCodec',
|
||||||
|
@ -67,7 +71,8 @@ export const defaultState = {
|
||||||
{
|
{
|
||||||
name: 'size',
|
name: 'size',
|
||||||
label: () => translate('Size'),
|
label: () => translate('Size'),
|
||||||
isVisible: true
|
isVisible: true,
|
||||||
|
isSortable: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'languages',
|
name: 'languages',
|
||||||
|
@ -96,12 +101,14 @@ export const defaultState = {
|
||||||
name: icons.SCORE,
|
name: icons.SCORE,
|
||||||
title: () => translate('CustomFormatScore')
|
title: () => translate('CustomFormatScore')
|
||||||
}),
|
}),
|
||||||
isVisible: true
|
isVisible: true,
|
||||||
|
isSortable: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'dateAdded',
|
name: 'dateAdded',
|
||||||
label: () => translate('Added'),
|
label: () => translate('Added'),
|
||||||
isVisible: false
|
isVisible: false,
|
||||||
|
isSortable: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'actions',
|
name: 'actions',
|
||||||
|
@ -114,7 +121,9 @@ export const defaultState = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const persistState = [
|
export const persistState = [
|
||||||
'movieFiles.columns'
|
'movieFiles.columns',
|
||||||
|
'movieFiles.sortDirection',
|
||||||
|
'movieFiles.sortKey'
|
||||||
];
|
];
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -125,6 +134,7 @@ export const DELETE_MOVIE_FILE = 'movieFiles/deleteMovieFile';
|
||||||
export const DELETE_MOVIE_FILES = 'movieFiles/deleteMovieFiles';
|
export const DELETE_MOVIE_FILES = 'movieFiles/deleteMovieFiles';
|
||||||
export const UPDATE_MOVIE_FILES = 'movieFiles/updateMovieFiles';
|
export const UPDATE_MOVIE_FILES = 'movieFiles/updateMovieFiles';
|
||||||
export const CLEAR_MOVIE_FILES = 'movieFiles/clearMovieFiles';
|
export const CLEAR_MOVIE_FILES = 'movieFiles/clearMovieFiles';
|
||||||
|
export const SET_MOVIE_FILES_SORT = 'movieFiles/setMovieFilesSort';
|
||||||
export const SET_MOVIE_FILES_TABLE_OPTION = 'movieFiles/setMovieFilesTableOption';
|
export const SET_MOVIE_FILES_TABLE_OPTION = 'movieFiles/setMovieFilesTableOption';
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -135,6 +145,7 @@ export const deleteMovieFile = createThunk(DELETE_MOVIE_FILE);
|
||||||
export const deleteMovieFiles = createThunk(DELETE_MOVIE_FILES);
|
export const deleteMovieFiles = createThunk(DELETE_MOVIE_FILES);
|
||||||
export const updateMovieFiles = createThunk(UPDATE_MOVIE_FILES);
|
export const updateMovieFiles = createThunk(UPDATE_MOVIE_FILES);
|
||||||
export const clearMovieFiles = createAction(CLEAR_MOVIE_FILES);
|
export const clearMovieFiles = createAction(CLEAR_MOVIE_FILES);
|
||||||
|
export const setMovieFilesSort = createAction(SET_MOVIE_FILES_SORT);
|
||||||
export const setMovieFilesTableOption = createAction(SET_MOVIE_FILES_TABLE_OPTION);
|
export const setMovieFilesTableOption = createAction(SET_MOVIE_FILES_TABLE_OPTION);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -327,6 +338,7 @@ export const actionHandlers = handleThunks({
|
||||||
// Reducers
|
// Reducers
|
||||||
|
|
||||||
export const reducers = createHandleActions({
|
export const reducers = createHandleActions({
|
||||||
|
|
||||||
[SET_MOVIE_FILES_TABLE_OPTION]: createSetTableOptionReducer(section),
|
[SET_MOVIE_FILES_TABLE_OPTION]: createSetTableOptionReducer(section),
|
||||||
|
|
||||||
[CLEAR_MOVIE_FILES]: (state) => {
|
[CLEAR_MOVIE_FILES]: (state) => {
|
||||||
|
@ -340,6 +352,8 @@ export const reducers = createHandleActions({
|
||||||
saveError: null,
|
saveError: null,
|
||||||
items: []
|
items: []
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
|
[SET_MOVIE_FILES_SORT]: createSetClientSideCollectionSortReducer(section)
|
||||||
|
|
||||||
}, defaultState, section);
|
}, defaultState, section);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using NzbDrone.Core.Extras.Files;
|
using NzbDrone.Core.Extras.Files;
|
||||||
using NzbDrone.Core.Extras.Metadata.Files;
|
using NzbDrone.Core.Extras.Metadata.Files;
|
||||||
|
@ -27,9 +28,9 @@ namespace Radarr.Api.V3.ExtraFiles
|
||||||
{
|
{
|
||||||
var extraFiles = new List<ExtraFileResource>();
|
var extraFiles = new List<ExtraFileResource>();
|
||||||
|
|
||||||
var subtitleFiles = _subtitleFileService.GetFilesByMovie(movieId);
|
var subtitleFiles = _subtitleFileService.GetFilesByMovie(movieId).OrderBy(f => f.RelativePath).ToList();
|
||||||
var metadataFiles = _metadataFileService.GetFilesByMovie(movieId);
|
var metadataFiles = _metadataFileService.GetFilesByMovie(movieId).OrderBy(f => f.RelativePath).ToList();
|
||||||
var otherExtraFiles = _otherFileService.GetFilesByMovie(movieId);
|
var otherExtraFiles = _otherFileService.GetFilesByMovie(movieId).OrderBy(f => f.RelativePath).ToList();
|
||||||
|
|
||||||
extraFiles.AddRange(subtitleFiles.ToResource());
|
extraFiles.AddRange(subtitleFiles.ToResource());
|
||||||
extraFiles.AddRange(metadataFiles.ToResource());
|
extraFiles.AddRange(metadataFiles.ToResource());
|
||||||
|
|
Loading…
Reference in New Issue