Version manager for pusher; Local spotify tokens

This commit is contained in:
James Barnsley
2016-10-29 22:40:02 +13:00
parent f4ca6b7fde
commit 5df6580f61
13 changed files with 181 additions and 42 deletions

5
src/js/bootstrap.js vendored
View File

@ -35,7 +35,10 @@ var initialState = {
username: '',
connections: [],
connected: false,
port: 6681
port: 6681,
version: {
current: '0.0.0'
}
},
spotify: {
connected: false,

View File

@ -38,9 +38,11 @@ class PusherConnectionList extends React.Component{
if( connection.connectionid == this.props.pusher.connectionid ) isMe = true;
return (
<div className="connection cf" key={connection.connectionid}>
<div className="col w20">{ connection.username } { isMe ? <span>(you)</span> : null }</div>
<div className="col w20 one-liner">{ connection.ip }</div>
<div className="col w20 one-liner">{ connection.connectionid }</div>
<div className="col w30">{ connection.username } { isMe ? <span>(you)</span> : null }</div>
<div className="col w70">
{ connection.ip }
<span className="grey-text"> ({ connection.connectionid })</span>
</div>
</div>
);
})

View File

@ -17,10 +17,6 @@ class Sidebar extends React.Component{
super(props);
}
componentDidMount(){
console.log( this.props )
}
render(){
return (
<aside>
@ -62,15 +58,15 @@ class Sidebar extends React.Component{
<Icon name="playlist" className="white" />
Playlists
</Link>
<Link activeClassName="active" to="/library/artists">
<Link activeClassName="active" disabled={!this.props.spotify.authorized} to="/library/artists">
<Icon name="mic" className="white" />
Artists
</Link>
<Link activeClassName="active" to="/library/albums">
<Link activeClassName="active" disabled={!this.props.spotify.authorized} to="/library/albums">
<Icon name="cd" className="white" />
Albums
</Link>
<Link activeClassName="active" to="/library/tracks">
<Link activeClassName="active" disabled={!this.props.spotify.authorized} to="/library/tracks">
<Icon name="music" className="white" />
Tracks
</Link>

View File

@ -0,0 +1,59 @@
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 VersionManager extends React.Component{
constructor(props) {
super(props);
}
renderUpdateButton(){
if( this.props.pusher.upgrading ){
return (
<button disabled>
<FontAwesome name="circle-o-notch" spin />
&nbsp;
Upgrading
</button>
);
}
if( !this.props.pusher.version.is_root ){
return <button className="secondary" disabled>Cannot upgrade</button>
}
if( this.props.pusher.version.upgrade_available ){
return <button className="secondary" onClick={() => this.props.pusherActions.performUpdate()}>Update now</button>
}
return null;
}
render(){
return (
<div className="version-manager">
<span>{ this.props.pusher.version.current } installed</span>
{ this.renderUpdateButton() }
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
pusherActions: bindActionCreators(pusherActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(VersionManager)

View File

@ -30,6 +30,12 @@ export function disconnect(){
}
}
export function performUpgrade(){
return {
type: 'PUSHER_UPGRADING'
}
}
export function getConnectionList(){
return {
type: 'PUSHER_INSTRUCT',

View File

@ -66,6 +66,12 @@ const PusherMiddleware = (function(){
case 'PUSHER_CONNECTED':
makeRequest({ action: 'get_version' });
makeRequest({ action: 'get_spotify_token' });
return next(action);
break;
case 'PUSHER_UPGRADING':
makeRequest({ action: 'perform_upgrade' });
return next(action);
break;

View File

@ -36,7 +36,13 @@ export default function reducer(pusher = {}, action){
return Object.assign({}, pusher, { connections: action.data.connections });
case 'PUSHER_VERSION':
return Object.assign({}, pusher, { version: action.data });
return Object.assign({}, pusher, {
version: action.data.version,
upgrading: false
});
case 'PUSHER_UPGRADING':
return Object.assign({}, pusher, { upgrading: true });
default:
return pusher

View File

@ -101,26 +101,56 @@ function getToken( dispatch, getState ){
function refreshToken( dispatch, getState ){
return new Promise( (resolve, reject) => {
$.ajax({
method: 'GET',
url: '//jamesbarnsley.co.nz/spotmop.php?action=refresh&refresh_token='+getState().spotify.refresh_token,
dataType: "json",
timeout: 10000
})
.then(
response => {
response.token_expiry = new Date().getTime() + ( response.expires_in * 1000 );
dispatch({
type: 'SPOTIFY_TOKEN_REFRESHED',
data: response
});
resolve(response);
},
error => {
console.error('Could not refresh token', error)
reject(error)
}
);
if( getState().spotify.authorized ){
$.ajax({
method: 'GET',
url: '//jamesbarnsley.co.nz/spotmop.php?action=refresh&refresh_token='+getState().spotify.refresh_token,
dataType: "json",
timeout: 10000
})
.then(
response => {
response.token_expiry = new Date().getTime() + ( response.expires_in * 1000 );
response.source = 'spotify';
dispatch({
type: 'SPOTIFY_TOKEN_REFRESHED',
data: response
});
resolve(response);
},
error => {
console.error('Could not refresh token', error)
reject(error)
}
);
}else{
$.ajax({
method: 'GET',
url: '//'+getState().mopidy.host+':'+getState().mopidy.port+'/spotmop/http?action=refresh_spotify_token',
dataType: "json",
timeout: 10000
})
.then(
response => {
response.token_expiry = new Date().getTime() + ( response.expires_in * 1000 );
response.source = 'mopidy';
dispatch({
type: 'SPOTIFY_TOKEN_REFRESHED',
data: response
});
resolve(response);
},
error => {
console.error('Could not refresh token', error)
reject(error)
}
);
}
})
}

View File

@ -17,6 +17,16 @@ export default function reducer(spotify = {}, action){
token_expiry: action.data.token_expiry
});
case 'PUSHER_SPOTIFY_TOKEN':
if( spotify.authorized ) return spotify;
return Object.assign({}, spotify, {
authorizing: false,
authorized: false,
authorization: false,
access_token: action.data.access_token,
token_expiry: action.data.token_expiry
});
case 'SPOTIFY_AUTHORIZATION_REVOKED':
return Object.assign({}, spotify, {
authorizing: false,

View File

@ -7,6 +7,7 @@ import FontAwesome from 'react-fontawesome'
import SpotifyAuthenticationFrame from '../components/SpotifyAuthenticationFrame'
import ConfirmationButton from '../components/ConfirmationButton'
import PusherConnectionList from '../components/PusherConnectionList'
import VersionManager from '../components/VersionManager'
import Header from '../components/Header'
import * as pusherActions from '../services/pusher/actions'
@ -142,9 +143,13 @@ class Settings extends React.Component{
<SpotifyAuthenticationFrame />
<ConfirmationButton content="Reset all settings" confirmingContent="Are you sure?" onConfirm={() => this.resetAllSettings()} />
<h3 className="underline">Advanced</h3>
<label>
<div className="label">Version</div>
<div className="input">
<VersionManager />
</div>
</label>
<label>
<div className="label">Connections</div>
<div className="input">
@ -152,6 +157,8 @@ class Settings extends React.Component{
</div>
</label>
<ConfirmationButton content="Reset all settings" confirmingContent="Are you sure?" onConfirm={() => this.resetAllSettings()} />
</section>
</div>
);

View File

@ -18,16 +18,21 @@ class LibraryPlaylists extends React.Component{
// on render
componentDidMount(){
if( this.props.mopidy.connected ) this.loadPlaylists();
if( this.props.mopidy.connected ){
this.props.mopidyActions.getPlaylists();
}
if( this.props.spotify.authorized ){
this.props.spotifyActions.getLibraryPlaylists();
}
}
componentWillReceiveProps( nextProps ){
if( !this.props.mopidy.connected && nextProps.mopidy.connected ) this.loadPlaylists();
}
loadPlaylists(){
this.props.spotifyActions.getLibraryPlaylists();
this.props.mopidyActions.getPlaylists();
if( !this.props.mopidy.connected && nextProps.mopidy.connected ){
this.props.mopidyActions.getPlaylists();
}
if( !this.props.spotify.authorized && nextProps.spotify.authorized ){
this.props.spotifyActions.getLibraryPlaylists();
}
}
compiledPlaylistSources(){

View File

@ -51,7 +51,12 @@ aside{
background: rgba( 0, 0, 0, 0.5 );
}
&:hover {
&[disabled] {
opacity: 0.3;
cursor: not-allowed;
}
&:hover:not([disabled]) {
background: rgba( 255, 255, 255, 0.1 );
}
}

View File

@ -95,4 +95,8 @@ main {
.one-liner{
@include one_line_text;
}
.grey-text {
color: $mid_grey;
}