Notifications in place
This commit is contained in:
3
src/js/bootstrap.js
vendored
3
src/js/bootstrap.js
vendored
@ -64,7 +64,8 @@ var initialState = {
|
||||
playlists: [],
|
||||
tracks: []
|
||||
},
|
||||
playlists: []
|
||||
playlists: [],
|
||||
notifications: []
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
47
src/js/components/Notifications.js
Executable file
47
src/js/components/Notifications.js
Executable file
@ -0,0 +1,47 @@
|
||||
|
||||
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{
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
render(){
|
||||
if( !this.props.notifications ) return null
|
||||
|
||||
return (
|
||||
<div className="notifications">
|
||||
{
|
||||
this.props.notifications.map( notification => {
|
||||
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>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {
|
||||
notifications: state.ui.notifications
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
actions: bindActionCreators(actions, dispatch)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Notifications)
|
||||
@ -12,7 +12,7 @@ const localstorageMiddleware = (function(){
|
||||
|
||||
// append our state to a global variable. This gives us access to debug the store at any point
|
||||
window._store = store
|
||||
console.log(action)
|
||||
//console.log(action)
|
||||
|
||||
switch( action.type ){
|
||||
|
||||
|
||||
@ -183,6 +183,13 @@ export function addTracksToPlaylist( playlist_uri, tracks_uris ){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modal
|
||||
*
|
||||
* Immersive full-screen dialog
|
||||
**/
|
||||
|
||||
export function openModal( name, data ){
|
||||
return {
|
||||
type: 'OPEN_MODAL',
|
||||
@ -197,3 +204,30 @@ export function closeModal(){
|
||||
return { type: 'CLOSE_MODAL' }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notifications
|
||||
*
|
||||
* Subtle info/tooltip messages
|
||||
**/
|
||||
|
||||
export function createNotification( content, type = 'default' ){
|
||||
return {
|
||||
type: 'CREATE_NOTIFICATION',
|
||||
notification: {
|
||||
id: helpers.generateGuid(),
|
||||
type: type,
|
||||
content: content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function removeNotification( id ){
|
||||
return {
|
||||
type: 'REMOVE_NOTIFICATION',
|
||||
id: id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -10,13 +10,26 @@ const UIMiddleware = (function(){
|
||||
* The actual middleware inteceptor
|
||||
**/
|
||||
return store => next => action => {
|
||||
var state = store.getState();
|
||||
|
||||
switch(action.type){
|
||||
|
||||
case 'CREATE_NOTIFICATION':
|
||||
|
||||
// start a timeout to remove this notification
|
||||
var timeout = setTimeout(
|
||||
function(){
|
||||
store.dispatch(actions.removeNotification(action.notification.id))
|
||||
},
|
||||
3000
|
||||
)
|
||||
|
||||
// we don't want to stop things happening as usual
|
||||
next(action)
|
||||
break
|
||||
|
||||
// This action is irrelevant to us, pass it on to the next middleware
|
||||
default:
|
||||
return next(action);
|
||||
return next(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -396,6 +396,26 @@ export default function reducer(ui = {}, action){
|
||||
return Object.assign({}, ui, { modal: false })
|
||||
|
||||
|
||||
/**
|
||||
* Notifications
|
||||
**/
|
||||
|
||||
case 'CREATE_NOTIFICATION':
|
||||
var notifications = [...ui.notifications, action.notification]
|
||||
return Object.assign({}, ui, { notifications: notifications })
|
||||
|
||||
case 'REMOVE_NOTIFICATION':
|
||||
var notifications = Object.assign([], ui.notifications)
|
||||
|
||||
function getByID( notification ){
|
||||
return notification.id === action.id
|
||||
}
|
||||
var index = notifications.findIndex(getByID)
|
||||
if( index > -1 ) notifications.splice(index, 1)
|
||||
|
||||
return Object.assign({}, ui, { notifications: notifications })
|
||||
|
||||
|
||||
default:
|
||||
return ui
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import SidebarToggleButton from '../components/SidebarToggleButton'
|
||||
import ContextMenu from '../components/ContextMenu'
|
||||
import Dragger from '../components/Dragger'
|
||||
import Modal from '../components/Modal/Modal'
|
||||
import Notifications from '../components/Notifications'
|
||||
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
import * as pusherActions from '../services/pusher/actions'
|
||||
@ -130,6 +131,7 @@ class App extends React.Component{
|
||||
<ContextMenu state={this.props.context_menu} />
|
||||
<Dragger />
|
||||
<Modal />
|
||||
<Notifications />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ class Debug extends React.Component{
|
||||
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) )
|
||||
this.props.uiActions.createNotification( 'Mopidy instruction sent' )
|
||||
}
|
||||
|
||||
callPusher(e){
|
||||
@ -56,6 +57,7 @@ class Debug extends React.Component{
|
||||
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) )
|
||||
this.props.uiActions.debugResponse({ status: 1, message: 'Sent', call: this.state.pusher_call, data: this.state.pusher_data })
|
||||
this.props.uiActions.createNotification( 'Pusher instruction sent' )
|
||||
}
|
||||
|
||||
render(){
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
@import 'components/lists';
|
||||
@import 'components/grid';
|
||||
@import 'components/header';
|
||||
@import 'components/notifications';
|
||||
@import 'components/dropdown-field';
|
||||
|
||||
@import 'views/artist';
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: #FFF39C !important;
|
||||
background: $yellow !important;
|
||||
}
|
||||
|
||||
&:not(.header):hover {
|
||||
|
||||
55
src/scss/components/_notifications.scss
Executable file
55
src/scss/components/_notifications.scss
Executable file
@ -0,0 +1,55 @@
|
||||
|
||||
|
||||
.notifications {
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
|
||||
.notification {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
background: $dark_grey;
|
||||
padding: 8px 28px 8px 14px;
|
||||
margin: 0 0.5px;
|
||||
color: #FFFFFF;
|
||||
|
||||
&: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: 7px;
|
||||
right: 8px;
|
||||
font-size: 10px;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.good {
|
||||
background: $green;
|
||||
}
|
||||
|
||||
&.info {
|
||||
background: $yellow;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
&.bad {
|
||||
background: $red;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ $secondary_grey: #888888;
|
||||
$red: #cf2d2d;
|
||||
$green: #47af2a;
|
||||
$blue: #32b5f2;
|
||||
$yellow: #FFF39C;
|
||||
|
||||
$bp_wide: 1000px;
|
||||
$bp_medium: 800px;
|
||||
|
||||
@ -86,7 +86,7 @@
|
||||
.intro {
|
||||
|
||||
padding: 50px 30px 0 30px;
|
||||
margin: 0 0 100px 0;
|
||||
margin: 0 0 140px 0;
|
||||
|
||||
.parallax {
|
||||
height: 100%;
|
||||
@ -117,10 +117,15 @@
|
||||
.details-wrapper {
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
bottom: -90px;
|
||||
left: 30px;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 30px;
|
||||
background: $faint_grey;
|
||||
z-index: 2;
|
||||
|
||||
.details {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user