9 changed files with 233 additions and 64 deletions
@ -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 @@ |
|||||||
|
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