14 changed files with 237 additions and 276 deletions
@ -1,114 +0,0 @@
@@ -1,114 +0,0 @@
|
||||
import { Component } from "react"; |
||||
import { Trans, useTranslation } from "react-i18next"; |
||||
|
||||
import { IconPatch } from "@/components/buttons/IconPatch"; |
||||
import { Icons } from "@/components/Icon"; |
||||
import { Link } from "@/components/text/Link"; |
||||
import { Title } from "@/components/text/Title"; |
||||
import { conf } from "@/setup/config"; |
||||
|
||||
interface ErrorShowcaseProps { |
||||
error: { |
||||
name: string; |
||||
description: string; |
||||
path: string; |
||||
}; |
||||
} |
||||
|
||||
export function ErrorShowcase(props: ErrorShowcaseProps) { |
||||
return ( |
||||
<div className="w-4xl mt-12 max-w-full rounded bg-denim-300 px-6 py-4"> |
||||
<p className="mb-1 break-words font-bold text-white"> |
||||
{props.error.name} - {props.error.description} |
||||
</p> |
||||
<p className="break-words">{props.error.path}</p> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
interface ErrorMessageProps { |
||||
error?: { |
||||
name: string; |
||||
description: string; |
||||
path: string; |
||||
}; |
||||
localSize?: boolean; |
||||
children?: React.ReactNode; |
||||
} |
||||
|
||||
export function ErrorMessage(props: ErrorMessageProps) { |
||||
const { t } = useTranslation(); |
||||
|
||||
return ( |
||||
<div |
||||
className={`${ |
||||
props.localSize ? "h-full" : "min-h-screen" |
||||
} flex w-full flex-col items-center justify-center px-4 py-12`}
|
||||
> |
||||
<div className="flex flex-col items-center justify-start text-center"> |
||||
<IconPatch icon={Icons.WARNING} className="mb-6 text-red-400" /> |
||||
<Title>{t("media.errors.genericTitle")}</Title> |
||||
{props.children ? ( |
||||
<p className="my-6 max-w-lg">{props.children}</p> |
||||
) : ( |
||||
<p className="my-6 max-w-lg"> |
||||
<Trans i18nKey="media.errors.videoFailed"> |
||||
<Link url={conf().DISCORD_LINK} newTab /> |
||||
<Link url={conf().GITHUB_LINK} newTab /> |
||||
</Trans> |
||||
</p> |
||||
)} |
||||
</div> |
||||
{props.error ? <ErrorShowcase error={props.error} /> : null} |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
interface ErrorBoundaryState { |
||||
hasError: boolean; |
||||
error?: { |
||||
name: string; |
||||
description: string; |
||||
path: string; |
||||
}; |
||||
} |
||||
|
||||
export class ErrorBoundary extends Component< |
||||
Record<string, unknown>, |
||||
ErrorBoundaryState |
||||
> { |
||||
constructor(props: { children: any }) { |
||||
super(props); |
||||
this.state = { |
||||
hasError: false, |
||||
}; |
||||
} |
||||
|
||||
static getDerivedStateFromError() { |
||||
return { |
||||
hasError: true, |
||||
}; |
||||
} |
||||
|
||||
componentDidCatch(error: any, errorInfo: any) { |
||||
console.error("Render error caught", error, errorInfo); |
||||
if (error instanceof Error) { |
||||
const realError: Error = error as Error; |
||||
this.setState((s) => ({ |
||||
...s, |
||||
hasError: true, |
||||
error: { |
||||
name: realError.name, |
||||
description: realError.message, |
||||
path: errorInfo.componentStack.split("\n")[1], |
||||
}, |
||||
})); |
||||
} |
||||
} |
||||
|
||||
render() { |
||||
if (!this.state.hasError) return this.props.children as any; |
||||
|
||||
return <ErrorMessage error={this.state.error} />; |
||||
} |
||||
} |
@ -1,80 +1,12 @@
@@ -1,80 +1,12 @@
|
||||
import { OverlayAnchor } from "@/components/overlays/OverlayAnchor"; |
||||
import { Overlay, OverlayDisplay } from "@/components/overlays/OverlayDisplay"; |
||||
import { OverlayPage } from "@/components/overlays/OverlayPage"; |
||||
import { OverlayRouter } from "@/components/overlays/OverlayRouter"; |
||||
import { useOverlayRouter } from "@/hooks/useOverlayRouter"; |
||||
import { useState } from "react"; |
||||
|
||||
// simple empty view, perfect for putting in tests
|
||||
import { Button } from "@/components/Button"; |
||||
|
||||
// mostly empty view, add whatever you need
|
||||
export default function TestView() { |
||||
const router = useOverlayRouter("test"); |
||||
const [val, setVal] = useState(false); |
||||
|
||||
if (val) throw new Error("I crashed"); |
||||
|
||||
return ( |
||||
<OverlayDisplay> |
||||
<div className="h-[400px] w-[800px] flex justify-center items-center"> |
||||
<button |
||||
type="button" |
||||
onClick={() => { |
||||
router.open(); |
||||
}} |
||||
> |
||||
Open |
||||
</button> |
||||
<OverlayAnchor |
||||
id={router.id} |
||||
className="h-20 w-20 hover:w-24 mt-[50rem] bg-white" |
||||
/> |
||||
<Overlay id={router.id}> |
||||
<OverlayRouter id={router.id}> |
||||
<OverlayPage id={router.id} path="/" width={400} height={400}> |
||||
<div className="bg-blue-900 p-4"> |
||||
<p>HOME</p> |
||||
<button |
||||
type="button" |
||||
onClick={() => { |
||||
router.navigate("/two"); |
||||
}} |
||||
> |
||||
open page two |
||||
</button> |
||||
<button |
||||
type="button" |
||||
onClick={() => { |
||||
router.navigate("/one"); |
||||
}} |
||||
> |
||||
open page one |
||||
</button> |
||||
</div> |
||||
</OverlayPage> |
||||
<OverlayPage id={router.id} path="/one" width={300} height={300}> |
||||
<div className="bg-blue-900 p-4"> |
||||
<p>ONE</p> |
||||
<button |
||||
type="button" |
||||
onClick={() => { |
||||
router.navigate("/"); |
||||
}} |
||||
> |
||||
back home |
||||
</button> |
||||
</div> |
||||
</OverlayPage> |
||||
<OverlayPage id={router.id} path="/two" width={200} height={200}> |
||||
<div className="bg-blue-900 p-4"> |
||||
<p>TWO</p> |
||||
<button |
||||
type="button" |
||||
onClick={() => { |
||||
router.navigate("/"); |
||||
}} |
||||
> |
||||
back home |
||||
</button> |
||||
</div> |
||||
</OverlayPage> |
||||
</OverlayRouter> |
||||
</Overlay> |
||||
</div> |
||||
</OverlayDisplay> |
||||
); |
||||
return <Button onClick={() => setVal(true)}>Crash me!</Button>; |
||||
} |
||||
|
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
import { Component } from "react"; |
||||
|
||||
import { ErrorPart } from "@/pages/parts/errors/ErrorPart"; |
||||
|
||||
interface ErrorBoundaryState { |
||||
error?: { |
||||
error: any; |
||||
errorInfo: any; |
||||
}; |
||||
} |
||||
|
||||
export class ErrorBoundary extends Component< |
||||
Record<string, unknown>, |
||||
ErrorBoundaryState |
||||
> { |
||||
constructor(props: { children: any }) { |
||||
super(props); |
||||
this.state = { |
||||
error: undefined, |
||||
}; |
||||
} |
||||
|
||||
componentDidCatch(error: any, errorInfo: any) { |
||||
console.error("Render error caught", error, errorInfo); |
||||
this.setState((s) => ({ |
||||
...s, |
||||
error: { |
||||
error, |
||||
errorInfo, |
||||
}, |
||||
})); |
||||
} |
||||
|
||||
render() { |
||||
if (!this.state.error) return this.props.children as any; |
||||
|
||||
return ( |
||||
<ErrorPart |
||||
error={this.state.error.error} |
||||
errorInfo={this.state.error.errorInfo} |
||||
/> |
||||
); |
||||
} |
||||
} |
@ -1,23 +1,5 @@
@@ -1,23 +1,5 @@
|
||||
import { useTranslation } from "react-i18next"; |
||||
|
||||
import { IconPatch } from "@/components/buttons/IconPatch"; |
||||
import { Icons } from "@/components/Icon"; |
||||
import { ArrowLink } from "@/components/text/ArrowLink"; |
||||
import { Title } from "@/components/text/Title"; |
||||
import { ErrorWrapperPart } from "@/pages/parts/errors/ErrorWrapperPart"; |
||||
import { NotFoundPart } from "@/pages/parts/errors/ErrorWrapperPart"; |
||||
|
||||
export function NotFoundPage() { |
||||
const { t } = useTranslation(); |
||||
|
||||
return ( |
||||
<ErrorWrapperPart> |
||||
<IconPatch |
||||
icon={Icons.EYE_SLASH} |
||||
className="mb-6 text-xl text-bink-600" |
||||
/> |
||||
<Title>{t("notFound.page.title")}</Title> |
||||
<p className="mb-12 mt-5 max-w-sm">{t("notFound.page.description")}</p> |
||||
<ArrowLink to="/" linkText={t("notFound.backArrow")} /> |
||||
</ErrorWrapperPart> |
||||
); |
||||
return <NotFoundPart />; |
||||
} |
||||
|
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
import { ButtonPlain } from "@/components/Button"; |
||||
import { Icons } from "@/components/Icon"; |
||||
import { IconPill } from "@/components/layout/IconPill"; |
||||
import { Title } from "@/components/text/Title"; |
||||
import { Paragraph } from "@/components/utils/Text"; |
||||
import { ErrorContainer, ErrorLayout } from "@/pages/layouts/ErrorLayout"; |
||||
|
||||
export function ErrorPart(props: { error: any; errorInfo: any }) { |
||||
const data = JSON.stringify({ |
||||
error: props.error, |
||||
errorInfo: props.errorInfo, |
||||
}); |
||||
return ( |
||||
<div className="relative flex flex-1 flex-col"> |
||||
<div className="flex h-full flex-1 flex-col items-center justify-center p-5 text-center"> |
||||
<ErrorLayout> |
||||
<ErrorContainer> |
||||
<IconPill icon={Icons.EYE_SLASH}>It broke</IconPill> |
||||
<Title>Failed to load meta data</Title> |
||||
<Paragraph>{data}</Paragraph> |
||||
<ButtonPlain |
||||
theme="purple" |
||||
className="mt-6 md:px-12 p-2.5" |
||||
onClick={() => window.location.reload()} |
||||
> |
||||
Reload the page |
||||
</ButtonPlain> |
||||
</ErrorContainer> |
||||
</ErrorLayout> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
import { BrandPill } from "@/components/layout/BrandPill"; |
||||
import { Loading } from "@/components/layout/Loading"; |
||||
import { BlurEllipsis } from "@/pages/layouts/SubPageLayout"; |
||||
|
||||
export function MigrationPart() { |
||||
return ( |
||||
<div className="flex flex-col justify-center items-center h-screen text-center font-medium"> |
||||
{/* Overlaid elements */} |
||||
<BlurEllipsis /> |
||||
<div className="right-[calc(2rem+env(safe-area-inset-right))] top-6 absolute"> |
||||
<BrandPill /> |
||||
</div> |
||||
|
||||
{/* Content */} |
||||
<Loading /> |
||||
<p className="max-w-[19rem] mt-3 mb-12 text-type-secondary"> |
||||
Please hold, we are migrating your data. This shouldn't take long. |
||||
Also, fuck you. |
||||
</p> |
||||
<div className="w-[8rem] h-1 rounded-full bg-progress-background bg-opacity-25 mb-2"> |
||||
<div className="w-1/4 h-full bg-progress-filled rounded-full" /> |
||||
</div> |
||||
<p>25%</p> |
||||
</div> |
||||
); |
||||
} |
Loading…
Reference in new issue