Notifications in bottom right; Loader as notification
This commit is contained in:
@ -73,6 +73,9 @@ class DebugInfo extends React.Component{
|
||||
<div className="item">
|
||||
{this.props.ui.users ? Object.keys(this.props.ui.users).length : '0'} users
|
||||
</div>
|
||||
<div className="item">
|
||||
{this.props.ui.notifications ? Object.keys(this.props.ui.notifications).length : '0'} notifications
|
||||
</div>
|
||||
<div className="item">
|
||||
{touch_state}
|
||||
</div>
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
|
||||
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,55 +1,66 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
|
||||
import * as actions from '../services/ui/actions'
|
||||
|
||||
class Notifications extends React.Component{
|
||||
export default class Notifications extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
render(){
|
||||
if( !this.props.notifications ) return null
|
||||
renderNotifications(){
|
||||
if (!this.props.notifications || this.props.notifications.length <= 0) return null
|
||||
|
||||
// we only care about the last notification
|
||||
var notification = this.props.notifications[this.props.notifications.length-1]
|
||||
|
||||
if (notification.is_shortcut){
|
||||
return (
|
||||
<div className="shortcut-notification">
|
||||
<FontAwesome name={notification.type} />
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className={notification.type+" notification"}>
|
||||
<FontAwesome name="close" className="close-button" onClick={ e => this.props.uiActions.removeNotification(notification.id) } />
|
||||
{ notification.content }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
renderLoader(){
|
||||
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="notification loading">
|
||||
Loading
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<div className="notifications">
|
||||
{
|
||||
this.props.notifications.map( notification => {
|
||||
if (notification.is_shortcut){
|
||||
return (
|
||||
<div className="shortcut-notification" key={notification.id}>
|
||||
<FontAwesome name={notification.type} />
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className={notification.type+" notification"} key={notification.id}>
|
||||
<FontAwesome name="close" className="close-button" onClick={ e => this.props.actions.removeNotification(notification.id) } />
|
||||
{ notification.content }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
{this.renderNotifications()}
|
||||
{this.renderLoader()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {
|
||||
notifications: state.ui.notifications
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
actions: bindActionCreators(actions, dispatch)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Notifications)
|
||||
}
|
||||
@ -292,7 +292,7 @@ const MopidyMiddleware = (function(){
|
||||
// treat empty response as a failed lookup
|
||||
if( !response || response.length <= 0 ){
|
||||
store.dispatch( uiActions.createNotification('Failed to load URI(s)', 'bad') )
|
||||
|
||||
console.error(action)
|
||||
}else{
|
||||
// play it
|
||||
store.dispatch( mopidyActions.changeTrack( response[0].tlid ) );
|
||||
|
||||
@ -252,7 +252,8 @@ const UIMiddleware = (function(){
|
||||
break
|
||||
|
||||
case 'PLAYLIST_TRACKS_ADDED':
|
||||
store.dispatch(uiActions.createNotification('Added '+action.tracks_uris.length+' tracks to playlist'))
|
||||
store.dispatch(uiActions.createNotification('Added '+action.tracks_uris.length+' tracks to playlist'))
|
||||
|
||||
switch(helpers.uriSource(action.key)){
|
||||
case 'spotify':
|
||||
store.dispatch(spotifyActions.getPlaylist(action.key))
|
||||
|
||||
@ -156,9 +156,11 @@ class App extends React.Component{
|
||||
<ContextMenu />
|
||||
<Dragger />
|
||||
<Modal />
|
||||
<Notifications />
|
||||
<Notifications
|
||||
uiActions={this.props.uiActions}
|
||||
notifications={this.props.notifications}
|
||||
load_queue={this.props.load_queue} />
|
||||
{this.props.debug_info ? <DebugInfo /> : null}
|
||||
<Loader load_queue={this.props.load_queue} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -172,6 +174,7 @@ class App extends React.Component{
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {
|
||||
notifications: (state.ui.notifications ? state.ui.notifications : []),
|
||||
load_queue: (state.ui.load_queue ? state.ui.load_queue : {}),
|
||||
mopidy_connected: state.mopidy.connected,
|
||||
spotify_authorized: state.spotify.authorized,
|
||||
|
||||
@ -59,20 +59,20 @@ class Debug extends React.Component{
|
||||
<h4 className="underline">User interface</h4>
|
||||
<form>
|
||||
<div className="field checkbox">
|
||||
<div className="name">Emulation</div>
|
||||
<div className="name">Debug</div>
|
||||
<div className="input">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="emulate_touch"
|
||||
checked={ this.props.emulate_touch }
|
||||
onChange={ e => this.props.uiActions.set({ emulate_touch: !this.props.emulate_touch })} />
|
||||
<span className="label">Mouse events as touch</span>
|
||||
name="debug_info"
|
||||
checked={ this.props.debug_info }
|
||||
onChange={ e => this.props.uiActions.set({ debug_info: !this.props.debug_info })} />
|
||||
<span className="label">Show debug info panel</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="field checkbox">
|
||||
<div className="name">Debug data</div>
|
||||
<div className="name">Logging</div>
|
||||
<div className="input">
|
||||
<label>
|
||||
<input
|
||||
@ -98,14 +98,12 @@ class Debug extends React.Component{
|
||||
onChange={ e => this.props.uiActions.set({ log_pusher: !this.props.log_pusher })} />
|
||||
<span className="label">Log Pusher</span>
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="debug_info"
|
||||
checked={ this.props.debug_info }
|
||||
onChange={ e => this.props.uiActions.set({ debug_info: !this.props.debug_info })} />
|
||||
<span className="label">Show debug info</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<div className="name"></div>
|
||||
<div className="input">
|
||||
<button className="secondary" onClick={e => this.props.uiActions.createNotification('Test '+Math.floor(Date.now() / 1000))}>Create notification</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -22,7 +22,6 @@
|
||||
@import 'components/header';
|
||||
@import 'components/notifications';
|
||||
@import 'components/dropdown-field';
|
||||
@import 'components/loader';
|
||||
@import 'components/debug';
|
||||
|
||||
@import 'views/artist';
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
|
||||
.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;
|
||||
}
|
||||
@ -2,32 +2,21 @@
|
||||
|
||||
.notifications {
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
bottom: 15px;
|
||||
right: 15px;
|
||||
z-index: 99;
|
||||
pointer-events: none;
|
||||
max-width: 400px;
|
||||
|
||||
.notification {
|
||||
display: inline-block;
|
||||
display: block;
|
||||
position: relative;
|
||||
background: $dark_grey;
|
||||
padding: 12px 32px 12px 18px;
|
||||
margin: 0 0.5px;
|
||||
margin: 10px 0 0 0;
|
||||
color: #FFFFFF;
|
||||
pointer-events: all;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 50px;
|
||||
border-bottom-left-radius: 50px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: 50px;
|
||||
border-bottom-right-radius: 50px;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
@ -54,6 +43,25 @@
|
||||
&.bad {
|
||||
background: $red;
|
||||
}
|
||||
|
||||
&.loading {
|
||||
position: relative;
|
||||
padding-right: 18px;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: '';
|
||||
background: $blue;
|
||||
opacity: 1;
|
||||
animation: slideloader 1s infinite;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shortcut-notification {
|
||||
@ -63,9 +71,10 @@
|
||||
height: 400px;
|
||||
width: 400px;
|
||||
margin: -200px 0 0 -200px;
|
||||
background: rgba(10, 10, 10, 0.95);
|
||||
background: $dark_grey;
|
||||
color: $white;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
|
||||
.fa {
|
||||
font-size: 140px;
|
||||
|
||||
@ -59,6 +59,12 @@ $bp_shallow: 650px;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
@keyframes slideloader {
|
||||
0% { left: 0%; right: 100%; }
|
||||
50% { left: 0%; right: 0%; }
|
||||
100% { left: 100%; right: 0%; }
|
||||
}
|
||||
|
||||
@keyframes shrink_from_right {
|
||||
from { width: 100%; }
|
||||
to { width: 0%; }
|
||||
|
||||
Reference in New Issue
Block a user