From 3b5babf52c3338788838cf961a6ca88cc445d89e Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Mon, 9 Jan 2017 09:29:56 +1300 Subject: [PATCH] Revamp Pusher server; Simplified broadcasts; Async pusher requests --- mopidy_iris/pusher.py | 306 +++++++++--------- .../Modal/SendAuthorizationModal.js | 10 +- src/js/components/PusherConnectionList.js | 4 +- src/js/services/localstorage/middleware.js | 6 +- src/js/services/pusher/actions.js | 27 +- src/js/services/pusher/middleware.js | 227 +++++++------ src/js/services/pusher/reducer.js | 10 +- src/js/services/spotify/actions.js | 39 ++- src/js/services/ui/middleware.js | 31 ++ src/js/views/Artist.js | 3 + src/js/views/Debug.js | 31 +- src/js/views/Settings.js | 4 +- 12 files changed, 374 insertions(+), 324 deletions(-) diff --git a/mopidy_iris/pusher.py b/mopidy_iris/pusher.py index e741053e..41720b18 100755 --- a/mopidy_iris/pusher.py +++ b/mopidy_iris/pusher.py @@ -14,21 +14,18 @@ frontend = {} # Send a message to an individual connection # # @param recipient_connection_ids = array -# @param type = string (type of event, ie connection_opened) # @param action = string (action method of this message) -# @param message_id = string (used for callbacks) +# @param request_id = string (used for callbacks) # @param data = array (any data required to include in our message) ## -def send_message( recipient_connection_id, type, action, message_id, data ): +def send_message( recipient_connection_id, action, request_id, data ): message = { - 'type': type, 'action': action, - 'message_id': message_id, + 'request_id': request_id, 'data': data } connections[recipient_connection_id]['connection'].write_message( json_encode(message) ) - ## # Broadcast a message to all recipients # @@ -143,173 +140,158 @@ class PusherWebsocketHandler(tornado.websocket.WebSocketHandler): } logger.debug('Pusher message received: '+message) - - # query-based message that is expecting a response - if messageJson['type'] == 'query': - - # fetch our pusher connections - if messageJson['action'] == 'get_connections': - - connectionsDetailsList = [] - for connection in connections.itervalues(): - connectionsDetailsList.append(connection['client']) - - send_message( - self.connectionid, - 'response', - messageJson['action'], - messageJson['message_id'], - { 'connections': connectionsDetailsList } - ) - - # change connection's client username - elif messageJson['action'] == 'change_username': - - # username is the only value we allow clients to change - connections[messageJson['origin']['connectionid']]['client']['username'] = messageJson['username'] - - # respond to request - send_message( - self.connectionid, - 'response', - messageJson['action'], - messageJson['message_id'], - { 'connection': connections[messageJson['origin']['connectionid']]['client'] } - ) - - # notify all clients of this change - broadcast( 'connection_updated', { 'connections': connections[messageJson['origin']['connectionid']]['client'] }) - - # start radio - elif messageJson['action'] == 'start_radio': - - # pull out just the radio data (we don't want all the message_id guff) - radio = { - 'enabled': 1, - 'seed_artists': messageJson['seed_artists'], - 'seed_genres': messageJson['seed_genres'], - 'seed_tracks': messageJson['seed_tracks'] + + # broadcast message to other connections (except for self) + if messageJson['action'] == 'broadcast': + + # respond to request with status update + send_message( + self.connectionid, + 'response', + messageJson['request_id'], + { 'status': 'Ok' } + ) + + for connection in connections.itervalues(): + if connection['client']['connectionid'] != self.connectionid: + connection['connection'].write_message(messageJson) + + # send authroization details + elif messageJson['action'] == 'send_authorization': + + # make sure we actually have a connection matching the provided connectionid + if messageJson['recipient_connectionid'] in connections: + + # send payload to recipient + authorization_message = { + 'type': 'broadcast', + 'action': 'received_authorization', + 'authorization': messageJson['authorization'], + 'me': messageJson['me'], + 'origin': messageJson['origin'] } - radio = self.frontend.start_radio( radio ) - send_message( - self.connectionid, - 'response', - 'radio', - messageJson['message_id'], - { 'radio': radio } - ) - - # stop radio - elif messageJson['action'] == 'stop_radio': - radio = self.frontend.stop_radio() - send_message( - self.connectionid, - 'response', - 'radio', - messageJson['message_id'], - { 'radio': self.frontend.radio } - ) - - # fetch our current radio state - elif messageJson['action'] == 'get_radio': - send_message( - self.connectionid, - 'response', - 'radio', - messageJson['message_id'], - { 'radio': self.frontend.radio } - ) + connections[messageJson['recipient_connectionid']]['connection'].write_message(authorization_message) - # MOVED TO HTTP ENDPOINT FOR SIMPLICITY IN REACT+REDUX - - # # get our spotify authentication token - # elif messageJson['action'] == 'get_spotify_token': - # send_message( - # self.connectionid, - # 'response', - # messageJson['action'], - # messageJson['message_id'], - # { 'token': self.frontend.spotify_token } - # ) - - # # refresh our spotify authentication token - # elif messageJson['action'] == 'refresh_spotify_token': - # token = self.frontend.refresh_spotify_token() - # send_message( - # self.connectionid, - # 'response', - # messageJson['action'], - # messageJson['message_id'], - # { 'token': token } - # ) - - # get system version and check for upgrade - elif messageJson['action'] == 'get_version': - version = self.frontend.get_version() - send_message( + # respond to request with status update + send_message( self.connectionid, 'response', - messageJson['action'], - messageJson['message_id'], - { 'version': version } + messageJson['request_id'], + { 'status': 'Ok' } ) - - # get system version and check for upgrade - elif messageJson['action'] == 'perform_upgrade': - version = self.frontend.get_version() - version['upgrade_successful'] = self.frontend.perform_upgrade() - send_message( - self.connectionid, - 'response', - messageJson['action'], - messageJson['message_id'], - { 'version': version } - ) - - # notify all clients of this change - broadcast( 'upgraded', { 'version': version }) - - # restart mopidy - elif messageJson['action'] == 'restart': - self.frontend.restart() - - - # not an action we recognise! else: - send_message( + # respond to request with status update + send_message( self.connectionid, 'response', - messageJson['action'], - messageJson['message_id'], - { 'error': 'Unhandled action' } + messageJson['request_id'], + { 'error': 'Could not send to that connection, does not exist' } ) + + # fetch our pusher connections + elif messageJson['action'] == 'get_connections': - # point-and-shoot one-way broadcast - elif messageJson['type'] == 'broadcast': - - # recipients array has items, so only send to specific clients - if messageJson.has_key('recipients'): - for connectionid in messageJson['recipients']: - connectionid = connectionid.encode("utf-8") - - # make sure we actually have a connection matching the provided connectionid - if connectionid in connections: - connections[connectionid]['connection'].write_message(messageJson) - else: - logger.warn('Pusher: Tried to broadcast to connectionid '+connectionid+' but it doesn\'t exist!'); - - # empty, so send to all clients - else: - for connection in connections.itervalues(): + connectionsDetailsList = [] + for connection in connections.itervalues(): + connectionsDetailsList.append(connection['client']) - # if we've set ignore_self, then don't send message to originating connection - if messageJson.has_key('ignore_self'): - if connection['client']['connectionid'] != messageJson['origin']['connectionid']: - connection['connection'].write_message(messageJson) - - # send it to everyone - else: - connection['connection'].write_message(messageJson) + send_message( + self.connectionid, + 'response', + messageJson['request_id'], + { 'connections': connectionsDetailsList } + ) + + # change connection's client username + elif messageJson['action'] == 'set_username': + + # username is the only value we allow clients to change + connections[messageJson['origin']['connectionid']]['client']['username'] = messageJson['username'] + + # respond to request + send_message( + self.connectionid, + 'response', + messageJson['request_id'], + { 'username': messageJson['username'] } + ) + + # notify all clients of this change + broadcast( 'connection_updated', { 'connections': connections[messageJson['origin']['connectionid']]['client'] }) + + # start radio + elif messageJson['action'] == 'start_radio': + + # pull out just the radio data (we don't want all the request_id guff) + radio = { + 'enabled': 1, + 'seed_artists': messageJson['seed_artists'], + 'seed_genres': messageJson['seed_genres'], + 'seed_tracks': messageJson['seed_tracks'] + } + radio = self.frontend.start_radio( radio ) + send_message( + self.connectionid, + 'response', + messageJson['request_id'], + { 'radio': radio } + ) + + # stop radio + elif messageJson['action'] == 'stop_radio': + radio = self.frontend.stop_radio() + send_message( + self.connectionid, + 'response', + messageJson['request_id'], + { 'radio': self.frontend.radio } + ) + + # fetch our current radio state + elif messageJson['action'] == 'get_radio': + send_message( + self.connectionid, + 'response', + messageJson['request_id'], + { 'radio': self.frontend.radio } + ) + + # get system version and check for upgrade + elif messageJson['action'] == 'get_version': + version = self.frontend.get_version() + send_message( + self.connectionid, + 'response', + messageJson['request_id'], + { 'version': version } + ) + + # get system version and check for upgrade + elif messageJson['action'] == 'perform_upgrade': + version = self.frontend.get_version() + version['upgrade_successful'] = self.frontend.perform_upgrade() + send_message( + self.connectionid, + 'response', + messageJson['request_id'], + { 'version': version } + ) + + # notify all clients of this change + broadcast( 'upgraded', { 'version': version }) + + # restart mopidy + elif messageJson['action'] == 'restart': + self.frontend.restart() + + # not an action we recognise! + else: + send_message( + self.connectionid, + 'response', + messageJson['request_id'], + { 'error': 'Unhandled action' } + ) logger.debug( 'Pusher: Message received from '+ self.connectionid ) diff --git a/src/js/components/Modal/SendAuthorizationModal.js b/src/js/components/Modal/SendAuthorizationModal.js index f68842fa..c609bc0d 100755 --- a/src/js/components/Modal/SendAuthorizationModal.js +++ b/src/js/components/Modal/SendAuthorizationModal.js @@ -17,15 +17,7 @@ class SendAuthorizationModal extends React.Component{ handleClick(e, connectionid){ e.preventDefault() - var data = { - recipients: [connectionid], - action: 'send_authorization', - data: { - authorization: this.props.authorization, - me: this.props.me - } - } - this.props.pusherActions.instruct( 'broadcast', data ) + this.props.pusherActions.sendAuthorization( connectionid, this.props.authorization, this.props.me ) this.props.uiActions.closeModal() return false; } diff --git a/src/js/components/PusherConnectionList.js b/src/js/components/PusherConnectionList.js index b777c234..163c31b2 100755 --- a/src/js/components/PusherConnectionList.js +++ b/src/js/components/PusherConnectionList.js @@ -16,13 +16,13 @@ class PusherConnectionList extends React.Component{ componentDidMount(){ if( this.props.connected ){ - this.props.pusherActions.getConnectionList(); + this.props.pusherActions.getConnections(); } } componentWillReceiveProps(newProps){ if( !this.props.connected && newProps.connected ){ - this.props.pusherActions.getConnectionList(); + this.props.pusherActions.getConnections(); } } diff --git a/src/js/services/localstorage/middleware.js b/src/js/services/localstorage/middleware.js index 3c323be8..435627b4 100755 --- a/src/js/services/localstorage/middleware.js +++ b/src/js/services/localstorage/middleware.js @@ -12,7 +12,7 @@ const localstorageMiddleware = (function(){ // append our state to a global variable. This gives us access to debug the store at any point window._store = store - //console.log(action) + console.log(action) switch( action.type ){ @@ -35,9 +35,9 @@ const localstorageMiddleware = (function(){ localStorage.setItem('pusher', JSON.stringify(pusher)); break; - case 'PUSHER_CHANGE_USERNAME': + case 'PUSHER_USERNAME': var stored_pusher = JSON.parse( localStorage.getItem('pusher') ) - var pusher = Object.assign({}, stored_pusher, { username: action.data.connection.username }) + var pusher = Object.assign({}, stored_pusher, { username: action.data.username }) localStorage.setItem('pusher', JSON.stringify(pusher)) break; diff --git a/src/js/services/pusher/actions.js b/src/js/services/pusher/actions.js index 42c8ed28..db1a2791 100755 --- a/src/js/services/pusher/actions.js +++ b/src/js/services/pusher/actions.js @@ -10,6 +10,13 @@ export function setPort( port ){ } } +export function setUsername( username ){ + return { + type: 'PUSHER_SET_USERNAME', + username: username + } +} + export function connect(){ return { type: 'PUSHER_CONNECT' @@ -28,21 +35,28 @@ export function performUpgrade(){ } } -export function getConnectionList(){ +export function getConnections(){ return { - type: 'PUSHER_INSTRUCT', - action: 'get_connections' + type: 'PUSHER_GET_CONNECTIONS' } } -export function instruct( message_type, data = null ){ +export function instruct( data = null ){ return { type: 'PUSHER_INSTRUCT', - message_type: message_type, data: data } } +export function sendAuthorization( recipient_connectionid, authorization, me ){ + return { + type: 'PUSHER_SEND_AUTHORIZATION', + recipient_connectionid: recipient_connectionid, + authorization: authorization, + me: me + } +} + export function startRadio( uris ){ return { type: 'PUSHER_START_RADIO', @@ -56,10 +70,9 @@ export function stopRadio(){ } } -export function debug( call, data = null ){ +export function debug( data = null ){ return { type: 'PUSHER_DEBUG', - call: call, data: data } } \ No newline at end of file diff --git a/src/js/services/pusher/middleware.js b/src/js/services/pusher/middleware.js index 90d24e49..902859b6 100755 --- a/src/js/services/pusher/middleware.js +++ b/src/js/services/pusher/middleware.js @@ -7,27 +7,53 @@ const PusherMiddleware = (function(){ // container for the actual Mopidy socket var socket = null; + var deferredRequests = []; + + const resolveRequest = (requestId, message ) => { + var response = JSON.parse( message ); + deferredRequests[request_id].resolve( response ); + delete deferredRequests[request_id]; + } + + const rejectRequest = (requestId, message) => { + deferredRequests[requestId].reject( message ); + } // handle all manner of socket messages const handleMessage = (ws, store, message) => { - switch( message.action ){ - default: - var name = 'unspecified' - if( message.action ) name = message.action - name = name.replace('get_','').toUpperCase() - store.dispatch({ type: 'PUSHER_'+name, data: message.data }) + + console.log('handle', message) + + switch (message.action){ + case 'response': + if (typeof( deferredRequests[ message.request_id ]) !== 'undefined' ){ + deferredRequests[ message.request_id ].resolve( message ) + } else { + console.error('Pusher: Response with no matching request', message); + } + break + + case 'broadcast': + var type = 'UNRECOGNISED_BROADCAST' + if( message.data.type ) type = message.data.type.toUpperCase() + store.dispatch({ type: type, data: message.data }) + break } } - const makeRequest = (data) => { - data.type = 'query'; - data.message_id = helpers.generateGuid(); - socket.send( JSON.stringify(data) ); - } + const request = (data) => { + return new Promise( (resolve, reject) => { - const broadcast = (data) => { - data.type = 'broadcast'; - socket.send( JSON.stringify(data) ); + // send the payload + data.request_id = helpers.generateGuid() + socket.send( JSON.stringify(data) ) + + // add query to our deferred responses + deferredRequests[ data.request_id ] = { + resolve: resolve, + reject: reject + } + }) } @@ -42,6 +68,15 @@ const PusherMiddleware = (function(){ switch(action.type) { + case 'PUSHER_INSTRUCT': + request( action.data ) + .then( + response => { + store.dispatch({ type: 'PUSHER_INSTRUCT', data: response.data }) + } + ) + break + case 'PUSHER_CONNECT': if(socket != null) socket.close(); @@ -63,7 +98,12 @@ const PusherMiddleware = (function(){ socket.onopen = () => { store.dispatch({ type: 'PUSHER_CONNECTED', connection: connection }); store.dispatch({ type: 'PUSHER_SET_USERNAME', username: connection.username }); - makeRequest({ action: 'get_radio' }); + request({ action: 'get_radio' }) + .then( + response => { + store.dispatch({ type: 'PUSHER_RADIO', data: response.data }) + } + ) }; socket.onmessage = (message) => { @@ -74,84 +114,69 @@ const PusherMiddleware = (function(){ break; case 'PUSHER_CONNECTED': - makeRequest({ action: 'get_version' }); + request({ action: 'get_version' }) + .then( + response => { + store.dispatch({ type: 'PUSHER_VERSION', data: response.data }) + } + ) return next(action); break; case 'PUSHER_UPGRADING': - makeRequest({ action: 'perform_upgrade' }); + request({ action: 'perform_upgrade' }) + .then( + response => { + store.dispatch({ type: 'PUSHER_UPGRADE', data: response.data }) + } + ) return next(action); break; + case 'PUSHER_SET_USERNAME': + request({ action: 'set_username', username: action.username }) + .then( + response => { + store.dispatch({ type: 'PUSHER_USERNAME', data: { username: response.data.username }}) + } + ) + return next(action); + break; + + case 'PUSHER_GET_CONNECTIONS': case 'PUSHER_CLIENT_CONNECTED': - makeRequest({ action: 'get_connections' }); + request({ action: 'get_connections' }) + .then( + response => { + store.dispatch({ type: 'PUSHER_CONNECTIONS', data: response.data }) + } + ) return next(action); - break; - - case 'PUSHER_INSTRUCT': - switch( action.message_type ){ - case 'query': - makeRequest( action.data ) - break - case 'broadcast': - broadcast( action.data ) - break - } - break; + break case 'PUSHER_DEBUG': - switch( action.call ){ - case 'query': - makeRequest( action.data ) - // THIS DOES NOT RETURN A PROMISE SO CAN'T DETECT RELATED RESPONSES - /*.then( response => { - store.dispatch({ type: 'DEBUG', response: response }) - })*/ - break - case 'broadcast': - broadcast( action.data ) - /*.then( response => { - store.dispatch({ type: 'DEBUG', response: response }) - })*/ - break - default: - store.dispatch({ type: 'DEBUG', response: '{ "error": "Invalid call" }' }) - break - } + request( action.data ) + .then( + response => { + store.dispatch({ type: 'DEBUG', response: response.data }) + } + ) break; - case 'PUSHER_SEND_BROADCAST': - broadcast({ action: action.action, data: action.data }); + case 'PUSHER_SEND_AUTHORIZATION': + request({ + action: 'send_authorization', + recipient_connectionid: action.recipient_connectionid, + authorization: action.authorization, + me: action.me + }) + .then( + response => { + uiActions.createNotification('Authorization sent') + } + ) break; - case 'PUSHER_NOTIFICATION': - case 'PUSHER_BROADCAST': - - var notification = window.Notification || window.mozNotification || window.webkitNotification; - if ('undefined' === typeof notification) return false; - if ('undefined' !== typeof notification) notification.requestPermission(function(permission){}); - - // handle nested data objects - var data = {} - if( typeof(action.data) ) data = action.data - if( typeof(data.data) ) data = Object.assign({}, data, data.data) - - // construct our browser notification - var title = ''; - var options = { - body: '', - dir: 'auto', - lang: 'EN', - tag: 'iris' - }; - if( data.title ) title = data.title; - if( data.body ) options.body = data.body; - if( data.icon ) options.icon = data.icon; - - // make it so - var notification = new notification( title, options ); - break - case 'PUSHER_SEND_AUTHORIZATION': if( window.confirm('Spotify authorization for user '+action.data.me.id+' received. Do you want to import?') ){ @@ -168,19 +193,20 @@ const PusherMiddleware = (function(){ case 'PUSHER_START_RADIO': - store.dispatch({ - type: 'PUSHER_SEND_BROADCAST', + request({ action: 'broadcast', - ignore_self: true, data: { - type: 'notification', - data: { - title: 'Radio started', - body: store.getState().pusher.username +' started radio mode', - icon: '' - } - } + type: 'browser_notification', + title: 'Radio started', + body: store.getState().pusher.username +' started radio mode', + icon: '' + } }) + .then( + response => { + uiActions.createNotification('Starting radio...') + } + ) var data = { action: 'start_radio', @@ -200,24 +226,25 @@ const PusherMiddleware = (function(){ } } - makeRequest( data ) + request( data ) break case 'PUSHER_STOP_RADIO': - store.dispatch({ - type: 'PUSHER_SEND_BROADCAST', + request({ action: 'broadcast', - ignore_self: true, data: { - type: 'notification', - data: { - title: 'Radio stopped', - body: store.getState().pusher.username +' stopped radio mode', - icon: '' - } - } + type: 'browser_notification', + title: 'Radio stopped', + body: store.getState().pusher.username +' stopped radio mode', + icon: '' + } }) + .then( + response => { + uiActions.createNotification('Stopping radio') + } + ) store.dispatch( uiActions.createNotification('Stopping radio') ) var data = { @@ -226,7 +253,7 @@ const PusherMiddleware = (function(){ seed_genres: [], seed_tracks: [] } - makeRequest( data ) + request( data ) break // This action is irrelevant to us, pass it on to the next middleware diff --git a/src/js/services/pusher/reducer.js b/src/js/services/pusher/reducer.js index c94091a4..109581f8 100755 --- a/src/js/services/pusher/reducer.js +++ b/src/js/services/pusher/reducer.js @@ -20,8 +20,11 @@ export default function reducer(pusher = {}, action){ case 'PUSHER_SET_PORT': return Object.assign({}, pusher, { port: action.port }); - case 'PUSHER_CHANGE_USERNAME': - return Object.assign({}, pusher, { username: action.data.connection.username }); + case 'PUSHER_USERNAME': + return Object.assign({}, pusher, { username: action.data.username }); + + case 'PUSHER_CONNECTIONS': + return Object.assign({}, pusher, { connections: action.data.connections }); case 'PUSHER_CONNECTION_UPDATED': function byID(connection){ @@ -34,9 +37,6 @@ export default function reducer(pusher = {}, action){ return Object.assign({}, pusher, { connections: connections }); - case 'PUSHER_CONNECTIONS': - return Object.assign({}, pusher, { connections: action.data.connections }); - case 'PUSHER_VERSION': return Object.assign({}, pusher, { version: action.data.version, diff --git a/src/js/services/spotify/actions.js b/src/js/services/spotify/actions.js index 06b76f6a..84c5303d 100755 --- a/src/js/services/spotify/actions.js +++ b/src/js/services/spotify/actions.js @@ -429,19 +429,44 @@ export function resolveRadioSeeds( radio ){ seed_genres: [] } - var artist_ids = ''; - for (var i = 0; i < radio.seed_artists.length; i++){ - if (i > 0) artist_ids += ',' - artist_ids += helpers.getFromUri('artistid', radio.seed_artists[i]) - } + var requests = [] - $.when( - sendRequest( dispatch, getState, 'artists/'+ artist_ids ) + if (radio.seed_artists.length > 0){ + var artist_ids = ''; + for (var i = 0; i < radio.seed_artists.length; i++){ + if (i > 0) artist_ids += ',' + artist_ids += helpers.getFromUri('artistid', radio.seed_artists[i]) + } + + // add to our list of async requests + requests.push( + sendRequest( dispatch, getState, 'artists/'+ artist_ids ) .then( response => { if (!(response instanceof Array)) response = [response] Object.assign(resolved_seeds.seed_artists, response); }) + ) + } + if (radio.seed_tracks.length > 0){ + var track_ids = ''; + for (var i = 0; i < radio.seed_tracks.length; i++){ + if (i > 0) track_ids += ',' + track_ids += helpers.getFromUri('trackid', radio.seed_tracks[i]) + } + + // add to our list of async requests + requests.push( + sendRequest( dispatch, getState, 'tracks/'+ track_ids ) + .then( response => { + if (!(response instanceof Array)) response = [response] + Object.assign(resolved_seeds.seed_tracks, response); + }) + ) + } + + $.when.apply( + $, requests ).then( () => { dispatch({ type: 'PUSHER_RADIO_SEEDS_RESOLVED', diff --git a/src/js/services/ui/middleware.js b/src/js/services/ui/middleware.js index 2e4137cc..f7392dc6 100755 --- a/src/js/services/ui/middleware.js +++ b/src/js/services/ui/middleware.js @@ -130,6 +130,37 @@ const UIMiddleware = (function(){ next(action) break + case 'RESTART': + location.reload() + break + + case 'BROWSER_NOTIFICATION': + + var notification = window.Notification || window.mozNotification || window.webkitNotification; + if ('undefined' === typeof notification) return false; + if ('undefined' !== typeof notification) notification.requestPermission(function(permission){}); + + // handle nested data objects + var data = {} + if( typeof(action.data) ) data = action.data + if( typeof(data.data) ) data = Object.assign({}, data, data.data) + + // construct our browser notification + var title = ''; + var options = { + body: '', + dir: 'auto', + lang: 'EN', + tag: 'iris' + }; + if( data.title ) title = data.title; + if( data.body ) options.body = data.body; + if( data.icon ) options.icon = data.icon; + + // make it so + var notification = new notification( title, options ); + break + case 'CREATE_NOTIFICATION': // start a timeout to remove this notification diff --git a/src/js/views/Artist.js b/src/js/views/Artist.js index b341912c..08927b09 100755 --- a/src/js/views/Artist.js +++ b/src/js/views/Artist.js @@ -61,6 +61,9 @@ class Artist extends React.Component{ }else if( source == 'local' && props.mopidy_connected ){ this.props.mopidyActions.getArtist( props.params.uri ); } + + // go back to overview + this.setState({ sub_view: 'overview' }) } loadMore(){ diff --git a/src/js/views/Debug.js b/src/js/views/Debug.js index 008945dc..ee468bff 100755 --- a/src/js/views/Debug.js +++ b/src/js/views/Debug.js @@ -25,7 +25,6 @@ class Debug extends React.Component{ this.state = { mopidy_call: 'playlists.asList', mopidy_data: '{}', - pusher_call: 'broadcast', pusher_data: '{}' } } @@ -33,9 +32,9 @@ class Debug extends React.Component{ componentDidMount(){ if( this.props.connectionid ){ var data = { - action: "notification", - recipients: [this.props.connectionid], + action: "broadcast", data: { + type: 'browser_notification', title: "Title", body: "Test notification", icon: "http://lorempixel.com/100/100/nature/" @@ -47,15 +46,12 @@ class Debug extends React.Component{ callMopidy(e){ e.preventDefault() - console.info('Mopidy Debugger', this.state.mopidy_call, JSON.parse(this.state.mopidy_data) ) this.props.mopidyActions.debug( this.state.mopidy_call, JSON.parse(this.state.mopidy_data) ) } callPusher(e){ e.preventDefault() - console.info('Pusher Debugger', this.state.pusher_call, JSON.parse(this.state.pusher_data) ) - this.props.pusherActions.debug( this.state.pusher_call, JSON.parse(this.state.pusher_data) ) - this.props.uiActions.debugResponse({ status: 1, message: 'Sent', call: this.state.pusher_call, data: this.state.pusher_data }) + this.props.pusherActions.debug( JSON.parse(this.state.pusher_data) ) } render(){ @@ -82,10 +78,6 @@ class Debug extends React.Component{ - - -
-

