mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-07 01:15:28 +00:00
feat: add global error handling and reporting
This commit is contained in:
63
src/lib/ErrorBoundary.tsx
Normal file
63
src/lib/ErrorBoundary.tsx
Normal 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}</>;
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user