Upgrading all request/responses to JSON-RPC
This commit is contained in:
1
src/js/bootstrap.js
vendored
1
src/js/bootstrap.js
vendored
@ -34,6 +34,7 @@ let reducers = combineReducers({
|
||||
// TODO: Look at using propTypes in the component for these falsy initial states
|
||||
var initialState = {
|
||||
core: {
|
||||
outputs: [],
|
||||
queue: [],
|
||||
queue_metadata: {},
|
||||
current_track_uri: null,
|
||||
|
||||
66
src/js/components/Snapcast.js
Executable file
66
src/js/components/Snapcast.js
Executable file
@ -0,0 +1,66 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import { Link, hashHistory } from 'react-router'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
|
||||
import ArtistSentence from './ArtistSentence'
|
||||
import * as helpers from '../helpers'
|
||||
import * as coreActions from '../services/core/actions'
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
import * as pusherActions from '../services/pusher/actions'
|
||||
|
||||
class Snapcast extends React.Component{
|
||||
|
||||
constructor(props){
|
||||
super(props)
|
||||
}
|
||||
|
||||
render(){
|
||||
if (!this.props.snapcast){
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="snapcast">
|
||||
{
|
||||
this.props.snapcast.groups.map(group => {
|
||||
return (
|
||||
<div className="group" key={group.id}>
|
||||
{group.muted ? <FontAwesome name="volume-off" /> : <FontAwesome name="volume-up" />} {group.name ? group.name : 'Untitled'}
|
||||
<div className="clients">
|
||||
{
|
||||
group.clients.map(client => {
|
||||
return (
|
||||
<div key={client.id}>
|
||||
{client.config.volume.muted ? <FontAwesome name="volume-off" onClick={e => this.props.pusherActions.setSnapcastClientVolume(client.id, false, client.config.volume.percent)} /> : <FontAwesome name="volume-up" onClick={e => this.props.pusherActions.setSnapcastClientVolume(client.id, true, client.config.volume.percent)} />} {client.config.name} {client.config.volume.percent}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {
|
||||
snapcast: state.pusher.snapcast
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
coreActions: bindActionCreators(coreActions, dispatch),
|
||||
uiActions: bindActionCreators(uiActions, dispatch),
|
||||
pusherActions: bindActionCreators(pusherActions, dispatch)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Snapcast)
|
||||
@ -205,10 +205,16 @@ export let digestMopidyImages = function(mopidy, images){
|
||||
|
||||
|
||||
export let generateGuid = function(format = 'xxxxxxxxxxxx'){
|
||||
// numeric
|
||||
return new Date().valueOf() + Math.random();
|
||||
|
||||
// alpha-numeric
|
||||
/*
|
||||
return format.replace(/[xy]/g, function(c){
|
||||
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
export let getCurrentPusherConnection = function(connections, connectionid){
|
||||
|
||||
@ -48,6 +48,18 @@ export function instruct(data = null){
|
||||
}
|
||||
}
|
||||
|
||||
export function getConfig(){
|
||||
return {
|
||||
type: 'PUSHER_GET_CONFIG'
|
||||
}
|
||||
}
|
||||
|
||||
export function getVersion(){
|
||||
return {
|
||||
type: 'PUSHER_GET_VERSION'
|
||||
}
|
||||
}
|
||||
|
||||
export function deliverBroadcast(data = null){
|
||||
return {
|
||||
type: 'PUSHER_DELIVER_BROADCAST',
|
||||
@ -69,6 +81,12 @@ export function sendAuthorization(recipient_connectionid, authorization, me){
|
||||
}
|
||||
}
|
||||
|
||||
export function getRadio(){
|
||||
return {
|
||||
type: 'PUSHER_GET_RADIO'
|
||||
}
|
||||
}
|
||||
|
||||
export function startRadio(uris){
|
||||
return {
|
||||
type: 'PUSHER_START_RADIO',
|
||||
@ -108,4 +126,36 @@ export function addQueueMetadata(tlids = [], from_uri = null){
|
||||
tlids: tlids,
|
||||
from_uri: from_uri
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Snapcast actions
|
||||
* TODO: Figure out how to cleanly split this out to it's own service
|
||||
* but still share the pusher middleware connection
|
||||
**/
|
||||
|
||||
export function getSnapcast(){
|
||||
return {
|
||||
type: 'PUSHER_GET_SNAPCAST',
|
||||
data: {
|
||||
method: 'Server.GetStatus'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setSnapcastClientVolume(id, muted, percent){
|
||||
return {
|
||||
type: 'PUSHER_SET_SNAPCAST_CLIENT_VOLUME',
|
||||
data: {
|
||||
method: 'Client.SetVolume',
|
||||
params: {
|
||||
id: id,
|
||||
volume: {
|
||||
muted: muted,
|
||||
percent: percent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -58,36 +58,45 @@ const PusherMiddleware = (function(){
|
||||
message
|
||||
));
|
||||
} else {
|
||||
message.type = 'PUSHER_'+message.type.toUpperCase();
|
||||
message.type = 'PUSHER_'+message.method.toUpperCase();
|
||||
store.dispatch(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const request = (store, method, data = {}) => {
|
||||
const request = (store, method, params = {}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
var request_id = helpers.generateGuid()
|
||||
var id = helpers.generateGuid();
|
||||
var message = {
|
||||
jsonrpc: 2.0,
|
||||
id: id,
|
||||
method: method,
|
||||
data: data,
|
||||
request_id: request_id
|
||||
params: params,
|
||||
}
|
||||
socket.send(JSON.stringify(message));
|
||||
|
||||
store.dispatch(uiActions.startLoading(request_id, 'pusher_'+method));
|
||||
store.dispatch(uiActions.startLoading(id, 'pusher_'+method));
|
||||
|
||||
// Start our 15 second timeout
|
||||
var timeout = setTimeout(
|
||||
function(){
|
||||
store.dispatch(uiActions.stopLoading(request_id));
|
||||
reject({message: "Request timed out", method: method, data: data});
|
||||
store.dispatch(uiActions.stopLoading(id));
|
||||
reject({
|
||||
jsonrpc: 2.0,
|
||||
id: id,
|
||||
error: {
|
||||
id: id,
|
||||
code: 32300,
|
||||
message: "Request timed out"
|
||||
}
|
||||
});
|
||||
},
|
||||
30000
|
||||
);
|
||||
|
||||
// add query to our deferred responses
|
||||
deferredRequests[request_id] = {
|
||||
deferredRequests[id] = {
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
@ -141,71 +150,14 @@ const PusherMiddleware = (function(){
|
||||
break;
|
||||
|
||||
case 'PUSHER_CONNECTED':
|
||||
ReactGA.event({ category: 'Pusher', action: 'Connected', label: action.username})
|
||||
request(store, 'get_config')
|
||||
.then(
|
||||
response => {
|
||||
if (response.error){
|
||||
console.error(response.error)
|
||||
return false
|
||||
}
|
||||
|
||||
response.type = 'PUSHER_CONFIG'
|
||||
store.dispatch(response);
|
||||
|
||||
var core = store.getState().core
|
||||
if (!core.country || !core.locale){
|
||||
store.dispatch(coreActions.set({
|
||||
country: response.config.country,
|
||||
locale: response.config.locale
|
||||
}))
|
||||
}
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not load config',
|
||||
error
|
||||
));
|
||||
}
|
||||
);
|
||||
request(store, 'get_version')
|
||||
.then(
|
||||
response => {
|
||||
if (response.error){
|
||||
console.error(response.error)
|
||||
return false
|
||||
}
|
||||
response.type = 'PUSHER_VERSION'
|
||||
store.dispatch(response)
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not load version',
|
||||
error
|
||||
));
|
||||
}
|
||||
);
|
||||
request(store, 'get_radio')
|
||||
.then(
|
||||
response => {
|
||||
if (response.error){
|
||||
console.error(response.error)
|
||||
return false
|
||||
}
|
||||
|
||||
response.type = 'PUSHER_RADIO'
|
||||
store.dispatch(response)
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not load radio',
|
||||
error
|
||||
));
|
||||
}
|
||||
);
|
||||
|
||||
store.dispatch(pusherActions.getQueueMetadata())
|
||||
ReactGA.event({ category: 'Pusher', action: 'Connected', label: action.username});
|
||||
|
||||
store.dispatch(pusherActions.getConfig());
|
||||
store.dispatch(pusherActions.getVersion());
|
||||
store.dispatch(pusherActions.getRadio());
|
||||
store.dispatch(pusherActions.getSnapcast());
|
||||
store.dispatch(pusherActions.getQueueMetadata());
|
||||
|
||||
return next(action);
|
||||
break;
|
||||
|
||||
@ -319,16 +271,60 @@ const PusherMiddleware = (function(){
|
||||
return next(action);
|
||||
break;
|
||||
|
||||
case 'PUSHER_GET_VERSION':
|
||||
request(store, 'get_version')
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'PUSHER_VERSION',
|
||||
version: response.result
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not load version',
|
||||
error
|
||||
));
|
||||
}
|
||||
);
|
||||
break;
|
||||
|
||||
case 'PUSHER_GET_CONFIG':
|
||||
console.log(action)
|
||||
request(store, 'get_config')
|
||||
.then(
|
||||
response => {
|
||||
console.log(response)
|
||||
store.dispatch({
|
||||
type: 'PUSHER_CONFIG',
|
||||
config: response.result
|
||||
});
|
||||
|
||||
var core = store.getState().core;
|
||||
if (!core.country || !core.locale){
|
||||
store.dispatch(coreActions.set({
|
||||
country: response.result.country,
|
||||
locale: response.result.locale
|
||||
}))
|
||||
}
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not load config',
|
||||
error
|
||||
));
|
||||
}
|
||||
);
|
||||
break;
|
||||
|
||||
case 'PUSHER_GET_CONNECTIONS':
|
||||
request(store, 'get_connections')
|
||||
.then(
|
||||
response => {
|
||||
if (response.error){
|
||||
console.error(response.error)
|
||||
return false
|
||||
}
|
||||
response.type = 'PUSHER_CONNECTIONS'
|
||||
store.dispatch(response)
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'PUSHER_CONNECTIONS',
|
||||
connections: response.result
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
@ -344,6 +340,24 @@ const PusherMiddleware = (function(){
|
||||
store.dispatch(uiActions.openModal('receive_authorization', {authorization: action.authorization, user: action.me}))
|
||||
break
|
||||
|
||||
case 'PUSHER_GET_RADIO':
|
||||
request(store, 'get_radio')
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'PUSHER_RADIO',
|
||||
radio: response.result.radio
|
||||
});
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not load radio',
|
||||
error
|
||||
));
|
||||
}
|
||||
);
|
||||
break;
|
||||
|
||||
case 'PUSHER_START_RADIO':
|
||||
case 'PUSHER_UPDATE_RADIO':
|
||||
ReactGA.event({ category: 'Pusher', action: 'Start radio', label: action.uris.join() })
|
||||
@ -479,6 +493,47 @@ const PusherMiddleware = (function(){
|
||||
ReactGA.event({ category: 'Pusher', action: 'Error', label: action.message })
|
||||
break
|
||||
|
||||
|
||||
/**
|
||||
* Snapcast actions
|
||||
**/
|
||||
case 'PUSHER_GET_SNAPCAST':
|
||||
request(store, 'snapcast_instruct', action.data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({type: 'PUSHER_SNAPCAST', snapcast: response.result.server})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not get Snapcast server',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'PUSHER_SET_SNAPCAST_CLIENT_VOLUME':
|
||||
request(store, 'snapcast_instruct', action.data)
|
||||
.then(
|
||||
response => {
|
||||
console.log(response)
|
||||
store.dispatch({
|
||||
type: 'PUSHER_SNAPCAST_CLIENT_UPDATED',
|
||||
key: response.client.id,
|
||||
client: response.client
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'ERR',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
// This action is irrelevant to us, pass it on to the next middleware
|
||||
default:
|
||||
return next(action);
|
||||
|
||||
@ -51,6 +51,9 @@ export default function reducer(pusher = {}, action){
|
||||
case 'PUSHER_START_UPGRADE':
|
||||
return Object.assign({}, pusher, { upgrading: true });
|
||||
|
||||
case 'PUSHER_SNAPCAST':
|
||||
return Object.assign({}, pusher, { snapcast: action.snapcast });
|
||||
|
||||
default:
|
||||
return pusher
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import URISchemesList from '../components/URISchemesList'
|
||||
import VersionManager from '../components/VersionManager'
|
||||
import Header from '../components/Header'
|
||||
import Thumbnail from '../components/Thumbnail'
|
||||
import Snapcast from '../components/Snapcast'
|
||||
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
import * as pusherActions from '../services/pusher/actions'
|
||||
@ -57,6 +58,11 @@ class Debug extends React.Component{
|
||||
|
||||
<div className="content-wrapper">
|
||||
|
||||
<h4 className="underline">Output</h4>
|
||||
<div className="field">
|
||||
<Snapcast />
|
||||
</div>
|
||||
|
||||
<h4 className="underline">User interface</h4>
|
||||
<form>
|
||||
<div className="field checkbox">
|
||||
|
||||
Reference in New Issue
Block a user