diff --git a/src/frontend/src/layouts/App.jsx b/src/frontend/src/layouts/App.jsx
index ebfe359..8e4b004 100644
--- a/src/frontend/src/layouts/App.jsx
+++ b/src/frontend/src/layouts/App.jsx
@@ -34,17 +34,17 @@ const App = () => {
toasts={[
{
type: ToastTypes.info,
- title: `Test Toast`,
+ title: `Info Test Toast`,
body: `This is a test toast. It should say some things. Then it should disappear.`,
},
{
type: ToastTypes.danger,
- title: `Test Toast`,
+ title: `Danger Test Toast`,
body: `This is a test toast. It should say some things. Then it should disappear.`,
},
{
type: ToastTypes.success,
- title: `Test Toast`,
+ title: `Success Test Toast`,
body: `This is a test toast. It should say some things. Then it should disappear.`,
},
]}
diff --git a/src/frontend/src/layouts/Toasts.jsx b/src/frontend/src/layouts/Toasts.jsx
index 2de128b..fa6f5bb 100644
--- a/src/frontend/src/layouts/Toasts.jsx
+++ b/src/frontend/src/layouts/Toasts.jsx
@@ -1,7 +1,9 @@
-import React, { useState, useEffect } from "react";
+import { useState, useEffect, useContext } from "react";
import { CSSTransition } from 'react-transition-group';
+
import Card, { CardBody, CardHeader, CardHeaderAction } from "../elements/Card";
import styled, { keyframes, css } from "styled-components";
+import { StateContext } from "../state";
// countdown animation does not use transition-group
const countdown = keyframes`
@@ -20,8 +22,6 @@ const countdownStyle = css`
animation-fill-mode: forwards;
`;
-import Card, { CardBody, CardHeader, CardHeaderAction } from "../elements/Card";
-
const ToastCard = styled(Card)`
height: 6em;
padding-top: 0;
@@ -121,29 +121,43 @@ const ToastContainer = styled.div`
`;
const Toasts = ({ toasts = [] }) => {
-
+ const { state } = useContext(StateContext);
return (
- {toasts.map(({ title, body, type }) => (
-
+ {toasts
+ .filter(toast => state.toast.includes(toast.title))
+ .map(({ title, body, type }) => (
+
))}
);
-
};
const Toast = ({ title, body, type }) => {
- const [show, setShow] = useState(false);
+ const [ show, setShow ] = useState(false);
+ const { dispatch } = useContext(StateContext);
let timer;
useEffect( () => {
- setShow(true); // activate enter animation on mount
- return clearTimeout(timer); // clear timer on unmount
+ setShow(true); // activate enter animation on mount
+ return () => { // clean up timer and state on unmount
+ clearTimeout(timer);
+ unmount();
+ };
}, []
);
+ // sets timer to 6 seconds before automatic close
const startCountdown = () => {
- timer = setTimeout(() =>setShow(false), 6000);
+ timer = setTimeout(() => setShow(false), 6000);
+ };
+
+ // clears state after component unmounts
+ const unmount = () => {
+ dispatch({
+ type: `unmountToast`,
+ payload: title
+ });
};
return (
@@ -152,8 +166,9 @@ const Toast = ({ title, body, type }) => {
timeout={400}
classNames='toast'
onEntered={() => startCountdown()}
+ onExited={() => unmount()}
unmountOnExit
- >
+ >
{title}
setShow(false)}>Close
diff --git a/src/frontend/src/state.js b/src/frontend/src/state.js
index 7815e06..f8f9c4c 100644
--- a/src/frontend/src/state.js
+++ b/src/frontend/src/state.js
@@ -2,7 +2,9 @@ import React, { useReducer } from "react";
const initialState = {
user: null,
+ toast: [],
};
+
const StateContext = React.createContext(null);
const reducer = (state = initialState, action) => {
@@ -11,6 +13,10 @@ const reducer = (state = initialState, action) => {
return { ...state, user: action.payload };
case `unsetUser`:
return { ...state, user: null };
+ case `triggerToast`:
+ return { ...state, toast: [...state.toast, action.payload] };
+ case `unmountToast`:
+ return { ...state, toast: state.toast.filter(toast => toast !== action.payload)};
default:
console.warn(`Unknown reducer action received`, action);
return state;