diff --git a/src/js/bootstrap.js b/src/js/bootstrap.js index fa909f90..8ba7ef11 100755 --- a/src/js/bootstrap.js +++ b/src/js/bootstrap.js @@ -1,16 +1,24 @@ console.info('Bootstrapping...'); -import { createStore, applyMiddleware } from 'redux' -import thunk from 'redux-thunk' -import reducer from './reducers/index' -import mopidyMiddleware from './services/mopidy/middleware' +import { createStore, applyMiddleware, combineReducers } from 'redux' + +import mopidy from './services/mopidy/reducer' +import spotify from './services/spotify/reducer' + +import thunk from 'redux-thunk' +import mopidyMiddleware from './services/mopidy/middleware' +import spotifyMiddleware from './services/spotify/middleware' + +let reducers = combineReducers({ + mopidy, + spotify +}); -// create our global store let store = createStore( - reducer, + reducers, {}, - applyMiddleware( thunk, mopidyMiddleware ) + applyMiddleware( thunk, mopidyMiddleware, spotifyMiddleware ) ); export default store; diff --git a/src/js/components/Album.js b/src/js/components/Album.js index d88e0ae9..0d50b570 100755 --- a/src/js/components/Album.js +++ b/src/js/components/Album.js @@ -18,19 +18,21 @@ class Album extends React.Component{ // on render componentDidMount(){ - this.loadAlbum( this.props.params.id ); + this.loadAlbum( this.props.params.uri ); } // when props changed componentWillReceiveProps( nextProps ){ - if( nextProps.params.id != this.props.params.id ){ - this.loadAlbum( nextProps.params.id ); + if( nextProps.params.uri != this.props.params.uri ){ + this.loadAlbum( nextProps.params.uri ); } } - loadAlbum( id ){ + loadAlbum( uri ){ let self = this; + var id = uri.replace('spotify:album:',''); + $.ajax({ method: 'GET', cache: true, diff --git a/src/js/components/AlbumLink.js b/src/js/components/AlbumLink.js new file mode 100755 index 00000000..6119ab18 --- /dev/null +++ b/src/js/components/AlbumLink.js @@ -0,0 +1,18 @@ + +import React, { PropTypes } from 'react' +import FontAwesome from 'react-fontawesome' +import { Link } from 'react-router' + +export default class AlbumLink extends React.Component{ + + constructor(props) { + super(props); + } + + render(){ + var link = '/album/' + this.props.album.uri; + return ( + { this.props.album.name } + ); + } +} \ No newline at end of file diff --git a/src/js/components/App.js b/src/js/components/App.js index 30817054..a2e7924c 100755 --- a/src/js/components/App.js +++ b/src/js/components/App.js @@ -7,6 +7,8 @@ import { connect } from 'react-redux' import * as actions from '../actions/index' import * as mopidyActions from '../services/mopidy/actions' +import * as spotifyActions from '../services/spotify/actions' +import SpotifyAuthenticationFrame from './SpotifyAuthenticationFrame' /** @@ -28,12 +30,9 @@ class App extends React.Component{
+ {this.props.children}
); @@ -53,7 +52,8 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = (dispatch) => { return { actions: bindActionCreators(actions, dispatch), - mopidyActions: bindActionCreators(mopidyActions, dispatch) + mopidyActions: bindActionCreators(mopidyActions, dispatch), + spotifyActions: bindActionCreators(spotifyActions, dispatch) } } diff --git a/src/js/components/ArtistList.js b/src/js/components/ArtistList.js new file mode 100755 index 00000000..e4e8a8a1 --- /dev/null +++ b/src/js/components/ArtistList.js @@ -0,0 +1,34 @@ + +import React, { PropTypes } from 'react' +import FontAwesome from 'react-fontawesome' +import { Link } from 'react-router' + +export default class ArtistList extends React.Component{ + + constructor(props) { + super(props); + } + + render(){ + return ( + + { + this.props.artists.map( (artist, index) => { + var separator = null; + if( index == this.props.artists.length - 2 ){ + separator = ' and '; + }else if( index < this.props.artists.length - 2 ){ + separator = ', '; + } + var link = '/artist/' + artist.uri; + return ( + + { artist.name }{ separator } + + ); + }) + } + + ); + } +} \ No newline at end of file diff --git a/src/js/components/Player.js b/src/js/components/Player.js index 5d99a1b4..1b72ccca 100755 --- a/src/js/components/Player.js +++ b/src/js/components/Player.js @@ -37,8 +37,12 @@ class Player extends React.Component{ return (
{ playButton } - this.props.actions.instruct('playback.previous')}> - this.props.actions.instruct('playback.next')}> + this.props.actions.instruct('playback.previous')}> + +   + this.props.actions.instruct('playback.next')}> + +   { consumeButton } { randomButton } { repeatButton } diff --git a/src/js/components/Queue.js b/src/js/components/Queue.js index bd45dc31..992d0afd 100755 --- a/src/js/components/Queue.js +++ b/src/js/components/Queue.js @@ -7,6 +7,8 @@ import FontAwesome from 'react-fontawesome' import TrackList from '../components/TrackList' import Track from '../components/Track' import Player from '../components/Player' +import ArtistList from '../components/ArtistList' +import AlbumLink from '../components/AlbumLink' import * as actions from '../services/mopidy/actions' class Queue extends React.Component{ @@ -18,7 +20,11 @@ class Queue extends React.Component{ renderTrackInFocus(){ if( this.props.mopidy && this.props.mopidy.trackInFocus ){ return ( -
{ this.props.mopidy.trackInFocus.track.name }
+
+
{ this.props.mopidy.trackInFocus.track.name }
+
+
+
); } return null; diff --git a/src/js/components/SpotifyAuthenticationFrame.js b/src/js/components/SpotifyAuthenticationFrame.js new file mode 100755 index 00000000..216d09a8 --- /dev/null +++ b/src/js/components/SpotifyAuthenticationFrame.js @@ -0,0 +1,43 @@ + +import React, { PropTypes } from 'react' +import { connect } from 'react-redux' +import { createStore, bindActionCreators } from 'redux' + +import * as actions from '../services/spotify/actions' + +class SpotifyAuthenticationFrame extends React.Component{ + + constructor(props) { + super(props); + } + + handleClick(e){ + console.log('handling click') + this.props.actions.authorize(); + } + + render(){ + var src = '//jamesbarnsley.co.nz/spotmop.php?action=frame'; + if( this.props.spotify.authorizing ){ + src = '//jamesbarnsley.co.nz/spotmop.php?action=authorize&app='+location.protocol+'//'+window.location.host; + } + return ( +
+ + +
+ ); + } +} + +const mapStateToProps = (state, ownProps) => { + return state; +} + +const mapDispatchToProps = (dispatch) => { + return { + actions: bindActionCreators(actions, dispatch) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(SpotifyAuthenticationFrame) \ No newline at end of file diff --git a/src/js/components/Track.js b/src/js/components/Track.js index df92f06c..daa2e790 100755 --- a/src/js/components/Track.js +++ b/src/js/components/Track.js @@ -1,6 +1,8 @@ import React, { PropTypes } from 'react' import FontAwesome from 'react-fontawesome' +import ArtistList from './ArtistList' +import AlbumLink from './AlbumLink' export default class Track extends React.Component{ @@ -24,9 +26,13 @@ export default class Track extends React.Component{ } return ( -
this.playTrack(e) }> - this.toggleSelected(e) } /> - #{this.props.track.uri}: {this.props.track.name} +
this.playTrack(e) }> + this.toggleSelected(e) } /> + {this.props.track.name} + + { this.props.track.album ? : null }
); } diff --git a/src/js/components/TrackList.js b/src/js/components/TrackList.js index e500de16..ddf2d3be 100755 --- a/src/js/components/TrackList.js +++ b/src/js/components/TrackList.js @@ -24,8 +24,22 @@ class TrackList extends React.Component{ playTrack( index ){ var tracks = this.state.tracks; - console.log('playTrack', tracks[index].tlid) - this.props.actions.changeTrack( index ) + this.props.actions.changeTrack( tracks[index].tlid ) + } + + deleteSelected(){ + + function isSelected( track ){ + return ( typeof(track.selected) !== 'undefined' && track.selected ); + } + + var selectedTracks = this.state.tracks.filter(isSelected) + var selectedTracksTlids = []; + for( var i = 0; i < selectedTracks.length; i++ ){ + selectedTracksTlids.push( selectedTracks[i].tlid ) + } + + this.props.actions.removeTracks( selectedTracksTlids ) } componentWillReceiveProps( nextProps ){ @@ -36,28 +50,31 @@ class TrackList extends React.Component{ let self = this; if( this.state.tracks ){ return ( -
    - { - this.state.tracks.map( - (track, index) => { +
    +
      + { + this.state.tracks.map( + (track, index) => { - // flatten nested track objects (as in the case of TlTracks) - if( typeof(track.track) !== 'undefined' ){ - for( var property in track.track ){ - if( track.track.hasOwnProperty(property) ){ - track[property] = track.track[property] + // flatten nested track objects (as in the case of TlTracks) + if( typeof(track.track) !== 'undefined' ){ + for( var property in track.track ){ + if( track.track.hasOwnProperty(property) ){ + track[property] = track.track[property] + } } } + return self.playTrack(index)} + toggleSelected={() => self.toggleSelected(index)} /> } - return self.playTrack(index)} - toggleSelected={() => self.toggleSelected(index)} /> - } - ) - } -
    + ) + } +
+ +
); } return null; diff --git a/src/js/index.js b/src/js/index.js index 3ca212c8..65add352 100755 --- a/src/js/index.js +++ b/src/js/index.js @@ -18,7 +18,7 @@ ReactDOM.render( - + diff --git a/src/js/services/MopidyService.js b/src/js/services/MopidyService.js deleted file mode 100755 index 83663429..00000000 --- a/src/js/services/MopidyService.js +++ /dev/null @@ -1,183 +0,0 @@ - -import Mopidy from 'mopidy' -import React, { PropTypes } from 'react' -import { connect } from 'react-redux' -import { bindActionCreators } from 'redux' -import FontAwesome from 'react-fontawesome' - -import * as actions from '../services/mopidy/actions' - - -/** - * Mopidy service - * - * Handles internal requests and passes them on to our connection - **/ -class MopidyService extends React.Component{ - - constructor( props ){ - super(props); -/* - this.connection = new Mopidy({ - webSocketUrl: "ws://tv.barnsley.nz:6680/mopidy/ws", - //webSocketUrl: "ws://music.james:6680/mopidy/ws", - callingConvention: 'by-position-or-by-name' - }); - - this.connection.on( (type, data) => this.handleMessage( type, data ) ); - */ - } - - componentDidMount(){ - - this.props.actions.connect(); - } - - handleMessage( type, data ){ - switch( type ){ - - case 'state:online': - this.props.actions.updateStatus( true ); - this.get( 'playback', 'State' ); - this.get( 'playback', 'Volume' ); - this.get( 'tracklist', 'Consume' ); - this.get( 'tracklist', 'Random' ); - this.get( 'tracklist', 'Repeat' ); - this.get( 'tracklist', 'TlTracks' ); - this.get( 'playback', 'CurrentTlTrack' ); - break; - - case 'state:offline': - this.props.actions.updateStatus( false ); - break; - - case 'event:tracklistChanged': - this.get( 'tracklist', 'TlTracks' ); - break; - - //case 'event:trackPlaybackEnded': - case 'event:playbackStateChanged': - case 'event:trackPlaybackStarted': - this.get( 'playback', 'State' ); - this.get( 'playback', 'CurrentTlTrack' ); - break; - - case 'event:volumeChanged': - this.props.actions.updateVolume( data.volume ); - break; - - case 'event:optionsChanged': - this.get( 'tracklist', 'Consume' ); - this.get( 'tracklist', 'Random' ); - this.get( 'tracklist', 'Repeat' ); - break; - - default: - //console.log( 'MopidyService: Unhandled event', type, message ); - } - } - - - /** - * Get something from Mopidy - * - * Sends request to Mopidy server, and updates our local storage on return - * @param string model Mopidy model (playback, tracklist, etc) - * @param string property the property to get (TlTracks, Consume, etc) - **/ - get( model, property ){ - console.log('MopidyService: '+model+'.get'+property); - let self = this; - this.connection[model]['get'+property]() - .then( - function( response ){ - self.props.actions['update'+property]( response ); - }, - function( error ){ - console.error( error ); - } - ); - } - - - /** - * Set something in Mopidy - * - * Sends request to Mopidy server, and updates our local storage on return - * @param string model Mopidy model (playback, tracklist, etc) - * @param string property the property to get (TlTracks, Consume, etc) - * @param mixed value - **/ - set( model, property, value ){ - console.log('MopidyServie: '+model+'.set'+property, value); - let self = this; - this.connection[model]['set'+property]( value ) - .then( - function( response ){ - self.props.actions['update'+property]( response ); - }, - function( error ){ - console.error( error ); - } - ); - } - - changeTlTrack( tlid ){ - console.log('Changing track to '+ tlid) - } - - - render(){ - console.log( this.props.mopidy ); - var playButton = this.connection.playback.play()}> - if( this.props.mopidy.state == 'playing' ){ - playButton = this.connection.playback.pause()}> - } - - var consumeButton = this.set('tracklist', 'Consume', [true])}>Consume - if( this.props.mopidy.consume ){ - consumeButton = this.set('tracklist', 'Consume', [false])}>Un-Consume - } - - var randomButton = this.set('tracklist', 'Random', [true])}>Random - if( this.props.mopidy.random ){ - randomButton = this.set('tracklist', 'Random', [false])}>Un-Random - } - - var repeatButton = this.set('tracklist', 'Repeat', [true])}>Repeat - if( this.props.mopidy.repeat ){ - repeatButton = this.set('tracklist', 'Repeat', [false])}>Un-Repeat - } - - return ( -
- { playButton } - this.connection.playback.previous()}> - this.connection.playback.next()}> - { consumeButton } - { randomButton } - { repeatButton } - { this.props.mopidy.volume } -
- ); - } -} - - -/** - * Export our component - * - * We also integrate our global store, using connect() - **/ - -const mapStateToProps = (state, ownProps) => { - return state; -} - -const mapDispatchToProps = (dispatch) => { - return { - actions: bindActionCreators(actions, dispatch) - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(MopidyService) \ No newline at end of file diff --git a/src/js/services/Services.js b/src/js/services/Services.js deleted file mode 100755 index 5136678b..00000000 --- a/src/js/services/Services.js +++ /dev/null @@ -1,11 +0,0 @@ - -import ServicesContainer from 'kioc'; - -import MopidyService from './MopidyService'; -import SpotifyService from './SpotifyService'; - -var Services = new ServicesContainer(); -Services.use(MopidyService, true); -//Services.use(SpotifyService, true); - -export default Services; \ No newline at end of file diff --git a/src/js/services/SpotifyService.js b/src/js/services/SpotifyService.js deleted file mode 100755 index 51f828d3..00000000 --- a/src/js/services/SpotifyService.js +++ /dev/null @@ -1,79 +0,0 @@ - -import React, { PropTypes } from 'react' -import * as actions from '../actions/spotifyActions' -import { connect } from 'react-redux' -import { bindActionCreators } from 'redux' - -class SpotifyService extends React.Component{ - - constructor(props){ - super(props); - let self = this; - this.urlBase = 'https://api.spotify.com/v1/'; - console.log('SpotifyProvider > constructor') - - // listen for incoming messages from the authorization iframe - // this is triggered when authentication is granted from the popup - window.addEventListener('message', function(event){ - if( !/^https?:\/\/jamesbarnsley\.co\.nz/.test(event.origin) ) return false; - self.handleMessage( event ); - }, false); - } - - handleMessage( event ){ - - // convert to json - var data = JSON.parse(event.data); - - // fire event - this.props.actions.authorizeSpotifySuccess( data ); - } - - test(){ - console.log('test stuff'); - } - - getAlbum( albumid ){ - return $.when( - $.ajax({ - method: 'GET', - cache: true, - url: this.urlBase+'albums/'+albumid - }) - ); - } - - render(){ - - if( this.props.config.authorizingSpotify ){ - var src = '//jamesbarnsley.co.nz/spotmop.php?action=authorize&app='+location.protocol+'//'+window.location.host; - return ( -
- Authorizing... - -
- ); - } - - return null - } -} - - -/** - * Export our component - * - * We also integrate our global store, using connect() - **/ - -const mapStateToProps = (state, ownProps) => { - return state; -} - -const mapDispatchToProps = (dispatch) => { - return { - actions: bindActionCreators(actions, dispatch) - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(SpotifyService) \ No newline at end of file diff --git a/src/js/services/mopidy/actions.js b/src/js/services/mopidy/actions.js index fd3972c2..b00a8b65 100755 --- a/src/js/services/mopidy/actions.js +++ b/src/js/services/mopidy/actions.js @@ -5,19 +5,35 @@ export function connect(){ return { - type: 'CONNECT' + type: 'MOPIDY_CONNECT' } } export function disconnect(){ return { - type: 'DISCONNECT' + type: 'MOPIDY_DISCONNECT' + } +} + +export function changeTrack( tlid ){ + return { + type: 'MOPIDY_CHANGE_TRACK', + call: 'playback.play', + value: { tlid: tlid } + } +} + +export function removeTracks( tlids ){ + return { + type: 'MOPIDY_REMOVE_TRACKS', + call: 'tracklist.remove', + value: { tlid: tlids } } } export function instruct( call, value ){ return { - type: 'INSTRUCT', + type: 'MOPIDY_INSTRUCT', call: call, value: value } diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index ba0ba0ab..74b33c74 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -4,27 +4,15 @@ import actions from './actions' const MopidyMiddleware = (function(){ + // container for the actual Mopidy socket var socket = null; - const onOpen = (ws, store, token) => evt => { - console.log('opened') - //Send a handshake, or authenticate with remote end - - //Tell the store we're connected - //store.dispatch(actions.connected()); - } - - const onClose = (ws, store) => evt => { - //Tell the store we've disconnected - //store.dispatch(actions.disconnected()); - } - + // handle all manner of socket messages const handleMessage = (ws, store, type, data) => { - switch( type ){ case 'state:online': - //store.dispatch( actions.updateStatus( true ) ); + store.dispatch({ type: 'MOPIDY_CONNECTED' }); instruct( ws, store, 'playback.getState' ); instruct( ws, store, 'playback.getVolume' ); instruct( ws, store, 'tracklist.getConsume' ); @@ -35,7 +23,7 @@ const MopidyMiddleware = (function(){ break; case 'state:offline': - store.dispatch( actions.updateStatus( false ) ); + store.dispatch({ type: 'MOPIDY_DISCONNECTED' }); break; case 'event:tracklistChanged': @@ -50,7 +38,7 @@ const MopidyMiddleware = (function(){ break; case 'event:volumeChanged': - store.dispatch( actions.updateVolume( data.volume ) ); + store.dispatch({ type: 'MOPIDY_Volume', data: data.volume }); break; case 'event:optionsChanged': @@ -60,35 +48,22 @@ const MopidyMiddleware = (function(){ break; default: - //console.log( 'MopidyService: Unhandled event', type, message ); + //console.log( 'MopidyService: Unhandled message', type, message ); } } - function doStuff( ws, store ){ - console.log('doing stuff', store); - ws.playback.getState() - .then( - response => { - store.dispatch({ type: 'STATE', state: response }) - }, - error => { - console.error( error ); - } - ); - } - - /** - * Get something from Mopidy + * Call something with Mopidy * * Sends request to Mopidy server, and updates our local storage on return - * @param object ws Websocket - * @param string model Mopidy model (playback, tracklist, etc) - * @param string property the property to get (TlTracks, Consume, etc) + * @param object ws = Mopidy class that wraps a Websocket + * @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 **/ const instruct = ( ws, store, call, value = false ) => { - console.log('Mopidy: '+call, value); + var callParts = call.split('.'); var model = callParts[0]; var method = callParts[1]; @@ -96,30 +71,38 @@ 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 ){ ws[model][method]( value ) .then( - response => store.dispatch({ type: property, model: model, data: response }), + response => store.dispatch({ type: 'MOPIDY_'+property, model: model, data: response }), error => console.error( error ) ); + + // no value, so omit the payload }else{ ws[model][method]() .then( - response => store.dispatch({ type: property, model: model, data: response }), + response => store.dispatch({ type: 'MOPIDY_'+property, model: model, data: response }), error => console.error( error ) ); } } + /** + * Middleware + * + * This behaves like an action interceptor. We listen for specific actions + * and handle special functionality. If the action is not in our switch, then + * it just proceeds to the next middleware, or default functionality + **/ return store => next => action => { - switch(action.type) { - //The user wants us to connect - case 'CONNECT': + case 'MOPIDY_CONNECT': if(socket != null) socket.close(); - store.dispatch({ type: 'CONNECTING' }); + store.dispatch({ type: 'MOPIDY_CONNECTING' }); socket = new Mopidy({ webSocketUrl: "ws://tv.barnsley.nz:6680/mopidy/ws", @@ -130,19 +113,20 @@ const MopidyMiddleware = (function(){ break; - //The user wants us to disconnect - case 'DISCONNECT': + case 'MOPIDY_DISCONNECT': if(socket != null) socket.close(); socket = null; - store.dispatch({ type: 'DISCONNECTED' }); + store.dispatch({ type: 'MOPIDY_DISCONNECTED' }); break; - //Send the 'SEND_MESSAGE' action down the websocket to the server - case 'INSTRUCT': + // send an instruction to the websocket + case 'MOPIDY_INSTRUCT': + case 'MOPIDY_CHANGE_TRACK': + case 'MOPIDY_REMOVE_TRACKS': instruct( socket, store, action.call, action.value ) break; - //This action is irrelevant to us, pass it on to the next middleware + // This action is irrelevant to us, pass it on to the next middleware default: return next(action); } diff --git a/src/js/services/mopidy/reducer.js b/src/js/services/mopidy/reducer.js index 8a2cec12..beafffc9 100755 --- a/src/js/services/mopidy/reducer.js +++ b/src/js/services/mopidy/reducer.js @@ -2,53 +2,56 @@ export default function reducer(mopidy = {}, action){ switch (action.type) { - case 'CONNECTING': + case 'MOPIDY_CONNECTING': return Object.assign({}, mopidy, { connected: false, connecting: true }); - case 'CONNECTED': + case 'MOPIDY_CONNECTED': return Object.assign({}, mopidy, { connected: true, connecting: false }); - case 'DISCONNECTED': + case 'MOPIDY_DISCONNECTED': return Object.assign({}, mopidy, { connected: false, connecting: false }); - case 'State': + case 'MOPIDY_CHANGE_TRACK': + return Object.assign({}, mopidy, { + tlid: action.tlid + }); + + /** + * Websocket-initiated actions + **/ + case 'MOPIDY_State': return Object.assign({}, mopidy, { state: action.data }); - case 'Consume': + case 'MOPIDY_Consume': return Object.assign({}, mopidy, { consume: action.data }); - case 'Random': + case 'MOPIDY_Random': return Object.assign({}, mopidy, { random: action.data }); - case 'Repeat': + case 'MOPIDY_Repeat': return Object.assign({}, mopidy, { repeat: action.data }); - case 'TlTracks': + case 'MOPIDY_TlTracks': return Object.assign({}, mopidy, { - tracks: action.data + tracks: action.data }); - case 'CurrentTlTrack': + case 'MOPIDY_CurrentTlTrack': return Object.assign({}, mopidy, { - trackInFocus: action.data + trackInFocus: action.data }); - case 'Volume': + case 'MOPIDY_Volume': return Object.assign({}, mopidy, { - volume: action.volume - }); - - case 'CHANGE_TRACK': - return Object.assign({}, mopidy, { - tlid: action.tlid + volume: action.data }); default: diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js new file mode 100755 index 00000000..8640ac1c --- /dev/null +++ b/src/js/services/spotify/actions.js @@ -0,0 +1,30 @@ + +/** + * Actions and Action Creators + **/ + +export function authorize(){ + return { + type: 'SPOTIFY_AUTHORIZING' + } +} + +export function connect(){ + return { + type: 'SPOTIFY_CONNECTING' + } +} + +export function disconnect(){ + return { + type: 'SPOTIFY_DISCONNECT' + } +} + +export function loadAlbum( uri ){ + return { + type: 'SPOTIFY_LOAD_ALBUM', + uri: uri + } +} + diff --git a/src/js/services/spotify/middleware.js b/src/js/services/spotify/middleware.js new file mode 100755 index 00000000..a04eccd1 --- /dev/null +++ b/src/js/services/spotify/middleware.js @@ -0,0 +1,28 @@ + +import actions from './actions' + +const SpotifyMiddleware = (function(){ + + return store => next => action => { + + switch(action.type) { + + 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); + } + } + +})(); + +export default SpotifyMiddleware \ No newline at end of file diff --git a/src/js/services/spotify/reducer.js b/src/js/services/spotify/reducer.js new file mode 100755 index 00000000..736832c8 --- /dev/null +++ b/src/js/services/spotify/reducer.js @@ -0,0 +1,23 @@ + +export default function reducer(spotify = {}, action){ + switch (action.type) { + + case 'SPOTIFY_CONNECTING': + return Object.assign({}, spotify, { connected: false, connecting: true }); + + case 'SPOTIFY_AUTHORIZING': + return Object.assign({}, spotify, { authorizing: true }); + + case 'SPOTIFY_CONNECTED': + return Object.assign({}, spotify, { connected: true, connecting: false }); + + case 'SPOTIFY_DISCONNECTED': + return Object.assign({}, spotify, { connected: false, connecting: false }); + + default: + return spotify + } +} + + +