mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
react boundary
This commit is contained in:
1 parent
2b955a281b
commit
5ea7bbd6a1
7 files changed
+139
-108
No files matched your search
@@ -1,12 +1,4 @@
|
|||||||
{
|
{
|
||||||
"error": {
|
|
||||||
"heading": "BlitzKit ran into a critical error",
|
|
||||||
"message": "Sorry about the inconvenience! You have a couple of options.",
|
|
||||||
"view": "View error",
|
|
||||||
"reload": "Reload",
|
|
||||||
"support": "Support"
|
|
||||||
},
|
|
||||||
|
|
||||||
"nations": {
|
"nations": {
|
||||||
"0": "Unspecified",
|
"0": "Unspecified",
|
||||||
"1": "USSR",
|
"1": "USSR",
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
---
|
|
||||||
import { api } from "../api/dynamic";
|
|
||||||
import { discordLink } from "../config/socials";
|
|
||||||
import { Button } from "./Button";
|
|
||||||
import { Heading } from "./Heading";
|
|
||||||
import { Link } from "./Link";
|
|
||||||
import { Text } from "./Text";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
locale: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { locale } = Astro.props;
|
|
||||||
const strings = await api.strings(locale);
|
|
||||||
---
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.error-display {
|
|
||||||
display: none;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
gap: var(--space-4);
|
|
||||||
padding: var(--space-4);
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100dvw;
|
|
||||||
height: 100dvh;
|
|
||||||
|
|
||||||
background-color: var(--gray-1);
|
|
||||||
|
|
||||||
body.error & {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.options {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--space-3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="error-display">
|
|
||||||
<div class="info">
|
|
||||||
<Heading size="2">{strings.error.heading}</Heading>
|
|
||||||
<Text lowContrast>{strings.error.message}</Text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="options">
|
|
||||||
<Button variant="outline">{strings.error.view}</Button>
|
|
||||||
|
|
||||||
<Link href="" underline="never">
|
|
||||||
<Button variant="outline">{strings.error.reload}</Button>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link href={discordLink} target="_blank" underline="never">
|
|
||||||
<Button>{strings.error.support}</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import type { ReactNode } from "react";
|
|
||||||
import { ErrorBoundary } from "react-error-boundary";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
children: ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ErrorWrapper({ children }: Props) {
|
|
||||||
return (
|
|
||||||
<ErrorBoundary
|
|
||||||
fallback={null}
|
|
||||||
onError={() => {
|
|
||||||
console.log("what?");
|
|
||||||
document.body.classList.add("error");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</ErrorBoundary>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
.error-display {
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
gap: var(--space-4);
|
||||||
|
padding: var(--space-4);
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100dvw;
|
||||||
|
height: 100dvh;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
background-color: var(--gray-1);
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
text-align: left;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
max-width: min(100%, var(--page-width));
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
width: 25%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
background: linear-gradient(90deg, transparent, var(--gray-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import { useState, type ReactNode } from "react";
|
||||||
|
import {
|
||||||
|
ErrorBoundary,
|
||||||
|
getErrorMessage,
|
||||||
|
type FallbackProps,
|
||||||
|
} from "react-error-boundary";
|
||||||
|
import { discordLink } from "../../config/socials";
|
||||||
|
import { Button } from "../Button";
|
||||||
|
import { Code } from "../Code";
|
||||||
|
import { Heading } from "../Heading";
|
||||||
|
import { Link } from "../Link";
|
||||||
|
import { Text } from "../Text";
|
||||||
|
import styles from "./index.module.css";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ErrorWrapper({ children }: Props) {
|
||||||
|
return (
|
||||||
|
<ErrorBoundary fallbackRender={(props) => <Fallback {...props} />}>
|
||||||
|
{children}
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Fallback({ error, resetErrorBoundary }: FallbackProps) {
|
||||||
|
const [showError, setShowError] = useState(false);
|
||||||
|
|
||||||
|
let body: string | undefined;
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
body = error.stack || getErrorMessage(error);
|
||||||
|
} else {
|
||||||
|
body = getErrorMessage(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
body ??= "No error message";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles["error-display"]}>
|
||||||
|
<div className={styles.info}>
|
||||||
|
<Heading size="3">BlitzKit crashed</Heading>
|
||||||
|
<Text lowContrast>Sorry about that! You have a couple of options.</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showError && (
|
||||||
|
<Code variant="solid" className={styles.error}>
|
||||||
|
{body}
|
||||||
|
<div className={styles.cover} />
|
||||||
|
</Code>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={styles.options}>
|
||||||
|
{!showError && (
|
||||||
|
<Button variant="outline" onClick={() => setShowError(true)}>
|
||||||
|
View error
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showError && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => navigator.clipboard.writeText(body)}
|
||||||
|
>
|
||||||
|
Copy error
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Link href="" underline="never">
|
||||||
|
<Button variant="outline">Hard reload</Button>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Button variant="outline" onClick={resetErrorBoundary}>
|
||||||
|
Soft reset
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Link href={discordLink} target="_blank" underline="never">
|
||||||
|
<Button>Discord server</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -136,10 +136,6 @@ function EquipmentOption({
|
|||||||
state.protagonist.equipment[equipmentIndex] === optionIndex,
|
state.protagonist.equipment[equipmentIndex] === optionIndex,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
throw "test error";
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
color={isSelected ? undefined : "gray"}
|
color={isSelected ? undefined : "gray"}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
---
|
---
|
||||||
import ErrorDisplay from "../components/ErrorDisplay.astro";
|
|
||||||
import { api } from "../api/dynamic";
|
|
||||||
import BaseMeta from "../components/BaseMeta.astro";
|
import BaseMeta from "../components/BaseMeta.astro";
|
||||||
import BaseStyles from "../components/BaseStyles.astro";
|
import BaseStyles from "../components/BaseStyles.astro";
|
||||||
import ColorAlias from "../components/ColorAlias.astro";
|
import ColorAlias from "../components/ColorAlias.astro";
|
||||||
@@ -79,8 +77,6 @@ const { accent, locale } = Astro.props;
|
|||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ErrorDisplay {locale} />
|
|
||||||
|
|
||||||
<Footer {locale} />
|
<Footer {locale} />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in new issue
Block a user