import React from "react"; import * as Sentry from "@sentry/browser"; import { resetCursor } from "../utils"; 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) { resetCursor(); const _localStorage: any = {}; for (const [key, value] of Object.entries({ ...localStorage })) { try { _localStorage[key] = JSON.parse(value); } catch (error) { _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) { 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) { console.error(error); } window.open( `https://github.com/excalidraw/excalidraw/issues/new?body=${body}`, ); } private errorSplash() { return (
{t("errorSplash.headingMain_pre")}
{t("errorSplash.clearCanvasMessage")}
⚠️ {t("errorSplash.clearCanvasCaveat")}
{t("errorSplash.trackedToSentry_pre")} {this.state.sentryEventId} {t("errorSplash.trackedToSentry_post")}
{t("errorSplash.openIssueMessage_pre")} {t("errorSplash.openIssueMessage_post")}