import React from "react"; import * as Sentry from "@sentry/browser"; import { t } from "../../packages/excalidraw/i18n"; import Trans from "../../packages/excalidraw/components/Trans"; 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) { 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}`, "_blank", "noopener noreferrer", ); } private errorSplash() { return (
( )} />
( )} />
⚠️ {t("errorSplash.clearCanvasCaveat")}
{t("errorSplash.trackedToSentry", { eventId: this.state.sentryEventId, })}
( )} />