Add additional error boundaries, improve display and reload behavior

This commit is contained in:
Tom Moor
2023-04-05 21:57:58 -04:00
parent 24729fa0d4
commit 1f3d7506d7
8 changed files with 45 additions and 25 deletions

View File

@@ -325,7 +325,7 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
);
return (
<ErrorBoundary reloadOnChunkMissing>
<ErrorBoundary component="div" reloadOnChunkMissing>
<>
<LazyLoadedEditor
ref={mergeRefs([ref, localRef, handleRefChanged])}

View File

@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import * as React from "react";
import { withTranslation, Trans, WithTranslation } from "react-i18next";
import styled from "styled-components";
import { githubIssuesUrl } from "@shared/utils/urlHelpers";
import { githubIssuesUrl, feedbackUrl } from "@shared/utils/urlHelpers";
import Button from "~/components/Button";
import CenteredContent from "~/components/CenteredContent";
import PageTitle from "~/components/PageTitle";
@@ -13,7 +13,12 @@ import Logger from "~/utils/Logger";
import isCloudHosted from "~/utils/isCloudHosted";
type Props = WithTranslation & {
/** Whether to reload the page if a chunk fails to load. */
reloadOnChunkMissing?: boolean;
/** Whether to show a title heading. */
showTitle?: boolean;
/** The wrapping component to use. */
component?: React.ComponentType | string;
};
@observer
@@ -51,26 +56,31 @@ class ErrorBoundary extends React.Component<Props> {
};
handleReportBug = () => {
window.open(githubIssuesUrl());
window.open(isCloudHosted ? feedbackUrl() : githubIssuesUrl());
};
render() {
const { t } = this.props;
const { t, component: Component = CenteredContent, showTitle } = this.props;
if (this.error) {
const error = this.error;
const isReported = !!env.SENTRY_DSN && isCloudHosted;
const isChunkError = this.error.message.match(
/dynamically imported module/
);
const isChunkError = [
"module script failed",
"dynamically imported module",
].some((msg) => this.error?.message?.includes(msg));
if (isChunkError) {
return (
<CenteredContent>
<PageTitle title={t("Module failed to load")} />
<h1>
<Trans>Loading Failed</Trans>
</h1>
<Component>
{showTitle && (
<>
<PageTitle title={t("Module failed to load")} />
<h1>
<Trans>Loading Failed</Trans>
</h1>
</>
)}
<Text type="secondary">
<Trans>
Sorry, part of the application failed to load. This may be
@@ -81,16 +91,20 @@ class ErrorBoundary extends React.Component<Props> {
<p>
<Button onClick={this.handleReload}>{t("Reload")}</Button>
</p>
</CenteredContent>
</Component>
);
}
return (
<CenteredContent>
<PageTitle title={t("Something Unexpected Happened")} />
<h1>
<Trans>Something Unexpected Happened</Trans>
</h1>
<Component>
{showTitle && (
<>
<PageTitle title={t("Something Unexpected Happened")} />
<h1>
<Trans>Something Unexpected Happened</Trans>
</h1>
</>
)}
<Text type="secondary">
<Trans
defaults="Sorry, an unrecoverable error occurred{{notified}}. Please try reloading the page, it may have been a temporary glitch."
@@ -114,7 +128,7 @@ class ErrorBoundary extends React.Component<Props> {
</Button>
)}
</p>
</CenteredContent>
</Component>
);
}

View File

@@ -10,6 +10,7 @@ const Flex = styled.div<{
column?: boolean;
align?: AlignValues;
justify?: JustifyValues;
wrap?: boolean;
shrink?: boolean;
reverse?: boolean;
gap?: number;
@@ -26,6 +27,7 @@ const Flex = styled.div<{
: "row"};
align-items: ${({ align }) => align};
justify-content: ${({ justify }) => justify};
flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "initial")};
flex-shrink: ${({ shrink }) => (shrink ? 1 : "initial")};
gap: ${({ gap }) => (gap ? `${gap}px` : "initial")};
min-height: 0;

View File

@@ -30,7 +30,7 @@ export default function LoadingError({ error, retry, ...rest }: Props) {
return (
<Content {...rest}>
<Flex align="center" gap={4}>
<Flex align="center" gap={4} wrap>
{message}{" "}
<ButtonLink onClick={() => retry()}>{t("Click to retry")}</ButtonLink>
</Flex>

View File

@@ -16,6 +16,7 @@ import usePrevious from "~/hooks/usePrevious";
import useUnmount from "~/hooks/useUnmount";
import { fadeAndScaleIn } from "~/styles/animations";
import Desktop from "~/utils/Desktop";
import ErrorBoundary from "./ErrorBoundary";
let openModals = 0;
type Props = {
@@ -82,7 +83,9 @@ const Modal: React.FC<Props> = ({
column
reverse
>
<SmallContent shadow>{children}</SmallContent>
<SmallContent shadow>
<ErrorBoundary component="div">{children}</ErrorBoundary>
</SmallContent>
<Header>
{title && (
<Text as="span" size="large">
@@ -112,7 +115,7 @@ const Modal: React.FC<Props> = ({
<Content>
<Centered onClick={(ev) => ev.stopPropagation()} column>
{title && <h1>{title}</h1>}
{children}
<ErrorBoundary>{children}</ErrorBoundary>
</Centered>
</Content>
<Close onClick={onRequestClose}>

View File

@@ -4,6 +4,7 @@ import * as React from "react";
import styled, { useTheme } from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { depths } from "@shared/styles";
import ErrorBoundary from "~/components/ErrorBoundary";
import Flex from "~/components/Flex";
import ResizeBorder from "~/components/Sidebar/components/ResizeBorder";
import useMobile from "~/hooks/useMobile";
@@ -94,7 +95,7 @@ function Right({ children, border, className }: Props) {
className={className}
>
<Position style={style} column>
{children}
<ErrorBoundary>{children}</ErrorBoundary>
{!isMobile && (
<ResizeBorder
onMouseDown={handleMouseDown}

View File

@@ -53,7 +53,7 @@ if (element) {
<Provider {...stores}>
<Analytics>
<Theme>
<ErrorBoundary>
<ErrorBoundary showTitle>
<KBarProvider actions={[]} options={commandBarOptions}>
<LazyPolyfill>
<LazyMotion features={loadFeatures}>

View File

@@ -400,7 +400,7 @@ class DocumentScene extends React.Component<Props> {
: updateDocumentUrl(this.props.match.url, document);
return (
<ErrorBoundary>
<ErrorBoundary showTitle>
{this.props.location.pathname !== canonicalUrl && (
<Redirect
to={{