Mopidy

this.callMopidy(e)}>
@@ -114,21 +106,8 @@ class Debug extends React.Component{
-
- -
-

Pusher

this.callPusher(e)}> -
-
Call
-
- this.setState({ pusher_call: e.target.value })} - value={ this.state.pusher_call } /> -
-
Data
@@ -146,13 +125,11 @@ class Debug extends React.Component{
-
- -

Response

 						{ this.props.debug_response ? JSON.stringify(this.props.debug_response, null, 2) : null }
 					
+
); diff --git a/src/js/views/Settings.js b/src/js/views/Settings.js index 1a9c40bd..47ee71c8 100755 --- a/src/js/views/Settings.js +++ b/src/js/views/Settings.js @@ -168,7 +168,7 @@ class Settings extends React.Component{ this.setState({ pusher_username: e.target.value }) } - onBlur={ e => this.props.pusherActions.instruct( 'query', { action: 'change_username', username: this.state.pusher_username }) } + onBlur={ e => this.props.pusherActions.setUsername(this.state.pusher_username) } value={ this.state.pusher_username } /> @@ -178,7 +178,7 @@ class Settings extends React.Component{ this.setState({ pusher_port: e.target.value })} - onBlur={ e => this.props.pusherActions.setPort( this.state.pusher_port ) } + onBlur={ e => this.props.pusherActions.setPort(this.state.pusher_port) } value={ this.state.pusher_port } />