Skip to content

Commit 6188c34

Browse files
author
Manuel Gellfart
authored
feat: add snackbar (#492)
* feat: snackbar for error messages
1 parent 33f14af commit 6188c34

File tree

4 files changed

+104
-100
lines changed

4 files changed

+104
-100
lines changed

src/App.tsx

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Unsubscribe } from "redux";
88
import { CSVForm } from "./components/CSVForm";
99
import { FAQModal } from "./components/FAQModal";
1010
import { Loading } from "./components/Loading";
11+
import { MessageSnackbar } from "./components/MessageSnackbar";
1112
import { Summary } from "./components/Summary";
1213
import { TransactionStatusScreen } from "./components/TransactionStatusScreen";
1314
import { useTokenList } from "./hooks/token";
@@ -106,7 +107,7 @@ const App: React.FC = () => {
106107
</Grid>
107108

108109
{!pendingTx && (
109-
<Card sx={{ padding: 2, mt: 3 }}>
110+
<Card sx={{ padding: 2 }}>
110111
<CSVForm />
111112
</Card>
112113
)}
@@ -115,24 +116,24 @@ const App: React.FC = () => {
115116
{pendingTx ? (
116117
<TransactionStatusScreen tx={pendingTx} reset={() => setPendingTx(undefined)} />
117118
) : (
118-
<Button
119-
variant="contained"
120-
style={{ alignSelf: "flex-start", marginTop: 16, marginBottom: 16 }}
121-
size="stretched"
122-
color={messages.length === 0 ? "primary" : "error"}
123-
onClick={submitTx}
124-
disabled={parsing || transfers.length + collectibleTransfers.length === 0}
125-
>
126-
{parsing ? (
127-
<>
128-
<CircularProgress size="small" color="secondary" /> Parsing
129-
</>
130-
) : messages.length === 0 ? (
131-
"Submit"
132-
) : (
133-
"Submit with errors"
134-
)}
135-
</Button>
119+
<Box display="flex" alignItems="center" gap={1}>
120+
<Button
121+
variant="contained"
122+
sx={{ alignSelf: "flex-start", display: "flex", marginTop: 2, marginBottom: 2, gap: 1 }}
123+
size="stretched"
124+
color={messages.length === 0 ? "primary" : "error"}
125+
onClick={submitTx}
126+
disabled={parsing || transfers.length + collectibleTransfers.length === 0}
127+
>
128+
{parsing && (
129+
<>
130+
<CircularProgress size={24} color="primary" /> Parsing
131+
</>
132+
)}
133+
{messages.length === 0 ? "Submit" : "Submit with errors"}
134+
</Button>
135+
<MessageSnackbar />
136+
</Box>
136137
)}
137138
</Card>
138139
</Box>

src/components/Header.tsx

Lines changed: 0 additions & 74 deletions
This file was deleted.

src/components/MessageSnackbar.tsx

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import CloseIcon from "@mui/icons-material/Close";
2+
import ErrorIcon from "@mui/icons-material/Error";
3+
import { Alert, Box, IconButton, LinearProgress, Snackbar } from "@mui/material";
4+
import { useEffect, useState } from "react";
5+
import { selectMessages } from "src/stores/slices/messageSlice";
6+
import { useAppSelector } from "src/stores/store";
7+
8+
const HIDE_TIME = 7_000;
9+
10+
export const MessageSnackbar = () => {
11+
const messages = useAppSelector(selectMessages);
12+
13+
const [open, setOpen] = useState(false);
14+
const [timeLeft, setTimeLeft] = useState(0);
15+
16+
useEffect(() => {
17+
let timer: NodeJS.Timer | undefined = undefined;
18+
if (messages.messages.length > 0) {
19+
setOpen(true);
20+
setTimeLeft(HIDE_TIME);
21+
timer = setInterval(() => {
22+
setTimeLeft((previousTime) => {
23+
if (previousTime === 100) {
24+
return 0;
25+
}
26+
return previousTime - 100;
27+
});
28+
}, 100);
29+
}
30+
return () => clearInterval(timer);
31+
}, [messages.messages]);
32+
33+
const onClose = () => {
34+
setOpen(false);
35+
};
36+
37+
const onOpen = () => {
38+
setTimeLeft(HIDE_TIME - 100);
39+
setOpen(true);
40+
};
41+
42+
return (
43+
<>
44+
{messages.messages.length > 0 ? (
45+
<div>
46+
<IconButton size="small" aria-label="close" color="error" disabled={open} onClick={onOpen}>
47+
<ErrorIcon fontSize="medium" />
48+
</IconButton>
49+
</div>
50+
) : null}
51+
<Snackbar
52+
open={open}
53+
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
54+
autoHideDuration={HIDE_TIME}
55+
onClose={onClose}
56+
action={
57+
<IconButton size="small" aria-label="close" color="inherit" onClick={onClose}>
58+
<CloseIcon fontSize="small" />
59+
</IconButton>
60+
}
61+
>
62+
<Box>
63+
<LinearProgress
64+
variant="determinate"
65+
value={100 * (timeLeft / HIDE_TIME)}
66+
sx={{
67+
borderRadius: "6px",
68+
}}
69+
/>
70+
{messages.messages.map((message, idx) => (
71+
<Alert key={idx} severity={message.severity}>
72+
{message.message}
73+
</Alert>
74+
))}
75+
</Box>
76+
</Snackbar>
77+
</>
78+
);
79+
};

src/stores/slices/messageSlice.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { AlertColor } from "@mui/material";
22
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
33

4+
import { RootState } from "../store";
5+
46
export type CodeWarning = {
57
message: string;
68
severity: string;
@@ -37,15 +39,11 @@ export const messageSlice = createSlice({
3739
removeMessage: (state, action: PayloadAction<Message>) => {
3840
state.messages = state.messages.filter((message) => message.message !== action.payload.message);
3941
},
40-
hideMessages: (state) => {
41-
state.showMessages = false;
42-
},
43-
toggleMessages: (state) => {
44-
state.showMessages = !state.showMessages;
45-
},
4642
},
4743
});
4844

49-
export const { setMessages, setCodeWarnings, removeMessage, hideMessages, toggleMessages } = messageSlice.actions;
45+
export const { setMessages, setCodeWarnings, removeMessage } = messageSlice.actions;
46+
47+
export const selectMessages = ({ messages }: RootState) => messages;
5048

5149
export default messageSlice.reducer;

0 commit comments

Comments
 (0)