diff options
| author | Pinapelz <yukais@pinapelz.com> | 2026-06-03 17:22:48 -0700 |
|---|---|---|
| committer | Pinapelz <yukais@pinapelz.com> | 2026-06-03 17:22:48 -0700 |
| commit | 14172f9dd64ce91ba5cf51f82c53deb6a81d68a6 (patch) | |
| tree | 5e12ce4e30ecaed9a2aac48d2959d99a4d8b4ef7 /src/components/Player | |
| parent | 818db3ef4aadf489dba5ba8ba4f3bb4e150f0b22 (diff) | |
create daily/unlimited mode, CDN audio file for daily mode
Diffstat (limited to 'src/components/Player')
| -rw-r--r-- | src/components/Player/index.styled.ts | 39 | ||||
| -rw-r--r-- | src/components/Player/index.tsx | 137 |
2 files changed, 89 insertions, 87 deletions
diff --git a/src/components/Player/index.styled.ts b/src/components/Player/index.styled.ts deleted file mode 100644 index 3c98f1e..0000000 --- a/src/components/Player/index.styled.ts +++ /dev/null @@ -1,39 +0,0 @@ -import styled from "styled-components"; - -export const ProgressBackground = styled.div` - position: relative; - width: 100%; - height: 12px; - background-color: var(--cl-gray-2); - border: 1px solid ${({ theme }) => theme.border}; - margin: 24px 0 4px 0; -`; - -export const Progress = styled.div<{ value: number }>` - width: ${({ value }) => value * 6.25}%; - height: 100%; - background-color: ${({ theme }) => theme.green}; - transition: width 0.5s; -`; - -export const Separator = styled.div` - position: absolute; - top: 0; - width: 1px; - height: 100%; - background-color: ${({ theme }) => theme.border}; -`; - -export const TimeStamps = styled.div` - display: flex; - justify-content: space-between; - width: 100%; - margin-bottom: 12px; -`; - -export const TimeStamp = styled.p` - margin: 0; - font-family: "Roboto Mono", monospace; - font-size: 0.7rem; - color: var(--cl-gray-5); -`; diff --git a/src/components/Player/index.tsx b/src/components/Player/index.tsx index fcfce72..e4dfd9e 100644 --- a/src/components/Player/index.tsx +++ b/src/components/Player/index.tsx @@ -1,101 +1,142 @@ import React from "react"; -import YouTube from "react-youtube"; import { IoPlay, IoPause } from "react-icons/io5"; import { playTimes } from "../../constants"; - -import * as Styled from "./index.styled"; +import * as Styled from "../YTPlayer/index.styled"; interface Props { - id: string; currentTry: number; } -export function Player({ id, currentTry }: Props) { - const opts = { - width: "0", - height: "0", - }; +const MAX_TIME = 16; - // react-youtube doesn't export types for this - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const playerRef = React.useRef<any>(null); +export function Player({ currentTry }: Props) { + const audioRef = React.useRef<HTMLAudioElement | null>(null); const currentPlayTime = playTimes[currentTry]; - const [play, setPlay] = React.useState<boolean>(false); + const [play, setPlay] = React.useState(false); + const [currentTime, setCurrentTime] = React.useState(0); + const [isReady, setIsReady] = React.useState(false); - const [currentTime, setCurrentTime] = React.useState<number>(0); + const CDN_URL = + import.meta.env.VITE_CDN_URL || "https://yena.pinapelz.com/kheardle"; - const [isReady, setIsReady] = React.useState<boolean>(false); + const dateString = new Date().toISOString().split("T")[0]; - React.useEffect(() => { - setInterval(() => { - playerRef.current?.internalPlayer - .getCurrentTime() - .then((time: number) => { - setCurrentTime(time); - }); - }, 250); + 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); }, []); React.useEffect(() => { - if (play) { - if (currentTime * 1000 >= currentPlayTime) { - playerRef.current?.internalPlayer.pauseVideo(); - playerRef.current?.internalPlayer.seekTo(0); + const audio = new Audio(`${CDN_URL}/${dateString}.mp3`); + audioRef.current = audio; + + audio.addEventListener("loadeddata", () => { + setIsReady(true); + }); + + audio.addEventListener("timeupdate", () => { + setCurrentTime(audio.currentTime); + }); + + audio.addEventListener("ended", () => { + setPlay(false); + audio.currentTime = 0; + }); + + return () => { + audio.pause(); + audio.src = ""; + }; + }, [dateString]); + + React.useEffect(() => { + if (!play || !audioRef.current) return; + + const interval = setInterval(() => { + const a = audioRef.current!; + const t = a.currentTime * 1000; + + setCurrentTime(a.currentTime); + + if (t >= currentPlayTime || t >= MAX_TIME * 1000) { + a.pause(); + a.currentTime = 0; setPlay(false); } - } - }, [play, currentTime]); + }, 100); - // don't call play video each time currentTime changes - const startPlayback = React.useCallback(() => { - playerRef.current?.internalPlayer.playVideo(); - setPlay(true); - }, []); + 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); - const setReady = React.useCallback(() => { - setIsReady(true); + return () => { + navigator.mediaSession.setActionHandler("play", null); + navigator.mediaSession.setActionHandler("pause", null); + navigator.mediaSession.setActionHandler("previoustrack", null); + navigator.mediaSession.setActionHandler("nexttrack", null); + }; }, []); return ( <> - <YouTube opts={opts} videoId={id} onReady={setReady} ref={playerRef} /> {isReady ? ( <> <Styled.ProgressBackground> - {currentTime !== 0 && <Styled.Progress value={currentTime} />} - {playTimes.map((playTime) => ( + {currentTime !== 0 && ( + <Styled.Progress value={currentTime} /> + )} + + {playTimes.map((t) => ( <Styled.Separator - style={{ left: `${(playTime / 16000) * 100}%` }} - key={playTime} + key={t} + style={{ left: `${(t / 16000) * 100}%` }} /> ))} </Styled.ProgressBackground> + <Styled.TimeStamps> <Styled.TimeStamp>1s</Styled.TimeStamp> <Styled.TimeStamp>16s</Styled.TimeStamp> </Styled.TimeStamps> - {!play && ( + + {!play ? ( <IoPlay style={{ cursor: "pointer" }} size={36} - color="var(--cl-green-6)" onClick={startPlayback} /> - )} - {play && ( + ) : ( <IoPause style={{ cursor: "pointer" }} size={36} - color="var(--cl-green-6)" - onClick={startPlayback} + onClick={stopPlayback} /> )} </> ) : ( - <p>Loading player...</p> + <p>Loading audio...</p> )} </> ); |
