|
|
import React from "react"; |
|
|
import { IconButton } from "./button"; |
|
|
import GithubIcon from "../icons/github.svg"; |
|
|
import ResetIcon from "../icons/reload.svg"; |
|
|
import { ISSUE_URL } from "../constant"; |
|
|
import Locale from "../locales"; |
|
|
import { downloadAs } from "../utils"; |
|
|
|
|
|
interface IErrorBoundaryState { |
|
|
hasError: boolean; |
|
|
error: Error | null; |
|
|
info: React.ErrorInfo | null; |
|
|
} |
|
|
|
|
|
export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> { |
|
|
constructor(props: any) { |
|
|
super(props); |
|
|
this.state = { hasError: false, error: null, info: null }; |
|
|
} |
|
|
|
|
|
componentDidCatch(error: Error, info: React.ErrorInfo) { |
|
|
|
|
|
this.setState({ hasError: true, error, info }); |
|
|
} |
|
|
|
|
|
clearAndSaveData() { |
|
|
try { |
|
|
downloadAs( |
|
|
JSON.stringify(localStorage), |
|
|
"chatgpt-next-web-snapshot.json", |
|
|
); |
|
|
} finally { |
|
|
localStorage.clear(); |
|
|
location.reload(); |
|
|
} |
|
|
} |
|
|
|
|
|
render() { |
|
|
if (this.state.hasError) { |
|
|
|
|
|
return ( |
|
|
<div className="error"> |
|
|
<h2>Oops, something went wrong!</h2> |
|
|
<pre> |
|
|
<code>{this.state.error?.toString()}</code> |
|
|
<code>{this.state.info?.componentStack}</code> |
|
|
</pre> |
|
|
|
|
|
<div style={{ display: "flex", justifyContent: "space-between" }}> |
|
|
<a href={ISSUE_URL} className="report"> |
|
|
<IconButton |
|
|
text="Report This Error" |
|
|
icon={<GithubIcon />} |
|
|
bordered |
|
|
/> |
|
|
</a> |
|
|
<IconButton |
|
|
icon={<ResetIcon />} |
|
|
text="Clear All Data" |
|
|
onClick={() => |
|
|
confirm(Locale.Settings.Actions.ConfirmClearAll) && |
|
|
this.clearAndSaveData() |
|
|
} |
|
|
bordered |
|
|
/> |
|
|
</div> |
|
|
</div> |
|
|
); |
|
|
} |
|
|
|
|
|
return this.props.children; |
|
|
} |
|
|
} |
|
|
|