Browse Source

Merge pull request #119 from SuperNETorg/multiple-toasts

Toaster component - enable multiple toasts
all-modes
pbca26 8 years ago
committed by GitHub
parent
commit
0fc448fddc
  1. 32
      react/src/actions/actionCreators.js
  2. 3
      react/src/actions/storeType.js
  3. 3
      react/src/actions/walletAuth.js
  4. 79
      react/src/components/toaster/toaster-item.js
  5. 70
      react/src/components/toaster/toaster.js
  6. 27
      react/src/reducers/toaster.js

32
react/src/actions/actionCreators.js

@ -51,16 +51,6 @@ try {
Config = _config; Config = _config;
} }
export function triggerToaster(display, message, title, _type) {
return {
type: storeType.TOASTER_MESSAGE,
display,
message,
title,
_type,
}
}
export function changeActiveAddress(address) { export function changeActiveAddress(address) {
return { return {
type: storeType.DASHBOARD_ACTIVE_ADDRESS, type: storeType.DASHBOARD_ACTIVE_ADDRESS,
@ -176,6 +166,24 @@ export function toggleSendReceiveCoinFormsState() {
} }
} }
export function triggerToaster(display, message, title, _type) {
return {
type: storeType.ADD_TOASTER_MESSAGE,
display,
message,
title,
_type,
}
}
// triggers removing of the toast with the provided toastId
export function dismissToaster(toastId) {
return {
type: storeType.REMOVE_TOASTER_MESSAGE,
toastId: toastId
}
}
export function toggleAddcoinModalState(display, isLogin) { export function toggleAddcoinModalState(display, isLogin) {
return { return {
type: storeType.DISPLAY_ADDCOIN_MODAL, type: storeType.DISPLAY_ADDCOIN_MODAL,
@ -250,9 +258,9 @@ export function toggleAddcoinModal(display, isLogin) {
} }
} }
export function dismissToasterMessage() { export function dismissToasterMessage(toastId) {
return dispatch => { return dispatch => {
dispatch(triggerToaster(false)) dispatch(dismissToaster(toastId))
} }
} }

3
react/src/actions/storeType.js

@ -1,4 +1,5 @@
export const TOASTER_MESSAGE = 'TOASTER_MESSAGE'; export const ADD_TOASTER_MESSAGE = 'ADD_TOASTER_MESSAGE';
export const REMOVE_TOASTER_MESSAGE = 'REMOVE_TOASTER_MESSAGE';
export const DISPLAY_ADDCOIN_MODAL = 'DISPLAY_ADDCOIN_MODAL'; export const DISPLAY_ADDCOIN_MODAL = 'DISPLAY_ADDCOIN_MODAL';
export const GET_ACTIVE_COINS = 'GET_ACTIVE_COINS'; export const GET_ACTIVE_COINS = 'GET_ACTIVE_COINS';
export const LOGIN = 'LOGIN'; export const LOGIN = 'LOGIN';

3
react/src/actions/walletAuth.js

@ -3,7 +3,8 @@ import { translate } from '../translate/translate';
import { import {
triggerToaster, triggerToaster,
Config, Config,
getMainAddressState getMainAddressState,
updateErrosStack
} from './actionCreators'; } from './actionCreators';
import { import {
logGuiHttp, logGuiHttp,

79
react/src/components/toaster/toaster-item.js

@ -0,0 +1,79 @@
import React from "react";
import {dismissToasterMessage} from "../../actions/actionCreators";
import Store from "../../store";
// each toast will be displayed for 5 seconds
const DISPLAY_LENGTH_MILLIS = 5000;
/**
* Displays one toast message
* each messages has a type, title and a content message
*/
class ToasterItem extends React.Component {
constructor(props) {
super(props);
this.state = {
display: false,
message: null,
type: null,
title: null
};
this.dismissToast = this.dismissToast.bind(this);
this.timeoutHandler = null;
}
componentWillReceiveProps(props) {
if (props &&
props.message &&
props.display) {
this.setState({
message: props.message,
display: props.display,
type: props._type,
title: props.title,
toastId: props.toastId
});
} else {
this.setState({
display: false,
message: null,
type: null,
title: null,
toastId: null
});
}
}
dismissToast(toastId) {
Store.dispatch(dismissToasterMessage(toastId));
}
renderToast() {
// ensure that setTimeout is called only once for each toast message
if (!this.timeoutHandler) {
this.timeoutHandler = setTimeout(() => {
this.dismissToast(this.state.toastId);
}, DISPLAY_LENGTH_MILLIS);
}
return (
<div className={ 'toast toast-' + this.state.type }>
<button className="toast-close-button" role="button"
onClick={ () => this.dismissToast(this.state.toastId) }>×
</button>
<div className="toast-title">{ this.state.title }</div>
<div className="toast-message">{ this.state.message }</div>
</div>
);
}
render() {
return (this.state.message && this.state.display) ?
this.renderToast() : null;
}
}
export default ToasterItem;

70
react/src/components/toaster/toaster.js

@ -1,69 +1,59 @@
import React from 'react'; import React from "react";
import { dismissToasterMessage } from '../../actions/actionCreators'; import {dismissToasterMessage} from "../../actions/actionCreators";
import Store from '../../store'; import Store from "../../store";
import ToasterItem from "./toaster-item";
/**
* Container component used for creating multiple toasts
*/
class Toaster extends React.Component { class Toaster extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
display: false, toasts: []
message: null,
type: null,
title: null,
}; };
this.dismissToast = this.dismissToast.bind(this); this.toastId = 0;
} }
componentWillReceiveProps(props) { componentWillReceiveProps(props) {
if (props && if (props &&
props.message && props.toasts) {
props.display) {
this.setState({ this.setState({
message: props.message, toasts: props.toasts,
display: props.display, toastId: props.toasts.length
type: props.type,
title: props.title,
}); });
} else { } else {
this.setState({ this.setState({
display: false, toasts: [],
message: null, toastId: 0
type: null,
title: null,
}); });
} }
} }
dismissToast() { dismissToast(toastId) {
Store.dispatch(dismissToasterMessage()); Store.dispatch(dismissToasterMessage(toastId));
} }
// TODO: multiple toasts // render all current toasts
renderToast() { render() {
setTimeout(() => {
Store.dispatch(dismissToasterMessage());
}, 5000);
return ( return (
<div className="toaster"> <div id="toast-container"
<div
id="toast-container"
className="single-toast toast-bottom-right" className="single-toast toast-bottom-right"
aria-live="polite" aria-live="polite"
role="alert"> role="alert">
<div className={ 'toast toast-' + this.state.type }> {this.state.toasts
<button className="toast-close-button" role="button" onClick={ this.dismissToast }>×</button> .map((toast) => {
<div className="toast-title">{ this.state.title }</div> // sets the toastId for all new toasts
<div className="toast-message">{ this.state.message }</div> if (!toast.toastId) {
</div> toast.toastId = this.toastId++;
</div>
</div>
);
} }
render() { return (
return (this.state.message && this.state.display) ? <ToasterItem key={toast.toastId} {...toast} />
this.renderToast() : null; );
})}
</div>
);
} }
} }

27
react/src/reducers/toaster.js

@ -1,21 +1,24 @@
import { import {
TOASTER_MESSAGE ADD_TOASTER_MESSAGE,
REMOVE_TOASTER_MESSAGE
} from '../actions/storeType'; } from '../actions/storeType';
export function toaster(state = { export function toaster(state = {
display: false, toasts: [],
message: null,
title: null,
type: null,
}, action) { }, action) {
if (state === null) state = {toasts: []};
switch (action.type) { switch (action.type) {
case TOASTER_MESSAGE: case ADD_TOASTER_MESSAGE:
return Object.assign({}, state, { return {
display: action.display, ...state,
message: action.message, toasts: [...state.toasts, action]
title: action.title, };
type: action._type, case REMOVE_TOASTER_MESSAGE:
}); // filter out the toastId that should be removed
return {
...state,
toasts: state.toasts.filter(t => t.toastId !== action.toastId)
};
default: default:
return state; return state;
} }

Loading…
Cancel
Save