mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-12 16:13:58 -07:00
Fix Interactive Import, Add Track Actions and Reducers
This commit is contained in:
parent
8f45fe0afe
commit
90d9741056
13 changed files with 193 additions and 27 deletions
|
@ -114,6 +114,11 @@ class SignalRConnector extends Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name === 'track') {
|
||||||
|
this.handleTrack(body);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (name === 'episodefile') {
|
if (name === 'episodefile') {
|
||||||
this.handleEpisodeFile(body);
|
this.handleEpisodeFile(body);
|
||||||
return;
|
return;
|
||||||
|
@ -192,6 +197,15 @@ class SignalRConnector extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleTrack = (body) => {
|
||||||
|
if (body.action === 'updated') {
|
||||||
|
this.props.updateItem({
|
||||||
|
section: 'tracks',
|
||||||
|
updateOnly: true,
|
||||||
|
...body.resource });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleEpisodeFile = (body) => {
|
handleEpisodeFile = (body) => {
|
||||||
if (body.action === 'updated') {
|
if (body.action === 'updated') {
|
||||||
this.props.updateItem({
|
this.props.updateItem({
|
||||||
|
|
|
@ -5,6 +5,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import SelectAlbumRow from './SelectAlbumRow';
|
import SelectAlbumRow from './SelectAlbumRow';
|
||||||
|
|
||||||
class SelectAlbumModalContent extends Component {
|
class SelectAlbumModalContent extends Component {
|
||||||
|
@ -16,7 +17,8 @@ class SelectAlbumModalContent extends Component {
|
||||||
const {
|
const {
|
||||||
items,
|
items,
|
||||||
onAlbumSelect,
|
onAlbumSelect,
|
||||||
onModalClose
|
onModalClose,
|
||||||
|
isFetching
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -26,6 +28,10 @@ class SelectAlbumModalContent extends Component {
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
{
|
||||||
|
isFetching &&
|
||||||
|
<LoadingIndicator />
|
||||||
|
}
|
||||||
{
|
{
|
||||||
items.map((item) => {
|
items.map((item) => {
|
||||||
return (
|
return (
|
||||||
|
@ -52,6 +58,7 @@ class SelectAlbumModalContent extends Component {
|
||||||
|
|
||||||
SelectAlbumModalContent.propTypes = {
|
SelectAlbumModalContent.propTypes = {
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
isFetching: PropTypes.bool.isRequired,
|
||||||
onAlbumSelect: PropTypes.func.isRequired,
|
onAlbumSelect: PropTypes.func.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,28 +2,48 @@ import _ from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
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 connectSection from 'Store/connectSection';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
import { fetchEpisodes, setEpisodesSort, clearEpisodes } from 'Store/Actions/episodeActions';
|
||||||
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
import SelectAlbumModalContent from './SelectAlbumModalContent';
|
import SelectAlbumModalContent from './SelectAlbumModalContent';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createArtistSelector(),
|
createClientSideCollectionSelector(),
|
||||||
(series) => {
|
(episodes) => {
|
||||||
return {
|
return episodes;
|
||||||
items: series.albums
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
|
fetchEpisodes,
|
||||||
|
setEpisodesSort,
|
||||||
|
clearEpisodes,
|
||||||
updateInteractiveImportItem
|
updateInteractiveImportItem
|
||||||
};
|
};
|
||||||
|
|
||||||
class SelectAlbumModalContentConnector extends Component {
|
class SelectAlbumModalContentConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const {
|
||||||
|
artistId
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
this.props.fetchEpisodes({ 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.clearEpisodes();
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
|
@ -58,8 +78,17 @@ SelectAlbumModalContentConnector.propTypes = {
|
||||||
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
artistId: PropTypes.number.isRequired,
|
artistId: PropTypes.number.isRequired,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
fetchEpisodes: PropTypes.func.isRequired,
|
||||||
|
setEpisodesSort: PropTypes.func.isRequired,
|
||||||
|
clearEpisodes: PropTypes.func.isRequired,
|
||||||
updateInteractiveImportItem: PropTypes.func.isRequired,
|
updateInteractiveImportItem: PropTypes.func.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(SelectAlbumModalContentConnector);
|
export default connectSection(
|
||||||
|
createMapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{ section: 'episodes' }
|
||||||
|
)(SelectAlbumModalContentConnector);
|
||||||
|
|
|
@ -293,7 +293,7 @@ class InteractiveImportModalContent extends Component {
|
||||||
<SelectAlbumModal
|
<SelectAlbumModal
|
||||||
isOpen={isSelectAlbumModalOpen}
|
isOpen={isSelectAlbumModalOpen}
|
||||||
ids={selectedIds}
|
ids={selectedIds}
|
||||||
artistId={selectedItem && selectedItem.series && selectedItem.series.id}
|
artistId={selectedItem && selectedItem.artist && selectedItem.artist.id}
|
||||||
onModalClose={this.onSelectAlbumModalClose}
|
onModalClose={this.onSelectAlbumModalClose}
|
||||||
/>
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
|
@ -177,7 +177,7 @@ class InteractiveImportRow extends Component {
|
||||||
|
|
||||||
const artistName = artist ? artist.artistName : '';
|
const artistName = artist ? artist.artistName : '';
|
||||||
const albumTitle = album ? album.title : '';
|
const albumTitle = album ? album.title : '';
|
||||||
const trackNumbers = tracks.map((episode) => episode.trackNumber)
|
const trackNumbers = tracks.map((track) => track.trackNumber)
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
const showArtistPlaceholder = isSelected && !artist;
|
const showArtistPlaceholder = isSelected && !artist;
|
||||||
|
|
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import connectSection from 'Store/connectSection';
|
import connectSection from 'Store/connectSection';
|
||||||
import { fetchEpisodes, setEpisodesSort, clearEpisodes } from 'Store/Actions/episodeActions';
|
import { fetchTracks, setTracksSort, clearTracks } from 'Store/Actions/trackActions';
|
||||||
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
import SelectTrackModalContent from './SelectTrackModalContent';
|
import SelectTrackModalContent from './SelectTrackModalContent';
|
||||||
|
@ -11,16 +11,16 @@ import SelectTrackModalContent from './SelectTrackModalContent';
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createClientSideCollectionSelector(),
|
createClientSideCollectionSelector(),
|
||||||
(episodes) => {
|
(tracks) => {
|
||||||
return episodes;
|
return tracks;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchEpisodes,
|
fetchTracks,
|
||||||
setEpisodesSort,
|
setTracksSort,
|
||||||
clearEpisodes,
|
clearTracks,
|
||||||
updateInteractiveImportItem
|
updateInteractiveImportItem
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,25 +35,25 @@ class SelectTrackModalContentConnector extends Component {
|
||||||
albumId
|
albumId
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.props.fetchEpisodes({ artistId, albumId });
|
this.props.fetchTracks({ artistId, albumId });
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
// This clears the episodes for the queue and hides the queue
|
// This clears the tracks for the queue and hides the queue
|
||||||
// We'll need another place to store episodes for manual import
|
// We'll need another place to store tracks for manual import
|
||||||
this.props.clearEpisodes();
|
this.props.clearTracks();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onSortPress = (sortKey, sortDirection) => {
|
onSortPress = (sortKey, sortDirection) => {
|
||||||
this.props.setEpisodesSort({ sortKey, sortDirection });
|
this.props.setTracksSort({ sortKey, sortDirection });
|
||||||
}
|
}
|
||||||
|
|
||||||
onTracksSelect = (episodeIds) => {
|
onTracksSelect = (trackIds) => {
|
||||||
const tracks = _.reduce(this.props.items, (acc, item) => {
|
const tracks = _.reduce(this.props.items, (acc, item) => {
|
||||||
if (episodeIds.indexOf(item.id) > -1) {
|
if (trackIds.indexOf(item.id) > -1) {
|
||||||
acc.push(item);
|
acc.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,9 +87,9 @@ SelectTrackModalContentConnector.propTypes = {
|
||||||
artistId: PropTypes.number.isRequired,
|
artistId: PropTypes.number.isRequired,
|
||||||
albumId: PropTypes.number.isRequired,
|
albumId: PropTypes.number.isRequired,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
fetchEpisodes: PropTypes.func.isRequired,
|
fetchTracks: PropTypes.func.isRequired,
|
||||||
setEpisodesSort: PropTypes.func.isRequired,
|
setTracksSort: PropTypes.func.isRequired,
|
||||||
clearEpisodes: PropTypes.func.isRequired,
|
clearTracks: PropTypes.func.isRequired,
|
||||||
updateInteractiveImportItem: PropTypes.func.isRequired,
|
updateInteractiveImportItem: PropTypes.func.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
@ -99,5 +99,5 @@ export default connectSection(
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
{ section: 'episodes' }
|
{ section: 'tracks' }
|
||||||
)(SelectTrackModalContentConnector);
|
)(SelectTrackModalContentConnector);
|
||||||
|
|
|
@ -84,6 +84,14 @@ export const CLEAR_EPISODES = 'CLEAR_EPISODES';
|
||||||
export const TOGGLE_EPISODE_MONITORED = 'TOGGLE_EPISODE_MONITORED';
|
export const TOGGLE_EPISODE_MONITORED = 'TOGGLE_EPISODE_MONITORED';
|
||||||
export const TOGGLE_EPISODES_MONITORED = 'TOGGLE_EPISODES_MONITORED';
|
export const TOGGLE_EPISODES_MONITORED = 'TOGGLE_EPISODES_MONITORED';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Tracks
|
||||||
|
|
||||||
|
export const FETCH_TRACKS = 'FETCH_TRACKS';
|
||||||
|
export const SET_TRACKS_SORT = 'SET_TRACKS_SORT';
|
||||||
|
export const SET_TRACKS_TABLE_OPTION = 'SET_TRACKS_TABLE_OPTION';
|
||||||
|
export const CLEAR_TRACKS = 'CLEAR_TRACKS';
|
||||||
|
|
||||||
//
|
//
|
||||||
// Episode Files
|
// Episode Files
|
||||||
|
|
||||||
|
|
11
frontend/src/Store/Actions/trackActionHandlers.js
Normal file
11
frontend/src/Store/Actions/trackActionHandlers.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import createFetchHandler from './Creators/createFetchHandler';
|
||||||
|
import * as types from './actionTypes';
|
||||||
|
|
||||||
|
const section = 'tracks';
|
||||||
|
|
||||||
|
const trackActionHandlers = {
|
||||||
|
[types.FETCH_TRACKS]: createFetchHandler(section, '/track')
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default trackActionHandlers;
|
8
frontend/src/Store/Actions/trackActions.js
Normal file
8
frontend/src/Store/Actions/trackActions.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { createAction } from 'redux-actions';
|
||||||
|
import * as types from './actionTypes';
|
||||||
|
import trackActionHandlers from './trackActionHandlers';
|
||||||
|
|
||||||
|
export const fetchTracks = trackActionHandlers[types.FETCH_TRACKS];
|
||||||
|
export const setTracksSort = createAction(types.SET_TRACKS_SORT);
|
||||||
|
export const setTracksTableOption = createAction(types.SET_TRACKS_TABLE_OPTION);
|
||||||
|
export const clearTracks = createAction(types.CLEAR_TRACKS);
|
|
@ -2,6 +2,7 @@ import _ from 'lodash';
|
||||||
import persistState from 'redux-localstorage';
|
import persistState from 'redux-localstorage';
|
||||||
import * as addArtistReducers from 'Store/Reducers/addArtistReducers';
|
import * as addArtistReducers from 'Store/Reducers/addArtistReducers';
|
||||||
import * as episodeReducers from 'Store/Reducers/episodeReducers';
|
import * as episodeReducers from 'Store/Reducers/episodeReducers';
|
||||||
|
import * as trackReducers from 'Store/Reducers/trackReducers';
|
||||||
import * as artistIndexReducers from 'Store/Reducers/artistIndexReducers';
|
import * as artistIndexReducers from 'Store/Reducers/artistIndexReducers';
|
||||||
import * as artistEditorReducers from 'Store/Reducers/artistEditorReducers';
|
import * as artistEditorReducers from 'Store/Reducers/artistEditorReducers';
|
||||||
import * as albumStudioReducers from 'Store/Reducers/albumStudioReducers';
|
import * as albumStudioReducers from 'Store/Reducers/albumStudioReducers';
|
||||||
|
@ -17,6 +18,7 @@ import * as queueReducers from 'Store/Reducers/queueReducers';
|
||||||
const reducers = [
|
const reducers = [
|
||||||
addArtistReducers,
|
addArtistReducers,
|
||||||
episodeReducers,
|
episodeReducers,
|
||||||
|
trackReducers,
|
||||||
artistIndexReducers,
|
artistIndexReducers,
|
||||||
artistEditorReducers,
|
artistEditorReducers,
|
||||||
albumStudioReducers,
|
albumStudioReducers,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import history, { defaultState as defaultHistoryState } from './historyReducers'
|
||||||
import queue, { defaultState as defaultQueueState } from './queueReducers';
|
import queue, { defaultState as defaultQueueState } from './queueReducers';
|
||||||
import blacklist, { defaultState as defaultBlacklistState } from './blacklistReducers';
|
import blacklist, { defaultState as defaultBlacklistState } from './blacklistReducers';
|
||||||
import episodes, { defaultState as defaultEpisodesState } from './episodeReducers';
|
import episodes, { defaultState as defaultEpisodesState } from './episodeReducers';
|
||||||
|
import tracks, { defaultState as defaultTracksState } from './trackReducers';
|
||||||
import episodeFiles, { defaultState as defaultEpisodeFilesState } from './episodeFileReducers';
|
import episodeFiles, { defaultState as defaultEpisodeFilesState } from './episodeFileReducers';
|
||||||
import albumHistory, { defaultState as defaultAlbumHistoryState } from './albumHistoryReducers';
|
import albumHistory, { defaultState as defaultAlbumHistoryState } from './albumHistoryReducers';
|
||||||
import releases, { defaultState as defaultReleasesState } from './releaseReducers';
|
import releases, { defaultState as defaultReleasesState } from './releaseReducers';
|
||||||
|
@ -41,6 +42,7 @@ export const defaultState = {
|
||||||
queue: defaultQueueState,
|
queue: defaultQueueState,
|
||||||
blacklist: defaultBlacklistState,
|
blacklist: defaultBlacklistState,
|
||||||
episodes: defaultEpisodesState,
|
episodes: defaultEpisodesState,
|
||||||
|
tracks: defaultTracksState,
|
||||||
episodeFiles: defaultEpisodeFilesState,
|
episodeFiles: defaultEpisodeFilesState,
|
||||||
albumHistory: defaultAlbumHistoryState,
|
albumHistory: defaultAlbumHistoryState,
|
||||||
releases: defaultReleasesState,
|
releases: defaultReleasesState,
|
||||||
|
@ -70,6 +72,7 @@ export default enableBatching(combineReducers({
|
||||||
queue,
|
queue,
|
||||||
blacklist,
|
blacklist,
|
||||||
episodes,
|
episodes,
|
||||||
|
tracks,
|
||||||
episodeFiles,
|
episodeFiles,
|
||||||
albumHistory,
|
albumHistory,
|
||||||
releases,
|
releases,
|
||||||
|
|
70
frontend/src/Store/Reducers/trackReducers.js
Normal file
70
frontend/src/Store/Reducers/trackReducers.js
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { handleActions } from 'redux-actions';
|
||||||
|
import * as types from 'Store/Actions/actionTypes';
|
||||||
|
import { sortDirections } from 'Helpers/Props';
|
||||||
|
import createSetReducer from './Creators/createSetReducer';
|
||||||
|
import createSetTableOptionReducer from './Creators/createSetTableOptionReducer';
|
||||||
|
import createUpdateReducer from './Creators/createUpdateReducer';
|
||||||
|
import createUpdateItemReducer from './Creators/createUpdateItemReducer';
|
||||||
|
import createSetClientSideCollectionSortReducer from './Creators/createSetClientSideCollectionSortReducer';
|
||||||
|
|
||||||
|
export const defaultState = {
|
||||||
|
isFetching: false,
|
||||||
|
isPopulated: false,
|
||||||
|
error: null,
|
||||||
|
sortKey: 'trackNumber',
|
||||||
|
sortDirection: sortDirections.DESCENDING,
|
||||||
|
items: [],
|
||||||
|
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'trackNumber',
|
||||||
|
label: 'Track Number',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
label: 'Title',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'duration',
|
||||||
|
label: 'Duration',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
columnLabel: 'Actions',
|
||||||
|
isVisible: true,
|
||||||
|
isModifiable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const persistState = [
|
||||||
|
'tracks.columns'
|
||||||
|
];
|
||||||
|
|
||||||
|
const reducerSection = 'tracks';
|
||||||
|
|
||||||
|
const trackReducers = handleActions({
|
||||||
|
|
||||||
|
[types.SET]: createSetReducer(reducerSection),
|
||||||
|
[types.UPDATE]: createUpdateReducer(reducerSection),
|
||||||
|
[types.UPDATE_ITEM]: createUpdateItemReducer(reducerSection),
|
||||||
|
|
||||||
|
[types.SET_TRACKS_TABLE_OPTION]: createSetTableOptionReducer(reducerSection),
|
||||||
|
|
||||||
|
[types.CLEAR_TRACKS]: (state) => {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
isFetching: false,
|
||||||
|
isPopulated: false,
|
||||||
|
error: null,
|
||||||
|
items: []
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
[types.SET_TRACKS_SORT]: createSetClientSideCollectionSortReducer(reducerSection)
|
||||||
|
|
||||||
|
}, defaultState);
|
||||||
|
|
||||||
|
export default trackReducers;
|
14
frontend/src/Store/Selectors/createTrackSelector.js
Normal file
14
frontend/src/Store/Selectors/createTrackSelector.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
function createTrackSelector() {
|
||||||
|
return createSelector(
|
||||||
|
(state, { trackId }) => trackId,
|
||||||
|
(state) => state.tracks,
|
||||||
|
(trackId, tracks) => {
|
||||||
|
return _.find(tracks.items, { id: trackId });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createTrackSelector;
|
Loading…
Add table
Add a link
Reference in a new issue