feat: add global error handling and reporting

This commit is contained in:
Paul
2021-12-25 14:32:28 +00:00
parent 56126fa303
commit de1b947b7d
5 changed files with 139 additions and 27 deletions

63
src/lib/ErrorBoundary.tsx Normal file
View File

@@ -0,0 +1,63 @@
import axios from "axios";
import * as stackTrace from "stacktrace-js";
import styled from "styled-components";
import { useEffect, useErrorBoundary } from "preact/hooks";
import { GIT_REVISION } from "../revision";
import { Children } from "../types/Preact";
const CrashContainer = styled.div`
height: 100%;
padding: 12px;
background: black;
color: white;
h3 {
margin: 0;
margin-bottom: 12px;
}
`;
interface Props {
children: Children;
}
const ERROR_URL = "https://reporting.revolt.chat";
export default function ErrorBoundary({ children }: Props) {
const [error, ignoreError] = useErrorBoundary();
useEffect(() => {
if (error) {
stackTrace.fromError(error).then((stackframes) =>
axios.post(ERROR_URL, {
stackframes,
rawStackTrace: error.stack,
origin: window.origin,
commitSHA: GIT_REVISION,
userAgent: navigator.userAgent,
}),
);
}
}, [error]);
if (error) {
return (
<CrashContainer>
<h3>Client Crash Report</h3>
<button onClick={ignoreError}>
Ignore error and try to reload app
</button>
<button onClick={() => location.reload()}>Refresh page</button>
<pre>
<code>{error?.stack}</code>
</pre>
<div>This error has been automatically reported.</div>
</CrashContainer>
);
}
return <>{children}</>;
}

View File

@@ -2,6 +2,8 @@ import { Route, Switch } from "react-router-dom";
import { lazy, Suspense } from "preact/compat";
import ErrorBoundary from "../lib/ErrorBoundary";
import Context from "../context";
import { CheckAuth } from "../context/revoltjs/CheckAuth";
@@ -15,33 +17,35 @@ const RevoltApp = lazy(() => import("./RevoltApp"));
export function App() {
return (
<Context>
<Masks />
{/*
// @ts-expect-error typings mis-match between preact... and preact? */}
<Suspense fallback={<Preloader type="spinner" />}>
<Switch>
<Route path="/login/verify/:token">
<Login />
</Route>
<Route path="/login/reset/:token">
<Login />
</Route>
<Route path="/invite/:code">
<Invite />
</Route>
<Route path="/login">
<CheckAuth>
<ErrorBoundary>
<Context>
<Masks />
{/*
// @ts-expect-error typings mis-match between preact... and preact? */}
<Suspense fallback={<Preloader type="spinner" />}>
<Switch>
<Route path="/login/verify/:token">
<Login />
</CheckAuth>
</Route>
<Route path="/">
<CheckAuth auth>
<RevoltApp />
</CheckAuth>
</Route>
</Switch>
</Suspense>
</Context>
</Route>
<Route path="/login/reset/:token">
<Login />
</Route>
<Route path="/invite/:code">
<Invite />
</Route>
<Route path="/login">
<CheckAuth>
<Login />
</CheckAuth>
</Route>
<Route path="/">
<CheckAuth auth>
<RevoltApp />
</CheckAuth>
</Route>
</Switch>
</Suspense>
</Context>
</ErrorBoundary>
);
}

View File

@@ -15,6 +15,7 @@ export default function Developer() {
const client = useContext(AppContext);
const userPermission = client.user!.permission;
const [ping, setPing] = useState<undefined | number>(client.websocket.ping);
const [crash, setCrash] = useState(false);
useEffect(() => {
const timer = setInterval(
@@ -44,6 +45,8 @@ export default function Developer() {
/>
</div>
<div style={{ padding: "16px" }}>
<a onClick={() => setCrash(true)}>click to crash app</a>
{crash && (window as any).sus.sus()}
{/*<span>
<b>Voice Status:</b> {VoiceStatus[voice.status]}
</span>