From 91cfad0c0e6538da4008e118df3ae6939a157c04 Mon Sep 17 00:00:00 2001 From: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Date: Sat, 10 Oct 2020 10:42:35 +0200 Subject: [PATCH] Add error boundaries with fallback component to show any error (#471) * add error boundaries with fallback component * use own ErrorBoundaries * fix build * simplistic implementation * Update packages/extension-ui/src/components/ErrorBoundary.tsx * keep fragments * at the root only * weird svg error * double click needed * remove trigger * demove errorDisplay and move goHome to class * cleanup and button * Update packages/extension-ui/src/Popup/ImportSeed.tsx * use trigger * remove intentionnal error * Update packages/extension-ui/src/Popup/index.tsx * Update packages/extension-ui/src/components/ErrorBoundary.tsx * fix build * Update packages/extension-ui/src/Popup/index.tsx Co-authored-by: Jaco Greeff Co-authored-by: Jaco Greeff --- .gitignore | 2 +- packages/extension-ui/src/Popup/index.tsx | 34 +++++---- .../src/components/ErrorBoundary.tsx | 74 +++++++++++++++++++ packages/extension-ui/src/components/index.ts | 1 + .../extension-ui/src/components/translate.ts | 10 +++ packages/extension-ui/src/partials/Header.tsx | 3 +- 6 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 packages/extension-ui/src/components/ErrorBoundary.tsx create mode 100644 packages/extension-ui/src/components/translate.ts diff --git a/.gitignore b/.gitignore index ed7cfb49..19df2314 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,4 @@ yarn-error.log* package-lock.json _book docs/html -.idea \ No newline at end of file +.idea diff --git a/packages/extension-ui/src/Popup/index.tsx b/packages/extension-ui/src/Popup/index.tsx index 3b66230c..ece85658 100644 --- a/packages/extension-ui/src/Popup/index.tsx +++ b/packages/extension-ui/src/Popup/index.tsx @@ -9,7 +9,7 @@ import { Route, Switch } from 'react-router'; import uiSettings from '@polkadot/ui-settings'; import { setSS58Format } from '@polkadot/util-crypto'; -import { Loading } from '../components'; +import { Loading, ErrorBoundary } from '../components'; import { AccountContext, ActionContext, AuthorizeReqContext, MediaContext, MetadataReqContext, SettingsContext, SigningReqContext } from '../components/contexts'; import ToastProvider from '../components/Toast/ToastProvider'; import { subscribeAccounts, subscribeAuthorizeRequests, subscribeMetadataRequests, subscribeSigningRequests } from '../messaging'; @@ -103,15 +103,19 @@ export default function Popup (): React.ReactElement { .catch(console.error); }, [cameraOn]); + function wrapWithErrorBoundary (component: React.ReactElement, trigger?: string): React.ReactElement { + return {component}; + } + const Root = isWelcomeDone ? authRequests && authRequests.length - ? Authorize + ? wrapWithErrorBoundary(, 'authorize') : metaRequests && metaRequests.length - ? Metadata + ? wrapWithErrorBoundary(, 'metadata') : signRequests && signRequests.length - ? Signing - : Accounts - : Welcome; + ? wrapWithErrorBoundary(, 'signing') + : wrapWithErrorBoundary(, 'accounts') + : wrapWithErrorBoundary(, 'welcome'); return ( {accounts && authRequests && metaRequests && signRequests && ( @@ -124,19 +128,19 @@ export default function Popup (): React.ReactElement { - - - - - - - - + {wrapWithErrorBoundary(, 'account-creation')} + {wrapWithErrorBoundary(, 'forget-address')} + {wrapWithErrorBoundary(, 'export-address')} + {wrapWithErrorBoundary(, 'import-qr')} + {wrapWithErrorBoundary(, 'import-seed')} + {wrapWithErrorBoundary(, 'restore-json')} + {wrapWithErrorBoundary(, 'derived-address-locked')} + {wrapWithErrorBoundary(, 'derive-address')} - + {Root} diff --git a/packages/extension-ui/src/components/ErrorBoundary.tsx b/packages/extension-ui/src/components/ErrorBoundary.tsx new file mode 100644 index 00000000..2d93d7a7 --- /dev/null +++ b/packages/extension-ui/src/components/ErrorBoundary.tsx @@ -0,0 +1,74 @@ +// Copyright 2019-2020 @polkadot/extension-ui authors & contributor +// SPDX-License-Identifier: Apache-2.0 + +import { WithTranslation } from 'react-i18next'; + +import React from 'react'; + +import ButtonArea from './ButtonArea'; +import Button from './Button'; +import VerticalSpace from './VerticalSpace'; +import Header from '../partials/Header'; +import translate from './translate'; + +interface Props extends WithTranslation { + children: React.ReactNode; + className?: string; + error?: Error | null; + trigger?: string; +} + +interface State { + error: Error | null; +} + +// NOTE: This is the only way to do an error boundary, via extend +class ErrorBoundary extends React.Component { + state: State = { error: null }; + + static getDerivedStateFromError (error: Error): Partial { + return { error }; + } + + componentDidUpdate (prevProps: Props) { + const { error } = this.state; + const { trigger } = this.props; + + if (error !== null && (prevProps.trigger !== trigger)) { + this.setState({ error: null }); + } + } + + goHome = () => { + this.setState({ error: null }); + window.location.hash = '/'; + }; + + public render (): React.ReactNode { + const { children, t } = this.props; + const { error } = this.state; + + return error + ? ( + <> +
('An error occured')} /> +
+ {t('Something went wrong with the query and rendering of this component. {{message}}', { + replace: { message: error.message } + })} +
+ + + + + + ) + : children; + } +} + +export default translate(ErrorBoundary); diff --git a/packages/extension-ui/src/components/index.ts b/packages/extension-ui/src/components/index.ts index ef862f67..c95db831 100644 --- a/packages/extension-ui/src/components/index.ts +++ b/packages/extension-ui/src/components/index.ts @@ -11,6 +11,7 @@ export { default as ButtonArea } from './ButtonArea'; export { default as ButtonWithSubtitle } from './ButtonWithSubtitle'; export { default as Checkbox } from './Checkbox'; export { default as Dropdown } from './Dropdown'; +export { default as ErrorBoundary } from './ErrorBoundary'; export { default as Fonts } from './Fonts'; export { default as Icon } from './Icon'; export { default as Identicon } from './Identicon'; diff --git a/packages/extension-ui/src/components/translate.ts b/packages/extension-ui/src/components/translate.ts new file mode 100644 index 00000000..36438c12 --- /dev/null +++ b/packages/extension-ui/src/components/translate.ts @@ -0,0 +1,10 @@ +// Copyright 2019-2020 @polkadot/extension-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { useTranslation as useTranslationBase, UseTranslationResponse, withTranslation } from 'react-i18next'; + +export function useTranslation (): UseTranslationResponse { + return useTranslationBase(); +} + +export default withTranslation(); diff --git a/packages/extension-ui/src/partials/Header.tsx b/packages/extension-ui/src/partials/Header.tsx index eb9013a3..f52f4a43 100644 --- a/packages/extension-ui/src/partials/Header.tsx +++ b/packages/extension-ui/src/partials/Header.tsx @@ -10,7 +10,8 @@ import ArrowLeftImage from '../assets/arrowLeft.svg'; import gear from '../assets/gear.svg'; import plus from '../assets/plus.svg'; import logo from '../assets/pjs.svg'; -import { Link, Svg } from '../components'; +import Link from '../components/Link'; +import Svg from '../components/Svg'; import useOutsideClick from '../hooks/useOutsideClick'; import MenuAdd from './MenuAdd'; import MenuSettings from './MenuSettings';