From 98770441cc34d779ba82831668097cc50f5723fd Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Wed, 16 Nov 2016 13:13:35 +1300 Subject: [PATCH] Reordering local playlist tracks; Lazy-loading track details --- src/js/components/Track.js | 2 +- src/js/components/TrackList.js | 8 +- src/js/services/localstorage/middleware.js | 2 +- src/js/services/mopidy/middleware.js | 116 ++++++++++++++------- src/js/services/ui/actions.js | 19 ++-- src/js/services/ui/reducer.js | 18 +++- src/js/views/Playlist.js | 10 +- src/js/views/Queue.js | 7 +- 8 files changed, 123 insertions(+), 59 deletions(-) diff --git a/src/js/components/Track.js b/src/js/components/Track.js index 5a033076..2c802560 100755 --- a/src/js/components/Track.js +++ b/src/js/components/Track.js @@ -61,7 +61,7 @@ export default class Track extends React.Component{ onContextMenu={ e => this.handleContextMenu(e) }> { this.props.track.selected ? : null } - { track.name ? track.name : '-' } + { track.name ? track.name : track.uri } { track.artists ? : '-' } diff --git a/src/js/components/TrackList.js b/src/js/components/TrackList.js index 0281fd19..edc28637 100755 --- a/src/js/components/TrackList.js +++ b/src/js/components/TrackList.js @@ -95,8 +95,12 @@ class TrackList extends React.Component{ handleMouseUp(e, index){ if( this.props.dragger && this.props.dragger.active ){ - var indexes = this.props.dragger.victims_indexes - this.props.uiActions.reorderTracks(this.props.context, indexes, index) + + // if this tracklist handles sorting, handle it + if( typeof(this.props.reorderTracks) !== 'undefined' ){ + var indexes = this.props.dragger.victims_indexes + return this.props.reorderTracks( indexes, index ); + } } } diff --git a/src/js/services/localstorage/middleware.js b/src/js/services/localstorage/middleware.js index db626418..71932e75 100755 --- a/src/js/services/localstorage/middleware.js +++ b/src/js/services/localstorage/middleware.js @@ -12,7 +12,7 @@ const localstorageMiddleware = (function(){ // append our state to a global variable. This gives us access to debug the store at any point window._store = store - //console.log(action) + console.log(action) switch( action.type ){ diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index 1b5752b2..8ce77ca7 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -225,48 +225,48 @@ const MopidyMiddleware = (function(){ tracks_total: ( response.tracks ? response.tracks.length : 0 ) } ) - - var uris = []; - for( var i = 0; i < playlist.tracks.length; i++ ){ - uris.push( playlist.tracks[i].uri ); - } - // no tracks - if( uris.length <= 0 ){ - store.dispatch({ type: 'MOPIDY_PLAYLIST_LOADED', data: playlist }); + // tracks? get the full track objects + if( playlist.tracks.length > 0 ) store.dispatch({ type: 'MOPIDY_FETCH_PLAYLIST_TRACKS', tracks: playlist.tracks }) - // tracks? let's flesh them out with full track objects, rather than just reference objects - }else{ - instruct( socket, store, 'library.lookup', { uris: uris } ) - .then( response => { - - for(var uri in response){ - if (response.hasOwnProperty(uri)) { - - var track = response[uri][0]; - if( track ){ - - // find the track reference, and drop in the full track data - function getByURI( trackReference ){ - return track.uri == trackReference.uri - } - var trackReferences = playlist.tracks.filter(getByURI); - - // there could be multiple instances of this track, so accommodate this - for( var j = 0; j < trackReferences.length; j++){ - var key = playlist.tracks.indexOf( trackReferences[j] ); - playlist.tracks[ key ] = track; - } - } - } - } - - store.dispatch({ type: 'MOPIDY_PLAYLIST_LOADED', data: playlist }); - }) - } + store.dispatch({ type: 'MOPIDY_PLAYLIST_LOADED', data: playlist }) }) break; + case 'MOPIDY_FETCH_PLAYLIST_TRACKS': + var tracks = Object.assign([], action.tracks) + var uris = []; + for( var i = 0; i < tracks.length; i++ ){ + uris.push( tracks[i].uri ); + } + + instruct( socket, store, 'library.lookup', { uris: uris } ) + .then( response => { + for(var uri in response){ + if (response.hasOwnProperty(uri)) { + + var track = response[uri][0] + if( track ){ + + // find the track reference, and drop in the full track data + function getByURI( trackReference ){ + return track.uri == trackReference.uri + } + var trackReferences = tracks.filter(getByURI); + + // there could be multiple instances of this track, so accommodate this + for( var j = 0; j < trackReferences.length; j++){ + var key = tracks.indexOf( trackReferences[j] ); + tracks[ key ] = track; + } + } + } + } + + store.dispatch({ type: 'PLAYLIST_TRACKS_LOADED', tracks: tracks }) + }) + break + case 'MOPIDY_ADD_PLAYLIST_TRACKS': instruct( socket, store, 'playlists.lookup', { uri: action.playlist_uri }) @@ -339,6 +339,48 @@ const MopidyMiddleware = (function(){ }); break + case 'MOPIDY_REORDER_PLAYLIST_TRACKS': + instruct( socket, store, 'playlists.lookup', { uri: action.uri }) + .then( response => { + + var playlist = Object.assign({}, response) + var tracks = Object.assign([], playlist.tracks) + var tracks_to_move = [] + + // calculate destination index: if dragging down, accommodate the offset created by the tracks we're moving + var to_index = action.to_index + var indexes = action.indexes + var range_start = indexes[0] + var range_length = indexes.length + if( to_index > range_start ) to_index = to_index - range_length + + // collate our tracks to be moved + for( var i = 0; i < indexes.length; i++ ){ + + // add to FRONT: we work backwards to avoid screwing up our indexes + tracks_to_move.unshift( tracks[indexes[i]] ) + } + + // remove tracks from their old location + // BIG ASSUMPTION: this is one continuious set of indexes, ie not 1,2,5,6,9 + tracks.splice( range_start, range_length ) + + // now plug them back in, in their new location + for( var i = 0; i < tracks_to_move.length; i++ ){ + tracks.splice( to_index, 0, tracks_to_move[i] ) + } + + // update playlist + playlist = Object.assign({}, playlist, { tracks: tracks }) + instruct( socket, store, 'playlists.save', { playlist: playlist } ) + .then( response => { + + // and now re-render our full track references + store.dispatch({ type: 'MOPIDY_FETCH_PLAYLIST_TRACKS', tracks: playlist.tracks }) + }) + }); + break + case 'MOPIDY_DELETE_PLAYLIST': instruct( socket, store, 'playlists.delete', { uri: action.uri }) .then( response => { diff --git a/src/js/services/ui/actions.js b/src/js/services/ui/actions.js index ee0c64cd..067cc292 100755 --- a/src/js/services/ui/actions.js +++ b/src/js/services/ui/actions.js @@ -59,22 +59,23 @@ export function dragEnd(){ return { type: 'DRAG_END' } } -export function reorderTracks( context, indexes, destination_index ){ - console.log('uiActions.reorderTracks', context, indexes, destination_index) - switch( context ){ +export function reorderPlaylistTracks( uri, indexes, to_index ){ + switch( helpers.uriSource( uri ) ){ - case 'queue': + case 'spotify': return { - type: 'MOPIDY_REORDER_TRACKLIST', + type: 'SPOTIFY_REORDER_PLAYLIST_TRACKS', + uri: uri, indexes: indexes, - destination_index: destination_index + to_index: to_index } - case 'editable-playlist': + case 'm3u': return { - type: 'SPOTIFY_REORDER_TRACKLIST', + type: 'MOPIDY_REORDER_PLAYLIST_TRACKS', + uri: uri, indexes: indexes, - destination_index: destination_index + to_index: to_index } } } diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index 3d332e28..645af616 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -221,20 +221,28 @@ export default function reducer(ui = {}, action){ case 'PLAYLIST_TRACKS_REMOVED': var tracks = Object.assign([], ui.playlist.tracks) - for( var i = 0; i < action.tracks_indexes.length; i++ ){ - tracks.splice( action.tracks_indexes[i], 1 ) + var indexes = action.tracks_indexes.reverse() + for( var i = 0; i < indexes.length; i++ ){ + tracks.splice( indexes[i], 1 ) } var snapshot_id = null if( action.snapshot_id ) snapshot_id = action.snapshot_id var playlist = Object.assign({}, ui.playlist, { tracks: tracks, snapshot_id: snapshot_id }) return Object.assign({}, ui, { playlist: playlist }); + case 'PLAYLIST_TRACKS_LOADED': + var playlist = Object.assign({}, ui.playlist, { tracks: action.tracks }) + return Object.assign({}, ui, { playlist: playlist }); + + case 'PLAYLIST_TRACKS_REORDERED': + var snapshot_id = null + if( action.snapshot_id ) snapshot_id = action.snapshot_id + var playlist = Object.assign({}, ui.playlist, { tracks: action.tracks, snapshot_id: snapshot_id }) + return Object.assign({}, ui, { playlist: playlist }); + /** * Library Playlists - * - * TODO: Map sources and merge any replicated URIS - * **/ case 'MOPIDY_PLAYLISTS_LOADED': diff --git a/src/js/views/Playlist.js b/src/js/views/Playlist.js index 8090ff0e..6d35feeb 100755 --- a/src/js/views/Playlist.js +++ b/src/js/views/Playlist.js @@ -73,8 +73,12 @@ class Playlist extends React.Component{ this.props.mopidyActions.deletePlaylist( this.props.params.uri ) } - removeTracks( track_indexes ){ - this.props.uiActions.removeTracksFromPlaylist( this.props.playlist.uri, track_indexes ) + reorderTracks( indexes, index ){ + this.props.uiActions.reorderPlaylistTracks( this.props.playlist.uri, indexes, index ) + } + + removeTracks( tracks_indexes ){ + this.props.uiActions.removeTracksFromPlaylist( this.props.playlist.uri, tracks_indexes ) } renderEditButtons(){ @@ -138,7 +142,7 @@ class Playlist extends React.Component{
- { this.props.playlist.tracks ? this.removeTracks(track_indexes) } /> : null } + { this.props.playlist.tracks ? this.removeTracks(tracks_indexes) } reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } /> : null } this.loadMore() }/>
diff --git a/src/js/views/Queue.js b/src/js/views/Queue.js index 29f723e3..81760c66 100755 --- a/src/js/views/Queue.js +++ b/src/js/views/Queue.js @@ -37,6 +37,10 @@ class Queue extends React.Component{ this.props.mopidyActions.changeTrack( tracks[0].tlid ) } + reorderTracks( indexes, index ){ + this.props.mopidyActions.reorderTracklist( indexes, index ) + } + render(){ return (
@@ -50,7 +54,8 @@ class Queue extends React.Component{ tracks={this.props.current_tracklist} removeTracks={ tracks => this.removeTracks( tracks ) } playTracks={ tracks => this.playTracks( tracks ) } - playTrack={ track => this.playTrack( track ) } /> + playTrack={ track => this.playTrack( track ) } + reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } />