Skip to content

Commit 9168e9c

Browse files
authored
Merge pull request #53 from kaleido-io/snackbar
add API error snackbars
2 parents 10591d5 + 3a735d7 commit 9168e9c

File tree

11 files changed

+196
-29
lines changed

11 files changed

+196
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Changelog
22

3-
The changelog will be updated with the next release
3+
The changelog will be updated with the next release

CODE_OF_CONDUCT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ Please review the Hyperledger [Code of
44
Conduct](https://wiki.hyperledger.org/community/hyperledger-project-code-of-conduct)
55
before participating. It is important that we keep things civil.
66

7-
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
7+
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ Please visit the
77
[contributors guide](https://labs.hyperledger.org/firefly/contributors/contributors.html) in the
88
docs to learn how to make contributions to this exciting project.
99

10-
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
10+
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.

MAINTAINERS.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ The following is the list of current maintainers this repo:
88
| Peter Broadhurst | peterbroadhurst | [email protected] | peterbroadhurst |
99
| Andrew Richardson | awrichar | [email protected] | Andrew.Richardson |
1010

11-
1211
This list is to be kept up to date as maintainers are added or removed.
1312

1413
# Expectations of Maintainers

SECURITY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ The other way is to file a confidential security bug in our
1717

1818
The process by which the Hyperledger Security Team handles security bugs is documented further in
1919
our [Defect Response page](https://wiki.hyperledger.org/display/SEC/Defect+Response) on our
20-
[wiki](https://wiki.hyperledger.org).
20+
[wiki](https://wiki.hyperledger.org).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "firefly-ui",
3-
"version": "0.2.2",
3+
"version": "0.2.3",
44
"private": true,
55
"dependencies": {
66
"@material-ui/core": "^4.11.4",

src/components/MessageSnackbar.tsx

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright © 2021 Kaleido, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
import React, { useEffect, useRef, useState } from 'react';
18+
import {
19+
IconButton,
20+
makeStyles,
21+
Slide,
22+
Snackbar,
23+
Theme,
24+
} from '@material-ui/core';
25+
import { TransitionProps } from '@material-ui/core/transitions';
26+
import AlertCircleIcon from 'mdi-react/AlertCircleIcon';
27+
import CloseIcon from 'mdi-react/CloseIcon';
28+
import CheckIcon from 'mdi-react/CheckIcon';
29+
30+
const TRANSITION_TIMEOUT = 400;
31+
export type SnackbarMessageType = 'error' | 'success';
32+
33+
interface MessageSnackbarProps {
34+
message: string;
35+
setMessage: React.Dispatch<React.SetStateAction<string>>;
36+
messageType?: SnackbarMessageType;
37+
}
38+
39+
function SlideTransition(props: TransitionProps) {
40+
return <Slide {...props} direction="up" timeout={TRANSITION_TIMEOUT} />;
41+
}
42+
43+
export const MessageSnackbar: React.FC<MessageSnackbarProps> = ({
44+
message,
45+
setMessage,
46+
messageType = 'error',
47+
}) => {
48+
const classes = useStyles();
49+
const [open, setOpen] = useState(message ? true : false);
50+
const timeoutRef = useRef<number>(0);
51+
52+
useEffect(() => {
53+
return () => window.clearTimeout(timeoutRef.current);
54+
}, []);
55+
56+
const handleClose = () => {
57+
window.clearTimeout(timeoutRef.current);
58+
setOpen(false);
59+
timeoutRef.current = window.setTimeout(
60+
() => setMessage(''),
61+
TRANSITION_TIMEOUT
62+
);
63+
};
64+
65+
useEffect(() => {
66+
setOpen(message ? true : false);
67+
}, [message]);
68+
69+
return (
70+
<Snackbar
71+
open={open}
72+
onClose={handleClose}
73+
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
74+
autoHideDuration={messageType === 'error' ? 60000 : 6000}
75+
ContentProps={{
76+
className: messageType === 'error' ? classes.error : classes.success,
77+
}}
78+
TransitionComponent={SlideTransition}
79+
message={
80+
<div className={classes.message}>
81+
{messageType === 'error' && (
82+
<AlertCircleIcon className={classes.icon} />
83+
)}
84+
{messageType === 'success' && <CheckIcon className={classes.icon} />}
85+
{message}
86+
</div>
87+
}
88+
action={[
89+
<IconButton
90+
key="close"
91+
aria-label="close"
92+
color="inherit"
93+
onClick={handleClose}
94+
>
95+
<CloseIcon />
96+
</IconButton>,
97+
]}
98+
/>
99+
);
100+
};
101+
102+
const useStyles = makeStyles<Theme>((theme) => ({
103+
error: {
104+
backgroundColor: theme.palette.error.main,
105+
color: theme.palette.text.primary,
106+
},
107+
success: {
108+
backgroundColor: theme.palette.success.main,
109+
color: theme.palette.text.primary,
110+
},
111+
message: {
112+
display: 'flex',
113+
alignItems: 'center',
114+
},
115+
icon: {
116+
marginRight: theme.spacing(1),
117+
},
118+
}));

src/components/Routes.tsx

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import { ApplicationContext } from '../contexts/ApplicationContext';
3737
import { NavWrapper } from './NavWrapper';
3838
import { fetchWithCredentials } from '../utils';
3939
import { CircularProgress } from '@material-ui/core';
40+
import { SnackbarContext } from '../contexts/SnackbarContext';
41+
import { MessageSnackbar, SnackbarMessageType } from './MessageSnackbar';
4042

4143
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
4244
const history = createBrowserHistory({
@@ -54,6 +56,8 @@ export const Routes: () => JSX.Element = () => {
5456
useState<CreatedFilterOptions>('24hours');
5557
const ws = useRef<ReconnectingWebSocket | null>(null);
5658
const [lastEvent, setLastEvent] = useState<any>();
59+
const [message, setMessage] = useState('');
60+
const [messageType, setMessageType] = useState<SnackbarMessageType>('error');
5761

5862
//path needs to update on every render for conditional rendering since its outside of Router
5963
const { pathname: currentPath } = useMemo(() => {
@@ -165,26 +169,33 @@ export const Routes: () => JSX.Element = () => {
165169
setCreatedFilter,
166170
}}
167171
>
168-
<Router history={history}>
169-
<QueryParamProvider ReactRouterRoute={Route}>
170-
<Switch>
171-
{routes.map((route, i) => (
172-
<Route
173-
key={i}
174-
exact={route.exact}
175-
path={route.path}
176-
render={() => (
177-
// navigation need to be within the route for access to url params
178-
<NavWrapper>
179-
<route.component />
180-
</NavWrapper>
181-
)}
182-
/>
183-
))}
184-
<Redirect to="/" />
185-
</Switch>
186-
</QueryParamProvider>
187-
</Router>
172+
<SnackbarContext.Provider value={{ setMessage, setMessageType }}>
173+
<MessageSnackbar
174+
{...{ message }}
175+
{...{ setMessage }}
176+
{...{ messageType }}
177+
/>
178+
<Router history={history}>
179+
<QueryParamProvider ReactRouterRoute={Route}>
180+
<Switch>
181+
{routes.map((route, i) => (
182+
<Route
183+
key={i}
184+
exact={route.exact}
185+
path={route.path}
186+
render={() => (
187+
// navigation need to be within the route for access to url params
188+
<NavWrapper>
189+
<route.component />
190+
</NavWrapper>
191+
)}
192+
/>
193+
))}
194+
<Redirect to="/" />
195+
</Switch>
196+
</QueryParamProvider>
197+
</Router>
198+
</SnackbarContext.Provider>
188199
</ApplicationContext.Provider>
189200
</NamespaceContext.Provider>
190201
);

src/contexts/SnackbarContext.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright © 2021 Kaleido, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
import { Dispatch, SetStateAction, createContext } from 'react';
18+
import { SnackbarMessageType } from '../components/MessageSnackbar';
19+
20+
export interface ISnackbarContext {
21+
setMessage: Dispatch<SetStateAction<string>>;
22+
setMessageType: Dispatch<SetStateAction<SnackbarMessageType>>;
23+
}
24+
25+
export const SnackbarContext = createContext({} as ISnackbarContext);

src/views/Messages/MessageDetails.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { NamespaceContext } from '../../contexts/NamespaceContext';
3636
import { useHistory } from 'react-router-dom';
3737
import Highlight from 'react-highlight';
3838
import { fetchWithCredentials } from '../../utils';
39+
import { SnackbarContext } from '../../contexts/SnackbarContext';
3940

4041
interface Props {
4142
message: IMessage;
@@ -51,6 +52,7 @@ export const MessageDetails: React.FC<Props> = ({ message, open, onClose }) => {
5152
const [txId, setTxId] = useState<string>();
5253
const [data, setData] = useState<IData[]>([]);
5354
const { selectedNamespace } = useContext(NamespaceContext);
55+
const { setMessage, setMessageType } = useContext(SnackbarContext);
5456

5557
const detailItem = (label: string, value: string | JSX.Element) => (
5658
<>
@@ -78,12 +80,21 @@ export const MessageDetails: React.FC<Props> = ({ message, open, onClose }) => {
7880
setData((await messageDataResponse.json()).data);
7981
const batch: IBatch = await batchResponse.json();
8082
setTxId(batch.payload.tx.id);
83+
} else {
84+
setMessageType('error');
85+
setMessage(`Error loading details for message ${message.header.id}`);
8186
}
8287
})
8388
.finally(() => {
8489
setLoading(false);
8590
});
86-
}, [message.batch, selectedNamespace, message.header.id]);
91+
}, [
92+
message.batch,
93+
selectedNamespace,
94+
message.header.id,
95+
setMessage,
96+
setMessageType,
97+
]);
8798

8899
const copyableHash = (hash: string) => (
89100
<Grid alignItems="center" container direction="row">

0 commit comments

Comments
 (0)