Debugger; Settings polish up; Album link on track when invalid uri

This commit is contained in:
James Barnsley
2016-12-05 10:20:01 +13:00
parent 5f07e855bd
commit 33619c6b7e
14 changed files with 293 additions and 35 deletions

View File

@ -16,13 +16,21 @@ export default class ArtistList extends React.Component{
<div className="artist-list">
{
this.props.artists.map( (artist, index) => {
var link = '/artist/' + artist.uri;
return (
<Link to={ link } key={artist.uri} className="artist">
<Thumbnail circle={true} size="small" images={artist.images} />
<span className="name">{ artist.name }</span>
</Link>
);
if( artist.uri ){
return (
<Link to={ '/artist/' + artist.uri } key={artist.uri} className="artist">
<Thumbnail circle={true} size="small" images={artist.images} />
<span className="name">{ artist.name }</span>
</Link>
)
}else{
return (
<span key={artist.uri} className="artist">
<Thumbnail circle={true} size="small" images={artist.images} />
<span className="name">{ artist.name }</span>
</span>
)
}
})
}
</div>

View File

@ -41,6 +41,15 @@ export default class Track extends React.Component{
if( typeof(track.selected) !== 'undefined' && track.selected ) className += ' selected';
if( track.playing ) className += ' playing';
var album = '-'
if( track.album ){
if( track.album.uri ){
album = <Link to={'/album/'+track.album.uri}>{track.album.name}</Link>
} else {
album = <span>{track.album.name}</span>
}
}
return (
<div
className={className}
@ -56,7 +65,7 @@ export default class Track extends React.Component{
{ track.artists ? <ArtistSentence artists={track.artists} /> : '-' }
</span>
<span className="col album">
{ track.album ? <Link to={'/album/'+track.album.uri}>{track.album.name}</Link> : '-' }
{album}
</span>
<span className="col duration">
{ track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null }

View File

@ -19,6 +19,7 @@ import Playlist from './views/Playlist'
import User from './views/User'
import Queue from './views/Queue'
import Settings from './views/Settings'
import Debug from './views/Debug'
import Search from './views/Search'
import Discover from './views/discover/Discover'
@ -44,6 +45,7 @@ ReactDOM.render(
<IndexRoute component={Queue} />
<Route path="queue" component={Queue} />
<Route path="settings" component={Settings} />
<Route path="debug" component={Debug} />
<Route path="search/:query" component={Search} />
<Route path="search/:query/:type" component={Search} />

View File

@ -28,6 +28,14 @@ export function instruct( call, value ){
}
}
export function debug( call, value ){
return {
type: 'MOPIDY_DEBUG',
call: call,
value: value
}
}
/**
* Playback-oriented actions

View File

@ -161,6 +161,14 @@ const MopidyMiddleware = (function(){
instruct( socket, store, action.call, action.value )
break;
// send an instruction to the websocket
case 'MOPIDY_DEBUG':
instruct( socket, store, action.call, action.value )
.then( response => {
store.dispatch({ type: 'DEBUG', response: response })
})
break;
// send an instruction to the websocket
case 'MOPIDY_NEXT':
var icon = false

View File

@ -49,4 +49,12 @@ export function instruct( action, data = null ){
action: action,
data: data
}
}
export function debug( call, data = null ){
return {
type: 'PUSHER_DEBUG',
call: call,
data: data
}
}

View File

@ -87,7 +87,35 @@ const PusherMiddleware = (function(){
break;
case 'PUSHER_INSTRUCT':
makeRequest({ action: action.action, data: action.data });
switch( action.action ){
case 'query':
makeRequest( action.data )
break
case 'broadcast':
broadcast( action.data )
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
}
break;
case 'PUSHER_SEND_BROADCAST':

View File

@ -7,6 +7,9 @@ export default function reducer(ui = {}, action){
case 'LAZY_LOADING':
return Object.assign({}, ui, { lazy_loading: action.start });
case 'DEBUG':
return Object.assign({}, ui, { debug_response: action.response })
/**
* UI views and view modes

160
src/js/views/Debug.js Executable file
View File

@ -0,0 +1,160 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import { bindActionCreators } from 'redux'
import FontAwesome from 'react-fontawesome'
import SpotifyAuthenticationFrame from '../components/SpotifyAuthenticationFrame'
import ConfirmationButton from '../components/ConfirmationButton'
import PusherConnectionList from '../components/PusherConnectionList'
import URISchemesList from '../components/URISchemesList'
import VersionManager from '../components/VersionManager'
import Header from '../components/Header'
import Thumbnail from '../components/Thumbnail'
import * as uiActions from '../services/ui/actions'
import * as pusherActions from '../services/pusher/actions'
import * as mopidyActions from '../services/mopidy/actions'
import * as spotifyActions from '../services/spotify/actions'
class Debug extends React.Component{
constructor(props) {
super(props);
this.state = {
mopidy_call: 'playlists.asList',
mopidy_data: '{}',
pusher_call: 'broadcast',
pusher_data: '{}'
}
}
componentDidMount(){
if( this.props.pusher.connectionid ){
var data = {
action: "notification",
recipients: [this.props.pusher.connectionid],
data: {
title: "Title",
body: "Test notification",
icon: "http://lorempixel.com/100/100/nature/"
}
}
this.setState({ pusher_data: JSON.stringify(data) })
}
}
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) )
}
render(){
return (
<div className="view debugger-view">
<Header icon="cog" title="Debugger" />
<section>
<h4 className="underline">Mopidy</h4>
<form onSubmit={(e) => this.callMopidy(e)}>
<div className="field">
<div className="name">Call</div>
<div className="input">
<input
type="text"
onChange={ e => this.setState({ mopidy_call: e.target.value })}
value={ this.state.mopidy_call } />
</div>
</div>
<div className="field">
<div className="name">Data</div>
<div className="input">
<textarea
onChange={ e => this.setState({ mopidy_data: e.target.value })}
value={ this.state.mopidy_data }>
</textarea>
</div>
</div>
<div className="field">
<div className="name"></div>
<div className="input">
<button type="submit" className="secondary">Send</button>
</div>
</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">
<textarea
onChange={ e => this.setState({ pusher_data: e.target.value })}
value={ this.state.pusher_data }>
</textarea>
</div>
</div>
<div className="field">
<div className="name"></div>
<div className="input">
<button type="submit" className="secondary">Send</button>
</div>
</div>
</form>
</section>
<section>
<h4 className="underline">Response</h4>
<pre>
{ this.props.ui.debug_response ? JSON.stringify(this.props.ui.debug_response, null, 2) : null }
</pre>
</section>
</div>
);
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
uiActions: bindActionCreators(uiActions, dispatch),
pusherActions: bindActionCreators(pusherActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Debug)

View File

@ -1,7 +1,7 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import { browserHistory, Link } from 'react-router'
import { bindActionCreators } from 'redux'
import FontAwesome from 'react-fontawesome'
@ -247,7 +247,17 @@ class Settings extends React.Component{
</div>
</div>
<ConfirmationButton content="Reset all settings" confirmingContent="Are you sure?" onConfirm={() => this.resetAllSettings()} />
<div className="field">
<div className="name"></div>
<div className="input">
<ConfirmationButton className="destructive" content="Reset all settings" confirmingContent="Are you sure?" onConfirm={() => this.resetAllSettings()} />
&nbsp;&nbsp;
<button className="primary" onClick={ () => browserHistory.push('debug') }>
<FontAwesome name="flask" />&nbsp;Debugger
</button>
</div>
</div>
</section>
</div>

View File

@ -71,6 +71,11 @@ main {
padding: 10px 0;
}
}
pre {
padding: 20px;
background: $faint_grey;
}
}
h1 {

View File

@ -125,13 +125,15 @@ input[type="submit"] {
width: 85%;
float: left;
input {
input,
select,
textarea {
width: 100%;
}
.text {
display: block;
padding: 10px 14px;
padding: 10px 14px 10px 0;
}
}

View File

@ -47,4 +47,11 @@
padding: 20px;
}
}
}
.debugger-view {
section {
padding: 40px;
}
}