diff --git a/VERSION.md b/VERSION.md index 81f1b89f..10fa79aa 100755 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -3.4.7 \ No newline at end of file +3.4.8 \ No newline at end of file diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 6c02e102..b31a64c1 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -19,63 +19,63 @@ const sendRequest = ( dispatch, getState, endpoint, method = 'GET', data = false return new Promise( (resolve, reject) => { getToken( dispatch, getState ) - .then( response => { + .then( + response => { - // prepend the API baseurl, unless the endpoint already has it (ie pagination requests) - var url = 'https://api.spotify.com/v1/'+endpoint - if (endpoint.startsWith('https://api.spotify.com/')) url = endpoint; + // prepend the API baseurl, unless the endpoint already has it (ie pagination requests) + var url = 'https://api.spotify.com/v1/'+endpoint + if (endpoint.startsWith('https://api.spotify.com/')) url = endpoint; - // create our ajax request config - var config = { - method: method, - url: url, - cached: true, - headers: { - Authorization: 'Bearer '+ response + // create our ajax request config + var config = { + method: method, + url: url, + cached: true, + headers: { + Authorization: 'Bearer '+ response + } } - } - // only if we've got data do we add it to the request (this prevents appending of "&false" to the URL) - if (data){ - if (typeof(data) === 'string'){ - config.data = data - } else { - config.data = JSON.stringify(data) + // only if we've got data do we add it to the request (this prevents appending of "&false" to the URL) + if (data){ + if (typeof(data) === 'string'){ + config.data = data + } else { + config.data = JSON.stringify(data) + } } - } - // add reference to loader queue - var loader_key = helpers.generateGuid() - dispatch(uiActions.startLoading(loader_key, 'spotify_'+endpoint)) + // add reference to loader queue + var loader_key = helpers.generateGuid() + dispatch(uiActions.startLoading(loader_key, 'spotify_'+endpoint)) - $.ajax(config).then( - response => { - dispatch(uiActions.stopLoading(loader_key)) - resolve(response) - }, - (xhr, status, error) => { - dispatch(uiActions.stopLoading(loader_key)) - dispatch(coreActions.handleException( - 'Spotify: '+xhr.responseJSON.error.message, - { - source: 'spotify/actions.js/sendRequest', + $.ajax(config).then( + response => { + dispatch(uiActions.stopLoading(loader_key)) + resolve(response) + }, + (xhr, status, error) => { + dispatch(uiActions.stopLoading(loader_key)) + + // TODO: Instead of allowing request to fail before renewing the token, once refreshed + // we should retry the original request(s) + if (xhr.responseJSON.error.message == 'The access token expired'){ + dispatch(refreshToken(dispatch, getState)) + } + + reject({ config: config, xhr: xhr, status: status, error: error - } - )) - - // TODO: Instead of allowing request to fail before renewing the token, once refreshed - // we should retry the original request(s) - if (xhr.responseJSON.error.message == 'The access token expired'){ - dispatch(refreshToken(dispatch, getState)) + }) } - - reject(error) - } - ) - }); + ) + }, + error => { + reject(error) + } + ); } ); } @@ -100,6 +100,9 @@ function getToken( dispatch, getState ){ .then( response => { resolve(response.access_token) + }, + error => { + reject(error) } ); }); @@ -130,43 +133,33 @@ function refreshToken( dispatch, getState ){ }, (xhr, status, error) => { dispatch({ type: 'SPOTIFY_DISCONNECTED' }) - dispatch(coreActions.handleException( - 'Spotify: '+xhr.responseJSON.error_description, - { - source: 'spotify/actions.js/refreshToken', - config: config, - xhr: xhr, - status: status, - error: error - } - )) - reject(error) + reject({ + config: config, + xhr: xhr, + status: status, + error: error + }); } ); } else { - $.ajax({ - method: 'GET', - url: '//'+getState().mopidy.host+':'+getState().mopidy.port+'/iris/http/refresh_spotify_token', - dataType: "json", - timeout: 10000 - }) + var config = { + method: 'GET', + url: '//'+getState().mopidy.host+':'+getState().mopidy.port+'/iris/http/refresh_spotify_token', + dataType: "json", + timeout: 10000 + } + + $.ajax(config) .then( response => { if (response.type == 'error'){ dispatch({ type: 'SPOTIFY_DISCONNECTED' }) - dispatch(coreActions.handleException( - 'Spotify: '+response.message, - { - source: 'spotify/actions.js/refreshToken', - config: config, - xhr: xhr, - status: status, - error: error - } - )) - reject(response) + reject({ + config: config, + error: response + }) } else { var token = response.spotify_token @@ -181,19 +174,14 @@ function refreshToken( dispatch, getState ){ } }, - error => { + (xhr, status, error) => { dispatch({ type: 'SPOTIFY_DISCONNECTED' }) - dispatch(coreActions.handleException( - 'Spotify: Could not refresh token', - { - source: 'spotify/actions.js/refreshToken', - config: config, - xhr: xhr, - status: status, - error: error - } - )) - reject(error) + reject({ + config: config, + xhr: xhr, + status: status, + error: error + }); } ); } @@ -216,11 +204,19 @@ export function connect(){ // send a generic request to ensure spotify is up and running // there is no 'test' or 'ping' endpoint on the Spotify API sendRequest( dispatch, getState, 'browse/categories?limit=1' ) - .then( response => { - dispatch({ - type: 'SPOTIFY_CONNECTED' - }); - }); + .then( + response => { + dispatch({ + type: 'SPOTIFY_CONNECTED' + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not connect to Spotify', + error + )); + } + ); } } @@ -264,12 +260,20 @@ export function getMe(){ dispatch({ type: 'SPOTIFY_ME_LOADED', data: false }); sendRequest( dispatch, getState, 'me' ) - .then( response => { - dispatch({ - type: 'SPOTIFY_ME_LOADED', - data: response - }); - }); + .then( + response => { + dispatch({ + type: 'SPOTIFY_ME_LOADED', + data: response + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load your profile', + error + )); + } + ); } } @@ -286,7 +290,8 @@ export function getTrack( uri ){ dispatch({ type: 'SPOTIFY_TRACK_LOADED', data: false }); sendRequest( dispatch, getState, 'tracks/'+ helpers.getFromUri('trackid', uri) ) - .then( response => { + .then( + response => { let track = Object.assign( {}, response, @@ -299,6 +304,12 @@ export function getTrack( uri ){ key: uri, track: track }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load track', + error + )); } ); } @@ -310,12 +321,20 @@ export function getLibraryTracks(){ dispatch({ type: 'SPOTIFY_LIBRARY_TRACKS_LOADED', data: false }); sendRequest( dispatch, getState, 'me/tracks?limit=50' ) - .then( response => { - dispatch({ - type: 'SPOTIFY_LIBRARY_TRACKS_LOADED', - data: response - }); - }); + .then( + response => { + dispatch({ + type: 'SPOTIFY_LIBRARY_TRACKS_LOADED', + data: response + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not get library tracks', + error + )); + } + ); } } @@ -340,49 +359,64 @@ export function getFeaturedPlaylists(){ var timestamp = year+'-'+month+'-'+day+'T'+hour+':'+min+':'+sec; sendRequest( dispatch, getState, 'browse/featured-playlists?timestamp='+timestamp+'&country='+getState().core.country+'&limit=50&locale='+getState().core.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 - } - )) - } - - // Pick the first playlist, and get the full playlist object - // We use this as in our introduction parallax panel, and need the full playlist - dispatch(getPlaylist(playlists[0].uri)) - - dispatch({ - type: 'PLAYLISTS_LOADED', - playlists: playlists - }); - - dispatch({ - type: 'SPOTIFY_FEATURED_PLAYLISTS_LOADED', - data: { - message: response.message, - playlists: helpers.arrayOf('uri',response.playlists.items) + .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 + } + )) } - }); - }); + + // Pick the first playlist, and get the full playlist object + // We use this as in our introduction parallax panel, and need the full playlist + dispatch(getPlaylist(playlists[0].uri)) + + dispatch({ + type: 'PLAYLISTS_LOADED', + playlists: playlists + }); + + dispatch({ + type: 'SPOTIFY_FEATURED_PLAYLISTS_LOADED', + data: { + message: response.message, + playlists: helpers.arrayOf('uri',response.playlists.items) + } + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load featured tracks', + error + )); + } + ); } } export function getCategories(){ return (dispatch, getState) => { sendRequest( dispatch, getState, 'browse/categories?limit=50&country='+getState().core.country+'&locale='+getState().core.locale ) - .then( response => { - dispatch({ - type: 'CATEGORIES_LOADED', - categories: response.categories.items - }); - }); + .then( + response => { + dispatch({ + type: 'CATEGORIES_LOADED', + categories: response.categories.items + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load categories', + error + )); + } + ); } } @@ -399,68 +433,99 @@ export function getCategory( id ){ // get the category sendRequest( dispatch, getState, 'browse/categories/'+id+'?country='+getState().core.country+'&locale='+getState().core.locale ) - .then( response => { - var category = Object.assign({}, response) - dispatch({ - type: 'CATEGORY_LOADED', - key: 'category:'+id, - category: Object.assign({}, response) - }); - }) + .then( + response => { + var category = Object.assign({}, response) + dispatch({ + type: 'CATEGORY_LOADED', + key: 'category:'+id, + category: Object.assign({}, response) + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load category', + error + )); + } + ) // and the category's playlists sendRequest( dispatch, getState, 'browse/categories/'+id+'/playlists?limit=50&country='+getState().core.country+'&locale='+getState().core.locale ) - .then( response => { + .then( + response => { + var playlists = [] + for (var i = 0; i < response.playlists.items.length; i++){ + playlists.push(Object.assign( + {}, + response.playlists.items[i], + { + tracks: null, + tracks_more: null, + tracks_total: response.playlists.items[i].tracks.total + } + )) + } - var playlists = [] - for (var i = 0; i < response.playlists.items.length; i++){ - playlists.push(Object.assign( - {}, - response.playlists.items[i], - { - tracks: null, - tracks_more: null, - tracks_total: response.playlists.items[i].tracks.total - } - )) + dispatch({ + type: 'PLAYLISTS_LOADED', + playlists: playlists + }); + + dispatch({ + type: 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED', + key: 'category:'+id, + data: response + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load category playlists', + error + )); } - - dispatch({ - type: 'PLAYLISTS_LOADED', - playlists: playlists - }); - - dispatch({ - type: 'SPOTIFY_CATEGORY_PLAYLISTS_LOADED', - key: 'category:'+id, - data: response - }); - }) + ) } } export function getNewReleases(){ return (dispatch, getState) => { sendRequest( dispatch, getState, 'browse/new-releases?country='+getState().core.country+'&limit=50' ) - .then( response => { - dispatch({ - type: 'SPOTIFY_NEW_RELEASES_LOADED', - data: response - }); - }); + .then( + response => { + dispatch({ + type: 'SPOTIFY_NEW_RELEASES_LOADED', + data: response + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load new releases', + error + )); + } + ); } } export function getURL( url, action_name, key = false ){ return (dispatch, getState) => { sendRequest( dispatch, getState, url ) - .then( response => { - dispatch({ - type: action_name, - key: key, - data: response - }); - }); + .then( + response => { + dispatch({ + type: action_name, + key: key, + data: response + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load URL', + error + )); + } + ); } } @@ -487,70 +552,77 @@ export function getSearchResults(type, query, limit = 50, offset = 0){ url += '&offset='+offset sendRequest( dispatch, getState, url ) - .then( response => { - - if (response.tracks !== undefined){ - dispatch({ - type: 'SPOTIFY_SEARCH_RESULTS_LOADED', - context: 'tracks', - results: response.tracks.items, - more: response.tracks.next, - }); - } - - if (response.artists !== undefined){ - dispatch({ - type: 'ARTISTS_LOADED', - artists: response.artists.items - }); - dispatch({ - type: 'SPOTIFY_SEARCH_RESULTS_LOADED', - context: 'artists', - results: helpers.arrayOf('uri',response.artists.items), - more: response.artists.next, - }); - } - - if (response.albums !== undefined){ - dispatch({ - type: 'ALBUMS_LOADED', - albums: response.albums.items - }); - dispatch({ - type: 'SPOTIFY_SEARCH_RESULTS_LOADED', - context: 'albums', - results: helpers.arrayOf('uri',response.albums.items), - more: response.albums.next, - }); - } - - if (response.playlists !== undefined){ - 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 - } - )) + .then( + response => { + if (response.tracks !== undefined){ + dispatch({ + type: 'SPOTIFY_SEARCH_RESULTS_LOADED', + context: 'tracks', + results: response.tracks.items, + more: response.tracks.next, + }); + } + + if (response.artists !== undefined){ + dispatch({ + type: 'ARTISTS_LOADED', + artists: response.artists.items + }); + dispatch({ + type: 'SPOTIFY_SEARCH_RESULTS_LOADED', + context: 'artists', + results: helpers.arrayOf('uri',response.artists.items), + more: response.artists.next, + }); + } + + if (response.albums !== undefined){ + dispatch({ + type: 'ALBUMS_LOADED', + albums: response.albums.items + }); + dispatch({ + type: 'SPOTIFY_SEARCH_RESULTS_LOADED', + context: 'albums', + results: helpers.arrayOf('uri',response.albums.items), + more: response.albums.next, + }); } - dispatch({ - type: 'PLAYLISTS_LOADED', - playlists: playlists - }); - dispatch({ - type: 'SPOTIFY_SEARCH_RESULTS_LOADED', - context: 'playlists', - results: helpers.arrayOf('uri',playlists), - more: response.playlists.next - }); + if (response.playlists !== undefined){ + 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_SEARCH_RESULTS_LOADED', + context: 'playlists', + results: helpers.arrayOf('uri',playlists), + more: response.playlists.next + }); + } + + dispatch(uiActions.processFinished('SPOTIFY_GET_SEARCH_RESULTS_PROCESSOR')) + }, + error => { + dispatch(coreActions.handleException( + 'Could not load search results', + error + )); } - - dispatch(uiActions.processFinished('SPOTIFY_GET_SEARCH_RESULTS_PROCESSOR')) - }); + ); } } @@ -570,34 +642,42 @@ export function getAutocompleteResults(field_id, query, types = ['album','artist endpoint += '&country='+getState().core.country sendRequest(dispatch, getState, endpoint) - .then(response => { - var genres = [] - if (genre_included){ - var available_genres = getState().ui.genres - if (available_genres){ - for (var i = 0; i < available_genres.length; i++){ - if (available_genres[i].includes(query)){ - var genre = available_genres[i] - genres.push({ - name: (genre.charAt(0).toUpperCase()+genre.slice(1)).replace('-',' '), - uri: 'spotify:genre:'+genre - }) + .then( + response => { + var genres = [] + if (genre_included){ + var available_genres = getState().ui.genres + if (available_genres){ + for (var i = 0; i < available_genres.length; i++){ + if (available_genres[i].includes(query)){ + var genre = available_genres[i] + genres.push({ + name: (genre.charAt(0).toUpperCase()+genre.slice(1)).replace('-',' '), + uri: 'spotify:genre:'+genre + }) + } + } } } + dispatch({ + type: 'SPOTIFY_AUTOCOMPLETE_LOADED', + field_id: field_id, + results: { + artists: (response.artists ? response.artists.items : []), + albums: (response.albums ? response.albums.items : []), + playlists: (response.playlists ? response.playlists.items : []), + tracks: (response.tracks ? response.tracks.items : []), + genres: genres + } + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load autocomplete results', + error + )); } - } - dispatch({ - type: 'SPOTIFY_AUTOCOMPLETE_LOADED', - field_id: field_id, - results: { - artists: (response.artists ? response.artists.items : []), - albums: (response.albums ? response.albums.items : []), - playlists: (response.playlists ? response.playlists.items : []), - tracks: (response.tracks ? response.tracks.items : []), - genres: genres - } - }); - }) + ); } } @@ -650,16 +730,24 @@ export function following(uri, method = 'GET'){ } sendRequest( dispatch, getState, endpoint, method, data ) - .then( response => { - if( response ) is_following = response - if( typeof(is_following) === 'object' ) is_following = is_following[0] + .then( + response => { + if( response ) is_following = response + if( typeof(is_following) === 'object' ) is_following = is_following[0] - dispatch({ - type: 'SPOTIFY_LIBRARY_'+asset_name.toUpperCase()+'_CHECK', - key: uri, - in_library: is_following - }) - }); + dispatch({ + type: 'SPOTIFY_LIBRARY_'+asset_name.toUpperCase()+'_CHECK', + key: uri, + in_library: is_following + }) + }, + error => { + dispatch(coreActions.handleException( + 'Could not follow/unfollow', + error + )); + } + ); } } @@ -679,16 +767,24 @@ export function resolveRadioSeeds( radio ){ } sendRequest( dispatch, getState, 'artists?ids='+ artist_ids ) - .then( response => { - if (response && response.artists){ - dispatch({ - type: 'ARTISTS_LOADED', - artists: response.artists - }) - } else { - console.error('No Spotify artists returned', artist_ids) + .then( + response => { + if (response && response.artists){ + dispatch({ + type: 'ARTISTS_LOADED', + artists: response.artists + }) + } else { + console.error('No Spotify artists returned', artist_ids) + } + }, + error => { + dispatch(coreActions.handleException( + 'Could not resolve radio artist seeds', + error + )); } - }) + ) } if (radio.seed_tracks.length > 0){ @@ -699,12 +795,20 @@ export function resolveRadioSeeds( radio ){ } sendRequest( dispatch, getState, 'tracks?ids='+ track_ids ) - .then( response => { - dispatch({ - type: 'TRACKS_LOADED', - tracks: response.tracks - }) - }) + .then( + response => { + dispatch({ + type: 'TRACKS_LOADED', + tracks: response.tracks + }) + }, + error => { + dispatch(coreActions.handleException( + 'Could not load radio track seeds', + error + )); + } + ) } } } @@ -730,13 +834,21 @@ export function getFavorites(limit = 50, term = 'long_term'){ sendRequest(dispatch, getState, 'me/top/artists?limit='+limit+'&time_range='+term), sendRequest(dispatch, getState, 'me/top/tracks?limit='+limit+'&time_range='+term) - ).then((artists_response, tracks_response) => { - dispatch({ - type: 'SPOTIFY_FAVORITES_LOADED', - artists: artists_response.items, - tracks: tracks_response.items - }); - }) + ).then( + (artists_response, tracks_response) => { + dispatch({ + type: 'SPOTIFY_FAVORITES_LOADED', + artists: artists_response.items, + tracks: tracks_response.items + }); + }, + (artists_error, tracks_error) => { + dispatch(coreActions.handleException( + 'Could not load favorites', + Object.assign({},artists_error,tracks_error) + )); + } + ) } } @@ -788,56 +900,64 @@ export function getRecommendations(uris = [], limit = 20){ endpoint += '&limit='+limit sendRequest(dispatch, getState, endpoint) - .then( response => { + .then( + response => { - // We only get simple artist objects, so we need to - // get the full object. We'll add URIs to our recommendations - // anyway so we can proceed in the meantime - var artists_uris = [] - if (response.tracks.length > artists_ids.length && response.tracks.length > 10){ - while (artists_uris.length < 5){ - var random_index = Math.round(Math.random() * (response.tracks.length - 1)) - var artist = response.tracks[random_index].artists[0] + // We only get simple artist objects, so we need to + // get the full object. We'll add URIs to our recommendations + // anyway so we can proceed in the meantime + var artists_uris = [] + if (response.tracks.length > artists_ids.length && response.tracks.length > 10){ + while (artists_uris.length < 5){ + var random_index = Math.round(Math.random() * (response.tracks.length - 1)) + var artist = response.tracks[random_index].artists[0] - // Make sure this artist is not already in our sample, and - // is not one of the seeds - if (!artists_uris.includes(artist.uri) && !artists_ids.includes(artist.id)){ - artists_uris.push(artist.uri) - dispatch(getArtist(artist.uri)) + // Make sure this artist is not already in our sample, and + // is not one of the seeds + if (!artists_uris.includes(artist.uri) && !artists_ids.includes(artist.id)){ + artists_uris.push(artist.uri) + dispatch(getArtist(artist.uri)) + } } } - } - // Copy already loaded albums into array - var albums = [] - var albums_uris = [] - if (response.tracks.length > 10){ - while (albums.length < 5){ - var random_index = Math.round(Math.random() * (response.tracks.length - 1)) - var album = response.tracks[random_index].album + // Copy already loaded albums into array + var albums = [] + var albums_uris = [] + if (response.tracks.length > 10){ + while (albums.length < 5){ + var random_index = Math.round(Math.random() * (response.tracks.length - 1)) + var album = response.tracks[random_index].album - // Make sure this album is not already in our sample - if (!albums_uris.includes(album.uri)){ - albums_uris.push(album.uri) - albums.push(album) + // Make sure this album is not already in our sample + if (!albums_uris.includes(album.uri)){ + albums_uris.push(album.uri) + albums.push(album) + } } } + + // Officially add albums to index + dispatch({ + type: 'ALBUMS_LOADED', + albums: albums + }) + + dispatch({ + type: 'SPOTIFY_RECOMMENDATIONS_LOADED', + seeds_uris: uris, + tracks: response.tracks, + artists_uris: artists_uris, + albums_uris: helpers.arrayOf('uri',albums) + }) + }, + error => { + dispatch(coreActions.handleException( + 'Could not load recommendations', + error + )); } - - // Officially add albums to index - dispatch({ - type: 'ALBUMS_LOADED', - albums: albums - }) - - dispatch({ - type: 'SPOTIFY_RECOMMENDATIONS_LOADED', - seeds_uris: uris, - tracks: response.tracks, - artists_uris: artists_uris, - albums_uris: helpers.arrayOf('uri',albums) - }) - }) + ) } } @@ -850,12 +970,20 @@ export function getRecommendations(uris = [], limit = 20){ export function getGenres(){ return (dispatch, getState) => { sendRequest(dispatch, getState, 'recommendations/available-genre-seeds') - .then( response => { - dispatch({ - type: 'SPOTIFY_GENRES_LOADED', - genres: response.genres - }); - }) + .then( + response => { + dispatch({ + type: 'SPOTIFY_GENRES_LOADED', + genres: response.genres + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load genres', + error + )); + } + ) } } @@ -883,29 +1011,55 @@ export function getArtist(uri, full = false){ // We need our artist, obviously var requests = [ sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) ) - .then( response => { - Object.assign(artist, response); - }) - ] + .then( + response => { + Object.assign(artist, response); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load artist', + error + )); + } + ) + ]; // Do we want a full artist, with all supporting material? if (full){ + requests.push( sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/top-tracks?country='+getState().core.country ) - .then( response => { - Object.assign(artist, response); - }) - ) + .then( + response => { + Object.assign(artist, response); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load artist\'s top tracks', + error + )); + } + ) + ); + requests.push( sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/related-artists' ) - .then( response => { - dispatch({ - type: 'ARTISTS_LOADED', - artists: response.artists - }); - Object.assign(artist, { related_artists_uris: helpers.arrayOf('uri',response.artists) }); - }) - ) + .then( + response => { + dispatch({ + type: 'ARTISTS_LOADED', + artists: response.artists + }); + Object.assign(artist, { related_artists_uris: helpers.arrayOf('uri',response.artists) }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load artist\'s related artists', + error + )); + } + ) + ); } // Run our requests @@ -926,13 +1080,21 @@ export function getArtist(uri, full = false){ // Now go get our artist albums if (full){ sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/albums?market='+getState().core.country ) - .then( response => { - dispatch({ - type: 'SPOTIFY_ARTIST_ALBUMS_LOADED', - data: response, - key: uri - }) - }) + .then( + response => { + dispatch({ + type: 'SPOTIFY_ARTIST_ALBUMS_LOADED', + data: response, + key: uri + }) + }, + error => { + dispatch(coreActions.handleException( + 'Could not load artist\'s albums', + error + )); + } + ); } }) } @@ -949,23 +1111,31 @@ export function getArtists( uris ){ } sendRequest( dispatch, getState, 'artists/?ids='+ids ) - .then( response => { - for (var i = i; i < response.length; i++){ - var artist = response - for (var i = 0; i < artist.albums.length; i++){ + .then( + 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.arrayOf('uri',artist.albums) + artist.albums_more = artist.albums.next dispatch({ - type: 'ALBUM_LOADED', - album: artist.albums[i] - }); + type: 'ARTIST_LOADED', + artist: artist + }); } - artist.albums = helpers.arrayOf('uri',artist.albums) - artist.albums_more = artist.albums.next - dispatch({ - type: 'ARTIST_LOADED', - artist: artist - }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load artists', + error + )); } - }); + ); } } @@ -982,10 +1152,18 @@ export function playArtistTopTracks(uri){ // We need to load the artist's top tracks first } else { sendRequest( dispatch, getState, 'artists/'+ helpers.getFromUri('artistid', uri) +'/top-tracks?country='+getState().core.country ) - .then( response => { - const uris = helpers.arrayOf('uri',response.tracks) - dispatch(mopidyActions.playURIs(uris, uri)) - }) + .then( + response => { + const uris = helpers.arrayOf('uri',response.tracks) + dispatch(mopidyActions.playURIs(uris, uri)) + }, + error => { + dispatch(coreActions.handleException( + 'Could not play artist\'s top tracks', + error + )); + } + ) } } } @@ -1002,13 +1180,21 @@ export function getUser(uri){ // get the user sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) ) - .then( response => { - dispatch({ - type: 'USER_LOADED', - key: response.uri, - user: response - }); - }) + .then( + response => { + dispatch({ + type: 'USER_LOADED', + key: response.uri, + user: response + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load user', + error + )); + } + ) dispatch(getUserPlaylists(uri)) } @@ -1019,37 +1205,44 @@ export function getUserPlaylists(user_uri){ // get the first page of playlists sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid', user_uri) +'/playlists?limit=40' ) - .then( response => { + .then( + response => { + var playlists = [] + for (var i = 0; i < response.items.length; i++){ - var playlists = [] - for (var i = 0; i < response.items.length; i++){ + var can_edit = false + if (getState().spotify.me && response.items[i].owner.id == getState().spotify.me.id){ + can_edit = true + } - var can_edit = false - if (getState().spotify.me && response.items[i].owner.id == getState().spotify.me.id){ - can_edit = true + playlists.push(Object.assign( + {}, + response.items[i], + { + can_edit: can_edit, + tracks_total: response.items[i].tracks.total + } + )) } - playlists.push(Object.assign( - {}, - response.items[i], - { - can_edit: can_edit, - tracks_total: response.items[i].tracks.total - } - )) + dispatch({ + type: 'PLAYLISTS_LOADED', + playlists: playlists + }); + + dispatch({ + type: 'SPOTIFY_USER_PLAYLISTS_LOADED', + key: user_uri, + data: response + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load user\'s playlists', + error + )); } - - dispatch({ - type: 'PLAYLISTS_LOADED', - playlists: playlists - }); - - dispatch({ - type: 'SPOTIFY_USER_PLAYLISTS_LOADED', - key: user_uri, - data: response - }); - }) + ); } } @@ -1071,56 +1264,72 @@ export function getAlbum( uri ){ // get the album sendRequest( dispatch, getState, 'albums/'+ helpers.getFromUri('albumid', uri) ) - .then( response => { + .then( + response => { - // dispatch our loaded artists (simple objects) - dispatch({ - type: 'ARTISTS_LOADED', - artists: response.artists - }); - - var album = Object.assign( - {}, - response, - { - artists_uris: helpers.arrayOf('uri',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', - key: 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 < response.artists.length; i++ ){ - artist_ids.push( helpers.getFromUri( 'artistid', response.artists[i].uri ) ) - } - - // get all album artists as full objects - sendRequest( dispatch, getState, 'artists/?ids='+artist_ids ) - .then( response => { - dispatch({ - type: 'ARTISTS_LOADED', - artists: response.artists - }); + // dispatch our loaded artists (simple objects) + dispatch({ + type: 'ARTISTS_LOADED', + artists: response.artists }); - }) + var album = Object.assign( + {}, + response, + { + artists_uris: helpers.arrayOf('uri',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', + key: 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 < response.artists.length; i++ ){ + artist_ids.push( helpers.getFromUri( 'artistid', response.artists[i].uri ) ) + } + + // get all album artists as full objects + sendRequest( dispatch, getState, 'artists/?ids='+artist_ids ) + .then( + response => { + dispatch({ + type: 'ARTISTS_LOADED', + artists: response.artists + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not load album\'s artists', + error + )); + } + ); + + }, + error => { + dispatch(coreActions.handleException( + 'Could not load album', + error + )); + } + ) } } @@ -1130,13 +1339,21 @@ export function toggleAlbumInLibrary( uri, method ){ return (dispatch, getState) => { sendRequest( dispatch, getState, 'me/albums?ids='+ helpers.getFromUri('albumid',uri), method ) - .then( response => { - dispatch({ - type: 'SPOTIFY_ALBUM_FOLLOWING', - key: uri, - data: new_state - }); - }); + .then( + response => { + dispatch({ + type: 'SPOTIFY_ALBUM_FOLLOWING', + key: uri, + data: new_state + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not add/remove library album', + error + )); + } + ); } } @@ -1160,29 +1377,36 @@ export function createPlaylist( name, description, is_public, is_collaborative ) } sendRequest(dispatch, getState, 'users/'+ getState().spotify.me.id +'/playlists/', 'POST', data) - .then( response => { + .then( + response => { + dispatch({ + type: 'PLAYLIST_LOADED', + key: response.uri, + playlist: Object.assign( + {}, + response, + { + can_edit: true, + tracks: [], + tracks_more: null, + tracks_total: 0 + }) + }); - dispatch({ - type: 'PLAYLIST_LOADED', - key: response.uri, - playlist: Object.assign( - {}, - response, - { - can_edit: true, - tracks: [], - tracks_more: null, - tracks_total: 0 - }) - }); + dispatch({ + type: 'LIBRARY_PLAYLISTS_LOADED', + uris: [response.uri] + }) - dispatch({ - type: 'LIBRARY_PLAYLISTS_LOADED', - uris: [response.uri] - }) - - dispatch(uiActions.createNotification('Created playlist')) - }) + dispatch(uiActions.createNotification('Created playlist')) + }, + error => { + dispatch(coreActions.handleException( + 'Could not create playlist', + error + )); + } + ) } } @@ -1197,19 +1421,27 @@ export function savePlaylist(uri, name, description, is_public, is_collaborative } sendRequest( dispatch, getState, 'users/'+ getState().spotify.me.id +'/playlists/'+ helpers.getFromUri('playlistid',uri), 'PUT', data) - .then( response => { - dispatch({ - type: 'PLAYLIST_UPDATED', - key: uri, - playlist: { - name: name, - public: is_public, - collaborative: is_collaborative, - description: description - } - }) - dispatch(uiActions.createNotification('Saved')) - }) + .then( + response => { + dispatch({ + type: 'PLAYLIST_UPDATED', + key: uri, + playlist: { + name: name, + public: is_public, + collaborative: is_collaborative, + description: description + } + }) + dispatch(uiActions.createNotification('Saved')) + }, + error => { + dispatch(coreActions.handleException( + 'Could not save playlist', + error + )); + } + ) } } @@ -1218,35 +1450,43 @@ export function getPlaylist(uri){ // get the main playlist object sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) +'/playlists/'+ helpers.getFromUri('playlistid',uri) +'?market='+getState().core.country ) - .then( response => { + .then( + response => { - // convert links in description - var description = null - if (response.description){ - description = response.description - description = description.split(' { + dispatch(coreActions.handleException( + 'Could not load playlist', + error + )); + } + ) } } @@ -1272,53 +1512,61 @@ export function getPlaylistTracksForPlaying(uri){ export function getPlaylistTracksForPlayingProcessor(data){ return (dispatch, getState) => { sendRequest(dispatch, getState, data.next) - .then( response => { + .then( + response => { - // Check to see if we've been cancelled - if (getState().ui.processes['SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR'] !== undefined){ - var processor = getState().ui.processes['SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR'] + // Check to see if we've been cancelled + if (getState().ui.processes['SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR'] !== undefined){ + var processor = getState().ui.processes['SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR'] - if (processor.status == 'cancelling'){ - dispatch(uiActions.processCancelled('SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR')) - return false + if (processor.status == 'cancelling'){ + dispatch(uiActions.processCancelled('SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR')) + return false + } } - } - // Add on our new batch of loaded tracks - var uris = [] - var new_uris = [] - for (var i = 0; i < response.items.length; i++){ - new_uris.push(response.items[i].track.uri) - } - if (data.uris){ - uris = [...data.uris, ...new_uris]; - } else { - uris = new_uris; - } + // Add on our new batch of loaded tracks + var uris = [] + var new_uris = [] + for (var i = 0; i < response.items.length; i++){ + new_uris.push(response.items[i].track.uri) + } + if (data.uris){ + uris = [...data.uris, ...new_uris]; + } else { + uris = new_uris; + } - // We got a next link, so we've got more work to be done - if (response.next){ - dispatch(uiActions.updateProcess( - 'SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR', - 'Loading '+(response.total-uris.length)+' playlist tracks', - { - next: response.next, - total: response.total, - remaining: response.total - uris.length - } - )) - dispatch(uiActions.runProcess( - 'SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR', - { - next: response.next, - uris: uris - } - )) - } else { - dispatch(mopidyActions.playURIs(uris, data.uri)) - dispatch(uiActions.processFinished('SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR')) + // We got a next link, so we've got more work to be done + if (response.next){ + dispatch(uiActions.updateProcess( + 'SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR', + 'Loading '+(response.total-uris.length)+' playlist tracks', + { + next: response.next, + total: response.total, + remaining: response.total - uris.length + } + )) + dispatch(uiActions.runProcess( + 'SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR', + { + next: response.next, + uris: uris + } + )) + } else { + dispatch(mopidyActions.playURIs(uris, data.uri)) + dispatch(uiActions.processFinished('SPOTIFY_GET_PLAYLIST_TRACKS_FOR_PLAYING_PROCESSOR')) + } + }, + error => { + dispatch(coreActions.handleException( + 'Could not load tracks to play playlist', + error + )); } - }); + ); } } @@ -1328,57 +1576,89 @@ export function toggleFollowingPlaylist(uri, method){ return (dispatch, getState) => { sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) + '/playlists/'+ helpers.getFromUri('playlistid',uri) + '/followers', method ) - .then( response => { - dispatch({ - type: 'SPOTIFY_PLAYLIST_FOLLOWING_LOADED', - key: uri, - is_following: new_state - }); - }); + .then( + response => { + dispatch({ + type: 'SPOTIFY_PLAYLIST_FOLLOWING_LOADED', + key: uri, + is_following: new_state + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not add/remove library playlist', + error + )); + } + ); } } export function addTracksToPlaylist( uri, tracks_uris ){ return (dispatch, getState) => { 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', - key: uri, - tracks_uris: tracks_uris, - snapshot_id: response.snapshot_id - }); - }); + .then( + response => { + dispatch({ + type: 'PLAYLIST_TRACKS_ADDED', + key: uri, + tracks_uris: tracks_uris, + snapshot_id: response.snapshot_id + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not add tracks to playlist', + error + )); + } + ); } } export function deleteTracksFromPlaylist( uri, snapshot_id, tracks_indexes ){ return (dispatch, getState) => { sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) + '/playlists/'+ helpers.getFromUri('playlistid',uri) + '/tracks', 'DELETE', { snapshot_id: snapshot_id, positions: tracks_indexes } ) - .then( response => { - dispatch({ - type: 'PLAYLIST_TRACKS_REMOVED', - key: uri, - tracks_indexes: tracks_indexes, - snapshot_id: response.snapshot_id - }); - }); + .then( + response => { + dispatch({ + type: 'PLAYLIST_TRACKS_REMOVED', + key: uri, + tracks_indexes: tracks_indexes, + snapshot_id: response.snapshot_id + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not remove tracks from playlist', + error + )); + } + ); } } export function reorderPlaylistTracks( uri, range_start, range_length, insert_before, snapshot_id ){ return (dispatch, getState) => { sendRequest( dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) + '/playlists/'+ helpers.getFromUri('playlistid',uri) + '/tracks', 'PUT', { uri: uri, range_start: range_start, range_length: range_length, insert_before: insert_before, snapshot_id: snapshot_id } ) - .then( response => { - dispatch({ - type: 'PLAYLIST_TRACKS_REORDERED', - key: uri, - range_start: range_start, - range_length: range_length, - insert_before: insert_before, - snapshot_id: response.snapshot_id - }); - }); + .then( + response => { + dispatch({ + type: 'PLAYLIST_TRACKS_REORDERED', + key: uri, + range_start: range_start, + range_length: range_length, + insert_before: insert_before, + snapshot_id: response.snapshot_id + }); + }, + error => { + dispatch(coreActions.handleException( + 'Could not reorder playlist tracks', + error + )); + } + ); } } @@ -1388,6 +1668,12 @@ export function reorderPlaylistTracks( uri, range_start, range_length, insert_be * =============================================================== LIBRARY ============== * ====================================================================================== **/ + +export function flushLibrary(){ + return { + type: "SPOTIFY_FLUSH_LIBRARY" + } +} /** @@ -1401,9 +1687,11 @@ export function getLibraryPlaylists(){ if (!last_run){ dispatch(uiActions.startProcess('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR','Loading Spotify playlists', {next: 'me/playlists?limit=50'})) } else if (last_run.status == 'cancelled'){ - dispatch(uiActions.resumeProcess('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR')) - } else if (last_run.status == 'finished'){ - // TODO: do we want to force a refresh? + dispatch(uiActions.resumeProcess('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR')) + + // We've already finished, but the status has been flushed + } else if (last_run.status == 'finished' && !getState().spotify.library_playlists_loaded_all){ + dispatch(uiActions.startProcess('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR','Loading Spotify playlists', {next: 'me/playlists?limit=50'})) } } } @@ -1411,43 +1699,50 @@ export function getLibraryPlaylists(){ export function getLibraryPlaylistsProcessor(data){ return (dispatch, getState) => { sendRequest(dispatch, getState, data.next) - .then( response => { + .then( + response => { + dispatch({ + type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED', + playlists: response.items + }) - dispatch({ - type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED', - playlists: response.items - }) + // Check to see if we've been cancelled + if (getState().ui.processes['SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR'] !== undefined){ + var processor = getState().ui.processes['SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR'] - // Check to see if we've been cancelled - if (getState().ui.processes['SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR'] !== undefined){ - var processor = getState().ui.processes['SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR'] - - if (processor.status == 'cancelling'){ - dispatch(uiActions.processCancelled('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR')) - return false - } - } - - // We got a next link, so we've got more work to be done - if (response.next){ - var total = response.total - var loaded = getState().spotify.library_playlists.length - var remaining = total - loaded - dispatch(uiActions.updateProcess( - 'SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR', - 'Loading '+remaining+' Spotify playlists', - { - next: response.next, - total: response.total, - remaining: remaining + if (processor.status == 'cancelling'){ + dispatch(uiActions.processCancelled('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR')) + return false } - )) - dispatch(uiActions.runProcess('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR', {next: response.next})) - } else { - dispatch(uiActions.processFinished('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR')) - dispatch({type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED_ALL'}) + } + + // We got a next link, so we've got more work to be done + if (response.next){ + var total = response.total + var loaded = getState().spotify.library_playlists.length + var remaining = total - loaded + dispatch(uiActions.updateProcess( + 'SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR', + 'Loading '+remaining+' Spotify playlists', + { + next: response.next, + total: response.total, + remaining: remaining + } + )) + dispatch(uiActions.runProcess('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR', {next: response.next})) + } else { + dispatch(uiActions.processFinished('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR')) + dispatch({type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED_ALL'}) + } + }, + error => { + dispatch(coreActions.handleException( + 'Could not load library playlists', + error + )); } - }); + ); } } @@ -1464,8 +1759,10 @@ export function getLibraryArtists(){ dispatch(uiActions.startProcess('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR','Loading Spotify artists', {next: 'me/following?type=artist&limit=50'})) } else if (last_run.status == 'cancelled'){ dispatch(uiActions.resumeProcess('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR')) - } else if (last_run.status == 'finished'){ - // TODO: do we want to force a refresh? + + // We've already finished, but the status has been flushed + } else if (last_run.status == 'finished' && !getState().spotify.library_artists_loaded_all){ + dispatch(uiActions.startProcess('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR','Loading Spotify artists', {next: 'me/following?type=artist&limit=50'})) } } } @@ -1473,42 +1770,49 @@ export function getLibraryArtists(){ export function getLibraryArtistsProcessor(data){ return (dispatch, getState) => { sendRequest(dispatch, getState, data.next) - .then( response => { + .then( + response => { + dispatch({ + type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', + artists: response.artists.items + }) - dispatch({ - type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', - artists: response.artists.items - }) + // Check to see if we've been cancelled + if (getState().ui.processes['SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR'] !== undefined){ + var processor = getState().ui.processes['SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR'] - // Check to see if we've been cancelled - if (getState().ui.processes['SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR'] !== undefined){ - var processor = getState().ui.processes['SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR'] - - if (processor.status == 'cancelling'){ - dispatch(uiActions.processCancelled('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR')) - return false - } - } - - // We got a next link, so we've got more work to be done - if (response.artists.next){ - var total = response.artists.total - var loaded = getState().spotify.library_artists.length - var remaining = total - loaded - dispatch(uiActions.updateProcess( - 'SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR', - 'Loading '+remaining+' Spotify artists', - { - next: response.artists.next, - total: response.artists.total, - remaining: remaining + if (processor.status == 'cancelling'){ + dispatch(uiActions.processCancelled('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR')) + return false } - )) - dispatch(uiActions.runProcess('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR', {next: response.artists.next})) - } else { - dispatch(uiActions.processFinished('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR')) + } + + // We got a next link, so we've got more work to be done + if (response.artists.next){ + var total = response.artists.total + var loaded = getState().spotify.library_artists.length + var remaining = total - loaded + dispatch(uiActions.updateProcess( + 'SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR', + 'Loading '+remaining+' Spotify artists', + { + next: response.artists.next, + total: response.artists.total, + remaining: remaining + } + )) + dispatch(uiActions.runProcess('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR', {next: response.artists.next})) + } else { + dispatch(uiActions.processFinished('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR')) + } + }, + error => { + dispatch(coreActions.handleException( + 'Could not load library artists', + error + )); } - }); + ); } } @@ -1525,8 +1829,10 @@ export function getLibraryAlbums(){ dispatch(uiActions.startProcess('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR','Loading Spotify albums', {next: 'me/albums?limit=50'})) } else if (last_run.status == 'cancelled'){ dispatch(uiActions.updateProcess('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR','Loading Spotify albums', {next: 'me/albums?limit=50'})) - } else if (last_run.status == 'finished'){ - // TODO: do we want to force a refresh? + + // We've already finished, but the status has been flushed + } else if (last_run.status == 'finished' && !getState().spotify.library_albums_loaded_all){ + dispatch(uiActions.startProcess('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR','Loading Spotify albums', {next: 'me/albums?limit=50'})) } } } @@ -1534,41 +1840,48 @@ export function getLibraryAlbums(){ export function getLibraryAlbumsProcessor(data){ return (dispatch, getState) => { sendRequest(dispatch, getState, data.next) - .then( response => { + .then( + response => { + dispatch({ + type: 'SPOTIFY_LIBRARY_ALBUMS_LOADED', + albums: response.items + }) - dispatch({ - type: 'SPOTIFY_LIBRARY_ALBUMS_LOADED', - albums: response.items - }) + // Check to see if we've been cancelled + if (getState().ui.processes['SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR'] !== undefined){ + var processor = getState().ui.processes['SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR'] - // Check to see if we've been cancelled - if (getState().ui.processes['SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR'] !== undefined){ - var processor = getState().ui.processes['SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR'] - - if (processor.status == 'cancelling'){ - dispatch(uiActions.processCancelled('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR')) - return false - } - } - - // We got a next link, so we've got more work to be done - if (response.next){ - var total = response.total - var loaded = getState().spotify.library_albums.length - var remaining = total - loaded - dispatch(uiActions.updateProcess( - 'SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR', - 'Loading '+remaining+' Spotify albums', - { - next: response.next, - total: response.total, - remaining: remaining + if (processor.status == 'cancelling'){ + dispatch(uiActions.processCancelled('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR')) + return false } - )) - dispatch(uiActions.runProcess('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR', {next: response.next})) - } else { - dispatch(uiActions.processFinished('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR')) + } + + // We got a next link, so we've got more work to be done + if (response.next){ + var total = response.total + var loaded = getState().spotify.library_albums.length + var remaining = total - loaded + dispatch(uiActions.updateProcess( + 'SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR', + 'Loading '+remaining+' Spotify albums', + { + next: response.next, + total: response.total, + remaining: remaining + } + )) + dispatch(uiActions.runProcess('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR', {next: response.next})) + } else { + dispatch(uiActions.processFinished('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR')) + } + }, + error => { + dispatch(coreActions.handleException( + 'Could not load library albums', + error + )); } - }); + ); } } \ No newline at end of file diff --git a/src/js/services/spotify/middleware.js b/src/js/services/spotify/middleware.js index af0fd92e..4132c64a 100755 --- a/src/js/services/spotify/middleware.js +++ b/src/js/services/spotify/middleware.js @@ -35,24 +35,41 @@ const SpotifyMiddleware = (function(){ case 'SPOTIFY_AUTHORIZATION_GRANTED': ReactGA.event({ category: 'Spotify', action: 'Authorization granted' }) + + // Flush out the previous user's library + store.dispatch(spotifyActions.flushLibrary()); + next(action) break case 'SPOTIFY_AUTHORIZATION_REVOKED': var label = null - if (store.getState().spotify.me) label = store.getState().spotify.me.id + if (store.getState().spotify.me){ + label = store.getState().spotify.me.id; + } ReactGA.event({ category: 'Spotify', action: 'Authorization revoked', label: label }) next(action) - break + + // Now dispatch a getMe to get the backend-provided user + store.dispatch(spotifyActions.getMe()) + + // Flush out the previous user's library + store.dispatch(spotifyActions.flushLibrary()); + + break; case 'SPOTIFY_IMPORT_AUTHORIZATION': - var label = null + var label = null; if (action.me && action.me.id){ label = action.me.id } - ReactGA.event({ category: 'Spotify', action: 'Authorization imported', label: label }) - next(action) - break + ReactGA.event({ category: 'Spotify', action: 'Authorization imported', label: label }); + + // Flush out the previous user's library + store.dispatch(spotifyActions.flushLibrary()); + + next(action); + break; case 'SPOTIFY_RECOMMENDATIONS_LOADED': if (action.seeds_uris){ @@ -71,7 +88,7 @@ const SpotifyMiddleware = (function(){ break case 'SPOTIFY_REMOVE_PLAYLIST_TRACKS': - var playlist = state.core.playlists[action.key] + var playlist = Object.assign({},state.core.playlists[action.key]); store.dispatch( spotifyActions.deleteTracksFromPlaylist( playlist.uri, playlist.snapshot_id, action.tracks_indexes )) break diff --git a/src/js/services/spotify/reducer.js b/src/js/services/spotify/reducer.js index 70a86293..e535e174 100755 --- a/src/js/services/spotify/reducer.js +++ b/src/js/services/spotify/reducer.js @@ -150,6 +150,26 @@ export default function reducer(spotify = {}, action){ * Library **/ + case 'SPOTIFY_FLUSH_LIBRARY': + return Object.assign( + {}, + spotify, + { + library_playlists: null, + library_playlists_loaded_all: null, + library_playlists_status: null, + library_albums: null, + library_albums_status: null, + library_albums_loaded_all: null, + library_artists: null, + library_artists_status: null, + library_artists_loaded_all: null, + library_tracks: null, + library_tracks_status: null, + library_tracks_loaded_all: null + } + ) + case 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED': if (spotify.library_playlists){ var uris = [...spotify.library_playlists,...action.uris]