Global loader
This commit is contained in:
@ -13,6 +13,41 @@ class DebugInfo extends React.Component{
|
||||
super(props);
|
||||
}
|
||||
|
||||
renderLoadQueue(){
|
||||
if (!this.props.ui.load_queue){
|
||||
return null
|
||||
}
|
||||
|
||||
var load_queue = this.props.ui.load_queue
|
||||
var queue = []
|
||||
|
||||
return (
|
||||
<div className="item">
|
||||
{queue}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderLoadQueue(){
|
||||
if (!this.props.ui.load_queue){
|
||||
return null
|
||||
}
|
||||
|
||||
var load_queue = this.props.ui.load_queue
|
||||
var queue = []
|
||||
for (var key in load_queue){
|
||||
if (load_queue.hasOwnProperty(key)){
|
||||
queue.push(<div key={key}>{load_queue[key]}</div>)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="item">
|
||||
{queue}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render(){
|
||||
var touch_state = 'no touch'
|
||||
if (this.props.ui.emulate_touch){
|
||||
@ -41,6 +76,7 @@ class DebugInfo extends React.Component{
|
||||
<div className="item">
|
||||
{touch_state}
|
||||
</div>
|
||||
{this.renderLoadQueue()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
35
src/js/components/Loader.js
Executable file
35
src/js/components/Loader.js
Executable file
@ -0,0 +1,35 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
|
||||
export default class Loader extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
render(){
|
||||
if (!this.props.load_queue){
|
||||
return null
|
||||
}
|
||||
|
||||
var load_queue = this.props.load_queue
|
||||
var is_loading = false
|
||||
for (var key in load_queue){
|
||||
if (load_queue.hasOwnProperty(key)){
|
||||
is_loading = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (is_loading){
|
||||
return (
|
||||
<div className="loader active"></div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className="loader"></div>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
var helpers = require('../../helpers.js')
|
||||
var uiActions = require('../ui/actions')
|
||||
var helpers = require('../../helpers')
|
||||
|
||||
/**
|
||||
* Send an ajax request to the Spotify API
|
||||
@ -13,13 +14,20 @@ const sendRequest = ( dispatch, getState, params ) => {
|
||||
|
||||
var url = '//ws.audioscrobbler.com/2.0/?format=json&api_key=4320a3ef51c9b3d69de552ac083c55e3&'+params
|
||||
|
||||
var loader_key = helpers.generateGuid()
|
||||
dispatch(uiActions.startLoading(loader_key, 'lastfm_'+params))
|
||||
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
cache: true,
|
||||
url: url
|
||||
}).then(
|
||||
response => resolve(response),
|
||||
response => {
|
||||
dispatch(uiActions.stopLoading(loader_key))
|
||||
resolve(response)
|
||||
},
|
||||
(xhr, status, error) => {
|
||||
dispatch(uiActions.stopLoading(loader_key))
|
||||
console.error( params+' failed', xhr.responseText)
|
||||
reject(error)
|
||||
}
|
||||
|
||||
@ -115,15 +115,20 @@ const MopidyMiddleware = (function(){
|
||||
property = property.replace('get','');
|
||||
property = property.replace('set','');
|
||||
|
||||
var loader_key = helpers.generateGuid()
|
||||
store.dispatch(uiActions.startLoading(loader_key, 'mopidy_'+property))
|
||||
|
||||
mopidyObject( value )
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({ type: 'MOPIDY_'+property.toUpperCase(), call: call, data: response });
|
||||
resolve(response);
|
||||
store.dispatch(uiActions.stopLoading(loader_key))
|
||||
store.dispatch({ type: 'MOPIDY_'+property.toUpperCase(), call: call, data: response })
|
||||
resolve(response)
|
||||
},
|
||||
error => {
|
||||
store.dispatch(uiActions.stopLoading(loader_key))
|
||||
console.error(error)
|
||||
reject(error);
|
||||
reject(error)
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@ -6,19 +6,11 @@ var spotifyActions = require('../spotify/actions.js')
|
||||
|
||||
const PusherMiddleware = (function(){
|
||||
|
||||
// container for the actual Mopidy socket
|
||||
var socket = null;
|
||||
var deferredRequests = [];
|
||||
// container for the actual websocket
|
||||
var socket = null
|
||||
|
||||
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 );
|
||||
}
|
||||
// requests pending
|
||||
var deferredRequests = []
|
||||
|
||||
// handle all manner of socket messages
|
||||
const handleMessage = (ws, store, message) => {
|
||||
@ -29,6 +21,7 @@ const PusherMiddleware = (function(){
|
||||
// response to a request [we] made
|
||||
if (typeof(message.request_id) !== 'undefined' && message.request_id){
|
||||
if (typeof( deferredRequests[ message.request_id ]) !== 'undefined' ){
|
||||
store.dispatch(uiActions.stopLoading(message.request_id))
|
||||
deferredRequests[ message.request_id ].resolve( message )
|
||||
} else {
|
||||
console.error('Pusher: Response with no matching request', message);
|
||||
@ -42,7 +35,7 @@ const PusherMiddleware = (function(){
|
||||
}
|
||||
}
|
||||
|
||||
const request = (method, data = {}) => {
|
||||
const request = (store, method, data = {}) => {
|
||||
return new Promise( (resolve, reject) => {
|
||||
var request_id = helpers.generateGuid()
|
||||
var message = {
|
||||
@ -51,6 +44,8 @@ const PusherMiddleware = (function(){
|
||||
request_id: request_id
|
||||
}
|
||||
socket.send( JSON.stringify(message) )
|
||||
|
||||
store.dispatch(uiActions.startLoading(request_id, 'pusher_'+method))
|
||||
|
||||
// add query to our deferred responses
|
||||
deferredRequests[request_id] = {
|
||||
@ -101,7 +96,7 @@ const PusherMiddleware = (function(){
|
||||
break;
|
||||
|
||||
case 'PUSHER_CONNECTED':
|
||||
request('get_config')
|
||||
request(store, 'get_config')
|
||||
.then(
|
||||
response => {
|
||||
if (response.error){
|
||||
@ -120,7 +115,7 @@ const PusherMiddleware = (function(){
|
||||
}
|
||||
}
|
||||
)
|
||||
request('get_version')
|
||||
request(store, 'get_version')
|
||||
.then(
|
||||
response => {
|
||||
if (response.error){
|
||||
@ -131,7 +126,7 @@ const PusherMiddleware = (function(){
|
||||
store.dispatch(response)
|
||||
}
|
||||
)
|
||||
request('get_radio')
|
||||
request(store, 'get_radio')
|
||||
.then(
|
||||
response => {
|
||||
if (response.error){
|
||||
@ -159,7 +154,7 @@ const PusherMiddleware = (function(){
|
||||
break
|
||||
|
||||
case 'PUSHER_DELIVER_MESSAGE':
|
||||
request('deliver_message', action.data)
|
||||
request(store, 'deliver_message', action.data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch( uiActions.createNotification('Message delivered') )
|
||||
@ -168,11 +163,11 @@ const PusherMiddleware = (function(){
|
||||
break
|
||||
|
||||
case 'PUSHER_DELIVER_BROADCAST':
|
||||
request('broadcast', action.data)
|
||||
request(store, 'broadcast', action.data)
|
||||
break
|
||||
|
||||
case 'PUSHER_GET_QUEUE_METADATA':
|
||||
request('get_queue_metadata')
|
||||
request(store, 'get_queue_metadata')
|
||||
.then(
|
||||
response => {
|
||||
response.type = 'PUSHER_QUEUE_METADATA'
|
||||
@ -182,7 +177,7 @@ const PusherMiddleware = (function(){
|
||||
break;
|
||||
|
||||
case 'PUSHER_ADD_QUEUE_METADATA':
|
||||
request('add_queue_metadata', {
|
||||
request(store, 'add_queue_metadata', {
|
||||
tlids: action.tlids,
|
||||
added_from: action.from_uri,
|
||||
added_by: store.getState().pusher.username
|
||||
@ -190,7 +185,7 @@ const PusherMiddleware = (function(){
|
||||
break;
|
||||
|
||||
case 'PUSHER_START_UPGRADE':
|
||||
request('upgrade')
|
||||
request(store, 'upgrade')
|
||||
.then(
|
||||
response => {
|
||||
if (response.error){
|
||||
@ -212,7 +207,7 @@ const PusherMiddleware = (function(){
|
||||
break;
|
||||
|
||||
case 'PUSHER_SET_USERNAME':
|
||||
request('set_username', {
|
||||
request(store, 'set_username', {
|
||||
username: action.username
|
||||
})
|
||||
.then(
|
||||
@ -229,7 +224,7 @@ const PusherMiddleware = (function(){
|
||||
break;
|
||||
|
||||
case 'PUSHER_GET_CONNECTIONS':
|
||||
request('get_connections')
|
||||
request(store, 'get_connections')
|
||||
.then(
|
||||
response => {
|
||||
if (response.error){
|
||||
@ -281,7 +276,7 @@ const PusherMiddleware = (function(){
|
||||
}
|
||||
|
||||
// we don't need to wait for response, as change will be broadcast
|
||||
request( 'start_radio', data )
|
||||
request(store, 'start_radio', data)
|
||||
break
|
||||
|
||||
case 'PUSHER_RADIO_STARTED':
|
||||
@ -304,7 +299,7 @@ const PusherMiddleware = (function(){
|
||||
}
|
||||
|
||||
// we don't need to wait for response, as change will be broadcast
|
||||
request( 'stop_radio', data )
|
||||
request(store, 'stop_radio', data)
|
||||
break
|
||||
|
||||
case 'PUSHER_RADIO_STOPPED':
|
||||
@ -329,7 +324,7 @@ const PusherMiddleware = (function(){
|
||||
break
|
||||
|
||||
case 'PUSHER_DEBUG':
|
||||
request( action.message.method, action.message.data )
|
||||
request(store, action.message.method, action.message.data )
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({type: 'DEBUG', response: response})
|
||||
|
||||
@ -13,6 +13,7 @@ var helpers = require('../../helpers.js')
|
||||
* @return Promise
|
||||
**/
|
||||
const sendRequest = ( dispatch, getState, endpoint, method = 'GET', data = false) => {
|
||||
|
||||
return new Promise( (resolve, reject) => {
|
||||
getToken( dispatch, getState )
|
||||
.then( response => {
|
||||
@ -34,11 +35,18 @@ const sendRequest = ( dispatch, getState, endpoint, method = 'GET', data = false
|
||||
// only if we've got data do we add it to the request (this prevents appending of "&false" to the URL)
|
||||
if (data) config.data = JSON.stringify(data)
|
||||
|
||||
// add reference to loader queue
|
||||
var loader_key = helpers.generateGuid()
|
||||
dispatch(uiActions.startLoading(loader_key, 'spotify_'+endpoint))
|
||||
|
||||
$.ajax(config).then(
|
||||
response => {
|
||||
dispatch(uiActions.stopLoading(loader_key))
|
||||
resolve(response)
|
||||
},
|
||||
(xhr, status, error) => {
|
||||
dispatch(uiActions.stopLoading(loader_key))
|
||||
|
||||
var message = xhr.responseText
|
||||
var response = JSON.parse(xhr.responseText)
|
||||
if (response.error && response.error.message) message = response.error.message
|
||||
|
||||
@ -244,4 +244,21 @@ export function removeNotification( id ){
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loaders
|
||||
**/
|
||||
|
||||
export function startLoading(key,source){
|
||||
return {
|
||||
type: 'START_LOADING',
|
||||
source: source,
|
||||
key: key
|
||||
}
|
||||
}
|
||||
|
||||
export function stopLoading(key){
|
||||
return {
|
||||
type: 'STOP_LOADING',
|
||||
key: key
|
||||
}
|
||||
}
|
||||
|
||||
@ -725,6 +725,24 @@ export default function reducer(ui = {}, action){
|
||||
return Object.assign({}, ui, { notifications: notifications })
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Loader
|
||||
**/
|
||||
|
||||
case 'START_LOADING':
|
||||
var load_queue = Object.assign({}, (ui.load_queue ? ui.load_queue : {}))
|
||||
load_queue[action.key] = action.source
|
||||
return Object.assign({}, ui, {load_queue: load_queue})
|
||||
|
||||
case 'STOP_LOADING':
|
||||
var load_queue = Object.assign({}, (ui.load_queue ? ui.load_queue : {}))
|
||||
if (load_queue[action.key]){
|
||||
delete load_queue[action.key]
|
||||
}
|
||||
return Object.assign({}, ui, {load_queue: load_queue})
|
||||
|
||||
|
||||
default:
|
||||
return ui
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import ContextMenu from '../components/ContextMenu'
|
||||
import Dragger from '../components/Dragger'
|
||||
import Modal from '../components/Modal/Modal'
|
||||
import Notifications from '../components/Notifications'
|
||||
import Loader from '../components/Loader'
|
||||
import DebugInfo from '../components/DebugInfo'
|
||||
|
||||
import * as helpers from '../helpers'
|
||||
@ -157,6 +158,7 @@ class App extends React.Component{
|
||||
<Modal />
|
||||
<Notifications />
|
||||
{this.props.debug_info ? <DebugInfo /> : null}
|
||||
<Loader load_queue={this.props.load_queue} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -170,6 +172,7 @@ class App extends React.Component{
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {
|
||||
load_queue: (state.ui.load_queue ? state.ui.load_queue : {}),
|
||||
mopidy_connected: state.mopidy.connected,
|
||||
spotify_authorized: state.spotify.authorized,
|
||||
play_state: state.mopidy.play_state,
|
||||
|
||||
@ -8,4 +8,5 @@
|
||||
background: rgba(0,0,0,0.5);
|
||||
color: #FFFFFF;
|
||||
pointer-events: none;
|
||||
width: 200px;
|
||||
}
|
||||
@ -1,24 +1,28 @@
|
||||
|
||||
.loader {
|
||||
opacity: 0;
|
||||
position: fixed;
|
||||
bottom: 12px;
|
||||
right: 12px;
|
||||
margin: 0 auto;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: {
|
||||
radius: 100%;
|
||||
style: solid;
|
||||
color: $blue;
|
||||
width: 2px;
|
||||
}
|
||||
border-top-color: transparent;
|
||||
pointer-events: none;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
@include animate();
|
||||
@include spin();
|
||||
}
|
||||
}
|
||||
|
||||
.lazy-loader {
|
||||
padding: 20px 0 40px;
|
||||
|
||||
.loader {
|
||||
@include animate();
|
||||
opacity: 0;
|
||||
margin: 0 auto;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border: {
|
||||
radius: 100%;
|
||||
style: solid;
|
||||
color: $mid_grey;
|
||||
width: 2px;
|
||||
}
|
||||
border-top-color: transparent;
|
||||
}
|
||||
|
||||
&.loading .loader {
|
||||
@include spin();
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user