notifications

This commit is contained in:
crptm 2017-06-22 03:31:59 +04:00
parent 59b0d446f2
commit d0becf7746
9 changed files with 180 additions and 11 deletions

View File

@ -0,0 +1,43 @@
// @flow
export type NOTIFICATION_LEVEL = 'danger' | 'warning' | 'success' | 'info';
export type Notification = {
level: NOTIFICATION_LEVEL,
msg: string,
duration?: number
};
export type ShowNotificationAction = {
type: 'SHOW_NOTIFICATION',
payload: Notification
};
type CloseNotificationAction = {
type: 'CLOSE_NOTIFICATION',
payload: Notification
};
export type NotificationsAction = ShowNotificationAction | CloseNotificationAction;
export function showNotification(
level: NOTIFICATION_LEVEL = 'info',
msg: string,
duration?: number
): ShowNotificationAction {
return {
type: 'SHOW_NOTIFICATION',
payload: {
level,
msg,
duration
}
};
}
export function closeNotification(notification: Notification): CloseNotificationAction {
return {
type: 'CLOSE_NOTIFICATION',
payload: notification
};
}

View File

@ -44,12 +44,16 @@
}
}
.alert.popup {
.alerts-container {
position: fixed;
border-radius: 0;
bottom: 0;
left: 0;
right: 0;
}
.alert.popup {
position: relative;
border-radius: 0;
padding: @space 0;
box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.33);
transition: @transition;
@ -153,4 +157,3 @@
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%220%200%20512%20512%22%20width%3D%2232%22%20height%3D%2232%22%3E%3Cpath%20d%3D%22M505.403%20406.394L295.389%2058.102c-8.274-13.721-23.367-22.245-39.39-22.245s-31.116%208.524-39.391%2022.246L6.595%20406.394c-8.551%2014.182-8.804%2031.95-.661%2046.37%208.145%2014.42%2023.491%2023.378%2040.051%2023.378h420.028c16.56%200%2031.907-8.958%2040.052-23.379%208.143-14.421%207.89-32.189-.662-46.369zm-28.364%2029.978a12.684%2012.684%200%200%201-11.026%206.436H45.985a12.68%2012.68%200%200%201-11.025-6.435%2012.683%2012.683%200%200%201%20.181-12.765L245.156%2075.316A12.732%2012.732%200%200%201%20256%2069.192c4.41%200%208.565%202.347%2010.843%206.124l210.013%20348.292a12.677%2012.677%200%200%201%20.183%2012.764z%22%20fill%3D%22%23FFF%22/%3E%3Cpath%20d%3D%22M256.154%20173.005c-12.68%200-22.576%206.804-22.576%2018.866%200%2036.802%204.329%2089.686%204.329%20126.489.001%209.587%208.352%2013.607%2018.248%2013.607%207.422%200%2017.937-4.02%2017.937-13.607%200-36.802%204.329-89.686%204.329-126.489%200-12.061-10.205-18.866-22.267-18.866zM256.465%20353.306c-13.607%200-23.814%2010.824-23.814%2023.814%200%2012.68%2010.206%2023.814%2023.814%2023.814%2012.68%200%2023.505-11.134%2023.505-23.814%200-12.99-10.826-23.814-23.505-23.814z%22%20fill%3D%22%23FFF%22/%3E%3C/svg%3E);
}
}

View File

@ -0,0 +1,63 @@
// @flow
import React from 'react';
import { connect } from 'react-redux';
import { closeNotification } from 'actions/notifications';
import type { Notification } from 'actions/notifications';
function NotificationRow(props: {
notification: Notification,
onClose: (n: Notification) => void
}) {
const { msg, level } = props.notification;
let klass = '';
switch (level) {
case 'danger':
klass = 'alert-danger';
break;
case 'success':
klass = 'alert-success';
break;
case 'warning':
klass = 'alert-warning';
break;
}
return (
<div className={`alert popup ${klass} animated-show-hide`} role="alert" aria-live="assertive">
<span className="sr-only">{level}</span>
<div className="container" dangerouslySetInnerHTML={{ __html: msg }} />
<i
tabIndex="0"
aria-label="dismiss"
className="icon-close"
onClick={() => props.onClose(props.notification)}
/>
</div>
);
}
export class Notifications extends React.Component {
props: {
notifications: Notification[],
closeNotification: (n: Notification) => void
};
render() {
if (!this.props.notifications.length) {
return null;
}
return (
<div className="alerts-container">
{this.props.notifications.map((n, i) =>
<NotificationRow key={`${n.level}-${i}`} notification={n} onClose={this.props.closeNotification} />
)}
</div>
);
}
}
const mapStateToProps = state => ({
notifications: state.notifications
});
export default connect(mapStateToProps, { closeNotification })(Notifications);

