diff --git a/mopidy_iris/handlers.py b/mopidy_iris/handlers.py index 9529ec40..409abaf8 100755 --- a/mopidy_iris/handlers.py +++ b/mopidy_iris/handlers.py @@ -91,13 +91,15 @@ class WebsocketHandler(tornado.websocket.WebSocketHandler): mem.iris.send_message(self.connection_id, response) else: response = { - 'error': 'Method "'+message['method']+'" does not exist', + 'status': 0, + 'message': 'Method "'+message['method']+'" does not exist', 'request_id': request_id } mem.iris.send_message(self.connection_id, response) else: response = { - 'error': 'Method key missing from request', + 'status': 0, + 'message': 'Method key missing from request', 'request_id': request_id } mem.iris.send_message(self.connection_id, response) diff --git a/src/js/components/ContextMenu.js b/src/js/components/ContextMenu.js index cacadd05..fe2a9391 100755 --- a/src/js/components/ContextMenu.js +++ b/src/js/components/ContextMenu.js @@ -139,7 +139,7 @@ class ContextMenu extends React.Component{ return (helpers.uriSource(this.props.menu.items[0].uri) == 'spotify') } - toggleInLibrary(in_library){ + toggleInLibrary(e, in_library){ this.props.uiActions.hideContextMenu() if (in_library){ this.props.spotifyActions.following(this.props.menu.items[0].uri, 'DELETE') @@ -148,13 +148,13 @@ class ContextMenu extends React.Component{ } } - playQueueItem(){ + playQueueItem(e){ this.props.uiActions.hideContextMenu() var tracks = this.props.menu.items; this.props.mopidyActions.changeTrack( tracks[0].tlid ) } - removeFromQueue(){ + removeFromQueue(e){ this.props.uiActions.hideContextMenu() var tracks = this.props.menu.items; var tracks_tlids = []; @@ -164,37 +164,37 @@ class ContextMenu extends React.Component{ this.props.mopidyActions.removeTracks( tracks_tlids ); } - playURIs(){ + playURIs(e){ this.props.uiActions.hideContextMenu() this.props.mopidyActions.playURIs(this.props.menu.uris, this.props.menu.tracklist_uri) } - playURIsNext(){ + playPlaylist(e){ this.props.uiActions.hideContextMenu() - this.props.mopidyActions.enqueueURIsNext(this.props.menu.uris, this.props.menu.tracklist_uri) + this.props.mopidyActions.playPlaylist(this.props.menu.uris[0]) } - addToQueue(){ + addToQueue(e, next = false){ this.props.uiActions.hideContextMenu() - this.props.mopidyActions.enqueueURIs(this.props.menu.uris, this.props.menu.tracklist_uri) + this.props.mopidyActions.enqueueURIs(this.props.menu.uris, this.props.menu.tracklist_uri, next) } - addTracksToPlaylist(playlist_uri){ + addTracksToPlaylist(e, playlist_uri){ this.props.uiActions.hideContextMenu() this.props.uiActions.addTracksToPlaylist(playlist_uri, this.props.menu.uris) } - removeFromPlaylist(){ + removeFromPlaylist(e){ this.props.uiActions.hideContextMenu() this.props.uiActions.removeTracksFromPlaylist(this.props.menu.tracklist_uri, this.props.menu.indexes) } - startRadio(){ + startRadio(e){ this.props.uiActions.hideContextMenu() this.props.pusherActions.startRadio(this.props.menu.uris) } - goToArtist(){ + goToArtist(e){ if (!this.props.menu.items || this.props.menu.items.length <= 0 || !this.props.menu.items[0].artists || this.props.menu.items[0].artists.length <= 0){ return null } else { @@ -203,7 +203,7 @@ class ContextMenu extends React.Component{ } } - goToUser(){ + goToUser(e){ if (!this.props.menu.items || this.props.menu.items.length <= 0){ return null } else { @@ -223,7 +223,7 @@ class ContextMenu extends React.Component{ this.props.uiActions.hideContextMenu() } - closeAndDeselectTracks(){ + closeAndDeselectTracks(e){ this.props.uiActions.hideContextMenu(); // TODO } @@ -331,6 +331,14 @@ class ContextMenu extends React.Component{ ) + var play_playlist = ( + + this.playPlaylist(e)}> + Play + + + ) + var play_queue_item = ( this.playQueueItem(e)}> @@ -341,7 +349,7 @@ class ContextMenu extends React.Component{ var play_uris_next = ( - this.playURIsNext(e)}> + this.addToQueue(e, true)}> Play next @@ -367,7 +375,7 @@ class ContextMenu extends React.Component{ var toggle_in_library = ( - this.toggleInLibrary(context.in_library)}> + this.toggleInLibrary(e, context.in_library)}> {context.in_library ? 'Remove from library' : 'Add to library'} @@ -451,7 +459,7 @@ class ContextMenu extends React.Component{ case 'playlist': return (
- {play_uris} + {play_playlist} {this.canBeInLibrary() ? toggle_in_library : null} {context.source == 'spotify' ? go_to_user : null} {copy_uris} diff --git a/src/js/components/DebugInfo.js b/src/js/components/DebugInfo.js index b4d965fc..1716ff51 100755 --- a/src/js/components/DebugInfo.js +++ b/src/js/components/DebugInfo.js @@ -74,12 +74,15 @@ class DebugInfo extends React.Component{
Notifications: {this.props.ui.notifications ? Object.keys(this.props.ui.notifications).length : '0'}
+
+ Processes: {this.props.ui.processes ? Object.keys(this.props.ui.processes).length : '0'} +

_testMode: {window._testMode ? 'on' : 'off'}
- Touch: {helpers.isTouchDevice() ? 'yes' : 'no'} + Touch: {helpers.isTouchDevice() ? 'on' : 'off'}
{this.renderLoadQueue()}
diff --git a/src/js/components/Notifications.js b/src/js/components/Notifications.js index 37ccc332..d3f23002 100755 --- a/src/js/components/Notifications.js +++ b/src/js/components/Notifications.js @@ -11,25 +11,65 @@ export default class Notifications extends React.Component{ renderNotifications(){ if (!this.props.notifications || this.props.notifications.length <= 0) return null - // we only care about the last notification - var notification = this.props.notifications[this.props.notifications.length-1] + return ( + + { + this.props.notifications.map(notification => { + switch (notification.type){ + case 'shortcut': + return ( +
+ +
+ ) - if (notification.is_shortcut){ - return ( -
- -
- ) - } else { - return ( -
- this.props.uiActions.removeNotification(notification.id) } /> - { notification.content } -
- ) - } + default: + return ( +
+ this.props.uiActions.removeNotification(notification.key) } /> + { notification.content } +
+ ) + } + }) + } +
+ ) } + renderProcesses(){ + if (!this.props.processes || this.props.processes.length <= 0) return null + var processes = this.props.processes + var items = [] + + for (var key in processes){ + if (processes.hasOwnProperty(key)){ + if (processes[key].cancelling){ + items.push( +
+ Cancelling +
+ ) + } else { + items.push( +
+ this.props.uiActions.cancelProcess(key) } /> + { processes[key].content } +
+ ) + } + } + } + + return ( + + {items} + + ) + } + + // do we want the loading of everything to be displayed? + // not likely... renderLoader(){ if (!this.props.load_queue){ return null @@ -59,7 +99,7 @@ export default class Notifications extends React.Component{ return (
{this.renderNotifications()} - {this.renderLoader()} + {this.renderProcesses()}
) } diff --git a/src/js/helpers.js b/src/js/helpers.js index e282c8c9..a1573e08 100755 --- a/src/js/helpers.js +++ b/src/js/helpers.js @@ -266,7 +266,7 @@ export let asURIs = function(items){ * Merge duplicated items in an array * * @param list Array the unclean array - * @param key string = the unique key (uri, tlid, etc) + * @param key string = the unique key (id, uri, tlid, etc) **/ export let mergeDuplicates = function(list, key){ var clean_list = []; diff --git a/src/js/services/mopidy/actions.js b/src/js/services/mopidy/actions.js index 00c2f2bf..ab78b5cb 100755 --- a/src/js/services/mopidy/actions.js +++ b/src/js/services/mopidy/actions.js @@ -57,24 +57,30 @@ export function playURIs( uris, from_uri = null ){ } } -export function enqueueURIsNext( uris, from_uri = null ){ - if( typeof(uris) !== 'object' ) uris = [uris] - return { - type: 'MOPIDY_ENQUEUE_URIS_NEXT', - uris: uris, - from_uri: from_uri - } -} - -export function enqueueURIs( uris, from_uri = null, at_position = false ){ +export function enqueueURIs( uris, from_uri = null, next = false, at_position = null ){ return { type: 'MOPIDY_ENQUEUE_URIS', uris: uris, at_position: at_position, + next: next, from_uri: from_uri } } +export function playPlaylist(uri){ + return { + type: 'MOPIDY_PLAY_PLAYLIST', + uri: uri + } +} + +export function playAlbum(uri){ + return { + type: 'MOPIDY_PLAY_ALBUM', + uri: uri + } +} + export function removeTracks( tlids ){ return { type: 'MOPIDY_INSTRUCT', diff --git a/src/js/services/mopidy/middleware.js b/src/js/services/mopidy/middleware.js index e75a3c06..92da4354 100755 --- a/src/js/services/mopidy/middleware.js +++ b/src/js/services/mopidy/middleware.js @@ -4,8 +4,9 @@ import * as helpers from '../../helpers' var mopidyActions = require('./actions.js') var uiActions = require('../ui/actions.js') -var lastfmActions = require('../lastfm/actions.js') +var spotifyActions = require('../spotify/actions.js') var pusherActions = require('../pusher/actions.js') +var lastfmActions = require('../lastfm/actions.js') const MopidyMiddleware = (function(){ @@ -219,38 +220,46 @@ const MopidyMiddleware = (function(){ icon: (store.getState().ui.current_track ? helpers.getTrackIcon( store.getState().ui.current_track ) : false) } store.dispatch( pusherActions.deliverBroadcast(data) ) - break; - - case 'MOPIDY_ENQUEUE_URIS': - - // create batches of 20 uris to load - var all_uris = Object.assign([], action.uris) - var batched_uris = [] - while (all_uris.length > 0){ - batched_uris.push( all_uris.splice(0,20) ) - } - - // run each batch - for (var i = 0; i < batched_uris.length; i++){ - var value = { uris: batched_uris[i] } - if (action.at_position){ - value.at_position = action.at_position - } - - instruct( socket, store, 'tracklist.add', value ) - .then( response => { - var tlids = [] - for (var i = 0; i < response.length; i++){ - tlids.push(response[i].tlid) - } - store.dispatch( pusherActions.addQueueMetadata(tlids, action.from_uri) ) - console.info('Added '+tlids.length+' URI(s) to queue') - }) - } break - case 'MOPIDY_ENQUEUE_URIS_NEXT': + case 'MOPIDY_PLAY_PLAYLIST': + if (helpers.uriSource(action.uri) == 'spotify'){ + // playlist already in index + if (store.getState().ui.playlists.hasOwnProperty(action.uri)){ + + // make sure we didn't get this playlist from Mopidy-Spotify + // if we did, we'd have a cached version on server so no need to fetch + if (!store.getState().ui.playlists[action.uri].is_mopidy){ + store.dispatch(uiActions.startProcess('MOPIDY_ENQUEUE_URIS', 'Fetching tracks')) + store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri)) + break + } + + // not loaded, so we need to fetch + } else { + store.dispatch(uiActions.startProcess('MOPIDY_ENQUEUE_URIS', 'Fetching tracks')) + store.dispatch(spotifyActions.getAllPlaylistTracks(action.uri)) + break + } + } + + // default to load it as per usual + store.dispatch(mopidyActions.playURIs([action.uri], action.uri)) + + break + + case 'SPOTIFY_ALL_PLAYLIST_TRACKS_LOADED_FOR_PLAYING': + var uris = [] + for (var i = 0; i < action.tracks.length; i++){ + uris.push(action.tracks[i].track.uri) + } + store.dispatch(mopidyActions.playURIs(uris, action.uri)) + break + + case 'MOPIDY_ENQUEUE_URIS': + var uris_added = 0 + var remaining_uris = Object.assign([], action.uris) var current_track = store.getState().ui.current_track var current_tracklist = store.getState().ui.current_tracklist var current_track_index = -1 @@ -264,18 +273,64 @@ const MopidyMiddleware = (function(){ } } - var at_position = null - if( current_track_index > -1 ) at_position = current_track_index + 1 + let process_batch = function(){ - instruct( socket, store, 'tracklist.add', { uris: action.uris, at_position: at_position } ) - .then( response => { - var tlids = [] - for (var i = 0; i < response.length; i++){ - tlids.push(response[i].tlid) + // process is not running + if (store.getState().ui.processes && store.getState().ui.processes[action.type]){ + if (store.getState().ui.processes[action.type].cancelling){ + // recognise as cancelled + store.dispatch(uiActions.stopProcess(action.type)) + return false } - store.dispatch( pusherActions.addQueueMetadata(tlids, action.from_uri) ) - store.dispatch( uiActions.createNotification('Added '+tlids.length+' URI(s) to queue') ) - }) + } else { + return false + } + + // update our process details + store.dispatch(uiActions.startProcess(action.type, 'Adding '+remaining_uris.length+' URI(s)')) + + var params = {uris: remaining_uris.splice(0,5)} + if (action.next && current_track_index > -1){ + params.at_position = current_track_index + uris_added + 1 + } else if (action.at_position){ + params.at_position = action.at_position + } + + instruct(socket, store, 'tracklist.add', params) + .then( response => { + + // append our counter + uris_added += response.length + + // add metadata to queue + var tlids = [] + for (var i = 0; i < response.length; i++){ + tlids.push(response[i].tlid) + } + store.dispatch(pusherActions.addQueueMetadata(tlids, action.from_uri)) + + // still more URIs? run again in 100ms + // this gives our server time to handle other requests + // crude, but prevents locking the server + if (remaining_uris.length > 0){ + setTimeout( + function(){ + process_batch() + }, + 100 + ) + + // all done + } else { + store.dispatch(uiActions.stopProcess(action.type)) + } + }) + } + + // start processing + store.dispatch(uiActions.startProcess(action.type, 'Adding '+remaining_uris.length+' URI(s)')) + process_batch() + break case 'MOPIDY_PLAY_URIS': @@ -285,29 +340,37 @@ const MopidyMiddleware = (function(){ store.dispatch( pusherActions.stopRadio() ) } - // add our first track - instruct( socket, store, 'tracklist.add', { uri: action.uris[0], at_position: 0 } ) - .then( response => { + var first_uri = action.uris[0] - // treat empty response as a failed lookup - if( !response || response.length <= 0 ){ - store.dispatch( uiActions.createNotification('Failed to load URI(s)', 'bad') ) - console.error(action) - }else{ - // play it - store.dispatch( mopidyActions.changeTrack( response[0].tlid ) ); + // add our first track + instruct(socket, store, 'tracklist.add', { uri: first_uri, at_position: 0 }) + .then(response => { + + // play it (only if we got a successful lookup) + if (response.length > 0){ + store.dispatch(mopidyActions.changeTrack(response[0].tlid)); var tlids = [] for (var i = 0; i < response.length; i++){ tlids.push(response[i].tlid) } - store.dispatch( pusherActions.addQueueMetadata(tlids, action.from_uri) ) + store.dispatch(pusherActions.addQueueMetadata(tlids, action.from_uri)) + } else { + store.dispatch(uiActions.createNotification('Failed to add some URI(s)', 'bad')) + } - // add the rest of our uris (if any) - action.uris.shift(); - if( action.uris.length > 0 ){ - store.dispatch( mopidyActions.enqueueURIs( action.uris, action.from_uri, 1 ) ) - } + // add the rest of our uris (if any) + action.uris.shift(); + if( action.uris.length > 0 ){ + + // wait 100ms so the server can trigger track_changed etc + // this means our UI feels snappier as the first track shows up quickly + setTimeout( + function(){ + store.dispatch(mopidyActions.enqueueURIs( action.uris, action.from_uri, 1 )) + }, + 100 + ) } }) break; @@ -453,7 +516,8 @@ const MopidyMiddleware = (function(){ type: 'playlist', name: response.name, uri: response.uri, - source: (source == 'spotify' ? 'local' : source), + source: source, + is_mopidy: true, last_modified: response.last_modified, tracks_total: ( response.tracks ? response.tracks.length : 0 ) } @@ -477,6 +541,7 @@ const MopidyMiddleware = (function(){ response, { type: 'playlist', + is_mopidy: true, tracks: ( response.tracks ? response.tracks : [] ), tracks_total: ( response.tracks ? response.tracks.length : [] ) } @@ -701,6 +766,7 @@ const MopidyMiddleware = (function(){ var album = Object.assign( {}, { + is_mopidy: true, artists: response[uri][0].artists, tracks: response[uri], tracks_total: response[uri].length @@ -729,6 +795,7 @@ const MopidyMiddleware = (function(){ { images: [] }, response[0].album, { + is_mopidy: true, artists: response[0].artists, tracks: response, tracks_total: response.length @@ -832,6 +899,7 @@ const MopidyMiddleware = (function(){ {}, (response ? response[0].artists[0] : {}), { + is_mopidy: true, albums_uris: helpers.asURIs(albums), tracks: response.slice(0,10) } diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index ba3a2ff3..2d215e7b 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -890,7 +890,7 @@ export function createPlaylist( name, is_public ){ } } -export function savePlaylist( uri, name, is_public ){ +export function savePlaylist(uri, name, is_public){ return (dispatch, getState) => { sendRequest( dispatch, getState, 'users/'+ getState().spotify.me.id +'/playlists/'+ helpers.getFromUri('playlistid',uri), 'PUT', { name: name, public: is_public } ) @@ -906,7 +906,7 @@ export function savePlaylist( uri, name, is_public ){ } } -export function getPlaylist( uri ){ +export function getPlaylist(uri){ return (dispatch, getState) => { // get the main playlist object @@ -919,6 +919,7 @@ export function getPlaylist( uri ){ description = response.description description = description.split('
{ + tracks = [...tracks, ...response.items] + loadNextPlaylistTracksBatch(dispatch, getState, uri, tracks, response) + }); + }else{ + dispatch({ + type: 'SPOTIFY_ALL_PLAYLIST_TRACKS_LOADED_FOR_PLAYING', + uri: uri, + tracks: tracks + }); + } +} + +export function getAllPlaylistTracks(uri){ + return (dispatch, getState) => { + sendRequest(dispatch, getState, 'users/'+ helpers.getFromUri('userid',uri) +'/playlists/'+ helpers.getFromUri('playlistid',uri) +'/tracks?market='+getState().spotify.country) + .then( response => { + loadNextPlaylistTracksBatch(dispatch, getState, uri, response.items, response) + }); + } +} + + +/** + * Get all of our playlists in one foul swoop + * + * Recursively gets playlists until no more .next value. Fails when user has + * a lot of playlists, and we hit the API limits + **/ +function loadNextPlaylistsBatch(dispatch, getState, playlists, lastResponse){ if( lastResponse.next ){ sendRequest( dispatch, getState, lastResponse.next ) .then( response => { @@ -966,7 +1004,7 @@ export function getAllLibraryPlaylists(){ } } -export function toggleFollowingPlaylist( uri, method ){ +export function toggleFollowingPlaylist(uri, method){ if( method == 'PUT' ) var new_state = 1 if( method == 'DELETE' ) var new_state = 0 diff --git a/src/js/services/ui/actions.js b/src/js/services/ui/actions.js index 85ba9a82..4170aee0 100755 --- a/src/js/services/ui/actions.js +++ b/src/js/services/ui/actions.js @@ -224,22 +224,21 @@ export function createBrowserNotification( data ){ } } -export function createNotification(content, type = 'default', is_shortcut = false){ +export function createNotification(content, type = 'default', key = helpers.generateGuid()){ return { type: 'CREATE_NOTIFICATION', notification: { - id: helpers.generateGuid(), + key: key, type: type, - content: content, - is_shortcut: is_shortcut + content: content } } } -export function removeNotification( id ){ +export function removeNotification(key){ return { type: 'REMOVE_NOTIFICATION', - id: id + key: key } } @@ -262,3 +261,25 @@ export function stopLoading(key){ key: key } } + +export function startProcess(key,content){ + return { + type: 'START_PROCESS', + key: key, + content: content + } +} + +export function cancelProcess(key){ + return { + type: 'CANCEL_PROCESS', + key: key + } +} + +export function stopProcess(key){ + return { + type: 'STOP_PROCESS', + key: key + } +} diff --git a/src/js/services/ui/middleware.js b/src/js/services/ui/middleware.js index fb2fae3a..9ea43811 100755 --- a/src/js/services/ui/middleware.js +++ b/src/js/services/ui/middleware.js @@ -271,9 +271,9 @@ const UIMiddleware = (function(){ // start a timeout to remove this notification var timeout = setTimeout( function(){ - store.dispatch(uiActions.removeNotification(action.notification.id)) + store.dispatch(uiActions.removeNotification(action.notification.key)) }, - 3000 + (action.notification.type == 'shortcut' ? 1000 : 3000) ) next(action) diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index 5950a4a1..28f9804d 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -711,15 +711,16 @@ export default function reducer(ui = {}, action){ case 'CREATE_NOTIFICATION': var notifications = [...ui.notifications, action.notification] + notifications = helpers.mergeDuplicates(notifications,'key') return Object.assign({}, ui, { notifications: notifications }) case 'REMOVE_NOTIFICATION': var notifications = Object.assign([], ui.notifications) - function getByID( notification ){ - return notification.id === action.id + function getByKey( notification ){ + return notification.key === action.key } - var index = notifications.findIndex(getByID) + var index = notifications.findIndex(getByKey) if( index > -1 ) notifications.splice(index, 1) return Object.assign({}, ui, { notifications: notifications }) @@ -727,7 +728,7 @@ export default function reducer(ui = {}, action){ /** - * Loader + * Loading and processes **/ case 'START_LOADING': @@ -742,6 +743,34 @@ export default function reducer(ui = {}, action){ } return Object.assign({}, ui, {load_queue: load_queue}) + case 'START_PROCESS': + var processes = Object.assign({}, (ui.processes ? ui.processes : [])) + processes[action.key] = { + key: action.key, + content: action.content + } + return Object.assign({}, ui, {processes: processes}) + + case 'CANCEL_PROCESS': + var processes = Object.assign({}, (ui.processes ? ui.processes : {})) + if (processes[action.key]){ + processes[action.key] = Object.assign( + {}, + processes[action.key], + { + cancelling: true + } + ) + } + return Object.assign({}, ui, {processes: processes}) + + case 'STOP_PROCESS': + var processes = Object.assign({}, (ui.processes ? ui.processes : {})) + if (processes[action.key]){ + delete processes[action.key] + } + return Object.assign({}, ui, {processes: processes}) + default: return ui diff --git a/src/js/views/App.js b/src/js/views/App.js index 5d51ef8d..541c97a2 100755 --- a/src/js/views/App.js +++ b/src/js/views/App.js @@ -106,10 +106,10 @@ class App extends React.Component{ case 32: // spacebar if (this.props.play_state == 'playing'){ this.props.mopidyActions.pause() - this.props.uiActions.createNotification(null, 'pause', true) + this.props.uiActions.createNotification('pause', 'shortcut', 'shortcut') }else{ this.props.mopidyActions.play() - this.props.uiActions.createNotification(null, 'play', true) + this.props.uiActions.createNotification('play', 'shortcut', 'shortcut') } break; @@ -158,6 +158,7 @@ class App extends React.Component{ {this.props.debug_info ? : null} @@ -174,6 +175,7 @@ class App extends React.Component{ const mapStateToProps = (state, ownProps) => { return { notifications: (state.ui.notifications ? state.ui.notifications : []), + processes: (state.ui.processes ? state.ui.processes : {}), load_queue: (state.ui.load_queue ? state.ui.load_queue : {}), mopidy_connected: state.mopidy.connected, spotify_authorized: state.spotify.authorized, diff --git a/src/js/views/Debug.js b/src/js/views/Debug.js index 0692402f..816927c6 100755 --- a/src/js/views/Debug.js +++ b/src/js/views/Debug.js @@ -103,7 +103,9 @@ class Debug extends React.Component{
- this.props.uiActions.createNotification('Test '+Math.floor(Date.now() / 1000))}>Create notification + this.props.uiActions.createNotification('Test notification')}>Create notification + this.props.uiActions.startProcess('test_process', 'Test process')}>Start process + this.props.uiActions.stopProcess('test_process')}>Stop process
diff --git a/src/js/views/Playlist.js b/src/js/views/Playlist.js index 121e59fa..dc7489d0 100755 --- a/src/js/views/Playlist.js +++ b/src/js/views/Playlist.js @@ -70,8 +70,7 @@ class Playlist extends React.Component{ play(){ ReactGA.event({ category: 'Playlist', action: 'Play', label: this.props.playlist.uri }) - var tracks_uris = helpers.asURIs(this.props.playlist.tracks) - this.props.mopidyActions.playURIs(tracks_uris, this.props.params.uri) + this.props.mopidyActions.playPlaylist(this.props.playlist.uri) } follow(){ diff --git a/src/scss/components/_notifications.scss b/src/scss/components/_notifications.scss index da2ee27e..12d6d206 100755 --- a/src/scss/components/_notifications.scss +++ b/src/scss/components/_notifications.scss @@ -44,7 +44,7 @@ background: $red; } - &.loading { + &.process { position: relative; padding-right: 18px;