diff --git a/react/src/actions/actionCreators.js b/react/src/actions/actionCreators.js
index fe8d8b6..19841c2 100644
--- a/react/src/actions/actionCreators.js
+++ b/react/src/actions/actionCreators.js
@@ -20,7 +20,8 @@ try {
Config = _config;
}
-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 GET_ACTIVE_COINS = 'GET_ACTIVE_COINS';
export const LOGIN = 'LOGIN';
@@ -195,7 +196,7 @@ function toggleSendReceiveCoinFormsState() {
export function triggerToaster(display, message, title, _type) {
return {
- type: TOASTER_MESSAGE,
+ type: ADD_TOASTER_MESSAGE,
display,
message,
title,
@@ -203,6 +204,14 @@ export function triggerToaster(display, message, title, _type) {
}
}
+// triggers removing of the toast with the provided toastId
+export function dismissToaster(toastId) {
+ return {
+ type: REMOVE_TOASTER_MESSAGE,
+ toastId: toastId
+ }
+}
+
function toggleAddcoinModalState(display, isLogin) {
return {
type: DISPLAY_ADDCOIN_MODAL,
@@ -378,9 +387,9 @@ export function copyCoinAddress(address) {
}
}
-export function dismissToasterMessage() {
+export function dismissToasterMessage(toastId) {
return dispatch => {
- dispatch(triggerToaster(false))
+ dispatch(dismissToaster(toastId))
}
}
diff --git a/react/src/components/toaster/toaster-item.js b/react/src/components/toaster/toaster-item.js
new file mode 100644
index 0000000..94904d7
--- /dev/null
+++ b/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 (
+
+
+
{ this.state.title }
+
{ this.state.message }
+
+ );
+ }
+
+ render() {
+ return (this.state.message && this.state.display) ?
+ this.renderToast() : null;
+ }
+}
+
+export default ToasterItem;
diff --git a/react/src/components/toaster/toaster.js b/react/src/components/toaster/toaster.js
index 2e94586..d8fdf6d 100644
--- a/react/src/components/toaster/toaster.js
+++ b/react/src/components/toaster/toaster.js
@@ -1,70 +1,60 @@
-import React from 'react';
-import { dismissToasterMessage } from '../../actions/actionCreators';
-import Store from '../../store';
+import React from "react";
+import {dismissToasterMessage} from "../../actions/actionCreators";
+import Store from "../../store";
+import ToasterItem from "./toaster-item";
+/**
+ * Container component used for creating multiple toasts
+ */
class Toaster extends React.Component {
constructor(props) {
super(props);
this.state = {
- display: false,
- message: null,
- type: null,
- title: null,
+ toasts: []
};
- this.dismissToast = this.dismissToast.bind(this);
+ this.toastId = 0;
}
componentWillReceiveProps(props) {
if (props &&
- props.message &&
- props.display) {
+ props.toasts) {
this.setState({
- message: props.message,
- display: props.display,
- type: props.type,
- title: props.title,
+ toasts: props.toasts,
+ toastId: props.toasts.length
});
} else {
this.setState({
- display: false,
- message: null,
- type: null,
- title: null,
+ toasts: [],
+ toastId: 0
});
}
}
- dismissToast() {
- Store.dispatch(dismissToasterMessage());
+ dismissToast(toastId) {
+ Store.dispatch(dismissToasterMessage(toastId));
}
- // TODO: multiple toasts
- renderToast() {
- setTimeout(() => {
- Store.dispatch(dismissToasterMessage());
- }, 5000);
-
+ // render all current toasts
+ render() {
return (
-
-
-
-
-
{ this.state.title }
-
{ this.state.message }
-
-
+
+ {this.state.toasts
+ .map((toast) => {
+ // sets the toastId for all new toasts
+ if (!toast.toastId) {
+ toast.toastId = this.toastId++;
+ }
+
+ return (
+
+ );
+ })}
);
}
-
- render() {
- return (this.state.message && this.state.display) ?
- this.renderToast() : null;
- }
}
-export default Toaster;
+export default Toaster;
\ No newline at end of file
diff --git a/react/src/reducers/toaster.js b/react/src/reducers/toaster.js
index 7ea1ed8..e0ddc4e 100644
--- a/react/src/reducers/toaster.js
+++ b/react/src/reducers/toaster.js
@@ -1,19 +1,21 @@
-import { TOASTER_MESSAGE } from '../actions/actionCreators';
+import {ADD_TOASTER_MESSAGE, REMOVE_TOASTER_MESSAGE} from "../actions/actionCreators";
export function toaster(state = {
- display: false,
- message: null,
- title: null,
- type: null,
+ toasts: [],
}, action) {
+ if (state === null) state = {toasts: []};
switch (action.type) {
- case TOASTER_MESSAGE:
- return Object.assign({}, state, {
- display: action.display,
- message: action.message,
- title: action.title,
- type: action._type,
- });
+ case ADD_TOASTER_MESSAGE:
+ return {
+ ...state,
+ toasts: [...state.toasts, action]
+ };
+ case REMOVE_TOASTER_MESSAGE:
+ // filter out the toastId that should be removed
+ return {
+ ...state,
+ toasts: state.toasts.filter(t => t.toastId !== action.toastId)
+ };
default:
return state;
}