View File

@ -3,8 +3,10 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Footer, Header } from 'components';
import PropTypes from 'prop-types';
import Notifications from './Notifications';
import { CHANGE_LANGUAGE, CHANGE_NODE } from 'actions/config';
import {showNotification} from 'actions/notifications'
class App extends Component {
constructor(props) {
@ -24,9 +26,19 @@ class App extends Component {
changeLanguage: PropTypes.func,
changeNode: PropTypes.func,
nodeSelection: PropTypes.object
nodeSelection: PropTypes.object,
showNotification: PropTypes.func
};
componentDidMount() {
// 0 is forever
this.props.showNotification('info', 'I am in <b>App/index</b>', 0)
this.props.showNotification('danger', 'Danger', 5000)
this.props.showNotification('warning', 'Warning', 6000)
this.props.showNotification('success', 'Success', 7000)
}
render() {
let {
children,
@ -54,7 +66,7 @@ class App extends Component {
</div>
<Footer />
</main>
<Notifications />
</div>
);
}
@ -77,6 +89,9 @@ function mapDispatchToProps(dispatch) {
},
changeLanguage: (i: any) => {
dispatch(CHANGE_LANGUAGE(i));
},
showNotification: (level, msg, duration) => {
dispatch(showNotification(level, msg, duration))
}
};
}

View File

@ -4,24 +4,26 @@ import {syncHistoryWithStore, routerMiddleware} from 'react-router-redux'
import {composeWithDevTools} from 'redux-devtools-extension'
import Perf from 'react-addons-perf'
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import RootReducer from './reducers'
import {Root} from 'components'
import {Routing, history} from './routing'
import {createLogger} from 'redux-logger'
import createSagaMiddleware from 'redux-saga'
import notificationsSaga from './sagas/notifications'
// application styles
import 'assets/styles/etherwallet-master.less'
const sagaMiddleware = createSagaMiddleware()
const configureStore = () => {
let thunkApplied = applyMiddleware(thunk);
let sagaApplied = applyMiddleware(sagaMiddleware);
let store;
let middleware;
if (process.env.NODE_ENV !== 'production') {
window.Perf = Perf;
thunkApplied = composeWithDevTools(thunkApplied);
sagaApplied = composeWithDevTools(sagaApplied);
const logger = createLogger({
collapsed: true
});
@ -30,8 +32,8 @@ const configureStore = () => {
middleware = applyMiddleware(routerMiddleware(history));
}
store = createStore(RootReducer, thunkApplied, middleware);
store = createStore(RootReducer, sagaApplied, middleware);
sagaMiddleware.run(notificationsSaga)
return store
};

View File

@ -1,6 +1,8 @@
// @flow
import * as generateWallet from './generateWallet'
import * as config from './config'
import * as swap from './swap'
import * as notifications from './notifications'
import { reducer as formReducer } from 'redux-form'
import {combineReducers} from 'redux';
@ -10,6 +12,7 @@ export default combineReducers({
...generateWallet,
...config,
...swap,
...notifications,
form: formReducer,
routing: routerReducer
})

View File

@ -0,0 +1,19 @@
// @flow
import type { NotificationsAction, Notification } from 'actions/notifications';
type State = Notification[];
const initialState: State = [];
export function notifications(state: State = initialState, action: NotificationsAction): State {
switch (action.type) {
case 'SHOW_NOTIFICATION':
return state.concat(action.payload);
case 'CLOSE_NOTIFICATION':
state = [...state]
state.splice(state.indexOf(action.payload), 1);
return state
default:
return state;
}
}

View File

@ -0,0 +1,21 @@
// @flow
import { takeEvery, put } from 'redux-saga/effects';
import {delay} from 'redux-saga'
import {closeNotification} from 'actions/notifications'
import type {ShowNotificationAction} from 'actions/notifications'
function* handleNotification(action: ShowNotificationAction) {
const {duration} = action.payload
// show forever
if (duration === 0) {
return
}
// FIXME
yield delay(duration || 5000)
yield put(closeNotification(action.payload))
}
export default function* notificationsSaga() {
yield takeEvery('SHOW_NOTIFICATION', handleNotification);
}

View File

@ -15,7 +15,7 @@
"redux": "^3.6.0",
"redux-form": "^6.6.3",
"redux-logger": "^3.0.1",
"redux-thunk": "^2.2.0",
"redux-saga": "^0.15.3",
"store2": "^2.5.0",
"whatwg-fetch": "^2.0.2"
},