Pusher username management; Spotify vs mopidy middleware glitching

This commit is contained in:
James Barnsley
2016-10-28 09:14:29 +13:00
parent 7be39dc4d4
commit 696e385b7e
10 changed files with 199 additions and 32 deletions

7
src/js/bootstrap.js vendored
View File

@ -22,17 +22,22 @@ let reducers = combineReducers({
}); });
// set application defaults // set application defaults
// TODO: Look at using propTypes in the component for these falsy initial states
var initialState = { var initialState = {
mopidy: { mopidy: {
connected: false,
host: window.location.hostname, host: window.location.hostname,
port: 6680, port: 6680,
volume: 0, volume: 0,
progress: 0 progress: 0
}, },
pusher: { pusher: {
connections: [],
connected: false,
port: 6681 port: 6681
}, },
spotify: { spotify: {
connected: false,
country: 'NZ', country: 'NZ',
locale: 'en_NZ', locale: 'en_NZ',
me: false me: false
@ -65,7 +70,7 @@ if( localStorage.getItem('spotify') ){
let store = createStore( let store = createStore(
reducers, reducers,
initialState, initialState,
applyMiddleware( thunk, localstorageMiddleware, pusherMiddleware, mopidyMiddleware, spotifyMiddleware ) applyMiddleware( thunk, localstorageMiddleware, mopidyMiddleware, pusherMiddleware, spotifyMiddleware )
); );
export default store; export default store;

View File

@ -0,0 +1,61 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import { createStore, bindActionCreators } from 'redux'
import FontAwesome from 'react-fontawesome'
import * as pusherActions from '../services/pusher/actions'
class PusherConnectionList extends React.Component{
constructor(props) {
super(props);
}
componentDidMount(){
if( this.props.pusher.connected ){
this.props.pusherActions.getConnectionList();
}
}
componentWillReceiveProps(newProps){
if( !this.props.pusher.connected && newProps.pusher.connected ){
this.props.pusherActions.getConnectionList();
}
}
render(){
if( !this.props.pusher.connected ) return null;
if( typeof(this.props.pusher.connections) == 'undefined' || this.props.pusher.connections.length <= 0 ) return null;
return (
<div className="pusher-connection-list">
{
this.props.pusher.connections.map( (connection, index) => {
return (
<div className="connection cf" key={connection.connectionid}>
<div className="col w20 one-liner">{ connection.username }</div>
<div className="col w20 one-liner">{ connection.ip }</div>
<div className="col w20 one-liner">{ connection.connectionid }</div>
</div>
);
})
}
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
pusherActions: bindActionCreators(pusherActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PusherConnectionList)

View File

@ -6,7 +6,7 @@ const localstorageMiddleware = (function(){
**/ **/
return store => next => action => { return store => next => action => {
console.log(action) console.log(action, store.getState())
// proceed as normal first // proceed as normal first
// this way, any reducers and middleware do their thing BEFORE we store our new state // this way, any reducers and middleware do their thing BEFORE we store our new state
@ -14,6 +14,25 @@ const localstorageMiddleware = (function(){
switch( action.type ){ switch( action.type ){
case 'PUSHER_SET_CONFIG':
var pusher = {
username: action.config.username,
port: action.config.port
};
localStorage.setItem('pusher', JSON.stringify(pusher));
break;
case 'PUSHER_CONNECTED':
var pusher = JSON.parse( localStorage.getItem('pusher') );
if( !pusher ) pusher = {};
Object.assign(
pusher,{
connectionid: action.connection.connectionid
}
);
localStorage.setItem('pusher', JSON.stringify(pusher));
break;
case 'MOPIDY_SET_CONFIG': case 'MOPIDY_SET_CONFIG':
var mopidy = { var mopidy = {
host: action.config.host, host: action.config.host,

View File

@ -10,6 +10,14 @@ export function setConfig( config ){
} }
} }
export function setUsername( username ){
return {
type: 'PUSHER_INSTRUCT',
action: 'set_username',
data: { username: username }
}
}
export function connect(){ export function connect(){
return { return {
type: 'PUSHER_CONNECT' type: 'PUSHER_CONNECT'
@ -22,10 +30,17 @@ export function disconnect(){
} }
} }
export function instruct( call, value ){ export function getConnectionList(){
return { return {
type: 'PUSHER_INSTRUCT', type: 'PUSHER_INSTRUCT',
call: call, action: 'get_connections'
value: value }
}
export function instruct( action, data = null ){
return {
type: 'PUSHER_INSTRUCT',
action: action,
data: data
} }
} }

View File

@ -40,16 +40,23 @@ const PusherMiddleware = (function(){
store.dispatch({ type: 'PUSHER_CONNECTING' }); store.dispatch({ type: 'PUSHER_CONNECTING' });
var state = store.getState(); var state = store.getState();
var connection = {
clientid: Math.random().toString(36).substr(2, 9),
connectionid: helpers.generateGuid(),
username: Math.random().toString(36).substr(2, 9)
}
//if( state.pusher.username ) connection.username = state.pusher.username;
socket = new WebSocket( socket = new WebSocket(
'ws://'+state.mopidy.host+':'+state.pusher.port+'/pusher' 'ws://'+state.mopidy.host+':'+state.pusher.port+'/pusher',
[ connection.clientid, connection.connectionid, connection.username ]
); );
socket.onopen = function(){ socket.onopen = () => {
store.dispatch({ type: 'PUSHER_CONNECTED' }); store.dispatch({ type: 'PUSHER_CONNECTED', connection: connection });
}; };
socket.onmessage = function(message){ socket.onmessage = (message) => {
var message = JSON.parse(message.data); var message = JSON.parse(message.data);
handleMessage( socket, store, message ) handleMessage( socket, store, message )
}; };
@ -58,6 +65,11 @@ const PusherMiddleware = (function(){
case 'PUSHER_CONNECTED': case 'PUSHER_CONNECTED':
makeRequest({ action: 'get_version' }); makeRequest({ action: 'get_version' });
return next(action);
break;
case 'PUSHER_INSTRUCT':
makeRequest({ action: action.action, data: action.data });
break; break;
// This action is irrelevant to us, pass it on to the next middleware // This action is irrelevant to us, pass it on to the next middleware

View File

@ -3,19 +3,22 @@ export default function reducer(pusher = {}, action){
switch (action.type) { switch (action.type) {
case 'PUSHER_CONNECTED': case 'PUSHER_CONNECTED':
return Object.assign({}, pusher, { connected: true, connecting: false }); return Object.assign({}, pusher, { connected: true, connecting: false, connectionid: action.connection.connectionid });
case 'PUSHER_DISCONNECTED': case 'PUSHER_DISCONNECTED':
return Object.assign({}, pusher, { connected: false, connecting: false }); return Object.assign({}, pusher, { connected: false, connecting: false });
case 'PUSHER_SET_PORT': case 'PUSHER_SET_CONFIG':
return Object.assign({}, pusher, { port: action.port }); return Object.assign({}, pusher, {
username: action.username,
port: action.port
});
case 'PUSHER_CLIENT_CONNECTED': case 'PUSHER_CLIENT_CONNECTED':
return Object.assign({}, pusher, { connections: action.data }); return Object.assign({}, pusher, { connection: action.data });
case 'PUSHER_CONNECTIONS_LOADED': case 'PUSHER_CONNECTIONS':
return Object.assign({}, pusher, { connections: action.data }); return Object.assign({}, pusher, { connections: action.data.connections });
case 'PUSHER_VERSION': case 'PUSHER_VERSION':
return Object.assign({}, pusher, { version: action.data }); return Object.assign({}, pusher, { version: action.data });

View File

@ -12,7 +12,8 @@ const SpotifyMiddleware = (function(){
switch(action.type){ switch(action.type){
// when our mopidy server current track changes // when our mopidy server current track changes
case 'MOPIDY_CURRENTTLTRACK': case 'MOPIDY_CURRENTTLTRACK_XX':
// DISABLED AS IT CAUSES ISSUE
// proceed as usual so we don't inhibit default functionality // proceed as usual so we don't inhibit default functionality
next(action) next(action)

View File

@ -6,8 +6,10 @@ import { bindActionCreators } from 'redux'
import FontAwesome from 'react-fontawesome' import FontAwesome from 'react-fontawesome'
import SpotifyAuthenticationFrame from '../components/SpotifyAuthenticationFrame' import SpotifyAuthenticationFrame from '../components/SpotifyAuthenticationFrame'
import ConfirmationButton from '../components/ConfirmationButton' import ConfirmationButton from '../components/ConfirmationButton'
import PusherConnectionList from '../components/PusherConnectionList'
import Header from '../components/Header' import Header from '../components/Header'
import * as pusherActions from '../services/pusher/actions'
import * as mopidyActions from '../services/mopidy/actions' import * as mopidyActions from '../services/mopidy/actions'
import * as spotifyActions from '../services/spotify/actions' import * as spotifyActions from '../services/spotify/actions'
@ -15,16 +17,27 @@ class Settings extends React.Component{
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
mopidy_host: this.props.mopidy.host, mopidy_host: this.props.mopidy.host,
mopidy_port: this.props.mopidy.port, mopidy_port: this.props.mopidy.port,
pusher_username: '',
pusher_port: this.props.pusher.port, pusher_port: this.props.pusher.port,
spotify_country: this.props.spotify.country, spotify_country: this.props.spotify.country,
spotify_locale: this.props.spotify.locale spotify_locale: this.props.spotify.locale
}; };
} }
componentWillReceiveProps( newProps ){
if( this.props.pusher.connectionid != newProps.pusher.connectionid ){
function isCurrentConnection(connection){
return connection.connectionid == newProps.pusher.connectionid;
}
var currentConnection = newProps.pusher.connections.find(isCurrentConnection);
this.setState({ pusher_username: currentConnection.username })
}
}
resetAllSettings(){ resetAllSettings(){
localStorage.clear(); localStorage.clear();
window.location.reload(true); window.location.reload(true);
@ -35,12 +48,13 @@ class Settings extends React.Component{
window.location.reload(true); window.location.reload(true);
} }
setSpotifyConfig(){
this.props.spotifyActions.setConfig({ country: this.state.spotify_country, locale: this.state.spotify_locale });
}
setPusherConfig(){ setPusherConfig(){
this.props.pusherActions.setConfig({ port: this.state.pusher_port }); this.props.pusherActions.setConfig({ port: this.state.pusher_port });
this.props.pusherActions.setUsername( this.state.pusher_username );
}
setSpotifyConfig(){
this.props.spotifyActions.setConfig({ country: this.state.spotify_country, locale: this.state.spotify_locale });
} }
render(){ render(){
@ -56,18 +70,22 @@ class Settings extends React.Component{
<h3 className="underline">Mopidy</h3> <h3 className="underline">Mopidy</h3>
<form onSubmit={() => this.setMopidyConfig()}> <form onSubmit={() => this.setMopidyConfig()}>
<label> <label>
<span className="label">Host</span> <div className="label">Host</div>
<div className="input">
<input <input
type="text" type="text"
onChange={ e => this.setState({ mopidy_host: e.target.value })} onChange={ e => this.setState({ mopidy_host: e.target.value })}
value={ this.state.mopidy_host } /> value={ this.state.mopidy_host } />
</div>
</label> </label>
<label> <label>
<span className="label">Port</span> <div className="label">Port</div>
<div className="input">
<input <input
type="text" type="text"
onChange={ e => this.setState({ mopidy_port: e.target.value })} onChange={ e => this.setState({ mopidy_port: e.target.value })}
value={ this.state.mopidy_port } /> value={ this.state.mopidy_port } />
</div>
</label> </label>
<button type="submit" className="secondary">Apply</button> <button type="submit" className="secondary">Apply</button>
</form> </form>
@ -75,11 +93,22 @@ class Settings extends React.Component{
<h3 className="underline">Pusher</h3> <h3 className="underline">Pusher</h3>
<form onSubmit={() => this.setPusherConfig()}> <form onSubmit={() => this.setPusherConfig()}>
<label> <label>
<span className="label">Port</span> <div className="label">Username</div>
<div className="input">
<input
type="text"
onChange={ e => this.setState({ pusher_username: e.target.value })}
value={ this.state.pusher_username } />
</div>
</label>
<label>
<div className="label">Port</div>
<div className="input">
<input <input
type="text" type="text"
onChange={ e => this.setState({ pusher_port: e.target.value })} onChange={ e => this.setState({ pusher_port: e.target.value })}
value={ this.state.pusher_port } /> value={ this.state.pusher_port } />
</div>
</label> </label>
<button type="submit" className="secondary">Apply</button> <button type="submit" className="secondary">Apply</button>
</form> </form>
@ -87,18 +116,22 @@ class Settings extends React.Component{
<h3 className="underline">Spotify</h3> <h3 className="underline">Spotify</h3>
<form onSubmit={() => this.setSpotifyConfig()}> <form onSubmit={() => this.setSpotifyConfig()}>
<label> <label>
<span className="label">Country</span> <div className="label">Country</div>
<div className="input">
<input <input
type="text" type="text"
onChange={ e => this.setState({ spotify_country: e.target.value })} onChange={ e => this.setState({ spotify_country: e.target.value })}
value={ this.state.spotify_country } /> value={ this.state.spotify_country } />
</div>
</label> </label>
<label> <label>
<span className="label">Locale</span> <div className="label">Locale</div>
<input <div className="input">
type="text" <input
onChange={ e => this.setState({ spotify_locale: e.target.value })} type="text"
value={ this.state.spotify_locale } /> onChange={ e => this.setState({ spotify_locale: e.target.value })}
value={ this.state.spotify_locale } />
</div>
</label> </label>
<button type="submit" className="secondary">Apply</button> <button type="submit" className="secondary">Apply</button>
</form> </form>
@ -107,6 +140,14 @@ class Settings extends React.Component{
<ConfirmationButton content="Reset all settings" confirmingContent="Are you sure?" onConfirm={() => this.resetAllSettings()} /> <ConfirmationButton content="Reset all settings" confirmingContent="Are you sure?" onConfirm={() => this.resetAllSettings()} />
<h3 className="underline">Advanced</h3>
<label>
<div className="label">Connections</div>
<div className="input">
<PusherConnectionList />
</div>
</label>
</section> </section>
</div> </div>
); );
@ -126,6 +167,7 @@ const mapStateToProps = (state, ownProps) => {
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return { return {
pusherActions: bindActionCreators(pusherActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch), mopidyActions: bindActionCreators(mopidyActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch) spotifyActions: bindActionCreators(spotifyActions, dispatch)
} }

View File

@ -91,4 +91,8 @@ main {
.cf{ .cf{
@include clearfix; @include clearfix;
}
.one-liner{
@include one_line_text;
} }

View File

@ -87,8 +87,13 @@ label {
float: left; float: left;
} }
input { .input {
width: 60%; width: 85%;
float: left;
input {
width: 60%;
}
} }
} }