diff --git a/assets/images/error-icon.svg b/assets/images/error-icon.svg
new file mode 100644
index 000000000..0918ec675
--- /dev/null
+++ b/assets/images/error-icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/info-icon.svg b/assets/images/info-icon.svg
new file mode 100644
index 000000000..425fc218d
--- /dev/null
+++ b/assets/images/info-icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/success-icon.svg b/assets/images/success-icon.svg
new file mode 100644
index 000000000..7f7965403
--- /dev/null
+++ b/assets/images/success-icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/warning-icon.svg b/assets/images/warning-icon.svg
new file mode 100644
index 000000000..f073d2756
--- /dev/null
+++ b/assets/images/warning-icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/components/messages/MessageManager.tsx b/src/components/messages/MessageManager.tsx
index 27b4b0210..241117f2b 100644
--- a/src/components/messages/MessageManager.tsx
+++ b/src/components/messages/MessageManager.tsx
@@ -3,115 +3,170 @@ import IconButton from "@material-ui/core/IconButton";
import Snackbar from "@material-ui/core/Snackbar";
import Typography from "@material-ui/core/Typography";
import CloseIcon from "@material-ui/icons/Close";
-import React from "react";
+import classNames from "classnames";
+import React, { useState } from "react";
+import { FormattedMessage } from "react-intl";
import { IMessage, MessageContext } from "./";
+import { useStyles } from "./styles";
interface Message extends IMessage {
key: string;
}
-interface MessageManagerState {
- message: Message;
- opened: boolean;
-}
-export class MessageManager extends React.Component<{}, MessageManagerState> {
- state: MessageManagerState = {
- message: { key: "0", onUndo: undefined, text: "" },
- opened: false
- };
- queue = [];
+export const MessageManager = props => {
+ const { children } = props;
- handleClose = (_, reason) => {
+ const [message, setMessage] = useState({
+ key: "0",
+ onUndo: undefined,
+ status: "info",
+ text: ""
+ });
+ const [opened, setOpened] = useState(false);
+
+ const classes = useStyles({});
+ const {
+ action,
+ autohide = 3000,
+ title,
+ text,
+ key,
+ onUndo,
+ status = "info"
+ } = message;
+
+ const queue = [];
+
+ const handleClose = (_, reason) => {
if (reason === "clickaway") {
return;
}
- this.setState({ opened: false });
+ setOpened(false);
};
- handleExited = () => {
- this.processQueue();
+ const processQueue = () => {
+ if (queue.length > 0) {
+ setMessage(queue.shift());
+ setOpened(true);
+ }
};
- pushMessage = (message: IMessage) => {
- this.queue.push({
+ const handleExited = () => {
+ processQueue();
+ };
+
+ const pushMessage = (message: IMessage) => {
+ queue.push({
key: new Date().getTime(),
...message
});
- if (this.state.opened) {
- this.setState({ opened: false });
+ if (opened) {
+ setOpened(false);
} else {
- this.processQueue();
+ processQueue();
}
};
- processQueue = () => {
- if (this.queue.length > 0) {
- this.setState({
- message: this.queue.shift(),
- opened: true
- });
- }
- };
-
- render() {
- const { autohide = 3000, title, text, key, onUndo } = this.state.message;
- return (
- <>
-
- {title && (
-
- {title}
-
- )}
- {text}
-
- }
- title={title}
- action={[
- !!onUndo ? (
-
- ) : (
- undefined
- ),
-
+
+ {title && (
+
+ {title}
+
+ )}
+
-
-
- ]}
- />
-
- {this.props.children}
-
- >
- );
- }
-}
+ {text}
+
+
+ }
+ title={title}
+ action={[
+ !!onUndo ? (
+
+ ) : (
+ undefined
+ ),
+ !!action ? (
+
+ ) : (
+ undefined
+ ),
+
+
+ ,
+
+ ]}
+ />
+
+ {children}
+
+ >
+ );
+};
+
export default MessageManager;
diff --git a/src/components/messages/index.ts b/src/components/messages/index.ts
index 95424f09f..1ea935669 100644
--- a/src/components/messages/index.ts
+++ b/src/components/messages/index.ts
@@ -1,10 +1,12 @@
import { createContext } from "react";
export interface IMessage {
+ action?: () => void;
autohide?: number;
title?: string;
text: string;
onUndo?: () => void;
+ status?: "success" | "error" | "info" | "warning";
}
export type IMessageContext = (message: IMessage) => void;
export const MessageContext = createContext(undefined);
diff --git a/src/components/messages/styles.ts b/src/components/messages/styles.ts
new file mode 100644
index 000000000..5743651cf
--- /dev/null
+++ b/src/components/messages/styles.ts
@@ -0,0 +1,114 @@
+import errorIcon from "@assets/images/error-icon.svg";
+import infoIcon from "@assets/images/info-icon.svg";
+import successIcon from "@assets/images/success-icon.svg";
+import warningIcon from "@assets/images/warning-icon.svg";
+import { makeStyles } from "@material-ui/core/styles";
+import { darken } from "@material-ui/core/styles/colorManipulator";
+
+const successColor = "#60DAA0";
+const warningColor = "#FFB84E";
+const infoColor = "#CAD8DF";
+
+export const useStyles = makeStyles(
+ theme => ({
+ "@keyframes bar": {
+ from: { transform: "translateX(-100%)" },
+ to: { transform: "translateX(0)" }
+ },
+ closeBtn: {
+ position: "absolute",
+ right: 0,
+ top: 0
+ },
+ error: {
+ "& > div": {
+ "& button span": {
+ color: "#fff"
+ },
+ "&:before": {
+ backgroundImage: `url(${errorIcon})`
+ },
+
+ backgroundColor: theme.palette.error.main,
+ color: "#fff"
+ }
+ },
+ progressBar: {
+ backgroundColor: infoColor,
+ height: 8,
+ transform: "translateX(-100%)",
+ width: "100%"
+ },
+ progressBarActive: {
+ animation: `$bar var(--animationTime) ease both`
+ },
+ progressBarContainer: {
+ borderRadius: "0 0 4px 4px",
+ bottom: 0,
+ left: 0,
+ overflow: "hidden",
+ position: "absolute",
+ width: "calc(100%)"
+ },
+ progressBarError: {
+ backgroundColor: darken(theme.palette.error.main, 0.2)
+ },
+ progressBarSuccess: {
+ backgroundColor: darken(successColor, 0.2)
+ },
+ progressBarWarning: {
+ backgroundColor: darken(warningColor, 0.2)
+ },
+ snackbar: {
+ "& > div": {
+ "&:before": {
+ backgroundImage: `url(${infoIcon})`,
+ backgroundRepeat: "no-repeat",
+ backgroundSize: "contain",
+ content: "''",
+ display: "block",
+ height: 32,
+ left: 15,
+ position: "absolute",
+ top: 13,
+ width: 32
+ },
+ paddingLeft: 60,
+ position: "relative"
+ }
+ },
+ success: {
+ "& > div": {
+ "& button span": {
+ color: "#fff"
+ },
+ "&:before": {
+ backgroundImage: `url(${successIcon})`
+ },
+
+ backgroundColor: successColor,
+ color: "#fff"
+ }
+ },
+ text: {
+ color: "#fff",
+ paddingTop: 5
+ },
+ textInfo: {
+ paddingTop: 5
+ },
+ warning: {
+ "& > div": {
+ "& button span": {
+ color: "#fff"
+ },
+ "&:before": {
+ backgroundImage: `url(${warningIcon})`
+ },
+ backgroundColor: warningColor,
+ color: "#fff"
+ }
+ }
+ }),
+ { name: "MessageManager" }
+);
diff --git a/src/theme.ts b/src/theme.ts
index 42a02f7b1..e02b3aba4 100644
--- a/src/theme.ts
+++ b/src/theme.ts
@@ -343,7 +343,9 @@ export default (colors: IThemeColors): Theme =>
color: colors.font.default
}
},
- alignSelf: "baseline"
+ display: "block",
+ paddingBottom: 15,
+ paddingLeft: 0
},
message: {
fontSize: 16
@@ -353,8 +355,7 @@ export default (colors: IThemeColors): Theme =>
boxShadow:
"0 6px 10px 0px rgba(0, 0, 0, 0.15), 0 1px 18px 0px rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.10)",
color: colors.font.default,
- display: "grid",
- gridTemplateColumns: "1fr 56px",
+ display: "block",
maxWidth: 480
}
},