From daab2133dfd5e41f9982bd39cc326fc5bf926798 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Thu, 20 Oct 2016 21:38:01 +1300 Subject: [PATCH] Fiddling with thunk; More library endpoints --- src/js/components/AlbumGridItem.js | 9 +- src/js/components/Player.js | 20 ++- src/js/components/TrackList.js | 48 +++++-- src/js/index.js | 4 + src/js/services/mopidy/actions.js | 27 +++- src/js/services/mopidy/middleware.js | 49 ++++--- src/js/services/mopidy/reducer.js | 17 +-- src/js/services/spotify/actions.js | 193 ++++++++++++++++++++++---- src/js/services/spotify/middleware.js | 61 -------- src/js/services/spotify/reducer.js | 6 +- src/js/views/Album.js | 24 +--- src/js/views/App.js | 2 + src/js/views/Artist.js | 6 +- src/js/views/LibraryAlbums.js | 54 +++++++ src/js/views/LibraryArtists.js | 2 +- src/js/views/LibraryTracks.js | 54 +++++++ 16 files changed, 413 insertions(+), 163 deletions(-) create mode 100755 src/js/views/LibraryAlbums.js create mode 100755 src/js/views/LibraryTracks.js diff --git a/src/js/components/AlbumGridItem.js b/src/js/components/AlbumGridItem.js index 2118227c..4e15f9c9 100755 --- a/src/js/components/AlbumGridItem.js +++ b/src/js/components/AlbumGridItem.js @@ -10,10 +10,15 @@ export default class AlbumGridItem extends React.Component{ } render(){ - var link = '/album/' + this.props.item.uri; + var item = this.props.item; + if( typeof(item.album) !== 'undefined' ){ + item.album.added_at = item.added_at; + item = item.album; + } + var link = '/album/' + item.uri; return ( - + { item.images ? : item.name } ); } diff --git a/src/js/components/Player.js b/src/js/components/Player.js index 029a3ffc..3a190619 100755 --- a/src/js/components/Player.js +++ b/src/js/components/Player.js @@ -13,11 +13,22 @@ class Player extends React.Component{ super(props); } + renderTrackInFocus(){ + if( this.props.mopidy && this.props.mopidy.trackInFocus ){ + return ( +
+
{ this.props.mopidy.trackInFocus.track.name }
+
+ ); + } + return null; + } + renderControls(){ - var playButton = this.props.actions.instruct('playback.play')}> + var playButton = this.props.actions.play()}> if( this.props.mopidy.state == 'playing' ){ - playButton = this.props.actions.instruct('playback.pause')}> + playButton = this.props.actions.pause()}> } var consumeButton = this.props.actions.instruct('tracklist.setConsume', [true])}>Consume @@ -37,11 +48,12 @@ class Player extends React.Component{ return (
+ { this.renderTrackInFocus() } { playButton } - this.props.actions.instruct('playback.previous')}> + this.props.actions.previous()}>   - this.props.actions.instruct('playback.next')}> + this.props.actions.next()}>   { consumeButton } diff --git a/src/js/components/TrackList.js b/src/js/components/TrackList.js index 42412825..0d111c6f 100755 --- a/src/js/components/TrackList.js +++ b/src/js/components/TrackList.js @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' -import * as actions from '../services/mopidy/actions' +import * as mopidyActions from '../services/mopidy/actions' import Track from './Track' @@ -17,6 +17,10 @@ class TrackList extends React.Component{ } } + componentWillReceiveProps( nextProps ){ + this.setState({ tracks: nextProps.tracks }); + } + handleClick( e, index ){ var tracks = this.state.tracks; @@ -49,6 +53,11 @@ class TrackList extends React.Component{ this.setState({ tracks: tracks, lastSelectedTrack: index }); } + handleDoubleClick( e, index ){ + var tracks = this.state.tracks; + this.playTrack( tracks[index] ) + } + selectedTracks(){ function isSelected( track ){ return ( typeof(track.selected) !== 'undefined' && track.selected ); @@ -56,21 +65,38 @@ class TrackList extends React.Component{ return this.state.tracks.filter(isSelected) } - handleDoubleClick( e, index ){ - var tracks = this.state.tracks; - this.props.playTrack( tracks[index] ) + playTracks(){ + + var tracks = this.selectedTracks(); + + if( typeof(this.props.playTracks) !== 'undefined' ){ + return this.props.playTracks( tracks ); + } + + var uris = []; + for( var i = 0; i < tracks.length; i++ ){ + uris.push( tracks[i].uri ) + } + return this.props.mopidyActions.playTracks( uris ) } - playTracks(){ - this.props.playTracks( this.selectedTracks() ) + playTrack( track ){ + + if( typeof(this.props.playTrack) !== 'undefined' ){ + return this.props.playTrack( this.selectedTracks() ); + } + + var uris = [track.uri]; + this.props.mopidyActions.playTracks( uris ) } removeTracks(){ - this.props.removeTracks( this.selectedTracks() ) - } - componentWillReceiveProps( nextProps ){ - this.setState({ tracks: nextProps.tracks }); + var tracks = this.selectedTracks(); + + if( typeof(this.props.removeTracks) !== 'undefined' ){ + return this.props.removeTracks( tracks ); + } } render(){ @@ -122,7 +148,7 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = (dispatch) => { return { - actions: bindActionCreators(actions, dispatch) + mopidyActions: bindActionCreators(mopidyActions, dispatch) } } diff --git a/src/js/index.js b/src/js/index.js index b70a9552..a0441b1d 100755 --- a/src/js/index.js +++ b/src/js/index.js @@ -18,6 +18,8 @@ import Artist from './views/Artist' import Queue from './views/Queue' import Settings from './views/Settings' import LibraryArtists from './views/LibraryArtists' +import LibraryAlbums from './views/LibraryAlbums' +import LibraryTracks from './views/LibraryTracks' ReactDOM.render( @@ -25,6 +27,8 @@ ReactDOM.render( + + diff --git a/src/js/services/mopidy/actions.js b/src/js/services/mopidy/actions.js index dccf9649..3f6fc59f 100755 --- a/src/js/services/mopidy/actions.js +++ b/src/js/services/mopidy/actions.js @@ -33,8 +33,18 @@ export function changeTrack( tlid ){ export function playTracks( uris ){ return { type: 'MOPIDY_PLAY_TRACKS', + uris: uris + } +} + +export function enqueueTracks( uris, at_position = false ){ + if( typeof(uris) !== 'array' ) uris = [uris]; + var value = { uris: uris }; + if( at_position ) value.at_position = at_position; + return { + type: 'MOPIDY_ENQUEUE_TRACKS', call: 'tracklist.add', - value: { at_position: 0, uris: uris } + value: value } } @@ -54,3 +64,18 @@ export function instruct( call, value ){ } } +export function play(){ + return instruct('playback.play'); +} + +export function pause(){ + return instruct('playback.pause'); +} + +export function next(){ + return instruct('playback.next'); +} + +export function previous(){ + return instruct('playback.previous'); +} diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index 6987ea87..3623f954 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -1,6 +1,6 @@ import Mopidy from 'mopidy' -import actions from './actions' +var actions = require('./actions.js') const MopidyMiddleware = (function(){ @@ -38,7 +38,7 @@ const MopidyMiddleware = (function(){ break; case 'event:volumeChanged': - store.dispatch({ type: 'MOPIDY_Volume', data: data.volume }); + store.dispatch({ type: 'MOPIDY_VOLUME', data: data.volume }); break; case 'event:optionsChanged': @@ -61,8 +61,9 @@ const MopidyMiddleware = (function(){ * @param string model = which Mopidy model (playback, tracklist, etc) * @param string property = TlTracks, Consume, etc * @param string value (optional) = value of the property to pass + * @return promise **/ - const instruct = ( ws, store, call, value = false ) => { + const instruct = ( ws, store, call, value = {} ) => { var callParts = call.split('.'); var model = callParts[0]; @@ -71,22 +72,19 @@ const MopidyMiddleware = (function(){ property = property.replace('get',''); property = property.replace('set',''); - // if we have a value, we need to include in our payload - if( value ){ + return new Promise( (resolve, reject) => { ws[model][method]( value ) .then( - response => store.dispatch({ type: 'MOPIDY_'+property, model: model, data: response }), - error => console.error( error ) + response => { + store.dispatch({ type: 'MOPIDY_'+property.toUpperCase(), model: model, data: response }); + resolve(response); + }, + error => { + console.error(error) + reject(error); + } ); - - // no value, so omit the payload - }else{ - ws[model][method]() - .then( - response => store.dispatch({ type: 'MOPIDY_'+property, model: model, data: response }), - error => console.error( error ) - ); - } + }); } /** @@ -97,6 +95,7 @@ const MopidyMiddleware = (function(){ * it just proceeds to the next middleware, or default functionality **/ return store => next => action => { + switch(action.type) { case 'MOPIDY_CONNECT': @@ -129,10 +128,20 @@ const MopidyMiddleware = (function(){ break; case 'MOPIDY_PLAY_TRACKS': - var callback = function(){ - store.dispatch({ type: 'MOPIDY_TEST', model: 'yeah', data: {} }) - } - instruct( socket, store, action.call, action.value, callback ) + + // add our first track + instruct( socket, store, 'tracklist.add', { uri: action.uris[0], at_position: 0 } ) + .then( response => { + + // play it + store.dispatch( actions.changeTrack( response[0].tlid ) ); + + // add the rest of our uris (if any) + action.uris.shift(); + if( action.uris.length > 0 ){ + store.dispatch( actions.enqueueTracks( action.uris, 1 ) ) + } + }) break; // This action is irrelevant to us, pass it on to the next middleware diff --git a/src/js/services/mopidy/reducer.js b/src/js/services/mopidy/reducer.js index dafd0280..78f39c81 100755 --- a/src/js/services/mopidy/reducer.js +++ b/src/js/services/mopidy/reducer.js @@ -2,9 +2,6 @@ export default function reducer(mopidy = {}, action){ switch (action.type) { - case 'MOPIDY_CONNECTING': - return Object.assign({}, mopidy, { connected: false, connecting: true }); - case 'MOPIDY_CONNECTED': return Object.assign({}, mopidy, { connected: true, connecting: false }); @@ -25,37 +22,37 @@ export default function reducer(mopidy = {}, action){ /** * Websocket-initiated actions **/ - case 'MOPIDY_State': + case 'MOPIDY_STATE': return Object.assign({}, mopidy, { state: action.data }); - case 'MOPIDY_Consume': + case 'MOPIDY_CONSUME': return Object.assign({}, mopidy, { consume: action.data }); - case 'MOPIDY_Random': + case 'MOPIDY_RANDOM': return Object.assign({}, mopidy, { random: action.data }); - case 'MOPIDY_Repeat': + case 'MOPIDY_REPEAT': return Object.assign({}, mopidy, { repeat: action.data }); - case 'MOPIDY_TlTracks': + case 'MOPIDY_TLTRACKS': return Object.assign({}, mopidy, { tracks: action.data }); - case 'MOPIDY_CurrentTlTrack': + case 'MOPIDY_CURRENTTLTRACK': return Object.assign({}, mopidy, { trackInFocus: action.data }); - case 'MOPIDY_Volume': + case 'MOPIDY_VOLUME': return Object.assign({}, mopidy, { volume: action.data }); diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 286ce1fd..ef3dc6f4 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -10,25 +10,6 @@ export function setConfig( config ){ } } -export function startAuthorization(){ - return { - type: 'SPOTIFY_START_AUTHORIZATION' - } -} - -export function removeAuthorization(){ - return { - type: 'SPOTIFY_REMOVE_AUTHORIZATION' - } -} - -export function authorizationGranted( data ){ - return { - type: 'SPOTIFY_AUTHORIZATION_GRANTED', - data: data - } -} - export function connect(){ return { type: 'SPOTIFY_CONNECTING' @@ -48,16 +29,176 @@ export function loadAlbum( uri ){ } } -export function loadArtist( uri ){ - return { - type: 'SPOTIFY_LOAD_ARTIST', - uri: uri + + +/** + * Send an ajax request to the Spotify API + * + * @param endpoint string = the url to query (ie /albums/:uri) + * @param method string + * @param data mixed = request payload + * @return ajax promise + **/ +const sendRequest = (token, endpoint, method = 'GET', data = false) => { + + var options = { + method: method, + cache: true, + url: 'https://api.spotify.com/v1/'+endpoint, + data: data + }; + + if( token ){ + options.headers = { + Authorization: 'Bearer '+ token + } + } + + return $.ajax( options ); +} + + +/** + * Handle authorization process + **/ + +export function startAuthorization(){ + return { type: 'SPOTIFY_START_AUTHORIZATION' } +} + +export function authorizationGranted( data ){ + return { type: 'SPOTIFY_AUTHORIZATION_GRANTED', data: data } +} + +export function removeAuthorization(){ + return { type: 'SPOTIFY_REMOVE_AUTHORIZATION' } +} + + +/** + * Get a single artist + * + * @param uri string + **/ +export function getArtist( uri ){ + return (dispatch, getState) => { + + // flush out the previous store value + dispatch({ type: 'SPOTIFY_ARTIST_LOADED', data: false }); + + var id = uri.replace('spotify:artist:',''); + var artist = {}; + + // while we're fiddling about, go get the albums + dispatch(getArtistAlbums(uri)); + + // get both the artist and the top tracks + $.when( + + sendRequest( false, 'artists/'+id ) + .then( response => { + Object.assign(artist, response); + }), + + sendRequest( false, 'artists/'+id+'/top-tracks?country='+getState().spotify.country ) + .then( response => { + Object.assign(artist, response); + }) + + ).then( () => { + dispatch({ + type: 'SPOTIFY_ARTIST_LOADED', + data: artist + }); + }); } } -export function loadLibraryArtists(){ - return { - type: 'SPOTIFY_LOAD_LIBRARY_ARTISTS' +export function getArtistAlbums( uri ){ + return dispatch => { + sendRequest( false, 'artists/'+ uri.replace('spotify:artist:','') +'/albums' ) + .then( response => { + dispatch({ + type: 'SPOTIFY_ARTIST_ALBUMS_LOADED', + data: response + }); + }); } } +/** + * Single album + * + * @oaram uri string + **/ +export function getAlbum( uri ){ + return dispatch => { + + // flush out the previous store value + dispatch({ type: 'SPOTIFY_ALBUM_LOADED', data: false }); + + sendRequest( false, 'albums/'+ uri.replace('spotify:album:','') ) + .then( response => { + + // inject the parent album object into each track for consistent track objects + for( var i = 0; i < response.tracks.items.length; i++ ){ + response.tracks.items[i].album = { + name: response.name, + uri: response.uri + } + } + + dispatch({ + type: 'SPOTIFY_ALBUM_LOADED', + data: response + }); + }); + } +} + + +export function getLibraryArtists(){ + return (dispatch, getState) => { + + dispatch({ type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', data: false }); + + sendRequest( getState().spotify.access_token, 'me/following?type=artist' ) + .then( response => { + dispatch({ + type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', + data: response + }); + }); + } +} + + +export function getLibraryAlbums(){ + return (dispatch, getState) => { + + dispatch({ type: 'SPOTIFY_LIBRARY_ALBUMS_LOADED', data: false }); + + sendRequest( getState().spotify.access_token, 'me/albums' ) + .then( response => { + dispatch({ + type: 'SPOTIFY_LIBRARY_ALBUMS_LOADED', + data: response + }); + }); + } +} + +export function getLibraryTracks(){ + return (dispatch, getState) => { + + dispatch({ type: 'SPOTIFY_LIBRARY_TRACKS_LOADED', data: false }); + + sendRequest( getState().spotify.access_token, 'me/tracks' ) + .then( response => { + dispatch({ + type: 'SPOTIFY_LIBRARY_TRACKS_LOADED', + data: response + }); + }); + } +} diff --git a/src/js/services/spotify/middleware.js b/src/js/services/spotify/middleware.js index a3369a87..3559f597 100755 --- a/src/js/services/spotify/middleware.js +++ b/src/js/services/spotify/middleware.js @@ -47,67 +47,6 @@ const SpotifyMiddleware = (function(){ }); break; - case 'SPOTIFY_LOAD_ALBUM': - - store.dispatch({ type: 'SPOTIFY_ALBUM_LOADED', data: false }) - var id = action.uri.replace('spotify:album:',''); - - sendRequest( false, 'albums/'+id ) - .then( (response) => { - for( var i = 0; i < response.tracks.items.length; i++ ){ - response.tracks.items[i].album = { - name: response.name, - uri: response.uri - } - } - store.dispatch({ type: 'SPOTIFY_ALBUM_LOADED', data: response }) - }); - break; - - - case 'SPOTIFY_LOAD_ARTIST': - - store.dispatch({ type: 'SPOTIFY_ARTIST_LOADED', data: false }) - store.dispatch({ type: 'SPOTIFY_ARTIST_ALBUMS_LOADED', data: false }) - - var id = action.uri.replace('spotify:artist:',''); - - sendRequest( false, 'artists/'+id ) - .then( (response) => { - store.dispatch({ type: 'SPOTIFY_ARTIST_LOADED', data: response }) - }); - - sendRequest( false, 'artists/'+id+'/albums' ) - .then( (response) => { - store.dispatch({ type: 'SPOTIFY_ARTIST_ALBUMS_LOADED', data: response }) - }); - - sendRequest( false, 'artists/'+id+'/top-tracks?country='+state.spotify.country ) - .then( (response) => { - store.dispatch({ type: 'SPOTIFY_ARTIST_TRACKS_LOADED', data: response }) - }); - break; - - - case 'SPOTIFY_LOAD_LIBRARY_ARTISTS': - - store.dispatch({ type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', data: false }) - - sendRequest( state.spotify.access_token, 'me/following?type=artist' ) - .then( (response) => { - store.dispatch({ type: 'SPOTIFY_LIBRARY_ARTISTS_LOADED', data: response }) - }); - break; - - - case 'SPOTIFY_CONNECT': - console.log('Spotify wants to connect') - break; - - case 'SPOTIFY_DISCONNECT': - console.log('Spotify wants to DISconnect') - 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 73e6bd6d..21034d98 100755 --- a/src/js/services/spotify/reducer.js +++ b/src/js/services/spotify/reducer.js @@ -41,12 +41,12 @@ export default function reducer(spotify = {}, action){ case 'SPOTIFY_ARTIST_ALBUMS_LOADED': return Object.assign({}, spotify, { artist_albums: action.data }); - case 'SPOTIFY_ARTIST_TRACKS_LOADED': - return Object.assign({}, spotify, { artist_top_tracks: action.data }); - case 'SPOTIFY_LIBRARY_ARTISTS_LOADED': return Object.assign({}, spotify, { libraryArtists: action.data }); + case 'SPOTIFY_LIBRARY_ALBUMS_LOADED': + return Object.assign({}, spotify, { libraryAlbums: action.data }); + default: return spotify } diff --git a/src/js/views/Album.js b/src/js/views/Album.js index 4c89591f..137b2792 100755 --- a/src/js/views/Album.js +++ b/src/js/views/Album.js @@ -15,40 +15,22 @@ class Album extends React.Component{ // on render componentDidMount(){ - this.props.spotifyActions.loadAlbum( this.props.params.uri ); + this.props.spotifyActions.getAlbum( this.props.params.uri ); } // when props changed componentWillReceiveProps( nextProps ){ if( nextProps.params.uri != this.props.params.uri ){ - this.props.spotifyActions.loadAlbum( nextProps.params.uri ); + this.props.spotifyActions.getAlbum( nextProps.params.uri ); } } - playTracks( tracks ){ - var uris = []; - for( var i = 0; i < tracks.length; i++ ){ - uris.push( tracks[i].uri ) - } - this.props.mopidyActions.playTracks( uris ) - } - - playTrack( track ){ - var uris = [track.uri]; - this.props.mopidyActions.playTracks( uris ) - } - render(){ if( this.props.spotify.album ){ return (

