mirror of
https://github.com/lidarr/Lidarr
synced 2024-12-21 23:32:27 +00:00
Fixed: Sorting by title and release dates in Select Album modal
Fixes #5145 Closes #5125 Co-authored-by: Mark McDowall <mark@mcdowall.ca>
This commit is contained in:
parent
3a5012655e
commit
bd7d25f963
5 changed files with 143 additions and 70 deletions
|
@ -11,6 +11,7 @@ import Scroller from 'Components/Scroller/Scroller';
|
|||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import { scrollDirections } from 'Helpers/Props';
|
||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import SelectAlbumRow from './SelectAlbumRow';
|
||||
import styles from './SelectAlbumModalContent.css';
|
||||
|
@ -19,6 +20,7 @@ const columns = [
|
|||
{
|
||||
name: 'title',
|
||||
label: () => translate('AlbumTitle'),
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
|
@ -29,6 +31,7 @@ const columns = [
|
|||
{
|
||||
name: 'releaseDate',
|
||||
label: () => translate('ReleaseDate'),
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
|
@ -63,16 +66,22 @@ class SelectAlbumModalContent extends Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
onAlbumSelect,
|
||||
onModalClose,
|
||||
isFetching,
|
||||
...otherProps
|
||||
isPopulated,
|
||||
error,
|
||||
items,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
onSortPress,
|
||||
onAlbumSelect,
|
||||
onModalClose
|
||||
} = this.props;
|
||||
|
||||
const filter = this.state.filter;
|
||||
const filterLower = filter.toLowerCase();
|
||||
|
||||
const errorMessage = getErrorMessage(error, 'Unable to load albums');
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
|
@ -83,27 +92,29 @@ class SelectAlbumModalContent extends Component {
|
|||
className={styles.modalBody}
|
||||
scrollDirection={scrollDirections.NONE}
|
||||
>
|
||||
{
|
||||
isFetching &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
<TextInput
|
||||
className={styles.filterInput}
|
||||
placeholder={translate('FilterAlbumPlaceholder')}
|
||||
name="filter"
|
||||
value={filter}
|
||||
autoFocus={true}
|
||||
onChange={this.onFilterChange}
|
||||
/>
|
||||
|
||||
<Scroller
|
||||
className={styles.scroller}
|
||||
autoFocus={false}
|
||||
>
|
||||
{
|
||||
{isFetching ? <LoadingIndicator /> : null}
|
||||
|
||||
{error ? <div>{errorMessage}</div> : null}
|
||||
|
||||
<TextInput
|
||||
className={styles.filterInput}
|
||||
placeholder={translate('FilterAlbumPlaceholder')}
|
||||
name="filter"
|
||||
value={filter}
|
||||
autoFocus={true}
|
||||
onChange={this.onFilterChange}
|
||||
/>
|
||||
|
||||
{isPopulated && !!items.length ? (
|
||||
<Table
|
||||
columns={columns}
|
||||
{...otherProps}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
onSortPress={onSortPress}
|
||||
>
|
||||
<TableBody>
|
||||
{
|
||||
|
@ -122,7 +133,7 @@ class SelectAlbumModalContent extends Component {
|
|||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
}
|
||||
) : null}
|
||||
</Scroller>
|
||||
</ModalBody>
|
||||
|
||||
|
@ -137,8 +148,13 @@ class SelectAlbumModalContent extends Component {
|
|||
}
|
||||
|
||||
SelectAlbumModalContent.propTypes = {
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
sortKey: PropTypes.string,
|
||||
sortDirection: PropTypes.string,
|
||||
onSortPress: PropTypes.func.isRequired,
|
||||
onAlbumSelect: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
|
|
@ -3,18 +3,14 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import {
|
||||
clearInteractiveImportAlbums,
|
||||
fetchInteractiveImportAlbums,
|
||||
saveInteractiveImportItem,
|
||||
setInteractiveImportAlbumsSort,
|
||||
updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||
import { clearAlbums, fetchAlbums, setAlbumsSort } from 'Store/Actions/albumSelectionActions';
|
||||
import { saveInteractiveImportItem, updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import SelectAlbumModalContent from './SelectAlbumModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createClientSideCollectionSelector('interactiveImport.albums'),
|
||||
createClientSideCollectionSelector('albumSelection'),
|
||||
(albums) => {
|
||||
return albums;
|
||||
}
|
||||
|
@ -22,9 +18,9 @@ function createMapStateToProps() {
|
|||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchInteractiveImportAlbums,
|
||||
setInteractiveImportAlbumsSort,
|
||||
clearInteractiveImportAlbums,
|
||||
fetchAlbums,
|
||||
setAlbumsSort,
|
||||
clearAlbums,
|
||||
updateInteractiveImportItem,
|
||||
saveInteractiveImportItem
|
||||
};
|
||||
|
@ -39,20 +35,20 @@ class SelectAlbumModalContentConnector extends Component {
|
|||
artistId
|
||||
} = this.props;
|
||||
|
||||
this.props.fetchInteractiveImportAlbums({ artistId });
|
||||
this.props.fetchAlbums({ artistId });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// This clears the albums for the queue and hides the queue
|
||||
// We'll need another place to store albums for manual import
|
||||
this.props.clearInteractiveImportAlbums();
|
||||
this.props.clearAlbums();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onSortPress = (sortKey, sortDirection) => {
|
||||
this.props.setInteractiveImportAlbumsSort({ sortKey, sortDirection });
|
||||
this.props.setAlbumsSort({ sortKey, sortDirection });
|
||||
};
|
||||
|
||||
onAlbumSelect = (albumId) => {
|
||||
|
@ -82,6 +78,7 @@ class SelectAlbumModalContentConnector extends Component {
|
|||
return (
|
||||
<SelectAlbumModalContent
|
||||
{...this.props}
|
||||
onSortPress={this.onSortPress}
|
||||
onAlbumSelect={this.onAlbumSelect}
|
||||
/>
|
||||
);
|
||||
|
@ -92,9 +89,9 @@ SelectAlbumModalContentConnector.propTypes = {
|
|||
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
artistId: PropTypes.number.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
fetchInteractiveImportAlbums: PropTypes.func.isRequired,
|
||||
setInteractiveImportAlbumsSort: PropTypes.func.isRequired,
|
||||
clearInteractiveImportAlbums: PropTypes.func.isRequired,
|
||||
fetchAlbums: PropTypes.func.isRequired,
|
||||
setAlbumsSort: PropTypes.func.isRequired,
|
||||
clearAlbums: PropTypes.func.isRequired,
|
||||
saveInteractiveImportItem: PropTypes.func.isRequired,
|
||||
updateInteractiveImportItem: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
|
|
86
frontend/src/Store/Actions/albumSelectionActions.js
Normal file
86
frontend/src/Store/Actions/albumSelectionActions.js
Normal file
|
@ -0,0 +1,86 @@
|
|||
import moment from 'moment';
|
||||
import { createAction } from 'redux-actions';
|
||||
import { sortDirections } from 'Helpers/Props';
|
||||
import { createThunk, handleThunks } from 'Store/thunks';
|
||||
import updateSectionState from 'Utilities/State/updateSectionState';
|
||||
import createFetchHandler from './Creators/createFetchHandler';
|
||||
import createHandleActions from './Creators/createHandleActions';
|
||||
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
|
||||
|
||||
//
|
||||
// Variables
|
||||
|
||||
export const section = 'albumSelection';
|
||||
|
||||
//
|
||||
// State
|
||||
|
||||
export const defaultState = {
|
||||
isFetching: false,
|
||||
isReprocessing: false,
|
||||
isPopulated: false,
|
||||
error: null,
|
||||
sortKey: 'title',
|
||||
sortDirection: sortDirections.ASCENDING,
|
||||
items: [],
|
||||
sortPredicates: {
|
||||
title: ({ title }) => {
|
||||
return title.toLocaleLowerCase();
|
||||
},
|
||||
|
||||
releaseDate: function({ releaseDate }, direction) {
|
||||
if (releaseDate) {
|
||||
return moment(releaseDate).unix();
|
||||
}
|
||||
|
||||
if (direction === sortDirections.DESCENDING) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Number.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const persistState = [
|
||||
'albumSelection.sortKey',
|
||||
'albumSelection.sortDirection'
|
||||
];
|
||||
|
||||
//
|
||||
// Actions Types
|
||||
|
||||
export const FETCH_ALBUMS = 'albumSelection/fetchAlbums';
|
||||
export const SET_ALBUMS_SORT = 'albumSelection/setAlbumsSort';
|
||||
export const CLEAR_ALBUMS = 'albumSelection/clearAlbums';
|
||||
|
||||
//
|
||||
// Action Creators
|
||||
|
||||
export const fetchAlbums = createThunk(FETCH_ALBUMS);
|
||||
export const setAlbumsSort = createAction(SET_ALBUMS_SORT);
|
||||
export const clearAlbums = createAction(CLEAR_ALBUMS);
|
||||
|
||||
//
|
||||
// Action Handlers
|
||||
|
||||
export const actionHandlers = handleThunks({
|
||||
[FETCH_ALBUMS]: createFetchHandler(section, '/album')
|
||||
});
|
||||
|
||||
//
|
||||
// Reducers
|
||||
|
||||
export const reducers = createHandleActions({
|
||||
|
||||
[SET_ALBUMS_SORT]: createSetClientSideCollectionSortReducer(section),
|
||||
|
||||
[CLEAR_ALBUMS]: (state) => {
|
||||
return updateSectionState(state, section, {
|
||||
...defaultState,
|
||||
sortKey: state.sortKey,
|
||||
sortDirection: state.sortDirection
|
||||
});
|
||||
}
|
||||
|
||||
}, defaultState, section);
|
|
@ -1,5 +1,6 @@
|
|||
import * as albums from './albumActions';
|
||||
import * as albumHistory from './albumHistoryActions';
|
||||
import * as albumSelection from './albumSelectionActions';
|
||||
import * as app from './appActions';
|
||||
import * as artist from './artistActions';
|
||||
import * as artistHistory from './artistHistoryActions';
|
||||
|
@ -29,14 +30,18 @@ import * as wanted from './wantedActions';
|
|||
|
||||
export default [
|
||||
app,
|
||||
albums,
|
||||
albumHistory,
|
||||
albumSelection,
|
||||
artist,
|
||||
artistHistory,
|
||||
artistIndex,
|
||||
blocklist,
|
||||
captcha,
|
||||
calendar,
|
||||
commands,
|
||||
customFilters,
|
||||
albums,
|
||||
trackFiles,
|
||||
albumHistory,
|
||||
history,
|
||||
interactiveImportActions,
|
||||
oAuth,
|
||||
|
@ -47,9 +52,6 @@ export default [
|
|||
providerOptions,
|
||||
queue,
|
||||
releases,
|
||||
artist,
|
||||
artistHistory,
|
||||
artistIndex,
|
||||
search,
|
||||
settings,
|
||||
system,
|
||||
|
|
|
@ -16,7 +16,6 @@ import createSetClientSideCollectionSortReducer from './Creators/Reducers/create
|
|||
|
||||
export const section = 'interactiveImport';
|
||||
|
||||
const albumsSection = `${section}.albums`;
|
||||
const trackFilesSection = `${section}.trackFiles`;
|
||||
let abortCurrentFetchRequest = null;
|
||||
let abortCurrentRequest = null;
|
||||
|
@ -58,15 +57,6 @@ export const defaultState = {
|
|||
}
|
||||
},
|
||||
|
||||
albums: {
|
||||
isFetching: false,
|
||||
isPopulated: false,
|
||||
error: null,
|
||||
sortKey: 'albumTitle',
|
||||
sortDirection: sortDirections.ASCENDING,
|
||||
items: []
|
||||
},
|
||||
|
||||
trackFiles: {
|
||||
isFetching: false,
|
||||
isPopulated: false,
|
||||
|
@ -97,10 +87,6 @@ export const ADD_RECENT_FOLDER = 'interactiveImport/addRecentFolder';
|
|||
export const REMOVE_RECENT_FOLDER = 'interactiveImport/removeRecentFolder';
|
||||
export const SET_INTERACTIVE_IMPORT_MODE = 'interactiveImport/setInteractiveImportMode';
|
||||
|
||||
export const FETCH_INTERACTIVE_IMPORT_ALBUMS = 'interactiveImport/fetchInteractiveImportAlbums';
|
||||
export const SET_INTERACTIVE_IMPORT_ALBUMS_SORT = 'interactiveImport/clearInteractiveImportAlbumsSort';
|
||||
export const CLEAR_INTERACTIVE_IMPORT_ALBUMS = 'interactiveImport/clearInteractiveImportAlbums';
|
||||
|
||||
export const FETCH_INTERACTIVE_IMPORT_TRACKFILES = 'interactiveImport/fetchInteractiveImportTrackFiles';
|
||||
export const CLEAR_INTERACTIVE_IMPORT_TRACKFILES = 'interactiveImport/clearInteractiveImportTrackFiles';
|
||||
|
||||
|
@ -117,10 +103,6 @@ export const addRecentFolder = createAction(ADD_RECENT_FOLDER);
|
|||
export const removeRecentFolder = createAction(REMOVE_RECENT_FOLDER);
|
||||
export const setInteractiveImportMode = createAction(SET_INTERACTIVE_IMPORT_MODE);
|
||||
|
||||
export const fetchInteractiveImportAlbums = createThunk(FETCH_INTERACTIVE_IMPORT_ALBUMS);
|
||||
export const setInteractiveImportAlbumsSort = createAction(SET_INTERACTIVE_IMPORT_ALBUMS_SORT);
|
||||
export const clearInteractiveImportAlbums = createAction(CLEAR_INTERACTIVE_IMPORT_ALBUMS);
|
||||
|
||||
export const fetchInteractiveImportTrackFiles = createThunk(FETCH_INTERACTIVE_IMPORT_TRACKFILES);
|
||||
export const clearInteractiveImportTrackFiles = createAction(CLEAR_INTERACTIVE_IMPORT_TRACKFILES);
|
||||
|
||||
|
@ -253,8 +235,6 @@ export const actionHandlers = handleThunks({
|
|||
});
|
||||
},
|
||||
|
||||
[FETCH_INTERACTIVE_IMPORT_ALBUMS]: createFetchHandler(albumsSection, '/album'),
|
||||
|
||||
[FETCH_INTERACTIVE_IMPORT_TRACKFILES]: createFetchHandler(trackFilesSection, '/trackFile')
|
||||
});
|
||||
|
||||
|
@ -336,14 +316,6 @@ export const reducers = createHandleActions({
|
|||
return Object.assign({}, state, { importMode: payload.importMode });
|
||||
},
|
||||
|
||||
[SET_INTERACTIVE_IMPORT_ALBUMS_SORT]: createSetClientSideCollectionSortReducer(albumsSection),
|
||||
|
||||
[CLEAR_INTERACTIVE_IMPORT_ALBUMS]: (state) => {
|
||||
return updateSectionState(state, albumsSection, {
|
||||
...defaultState.albums
|
||||
});
|
||||
},
|
||||
|
||||
[CLEAR_INTERACTIVE_IMPORT_TRACKFILES]: (state) => {
|
||||
return updateSectionState(state, trackFilesSection, {
|
||||
...defaultState.trackFiles
|
||||
|
|
Loading…
Reference in a new issue