Revamp Pusher server; Simplified broadcasts; Async pusher requests
This commit is contained in:
@ -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 )
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(){
|
||||
|
||||
@ -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{
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
<h4 className="underline">Mopidy</h4>
|
||||
<form onSubmit={(e) => this.callMopidy(e)}>
|
||||
<div className="field">
|
||||
@ -114,21 +106,8 @@ class Debug extends React.Component{
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
<h4 className="underline">Pusher</h4>
|
||||
<form onSubmit={(e) => this.callPusher(e)}>
|
||||
<div className="field">
|
||||
<div className="name">Call</div>
|
||||
<div className="input">
|
||||
<input
|
||||
type="text"
|
||||
onChange={ e => this.setState({ pusher_call: e.target.value })}
|
||||
value={ this.state.pusher_call } />
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<div className="name">Data</div>
|
||||
<div className="input">
|
||||
@ -146,13 +125,11 @@ class Debug extends React.Component{
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h4 className="underline">Response</h4>
|
||||
<pre>
|
||||
{ this.props.debug_response ? JSON.stringify(this.props.debug_response, null, 2) : null }
|
||||
</pre>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -168,7 +168,7 @@ class Settings extends React.Component{
|
||||
<input
|
||||
type="text"
|
||||
onChange={ e => 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 } />
|
||||
</div>
|
||||
</div>
|
||||
@ -178,7 +178,7 @@ class Settings extends React.Component{
|
||||
<input
|
||||
type="text"
|
||||
onChange={ e => 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 } />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user