From a38298fc428ce70a8053a8875aa72d6f4e82010e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yago=20P=C3=A9rez=20V=C3=A1zquez?= Date: Wed, 8 Jun 2022 09:12:36 +0200 Subject: [PATCH] feat: Add specific error boundary for safe apps (#3932) * Add specific error boundary for safe apps * Use render props for the error boundary UI --- .../Apps/components/SafeAppsErrorBoundary.tsx | 40 ++++++++++ .../Apps/components/SafeAppsLoadError.tsx | 78 +++++++++++++++++++ src/routes/safe/components/Apps/index.tsx | 9 ++- 3 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 src/routes/safe/components/Apps/components/SafeAppsErrorBoundary.tsx create mode 100644 src/routes/safe/components/Apps/components/SafeAppsLoadError.tsx diff --git a/src/routes/safe/components/Apps/components/SafeAppsErrorBoundary.tsx b/src/routes/safe/components/Apps/components/SafeAppsErrorBoundary.tsx new file mode 100644 index 0000000000..f78af46e34 --- /dev/null +++ b/src/routes/safe/components/Apps/components/SafeAppsErrorBoundary.tsx @@ -0,0 +1,40 @@ +import React, { ReactNode, ErrorInfo } from 'react' + +type SafeAppsErrorBoundaryProps = { + children?: ReactNode + render: () => ReactNode +} + +type SafeAppsErrorBoundaryState = { + hasError: boolean + error?: Error +} + +class SafeAppsErrorBoundary extends React.Component { + public state: SafeAppsErrorBoundaryState = { + hasError: false, + } + + constructor(props: SafeAppsErrorBoundaryProps) { + super(props) + this.state = { hasError: false } + } + + public componentDidCatch(error: Error, errorInfo: ErrorInfo): void { + console.error('Uncaught error:', error, errorInfo) + } + + public static getDerivedStateFromError(error: Error): SafeAppsErrorBoundaryState { + return { hasError: true, error } + } + + public render(): React.ReactNode { + if (this.state.hasError) { + return this.props.render() + } + + return this.props.children + } +} + +export default SafeAppsErrorBoundary diff --git a/src/routes/safe/components/Apps/components/SafeAppsLoadError.tsx b/src/routes/safe/components/Apps/components/SafeAppsLoadError.tsx new file mode 100644 index 0000000000..9e99232c1e --- /dev/null +++ b/src/routes/safe/components/Apps/components/SafeAppsLoadError.tsx @@ -0,0 +1,78 @@ +import { useSelector } from 'react-redux' +import { useHistory } from 'react-router-dom' +import { currentSession } from 'src/logic/currentSession/store/selectors' +import styled from 'styled-components' +import { Text, Link, Icon, FixedIcon, Title } from '@gnosis.pm/safe-react-components' +import { generateSafeRoute, SAFE_ROUTES } from 'src/routes/routes' + +const SafeAppsLoadError = (): React.ReactElement => { + const history = useHistory() + const { currentShortName, currentSafeAddress } = useSelector(currentSession) + const handleGoBack = () => { + history.push( + generateSafeRoute(SAFE_ROUTES.APPS, { + safeAddress: currentSafeAddress, + shortName: currentShortName, + }), + ) + } + + return ( + + + Safe App could not be loaded. + + +
+ + In case the problem persists, please reach out to us via{' '} + + + + + Discord + + + + +
+ + + Go back to the Safe Apps list + +
+
+ ) +} + +const Wrapper = styled.div` + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +` + +const Content = styled.div` + width: 400px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + + > * { + margin-top: 10px; + } +` + +const LinkWrapper = styled.div` + display: inline-flex; + margin-bottom: 10px; + + > :first-of-type { + margin-right: 5px; + } +` + +export default SafeAppsLoadError diff --git a/src/routes/safe/components/Apps/index.tsx b/src/routes/safe/components/Apps/index.tsx index acb9ff7255..f14ee9f392 100644 --- a/src/routes/safe/components/Apps/index.tsx +++ b/src/routes/safe/components/Apps/index.tsx @@ -1,10 +1,11 @@ import { useHistory } from 'react-router-dom' - import { useSafeAppUrl } from 'src/logic/hooks/useSafeAppUrl' import AppFrame from 'src/routes/safe/components/Apps/components/AppFrame' import AppsList from 'src/routes/safe/components/Apps/components/AppsList' import LegalDisclaimer from 'src/routes/safe/components/Apps/components/LegalDisclaimer' import { useLegalConsent } from 'src/routes/safe/components/Apps/hooks/useLegalConsent' +import SafeAppsErrorBoundary from './components/SafeAppsErrorBoundary' +import SafeAppsLoadError from './components/SafeAppsLoadError' const Apps = (): React.ReactElement => { const history = useHistory() @@ -19,7 +20,11 @@ const Apps = (): React.ReactElement => { return } - return + return ( + }> + + + ) } else { return }