Skip to content

Commit e93c7ab

Browse files
authored
New error display element in header (#336)
* New error display element in header * The new header contains a status icon displaying if any errors / warnings exist * on click it opens the message display * the message provider was reworked to be more consistent * the submit button turns red and changes its label if there are any existing errors Other: * Fixes mergify config
1 parent bf6025f commit e93c7ab

File tree

8 files changed

+128
-46
lines changed

8 files changed

+128
-46
lines changed

.mergify.yml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
queue_rules:
2+
- name: default
3+
conditions:
4+
# Conditions to get out of the queue (= merged)
5+
- check-success=^test \([0-9]+\.x, ubuntu-latest\)$
6+
17
pull_request_rules:
28
- name: Merge approved and green PRs with `merge when green` label
39
conditions:
@@ -6,10 +12,10 @@ pull_request_rules:
612
- base=master
713
- label=merge when green
814
actions:
9-
merge:
15+
queue:
1016
method: squash
11-
strict: smart+fasttrack
1217
commit_message: title+body
18+
name: default
1319
- name: Automatic merge for Dependabot pull requests
1420
conditions:
1521
- author~=^dependabot(|-preview)\[bot\]$
@@ -18,8 +24,8 @@ pull_request_rules:
1824
actions:
1925
review:
2026
type: APPROVE
21-
message: Automatically approving dependabot
22-
merge:
27+
message: Automatically approving dependabo
28+
queue:
2329
method: squash
24-
strict: smart+fasttrack
2530
commit_message: title+body
31+
name: default

src/App.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { useSafeAppsSDK } from "@gnosis.pm/safe-apps-react-sdk";
22
import { BaseTransaction } from "@gnosis.pm/safe-apps-sdk";
33
import { Breadcrumb, BreadcrumbElement, Button, Card, Divider, Loader, Text } from "@gnosis.pm/safe-react-components";
44
import { setUseWhatChange } from "@simbathesailor/use-what-changed";
5-
import React, { useCallback, useState } from "react";
5+
import React, { useCallback, useState, useContext } from "react";
66
import styled from "styled-components";
77

88
import { CSVForm } from "./components/CSVForm";
9-
import { FAQModal } from "./components/FAQModal";
109
import { Header } from "./components/Header";
1110
import { Summary } from "./components/Summary";
11+
import { MessageContext } from "./contexts/MessageContextProvider";
1212
import { useBalances } from "./hooks/balances";
1313
import { useTokenList } from "./hooks/token";
1414
import { AssetTransfer, CollectibleTransfer, Transfer } from "./parser/csvParser";
@@ -20,6 +20,7 @@ const App: React.FC = () => {
2020
const { isLoading } = useTokenList();
2121
const balanceLoader = useBalances();
2222
const [tokenTransfers, setTokenTransfers] = useState<Transfer[]>([]);
23+
const { messages } = useContext(MessageContext);
2324

2425
const [submitting, setSubmitting] = useState(false);
2526
const [parsing, setParsing] = useState(false);
@@ -95,18 +96,23 @@ const App: React.FC = () => {
9596
<Button
9697
style={{ alignSelf: "flex-start", marginTop: 16, marginBottom: 16 }}
9798
size="lg"
98-
color="primary"
99+
color={messages.length === 0 ? "primary" : "error"}
99100
onClick={submitTx}
100101
disabled={parsing || tokenTransfers.length + collectibleTransfers.length === 0}
101102
>
102-
{parsing ? <Loader size="sm" color="primaryLight" /> : "Submit"}
103+
{parsing ? (
104+
<Loader size="sm" color="primaryLight" />
105+
) : messages.length === 0 ? (
106+
"Submit"
107+
) : (
108+
"Submit with errors"
109+
)}
103110
</Button>
104111
)}
105112
</Card>
106113
)}
107114
</>
108115
}
109-
<FAQModal />
110116
</Container>
111117
);
112118
};

src/GlobalStyle.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,25 @@ const GlobalStyle = createGlobalStyle`
9393
background-color: rgb(247, 245, 245);
9494
border-radius: 8px;
9595
}
96+
97+
.MuiFab-root.statusDotButtonEmpty {
98+
background-color: #4caf50;
99+
}
100+
101+
.MuiFab-root.statusDotButtonEmpty:hover {
102+
background-color: #2e7d32;
103+
cursor: pointer;
104+
}
105+
106+
.MuiFab-root.statusDotButtonErrors {
107+
background-color: #ef5350;
108+
}
109+
110+
.MuiFab-root.statusDotButtonErrors:hover {
111+
background-color: #d32f2f;
112+
cursor: pointer;
113+
}
114+
96115
`;
97116

