Notifications in bottom right; Loader as notification

This commit is contained in:
James Barnsley
2017-04-03 20:58:47 +12:00
parent 84f368869e
commit 0252cf0e4f
11 changed files with 106 additions and 139 deletions

View File

@ -73,6 +73,9 @@ class DebugInfo extends React.Component{
<div className="item">
{this.props.ui.users ? Object.keys(this.props.ui.users).length : '0'}&nbsp;users
</div>
<div className="item">
{this.props.ui.notifications ? Object.keys(this.props.ui.notifications).length : '0'}&nbsp;notifications
</div>
<div className="item">
{touch_state}
</div>

View File

@ -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>
)
}
}
}

View File

@ -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)
}

View File

@ -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 ) );

View File

@ -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))

View File

@ -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,

View File

@ -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>

View File

@ -22,7 +22,6 @@
@import 'components/header';
@import 'components/notifications';
@import 'components/dropdown-field';
@import 'components/loader';
@import 'components/debug';
@import 'views/artist';

View File

@ -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;
}

View File

@ -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;

View File

@ -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%; }