UNPKG

@charisma-ai/react

Version:
98 lines (97 loc) 4.95 kB
import { useRef, useState, useCallback, useEffect } from "react"; import { fetchMedia } from "./fetchMedia.js"; const useBackgroundVideo = ({ disabled } = {})=>{ const [backgroundVideo, setBackgroundVideo] = useState(); const [backgroundVideoIdle, setBackgroundVideoIdle] = useState(); const backgroundVideoIdleSrc = useRef(); const [isVideoIdleActive, setIsVideoIdleActive] = useState(true); const videoRef = useRef(null); const activeVideo = isVideoIdleActive ? backgroundVideoIdle : backgroundVideo; // When the story ends, we pause the current video useEffect(()=>{ if (videoRef.current) { if (disabled) { videoRef.current.pause(); } else { videoRef.current.play().catch(()=>{ // Don't worry about this throwing. }); } } }, [ disabled ]); const onEndedVideo = useCallback(()=>{ // when `background-once` finishes, default back to the idle if it exists if (!isVideoIdleActive && backgroundVideoIdleSrc.current) { setIsVideoIdleActive(true); } }, [ isVideoIdleActive ]); const onMessage = useCallback(async (messageEvent)=>{ if (messageEvent.type === "character" || messageEvent.type === "panel") { const { metadata } = messageEvent.message; // `background-once` is played a single time before falling back to the `background` idle const newBackgroundVideo = metadata["background-once"]; const newBackgroundVideoIdle = metadata.background; if (newBackgroundVideo === undefined && newBackgroundVideoIdle === undefined) { // Don't do anything if neither metadata key is specified. return; } // If at least one key _is_ specified, then fetch both videos if specified and not "false" const newBackgroundVideoBlobPromise = newBackgroundVideo !== undefined && newBackgroundVideo !== "false" ? fetchMedia(newBackgroundVideo) : undefined; const newBackgroundVideoIdleBlobPromise = newBackgroundVideoIdle !== undefined && newBackgroundVideoIdle !== "false" ? fetchMedia(newBackgroundVideoIdle) : undefined; // If we have a value for `background-once`... if (newBackgroundVideo !== undefined && newBackgroundVideo !== "false") { // Wait for it to load... const newBackgroundVideoBlob = await newBackgroundVideoBlobPromise; // Now it's fetched, we can turn off the idle and play this one-shot video setBackgroundVideo(newBackgroundVideoBlob); setIsVideoIdleActive(false); if (videoRef.current) { // Replay `once` videos from the start on subsequent equal nodes videoRef.current.currentTime = 0; videoRef.current.play(); } } // If we have a new value for `background`... if (newBackgroundVideoIdle !== undefined && newBackgroundVideoIdle !== "false" && newBackgroundVideoIdle !== backgroundVideoIdleSrc.current) { // Wait for it to load... const newBackgroundVideoIdleBlob = await newBackgroundVideoIdleBlobPromise; backgroundVideoIdleSrc.current = newBackgroundVideoIdle; // Now it's fetched, we can make it the pending idle, which will be switched to // when the one-shot video finishes setBackgroundVideoIdle(newBackgroundVideoIdleBlob); if (videoRef.current) { // It's possible that fetch took a long time, so the `once` video finished. // In this case, start playing the idle. // Alternatively, if `background-once` isn't specified, also play immediately. if (videoRef.current.ended || newBackgroundVideo === undefined || newBackgroundVideo === "false") { setIsVideoIdleActive(true); } videoRef.current.play(); } } else if (newBackgroundVideoIdle === undefined || newBackgroundVideoIdle === "false") { // If it's not defined, we remove the pending idle, so that if a one-shot video _is_ // defined, then it won't switch to it backgroundVideoIdleSrc.current = undefined; setBackgroundVideoIdle(undefined); } } }, []); return { videoProps: { ref: videoRef, src: activeVideo, autoPlay: true, loop: isVideoIdleActive, onEnded: onEndedVideo, style: { display: activeVideo === undefined ? "none" : "" } }, onMessage }; }; export default useBackgroundVideo;