Moving tracks into items (need to consider performance knock-ons)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -106,7 +106,7 @@
|
||||
|
||||
// Release details
|
||||
// These are automatically injected to built HTML
|
||||
var build = "1599289916";
|
||||
var build = "1599362472";
|
||||
var version = "3.52.4";
|
||||
|
||||
// Construct the script tag
|
||||
|
||||
@ -333,22 +333,30 @@ class PlaybackControls extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
snapcast_enabled: state.pusher.config.snapcast_enabled,
|
||||
current_track: (state.core.current_track && state.core.tracks[state.core.current_track.uri] !== undefined ? state.core.tracks[state.core.current_track.uri] : null),
|
||||
next_track: (state.core.next_track_uri && state.core.tracks[state.core.next_track_uri] !== undefined ? state.core.tracks[state.core.next_track_uri] : null),
|
||||
radio_enabled: (!!(state.ui.radio && state.ui.radio.enabled)),
|
||||
play_state: state.mopidy.play_state,
|
||||
time_position: state.mopidy.time_position,
|
||||
consume: state.mopidy.consume,
|
||||
repeat: state.mopidy.repeat,
|
||||
random: state.mopidy.random,
|
||||
volume: state.mopidy.volume,
|
||||
mute: state.mopidy.mute,
|
||||
sidebar_open: state.ui.sidebar_open,
|
||||
slim_mode: state.ui.slim_mode,
|
||||
touch_enabled: state.ui.playback_controls_touch_enabled,
|
||||
});
|
||||
const mapStateToProps = (state) => {
|
||||
const {
|
||||
items,
|
||||
next_track_uri,
|
||||
current_track,
|
||||
} = state.core;
|
||||
|
||||
return {
|
||||
snapcast_enabled: state.pusher.config.snapcast_enabled,
|
||||
current_track: current_track ? items[current_track.uri] || current_track : null,
|
||||
next_track: items[next_track_uri],
|
||||
radio_enabled: (!!(state.ui.radio && state.ui.radio.enabled)),
|
||||
play_state: state.mopidy.play_state,
|
||||
time_position: state.mopidy.time_position,
|
||||
consume: state.mopidy.consume,
|
||||
repeat: state.mopidy.repeat,
|
||||
random: state.mopidy.random,
|
||||
volume: state.mopidy.volume,
|
||||
mute: state.mopidy.mute,
|
||||
sidebar_open: state.ui.sidebar_open,
|
||||
slim_mode: state.ui.slim_mode,
|
||||
touch_enabled: state.ui.playback_controls_touch_enabled,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
coreActions: bindActionCreators(coreActions, dispatch),
|
||||
|
||||
@ -107,71 +107,74 @@ export function updateColdStore(items) {
|
||||
* relevant service to load the record - all from one neat package.
|
||||
* */
|
||||
|
||||
export function loadItems(uris, force_reload = false) {
|
||||
export function loadItems(uris, forceReload = false) {
|
||||
return {
|
||||
type: 'LOAD_ITEMS',
|
||||
uris,
|
||||
force_reload,
|
||||
forceReload,
|
||||
};
|
||||
}
|
||||
|
||||
export function loadItem(uri, force_reload = false) {
|
||||
return loadItems([uri], force_reload);
|
||||
export function loadItem(uri, forceReload = false) {
|
||||
return loadItems([uri], forceReload);
|
||||
}
|
||||
|
||||
export function loadTrack(uri, force_reload = false) {
|
||||
export function loadTrack(uri, forceReload = false) {
|
||||
return {
|
||||
type: 'LOAD_TRACK',
|
||||
uri,
|
||||
force_reload,
|
||||
forceReload,
|
||||
};
|
||||
}
|
||||
|
||||
export function loadAlbum(uri, force_reload = false) {
|
||||
export function loadAlbum(uri, forceReload = false, callbackAction) {
|
||||
return {
|
||||
type: 'LOAD_ALBUM',
|
||||
uri,
|
||||
force_reload,
|
||||
forceReload,
|
||||
callbackAction,
|
||||
};
|
||||
}
|
||||
|
||||
export function loadArtist(uri, force_reload = false) {
|
||||
export function loadArtist(uri, forceReload = false, callbackAction) {
|
||||
return {
|
||||
type: 'LOAD_ARTIST',
|
||||
uri,
|
||||
force_reload,
|
||||
forceReload,
|
||||
callbackAction,
|
||||
};
|
||||
}
|
||||
|
||||
export function loadPlaylist(uri, force_reload = false) {
|
||||
export function loadPlaylist(uri, forceReload = false, callbackAction) {
|
||||
return {
|
||||
type: 'LOAD_PLAYLIST',
|
||||
uri,
|
||||
force_reload,
|
||||
forceReload,
|
||||
callbackAction,
|
||||
};
|
||||
}
|
||||
|
||||
export function loadUser(uri, force_reload = false) {
|
||||
export function loadUser(uri, forceReload = false) {
|
||||
return {
|
||||
type: 'LOAD_USER',
|
||||
uri,
|
||||
force_reload,
|
||||
forceReload,
|
||||
};
|
||||
}
|
||||
|
||||
export function loadUserPlaylists(uri, force_reload = false) {
|
||||
export function loadUserPlaylists(uri, forceReload = false) {
|
||||
return {
|
||||
type: 'LOAD_USER_PLAYLISTS',
|
||||
uri,
|
||||
force_reload,
|
||||
forceReload,
|
||||
};
|
||||
}
|
||||
|
||||
export function loadLibrary(uri, force_reload = false) {
|
||||
export function loadLibrary(uri, forceReload = false) {
|
||||
return {
|
||||
type: 'LOAD_LIBRARY',
|
||||
uri,
|
||||
force_reload,
|
||||
forceReload,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -318,35 +318,50 @@ const CoreMiddleware = (function () {
|
||||
store.dispatch({
|
||||
type: `LOAD_${uriType(uri).toUpperCase()}`,
|
||||
uri,
|
||||
force_reload: action.force_reload,
|
||||
forceReload: action.forceReload,
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case 'LOAD_TRACK':
|
||||
if (
|
||||
!action.force_reload
|
||||
&& store.getState().core.tracks[action.uri]) {
|
||||
console.info(`Loading "${action.uri}" from index`);
|
||||
case 'LOAD_TRACK': {
|
||||
const fetchTrack = () => {
|
||||
switch (uriSource(action.uri)) {
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getTrack(action.uri));
|
||||
|
||||
if (spotify.me) {
|
||||
store.dispatch(spotifyActions.following(action.uri));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
store.dispatch(mopidyActions.getTrack(action.uri));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (action.forceReload) {
|
||||
console.info(`Force-reloading "${action.uri}"`);
|
||||
fetchTrack();
|
||||
break;
|
||||
}
|
||||
if (store.getState().core.items[action.uri]) {
|
||||
console.info(`Using "${action.uri}" from index`);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (uriSource(action.uri)) {
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getTrack(action.uri));
|
||||
|
||||
if (spotify.me) {
|
||||
store.dispatch(spotifyActions.following(action.uri));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
store.dispatch(mopidyActions.getTrack(action.uri));
|
||||
break;
|
||||
}
|
||||
localForage.getItem(action.uri).then((result) => {
|
||||
if (result) {
|
||||
console.info(`Loading "${action.uri}" from database`);
|
||||
store.dispatch(coreActions.restoreItemsFromColdStore([result]));
|
||||
} else {
|
||||
fetchTrack();
|
||||
}
|
||||
});
|
||||
|
||||
next(action);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'LOAD_ALBUM':
|
||||
const fetchAlbum = () => {
|
||||
@ -365,7 +380,7 @@ const CoreMiddleware = (function () {
|
||||
};
|
||||
};
|
||||
|
||||
if (action.force_reload) {
|
||||
if (action.forceReload) {
|
||||
console.info(`Force-reloading "${action.uri}"`);
|
||||
fetchAlbum();
|
||||
break;
|
||||
@ -412,7 +427,7 @@ const CoreMiddleware = (function () {
|
||||
}
|
||||
};
|
||||
|
||||
if (action.force_reload) {
|
||||
if (action.forceReload) {
|
||||
console.info(`Force-reloading "${action.uri}"`);
|
||||
fetchArtist();
|
||||
break;
|
||||
@ -460,7 +475,7 @@ const CoreMiddleware = (function () {
|
||||
}
|
||||
};
|
||||
|
||||
if (action.force_reload) {
|
||||
if (action.forceReload) {
|
||||
console.info(`Force-reloading "${action.uri}"`);
|
||||
fetchPlaylist();
|
||||
break;
|
||||
@ -477,7 +492,6 @@ const CoreMiddleware = (function () {
|
||||
if (result) {
|
||||
console.info(`Restoring "${action.uri}" from database`);
|
||||
store.dispatch(coreActions.restoreItemsFromColdStore([result]));
|
||||
|
||||
if (!result.tracks) {
|
||||
fetchPlaylist();
|
||||
}
|
||||
@ -491,7 +505,7 @@ const CoreMiddleware = (function () {
|
||||
|
||||
case 'LOAD_USER':
|
||||
if (
|
||||
!action.force_reload
|
||||
!action.forceReload
|
||||
&& store.getState().core.users[action.uri]
|
||||
&& store.getState().core.users[action.uri].playlists_uris) {
|
||||
console.info(`Loading "${action.uri}" from index`);
|
||||
@ -517,7 +531,7 @@ const CoreMiddleware = (function () {
|
||||
|
||||
case 'LOAD_USER_PLAYLISTS':
|
||||
if (
|
||||
!action.force_reload
|
||||
!action.forceReload
|
||||
&& store.getState().core.users[action.uri]
|
||||
&& store.getState().core.users[action.uri].playlists_uris) {
|
||||
console.info(`Loading "${action.uri}" playlists from index`);
|
||||
@ -558,7 +572,7 @@ const CoreMiddleware = (function () {
|
||||
}
|
||||
};
|
||||
|
||||
if (action.force_reload) {
|
||||
if (action.forceReload) {
|
||||
console.info(`Force-reloading "${action.uri}"`);
|
||||
fetchLibrary();
|
||||
break;
|
||||
@ -592,11 +606,15 @@ const CoreMiddleware = (function () {
|
||||
* These modify our asset indexes, which are used globally
|
||||
* */
|
||||
|
||||
case 'CURRENT_TRACK_LOADED':
|
||||
store.dispatch(coreActions.trackLoaded(action.track));
|
||||
action.track = formatTrack(action.track);
|
||||
next(action);
|
||||
case 'CURRENT_TRACK_LOADED': {
|
||||
const track = formatTrack(action.track);
|
||||
store.dispatch(coreActions.itemLoaded(track));
|
||||
next({
|
||||
...action,
|
||||
track,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'QUEUE_LOADED':
|
||||
store.dispatch(coreActions.tracksLoaded(action.tracks));
|
||||
|
||||
@ -651,109 +651,65 @@ const MopidyMiddleware = (function () {
|
||||
* Advanced playback events
|
||||
* */
|
||||
|
||||
case 'MOPIDY_PLAY_PLAYLIST':
|
||||
var playlist = store.getState().core.items[action.uri];
|
||||
|
||||
// We have the playlist loaded already, and we've got at least 1 track to start playing
|
||||
if (playlist && playlist.tracks_uris && playlist.tracks_uris.length > 0) {
|
||||
// We've got all of the tracks, so just play those; no further action required
|
||||
if (playlist.tracks_total == playlist.tracks_uris.length) {
|
||||
store.dispatch(mopidyActions.playURIs(playlist.tracks_uris, action.uri, action.shuffle));
|
||||
break;
|
||||
}
|
||||
|
||||
// Spotify-provided playlists need to be handled by the Spotify service
|
||||
if (playlist.provider == 'spotify') {
|
||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'play'));
|
||||
break;
|
||||
}
|
||||
|
||||
// It's a Spotify playlist that we haven't loaded yet, so Spotify HTTP API needs to go get it
|
||||
} else if (uriSource(action.uri) == 'spotify' && store.getState().spotify.enabled) {
|
||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'play'));
|
||||
case 'MOPIDY_PLAY_PLAYLIST': {
|
||||
const playlist = store.getState().core.items[action.uri];
|
||||
if (playlist && playlist.tracks) {
|
||||
store.dispatch(
|
||||
mopidyActions.playURIs(
|
||||
arrayOf('uri', playlist.tracks),
|
||||
action.uri,
|
||||
action.shuffle,
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Not in index, and Spotify HTTP not enabled, so just play it as-is
|
||||
// Fetch the playlist tracks via backend and add each track by URI
|
||||
request(store, 'playlists.lookup', { uri: action.uri })
|
||||
.then(
|
||||
(response) => {
|
||||
if (!response || response.tracks === undefined || !response.tracks) {
|
||||
store.dispatch(uiActions.createNotification({ content: 'Failed to load playlist tracks', level: 'error' }));
|
||||
} else {
|
||||
let tracks_uris = arrayOf('uri', response.tracks);
|
||||
if (action.shuffle) {
|
||||
tracks_uris = shuffle(tracks_uris);
|
||||
}
|
||||
store.dispatch(mopidyActions.playURIs(tracks_uris, action.uri));
|
||||
}
|
||||
store.dispatch(
|
||||
coreActions.loadPlaylist(
|
||||
action.uri,
|
||||
false,
|
||||
{
|
||||
name: 'play',
|
||||
shuffle: action.shuffle,
|
||||
},
|
||||
(error) => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
`Mopidy: ${error.message ? error.message : 'Lookup failed'}`,
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'MOPIDY_ENQUEUE_PLAYLIST':
|
||||
|
||||
var playlist = store.getState().core.items[action.uri];
|
||||
|
||||
// We have the playlist loaded already, and we've got at least 1 track to start playing
|
||||
if (playlist && playlist.tracks_uris && playlist.tracks_uris.length > 0) {
|
||||
// We've got all of the tracks, so just play those; no further action required
|
||||
if (playlist.tracks_total == playlist.tracks_uris.length) {
|
||||
let tracks_uris = Object.assign([], playlist.tracks_uris);
|
||||
if (action.shuffle) {
|
||||
tracks_uris = shuffle(tracks_uris);
|
||||
}
|
||||
store.dispatch(mopidyActions.enqueueURIs(tracks_uris, action.uri, action.play_next, action.at_position, action.offset));
|
||||
break;
|
||||
}
|
||||
|
||||
// Spotify-provided playlists need to be handled by the Spotify service
|
||||
if (playlist.provider == 'spotify') {
|
||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'enqueue', action.play_next));
|
||||
break;
|
||||
}
|
||||
|
||||
// It's a Spotify playlist that we haven't loaded yet, so Spotify HTTP API needs to go get it
|
||||
} else if (uriSource(action.uri) == 'spotify' && store.getState().spotify.enabled) {
|
||||
store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri, action.shuffle, 'enqueue', action.play_next));
|
||||
case 'MOPIDY_ENQUEUE_PLAYLIST': {
|
||||
const playlist = store.getState().core.items[action.uri];
|
||||
if (playlist && playlist.tracks) {
|
||||
store.dispatch(
|
||||
mopidyActions.enqueueURIs(
|
||||
arrayOf('uri', playlist.tracks),
|
||||
action.uri,
|
||||
action.shuffle,
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Not in index, and Spotify HTTP not enabled, so just play it as-is
|
||||
// Fetch the playlist tracks via backend and add each track by URI
|
||||
request(store, 'playlists.lookup', { uri: action.uri })
|
||||
.then(
|
||||
(response) => {
|
||||
if (response.tracks === undefined) {
|
||||
store.dispatch(uiActions.createNotification({ content: 'Failed to load playlist tracks', level: 'error' }));
|
||||
} else {
|
||||
let tracks_uris = arrayOf('uri', response.tracks);
|
||||
if (action.shuffle) {
|
||||
tracks_uris = shuffle(tracks_uris);
|
||||
}
|
||||
store.dispatch(mopidyActions.enqueueURIs(tracks_uris, action.uri, action.play_next, action.at_position, action.offset));
|
||||
}
|
||||
store.dispatch(
|
||||
coreActions.loadPlaylist(
|
||||
action.uri,
|
||||
false,
|
||||
{
|
||||
name: 'enqueue',
|
||||
shuffle: action.shuffle,
|
||||
play_next: action.play_next,
|
||||
at_position: action.at_position,
|
||||
offset: action.offset,
|
||||
},
|
||||
(error) => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
`Mopidy: ${error.message ? error.message : 'Lookup failed'}`,
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'MOPIDY_ENQUEUE_URIS':
|
||||
|
||||
case 'MOPIDY_ENQUEUE_URIS': {
|
||||
if (!action.uris || action.uris.length <= 0) {
|
||||
this.props.uiActions.createNotification({ content: 'No URIs to enqueue', level: 'warning' });
|
||||
this.props.uiActions.createNotification({
|
||||
content: 'No URIs to enqueue',
|
||||
level: 'warning',
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@ -762,15 +718,18 @@ const MopidyMiddleware = (function () {
|
||||
{
|
||||
notification: {
|
||||
content: `${store.getState().pusher.username} is adding ${action.uris.length} URIs to queue`,
|
||||
icon: (store.getState().core.current_track ? getTrackIcon(store.getState().core.current_track, store.getState().core) : false),
|
||||
icon: (
|
||||
store.getState().core.current_track
|
||||
? getTrackIcon(store.getState().core.current_track, store.getState().core)
|
||||
: false
|
||||
),
|
||||
},
|
||||
},
|
||||
));
|
||||
|
||||
// split into batches
|
||||
var uris = Object.assign([], action.uris);
|
||||
var batches = [];
|
||||
var batch_size = 5;
|
||||
const uris = Object.assign([], action.uris);
|
||||
const batches = [];
|
||||
const batch_size = 5;
|
||||
while (uris.length > 0) {
|
||||
batches.push({
|
||||
uris: uris.splice(0, batch_size),
|
||||
@ -781,11 +740,11 @@ const MopidyMiddleware = (function () {
|
||||
});
|
||||
}
|
||||
|
||||
// pass this modified action to the reducer (and other middleware)
|
||||
action.batches = batches;
|
||||
next(action);
|
||||
next({
|
||||
...action,
|
||||
batches,
|
||||
});
|
||||
|
||||
// start our processor
|
||||
store.dispatch(uiActions.startProcess(
|
||||
'MOPIDY_ENQUEUE_URIS_PROCESSOR',
|
||||
i18n('services.mopidy.adding_uris', { count: action.uris.length }),
|
||||
@ -796,17 +755,14 @@ const MopidyMiddleware = (function () {
|
||||
},
|
||||
));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'MOPIDY_ENQUEUE_URIS_PROCESSOR':
|
||||
case 'MOPIDY_ENQUEUE_URIS_PROCESSOR': {
|
||||
const last_run = store.getState().ui.processes.MOPIDY_ENQUEUE_URIS_PROCESSOR;
|
||||
|
||||
var last_run = store.getState().ui.processes.MOPIDY_ENQUEUE_URIS_PROCESSOR;
|
||||
|
||||
// Cancelling
|
||||
if (last_run && last_run.status == 'cancelling') {
|
||||
store.dispatch(uiActions.processCancelled('MOPIDY_ENQUEUE_URIS_PROCESSOR'));
|
||||
return;
|
||||
|
||||
// make sure we have some uris in the queue
|
||||
} if (action.data.batches && action.data.batches.length > 0) {
|
||||
var batches = Object.assign([], action.data.batches);
|
||||
var batch = batches[0];
|
||||
@ -829,9 +785,11 @@ const MopidyMiddleware = (function () {
|
||||
break;
|
||||
}
|
||||
|
||||
var { current_track } = store.getState().core;
|
||||
var { queue } = store.getState().core;
|
||||
var current_track_index = -1;
|
||||
const {
|
||||
current_track,
|
||||
queue,
|
||||
} = store.getState().core;
|
||||
let current_track_index = -1;
|
||||
|
||||
if (current_track) {
|
||||
for (var i = 0; i < queue.length; i++) {
|
||||
@ -888,8 +846,8 @@ const MopidyMiddleware = (function () {
|
||||
));
|
||||
},
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'MOPIDY_PLAY_URIS':
|
||||
const { from_uri } = action;
|
||||
@ -2039,7 +1997,7 @@ const MopidyMiddleware = (function () {
|
||||
|
||||
// Some other source, rely on Mopidy backends to do their work
|
||||
} else {
|
||||
store.dispatch(mopidyActions.getImages('tracks', [track.uri]));
|
||||
store.dispatch(mopidyActions.getImages([track.uri]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2060,17 +2018,7 @@ const MopidyMiddleware = (function () {
|
||||
uri: track.uri,
|
||||
});
|
||||
|
||||
// We don't have the track (including images) already in our index
|
||||
if (store.getState().core.tracks[track.uri] === undefined || store.getState().core.tracks[track.uri].images === undefined) {
|
||||
// We've got Spotify running, and it's a spotify track - go straight to the source!
|
||||
if (store.getState().spotify.enabled && uriSource(track.uri) == 'spotify') {
|
||||
store.dispatch(spotifyActions.getTrack(track.uri));
|
||||
|
||||
// Some other source, rely on Mopidy backends to do their work
|
||||
} else {
|
||||
store.dispatch(mopidyActions.getImages('tracks', [track.uri]));
|
||||
}
|
||||
}
|
||||
store.dispatch(coreActions.loadItem(track.uri));
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2088,7 +2036,7 @@ const MopidyMiddleware = (function () {
|
||||
store.dispatch(coreActions.tracksLoaded(tracks));
|
||||
|
||||
if (action.get_images) {
|
||||
store.dispatch(mopidyActions.getImages('tracks', arrayOf('uri', tracks)));
|
||||
store.dispatch(mopidyActions.getImages(arrayOf('uri', tracks)));
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
|
||||
@ -946,7 +946,7 @@ export function getRecommendations(uris = [], limit = 20, tunabilities = null) {
|
||||
request(dispatch, getState, endpoint)
|
||||
.then(
|
||||
(response) => {
|
||||
const tracks = Object.assign([], response.tracks);
|
||||
const tracks = Object.assign([], formatTracks(response.tracks));
|
||||
|
||||
// We only get simple artist objects, so we need to
|
||||
// get the full object. We'll add URIs to our recommendations
|
||||
@ -983,11 +983,11 @@ export function getRecommendations(uris = [], limit = 20, tunabilities = null) {
|
||||
}
|
||||
|
||||
if (albums.length > 0) {
|
||||
dispatch(coreActions.albumsLoaded(albums));
|
||||
dispatch(coreActions.itemsLoaded(albums));
|
||||
}
|
||||
|
||||
if (tracks.length > 0) {
|
||||
dispatch(coreActions.tracksLoaded(tracks));
|
||||
dispatch(coreActions.itemsLoaded(tracks));
|
||||
}
|
||||
|
||||
dispatch({
|
||||
@ -1104,7 +1104,6 @@ export function getArtistImages(artist) {
|
||||
return (dispatch, getState) => {
|
||||
request(dispatch, getState, `search?q=${artist.name}&type=artist`)
|
||||
.then(response => {
|
||||
console.log('images', response);
|
||||
if (response.artists.items.length > 0) {
|
||||
dispatch(coreActions.itemLoaded({
|
||||
uri: artist.uri,
|
||||
@ -1117,22 +1116,27 @@ export function getArtistImages(artist) {
|
||||
|
||||
export function playArtistTopTracks(uri) {
|
||||
return (dispatch, getState) => {
|
||||
const { artists } = getState().core;
|
||||
const {
|
||||
items: {
|
||||
[uri]: artist,
|
||||
},
|
||||
} = getState().core;
|
||||
|
||||
// Do we have this artist (and their tracks) in our index already?
|
||||
if (typeof (artists[uri]) !== 'undefined' && typeof (artists[uri].tracks) !== 'undefined') {
|
||||
const uris = arrayOf('uri', artists[uri].tracks);
|
||||
if (artist && artist.tracks) {
|
||||
const uris = arrayOf('uri', artist.tracks);
|
||||
dispatch(mopidyActions.playURIs(uris, uri));
|
||||
|
||||
// We need to load the artist's top tracks first
|
||||
} else {
|
||||
request(dispatch, getState, `artists/${getFromUri('artistid', uri)}/top-tracks?country=${getState().spotify.country}`)
|
||||
.then(
|
||||
(response) => {
|
||||
const uris = arrayOf('uri', response.tracks);
|
||||
dispatch(mopidyActions.playURIs(uris, uri));
|
||||
},
|
||||
);
|
||||
request(
|
||||
dispatch,
|
||||
getState,
|
||||
`artists/${getFromUri('artistid', uri)}/top-tracks?country=${getState().spotify.country}`,
|
||||
).then(
|
||||
(response) => {
|
||||
const uris = arrayOf('uri', response.tracks);
|
||||
dispatch(mopidyActions.playURIs(uris, uri));
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1359,9 +1363,8 @@ export function savePlaylist(uri, name, description, is_public, is_collaborative
|
||||
};
|
||||
}
|
||||
|
||||
export function getPlaylist(uri) {
|
||||
export function getPlaylist(uri, callbackAction = null) {
|
||||
return (dispatch, getState) => {
|
||||
// get the main playlist object
|
||||
request(dispatch, getState, `playlists/${getFromUri('playlistid', uri)}?market=${getState().spotify.country}`)
|
||||
.then(
|
||||
(response) => {
|
||||
@ -1393,6 +1396,29 @@ export function getPlaylist(uri) {
|
||||
uri,
|
||||
tracks,
|
||||
}));
|
||||
|
||||
if (callbackAction) {
|
||||
switch (callbackAction.name) {
|
||||
case 'enqueue':
|
||||
dispatch(mopidyActions.enqueueURIs(
|
||||
arrayOf('uri', tracks),
|
||||
uri,
|
||||
callbackAction.play_next,
|
||||
callbackAction.at_position,
|
||||
callbackAction.offset,
|
||||
));
|
||||
break;
|
||||
case 'play':
|
||||
dispatch(mopidyActions.playURIs(
|
||||
arrayOf('uri', tracks),
|
||||
uri,
|
||||
callbackAction.shuffle,
|
||||
));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1498,8 +1524,26 @@ export function getLibraryTracksAndPlayProcessor(data) {
|
||||
* Recursively get .next until we have all tracks
|
||||
* */
|
||||
|
||||
export function getAllPlaylistTracks(uri, shuffle = false, callback_action = null, play_next = false, at_position = null, offset = 0) {
|
||||
export function getAllPlaylistTracks(
|
||||
uri,
|
||||
shuffle = false,
|
||||
callback_action = null,
|
||||
play_next = false,
|
||||
at_position = null,
|
||||
offset = 0,
|
||||
) {
|
||||
return (dispatch, getState) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (data.callback_action == 'enqueue') {
|
||||
dispatch(mopidyActions.enqueueURIs(uris, data.uri, data.play_next, data.at_position, data.offset));
|
||||
} else {
|
||||
dispatch(mopidyActions.playURIs(uris, data.uri));
|
||||
}
|
||||
|
||||
dispatch(uiActions.startProcess(
|
||||
'SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR',
|
||||
'Loading playlist tracks',
|
||||
@ -1690,7 +1734,6 @@ export function getLibraryPlaylists() {
|
||||
let libraryPlaylists = [];
|
||||
const fetchLibraryPlaylists = (endpoint) => request(dispatch, getState, endpoint)
|
||||
.then((response) => {
|
||||
console.log('spot', response);
|
||||
libraryPlaylists = [...libraryPlaylists, ...formatPlaylists(response.items)];
|
||||
if (response.next) {
|
||||
fetchLibraryPlaylists(response.next);
|
||||
|
||||
@ -172,6 +172,7 @@ const uiPersistConfig = {
|
||||
blacklist: [
|
||||
'load_queue',
|
||||
'notifications',
|
||||
'processes',
|
||||
'context_menu',
|
||||
'current_track_transition',
|
||||
'dragger',
|
||||
|
||||
@ -12,9 +12,9 @@ const indexToArray = (index, keys) => {
|
||||
if (!index) return [];
|
||||
|
||||
if (keys) {
|
||||
return keys.map((key) => index[key]);
|
||||
return compact(keys.map((key) => index[key]));
|
||||
}
|
||||
return Object.keys(index).map((key) => index[key]);
|
||||
return compact(Object.keys(index).map((key) => index[key]));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -31,7 +31,7 @@ import {
|
||||
titleCase,
|
||||
} from '../util/helpers';
|
||||
import { collate } from '../util/format';
|
||||
import { sortItems, applyFilter } from '../util/arrays';
|
||||
import { sortItems, applyFilter, arrayOf } from '../util/arrays';
|
||||
import { i18n, I18n } from '../locale';
|
||||
import Button from '../components/Button';
|
||||
import { trackEvent } from '../components/Trackable';
|
||||
@ -102,12 +102,14 @@ class Artist extends React.Component {
|
||||
const {
|
||||
artist: {
|
||||
uri,
|
||||
tracks_uris,
|
||||
tracks,
|
||||
albums_uris,
|
||||
},
|
||||
mopidyActions: { playURIs },
|
||||
mopidyActions: {
|
||||
playURIs,
|
||||
},
|
||||
} = this.props;
|
||||
playURIs(tracks_uris || albums_uris, uri);
|
||||
playURIs(arrayOf('uri', tracks) || albums_uris, uri);
|
||||
}
|
||||
|
||||
setWindowTitle = (artist = this.props.artist) => {
|
||||
@ -532,8 +534,8 @@ const mapStateToProps = (state, ownProps) => {
|
||||
load_queue: state.ui.load_queue,
|
||||
artist: (state.core.items[uri] || null),
|
||||
items: state.core.items,
|
||||
spotify_library_artists: state.core.items['spotify:library:artists'] || { items_uris: [] },
|
||||
mopidy_library_artists: state.core.items['mopidy:library:artists'] || { items_uris: [] },
|
||||
spotify_library_artists: state.core.libraries['spotify:library:artists'] || { items_uris: [] },
|
||||
mopidy_library_artists: state.core.libraries['mopidy:library:artists'] || { items_uris: [] },
|
||||
albums: (state.core.albums ? state.core.albums : []),
|
||||
filter: (state.ui.artist_albums_filter ? state.ui.artist_albums_filter : null),
|
||||
sort: (state.ui.artist_albums_sort ? state.ui.artist_albums_sort : null),
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
} from '../util/helpers';
|
||||
import { i18n, I18n } from '../locale';
|
||||
import Button from '../components/Button';
|
||||
import { indexToArray } from '../util/arrays';
|
||||
|
||||
const Artwork = ({
|
||||
image,
|
||||
@ -79,28 +80,13 @@ class Queue extends React.Component {
|
||||
}) => {
|
||||
const {
|
||||
coreActions: {
|
||||
loadAlbum,
|
||||
loadArtist,
|
||||
loadPlaylist,
|
||||
loadItem,
|
||||
},
|
||||
added_from_uri,
|
||||
} = this.props;
|
||||
|
||||
if (added_from_uri && added_from_uri !== prev_added_from_uri) {
|
||||
const item_type = uriType(added_from_uri);
|
||||
switch (item_type) {
|
||||
case 'album':
|
||||
loadAlbum(added_from_uri);
|
||||
break;
|
||||
case 'artist':
|
||||
loadArtist(added_from_uri);
|
||||
break;
|
||||
case 'playlist':
|
||||
loadPlaylist(added_from_uri);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
loadItem(added_from_uri);
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +106,12 @@ class Queue extends React.Component {
|
||||
}
|
||||
|
||||
removeTracks = (track_indexes) => {
|
||||
const { queue_tracks, mopidyActions } = this.props;
|
||||
const {
|
||||
queue_tracks,
|
||||
mopidyActions: {
|
||||
removeTracks: doRemoveTracks,
|
||||
},
|
||||
} = this.props;
|
||||
const tlids = [];
|
||||
for (let i = 0; i < track_indexes.length; i++) {
|
||||
const track = queue_tracks[track_indexes[i]];
|
||||
@ -130,7 +121,7 @@ class Queue extends React.Component {
|
||||
}
|
||||
|
||||
if (tlids.length > 0) {
|
||||
mopidyActions.removeTracks(tlids);
|
||||
doRemoveTracks(tlids);
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,61 +141,51 @@ class Queue extends React.Component {
|
||||
}
|
||||
|
||||
renderAddedFrom = () => {
|
||||
const { added_from_uri } = this.props;
|
||||
const {
|
||||
items,
|
||||
added_from_uri,
|
||||
} = this.props;
|
||||
if (!added_from_uri) return null;
|
||||
|
||||
const uri_type = uriType(added_from_uri);
|
||||
const items = [];
|
||||
let addedFromItems = [];
|
||||
|
||||
// Radio nests it's seed URIs in an encoded URI format
|
||||
switch (uri_type){
|
||||
case 'radio':
|
||||
const radio_seeds = getFromUri('seeds', added_from_uri);
|
||||
|
||||
for (let seed of radio_seeds) {
|
||||
let item_type = uriType(seed);
|
||||
let item_library = this.props[`${item_type}s`];
|
||||
if (item_library && item_library[seed]) {
|
||||
items.push(item_library[seed]);
|
||||
}
|
||||
}
|
||||
addedFromItems = indexToArray(items, getFromUri('seeds', added_from_uri));
|
||||
break;
|
||||
|
||||
case 'search':
|
||||
items.push({
|
||||
addedFromItems = [{
|
||||
uri: added_from_uri,
|
||||
name: `"${getFromUri('searchterm', added_from_uri)}" search`,
|
||||
})
|
||||
}];
|
||||
break;
|
||||
|
||||
default:
|
||||
const item_library = this.props[`${uri_type}s`];
|
||||
if (item_library && item_library[added_from_uri]) {
|
||||
items.push(item_library[added_from_uri]);
|
||||
}
|
||||
addedFromItems = indexToArray(items, [added_from_uri]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (items.length <= 0) return null;
|
||||
if (!addedFromItems.length) return null;
|
||||
|
||||
return (
|
||||
<div className="current-track__added-from">
|
||||
{items[0].images && (
|
||||
{addedFromItems[0].images && (
|
||||
<URILink
|
||||
uri={items[0].uri}
|
||||
uri={addedFromItems[0].uri}
|
||||
className="current-track__added-from__thumbnail"
|
||||
>
|
||||
<Thumbnail
|
||||
images={items[0].images}
|
||||
images={addedFromItems[0].images}
|
||||
size="small"
|
||||
circle={uriType(items[0].uri) === 'artist'}
|
||||
circle={uriType(addedFromItems[0].uri) === 'artist'}
|
||||
type="artist"
|
||||
/>
|
||||
</URILink>
|
||||
)}
|
||||
<div className="current-track__added-from__text">
|
||||
{'Playing from '}
|
||||
<LinksSentence items={items} />
|
||||
<LinksSentence items={addedFromItems} />
|
||||
{uri_type === 'radio' && (
|
||||
<span className="flag flag--blue">
|
||||
{i18n('now_playing.current_track.radio')}
|
||||
@ -221,6 +202,12 @@ class Queue extends React.Component {
|
||||
queue_tracks,
|
||||
theme,
|
||||
current_track_uri,
|
||||
spotify_enabled,
|
||||
uiActions,
|
||||
mopidyActions: {
|
||||
clearTracklist,
|
||||
shuffleTracklist,
|
||||
},
|
||||
} = this.props;
|
||||
const { limit } = this.state;
|
||||
const total_queue_tracks = queue_tracks.length;
|
||||
@ -235,7 +222,7 @@ class Queue extends React.Component {
|
||||
|
||||
const options = (
|
||||
<span>
|
||||
{this.props.spotify_enabled && (
|
||||
{spotify_enabled && (
|
||||
<Button noHover discrete to="/queue/radio">
|
||||
<Icon name="radio" />
|
||||
<I18n path="now_playing.context_actions.radio" />
|
||||
@ -254,7 +241,7 @@ class Queue extends React.Component {
|
||||
|
||||
return (
|
||||
<div className="view queue-view preserve-3d">
|
||||
<Header options={options} uiActions={this.props.uiActions}>
|
||||
<Header options={options} uiActions={uiActions}>
|
||||
<Icon name="play_arrow" type="material" />
|
||||
<I18n path="now_playing.title" />
|
||||
</Header>
|
||||
@ -293,7 +280,7 @@ class Queue extends React.Component {
|
||||
<li><Dater type="total-time" data={queue_tracks} /></li>
|
||||
{queue_tracks.length > 0 && (
|
||||
<li>
|
||||
<a onClick={this.props.mopidyActions.shuffleTracklist}>
|
||||
<a onClick={shuffleTracklist}>
|
||||
<Icon name="shuffle" />
|
||||
<I18n path="now_playing.current_track.shuffle" />
|
||||
</a>
|
||||
@ -301,7 +288,7 @@ class Queue extends React.Component {
|
||||
)}
|
||||
{queue_tracks.length > 0 && (
|
||||
<li>
|
||||
<a onClick={this.props.mopidyActions.clearTracklist}>
|
||||
<a onClick={clearTracklist}>
|
||||
<Icon name="delete_sweep" />
|
||||
<I18n path="now_playing.current_track.clear" />
|
||||
</a>
|
||||
@ -341,12 +328,17 @@ class Queue extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const mapStateToProps = (state) => {
|
||||
let { current_track } = state.core;
|
||||
const {
|
||||
items,
|
||||
queue,
|
||||
queue_metadata,
|
||||
} = state.core;
|
||||
const queue_tracks = [];
|
||||
|
||||
if (state.core.queue && state.core.tracks) {
|
||||
for (const queue_track of state.core.queue) {
|
||||
if (queue && items) {
|
||||
for (const queue_track of queue) {
|
||||
let track = {
|
||||
...queue_track,
|
||||
playing: current_track && current_track.tlid == queue_track.tlid,
|
||||
@ -355,18 +347,18 @@ const mapStateToProps = (state, ownProps) => {
|
||||
// If we have the track in our index, merge it in.
|
||||
// We prioritise queue track over index track as queue has unique data, like which track
|
||||
// is playing and tlids.
|
||||
if (state.core.tracks.hasOwnProperty(track.uri)) {
|
||||
if (items[track.uri]) {
|
||||
track = {
|
||||
...state.core.tracks[track.uri],
|
||||
...items[track.uri],
|
||||
...track,
|
||||
};
|
||||
}
|
||||
|
||||
// Now merge in our queue metadata
|
||||
if (state.core.queue_metadata[`tlid_${track.tlid}`] !== undefined) {
|
||||
if (queue_metadata[`tlid_${track.tlid}`] !== undefined) {
|
||||
track = {
|
||||
...track,
|
||||
...state.core.queue_metadata[`tlid_${track.tlid}`],
|
||||
...queue_metadata[`tlid_${track.tlid}`],
|
||||
};
|
||||
}
|
||||
|
||||
@ -389,6 +381,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
albums: state.core.albums,
|
||||
playlists: state.core.playlists,
|
||||
tracks: state.core.tracks,
|
||||
items,
|
||||
queue_tracks,
|
||||
current_track_uri: state.core.current_track_uri,
|
||||
current_track,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { compact } from 'lodash';
|
||||
import InputRange from 'react-input-range';
|
||||
import AlbumGrid from '../../components/AlbumGrid';
|
||||
import TrackList from '../../components/TrackList';
|
||||
@ -16,6 +16,7 @@ import Icon from '../../components/Icon';
|
||||
import * as uiActions from '../../services/ui/actions';
|
||||
import * as mopidyActions from '../../services/mopidy/actions';
|
||||
import * as spotifyActions from '../../services/spotify/actions';
|
||||
import * as coreActions from '../../services/core/actions';
|
||||
import {
|
||||
isLoading,
|
||||
getFromUri,
|
||||
@ -195,10 +196,10 @@ class Discover extends React.Component {
|
||||
uiActions: {
|
||||
showContextMenu,
|
||||
},
|
||||
tracks: tracksProp,
|
||||
items: itemsProp,
|
||||
} = this.props;
|
||||
|
||||
const tracks = indexToArray(tracksProp, tracks_uris);
|
||||
const tracks = indexToArray(itemsProp, tracks_uris);
|
||||
|
||||
showContextMenu({
|
||||
e,
|
||||
@ -211,9 +212,8 @@ class Discover extends React.Component {
|
||||
|
||||
handleURLSeeds = () => {
|
||||
const {
|
||||
spotifyActions: {
|
||||
getArtist,
|
||||
getTrack,
|
||||
coreActions: {
|
||||
loadItems,
|
||||
},
|
||||
match: {
|
||||
params: {
|
||||
@ -226,19 +226,7 @@ class Discover extends React.Component {
|
||||
// We'd need to do this if our URL has been encoded so the whole URL can become
|
||||
// it's own URI (eg iris:discover:spotify_artist_1234) where we can't use ":"
|
||||
const seeds = seedsProp.split('_').join(':').split(',');
|
||||
|
||||
for (let i = 0; i < seeds.length; i++) {
|
||||
switch (uriType(seeds[i])) {
|
||||
case 'artist':
|
||||
getArtist(seeds[i]);
|
||||
break;
|
||||
case 'track':
|
||||
getTrack(seeds[i]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
loadItems(seeds);
|
||||
|
||||
this.setState(
|
||||
{ seeds },
|
||||
@ -251,7 +239,7 @@ class Discover extends React.Component {
|
||||
seeds,
|
||||
} = this.state;
|
||||
|
||||
var uri = 'iris:discover';
|
||||
let uri = 'iris:discover';
|
||||
if (seeds) {
|
||||
uri += ':';
|
||||
for (var i = 0; i < seeds.length; i++) {
|
||||
@ -325,8 +313,7 @@ class Discover extends React.Component {
|
||||
|
||||
renderSeeds = () => {
|
||||
const {
|
||||
tracks,
|
||||
artists,
|
||||
items,
|
||||
} = this.props;
|
||||
const {
|
||||
seeds,
|
||||
@ -338,34 +325,22 @@ class Discover extends React.Component {
|
||||
const uri = seeds[i];
|
||||
|
||||
switch (uriType(uri)) {
|
||||
case 'track':
|
||||
if (typeof (tracks[uri]) !== 'undefined') {
|
||||
seeds_objects.push(tracks[uri]);
|
||||
} else {
|
||||
seeds_objects.push({
|
||||
name: 'Loading...',
|
||||
uri,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'artist':
|
||||
if (typeof (artists[uri]) !== 'undefined') {
|
||||
seeds_objects.push(artists[uri]);
|
||||
} else {
|
||||
seeds_objects.push({
|
||||
name: 'Loading...',
|
||||
uri,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'genre':
|
||||
var name = getFromUri('genreid', uri);
|
||||
const name = getFromUri('genreid', uri);
|
||||
seeds_objects.push({
|
||||
name: (name.charAt(0).toUpperCase() + name.slice(1)).replace('-', ' '),
|
||||
uri,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
if (typeof (items[uri]) !== 'undefined') {
|
||||
seeds_objects.push(items[uri]);
|
||||
} else {
|
||||
seeds_objects.push({
|
||||
name: 'Loading...',
|
||||
uri,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -475,44 +450,16 @@ class Discover extends React.Component {
|
||||
albums_uris,
|
||||
artists_uris,
|
||||
} = {},
|
||||
tracks: tracksProp,
|
||||
artists: artistsProp,
|
||||
albums: albumsProp,
|
||||
items: itemsProp,
|
||||
} = this.props;
|
||||
|
||||
if (!albums_uris === undefined || artists_uris === undefined) {
|
||||
return <div className="content-wrapper recommendations-results" />;
|
||||
}
|
||||
|
||||
const tracks = [];
|
||||
if (tracks_uris && tracksProp) {
|
||||
for (var i = 0; i < tracks_uris.length; i++) {
|
||||
var uri = tracks_uris[i];
|
||||
if (tracksProp.hasOwnProperty(uri)) {
|
||||
tracks.push(tracksProp[uri]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const artists = [];
|
||||
if (artists_uris && artistsProp) {
|
||||
for (var i = 0; i < artists_uris.length; i++) {
|
||||
var uri = artists_uris[i];
|
||||
if (artistsProp.hasOwnProperty(uri)) {
|
||||
artists.push(artistsProp[uri]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const albums = [];
|
||||
if (albums_uris && albumsProp) {
|
||||
for (var i = 0; i < albums_uris.length; i++) {
|
||||
var uri = albums_uris[i];
|
||||
if (albumsProp.hasOwnProperty(uri)) {
|
||||
albums.push(albumsProp[uri]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const tracks = compact(tracks_uris.map((uri) => itemsProp[uri]));
|
||||
const artists = compact(artists_uris.map((uri) => itemsProp[uri]));
|
||||
const albums = compact(albums_uris.map((uri) => itemsProp[uri]));
|
||||
|
||||
// Complete records not yet in our index
|
||||
if (tracks.length <= 0 && artists.length <= 0 && albums.length <= 0) {
|
||||
@ -640,9 +587,8 @@ class Discover extends React.Component {
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.ui.theme,
|
||||
albums: state.core.albums,
|
||||
artists: state.core.artists,
|
||||
tracks: state.core.tracks,
|
||||
items: state.core.items,
|
||||
genres: (state.core.genres ? state.core.genres : []),
|
||||
authorized: state.spotify.authorization,
|
||||
load_queue: state.ui.load_queue,
|
||||
@ -654,6 +600,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
uiActions: bindActionCreators(uiActions, dispatch),
|
||||
mopidyActions: bindActionCreators(mopidyActions, dispatch),
|
||||
spotifyActions: bindActionCreators(spotifyActions, dispatch),
|
||||
coreActions: bindActionCreators(coreActions, dispatch),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Discover);
|
||||
|
||||
Reference in New Issue
Block a user