{ this.props.spotify.album.name }

- this.playTracks( tracks ) } - playTrack={ track => this.playTrack( track ) } - /> +
); } diff --git a/src/js/views/App.js b/src/js/views/App.js index 2d88a544..c7ea001f 100755 --- a/src/js/views/App.js +++ b/src/js/views/App.js @@ -31,6 +31,8 @@ class App extends React.Component{
  • Now playing
  • Library: My artists
  • +
  • Library: My albums
  • +
  • Library: My playlists
  • Settings
{this.props.children} diff --git a/src/js/views/Artist.js b/src/js/views/Artist.js index a58b2d7a..59180eb3 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -16,13 +16,13 @@ class Artist extends React.Component{ // on render componentDidMount(){ - this.props.spotifyActions.loadArtist( this.props.params.uri ); + this.props.spotifyActions.getArtist( this.props.params.uri ); } // when props changed componentWillReceiveProps( nextProps ){ if( nextProps.params.uri != this.props.params.uri ){ - this.props.spotifyActions.loadArtist( nextProps.params.uri ); + this.props.spotifyActions.getArtist( nextProps.params.uri ); } } @@ -32,7 +32,7 @@ class Artist extends React.Component{

{ this.props.spotify.artist.name }

{ this.props.spotify.artist.followers.total.toLocaleString() } followers

- { this.props.spotify.artist_top_tracks ? : null } + { this.props.spotify.artist.tracks ? : null } { this.props.spotify.artist_albums ? : null }
); diff --git a/src/js/views/LibraryAlbums.js b/src/js/views/LibraryAlbums.js new file mode 100755 index 00000000..714f8ae4 --- /dev/null +++ b/src/js/views/LibraryAlbums.js @@ -0,0 +1,54 @@ + +import React, { PropTypes } from 'react' +import { connect } from 'react-redux' +import { bindActionCreators } from 'redux' +import { Link } from 'react-router' + +import AlbumGrid from '../components/AlbumGrid' + +import * as mopidyActions from '../services/mopidy/actions' +import * as spotifyActions from '../services/spotify/actions' + +class LibraryAlbums extends React.Component{ + + constructor(props) { + super(props); + } + + // on render + componentDidMount(){ + this.props.spotifyActions.getLibraryAlbums(); + } + + render(){ + if( this.props.spotify.libraryAlbums ){ + return ( +
+

My albums

+ +
+ ); + } + return null; + } +} + + +/** + * Export our component + * + * We also integrate our global store, using connect() + **/ + +const mapStateToProps = (state, ownProps) => { + return state; +} + +const mapDispatchToProps = (dispatch) => { + return { + mopidyActions: bindActionCreators(mopidyActions, dispatch), + spotifyActions: bindActionCreators(spotifyActions, dispatch) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(LibraryAlbums) \ No newline at end of file diff --git a/src/js/views/LibraryArtists.js b/src/js/views/LibraryArtists.js index 2203e3c1..13090caf 100755 --- a/src/js/views/LibraryArtists.js +++ b/src/js/views/LibraryArtists.js @@ -15,7 +15,7 @@ class LibraryArtists extends React.Component{ // on render componentDidMount(){ - this.props.spotifyActions.loadLibraryArtists(); + this.props.spotifyActions.getLibraryArtists(); } render(){ diff --git a/src/js/views/LibraryTracks.js b/src/js/views/LibraryTracks.js new file mode 100755 index 00000000..b78ea278 --- /dev/null +++ b/src/js/views/LibraryTracks.js @@ -0,0 +1,54 @@ + +import React, { PropTypes } from 'react' +import { connect } from 'react-redux' +import { bindActionCreators } from 'redux' +import { Link } from 'react-router' + +import TrackList from '../components/TrackList' + +import * as mopidyActions from '../services/mopidy/actions' +import * as spotifyActions from '../services/spotify/actions' + +class LibraryTracks extends React.Component{ + + constructor(props) { + super(props); + } + + // on render + componentDidMount(){ + this.props.spotifyActions.getLibraryTracks(); + } + + render(){ + if( this.props.spotify.libraryAlbums ){ + return ( +
+

My tracks

+ +
+ ); + } + return null; + } +} + + +/** + * Export our component + * + * We also integrate our global store, using connect() + **/ + +const mapStateToProps = (state, ownProps) => { + return state; +} + +const mapDispatchToProps = (dispatch) => { + return { + mopidyActions: bindActionCreators(mopidyActions, dispatch), + spotifyActions: bindActionCreators(spotifyActions, dispatch) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(LibraryTracks) \ No newline at end of file