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/bootstrap.js b/src/js/bootstrap.js index 2a6371df..2ba1661b 100755 --- a/src/js/bootstrap.js +++ b/src/js/bootstrap.js @@ -65,6 +65,7 @@ var initialState = { playlists: [], tracks: [] }, + library_playlists: [], playlists: [], notifications: [] } diff --git a/src/js/components/ArtistSentence.js b/src/js/components/ArtistSentence.js index dfe527c6..74677d94 100755 --- a/src/js/components/ArtistSentence.js +++ b/src/js/components/ArtistSentence.js @@ -16,14 +16,16 @@ 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 '; }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 0e65d0a1..ecf4707d 100755 --- a/src/js/components/FollowButton.js +++ b/src/js/components/FollowButton.js @@ -30,9 +30,8 @@ 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 )] - if( item.following === true ){ + if( this.props.is_following === true ){ return }else{ return @@ -42,11 +41,7 @@ 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 + spotify_authorized: state.spotify.authorized } } diff --git a/src/js/components/GridItem.js b/src/js/components/GridItem.js index ac8ff435..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 ? 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/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/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/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..15e74f08 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 +} + + /** @@ -174,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/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 13954f9c..d06bac5b 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', - data: { uri: uri } - } -} - -export function getAlbum( uri ){ - return { - type: 'MOPIDY_ALBUM', + type: 'MOPIDY_GET_DIRECTORY', 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 getAlbum( uri ){ + return { + type: 'MOPIDY_GET_ALBUM', + data: { uri: uri } } } 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..b0de7a5b 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -282,57 +282,75 @@ 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( + {}, + { + 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 ) + } + ) + + store.dispatch({ + type: 'PLAYLIST_LOADED', + uri: playlist.uri, + 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 => { 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_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: 'MOPIDY_PLAYLIST_LOADED', data: playlist }) + store.dispatch({ + type: 'PLAYLIST_LOADED', + uri: playlist.uri, + 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,13 +375,17 @@ const MopidyMiddleware = (function(){ } } - store.dispatch({ type: 'PLAYLIST_TRACKS_LOADED', tracks: tracks }) + 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++ ){ @@ -382,7 +404,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 @@ -396,7 +418,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++ ){ @@ -404,7 +426,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 @@ -415,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', playlist: playlist }) + store.dispatch({ + type: 'PLAYLIST_UPDATED', + uri: action.uri, + playlist: playlist + }) }) }); break @@ -454,23 +480,50 @@ 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 + 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 + - case 'MOPIDY_ALBUM': - //store.dispatch({ type: 'MOPIDY_ALBUM_LOADED', data: false }); + /** + * =============================================================== ALBUM(S) ============= + * ====================================================================================== + **/ + + case 'MOPIDY_GET_ALBUMS': + instruct( socket, store, 'library.browse', { uri: 'local:directory?type=album' } ) + .then( response => { + store.dispatch({ + type: 'ALBUMS_LOADED', + albums: response + }); + store.dispatch({ + type: 'LOCAL_ALBUMS_LOADED', + uris: helpers.asURIs(response) + }); + }) + break; + + case 'MOPIDY_GET_ALBUM': instruct( socket, store, 'library.lookup', action.data ) .then( response => { var album = Object.assign( @@ -522,45 +575,92 @@ const MopidyMiddleware = (function(){ } } - store.dispatch({ type: 'MOPIDY_ALBUM_LOADED', data: album }); + store.dispatch({ + type: 'ALBUM_LOADED', + uri: album.uri, + album: album + }); }) }) break; + - case 'MOPIDY_ARTIST': - store.dispatch({ type: 'MOPIDY_ARTIST_LOADED', data: false }); - 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); - - for( var i = 0; i < response.length; i++ ){ - var album = response[i].album; + /** + * =============================================================== ARTIST(S) ============ + * ====================================================================================== + **/ - function getByURI( albumToCheck ){ - return album.uri == albumToCheck.uri - } - var existingAlbum = artist.albums.find(getByURI); - if( !existingAlbum ){ - artist.albums.push(album) - } - } - - // load artwork from LastFM - if( artist.images.length <= 0 ){ - if( artist.musicbrainz_id ){ - store.dispatch( lastfmActions.getArtist( false, artist.musicbrainz_id ) ) - }else{ - store.dispatch( lastfmActions.getArtist( artist.name ) ) - } - } - - store.dispatch({ type: 'MOPIDY_ARTIST_LOADED', data: artist }); + 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: 'ARTISTS_LOADED', + artists: response + }); + store.dispatch({ + type: 'LOCAL_ARTISTS_LOADED', + uris: helpers.asURIs(response) + }); }) break; + case 'MOPIDY_GET_ARTIST': + instruct( socket, store, 'library.lookup', action.data ) + .then( response => { + + var albums = [] + for( var i = 0; i < response.length; i++ ){ + var album = response[i].album; + if (album){ + function getByURI( albumToCheck ){ + return album.uri == albumToCheck.uri + } + var existingAlbum = albums.find(getByURI); + if( !existingAlbum ){ + albums.push(album) + } + } + } + if (albums){ + store.dispatch({ + type: 'ALBUMS_LOADED', + albums: albums + }) + } + + var artist = Object.assign( + {}, + response[0].artists[0], + { + albums_uris: helpers.asURIs(albums), + tracks: response.slice(0,10) + } + ) + + 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; + + + /** + * =============================================================== LOCAL ================ + * ====================================================================================== + **/ + case 'MOPIDY_DIRECTORY': store.dispatch({ type: 'MOPIDY_DIRECTORY_LOADED', data: false }); instruct( socket, store, 'library.browse', action.data ) @@ -569,22 +669,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..202f6e79 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -262,9 +262,29 @@ 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++){ + playlists.push(Object.assign( + {}, + response.playlists.items[i], + { + 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: 'PLAYLISTS_LOADED', + playlists: playlists + }); dispatch({ type: 'SPOTIFY_FEATURED_PLAYLISTS_LOADED', - data: response + data: { + message: response.message, + playlists: helpers.asURIs(response.playlists.items) + } }); }); } @@ -290,52 +310,69 @@ 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 ) + $.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 ) => { + + for (var i = 0; i < playlists_response.playlists.items.length; i++){ + + 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( + {}, + category_response, + { + items: false, + playlists: helpers.asURIs(playlists_response.playlists.items), + playlists_more: playlists_response.playlists.next, + playlists_total: playlists_response.playlists.total + } + ) + + dispatch({ + type: 'SPOTIFY_CATEGORY_LOADED', + data: category + }); + }); + } +} + +export function getNewReleases(){ + return (dispatch, getState) => { + sendRequest( dispatch, getState, 'browse/new-releases?country='+getState().spotify.country+'&limit=50' ) .then( response => { dispatch({ - type: 'SPOTIFY_CATEGORY_LOADED', + type: 'SPOTIFY_NEW_RELEASES_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 - }); - }); - } -} - -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 - }); - }); - } -} - -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, + uri: uri, data: response }); }); @@ -366,8 +403,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 @@ -411,7 +446,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 }); }); @@ -547,9 +583,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 @@ -567,19 +600,29 @@ export function getArtist( uri ){ sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/related-artists' ) .then( response => { - Object.assign(artist, { related_artists: response.artists }); - }), - - sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/albums' ) - .then( response => { - Object.assign(artist, { albums: 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: 'SPOTIFY_ARTIST_LOADED', - data: artist + 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 + }); + }) }); } } @@ -587,9 +630,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++ ){ @@ -599,10 +639,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 + }); + } }); } } @@ -610,15 +661,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 => { dispatch({ type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', data: response - }); + }) }); } } @@ -675,29 +723,54 @@ 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 + // dispatch our loaded artists (simple objects) + dispatch({ + type: 'ARTISTS_LOADED', + artists: response.artists + }); + + var album = Object.assign( + {}, + response, + { + 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 + }); // 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 + type: 'ARTISTS_LOADED', + artists: response.artists }); }); @@ -708,14 +781,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 - }); + }) }); } } @@ -774,15 +847,25 @@ 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', + uri: playlist.uri, + playlist: playlist }) }) } @@ -796,29 +879,15 @@ function loadNextPlaylistsBatch( dispatch, getState, playlists, lastResponse ){ loadNextPlaylistsBatch( dispatch, getState, playlists, response ) }); }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++ ){ - if( playlists[i].owner.id == getState().spotify.me.id ){ - playlists[i] = Object.assign({}, playlists[i], { can_edit: true }) - } - } - } - dispatch({ type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED', - data: playlists + 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 ) @@ -842,12 +911,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 }); @@ -861,6 +931,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 }); @@ -874,6 +945,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..68cd159e 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' ) ) @@ -95,6 +93,135 @@ 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', + 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/spotify/reducer.js b/src/js/services/spotify/reducer.js index cd91ae27..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) @@ -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/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 72c1487b..77498346 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 }) @@ -132,36 +129,71 @@ export default function reducer(ui = {}, action){ * Albums **/ - case 'MOPIDY_ALBUM_LOADED': - if( !action.data ) return Object.assign({}, ui, { album: false }) - return Object.assign({}, ui, { album: action.data }); + case 'ALBUM_LOADED': + var albums = Object.assign([], ui.albums) - case 'LASTFM_ALBUM_LOADED': - if( !action.data.image ) return ui + if (albums[action.uri]){ + var album = Object.assign({}, albums[action.uri], action.album) + }else{ + var album = Object.assign({}, action.album) + } - var album = Object.assign({}, ui.album, { images: action.data.image }) - return Object.assign({}, ui, { album: album }); + albums[action.uri] = album + return Object.assign({}, ui, { albums: albums }); - case 'SPOTIFY_ALBUM_LOADED': - if( !action.data ) return Object.assign({}, ui, { album: false }) + case 'ALBUMS_LOADED': + console.log(action) + var albums = Object.assign([], ui.albums) - 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 }) + 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 + } - 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 }); + return Object.assign({}, ui, { albums: albums }); - 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, { + 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 + }); + + 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, { + 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 + }); @@ -169,34 +201,73 @@ 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': + console.log(action) + var artists = Object.assign([], ui.artists) - case 'LASTFM_ARTIST_LOADED': - if( !action.data.image ) return ui + 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) + } - // 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 }); + artists[action.uri] = artist + return Object.assign({}, ui, { artists: artists }); - case 'SPOTIFY_ARTIST_LOADED': - if( !action.data ) return Object.assign({}, ui, { artist: false }) - return Object.assign({}, ui, { artist: action.data }) + case 'ARTISTS_LOADED': + var artists = Object.assign([], ui.artists) - 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 }); + 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 + } - case 'ARTIST_FOLLOWING_LOADED': - var artist = Object.assign({}, ui.artist, { following: action.is_following }) - return Object.assign({}, ui, { artist: artist, following_loading: false }); + 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 '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 + }); + + case 'LOCAL_ARTISTS_LOADED': + if (!action.uris) return Object.assign({}, ui, { local_artists: null }); + return Object.assign({}, ui, { local_artists: action.uris }); /** @@ -223,74 +294,76 @@ 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) + + 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 + } + + 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 'SPOTIFY_PLAYLIST_LOADED': - if( !action.data ) return Object.assign({}, ui, { playlist: false }) + case 'PLAYLIST_LOADED_MORE_TRACKS': + var playlists = Object.assign([], 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 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 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) + 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_LOADED': - var playlist = Object.assign({}, ui.playlist, { tracks: action.tracks }) - return Object.assign({}, ui, { playlist: playlist }); + case 'PLAYLIST_TRACKS': + 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 - 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 @@ -304,26 +377,25 @@ 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 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 }) + playlists[action.uri] = playlist + return Object.assign({}, ui, { playlists: playlists }); - /** - * Library Playlists - **/ + case 'LIBRARY_PLAYLISTS_LOADED': + var library_playlists = [] + if (ui.library_playlists) library_playlists = ui.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') return Object.assign({}, ui, { - playlists: playlists + library_playlists: [...library_playlists, ...action.uris] }); diff --git a/src/js/views/Album.js b/src/js/views/Album.js index 0b402ad3..2adaa4f6 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 ){ @@ -46,11 +46,21 @@ class Album extends React.Component{ 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': - 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; } } @@ -68,16 +78,26 @@ class Album extends React.Component{ render(){ if( !this.props.album ) return null + var artists = [] + 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]) + } + } + } + return (
- +
- { helpers.uriSource(this.props.params.uri) == 'spotify' ? : null } + { helpers.uriSource(this.props.params.uri) == 'spotify' ? : null }
    @@ -93,7 +113,7 @@ class Album extends React.Component{

    { this.props.album.name }

    -

    +

    @@ -116,7 +136,9 @@ class Album extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - album: state.ui.album, + artists: state.ui.artists, + 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/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/Artist.js b/src/js/views/Artist.js index 08927b09..e80d05f9 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -49,17 +49,31 @@ 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') ) } } } 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': + 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 } // go back to overview @@ -68,7 +82,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(){ @@ -83,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 @@ -94,13 +106,32 @@ 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++){ + var uri = this.props.artist.related_artists_uris[i] + if (this.props.artists.hasOwnProperty(uri)){ + related_artists.push(this.props.artists[uri]) + } + } + } + + var albums = [] + 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]) + } + } + } if( this.state.sub_view == 'related_artists' ){ return (

    Related artists

    - { this.props.artist.related_artists ? : null } +
    ) @@ -117,26 +148,22 @@ class Artist extends React.Component{ ) } - // default body return (
    -
    +
    0 ? "col w70" : "col w100"}>

    Top tracks

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

    Related artists

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

    Related artists

    : null}

    Albums

    - { this.props.artist.albums ? : null } + this.loadMore() }/>
    @@ -147,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 ( @@ -171,7 +198,7 @@ class Artist extends React.Component{
    - { helpers.uriSource(this.props.params.uri) == 'spotify' ? : null } + { helpers.uriSource(this.props.params.uri) == 'spotify' ? : null }
      @@ -203,7 +230,9 @@ class Artist extends React.Component{ const mapStateToProps = (state, ownProps) => { return { - artist: state.ui.artist, + artist: (state.ui.artists && typeof(state.ui.artists[ownProps.params.uri]) !== 'undefined' ? state.ui.artists[ownProps.params.uri] : false ), + artists: state.ui.artists, + albums: state.ui.albums, spotify_authorized: state.spotify.authorized, mopidy_connected: state.mopidy.connected } diff --git a/src/js/views/Playlist.js b/src/js/views/Playlist.js index f985a768..dceb80f0 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 ( @@ -123,7 +110,7 @@ class Playlist extends React.Component{ ) } - return + 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[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 6ac1dcd1..a22f5026 100755 --- a/src/js/views/discover/DiscoverCategory.js +++ b/src/js/views/discover/DiscoverCategory.js @@ -17,29 +17,35 @@ 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 ); } } 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() }/>
    @@ -55,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/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/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 b3f731c7..e7ad2ab8 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 (
    @@ -85,7 +85,7 @@ class LibraryAlbums extends React.Component{

    - +
) }) @@ -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 } } 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 } } diff --git a/src/js/views/library/LibraryPlaylists.js b/src/js/views/library/LibraryPlaylists.js index 6e89607a..8d8273a5 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) } @@ -60,7 +67,7 @@ class LibraryPlaylists extends React.Component{ { width: 10, label: 'Tracks', - name: 'tracks.total' + name: 'tracks_total' } ] return ( @@ -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 } }