excalidraw/src/components/TopErrorBoundary.tsx

137 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from "react";
import * as Sentry from "@sentry/browser";
import { t } from "../i18n";
interface TopErrorBoundaryState {
hasError: boolean;
sentryEventId: string;
localStorage: string;
}
export class TopErrorBoundary extends React.Component<
any,
TopErrorBoundaryState
> {
state: TopErrorBoundaryState = {
hasError: false,
sentryEventId: "",
localStorage: "",
};
render() {
return this.state.hasError ? this.errorSplash() : this.props.children;
}
componentDidCatch(error: Error, errorInfo: any) {
const _localStorage: any = {};
for (const [key, value] of Object.entries({ ...localStorage })) {
try {
_localStorage[key] = JSON.parse(value);
} catch (error: any) {
_localStorage[key] = value;
}
}
Sentry.withScope((scope) => {
scope.setExtras(errorInfo);
const eventId = Sentry.captureException(error);
this.setState((state) => ({
hasError: true,
sentryEventId: eventId,
localStorage: JSON.stringify(_localStorage),
}));
});
}
private selectTextArea(event: React.MouseEvent<HTMLTextAreaElement>) {
if (event.target !== document.activeElement) {
event.preventDefault();
(event.target as HTMLTextAreaElement).select();
}
}
private async createGithubIssue() {
let body = "";
try {
const templateStrFn = (
await import(
/* webpackChunkName: "bug-issue-template" */ "../bug-issue-template"
)
).default;
body = encodeURIComponent(templateStrFn(this.state.sentryEventId));
} catch (error: any) {
console.error(error);
}
window.open(
`https://github.com/excalidraw/excalidraw/issues/new?body=${body}`,
);
}
private errorSplash() {
return (
<div className="ErrorSplash excalidraw">
<div className="ErrorSplash-messageContainer">
<div className="ErrorSplash-paragraph bigger align-center">
{t("errorSplash.headingMain_pre")}
<button onClick={() => window.location.reload()}>
{t("errorSplash.headingMain_button")}
</button>
</div>
<div className="ErrorSplash-paragraph align-center">
{t("errorSplash.clearCanvasMessage")}
<button
onClick={() => {
try {
localStorage.clear();
window.location.reload();
} catch (error: any) {
console.error(error);
}
}}
>
{t("errorSplash.clearCanvasMessage_button")}
</button>
<br />
<div className="smaller">
<span role="img" aria-label="warning">
</span>
{t("errorSplash.clearCanvasCaveat")}
<span role="img" aria-hidden="true">
</span>
</div>
</div>
<div>
<div className="ErrorSplash-paragraph">
{t("errorSplash.trackedToSentry_pre")}
{this.state.sentryEventId}
{t("errorSplash.trackedToSentry_post")}
</div>
<div className="ErrorSplash-paragraph">
{t("errorSplash.openIssueMessage_pre")}
<button onClick={() => this.createGithubIssue()}>
{t("errorSplash.openIssueMessage_button")}
</button>
{t("errorSplash.openIssueMessage_post")}
</div>
<div className="ErrorSplash-paragraph">
<div className="ErrorSplash-details">
<label>{t("errorSplash.sceneContent")}</label>
<textarea
rows={5}
onPointerDown={this.selectTextArea}
readOnly={true}
value={this.state.localStorage}
/>
</div>
</div>
</div>
</div>
</div>
);
}
}