98117
export default GlobalStyle;

src/components/CSVForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export const CSVForm = (props: CSVFormProps): JSX.Element => {
8282
) {
8383
return {
8484
message: `Insufficient Balance: ${insufficientBalanceInfo.transferAmount} of ${insufficientBalanceInfo.token}`,
85-
severity: "warning",
85+
severity: "error",
8686
};
8787
} else {
8888
if (insufficientBalanceInfo.isDuplicate) {
@@ -93,7 +93,7 @@ export const CSVForm = (props: CSVFormProps): JSX.Element => {
9393
} else {
9494
return {
9595
message: `Collectible ERC721 token ${insufficientBalanceInfo.token} with ID ${insufficientBalanceInfo.id} is not held by this safe`,
96-
severity: "warning",
96+
severity: "error",
9797
};
9898
}
9999
}

src/components/FAQModal.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@ export const FAQModal: () => JSX.Element = () => {
66
const [showHelp, setShowHelp] = useState(false);
77
return (
88
<>
9-
<Fab
10-
variant="extended"
11-
size="small"
12-
style={{ position: "absolute", top: 24, right: 24, textTransform: "none" }}
13-
onClick={() => setShowHelp(true)}
14-
>
9+
<Fab variant="extended" size="small" style={{ textTransform: "none" }} onClick={() => setShowHelp(true)}>
1510
<Icon size="md" type="question" />
1611
<Text size="xl">Help</Text>
1712
</Fab>

src/components/Header.tsx

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1-
import { Snackbar } from "@material-ui/core";
1+
import { Icon, Text } from "@gnosis.pm/safe-react-components";
2+
import { Fab, Snackbar } from "@material-ui/core";
23
import MuiAlert from "@material-ui/lab/Alert";
34
import React, { useContext } from "react";
45
import styled from "styled-components";
56

67
import { MessageContext, Message } from "../contexts/MessageContextProvider";
78

9+
import { FAQModal } from "./FAQModal";
10+
811
export function Alert(props) {
912
return <MuiAlert elevation={6} variant="filled" {...props} />;
1013
}
1114
const HeaderContainer = styled.div`
1215
flex: 1;
1316
width: 100%;
17+
position: absolute;
18+
justify-content: flex-end;
19+
display: flex;
20+
top: 24px;
21+
right: 24px;
22+
z-index: 2;
23+
gap: 8px;
1424
`;
1525

1626
const AlertWrapper = styled.div`
@@ -22,31 +32,54 @@ const AlertWrapper = styled.div`
2232
`;
2333

2434
export const Header = (): JSX.Element => {
25-
const messageContext = useContext(MessageContext);
26-
const messages = messageContext.messages;
35+
const { messages, showMessages, hideMessages, toggleMessages, removeMessage } = useContext(MessageContext);
36+
37+
const handleClose = (event: React.SyntheticEvent | Event, reason?: string) => {
38+
if (reason === "clickaway") {
39+
return;
40+
}
41+
42+
hideMessages();
43+
};
44+
2745
return (
2846
<HeaderContainer>
29-
{messages?.length > 0 && (
30-
<Snackbar
31-
anchorOrigin={{ vertical: "top", horizontal: "right" }}
32-
open={messages?.length > 0}
33-
onClose={() => messageContext.setMessages([])}
34-
autoHideDuration={6000}
35-
style={{ gap: "4px" }}
36-
>
37-
<AlertWrapper>
38-
{messages.map((message: Message, index: number) => (
39-
<Alert
40-
severity={message.severity}
41-
key={"message" + index}
42-
onClose={() => messageContext.removeMessage(message)}
43-
>
44-
{message.message}
45-
</Alert>
46-
))}
47-
</AlertWrapper>
48-
</Snackbar>
49-
)}
47+
<Fab
48+
variant="circular"
49+
size="small"
50+
className={messages.length === 0 ? "statusDotButtonEmpty" : "statusDotButtonErrors"}
51+
style={{ textTransform: "none", width: "34px", height: "34px" }}
52+
onClick={toggleMessages}
53+
>
54+
{messages.length === 0 ? (
55+
<Icon color="white" type="check" size="sm" />
56+
) : (
57+
<Text size="xl" color="white">
58+
{messages.length}
59+
</Text>
60+
)}
61+
</Fab>
62+
<FAQModal />
63+
<Snackbar
64+
anchorOrigin={{ vertical: "top", horizontal: "right" }}
65+
open={showMessages}
66+
onClose={handleClose}
67+
autoHideDuration={6000}
68+
style={{ gap: "4px", top: "64px" }}
69+
>
70+
<AlertWrapper>
71+
{messages.length === 0 && (
72+
<Alert secerity="success" key="successMessage">
73+
No warnings or errors.
74+
</Alert>
75+
)}
76+
{messages.map((message: Message, index: number) => (
77+
<Alert severity={message.severity} key={"message" + index} onClose={() => removeMessage(message)}>
78+
{message.message}
79+
</Alert>
80+
))}
81+
</AlertWrapper>
82+
</Snackbar>
5083
</HeaderContainer>
5184
);
5285
};

src/contexts/MessageContextProvider.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, ReactElement } from "react";
1+
import React, { useState, ReactElement, useCallback } from "react";
22

33
export type CodeWarning = {
44
message: string;
@@ -18,6 +18,9 @@ type MessageContextType = {
1818
addMessage: (message: Message) => void;
1919
codeWarnings: CodeWarning[];
2020
setCodeWarnings: (messages: CodeWarning[]) => void;
21+
showMessages: boolean;
22+
hideMessages: () => void;
23+
toggleMessages: () => void;
2124
};
2225

2326
export const MessageContext = React.createContext<MessageContextType>({
@@ -27,18 +30,31 @@ export const MessageContext = React.createContext<MessageContextType>({
2730
addMessage: (message: Message | CodeWarning) => {},
2831
codeWarnings: [],
2932
setCodeWarnings: (messages: CodeWarning[]) => {},
33+
showMessages: false,
34+
hideMessages: () => {},
35+
toggleMessages: () => {},
3036
});
3137

3238
type MessageContextProviderProps = {
3339
children: ReactElement;
3440
};
3541

3642
export const MessageContextProvider = (props: MessageContextProviderProps) => {
37-
const [messages, setMessages] = useState<Message[]>([]);
43+
const [messages, internalSetMessages] = useState<Message[]>([]);
3844
const [codeWarnings, setCodeWarnings] = useState<CodeWarning[]>([]);
45+
const [showMessages, setShowMessages] = useState(false);
3946

40-
const removeMessage = (messageToRemove: Message | CodeWarning) =>
47+
const removeMessage = (messageToRemove: Message | CodeWarning) => {
48+
console.log("Removing Message");
4149
setMessages(messages.filter((message) => message.message !== messageToRemove.message));
50+
};
51+
52+
const setMessages = useCallback((newMessages: Message[]) => {
53+
internalSetMessages(newMessages);
54+
if (newMessages.length > 0) {
55+
setShowMessages(true);
56+
}
57+
}, []);
4258

4359
const addMessage = (messageToAdd: Message | CodeWarning) => {
4460
// Do not add equal message
@@ -47,13 +63,20 @@ export const MessageContextProvider = (props: MessageContextProviderProps) => {
4763
}
4864
};
4965

66+
const hideMessages = () => setShowMessages(false);
67+
68+
const toggleMessages = () => setShowMessages(!showMessages);
69+
5070
const contextValue = {
5171
messages,
5272
setMessages,
5373
removeMessage,
5474
addMessage,
5575
codeWarnings,
5676
setCodeWarnings,
77+
showMessages,
78+
hideMessages,
79+
toggleMessages,
5780
};
5881

5982
return <MessageContext.Provider value={contextValue}>{props.children}</MessageContext.Provider>;

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6520,7 +6520,7 @@ eslint-plugin-react-hooks@^4.2.0, eslint-plugin-react-hooks@^4.3.0:
65206520
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172"
65216521
integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==
65226522

6523-
eslint-plugin-react@^7.21.5, eslint-plugin-react@^7.28.0:
6523+
eslint-plugin-react@^7.21.5, eslint-plugin-react@^7.27.1, eslint-plugin-react@^7.28.0:
65246524
version "7.28.0"
65256525
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz#8f3ff450677571a659ce76efc6d80b6a525adbdf"
65266526
integrity sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==

0 commit comments

Comments
 (0)