import React from "react"; import { IoPlay, IoPause } from "react-icons/io5"; import { playTimes } from "../../constants"; import * as Styled from "../YTPlayer/index.styled"; interface Props { currentTry: number; } const MAX_TIME = 16; const DEFAULT_VOLUME = 0.7; const loadVolume = () => { try { const storedVolume = localStorage.getItem("playerVolume"); if (storedVolume === null) return DEFAULT_VOLUME; const parsedVolume = Number(storedVolume); if (!Number.isFinite(parsedVolume)) return DEFAULT_VOLUME; return Math.max(0, Math.min(1, parsedVolume)); } catch { return DEFAULT_VOLUME; } }; export function Player({ currentTry }: Props) { const audioRef = React.useRef(null); const currentPlayTime = playTimes[currentTry]; const [play, setPlay] = React.useState(false); const [currentTime, setCurrentTime] = React.useState(0); const [isReady, setIsReady] = React.useState(false); const [isUnavailable, setIsUnavailable] = React.useState(false); const [volume, setVolume] = React.useState(loadVolume); const CDN_URL = import.meta.env.VITE_CDN_URL || "localhost"; const dateString = new Date().toISOString().split("T")[0]; const startPlayback = React.useCallback(() => { const audio = audioRef.current; if (!audio) return; audio.play(); setPlay(true); }, []); const stopPlayback = React.useCallback(() => { const audio = audioRef.current; if (!audio) return; audio.pause(); audio.currentTime = 0; setPlay(false); }, []); const updateVolume = React.useCallback( (event: React.ChangeEvent) => { setVolume(Number(event.target.value)); }, [] ); React.useEffect(() => { setIsReady(false); setIsUnavailable(false); const audio = new Audio(`${CDN_URL}/${dateString}.mp3`); audio.volume = loadVolume(); audioRef.current = audio; audio.addEventListener("loadeddata", () => { setIsReady(true); setIsUnavailable(false); }); audio.addEventListener("error", () => { setIsReady(false); setIsUnavailable(true); }); audio.addEventListener("timeupdate", () => { setCurrentTime(audio.currentTime); }); audio.addEventListener("ended", () => { setPlay(false); audio.currentTime = 0; }); return () => { audio.pause(); audio.src = ""; }; }, [CDN_URL, dateString]); React.useEffect(() => { if (!audioRef.current) return; audioRef.current.volume = volume; try { localStorage.setItem("playerVolume", String(volume)); } catch { } }, [volume]); React.useEffect(() => { if (!play || !audioRef.current) return; const interval = setInterval(() => { const a = audioRef.current; if (!a) return; const t = a.currentTime * 1000; setCurrentTime(a.currentTime); if (t >= currentPlayTime || t >= MAX_TIME * 1000) { a.pause(); a.currentTime = 0; setPlay(false); } }, 100); return () => clearInterval(interval); }, [play, currentPlayTime]); React.useEffect(() => { if (!("mediaSession" in navigator)) return; navigator.mediaSession.setActionHandler("play", () => undefined); navigator.mediaSession.setActionHandler("pause", () => undefined); navigator.mediaSession.setActionHandler("previoustrack", () => undefined); navigator.mediaSession.setActionHandler("nexttrack", () => undefined); return () => { navigator.mediaSession.setActionHandler("play", null); navigator.mediaSession.setActionHandler("pause", null); navigator.mediaSession.setActionHandler("previoustrack", null); navigator.mediaSession.setActionHandler("nexttrack", null); }; }, []); return ( <> {isUnavailable ? (

We are still generating the audio! Come back later.

) : isReady ? ( <> {currentTime !== 0 && ( )} {playTimes.map((t) => ( ))} 1s 16s {!play ? ( ) : ( )} Volume {Math.round(volume * 100)}% ) : (

Loading audio...

)} ); }