on master help sign in
feat: error boundary mvp
0552c85Matt Condon (shrugs)
package-lock.json+5
@@ -12113,6 +12113,11 @@
12113 "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
12113 "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
12114 "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw=="
12114 "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw=="
12115 },
12115 },
12116+ "new-github-issue-url": {
12117+ "version": "0.2.1",
12118+ "resolved": "https://registry.npmjs.org/new-github-issue-url/-/new-github-issue-url-0.2.1.tgz",
12119+ "integrity": "sha512-md4cGoxuT4T4d/HDOXbrUHkTKrp/vp+m3aOA7XXVYwNsUNMK49g3SQicTSeV5GIz/5QVGAeYRAOlyp9OvlgsYA=="
12120+ },
12116 "nice-try": {
12121 "nice-try": {
12117 "version": "1.0.5",
12122 "version": "1.0.5",
12118 "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
12123 "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
package.json+1
@@ -39,6 +39,7 @@
39 "keythereum": "^1.0.4",
39 "keythereum": "^1.0.4",
40 "lodash": "^4.17.14",
40 "lodash": "^4.17.14",
41 "more-entropy": "^0.0.7",
41 "more-entropy": "^0.0.7",
42+ "new-github-issue-url": "^0.2.1",
42 "prop-types": "^15.6.2",
43 "prop-types": "^15.6.2",
43 "react": "^16.8.6",
44 "react": "^16.8.6",
44 "react-dom": "^16.8.6",
45 "react-dom": "^16.8.6",
src/Bridge.js+16−12
@@ -16,6 +16,8 @@ import useImpliedTicket from 'lib/useImpliedTicket';
16import useHasDisclaimed from 'lib/useHasDisclaimed';
16import useHasDisclaimed from 'lib/useHasDisclaimed';
17
17
18import 'style/index.scss';
18import 'style/index.scss';
19+import WithErrorBoundary from 'components/WithErrorBoundary';
20+import GlobalErrorBoundary from 'components/GlobalErrorBoundary';
19
21
20const INITIAL_NETWORK_TYPE = isDevelopment
22const INITIAL_NETWORK_TYPE = isDevelopment
21 ? NETWORK_TYPES.LOCAL
23 ? NETWORK_TYPES.LOCAL
@@ -63,18 +65,20 @@ function Bridge() {
63 const initialRoutes = useInitialRoutes();
65 const initialRoutes = useInitialRoutes();
64
66
65 return (
67 return (
66− <Provider
68+ <WithErrorBoundary render={error => <GlobalErrorBoundary error={error} />}>
69+ <Provider
67− views={ROUTES}
70+ views={ROUTES}
68− names={ROUTE_NAMES}
71+ names={ROUTE_NAMES}
69− initialRoutes={initialRoutes}
72+ initialRoutes={initialRoutes}
70− initialNetworkType={INITIAL_NETWORK_TYPE}
73+ initialNetworkType={INITIAL_NETWORK_TYPE}
71− initialWallet={INITIAL_WALLET}
74+ initialWallet={INITIAL_WALLET}
72− initialMnemonic={INITIAL_MNEMONIC}
75+ initialMnemonic={INITIAL_MNEMONIC}
73− initialPointCursor={INITIAL_POINT_CURSOR}>
76+ initialPointCursor={INITIAL_POINT_CURSOR}>
74− <IndigoApp>
77+ <IndigoApp>
75− <Router />
78+ <Router />
76− </IndigoApp>
79+ </IndigoApp>
77− </Provider>
80+ </Provider>
81+ </WithErrorBoundary>
78 );
82 );
79}
83}
80
84
src/components/GlobalErrorBoundary.jsnew file+72
@@ -0,0 +1,72 @@
1+import React, { useMemo } from 'react';
2+import newGithubIssueUrl from 'new-github-issue-url';
3+import { OutButton, RestartButton } from './Buttons';
4+
5+const buildTitle = error => `Error Report: ${error.message}`;
6+
7+const buildBody = error => `\n
8+<!-- Thanks for submitting this error! Can you help us identify the issue by providing additional context? -->
9+\n
10+## Context
11+\n
12+<!-- 1) What were you doing when you encountered this error? -->
13+\n
14+<!-- 2) Only if relevant (don't self-dox!), what point were you interacting with? -->
15+\n
16+## Error\n\n
17+\`\`\`
18+${error.stack}
19+\`\`\`
20+\n
21+`;
22+
23+/**
24+ * NOTE: we want to use as few normal components here as possible, since the
25+ * error might have originated from within those components, making this
26+ * whole error boundary useless.
27+ */
28+export default function GlobalErrorBoundary({ error }) {
29+ const url = useMemo(
30+ () =>
31+ newGithubIssueUrl({
32+ user: 'urbit',
33+ repo: 'bridge',
34+ labels: ['bug', 'error handling'],
35+ title: buildTitle(error),
36+ body: buildBody(error),
37+ }),
38+ [error]
39+ );
40+
41+ return (
42+ <div className="mw1 ph4">
43+ <h2 className="mt8">
44+ Bridge Error!{' '}
45+ <span role="img" aria-label="a bridge on fire">
46+ 🌉🔥
47+ </span>
48+ </h2>
49+ <p className="mt4 f5">
50+ We caught an error thrown by the Bridge application client-side code.
51+ <br />
52+ <b>Your points and assets are most likely safe.</b>
53+ </p>
54+ <p className="mt4">
55+ If you'd like to help us make Bridge better, you can submit the error
56+ and your description of events to our GitHub issue tracker.
57+ </p>
58+ <OutButton href={url} solid>
59+ Submit the Issue on GitHub
60+ </OutButton>
61+ <pre className="bg-gray1 mt4 p4 mono scroll-x">{error.stack}</pre>
62+ <p className="mt4">
63+ You can reload Bridge and retry the operation that caused an error. If
64+ the error persists, please submit a bug report and we'll work with you
65+ to resolve it!
66+ </p>
67+ <RestartButton onClick={() => document.location.reload()} solid success>
68+ Reload Bridge
69+ </RestartButton>
70+ </div>
71+ );
72+}
src/components/WithErrorBoundary.jsnew file+20
@@ -0,0 +1,20 @@
1+import React from 'react';
2+
3+export default class WithErrorBoundary extends React.Component {
4+ static getDerivedStateFromError(error) {
5+ return { error };
6+ }
7+ constructor(props) {
8+ super(props);
9+
10+ this.state = { error: null };
11+ }
12+
13+ render() {
14+ if (this.state.error) {
15+ return this.props.render(this.state.error);
16+ }
17+
18+ return this.props.children;
19+ }
20+}