diff --git a/components/WebcamView.tsx b/components/WebcamView.tsx index 9b4ba7b0..c37361c9 100644 --- a/components/WebcamView.tsx +++ b/components/WebcamView.tsx @@ -1,9 +1,8 @@ "use client"; -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import MuxVideo from "@mux/mux-video-react"; import { Box, LoadingOverlay } from "@mantine/core"; -import { useDisclosure } from "@mantine/hooks"; export function WebcamView(props: { webcamUrl: string; @@ -12,18 +11,34 @@ export function WebcamView(props: { }) { const ref = useRef(null); - const [videoReady, { open: setReady, close: setUnready }] = - useDisclosure(false); + const [videoReady, setVideoReady] = useState(false); useEffect(() => { - // The four (4) here is the value of readyState when a video element is ready to play - // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState - if (ref.current?.readyState === 4) { - setReady(); - } else { - setUnready(); + // Copy the element ref so that we can still reference it in the + // cleanup callback. + const el = ref.current; + if (!el) { + return; } - }, [ref.current?.readyState, setReady, setUnready]); + function isReady() { + setVideoReady(true); + } + function isNotReady() { + setVideoReady(false); + } + el.addEventListener("canplay", isReady); + for (const evt of ["stalled", "waiting", "error"]) { + el.addEventListener(evt, isNotReady); + } + return () => { + el.removeEventListener("canplay", isReady); + for (const evt of ["stalled", "waiting", "error"]) { + el.removeEventListener(evt, isNotReady); + } + }; + // Since the key of the video element is also webcamUrl, this effect + // will clean up and re-run whenever the webcamUrl changes. + }, [props.webcamUrl]); return ( <> @@ -35,6 +50,7 @@ export function WebcamView(props: { />