9 changed files with 233 additions and 64 deletions
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
import { ErrorMessage } from "@/components/layout/ErrorBoundary"; |
||||
import { Link } from "@/components/text/Link"; |
||||
import { conf } from "@/setup/config"; |
||||
import { Component, ReactNode } from "react"; |
||||
import { VideoPlayerHeader } from "./VideoPlayerHeader"; |
||||
|
||||
interface ErrorBoundaryState { |
||||
hasError: boolean; |
||||
error?: { |
||||
name: string; |
||||
description: string; |
||||
path: string; |
||||
}; |
||||
} |
||||
|
||||
interface VideoErrorBoundaryProps { |
||||
children?: ReactNode; |
||||
title?: string; |
||||
onGoBack?: () => void; |
||||
} |
||||
|
||||
export class VideoErrorBoundary extends Component< |
||||
VideoErrorBoundaryProps, |
||||
ErrorBoundaryState |
||||
> { |
||||
constructor(props: VideoErrorBoundaryProps) { |
||||
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; |
||||
|
||||
// TODO make responsive, needs to work in tiny player
|
||||
|
||||
return ( |
||||
<div className="absolute inset-0 bg-denim-100"> |
||||
<div className="pointer-events-auto absolute inset-x-0 top-0 flex flex-col py-6 px-8 pb-2"> |
||||
<VideoPlayerHeader |
||||
title={this.props.title} |
||||
onClick={this.props.onGoBack} |
||||
/> |
||||
</div> |
||||
<ErrorMessage error={this.state.error} localSize> |
||||
The video player encounted a fatal error, please report it to the{" "} |
||||
<Link url={conf().DISCORD_LINK} newTab> |
||||
Discord server |
||||
</Link>{" "} |
||||
or on{" "} |
||||
<Link url={conf().GITHUB_LINK} newTab> |
||||
GitHub |
||||
</Link> |
||||
. |
||||
</ErrorMessage> |
||||
</div> |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
import { IconPatch } from "@/components/buttons/IconPatch"; |
||||
import { Icons } from "@/components/Icon"; |
||||
import { Title } from "@/components/text/Title"; |
||||
import { ReactNode } from "react"; |
||||
import { useVideoPlayerState } from "../VideoContext"; |
||||
import { VideoPlayerHeader } from "./VideoPlayerHeader"; |
||||
|
||||
interface VideoPlayerErrorProps { |
||||
title?: string; |
||||
onGoBack?: () => void; |
||||
children?: ReactNode; |
||||
} |
||||
|
||||
export function VideoPlayerError(props: VideoPlayerErrorProps) { |
||||
const { videoState } = useVideoPlayerState(); |
||||
|
||||
const err = videoState.error; |
||||
|
||||
if (!err) return props.children as any; |
||||
|
||||
return ( |
||||
<div> |
||||
<div className="absolute inset-0 flex flex-col items-center justify-center bg-denim-100"> |
||||
<IconPatch icon={Icons.WARNING} className="mb-6 text-red-400" /> |
||||
<Title>Failed to load media</Title> |
||||
<p className="my-6 max-w-lg"> |
||||
{err.name}: {err.description} |
||||
</p> |
||||
</div> |
||||
<div className="pointer-events-auto absolute inset-x-0 top-0 flex flex-col py-6 px-8 pb-2"> |
||||
<VideoPlayerHeader title={props.title} onClick={props.onGoBack} /> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
Loading…
Reference in new issue