excalidraw/src/components/TopErrorBoundary.tsx

137 lines
3.9 KiB
TypeScript
Raw Normal View History

import React from "react";
2020-03-31 09:37:51 +01:00
import * as Sentry from "@sentry/browser";
import { t } from "../i18n";
interface TopErrorBoundaryState {
hasError: boolean;
2020-03-31 09:37:51 +01:00
sentryEventId: string;
localStorage: string;
}
export class TopErrorBoundary extends React.Component<
any,
TopErrorBoundaryState
> {
state: TopErrorBoundaryState = {
hasError: false,
2020-03-31 09:37:51 +01:00
sentryEventId: "",
localStorage: "",
};
render() {
return this.state.hasError ? this.errorSplash() : this.props.children;
}
2020-03-31 09:37:51 +01:00
componentDidCatch(error: Error, errorInfo: any) {
const _localStorage: any = {};
for (const [key, value] of Object.entries({ ...localStorage })) {
try {
_localStorage[key] = JSON.parse(value);
} catch (error) {
_localStorage[key] = value;
}
}
2020-03-31 09:37:51 +01:00
Sentry.withScope((scope) => {
scope.setExtras(errorInfo);
const eventId = Sentry.captureException(error);
this.setState((state) => ({
2020-03-31 09:37:51 +01:00
hasError: true,
sentryEventId: eventId,
localStorage: JSON.stringify(_localStorage),
}));
2020-03-31 09:37:51 +01:00
});
}
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;
2020-03-31 09:37:51 +01:00
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 (
<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) {
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>
2020-03-31 09:37:51 +01:00
<div className="ErrorSplash-paragraph">
{t("errorSplash.trackedToSentry_pre")}
{this.state.sentryEventId}
{t("errorSplash.trackedToSentry_post")}
</div>
<div className="ErrorSplash-paragraph">
{t("errorSplash.openIssueMessage_pre")}
2020-03-31 09:37:51 +01:00
<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>
);
}
}