From 8b718c0a33fcd9a13933423fb212449729ddd20f Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Fri, 13 Jan 2017 13:17:47 +1300 Subject: [PATCH 1/9] Playlists stored in universal store, indexes stored for each view --- src/js/bootstrap.js | 5 + src/js/components/GridItem.js | 2 +- src/js/components/TrackList.js | 2 +- src/js/helpers.js | 22 ++++ src/js/services/localstorage/middleware.js | 2 +- src/js/services/mopidy/actions.js | 16 +-- src/js/services/mopidy/middleware.js | 126 ++++++++++++++------- src/js/services/spotify/actions.js | 113 ++++++++++++------ src/js/services/ui/reducer.js | 76 +++++-------- src/js/views/App.js | 2 +- src/js/views/Playlist.js | 21 +--- src/js/views/discover/DiscoverCategory.js | 4 +- src/js/views/discover/DiscoverFeatured.js | 18 ++- src/js/views/library/LibraryPlaylists.js | 12 +- 14 files changed, 264 insertions(+), 157 deletions(-) diff --git a/src/js/bootstrap.js b/src/js/bootstrap.js index 2a6371df..e3611045 100755 --- a/src/js/bootstrap.js +++ b/src/js/bootstrap.js @@ -65,6 +65,11 @@ var initialState = { playlists: [], tracks: [] }, + library_artists: [], + artists: [], + library_albums: [], + albums: [], + library_playlists: [], playlists: [], notifications: [] } diff --git a/src/js/components/GridItem.js b/src/js/components/GridItem.js index ac8ff435..bd3a2d50 100755 --- a/src/js/components/GridItem.js +++ b/src/js/components/GridItem.js @@ -38,7 +38,7 @@ export default class GridItem extends React.Component{
{ item.name }
{ item.artists ? : null } - { item.type == 'playlist' && item.tracks ? item.tracks.total+' tracks' : null } + { item.type == 'playlist' && item.tracks_total ? item.tracks_total+' tracks' : null } { item.followers ? item.followers.total.toLocaleString()+' followers' : null } { item.type == 'playlist' && item.can_edit ? : null }
diff --git a/src/js/components/TrackList.js b/src/js/components/TrackList.js index 426198b9..b4e40abe 100755 --- a/src/js/components/TrackList.js +++ b/src/js/components/TrackList.js @@ -245,7 +245,7 @@ class TrackList extends React.Component{ } render(){ - if( !this.state.tracks ) return null + if( !this.state.tracks || Object.prototype.toString.call(this.state.tracks) !== '[object Array]' ) return null let self = this; return ( diff --git a/src/js/helpers.js b/src/js/helpers.js index 9843299d..17cb6a8c 100755 --- a/src/js/helpers.js +++ b/src/js/helpers.js @@ -93,6 +93,28 @@ export let getTrackIcon = function( track = false ){ } +/** + * Get a track's icon + * @param track object + * @return string + **/ +export let flattenTracks = function( tracks ){ + var flattened = [] + for( var i = 0; i < tracks.length; i++ ){ + flattened.push( Object.assign( + {}, + tracks[i].track, + { + added_by: tracks[i].added_by, + added_at: tracks[i].added_at + } + )) + } + + return flattened +} + + /** diff --git a/src/js/services/localstorage/middleware.js b/src/js/services/localstorage/middleware.js index b04cc042..435627b4 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/actions.js b/src/js/services/mopidy/actions.js index 13954f9c..09f3a6be 100755 --- a/src/js/services/mopidy/actions.js +++ b/src/js/services/mopidy/actions.js @@ -157,47 +157,47 @@ export function deletePlaylist( uri ){ } } -export function getPlaylists(){ - return { type: 'MOPIDY_PLAYLISTS' } +export function getLibraryPlaylists(){ + return { type: 'MOPIDY_GET_LIBRARY_PLAYLISTS' } } export function getPlaylist( uri ){ return { - type: 'MOPIDY_PLAYLIST', + type: 'MOPIDY_GET_PLAYLIST', data: { uri: uri } } } export function getDirectory( uri ){ return { - type: 'MOPIDY_DIRECTORY', + type: 'MOPIDY_GET_DIRECTORY', data: { uri: uri } } } export function getAlbum( uri ){ return { - type: 'MOPIDY_ALBUM', + type: 'MOPIDY_GET_ALBUM', data: { uri: uri } } } export function getArtist( uri ){ return { - type: 'MOPIDY_ARTIST', + type: 'MOPIDY_GET_ARTIST', data: { uri: uri } } } export function getArtists(){ return { - type: 'MOPIDY_ARTISTS' + type: 'MOPIDY_GET_ARTISTS' } } export function getAlbums(){ return { - type: 'MOPIDY_ALBUMS' + type: 'MOPIDY_GET_ALBUMS' } } diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index 5f2ff38d..f10054fe 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -282,29 +282,47 @@ const MopidyMiddleware = (function(){ }) break; - case 'MOPIDY_PLAYLISTS': + + /** + * =============================================================== PLAYLIST(S) ========== + * ====================================================================================== + **/ + + case 'MOPIDY_GET_LIBRARY_PLAYLISTS': instruct( socket, store, 'playlists.asList' ) .then( response => { - for( var i = 0; i < response.length; i++ ){ - if( response[i].uri.startsWith('m3u:')) response[i] = Object.assign({}, response[i], { can_edit: true }) - } - store.dispatch({ type: 'MOPIDY_PLAYLISTS_LOADED', data: response }); - // TODO: dispatch an action to fetch extra data for each playlist - // this will then trigger an PLAYLIST_REFRESHED action to update a specific store item + // drop in our URI list + var playlist_uris = helpers.asURIs(response) + + store.dispatch({ type: 'LIBRARY_PLAYLISTS_LOADED', uris: playlist_uris }); + + // get the full playlist objects + for (var i = 0; i < response.length; i++ ){ + instruct( socket, store, 'playlists.lookup', { uri: response[i].uri }) + .then( response => { + var playlist = Object.assign( + {}, + { + images: [] + }, + { + can_edit: (response.uri.startsWith('m3u:')) + }, + response, + { + tracks: ( response.tracks ? response.tracks : [] ), + tracks_total: ( response.tracks ? response.tracks.length : 0 ) + } + ) + + store.dispatch({ type: 'PLAYLIST_LOADED', playlist: playlist }) + }) + } }) break; - case 'MOPIDY_CREATE_PLAYLIST': - instruct( socket, store, 'playlists.create', { name: action.name, uri_scheme: action.scheme }) - .then( response => { - - // re-load our global playlists - store.dispatch({ type: 'MOPIDY_PLAYLISTS' }); - }); - break - - case 'MOPIDY_PLAYLIST': + case 'MOPIDY_GET_PLAYLIST': store.dispatch({ type: 'MOPIDY_PLAYLIST_LOADED', data: false }); instruct( socket, store, 'playlists.lookup', action.data ) .then( response => { @@ -321,18 +339,15 @@ const MopidyMiddleware = (function(){ ) // tracks? get the full track objects - if( playlist.tracks.length > 0 ) store.dispatch({ type: 'MOPIDY_PLAYLIST_TRACKS', tracks: playlist.tracks }) + if( playlist.tracks.length > 0 ) store.dispatch({ type: 'MOPIDY_RESOLVE_PLAYLIST_TRACKS', tracks: playlist.tracks }) - store.dispatch({ type: 'MOPIDY_PLAYLIST_LOADED', data: playlist }) + store.dispatch({ type: 'PLAYLIST_LOADED', playlist: playlist }) }) break; - case 'MOPIDY_PLAYLIST_TRACKS': + case 'MOPIDY_RESOLVE_PLAYLIST_TRACKS': var tracks = Object.assign([], action.tracks) - var uris = []; - for( var i = 0; i < tracks.length; i++ ){ - uris.push( tracks[i].uri ); - } + var uris = helpers.asURIs(tracks) instruct( socket, store, 'library.lookup', { uris: uris } ) .then( response => { @@ -357,7 +372,7 @@ const MopidyMiddleware = (function(){ } } - store.dispatch({ type: 'PLAYLIST_TRACKS_LOADED', tracks: tracks }) + store.dispatch({ type: 'PLAYLIST_TRACKS_LOADED', playlist_uri: action.playlist_uri, tracks: tracks }) }) break @@ -460,14 +475,37 @@ const MopidyMiddleware = (function(){ }); break + case 'MOPIDY_CREATE_PLAYLIST': + instruct( socket, store, 'playlists.create', { name: action.name, uri_scheme: action.scheme }) + .then( response => { + + // re-load our global playlists + //store.dispatch({ type: 'MOPIDY_GET_PLAYLISTS' }); + }); + break + case 'MOPIDY_DELETE_PLAYLIST': instruct( socket, store, 'playlists.delete', { uri: action.uri }) .then( response => { // re-load our global playlists - store.dispatch({ type: 'MOPIDY_PLAYLISTS' }); + // store.dispatch({ type: 'MOPIDY_PLAYLISTS' }); }); break + + + /** + * =============================================================== ALBUM(S) ============= + * ====================================================================================== + **/ + + case 'MOPIDY_ALBUMS': + store.dispatch({ type: 'MOPIDY_ALBUMS_LOADED', data: false }); + instruct( socket, store, 'library.browse', { uri: 'local:directory?type=album' } ) + .then( response => { + store.dispatch({ type: 'MOPIDY_ALBUMS_LOADED', data: response }); + }) + break; case 'MOPIDY_ALBUM': //store.dispatch({ type: 'MOPIDY_ALBUM_LOADED', data: false }); @@ -526,6 +564,20 @@ const MopidyMiddleware = (function(){ }) }) break; + + + /** + * =============================================================== ARTIST(S) ============ + * ====================================================================================== + **/ + + case 'MOPIDY_ARTISTS': + store.dispatch({ type: 'MOPIDY_ARTISTS_LOADED', data: false }); + instruct( socket, store, 'library.browse', { uri: 'local:directory?type=artist' } ) + .then( response => { + store.dispatch({ type: 'MOPIDY_ARTISTS_LOADED', data: response }); + }) + break; case 'MOPIDY_ARTIST': store.dispatch({ type: 'MOPIDY_ARTIST_LOADED', data: false }); @@ -560,6 +612,12 @@ const MopidyMiddleware = (function(){ store.dispatch({ type: 'MOPIDY_ARTIST_LOADED', data: artist }); }) break; + + + /** + * =============================================================== LOCAL ================ + * ====================================================================================== + **/ case 'MOPIDY_DIRECTORY': store.dispatch({ type: 'MOPIDY_DIRECTORY_LOADED', data: false }); @@ -569,22 +627,6 @@ const MopidyMiddleware = (function(){ }) break; - case 'MOPIDY_ARTISTS': - store.dispatch({ type: 'MOPIDY_ARTISTS_LOADED', data: false }); - instruct( socket, store, 'library.browse', { uri: 'local:directory?type=artist' } ) - .then( response => { - store.dispatch({ type: 'MOPIDY_ARTISTS_LOADED', data: response }); - }) - break; - - case 'MOPIDY_ALBUMS': - store.dispatch({ type: 'MOPIDY_ALBUMS_LOADED', data: false }); - instruct( socket, store, 'library.browse', { uri: 'local:directory?type=album' } ) - .then( response => { - store.dispatch({ type: 'MOPIDY_ALBUMS_LOADED', data: response }); - }) - break; - // This action is irrelevant to us, pass it on to the next middleware default: return next(action); diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 11ca1f07..4034ae29 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -262,9 +262,18 @@ export function getFeaturedPlaylists(){ sendRequest( dispatch, getState, 'browse/featured-playlists?timestamp='+timestamp+'&country='+getState().spotify.country+'&limit=50&locale='+getState().spotify.locale ) .then( response => { + for (var i = 0; i < response.playlists.items.length; i++){ + dispatch({ + type: 'PLAYLIST_LOADED', + playlist: response.playlists.items[i] + }); + } dispatch({ type: 'SPOTIFY_FEATURED_PLAYLISTS_LOADED', - data: response + data: { + message: response.message, + playlists: helpers.asURIs(response.playlists.items) + } }); }); } @@ -290,28 +299,41 @@ export function getCategory( id ){ dispatch({ type: 'SPOTIFY_CATEGORY_LOADED', data: false }); - sendRequest( dispatch, getState, 'browse/categories/'+id+'?country='+getState().spotify.country+'&locale='+getState().spotify.locale ) - .then( response => { - dispatch({ - type: 'SPOTIFY_CATEGORY_LOADED', - data: response - }); - }); - } -} - -export function getCategoryPlaylists( id ){ - return (dispatch, getState) => { - - dispatch({ type: 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED', data: false }); - - sendRequest( dispatch, getState, 'browse/categories/'+id+'/playlists?limit=50&country='+getState().spotify.country+'&locale='+getState().spotify.locale ) - .then( response => { - dispatch({ - type: 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED', - data: response.playlists - }); + $.when( + + sendRequest( dispatch, getState, 'browse/categories/'+id+'?country='+getState().spotify.country+'&locale='+getState().spotify.locale ), + sendRequest( dispatch, getState, 'browse/categories/'+id+'/playlists?limit=50&country='+getState().spotify.country+'&locale='+getState().spotify.locale ) + + ).then( ( category_response, playlists_response ) => { + + var playlists = [] + for (var i = 0; i < playlists_response.playlists.items.length; i++){ + playlists.push( + Object.assign( + {}, + playlists_response.playlists.items[i], + { + tracks: false, + tracks_total: playlists_response.playlists.items[i].tracks.total + } + ) + ) + } + + var category = Object.assign( + {}, + category_response, + { + items: false, + playlists: playlists + } + ) + + dispatch({ + type: 'SPOTIFY_CATEGORY_LOADED', + data: category }); + }); } } @@ -330,13 +352,14 @@ export function getNewReleases(){ } } -export function getURL( url, action_name ){ +export function getURL( url, action_name, uri = false ){ return (dispatch, getState) => { sendRequest( dispatch, getState, url ) .then( response => { dispatch({ type: action_name, - data: response + data: response, + uri: uri }); }); } @@ -774,15 +797,24 @@ export function savePlaylist( uri, name, is_public ){ export function getPlaylist( uri ){ return (dispatch, getState) => { - // flush out the previous store value - dispatch({ type: 'SPOTIFY_PLAYLIST_LOADED', data: false }); - // get the main playlist object sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) +'/playlists/'+ helpers.getFromUri('playlistid',uri) +'?market='+getState().spotify.country ) .then( response => { + + var playlist = Object.assign( + {}, + response, + { + can_edit: (getState().spotify.me && response.owner.id == getState().spotify.me.id), + tracks: helpers.flattenTracks(response.tracks.items), + tracks_more: response.tracks.next, + tracks_total: response.tracks.total + } + ) + dispatch({ - type: 'SPOTIFY_PLAYLIST_LOADED', - data: response + type: 'PLAYLIST_LOADED', + playlist: playlist }) }) } @@ -801,15 +833,28 @@ function loadNextPlaylistsBatch( dispatch, getState, playlists, lastResponse ){ // used to define what we can add tracks to if( getState().spotify.authorized ){ for( var i = 0; i < playlists.length; i++ ){ - if( playlists[i].owner.id == getState().spotify.me.id ){ - playlists[i] = Object.assign({}, playlists[i], { can_edit: true }) - } + + var playlist = Object.assign( + {}, + playlists[i], + { + can_edit: (getState().spotify.me && playlists[i].owner.id == getState().spotify.me.id), + tracks: helpers.flattenTracks(playlists[i].tracks.items), + tracks_more: response.tracks.next, + tracks_total: response.tracks.total + } + ) + + dispatch({ + type: 'PLAYLIST_LOADED', + playlist: playlist + }); } } dispatch({ - type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED', - data: playlists + type: 'LIBRARY_PLAYLISTS_LOADED', + uris: helpers.asURIs(playlists) }); } } diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index 72c1487b..a7024819 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -231,46 +231,21 @@ export default function reducer(ui = {}, action){ if( !action.data ) return Object.assign({}, ui, { playlist: false }) return Object.assign({}, ui, { playlist: action.data }) - case 'SPOTIFY_PLAYLIST_LOADED': - if( !action.data ) return Object.assign({}, ui, { playlist: false }) + case 'PLAYLIST_LOADED_MORE_TRACKS': - var tracks = [] - for( var i = 0; i < action.data.tracks.items.length; i++ ){ - tracks.push( Object.assign( - {}, - action.data.tracks.items[i].track, - { - added_by: action.data.tracks.items[i].added_by, - added_at: action.data.tracks.items[i].added_at - } - )) - } + var playlists = ui.playlists + var playlist = Object.assign( + {}, + playlists[action.uri], + { + tracks: [...playlists[action.uri].tracks, ...helpers.flattenTracks(action.data.items)], + tracks_more: action.data.next, + tracks_total: action.data.total + } + ) - var playlist = Object.assign({}, action.data, { - tracks: tracks, - tracks_more: action.data.tracks.next, - tracks_total: action.data.tracks.total - }) - return Object.assign({}, ui, { playlist: playlist }); - - case 'SPOTIFY_PLAYLIST_LOADED_MORE': - var tracks = [] - for( var i = 0; i < action.data.items.length; i++ ){ - tracks.push( Object.assign( - {}, - action.data.items[i].track, - { - added_by: action.data.items[i].added_by, - added_at: action.data.items[i].added_at - } - )) - } - - var playlist = Object.assign({}, ui.playlist, { - tracks: [...ui.playlist.tracks, ...tracks], - tracks_more: action.data.next - }) - return Object.assign({}, ui, { playlist: playlist }); + playlists[action.uri] = playlist + return Object.assign({}, ui, { playlists: playlists }); case 'PLAYLIST_TRACKS_REMOVED': var tracks = Object.assign([], ui.playlist.tracks) @@ -316,14 +291,25 @@ export default function reducer(ui = {}, action){ * Library Playlists **/ - case 'MOPIDY_PLAYLISTS_LOADED': - case 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED': - if( !action.data ) return ui - var playlists = [...ui.playlists, ...action.data] - playlists = helpers.mergeDuplicates(playlists, 'uri') - helpers.sortItems(playlists, 'name') + case 'PLAYLIST_LOADED': + case 'PLAYLIST_UPDATED': + var playlists = ui.playlists + var playlist = Object.assign({}, action.playlist) + + // if we already have one in our list, fetch it and update it + if (playlists[action.playlist.uri]){ + playlist = Object.assign({}, playlists[action.playlist.uri], action.playlist) + } + + playlists[action.playlist.uri] = playlist + return Object.assign({}, ui, { playlists: playlists }); + + case 'LIBRARY_PLAYLISTS_LOADED': + var library_playlists = [] + if (ui.library_playlists) library_playlists = ui.library_playlists + return Object.assign({}, ui, { - playlists: playlists + library_playlists: [...library_playlists, ...action.uris] }); diff --git a/src/js/views/App.js b/src/js/views/App.js index fe17141c..45e80322 100755 --- a/src/js/views/App.js +++ b/src/js/views/App.js @@ -53,7 +53,7 @@ class App extends React.Component{ // mopidy comes online if( !this.props.mopidy_connected && nextProps.mopidy_connected ){ - this.props.mopidyActions.getPlaylists(); + this.props.mopidyActions.getLibraryPlaylists(); } // spotify authorized diff --git a/src/js/views/Playlist.js b/src/js/views/Playlist.js index f985a768..faa42ca4 100755 --- a/src/js/views/Playlist.js +++ b/src/js/views/Playlist.js @@ -55,7 +55,7 @@ class Playlist extends React.Component{ loadMore(){ if( !this.props.playlist.tracks_more ) return - this.props.spotifyActions.getURL( this.props.playlist.tracks_more, 'SPOTIFY_PLAYLIST_LOADED_MORE' ); + this.props.spotifyActions.getURL( this.props.playlist.tracks_more, 'PLAYLIST_LOADED_MORE_TRACKS', this.props.playlist.uri ); } play(){ @@ -90,19 +90,6 @@ class Playlist extends React.Component{ this.props.uiActions.removeTracksFromPlaylist( this.props.playlist.uri, tracks_indexes ) } - isEditable(){ - if( helpers.uriSource( this.props.playlist.uri ) == 'spotify' ){ - if( !this.props.spotify_authorized ) return false - - return ( this.props.playlist && - this.props.playlist.owner && - this.props.playlist.owner.id == this.props.spotify_userid - ) - }else{ - return true - } - } - renderExtraButtons(){ switch( helpers.uriSource( this.props.playlist.uri ) ){ @@ -115,7 +102,7 @@ class Playlist extends React.Component{ ) case 'spotify': - if( this.isEditable() ){ + if( this.props.playlist.can_edit ){ return ( @@ -167,7 +154,7 @@ class Playlist extends React.Component{
- { this.props.playlist.tracks ? this.removeTracks(tracks_indexes) } reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } /> : null } + { this.props.playlist.tracks ? this.removeTracks(tracks_indexes) } reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } /> : null } this.loadMore() }/>
@@ -186,7 +173,7 @@ class Playlist extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - playlist: state.ui.playlist, + playlist: state.ui.playlists[ownProps.params.uri], mopidy_connected: state.mopidy.connected, spotify_authorized: state.spotify.authorized, spotify_userid: state.spotify.me.id diff --git a/src/js/views/discover/DiscoverCategory.js b/src/js/views/discover/DiscoverCategory.js index 6ac1dcd1..769f1188 100755 --- a/src/js/views/discover/DiscoverCategory.js +++ b/src/js/views/discover/DiscoverCategory.js @@ -17,13 +17,11 @@ class DiscoverCategory extends React.Component{ componentDidMount(){ this.props.spotifyActions.getCategory( this.props.params.id ); - this.props.spotifyActions.getCategoryPlaylists( this.props.params.id ); } componentWillReceiveProps( nextProps ){ if( nextProps.params.id != this.props.params.id ){ this.props.spotifyActions.getCategory( nextProps.params.id ); - this.props.spotifyActions.getCategoryPlaylists( nextProps.params.id ); } } @@ -39,7 +37,7 @@ class DiscoverCategory extends React.Component{
- { this.props.spotify.category_playlists ? : null } + { this.props.spotify.category.playlists ? : null }
this.loadMore() }/>
diff --git a/src/js/views/discover/DiscoverFeatured.js b/src/js/views/discover/DiscoverFeatured.js index 7e6d8ad9..afa19248 100755 --- a/src/js/views/discover/DiscoverFeatured.js +++ b/src/js/views/discover/DiscoverFeatured.js @@ -20,11 +20,22 @@ class DiscoverFeatured extends React.Component{ } render(){ + + var playlists = [] + if (this.props.featured_playlists){ + for (var i = 0; i < this.props.featured_playlists.playlists.length; i++){ + var uri = this.props.featured_playlists.playlists[i] + if (this.props.playlists.hasOwnProperty(uri)){ + playlists.push(this.props.playlists[uri]) + } + } + } + return (
- { this.props.spotify.featured_playlists ? : null } + { playlists ? : null }
); @@ -39,7 +50,10 @@ class DiscoverFeatured extends React.Component{ **/ const mapStateToProps = (state, ownProps) => { - return state; + return { + featured_playlists: state.spotify.featured_playlists, + playlists: state.ui.playlists + } } const mapDispatchToProps = (dispatch) => { diff --git a/src/js/views/library/LibraryPlaylists.js b/src/js/views/library/LibraryPlaylists.js index 6e89607a..f4f2ef00 100755 --- a/src/js/views/library/LibraryPlaylists.js +++ b/src/js/views/library/LibraryPlaylists.js @@ -33,9 +33,16 @@ class LibraryPlaylists extends React.Component{ } renderView(){ - if( !this.props.playlists ) return null + if( !this.props.library_playlists || !this.props.playlists ) return null + + var playlists = [] + for (var i = 0; i < this.props.library_playlists.length; i++){ + var uri = this.props.library_playlists[i] + if (this.props.playlists.hasOwnProperty(uri)){ + playlists.push(this.props.playlists[uri]) + } + } - var playlists = Object.assign([], this.props.playlists) if( this.props.sort ){ playlists = helpers.sortItems(playlists, this.props.sort, this.props.sort_reverse) } @@ -141,6 +148,7 @@ const mapStateToProps = (state, ownProps) => { view: state.ui.library_playlists_view, sort: state.ui.library_playlists_sort, sort_reverse: state.ui.library_playlists_sort_reverse, + library_playlists: state.ui.library_playlists, playlists: state.ui.playlists } } From c5bb4ee6ca5c6db61f04c4600c4be8e3305e1c7d Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Sat, 14 Jan 2017 01:01:48 +1300 Subject: [PATCH 2/9] Moving more playlist functions to the pool --- build.sh | 2 + src/js/components/FollowButton.js | 24 +++++-- src/js/components/GridItem.js | 31 +++++++-- src/js/helpers.js | 12 ++++ src/js/services/localstorage/middleware.js | 2 +- src/js/services/mopidy/middleware.js | 25 +++---- src/js/services/spotify/actions.js | 79 ++++++++++++---------- src/js/services/spotify/reducer.js | 4 +- src/js/services/ui/reducer.js | 49 ++++++-------- src/js/views/Playlist.js | 2 +- src/js/views/discover/DiscoverCategory.js | 23 +++++-- src/js/views/library/LibraryPlaylists.js | 2 +- 12 files changed, 157 insertions(+), 98 deletions(-) diff --git a/build.sh b/build.sh index aced77c7..879480a0 100755 --- a/build.sh +++ b/build.sh @@ -20,5 +20,7 @@ CACHEBUSTER=$(date | md5sum | cut -f1 -d' ') echo -e Cachebusting js/css URLs $CACHEBUSTER sed -i 's/app.js/app.min.js?'$CACHEBUSTER'/g' mopidy_iris/static/index.html sed -i 's/app.css/app.min.css?'$CACHEBUSTER'/g' mopidy_iris/static/index.html +sed -i 's/app.js/app.js?'$CACHEBUSTER'/g' mopidy_iris/static/test.html +sed -i 's/app.css/app.css?'$CACHEBUSTER'/g' mopidy_iris/static/test.html echo -e "\x1b[32;01m"Done!"\x1b[39;49;00m" \ No newline at end of file diff --git a/src/js/components/FollowButton.js b/src/js/components/FollowButton.js index 0e65d0a1..42e62ce2 100755 --- a/src/js/components/FollowButton.js +++ b/src/js/components/FollowButton.js @@ -30,7 +30,21 @@ class FollowButton extends React.Component{ render(){ if( !this.props.spotify_authorized || !this.props.uri ) return false - var item = this.props[helpers.uriType( this.props.uri )] + var item = {} + switch (helpers.uriType( this.props.uri )){ + case 'artist': + item = this.props.artists[this.props.uri] + break + case 'user': + item = this.props.users[this.props.uri] + break + case 'album': + item = this.props.albums[this.props.uri] + break + case 'playlist': + item = this.props.playlists[this.props.uri] + break + } if( item.following === true ){ return @@ -43,10 +57,10 @@ class FollowButton extends React.Component{ const mapStateToProps = (state, ownProps) => { return { spotify_authorized: state.spotify.authorized, - artist: state.ui.artist, - user: state.ui.user, - album: state.ui.album, - playlist: state.ui.playlist + artists: state.ui.artists, + users: state.ui.users, + albums: state.ui.albums, + playlists: state.ui.playlists } } diff --git a/src/js/components/GridItem.js b/src/js/components/GridItem.js index bd3a2d50..addbd4f2 100755 --- a/src/js/components/GridItem.js +++ b/src/js/components/GridItem.js @@ -24,6 +24,32 @@ export default class GridItem extends React.Component{ return } + renderSecondary(item){ + var output = '' + + switch (item.type){ + + case 'playlist': + return ( + + { item.tracks_total ? item.tracks_total+' tracks' : null } + { item.can_edit ? : null } + + ) + break + + default: + return ( + + { item.artists ? : null } + { item.followers ? item.followers.total.toLocaleString()+' followers' : null } + + ) + } + + return output + } + render(){ if( !this.props.item ) return null @@ -37,10 +63,7 @@ export default class GridItem extends React.Component{ { this.renderThumbnail() }
{ item.name }
- { item.artists ? : null } - { item.type == 'playlist' && item.tracks_total ? item.tracks_total+' tracks' : null } - { item.followers ? item.followers.total.toLocaleString()+' followers' : null } - { item.type == 'playlist' && item.can_edit ? : null } + { this.renderSecondary(item) }
); diff --git a/src/js/helpers.js b/src/js/helpers.js index 17cb6a8c..15e74f08 100755 --- a/src/js/helpers.js +++ b/src/js/helpers.js @@ -196,6 +196,18 @@ export let uriType = function( uri ){ } +/** + * Convert a raw URI into a object index-friendly format. Primarily used for loading local playlists + * @param $uri = string + * @return string + **/ +export let indexFriendlyUri = function (uri){ + var output = encodeURI(uri) + output = output.replace("'",'%27') + return output +} + + /** * Digest an array of objects, and pull into simple array of uris * diff --git a/src/js/services/localstorage/middleware.js b/src/js/services/localstorage/middleware.js index 435627b4..b04cc042 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 f10054fe..edc1b07f 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -304,14 +304,11 @@ const MopidyMiddleware = (function(){ var playlist = Object.assign( {}, { - images: [] - }, - { - can_edit: (response.uri.startsWith('m3u:')) - }, - response, - { - tracks: ( response.tracks ? response.tracks : [] ), + type: 'playlist', + name: response.name, + uri: response.uri, + last_modified: response.last_modified, + can_edit: (response.uri.startsWith('m3u:')), tracks_total: ( response.tracks ? response.tracks.length : 0 ) } ) @@ -328,18 +325,16 @@ const MopidyMiddleware = (function(){ .then( response => { var playlist = Object.assign( {}, - { - images: [] - }, response, { - tracks: ( response.tracks ? response.tracks : [] ), - tracks_total: ( response.tracks ? response.tracks.length : 0 ) + type: 'playlist', + tracks: ( response.tracks ? response.tracks : null ), + tracks_total: ( response.tracks ? response.tracks.length : null ) } ) // tracks? get the full track objects - if( playlist.tracks.length > 0 ) store.dispatch({ type: 'MOPIDY_RESOLVE_PLAYLIST_TRACKS', tracks: playlist.tracks }) + if( playlist.tracks.length > 0 ) store.dispatch({ type: 'MOPIDY_RESOLVE_PLAYLIST_TRACKS', tracks: playlist.tracks, uri: response.uri }) store.dispatch({ type: 'PLAYLIST_LOADED', playlist: playlist }) }) @@ -372,7 +367,7 @@ const MopidyMiddleware = (function(){ } } - store.dispatch({ type: 'PLAYLIST_TRACKS_LOADED', playlist_uri: action.playlist_uri, tracks: tracks }) + store.dispatch({ type: 'PLAYLIST_TRACKS_RESOLVED', tracks: tracks, uri: action.uri }) }) break diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 4034ae29..e6046f44 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -263,9 +263,19 @@ export function getFeaturedPlaylists(){ sendRequest( dispatch, getState, 'browse/featured-playlists?timestamp='+timestamp+'&country='+getState().spotify.country+'&limit=50&locale='+getState().spotify.locale ) .then( response => { for (var i = 0; i < response.playlists.items.length; i++){ + + var playlist = response.playlists.items[i] + Object.assign( + playlist, + { + can_edit: (getState().spotify.me && playlist.owner.id == getState().spotify.me.id), + tracks_total: playlist.tracks.total + } + ) + dispatch({ type: 'PLAYLIST_LOADED', - playlist: response.playlists.items[i] + playlist: playlist }); } dispatch({ @@ -306,18 +316,22 @@ export function getCategory( id ){ ).then( ( category_response, playlists_response ) => { - var playlists = [] for (var i = 0; i < playlists_response.playlists.items.length; i++){ - playlists.push( - Object.assign( - {}, - playlists_response.playlists.items[i], - { - tracks: false, - tracks_total: playlists_response.playlists.items[i].tracks.total - } - ) + + var playlist = Object.assign( + {}, + playlists_response.playlists.items[i], + { + tracks: null, + tracks_more: null, + tracks_total: playlists_response.playlists.items[i].tracks.total + } ) + + dispatch({ + type: 'PLAYLIST_LOADED', + playlist: playlist + }); } var category = Object.assign( @@ -325,10 +339,14 @@ export function getCategory( id ){ category_response, { items: false, - playlists: playlists + playlists: helpers.asURIs(playlists_response.playlists.items), + playlists_more: playlists_response.playlists.next, + playlists_total: playlists_response.playlists.total } ) + console.log(category) + dispatch({ type: 'SPOTIFY_CATEGORY_LOADED', data: category @@ -358,8 +376,8 @@ export function getURL( url, action_name, uri = false ){ .then( response => { dispatch({ type: action_name, - data: response, - uri: uri + uri: uri, + data: response }); }); } @@ -829,27 +847,20 @@ function loadNextPlaylistsBatch( dispatch, getState, playlists, lastResponse ){ }); }else{ - // check our editability of each playlist - // used to define what we can add tracks to - if( getState().spotify.authorized ){ - for( var i = 0; i < playlists.length; i++ ){ + for( var i = 0; i < playlists.length; i++ ){ + var playlist = Object.assign( + {}, + playlists[i], + { + can_edit: (getState().spotify.authorized && getState().spotify.me && playlists[i].owner.id == getState().spotify.me.id), + tracks_total: playlists[i].tracks.total + } + ) - var playlist = Object.assign( - {}, - playlists[i], - { - can_edit: (getState().spotify.me && playlists[i].owner.id == getState().spotify.me.id), - tracks: helpers.flattenTracks(playlists[i].tracks.items), - tracks_more: response.tracks.next, - tracks_total: response.tracks.total - } - ) - - dispatch({ - type: 'PLAYLIST_LOADED', - playlist: playlist - }); - } + dispatch({ + type: 'PLAYLIST_LOADED', + playlist: playlist + }); } dispatch({ diff --git a/src/js/services/spotify/reducer.js b/src/js/services/spotify/reducer.js index cd91ae27..9168d64f 100755 --- a/src/js/services/spotify/reducer.js +++ b/src/js/services/spotify/reducer.js @@ -156,9 +156,7 @@ export default function reducer(spotify = {}, action){ case 'SPOTIFY_CATEGORY_LOADED': return Object.assign({}, spotify, { category: action.data }) - case 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED': - return Object.assign({}, spotify, { category_playlists: action.data }) - + // TODO: update this case 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED_MORE': return Object.assign({}, spotify, { category_playlists: { href: action.data.href, diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index a7024819..f9d29fc3 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -114,9 +114,6 @@ export default function reducer(ui = {}, action){ current_track: current_track }); - case 'FOLLOWING_LOADING': - return Object.assign({}, ui, { following_loading: true }) - case 'RADIO': case 'START_RADIO': return Object.assign({}, ui, { seeds_resolved: false }, { radio: action.data.radio }) @@ -223,17 +220,25 @@ export default function reducer(ui = {}, action){ * Playlists **/ + case 'PLAYLIST_LOADED': case 'PLAYLIST_UPDATED': - var playlist = Object.assign({}, ui.playlist, action.playlist) - return Object.assign({}, ui, { playlist: playlist }) + var playlists = Object.assign([], ui.playlists) + var playlist = Object.assign({}, action.playlist) + + // if we already have one in our list, fetch it and update it + if (playlists[action.playlist.uri]){ + playlist = Object.assign({}, playlists[action.playlist.uri], action.playlist) + } + + playlists[action.playlist.uri] = playlist + return Object.assign({}, ui, { playlists: playlists }); case 'MOPIDY_PLAYLIST_LOADED': if( !action.data ) return Object.assign({}, ui, { playlist: false }) return Object.assign({}, ui, { playlist: action.data }) case 'PLAYLIST_LOADED_MORE_TRACKS': - - var playlists = ui.playlists + var playlists = Object.assign([], ui.playlists) var playlist = Object.assign( {}, playlists[action.uri], @@ -258,9 +263,12 @@ export default function reducer(ui = {}, action){ 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_RESOLVED': + var playlists = Object.assign([], ui.playlists) + var playlist = Object.assign({}, playlists[action.uri], { tracks: action.tracks }) + + playlists[action.uri] = playlist + return Object.assign({}, ui, { playlists: playlists }); case 'PLAYLIST_TRACKS_REORDERED': var snapshot_id = null @@ -283,25 +291,10 @@ export default function reducer(ui = {}, action){ return Object.assign({}, ui, { playlist: playlist }); case 'PLAYLIST_FOLLOWING_LOADED': - var playlist = Object.assign({}, ui.playlist, { following: action.is_following }) - return Object.assign({}, ui, { playlist: playlist, following_loading: false }); + var playlists = Object.assign([], ui.playlists) + var playlist = Object.assign({}, playlists[action.uri], { following: action.is_following }) - - /** - * Library Playlists - **/ - - case 'PLAYLIST_LOADED': - case 'PLAYLIST_UPDATED': - var playlists = ui.playlists - var playlist = Object.assign({}, action.playlist) - - // if we already have one in our list, fetch it and update it - if (playlists[action.playlist.uri]){ - playlist = Object.assign({}, playlists[action.playlist.uri], action.playlist) - } - - playlists[action.playlist.uri] = playlist + playlists[action.uri] = playlist return Object.assign({}, ui, { playlists: playlists }); case 'LIBRARY_PLAYLISTS_LOADED': diff --git a/src/js/views/Playlist.js b/src/js/views/Playlist.js index faa42ca4..55927ceb 100755 --- a/src/js/views/Playlist.js +++ b/src/js/views/Playlist.js @@ -173,7 +173,7 @@ class Playlist extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - playlist: state.ui.playlists[ownProps.params.uri], + playlist: state.ui.playlists[helpers.indexFriendlyUri(ownProps.params.uri)], mopidy_connected: state.mopidy.connected, spotify_authorized: state.spotify.authorized, spotify_userid: state.spotify.me.id diff --git a/src/js/views/discover/DiscoverCategory.js b/src/js/views/discover/DiscoverCategory.js index 769f1188..a22f5026 100755 --- a/src/js/views/discover/DiscoverCategory.js +++ b/src/js/views/discover/DiscoverCategory.js @@ -26,18 +26,26 @@ class DiscoverCategory extends React.Component{ } loadMore(){ - if( !this.props.spotify.new_releases || !this.props.spotify.new_releases.next ) return - this.props.spotifyActions.getURL( this.props.spotify.new_releases.next, 'SPOTIFY_NEW_RELEASES_LOADED_MORE' ); + if( !this.props.playlists_next || !this.props.playlists_next ) return + this.props.spotifyActions.getURL( this.props.playlists_next, 'SPOTIFY_NEW_RELEASES_LOADED_MORE' ); } render(){ - if( !this.props.spotify.category ) return null; + if (!this.props.category) return null + + var playlists = [] + for (var i = 0; i < this.props.category.playlists.length; i++){ + var uri = this.props.category.playlists[i] + if (this.props.playlists.hasOwnProperty(uri)){ + playlists.push(this.props.playlists[uri]) + } + } return (
-
+
- { this.props.spotify.category.playlists ? : null } +
this.loadMore() }/>
@@ -53,7 +61,10 @@ class DiscoverCategory extends React.Component{ **/ const mapStateToProps = (state, ownProps) => { - return state; + return { + playlists: state.ui.playlists, + category: state.spotify.category + } } const mapDispatchToProps = (dispatch) => { diff --git a/src/js/views/library/LibraryPlaylists.js b/src/js/views/library/LibraryPlaylists.js index f4f2ef00..8d8273a5 100755 --- a/src/js/views/library/LibraryPlaylists.js +++ b/src/js/views/library/LibraryPlaylists.js @@ -67,7 +67,7 @@ class LibraryPlaylists extends React.Component{ { width: 10, label: 'Tracks', - name: 'tracks.total' + name: 'tracks_total' } ] return ( From 74b7bf86756f4b7bb4eddedf8fa1a43c0befc992 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Sat, 14 Jan 2017 09:12:53 +1300 Subject: [PATCH 3/9] Managing playlist tracks --- src/js/components/List.js | 2 +- src/js/services/localstorage/middleware.js | 2 +- src/js/services/mopidy/middleware.js | 15 +++++++------- src/js/services/spotify/actions.js | 12 +++++++---- src/js/services/spotify/middleware.js | 4 +--- src/js/services/ui/actions.js | 16 +++++++-------- src/js/services/ui/reducer.js | 24 ++++++++++++++-------- 7 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/js/components/List.js b/src/js/components/List.js index 04f99e6d..a24303bc 100755 --- a/src/js/components/List.js +++ b/src/js/components/List.js @@ -18,7 +18,7 @@ export default class List extends React.Component{ // make sure we haven't clicked a nested link (ie Artist name) if( e.target.tagName.toLowerCase() !== 'a' ){ - hashHistory.push( this.props.link_prefix + encodeURIComponent(uri) ); + hashHistory.push( this.props.link_prefix + uri ); } } diff --git a/src/js/services/localstorage/middleware.js b/src/js/services/localstorage/middleware.js index b04cc042..435627b4 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 edc1b07f..7a75229b 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -367,13 +367,13 @@ const MopidyMiddleware = (function(){ } } - store.dispatch({ type: 'PLAYLIST_TRACKS_RESOLVED', tracks: tracks, uri: action.uri }) + store.dispatch({ type: 'PLAYLIST_TRACKS', tracks: tracks, uri: action.uri }) }) break case 'MOPIDY_ADD_PLAYLIST_TRACKS': - instruct( socket, store, 'playlists.lookup', { uri: action.playlist_uri }) + instruct( socket, store, 'playlists.lookup', { uri: action.uri }) .then( response => { var tracks = []; for( var i = 0; i < action.tracks_uris.length; i++ ){ @@ -392,7 +392,7 @@ const MopidyMiddleware = (function(){ instruct( socket, store, 'playlists.save', { playlist: playlist } ) .then( response => { - store.dispatch({ type: 'PLAYLIST_TRACKS_ADDED', tracks_uris: action.tracks_uris }); + store.dispatch({ type: 'PLAYLIST_TRACKS_ADDED', uri: action.uri, tracks_uris: action.tracks_uris }); }) }); break @@ -406,7 +406,7 @@ const MopidyMiddleware = (function(){ var indexes = Object.assign([], action.tracks_indexes) indexes.sort(descending); - instruct( socket, store, 'playlists.lookup', { uri: action.playlist_uri }) + instruct( socket, store, 'playlists.lookup', { uri: action.uri }) .then( response => { var playlist = Object.assign({}, response) for( var i = 0; i < indexes.length; i++ ){ @@ -414,7 +414,7 @@ const MopidyMiddleware = (function(){ } instruct( socket, store, 'playlists.save', { playlist: playlist } ) .then( response => { - store.dispatch({ type: 'PLAYLIST_TRACKS_REMOVED', tracks_indexes: action.tracks_indexes }); + store.dispatch({ type: 'PLAYLIST_TRACKS_REMOVED', uri: action.uri, tracks_indexes: action.tracks_indexes }); }) }); break @@ -425,7 +425,7 @@ const MopidyMiddleware = (function(){ var playlist = Object.assign({}, response, { name: action.name }) instruct( socket, store, 'playlists.save', { playlist: playlist } ) .then( response => { - store.dispatch({ type: 'PLAYLIST_UPDATED', playlist: playlist }) + store.dispatch({ type: 'PLAYLIST_UPDATED', uri: action.uri, playlist: playlist }) }) }); break @@ -464,8 +464,7 @@ const MopidyMiddleware = (function(){ instruct( socket, store, 'playlists.save', { playlist: playlist } ) .then( response => { - // and now re-render our full track references - store.dispatch({ type: 'MOPIDY_PLAYLIST_TRACKS', tracks: playlist.tracks }) + store.dispatch({ type: 'MOPIDY_RESOLVE_PLAYLIST_TRACKS', tracks: playlist.tracks, uri: playlist.uri }) }) }); break diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index e6046f44..9b3db75a 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -345,8 +345,6 @@ export function getCategory( id ){ } ) - console.log(category) - dispatch({ type: 'SPOTIFY_CATEGORY_LOADED', data: category @@ -857,6 +855,9 @@ function loadNextPlaylistsBatch( dispatch, getState, playlists, lastResponse ){ } ) + // remove our tracklist. It'll overwrite any full records otherwise + delete playlist.tracks + dispatch({ type: 'PLAYLIST_LOADED', playlist: playlist @@ -898,12 +899,13 @@ export function toggleFollowingPlaylist( uri, method ){ } } -export function addTracksToPlaylist( playlist_uri, tracks_uris ){ +export function addTracksToPlaylist( uri, tracks_uris ){ return (dispatch, getState) => { - sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',playlist_uri) + '/playlists/'+ helpers.getFromUri('playlistid',playlist_uri) + '/tracks', 'POST', { uris: tracks_uris } ) + sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) + '/playlists/'+ helpers.getFromUri('playlistid',uri) + '/tracks', 'POST', { uris: tracks_uris } ) .then( response => { dispatch({ type: 'PLAYLIST_TRACKS_ADDED', + uri: uri, tracks_uris: tracks_uris, snapshot_id: response.snapshot_id }); @@ -917,6 +919,7 @@ export function deleteTracksFromPlaylist( uri, snapshot_id, tracks_indexes ){ .then( response => { dispatch({ type: 'PLAYLIST_TRACKS_REMOVED', + uri: uri, tracks_indexes: tracks_indexes, snapshot_id: response.snapshot_id }); @@ -930,6 +933,7 @@ export function reorderPlaylistTracks( uri, range_start, range_length, insert_be .then( response => { dispatch({ type: 'PLAYLIST_TRACKS_REORDERED', + uri: uri, range_start: range_start, range_length: range_length, insert_before: insert_before, diff --git a/src/js/services/spotify/middleware.js b/src/js/services/spotify/middleware.js index 0ead3434..4a078ae8 100755 --- a/src/js/services/spotify/middleware.js +++ b/src/js/services/spotify/middleware.js @@ -18,8 +18,6 @@ const SpotifyMiddleware = (function(){ break case 'SPOTIFY_CREATE_PLAYLIST': - var playlist = state.ui.playlist - if( !store.getState().spotify.authorized ){ store.dispatch( uiActions.createNotification( "Must be logged in to Spotify to do that", 'bad' ) ) return @@ -28,7 +26,7 @@ const SpotifyMiddleware = (function(){ break case 'SPOTIFY_REMOVE_PLAYLIST_TRACKS': - var playlist = state.ui.playlist + var playlist = state.ui.playlists[action.uri] if( !store.getState().spotify.authorized ){ store.dispatch( uiActions.createNotification( "Must be logged in to Spotify to do that", 'bad' ) ) diff --git a/src/js/services/ui/actions.js b/src/js/services/ui/actions.js index 5fed3166..64a73951 100755 --- a/src/js/services/ui/actions.js +++ b/src/js/services/ui/actions.js @@ -147,39 +147,39 @@ export function createPlaylist( scheme, name, is_public = false ){ return false } -export function removeTracksFromPlaylist( playlist_uri, tracks_indexes ){ - switch( helpers.uriSource( playlist_uri ) ){ +export function removeTracksFromPlaylist( uri, tracks_indexes ){ + switch( helpers.uriSource( uri ) ){ case 'spotify': return { type: 'SPOTIFY_REMOVE_PLAYLIST_TRACKS', - playlist_uri: playlist_uri, + uri: uri, tracks_indexes: tracks_indexes } case 'm3u': return { type: 'MOPIDY_REMOVE_PLAYLIST_TRACKS', - playlist_uri: playlist_uri, + uri: uri, tracks_indexes: tracks_indexes } } } -export function addTracksToPlaylist( playlist_uri, tracks_uris ){ - switch( helpers.uriSource( playlist_uri ) ){ +export function addTracksToPlaylist( uri, tracks_uris ){ + switch( helpers.uriSource( uri ) ){ case 'spotify': return { type: 'SPOTIFY_ADD_PLAYLIST_TRACKS', - playlist_uri: playlist_uri, + uri: uri, tracks_uris: tracks_uris } case 'm3u': return { type: 'MOPIDY_ADD_PLAYLIST_TRACKS', - playlist_uri: playlist_uri, + uri: uri, tracks_uris: tracks_uris } } diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index f9d29fc3..9b62a0b6 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -253,17 +253,20 @@ export default function reducer(ui = {}, action){ return Object.assign({}, ui, { playlists: playlists }); case 'PLAYLIST_TRACKS_REMOVED': - var tracks = Object.assign([], ui.playlist.tracks) + var playlists = Object.assign([], ui.playlists) + var playlist = Object.assign({}, playlists[action.uri]) + var tracks = Object.assign([], playlist.tracks) 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 }); + Object.assign(playlist, { tracks: tracks, snapshot_id: snapshot_id }) + playlists[action.uri] = playlist + return Object.assign({}, ui, { playlists: playlists }); - case 'PLAYLIST_TRACKS_RESOLVED': + case 'PLAYLIST_TRACKS': var playlists = Object.assign([], ui.playlists) var playlist = Object.assign({}, playlists[action.uri], { tracks: action.tracks }) @@ -271,9 +274,9 @@ export default function reducer(ui = {}, action){ return Object.assign({}, ui, { playlists: playlists }); case 'PLAYLIST_TRACKS_REORDERED': - var snapshot_id = null - if( action.snapshot_id ) snapshot_id = action.snapshot_id - var tracks = Object.assign([], ui.playlist.tracks) + var playlists = Object.assign([], ui.playlists) + var playlist = Object.assign({}, playlists[action.uri]) + var tracks = Object.assign([], playlist.tracks) // handle insert_before offset if we're moving BENEATH where we're slicing tracks var insert_before = action.insert_before @@ -287,8 +290,11 @@ export default function reducer(ui = {}, action){ tracks.splice(insert_before, 0, tracks_to_move[i]) } - var playlist = Object.assign({}, ui.playlist, { snapshot_id: snapshot_id, tracks: tracks }) - return Object.assign({}, ui, { playlist: playlist }); + var snapshot_id = null + if( action.snapshot_id ) snapshot_id = action.snapshot_id + Object.assign(playlist, { tracks: tracks, snapshot_id: snapshot_id }) + playlists[action.uri] = playlist + return Object.assign({}, ui, { playlists: playlists }); case 'PLAYLIST_FOLLOWING_LOADED': var playlists = Object.assign([], ui.playlists) From 539748b11202cb157e6b0dc95990cfdd6cba4060 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Sat, 14 Jan 2017 10:03:45 +1300 Subject: [PATCH 4/9] Artists and albums; Not lazy-loading yet --- src/js/services/localstorage/middleware.js | 2 +- src/js/services/spotify/actions.js | 101 +++++++++++++++------ src/js/services/spotify/reducer.js | 4 +- src/js/services/ui/reducer.js | 26 +++++- src/js/views/Album.js | 18 +++- src/js/views/Artist.js | 30 +++++- 6 files changed, 140 insertions(+), 41 deletions(-) diff --git a/src/js/services/localstorage/middleware.js b/src/js/services/localstorage/middleware.js index 435627b4..b04cc042 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/spotify/actions.js b/src/js/services/spotify/actions.js index 9b3db75a..2df68464 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -586,9 +586,6 @@ export function getDiscover(){ export function getArtist( uri ){ return (dispatch, getState) => { - // flush out the previous store value - dispatch({ type: 'SPOTIFY_ARTIST_LOADED', data: false }); - var artist = {}; // get both the artist and the top tracks @@ -606,18 +603,30 @@ export function getArtist( uri ){ sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/related-artists' ) .then( response => { - Object.assign(artist, { related_artists: response.artists }); + for (var i = 0; i < response.artists.length; i++){ + dispatch({ + type: 'ARTIST_LOADED', + artist: response.artists[i] + }); + } + Object.assign(artist, { related_artists: helpers.asURIs(response.artists) }); }), sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/albums' ) .then( response => { - Object.assign(artist, { albums: response.items, albums_more: response.next }); + for (var i = 0; i < response.items.length; i++){ + dispatch({ + type: 'ALBUM_LOADED', + album: response.items[i] + }); + } + Object.assign(artist, { albums: helpers.asURIs(response.items), albums_more: response.next }); }) ).then( () => { dispatch({ - type: 'SPOTIFY_ARTIST_LOADED', - data: artist + type: 'ARTIST_LOADED', + artist: artist }); }); } @@ -626,9 +635,6 @@ export function getArtist( uri ){ export function getArtists( uris ){ return (dispatch, getState) => { - // flush out the previous store value - dispatch({ type: 'SPOTIFY_ARTISTS_LOADED', data: false }); - // now get all the artists for this album (full objects) var ids = ''; for( var i = 0; i < uris.length; i++ ){ @@ -638,10 +644,21 @@ export function getArtists( uris ){ sendRequest( dispatch, getState, 'artists/?ids='+ids ) .then( response => { - dispatch({ - type: 'SPOTIFY_ARTISTS_LOADED', - data: response - }); + for (var i = i; i < response.length; i++){ + var artist = response + for (var i = 0; i < artist.albums.length; i++){ + dispatch({ + type: 'ALBUM_LOADED', + album: artist.albums[i] + }); + } + artist.albums = helpers.asURIs(artist.albums) + artist.albums_more = artist.albums.next + dispatch({ + type: 'ARTIST_LOADED', + artist: artist + }); + } }); } } @@ -654,9 +671,25 @@ export function getLibraryArtists(){ sendRequest( dispatch, getState, 'me/following?type=artist&limit=50' ) .then( response => { + for (var i = 0; i < response.length; i++){ + var artist = response[i] + for (var i = 0; i < artist.albums.length; i++){ + dispatch({ + type: 'ALBUM_LOADED', + album: artist.albums[i] + }); + } + artist.albums = helpers.asURIs(artist.albums) + artist.albums_more = artist.albums.next + dispatch({ + type: 'ARTIST_LOADED', + artist: artist + }); + } + dispatch({ - type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', - data: response + type: 'LIBRARY_ARTISTS_LOADED', + uris: helpers.asURIs(response) }); }); } @@ -714,30 +747,42 @@ export function getUser( uri ){ export function getAlbum( uri ){ return (dispatch, getState) => { - // flush out the previous store value - dispatch({ type: 'SPOTIFY_ALBUM_LOADED', data: false }); - // get the album sendRequest( dispatch, getState, 'albums/'+ helpers.getFromUri('albumid', uri) ) .then( response => { - var album = response + var album = Object.assign( + {}, + response, + { + artists: helpers.asURIs(response.artists), + tracks: response.tracks.items, + tracks_more: response.tracks.next, + tracks_total: response.tracks.total + } + ) + + dispatch({ + type: 'ALBUM_LOADED', + album: album + }); // now get all the artists for this album (full objects) // we do this to get the artist artwork var artist_ids = []; - for( var i = 0; i < album.artists.length; i++ ){ - artist_ids.push( helpers.getFromUri( 'artistid', album.artists[i].uri ) ) + for( var i = 0; i < response.artists.length; i++ ){ + artist_ids.push( helpers.getFromUri( 'artistid', response.artists[i].uri ) ) } - // get all album artists + // get all album artists as full objects sendRequest( dispatch, getState, 'artists/?ids='+artist_ids ) .then( response => { - Object.assign(album, { artists: response.artists }) - dispatch({ - type: 'SPOTIFY_ALBUM_LOADED', - data: album - }); + for (var i = 0; i < response.artists.length; i++){ + dispatch({ + type: 'ARTIST_LOADED', + artist: response.artists[i] + }); + } }); }) diff --git a/src/js/services/spotify/reducer.js b/src/js/services/spotify/reducer.js index 9168d64f..ea3d776a 100755 --- a/src/js/services/spotify/reducer.js +++ b/src/js/services/spotify/reducer.js @@ -60,7 +60,7 @@ export default function reducer(spotify = {}, action){ case 'SPOTIFY_ME_LOADED': return Object.assign({}, spotify, { me: action.data }) - +/* case 'SPOTIFY_ARTISTS_LOADED': if( !action.data ) return Object.assign({}, spotify) return Object.assign({}, spotify, { @@ -79,7 +79,7 @@ export default function reducer(spotify = {}, action){ return Object.assign({}, spotify, { library_artists: [ ...spotify.library_artists, ...action.data.artists.items ], library_artists_more: action.data.artists.next - }) + })*/ case 'SPOTIFY_LIBRARY_ALBUMS_LOADED': if( !action.data ) return Object.assign({}, spotify) diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index 9b62a0b6..bd0509ba 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -129,6 +129,18 @@ export default function reducer(ui = {}, action){ * Albums **/ + case 'ALBUM_LOADED': + var albums = Object.assign([], ui.albums) + var album = Object.assign({}, action.album) + + // if we already have one in our list, fetch it and update it + if (albums[action.album.uri]){ + album = Object.assign({}, albums[action.album.uri], action.album) + } + + albums[action.album.uri] = album + return Object.assign({}, ui, { albums: albums }); + case 'MOPIDY_ALBUM_LOADED': if( !action.data ) return Object.assign({}, ui, { album: false }) return Object.assign({}, ui, { album: action.data }); @@ -166,9 +178,17 @@ export default function reducer(ui = {}, action){ * Artists **/ - case 'MOPIDY_ARTIST_LOADED': - if( !action.data ) return Object.assign({}, ui, { artist: false }) - return Object.assign({}, ui, { artist: action.data }) + case 'ARTIST_LOADED': + var artists = Object.assign([], ui.artists) + var artist = Object.assign({}, action.artist) + + // if we already have one in our list, fetch it and update it + if (artists[action.artist.uri]){ + artist = Object.assign({}, artists[action.artist.uri], action.artist) + } + + artists[action.artist.uri] = artist + return Object.assign({}, ui, { artists: artists }); case 'LASTFM_ARTIST_LOADED': if( !action.data.image ) return ui diff --git a/src/js/views/Album.js b/src/js/views/Album.js index 0b402ad3..4e4e5cc8 100755 --- a/src/js/views/Album.js +++ b/src/js/views/Album.js @@ -68,12 +68,22 @@ class Album extends React.Component{ render(){ if( !this.props.album ) return null + var artists = [] + if (this.props.album.artists){ + for (var i = 0; i < this.props.album.artists.length; i++){ + var uri = this.props.album.artists[i] + if (this.props.artists.hasOwnProperty(uri)){ + artists.push(this.props.artists[uri]) + } + } + } + return (
- +
@@ -93,7 +103,7 @@ class Album extends React.Component{

{ this.props.album.name }

-

+

@@ -116,7 +126,9 @@ class Album extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - album: state.ui.album, + artists: state.ui.artists, + album: state.ui.albums[ownProps.params.uri], + albums: state.ui.albums, spotify_authorized: state.spotify.authorized, mopidy_connected: state.mopidy.connected }; diff --git a/src/js/views/Artist.js b/src/js/views/Artist.js index 08927b09..85b9d10b 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -95,12 +95,32 @@ class Artist extends React.Component{ renderBody(){ + var related_artists = [] + if (this.props.artist.related_artists){ + for (var i = 0; i < this.props.artist.related_artists.length; i++){ + var uri = this.props.artist.related_artists[i] + if (this.props.artists.hasOwnProperty(uri)){ + related_artists.push(this.props.artists[uri]) + } + } + } + + var albums = [] + if (this.props.artist.albums){ + for (var i = 0; i < this.props.artist.albums.length; i++){ + var uri = this.props.artist.albums[i] + if (this.props.albums.hasOwnProperty(uri)){ + albums.push(this.props.albums[uri]) + } + } + } + if( this.state.sub_view == 'related_artists' ){ return (

Related artists

- { this.props.artist.related_artists ? : null } +
) @@ -129,14 +149,14 @@ class Artist extends React.Component{

Related artists

- { this.props.artist.related_artists ? : null } +

Albums

- { this.props.artist.albums ? : null } + this.loadMore() }/>
@@ -203,7 +223,9 @@ class Artist extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - artist: state.ui.artist, + artists: state.ui.artists, + artist: state.ui.artists[ownProps.params.uri], + albums: state.ui.albums, spotify_authorized: state.spotify.authorized, mopidy_connected: state.mopidy.connected } From ce4c26f18b59cae8f24768e80e308f0723b3c11a Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Sat, 14 Jan 2017 22:32:39 +1300 Subject: [PATCH 5/9] Bunched action calls; Library artists and albums --- src/js/bootstrap.js | 4 -- src/js/components/ArtistSentence.js | 1 + src/js/services/spotify/actions.js | 39 ++++------------ src/js/services/spotify/middleware.js | 45 +++++++++++++++++++ src/js/services/ui/reducer.js | 62 ++++++++++++++++++++++++++ src/js/views/Album.js | 3 ++ src/js/views/Artist.js | 2 +- src/js/views/library/LibraryAlbums.js | 33 +++++++++----- src/js/views/library/LibraryArtists.js | 34 ++++++++------ 9 files changed, 164 insertions(+), 59 deletions(-) diff --git a/src/js/bootstrap.js b/src/js/bootstrap.js index e3611045..2ba1661b 100755 --- a/src/js/bootstrap.js +++ b/src/js/bootstrap.js @@ -65,10 +65,6 @@ var initialState = { playlists: [], tracks: [] }, - library_artists: [], - artists: [], - library_albums: [], - albums: [], library_playlists: [], playlists: [], notifications: [] diff --git a/src/js/components/ArtistSentence.js b/src/js/components/ArtistSentence.js index dfe527c6..f7e86273 100755 --- a/src/js/components/ArtistSentence.js +++ b/src/js/components/ArtistSentence.js @@ -16,6 +16,7 @@ export default class ArtistSentence extends React.Component{ { this.props.artists.map( (artist, index) => { + if (!artist) return - var separator = null; if( index == this.props.artists.length - 2 ){ separator = ' and '; diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 2df68464..860f45f3 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -666,31 +666,12 @@ export function getArtists( uris ){ export function getLibraryArtists(){ return (dispatch, getState) => { - - dispatch({ type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', data: false }); - sendRequest( dispatch, getState, 'me/following?type=artist&limit=50' ) .then( response => { - for (var i = 0; i < response.length; i++){ - var artist = response[i] - for (var i = 0; i < artist.albums.length; i++){ - dispatch({ - type: 'ALBUM_LOADED', - album: artist.albums[i] - }); - } - artist.albums = helpers.asURIs(artist.albums) - artist.albums_more = artist.albums.next - dispatch({ - type: 'ARTIST_LOADED', - artist: artist - }); - } - dispatch({ - type: 'LIBRARY_ARTISTS_LOADED', - uris: helpers.asURIs(response) - }); + type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', + data: response + }) }); } } @@ -777,12 +758,10 @@ export function getAlbum( uri ){ // get all album artists as full objects sendRequest( dispatch, getState, 'artists/?ids='+artist_ids ) .then( response => { - for (var i = 0; i < response.artists.length; i++){ - dispatch({ - type: 'ARTIST_LOADED', - artist: response.artists[i] - }); - } + dispatch({ + type: 'ARTISTS_LOADED', + artists: response.artists + }); }); }) @@ -792,14 +771,14 @@ export function getAlbum( uri ){ export function getLibraryAlbums(){ return (dispatch, getState) => { - dispatch({ type: 'SPOTIFY_LIBRARY_ALBUMS_LOADED', data: false }); + dispatch({ type: 'LIBRARY_ALBUMS_LOADED', uris: false }); sendRequest( dispatch, getState, 'me/albums?limit=40' ) .then( response => { dispatch({ type: 'SPOTIFY_LIBRARY_ALBUMS_LOADED', data: response - }); + }) }); } } diff --git a/src/js/services/spotify/middleware.js b/src/js/services/spotify/middleware.js index 4a078ae8..b8bb009e 100755 --- a/src/js/services/spotify/middleware.js +++ b/src/js/services/spotify/middleware.js @@ -93,6 +93,51 @@ const SpotifyMiddleware = (function(){ } break + case 'SPOTIFY_LIBRARY_ARTISTS_LOADED': + store.dispatch({ + type: 'ARTISTS_LOADED', + artists: action.data.artists.items + }); + store.dispatch({ + type: 'LIBRARY_ARTISTS_LOADED', + uris: helpers.asURIs(action.data.artists.items), + more: action.data.artists.next, + total: action.data.artists.total + }); + break + + case 'SPOTIFY_LIBRARY_ALBUMS_LOADED': + + var albums = [] + + for (var i = 0; i < action.data.items.length; i++){ + albums.push( + Object.assign( + {}, + action.data.items[i].album, + { + added_at: action.data.items[i].added_at, + tracks: action.data.items[i].album.tracks.items, + tracks_next: action.data.items[i].album.tracks.tracks_next, + tracks_total: action.data.items[i].album.tracks.tracks_total + } + ) + ) + } + + store.dispatch({ + type: 'ALBUMS_LOADED', + albums: albums + }); + + store.dispatch({ + type: 'LIBRARY_ALBUMS_LOADED', + uris: helpers.asURIs(albums), + more: action.data.next, + total: action.data.total + }); + break + // This action is irrelevant to us, pass it on to the next middleware default: return next(action); diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index bd0509ba..9defb420 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -129,6 +129,19 @@ export default function reducer(ui = {}, action){ * Albums **/ + case 'ALBUMS_LOADED': + var albums = Object.assign([], ui.albums) + + for (var i = 0; i < action.albums.length; i++){ + var album = action.albums[i] + if (typeof(albums[album.uri]) !== 'undefined'){ + artist = Object.assign({}, albums[album.uri], album) + } + albums[album.uri] = album + } + + return Object.assign({}, ui, { albums: albums }); + case 'ALBUM_LOADED': var albums = Object.assign([], ui.albums) var album = Object.assign({}, action.album) @@ -172,12 +185,43 @@ export default function reducer(ui = {}, action){ var album = Object.assign({}, ui.album, { following: action.is_following }) return Object.assign({}, ui, { album: album, following_loading: false }); + case 'LIBRARY_ALBUMS_LOADED': + if (!action.uris){ + return Object.assign({}, ui, { + library_albums: null, + library_albums_more: null, + library_albums_total: null + }); + } + + var library_albums = [] + if (ui.library_albums) library_albums = Object.assign([], ui.library_albums) + + return Object.assign({}, ui, { + library_albums: [...library_albums, ...action.uris], + library_albums_more: action.more, + library_albums_total: action.total + }); + /** * Artists **/ + case 'ARTISTS_LOADED': + var artists = Object.assign([], ui.artists) + + for (var i = 0; i < action.artists.length; i++){ + var artist = action.artists[i] + if (typeof(artists[artist.uri]) !== 'undefined'){ + artist = Object.assign({}, artists[artist.uri], artist) + } + artists[artist.uri] = artist + } + + return Object.assign({}, ui, { artists: artists }); + case 'ARTIST_LOADED': var artists = Object.assign([], ui.artists) var artist = Object.assign({}, action.artist) @@ -215,6 +259,24 @@ export default function reducer(ui = {}, action){ var artist = Object.assign({}, ui.artist, { following: action.is_following }) return Object.assign({}, ui, { artist: artist, following_loading: false }); + case 'LIBRARY_ARTISTS_LOADED': + if (!action.uris){ + return Object.assign({}, ui, { + library_artists: null, + library_artists_more: null, + library_artists_total: null + }); + } + + var library_artists = [] + if (ui.library_artists) library_artists = Object.assign([], ui.library_artists) + + return Object.assign({}, ui, { + library_artists: [...library_artists, ...action.uris], + library_artists_more: action.more, + library_artists_total: action.total + }); + /** * User diff --git a/src/js/views/Album.js b/src/js/views/Album.js index 4e4e5cc8..5054565e 100755 --- a/src/js/views/Album.js +++ b/src/js/views/Album.js @@ -43,6 +43,9 @@ class Album extends React.Component{ } loadAlbum( props = this.props ){ + // if we've already loaded the full version of this album, halt! + if (this.props.album && this.props.album.tracks) return false + switch( helpers.uriSource( props.params.uri ) ){ case 'spotify': diff --git a/src/js/views/Artist.js b/src/js/views/Artist.js index 85b9d10b..90d1680a 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -223,8 +223,8 @@ class Artist extends React.Component{ const mapStateToProps = (state, ownProps) => { return { + artist: (state.ui.artists && typeof(state.ui.artists[ownProps.params.uri]) !== 'undefined' ? state.ui.artists[ownProps.params.uri] : false ), artists: state.ui.artists, - artist: state.ui.artists[ownProps.params.uri], albums: state.ui.albums, spotify_authorized: state.spotify.authorized, mopidy_connected: state.mopidy.connected diff --git a/src/js/views/library/LibraryAlbums.js b/src/js/views/library/LibraryAlbums.js index b3f731c7..2a383fb2 100755 --- a/src/js/views/library/LibraryAlbums.js +++ b/src/js/views/library/LibraryAlbums.js @@ -24,16 +24,16 @@ class LibraryAlbums extends React.Component{ } componentDidMount(){ - this.props.spotifyActions.getLibraryAlbums(); + if (!this.props.library_albums) this.props.spotifyActions.getLibraryAlbums(); } loadMore(){ - if( !this.props.albums_more ) return - this.props.spotifyActions.getURL( this.props.albums_more, 'SPOTIFY_LIBRARY_ALBUMS_LOADED_MORE' ); + if( !this.props.library_albums_more ) return + this.props.spotifyActions.getURL( this.props.library_albums_more, 'SPOTIFY_LIBRARY_ALBUMS_LOADED' ); } - renderView(){ - if( !this.props.albums ) return null + renderView(albums){ + if (!albums || albums.length <= 0) return null if( this.props.view == 'list' ){ var columns = [ @@ -60,20 +60,20 @@ class LibraryAlbums extends React.Component{ ] return (
- +
) }else if( this.props.view == 'thumbnails' ){ return (
- { this.props.albums ? : null } +
) }else{ return (
{ - this.props.albums.map( album => { + albums.map( album => { return (
@@ -96,6 +96,16 @@ class LibraryAlbums extends React.Component{ } render(){ + var albums = [] + + if (this.props.library_albums && this.props.albums){ + for (var i = 0; i < this.props.library_albums.length; i++){ + var uri = this.props.library_albums[i] + if (this.props.albums.hasOwnProperty(uri)){ + albums.push(this.props.albums[uri]) + } + } + } var view_options = [ { @@ -119,7 +129,7 @@ class LibraryAlbums extends React.Component{ return (
- { this.renderView() } + { this.renderView(albums) } this.loadMore() }/>
); @@ -136,8 +146,9 @@ class LibraryAlbums extends React.Component{ const mapStateToProps = (state, ownProps) => { return { view: state.ui.library_albums_view, - albums: state.spotify.library_albums, - albums_more: state.spotify.library_albums_more, + albums: state.ui.albums, + library_albums: state.ui.library_albums, + library_albums_more: state.ui.library_albums_more, } } diff --git a/src/js/views/library/LibraryArtists.js b/src/js/views/library/LibraryArtists.js index cad25853..9fa69475 100755 --- a/src/js/views/library/LibraryArtists.js +++ b/src/js/views/library/LibraryArtists.js @@ -20,19 +20,16 @@ class LibraryArtists extends React.Component{ super(props); } - // on render componentDidMount(){ - this.props.spotifyActions.getLibraryArtists(); + if (!this.props.library_artists) this.props.spotifyActions.getLibraryArtists(); } loadMore(){ - if( !this.props.artists_more ) return - this.props.spotifyActions.getURL( this.props.artists_more, 'SPOTIFY_LIBRARY_ARTISTS_LOADED_MORE' ); + if( !this.props.library_artists_more ) return + this.props.spotifyActions.getURL( this.props.library_artists_more, 'SPOTIFY_LIBRARY_ARTISTS_LOADED' ); } - renderView(){ - if( !this.props.artists ) return null - + renderView(artists){ if( this.props.view == 'list' ){ var columns = [ { @@ -48,13 +45,13 @@ class LibraryArtists extends React.Component{ ] return (
- +
) }else{ return (
- +
) } @@ -62,6 +59,16 @@ class LibraryArtists extends React.Component{ render(){ + var artists = [] + if (this.props.library_artists && this.props.artists){ + for (var i = 0; i < this.props.library_artists.length; i++){ + var uri = this.props.library_artists[i] + if (this.props.artists.hasOwnProperty(uri)){ + artists.push(this.props.artists[uri]) + } + } + } + var view_options = [ { value: 'thumbnails', @@ -80,7 +87,7 @@ class LibraryArtists extends React.Component{ return (
- { this.renderView() } + { this.renderView(artists) } this.loadMore() }/>
); @@ -96,9 +103,10 @@ class LibraryArtists extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - view: state.ui.library_artists_view, - artists: state.spotify.library_artists, - artists_more: state.spotify.library_artists_more + artists: state.ui.artists, + library_artists: state.ui.library_artists, + library_artists_more: state.ui.library_artists_more, + view: state.ui.library_artists_view } } From 8b15e024cd1b94b118ee49a6c7f8c1530dc17fc2 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Sun, 15 Jan 2017 11:40:08 +1300 Subject: [PATCH 6/9] Following; PLAYLISTS_LOADED; Full index --- src/js/components/ArtistSentence.js | 5 +- src/js/components/FollowButton.js | 23 +--- src/js/services/mopidy/middleware.js | 24 +++- src/js/services/spotify/actions.js | 89 ++++++------- src/js/services/spotify/middleware.js | 88 ++++++++++++- src/js/services/ui/reducer.js | 126 ++++++++++--------- src/js/views/Album.js | 21 ++-- src/js/views/Artist.js | 34 +++-- src/js/views/Playlist.js | 2 +- src/js/views/discover/DiscoverNewReleases.js | 26 +++- src/js/views/library/LibraryAlbums.js | 2 +- 11 files changed, 269 insertions(+), 171 deletions(-) diff --git a/src/js/components/ArtistSentence.js b/src/js/components/ArtistSentence.js index f7e86273..74677d94 100755 --- a/src/js/components/ArtistSentence.js +++ b/src/js/components/ArtistSentence.js @@ -23,8 +23,9 @@ export default class ArtistSentence extends React.Component{ }else if( index < this.props.artists.length - 2 ){ separator = ', '; } - if( !artist.name ) artist.name = '-' - if( artist.uri ){ + if (!artist.name){ + var content = - + } else if (artist.uri){ var content = { artist.name } }else{ var content = { artist.name } diff --git a/src/js/components/FollowButton.js b/src/js/components/FollowButton.js index 42e62ce2..ecf4707d 100755 --- a/src/js/components/FollowButton.js +++ b/src/js/components/FollowButton.js @@ -30,23 +30,8 @@ class FollowButton extends React.Component{ render(){ if( !this.props.spotify_authorized || !this.props.uri ) return false - var item = {} - switch (helpers.uriType( this.props.uri )){ - case 'artist': - item = this.props.artists[this.props.uri] - break - case 'user': - item = this.props.users[this.props.uri] - break - case 'album': - item = this.props.albums[this.props.uri] - break - case 'playlist': - item = this.props.playlists[this.props.uri] - break - } - if( item.following === true ){ + if( this.props.is_following === true ){ return }else{ return @@ -56,11 +41,7 @@ class FollowButton extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - spotify_authorized: state.spotify.authorized, - artists: state.ui.artists, - users: state.ui.users, - albums: state.ui.albums, - playlists: state.ui.playlists + spotify_authorized: state.spotify.authorized } } diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index 7a75229b..3bfb2777 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -313,7 +313,11 @@ const MopidyMiddleware = (function(){ } ) - store.dispatch({ type: 'PLAYLIST_LOADED', playlist: playlist }) + store.dispatch({ + type: 'PLAYLIST_LOADED', + uri: playlist.uri, + playlist: playlist + }) }) } }) @@ -336,7 +340,11 @@ const MopidyMiddleware = (function(){ // tracks? get the full track objects if( playlist.tracks.length > 0 ) store.dispatch({ type: 'MOPIDY_RESOLVE_PLAYLIST_TRACKS', tracks: playlist.tracks, uri: response.uri }) - store.dispatch({ type: 'PLAYLIST_LOADED', playlist: playlist }) + store.dispatch({ + type: 'PLAYLIST_LOADED', + uri: playlist.uri, + playlist: playlist + }) }) break; @@ -367,7 +375,11 @@ const MopidyMiddleware = (function(){ } } - store.dispatch({ type: 'PLAYLIST_TRACKS', tracks: tracks, uri: action.uri }) + store.dispatch({ + type: 'PLAYLIST_TRACKS', + tracks: tracks, + uri: action.uri + }) }) break @@ -425,7 +437,11 @@ const MopidyMiddleware = (function(){ var playlist = Object.assign({}, response, { name: action.name }) instruct( socket, store, 'playlists.save', { playlist: playlist } ) .then( response => { - store.dispatch({ type: 'PLAYLIST_UPDATED', uri: action.uri, playlist: playlist }) + store.dispatch({ + type: 'PLAYLIST_UPDATED', + uri: action.uri, + playlist: playlist + }) }) }); break diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 860f45f3..0d3c5134 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -355,14 +355,11 @@ export function getCategory( id ){ export function getNewReleases(){ return (dispatch, getState) => { - - dispatch({ type: 'SPOTIFY_NEW_RELEASES_LOADED', data: false }); - sendRequest( dispatch, getState, 'browse/new-releases?country='+getState().spotify.country+'&limit=50' ) .then( response => { dispatch({ type: 'SPOTIFY_NEW_RELEASES_LOADED', - data: response.albums + data: response }); }); } @@ -405,8 +402,6 @@ export function getSearchResults( query, type = 'album,artist,playlist,track', l export function following(uri, method = 'GET'){ return (dispatch, getState) => { - dispatch({ type: 'FOLLOWING_LOADING' }); - if( method == 'PUT' ) var is_following = true if( method == 'DELETE' ) var is_following = false @@ -450,7 +445,8 @@ export function following(uri, method = 'GET'){ if( response ) is_following = response if( typeof(is_following) === 'object' ) is_following = is_following[0] dispatch({ - type: asset_name.toUpperCase()+'_FOLLOWING_LOADED', + type: 'SPOTIFY_'+asset_name.toUpperCase()+'_FOLLOWING_LOADED', + uri: uri, is_following: is_following }); }); @@ -603,31 +599,29 @@ export function getArtist( uri ){ sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/related-artists' ) .then( response => { - for (var i = 0; i < response.artists.length; i++){ - dispatch({ - type: 'ARTIST_LOADED', - artist: response.artists[i] - }); - } - Object.assign(artist, { related_artists: helpers.asURIs(response.artists) }); - }), - - sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/albums' ) - .then( response => { - for (var i = 0; i < response.items.length; i++){ - dispatch({ - type: 'ALBUM_LOADED', - album: response.items[i] - }); - } - Object.assign(artist, { albums: helpers.asURIs(response.items), albums_more: response.next }); + dispatch({ + type: 'ARTISTS_LOADED', + artists: response.artists + }); + Object.assign(artist, { related_artists_uris: helpers.asURIs(response.artists) }); }) ).then( () => { dispatch({ type: 'ARTIST_LOADED', + uri: artist.uri, artist: artist }); + + // now go get our artist albums + sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/albums' ) + .then( response => { + dispatch({ + type: 'SPOTIFY_ARTIST_ALBUMS_LOADED', + data: response, + uri: uri + }); + }) }); } } @@ -732,19 +726,34 @@ export function getAlbum( uri ){ sendRequest( dispatch, getState, 'albums/'+ helpers.getFromUri('albumid', uri) ) .then( response => { + // dispatch our loaded artists (simple objects) + dispatch({ + type: 'ARTISTS_LOADED', + artists: response.artists + }); + var album = Object.assign( {}, response, { - artists: helpers.asURIs(response.artists), + artists_uris: helpers.asURIs(response.artists), tracks: response.tracks.items, tracks_more: response.tracks.next, tracks_total: response.tracks.total } ) + // add our album to all the tracks + for (var i = 0; i < album.tracks.length; i++){ + album.tracks[i].album = { + name: album.name, + uri: album.uri + } + } + dispatch({ type: 'ALBUM_LOADED', + uri: album.uri, album: album }); @@ -854,6 +863,7 @@ export function getPlaylist( uri ){ dispatch({ type: 'PLAYLIST_LOADED', + uri: playlist.uri, playlist: playlist }) }) @@ -868,38 +878,15 @@ function loadNextPlaylistsBatch( dispatch, getState, playlists, lastResponse ){ loadNextPlaylistsBatch( dispatch, getState, playlists, response ) }); }else{ - - for( var i = 0; i < playlists.length; i++ ){ - var playlist = Object.assign( - {}, - playlists[i], - { - can_edit: (getState().spotify.authorized && getState().spotify.me && playlists[i].owner.id == getState().spotify.me.id), - tracks_total: playlists[i].tracks.total - } - ) - - // remove our tracklist. It'll overwrite any full records otherwise - delete playlist.tracks - - dispatch({ - type: 'PLAYLIST_LOADED', - playlist: playlist - }); - } - dispatch({ - type: 'LIBRARY_PLAYLISTS_LOADED', - uris: helpers.asURIs(playlists) + type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED', + playlists: playlists }); } } export function getAllLibraryPlaylists(){ return (dispatch, getState) => { - - dispatch({ type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED', data: false }); - sendRequest( dispatch, getState, 'me/playlists?limit=50' ) .then( response => { loadNextPlaylistsBatch( dispatch, getState, response.items, response ) diff --git a/src/js/services/spotify/middleware.js b/src/js/services/spotify/middleware.js index b8bb009e..68cd159e 100755 --- a/src/js/services/spotify/middleware.js +++ b/src/js/services/spotify/middleware.js @@ -93,6 +93,92 @@ const SpotifyMiddleware = (function(){ } break + case 'SPOTIFY_PLAYLIST_FOLLOWING_LOADED': + store.dispatch({ + type: 'PLAYLIST_LOADED', + uri: action.uri, + playlist: { + is_following: action.is_following + } + }); + break + + case 'SPOTIFY_ALBUM_FOLLOWING_LOADED': + store.dispatch({ + type: 'ALBUM_LOADED', + uri: action.uri, + album: { + is_following: action.is_following + } + }); + break + + case 'SPOTIFY_ARTIST_FOLLOWING_LOADED': + store.dispatch({ + type: 'ARTIST_LOADED', + uri: action.uri, + artist: { + is_following: action.is_following + } + }); + break + + case 'SPOTIFY_NEW_RELEASES_LOADED': + store.dispatch({ + type: 'ALBUMS_LOADED', + albums: action.data.albums.items + }); + store.dispatch({ + type: 'NEW_RELEASES_LOADED', + uris: helpers.asURIs(action.data.albums.items), + more: action.data.albums.next, + total: action.data.albums.total + }); + break + + case 'SPOTIFY_ARTIST_ALBUMS_LOADED': + store.dispatch({ + type: 'ALBUMS_LOADED', + albums: action.data.items + }); + store.dispatch({ + type: 'ARTIST_ALBUMS_LOADED', + uri: action.uri, + uris: helpers.asURIs(action.data.items), + more: action.data.next, + total: action.data.total + }); + break + + case 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED': + var playlists = [] + for( var i = 0; i < action.playlists.length; i++ ){ + var playlist = Object.assign( + {}, + action.playlists[i], + { + can_edit: (store.getState().spotify.authorized && store.getState().spotify.me && action.playlists[i].owner.id == store.getState().spotify.me.id), + tracks_total: action.playlists[i].tracks.total + } + ) + + // remove our tracklist. It'll overwrite any full records otherwise + delete playlist.tracks + + playlists.push(playlist) + } + + store.dispatch({ + type: 'PLAYLISTS_LOADED', + playlists: playlists + }); + + store.dispatch({ + type: 'LIBRARY_PLAYLISTS_LOADED', + uris: helpers.asURIs(playlists) + }); + break + case 'SPOTIFY_LIBRARY_ARTISTS_LOADED': store.dispatch({ type: 'ARTISTS_LOADED', @@ -107,9 +193,7 @@ const SpotifyMiddleware = (function(){ break case 'SPOTIFY_LIBRARY_ALBUMS_LOADED': - var albums = [] - for (var i = 0; i < action.data.items.length; i++){ albums.push( Object.assign( diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index 9defb420..bdb39263 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -144,47 +144,16 @@ export default function reducer(ui = {}, action){ case 'ALBUM_LOADED': var albums = Object.assign([], ui.albums) - var album = Object.assign({}, action.album) - // if we already have one in our list, fetch it and update it - if (albums[action.album.uri]){ - album = Object.assign({}, albums[action.album.uri], action.album) + if (albums[action.uri]){ + var album = Object.assign({}, albums[action.uri], action.album) + }else{ + var album = Object.assign({}, action.album) } - albums[action.album.uri] = album + albums[action.uri] = album return Object.assign({}, ui, { albums: albums }); - case 'MOPIDY_ALBUM_LOADED': - if( !action.data ) return Object.assign({}, ui, { album: false }) - return Object.assign({}, ui, { album: action.data }); - - case 'LASTFM_ALBUM_LOADED': - if( !action.data.image ) return ui - - var album = Object.assign({}, ui.album, { images: action.data.image }) - return Object.assign({}, ui, { album: album }); - - case 'SPOTIFY_ALBUM_LOADED': - if( !action.data ) return Object.assign({}, ui, { album: false }) - - var album = Object.assign({}, { images: [] }, action.data, { - tracks: action.data.tracks.items, - tracks_total: action.data.tracks.total, - tracks_more: action.data.tracks.next - }) - return Object.assign({}, ui, { album: album }) - - case 'SPOTIFY_ALBUM_LOADED_MORE': - var album = Object.assign({}, ui.album, { - tracks: [ ...ui.album.tracks, ...action.data.items ], - tracks_more: action.data.next - }) - return Object.assign({}, ui, { album: album }); - - case 'ALBUM_FOLLOWING_LOADED': - var album = Object.assign({}, ui.album, { following: action.is_following }) - return Object.assign({}, ui, { album: album, following_loading: false }); - case 'LIBRARY_ALBUMS_LOADED': if (!action.uris){ return Object.assign({}, ui, { @@ -203,6 +172,24 @@ export default function reducer(ui = {}, action){ library_albums_total: action.total }); + case 'NEW_RELEASES_LOADED': + if (!action.uris){ + return Object.assign({}, ui, { + new_releases: null, + new_releases_more: null, + new_releases_total: null + }); + } + + var new_releases = [] + if (ui.new_releases) new_releases = Object.assign([], ui.new_releases) + + return Object.assign({}, ui, { + new_releases: [...new_releases, ...action.uris], + new_releases_more: action.more, + new_releases_total: action.total + }); + /** @@ -224,16 +211,34 @@ export default function reducer(ui = {}, action){ case 'ARTIST_LOADED': var artists = Object.assign([], ui.artists) - var artist = Object.assign({}, action.artist) - // if we already have one in our list, fetch it and update it - if (artists[action.artist.uri]){ - artist = Object.assign({}, artists[action.artist.uri], action.artist) + if (artists[action.uri]){ + var artist = Object.assign({}, artists[action.uri], action.artist) + }else{ + var artist = Object.assign({}, action.artist) } - artists[action.artist.uri] = artist + artists[action.uri] = artist return Object.assign({}, ui, { artists: artists }); + case 'ARTIST_ALBUMS_LOADED': + var artists = Object.assign([], ui.artists) + var albums_uris = [] + if (artists[action.uri].albums_uris) albums_uris = artists[action.uri].albums_uris + + var artist = Object.assign( + {}, + artists[action.uri], + { + albums_uris: [...albums_uris, ...action.uris], + albums_more: action.more, + albums_total: action.total + } + ) + artists[action.uri] = artist + return Object.assign({}, ui, { artists: artists }); + +/* case 'LASTFM_ARTIST_LOADED': if( !action.data.image ) return ui @@ -243,21 +248,7 @@ export default function reducer(ui = {}, action){ var artist = Object.assign({}, ui.artist, { images: images, bio: action.data.bio, listeners: parseInt(action.data.stats.listeners), on_tour: action.data.ontour }, ) return Object.assign({}, ui, { artist: artist }); - - case 'SPOTIFY_ARTIST_LOADED': - if( !action.data ) return Object.assign({}, ui, { artist: false }) - return Object.assign({}, ui, { artist: action.data }) - - case 'SPOTIFY_ARTIST_ALBUMS_LOADED_MORE': - var artist = Object.assign({}, ui.artist, { - albums: [ ...ui.artist.albums, ...action.data.items ], - albums_more: action.data.next - }) - return Object.assign({}, ui, { artist: artist }); - - case 'ARTIST_FOLLOWING_LOADED': - var artist = Object.assign({}, ui.artist, { following: action.is_following }) - return Object.assign({}, ui, { artist: artist, following_loading: false }); + */ case 'LIBRARY_ARTISTS_LOADED': if (!action.uris){ @@ -305,14 +296,27 @@ export default function reducer(ui = {}, action){ case 'PLAYLIST_LOADED': case 'PLAYLIST_UPDATED': var playlists = Object.assign([], ui.playlists) - var playlist = Object.assign({}, action.playlist) - // if we already have one in our list, fetch it and update it - if (playlists[action.playlist.uri]){ - playlist = Object.assign({}, playlists[action.playlist.uri], action.playlist) + if (playlists[action.uri]){ + var playlist = Object.assign({}, playlists[action.uri], action.playlist) + }else{ + var playlist = Object.assign({}, action.playlist) + } + + playlists[action.uri] = playlist + return Object.assign({}, ui, { playlists: playlists }); + + case 'PLAYLISTS_LOADED': + var playlists = Object.assign([], ui.playlists) + + for (var i = 0; i < action.playlists.length; i++){ + var playlist = action.playlists[i] + if (typeof(playlists[playlist.uri]) !== 'undefined'){ + artist = Object.assign({}, playlists[playlist.uri], playlist) + } + playlists[playlist.uri] = playlist } - playlists[action.playlist.uri] = playlist return Object.assign({}, ui, { playlists: playlists }); case 'MOPIDY_PLAYLIST_LOADED': diff --git a/src/js/views/Album.js b/src/js/views/Album.js index 5054565e..8564e03b 100755 --- a/src/js/views/Album.js +++ b/src/js/views/Album.js @@ -25,7 +25,7 @@ class Album extends React.Component{ } componentDidMount(){ - this.loadAlbum(); + this.loadAlbum() } componentWillReceiveProps( nextProps ){ @@ -43,13 +43,14 @@ class Album extends React.Component{ } loadAlbum( props = this.props ){ - // if we've already loaded the full version of this album, halt! - if (this.props.album && this.props.album.tracks) return false - switch( helpers.uriSource( props.params.uri ) ){ case 'spotify': - this.props.spotifyActions.getAlbum( props.params.uri ); + if (props.album && props.album.tracks && props.album.artists_uris){ + console.info('Loading album from index') + }else{ + this.props.spotifyActions.getAlbum( props.params.uri ); + } break; case 'local': @@ -72,9 +73,9 @@ class Album extends React.Component{ if( !this.props.album ) return null var artists = [] - if (this.props.album.artists){ - for (var i = 0; i < this.props.album.artists.length; i++){ - var uri = this.props.album.artists[i] + if (this.props.album.artists_uris && this.props.artists){ + for (var i = 0; i < this.props.album.artists_uris.length; i++){ + var uri = this.props.album.artists_uris[i] if (this.props.artists.hasOwnProperty(uri)){ artists.push(this.props.artists[uri]) } @@ -90,7 +91,7 @@ class Album extends React.Component{
- { helpers.uriSource(this.props.params.uri) == 'spotify' ? : null } + { helpers.uriSource(this.props.params.uri) == 'spotify' ? : null }
    @@ -130,7 +131,7 @@ class Album extends React.Component{ const mapStateToProps = (state, ownProps) => { return { artists: state.ui.artists, - album: state.ui.albums[ownProps.params.uri], + album: (state.ui.albums && typeof(state.ui.albums[ownProps.params.uri]) !== 'undefined' ? state.ui.albums[ownProps.params.uri] : false ), albums: state.ui.albums, spotify_authorized: state.spotify.authorized, mopidy_connected: state.mopidy.connected diff --git a/src/js/views/Artist.js b/src/js/views/Artist.js index 90d1680a..7d53c057 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -55,11 +55,19 @@ class Artist extends React.Component{ } loadArtist( props = this.props ){ - var source = helpers.uriSource( props.params.uri ); - if( source == 'spotify' ){ - this.props.spotifyActions.getArtist( props.params.uri ); - }else if( source == 'local' && props.mopidy_connected ){ - this.props.mopidyActions.getArtist( props.params.uri ); + switch( helpers.uriSource( props.params.uri ) ){ + + case 'spotify': + if (props.artist && props.artist.albums_uris && props.artist.related_artists_uris){ + console.info('Loading spotify artist from index') + }else{ + this.props.spotifyActions.getArtist( props.params.uri ); + } + break + + case 'local': + this.props.mopidyActions.getArtist( props.params.uri ); + break } // go back to overview @@ -68,7 +76,7 @@ class Artist extends React.Component{ loadMore(){ if( !this.props.artist.albums_more ) return - this.props.spotifyActions.getURL( this.props.artist.albums_more, 'SPOTIFY_ARTIST_ALBUMS_LOADED_MORE' ); + this.props.spotifyActions.getURL( this.props.artist.albums_more, 'SPOTIFY_ARTIST_ALBUMS_LOADED', this.props.params.uri ); } play(){ @@ -96,9 +104,9 @@ class Artist extends React.Component{ renderBody(){ var related_artists = [] - if (this.props.artist.related_artists){ - for (var i = 0; i < this.props.artist.related_artists.length; i++){ - var uri = this.props.artist.related_artists[i] + if (this.props.artist.related_artists_uris){ + for (var i = 0; i < this.props.artist.related_artists_uris.length; i++){ + var uri = this.props.artist.related_artists_uris[i] if (this.props.artists.hasOwnProperty(uri)){ related_artists.push(this.props.artists[uri]) } @@ -106,9 +114,9 @@ class Artist extends React.Component{ } var albums = [] - if (this.props.artist.albums){ - for (var i = 0; i < this.props.artist.albums.length; i++){ - var uri = this.props.artist.albums[i] + if (this.props.artist.albums_uris){ + for (var i = 0; i < this.props.artist.albums_uris.length; i++){ + var uri = this.props.artist.albums_uris[i] if (this.props.albums.hasOwnProperty(uri)){ albums.push(this.props.albums[uri]) } @@ -191,7 +199,7 @@ class Artist extends React.Component{
    - { helpers.uriSource(this.props.params.uri) == 'spotify' ? : null } + { helpers.uriSource(this.props.params.uri) == 'spotify' ? : null }
      diff --git a/src/js/views/Playlist.js b/src/js/views/Playlist.js index 55927ceb..dceb80f0 100755 --- a/src/js/views/Playlist.js +++ b/src/js/views/Playlist.js @@ -110,7 +110,7 @@ class Playlist extends React.Component{ ) } - return + return } } diff --git a/src/js/views/discover/DiscoverNewReleases.js b/src/js/views/discover/DiscoverNewReleases.js index be5e2d05..a6a50f7f 100755 --- a/src/js/views/discover/DiscoverNewReleases.js +++ b/src/js/views/discover/DiscoverNewReleases.js @@ -17,20 +17,31 @@ class DiscoverNewReleases extends React.Component{ } componentDidMount(){ - this.props.spotifyActions.getNewReleases(); + if (!this.props.new_releases) this.props.spotifyActions.getNewReleases(); } loadMore(){ - if( !this.props.spotify.new_releases || !this.props.spotify.new_releases.next ) return - this.props.spotifyActions.getURL( this.props.spotify.new_releases.next, 'SPOTIFY_NEW_RELEASES_LOADED_MORE' ); + if (!this.props.new_releases_more) return + this.props.spotifyActions.getURL(this.props.new_releases_more, 'SPOTIFY_NEW_RELEASES_LOADED'); } render(){ + + var albums = [] + if (this.props.new_releases){ + for (var i = 0; i < this.props.new_releases.length; i++){ + var uri = this.props.new_releases[i] + if (this.props.albums.hasOwnProperty(uri)){ + albums.push(this.props.albums[uri]) + } + } + } + return (
      - { this.props.spotify.new_releases ? : null } +
      this.loadMore() }/>
      @@ -46,7 +57,12 @@ class DiscoverNewReleases extends React.Component{ **/ const mapStateToProps = (state, ownProps) => { - return state; + return { + albums: state.ui.albums, + new_releases: state.ui.new_releases, + new_releases_more: state.ui.new_releases_more, + new_releases_total: state.ui.new_releases_total + } } const mapDispatchToProps = (dispatch) => { diff --git a/src/js/views/library/LibraryAlbums.js b/src/js/views/library/LibraryAlbums.js index 2a383fb2..e7ad2ab8 100755 --- a/src/js/views/library/LibraryAlbums.js +++ b/src/js/views/library/LibraryAlbums.js @@ -85,7 +85,7 @@ class LibraryAlbums extends React.Component{

- +
) }) From 838255b34b30e1a2e548c6d2bae829f7287eddb1 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Sun, 15 Jan 2017 21:06:24 +1300 Subject: [PATCH 7/9] Local assets --- src/js/components/Thumbnail.js | 7 +- src/js/services/lastfm/actions.js | 13 ++-- src/js/services/mopidy/actions.js | 14 ++-- src/js/services/mopidy/middleware.js | 76 ++++++++++++++------- src/js/services/ui/reducer.js | 8 +++ src/js/views/Album.js | 8 ++- src/js/views/Artist.js | 25 ++++--- src/js/views/library/LibraryLocalAlbums.js | 16 +++-- src/js/views/library/LibraryLocalArtists.js | 17 +++-- 9 files changed, 120 insertions(+), 64 deletions(-) diff --git a/src/js/components/Thumbnail.js b/src/js/components/Thumbnail.js index 0a1ce0bf..94a02486 100755 --- a/src/js/components/Thumbnail.js +++ b/src/js/components/Thumbnail.js @@ -13,12 +13,7 @@ export default class Thumbnail extends React.Component{ shouldComponentUpdate(nextProps, nextState){ // no images at all, and we already know it - if( - typeof(nextProps.image) == 'undefined' && - typeof(this.props.image) == 'undefined' && - typeof(nextProps.images) == 'undefined' && - typeof(this.props.images) == 'undefined' - ) return false + if (!nextProps.image && !this.props.image && !nextProps.images && !this.props.images) return false // image changed if( !this.props.image && nextProps.image ) return true diff --git a/src/js/services/lastfm/actions.js b/src/js/services/lastfm/actions.js index eb79c61e..e57c6cf6 100755 --- a/src/js/services/lastfm/actions.js +++ b/src/js/services/lastfm/actions.js @@ -27,11 +27,8 @@ const sendRequest = ( dispatch, getState, params ) => { }) } -export function getArtist( artist, mbid = false ){ +export function getArtist( uri, artist, mbid = false ){ return (dispatch, getState) => { - - dispatch({ type: 'LASTFM_ARTIST_LOADED', data: false }); - if( mbid ){ var params = 'method=artist.getInfo&mbid='+mbid }else{ @@ -43,8 +40,12 @@ export function getArtist( artist, mbid = false ){ response => { if( response.artist ){ dispatch({ - type: 'LASTFM_ARTIST_LOADED', - data: response.artist + type: 'ARTIST_LOADED', + uri: uri, + artist: { + images: response.artist.image, + bio: response.artist.bio + } }); } } diff --git a/src/js/services/mopidy/actions.js b/src/js/services/mopidy/actions.js index 09f3a6be..d06bac5b 100755 --- a/src/js/services/mopidy/actions.js +++ b/src/js/services/mopidy/actions.js @@ -175,13 +175,6 @@ export function getDirectory( uri ){ } } -export function getAlbum( uri ){ - return { - type: 'MOPIDY_GET_ALBUM', - data: { uri: uri } - } -} - export function getArtist( uri ){ return { type: 'MOPIDY_GET_ARTIST', @@ -195,6 +188,13 @@ export function getArtists(){ } } +export function getAlbum( uri ){ + return { + type: 'MOPIDY_GET_ALBUM', + data: { uri: uri } + } +} + export function getAlbums(){ return { type: 'MOPIDY_GET_ALBUMS' diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index 3bfb2777..4f0773a2 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -509,16 +509,21 @@ const MopidyMiddleware = (function(){ * ====================================================================================== **/ - case 'MOPIDY_ALBUMS': - store.dispatch({ type: 'MOPIDY_ALBUMS_LOADED', data: false }); + case 'MOPIDY_GET_ALBUMS': instruct( socket, store, 'library.browse', { uri: 'local:directory?type=album' } ) - .then( response => { - store.dispatch({ type: 'MOPIDY_ALBUMS_LOADED', data: response }); + .then( response => { + store.dispatch({ + type: 'ALBUMS_LOADED', + albums: response + }); + store.dispatch({ + type: 'LOCAL_ALBUMS_LOADED', + uris: helpers.asURIs(response) + }); }) break; - case 'MOPIDY_ALBUM': - //store.dispatch({ type: 'MOPIDY_ALBUM_LOADED', data: false }); + case 'MOPIDY_GET_ALBUM': instruct( socket, store, 'library.lookup', action.data ) .then( response => { var album = Object.assign( @@ -570,7 +575,11 @@ const MopidyMiddleware = (function(){ } } - store.dispatch({ type: 'MOPIDY_ALBUM_LOADED', data: album }); + store.dispatch({ + type: 'ALBUM_LOADED', + uri: album.uri, + album: album + }); }) }) break; @@ -581,45 +590,66 @@ const MopidyMiddleware = (function(){ * ====================================================================================== **/ - case 'MOPIDY_ARTISTS': - store.dispatch({ type: 'MOPIDY_ARTISTS_LOADED', data: false }); + case 'MOPIDY_GET_ARTISTS': + store.dispatch({ type: 'LOCAL_ARTISTS_LOADED', data: false }); instruct( socket, store, 'library.browse', { uri: 'local:directory?type=artist' } ) .then( response => { - store.dispatch({ type: 'MOPIDY_ARTISTS_LOADED', data: response }); + store.dispatch({ + type: 'ARTISTS_LOADED', + artists: response + }); + store.dispatch({ + type: 'LOCAL_ARTISTS_LOADED', + uris: helpers.asURIs(response) + }); }) break; - case 'MOPIDY_ARTIST': - store.dispatch({ type: 'MOPIDY_ARTIST_LOADED', data: false }); + case 'MOPIDY_GET_ARTIST': instruct( socket, store, 'library.lookup', action.data ) - .then( response => { - var artist = response[0].artists[0]; - if( !artist.images ) artist.images = []; - if( !artist.albums ) artist.albums = []; - artist.tracks = response.slice(0,10); - + .then( response => { + var albums = [] for( var i = 0; i < response.length; i++ ){ var album = response[i].album; function getByURI( albumToCheck ){ return album.uri == albumToCheck.uri } - var existingAlbum = artist.albums.find(getByURI); + var existingAlbum = albums.find(getByURI); if( !existingAlbum ){ - artist.albums.push(album) + albums.push(album) } } + + store.dispatch({ + type: 'ALBUMS_LOADED', + albums: albums + }) + + var artist = Object.assign( + {}, + response[0].artists[0], + { + images: [], + albums_uris: helpers.asURIs(albums), + tracks: response.slice(0,10) + } + ) // load artwork from LastFM if( artist.images.length <= 0 ){ if( artist.musicbrainz_id ){ - store.dispatch( lastfmActions.getArtist( false, artist.musicbrainz_id ) ) + store.dispatch( lastfmActions.getArtist( artist.uri, false, artist.musicbrainz_id ) ) }else{ - store.dispatch( lastfmActions.getArtist( artist.name ) ) + store.dispatch( lastfmActions.getArtist( artist.uri, artist.name.replace('&','and') ) ) } } - store.dispatch({ type: 'MOPIDY_ARTIST_LOADED', data: artist }); + store.dispatch({ + type: 'ARTIST_LOADED', + uri: artist.uri, + artist: artist + }); }) break; diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index bdb39263..522e5f9e 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -172,6 +172,10 @@ export default function reducer(ui = {}, action){ library_albums_total: action.total }); + case 'LOCAL_ALBUMS_LOADED': + if (!action.uris) return Object.assign({}, ui, { local_albums: null }); + return Object.assign({}, ui, { local_albums: action.uris }); + case 'NEW_RELEASES_LOADED': if (!action.uris){ return Object.assign({}, ui, { @@ -268,6 +272,10 @@ export default function reducer(ui = {}, action){ library_artists_total: action.total }); + case 'LOCAL_ARTISTS_LOADED': + if (!action.uris) return Object.assign({}, ui, { local_artists: null }); + return Object.assign({}, ui, { local_artists: action.uris }); + /** * User diff --git a/src/js/views/Album.js b/src/js/views/Album.js index 8564e03b..2adaa4f6 100755 --- a/src/js/views/Album.js +++ b/src/js/views/Album.js @@ -54,7 +54,13 @@ class Album extends React.Component{ break; case 'local': - if( props.mopidy_connected ) this.props.mopidyActions.getAlbum( props.params.uri ); + if (props.mopidy_connected){ + if (props.album && props.album.tracks){ + console.info('Loading album from index') + } else { + this.props.mopidyActions.getAlbum( props.params.uri ); + } + } break; } } diff --git a/src/js/views/Artist.js b/src/js/views/Artist.js index 7d53c057..4d0ca796 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -49,7 +49,7 @@ class Artist extends React.Component{ componentWillUpdate( nextProps, nextState ){ if( nextState.sub_view != this.state.sub_view && nextState.sub_view == 'biography' ){ if( this.props.artist && !this.props.artist.bio ){ - this.props.lastfmActions.getArtist( this.props.artist.name.replace('&','and') ) + this.props.lastfmActions.getArtist( this.props.params.uri, this.props.artist.name.replace('&','and') ) } } } @@ -66,7 +66,13 @@ class Artist extends React.Component{ break case 'local': - this.props.mopidyActions.getArtist( props.params.uri ); + if (props.mopidy_connected){ + if (props.artist && props.artist.images){ + console.info('Loading local artist from index') + } else { + this.props.mopidyActions.getArtist( props.params.uri ); + } + } break } @@ -91,9 +97,7 @@ class Artist extends React.Component{ this.setState({ sub_view: 'overview' })}> Overview - this.setState({ sub_view: 'related_artists' })}> - Related artists - + {this.props.artist.related_artists_uris ? this.setState({ sub_view: 'related_artists' })}>Related artists : null} this.setState({ sub_view: 'biography' })}> Biography @@ -102,7 +106,6 @@ class Artist extends React.Component{ } renderBody(){ - var related_artists = [] if (this.props.artist.related_artists_uris){ for (var i = 0; i < this.props.artist.related_artists_uris.length; i++){ @@ -145,20 +148,16 @@ class Artist extends React.Component{ ) } - // default body return (
-
+

Top tracks

{ this.props.artist.tracks ? : null }
-
-

Related artists

- -
+ {this.props.artist.related_artists ?

Related artists

: null}
@@ -175,7 +174,7 @@ class Artist extends React.Component{ if( !this.props.artist ) return null var scheme = helpers.uriSource( this.props.params.uri ); - var image = false + var image = null if( this.props.artist.images ) image = helpers.sizedImages( this.props.artist.images ).huge return ( diff --git a/src/js/views/library/LibraryLocalAlbums.js b/src/js/views/library/LibraryLocalAlbums.js index d54f0310..5575806a 100755 --- a/src/js/views/library/LibraryLocalAlbums.js +++ b/src/js/views/library/LibraryLocalAlbums.js @@ -15,7 +15,6 @@ class LibraryLocalAlbums extends React.Component{ super(props); } - // on render componentDidMount(){ this.loadAlbums() } @@ -33,13 +32,21 @@ class LibraryLocalAlbums extends React.Component{ } render(){ - if( !this.props.albums ) return null + var albums = [] + if (this.props.albums && this.props.local_albums){ + for (var i = 0; i < this.props.local_albums.length; i++){ + var uri = this.props.local_albums[i] + if (this.props.albums.hasOwnProperty(uri)){ + albums.push(this.props.albums[uri]) + } + } + } return (
- +
); @@ -56,7 +63,8 @@ class LibraryLocalAlbums extends React.Component{ const mapStateToProps = (state, ownProps) => { return { mopidy_connected: state.mopidy.connected, - albums: state.mopidy.albums + local_albums: state.ui.local_albums, + albums: state.ui.albums } } diff --git a/src/js/views/library/LibraryLocalArtists.js b/src/js/views/library/LibraryLocalArtists.js index 35b031a9..156d54d7 100755 --- a/src/js/views/library/LibraryLocalArtists.js +++ b/src/js/views/library/LibraryLocalArtists.js @@ -27,19 +27,27 @@ class LibraryLocalArtists extends React.Component{ } loadArtists(props = this.props){ - if( props.mopidy_connected ){ + if( props.mopidy_connected && !props.local_artists ){ this.props.mopidyActions.getArtists(); } } render(){ - if( !this.props.artists ) return null + var artists = [] + if (this.props.artists && this.props.local_artists){ + for (var i = 0; i < this.props.local_artists.length; i++){ + var uri = this.props.local_artists[i] + if (this.props.artists.hasOwnProperty(uri)){ + artists.push(this.props.artists[uri]) + } + } + } return (
- +
); @@ -56,7 +64,8 @@ class LibraryLocalArtists extends React.Component{ const mapStateToProps = (state, ownProps) => { return { mopidy_connected: state.mopidy.connected, - artists: state.mopidy.artists + local_artists: state.ui.local_artists, + artists: state.ui.artists } } From ef17d36f223d7d6112d0ca11c264e64dd13429d3 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Mon, 16 Jan 2017 05:58:13 +1300 Subject: [PATCH 8/9] Updated PLAYLISTS_LOADING --- src/js/services/spotify/actions.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 0d3c5134..202f6e79 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -262,22 +262,23 @@ export function getFeaturedPlaylists(){ sendRequest( dispatch, getState, 'browse/featured-playlists?timestamp='+timestamp+'&country='+getState().spotify.country+'&limit=50&locale='+getState().spotify.locale ) .then( response => { + + var playlists = [] for (var i = 0; i < response.playlists.items.length; i++){ - - var playlist = response.playlists.items[i] - Object.assign( - playlist, + playlists.push(Object.assign( + {}, + response.playlists.items[i], { - can_edit: (getState().spotify.me && playlist.owner.id == getState().spotify.me.id), - tracks_total: playlist.tracks.total + can_edit: (getState().spotify.me && response.playlists.items[i].owner.id == getState().spotify.me.id), + tracks_total: response.playlists.items[i].tracks.total } - ) - - dispatch({ - type: 'PLAYLIST_LOADED', - playlist: playlist - }); + )) } + + dispatch({ + type: 'PLAYLISTS_LOADED', + playlists: playlists + }); dispatch({ type: 'SPOTIFY_FEATURED_PLAYLISTS_LOADED', data: { From 6901f05af9d869fd7c7be1540959a29282c810ba Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Mon, 16 Jan 2017 08:11:32 +1300 Subject: [PATCH 9/9] Artwork for local artists; Hide related when empty --- src/js/services/mopidy/middleware.js | 48 ++++++++++---------- src/js/services/ui/reducer.js | 67 +++++++++++++--------------- src/js/views/Artist.js | 4 +- 3 files changed, 57 insertions(+), 62 deletions(-) diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index 4f0773a2..b0de7a5b 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -607,49 +607,51 @@ const MopidyMiddleware = (function(){ case 'MOPIDY_GET_ARTIST': instruct( socket, store, 'library.lookup', action.data ) - .then( response => { + .then( response => { + var albums = [] for( var i = 0; i < response.length; i++ ){ var album = response[i].album; - - function getByURI( albumToCheck ){ - return album.uri == albumToCheck.uri - } - var existingAlbum = albums.find(getByURI); - if( !existingAlbum ){ - albums.push(album) + if (album){ + function getByURI( albumToCheck ){ + return album.uri == albumToCheck.uri + } + var existingAlbum = albums.find(getByURI); + if( !existingAlbum ){ + albums.push(album) + } } } - - store.dispatch({ - type: 'ALBUMS_LOADED', - albums: albums - }) + if (albums){ + store.dispatch({ + type: 'ALBUMS_LOADED', + albums: albums + }) + } var artist = Object.assign( {}, response[0].artists[0], { - images: [], albums_uris: helpers.asURIs(albums), tracks: response.slice(0,10) } ) - - // load artwork from LastFM - if( artist.images.length <= 0 ){ - if( artist.musicbrainz_id ){ - store.dispatch( lastfmActions.getArtist( artist.uri, false, artist.musicbrainz_id ) ) - }else{ - store.dispatch( lastfmActions.getArtist( artist.uri, artist.name.replace('&','and') ) ) - } - } store.dispatch({ type: 'ARTIST_LOADED', uri: artist.uri, artist: artist }); + + // load artwork from LastFM + if( !artist.images || artist.images.length <= 0 ){ + if( artist.musicbrainz_id ){ + store.dispatch( lastfmActions.getArtist( artist.uri, false, artist.musicbrainz_id ) ) + }else{ + store.dispatch( lastfmActions.getArtist( artist.uri, artist.name.replace('&','and') ) ) + } + } }) break; diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index 522e5f9e..77498346 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -129,19 +129,6 @@ export default function reducer(ui = {}, action){ * Albums **/ - case 'ALBUMS_LOADED': - var albums = Object.assign([], ui.albums) - - for (var i = 0; i < action.albums.length; i++){ - var album = action.albums[i] - if (typeof(albums[album.uri]) !== 'undefined'){ - artist = Object.assign({}, albums[album.uri], album) - } - albums[album.uri] = album - } - - return Object.assign({}, ui, { albums: albums }); - case 'ALBUM_LOADED': var albums = Object.assign([], ui.albums) @@ -154,6 +141,20 @@ export default function reducer(ui = {}, action){ albums[action.uri] = album return Object.assign({}, ui, { albums: albums }); + case 'ALBUMS_LOADED': + console.log(action) + var albums = Object.assign([], ui.albums) + + for (var i = 0; i < action.albums.length; i++){ + var album = action.albums[i] + if (typeof(albums[album.uri]) !== 'undefined'){ + artist = Object.assign({}, albums[album.uri], album) + } + albums[album.uri] = album + } + + return Object.assign({}, ui, { albums: albums }); + case 'LIBRARY_ALBUMS_LOADED': if (!action.uris){ return Object.assign({}, ui, { @@ -200,6 +201,22 @@ export default function reducer(ui = {}, action){ * Artists **/ + case 'ARTIST_LOADED': + console.log(action) + var artists = Object.assign([], ui.artists) + + if (artists[action.uri]){ + // if we've already got images, delete our new ones + // this is to prevent LastFM overwriting Spotify images + if (artists[action.uri].images) delete action.artist.images + var artist = Object.assign({}, artists[action.uri], action.artist) + }else{ + var artist = Object.assign({}, action.artist) + } + + artists[action.uri] = artist + return Object.assign({}, ui, { artists: artists }); + case 'ARTISTS_LOADED': var artists = Object.assign([], ui.artists) @@ -213,18 +230,6 @@ export default function reducer(ui = {}, action){ return Object.assign({}, ui, { artists: artists }); - case 'ARTIST_LOADED': - var artists = Object.assign([], ui.artists) - - if (artists[action.uri]){ - var artist = Object.assign({}, artists[action.uri], action.artist) - }else{ - var artist = Object.assign({}, action.artist) - } - - artists[action.uri] = artist - return Object.assign({}, ui, { artists: artists }); - case 'ARTIST_ALBUMS_LOADED': var artists = Object.assign([], ui.artists) var albums_uris = [] @@ -242,18 +247,6 @@ export default function reducer(ui = {}, action){ artists[action.uri] = artist return Object.assign({}, ui, { artists: artists }); -/* - case 'LASTFM_ARTIST_LOADED': - if( !action.data.image ) return ui - - // if we already have images, don't overwrite them - var images = ui.artist.images - if( images.length <= 0 ) images = action.data.image - - var artist = Object.assign({}, ui.artist, { images: images, bio: action.data.bio, listeners: parseInt(action.data.stats.listeners), on_tour: action.data.ontour }, ) - return Object.assign({}, ui, { artist: artist }); - */ - case 'LIBRARY_ARTISTS_LOADED': if (!action.uris){ return Object.assign({}, ui, { diff --git a/src/js/views/Artist.js b/src/js/views/Artist.js index 4d0ca796..e80d05f9 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -150,14 +150,14 @@ class Artist extends React.Component{ return (
-
+
0 ? "col w70" : "col w100"}>

Top tracks

{ this.props.artist.tracks ? : null }
- {this.props.artist.related_artists ?

Related artists

: null} + {related_artists.length > 0 ?

Related artists

: null}