From 14172f9dd64ce91ba5cf51f82c53deb6a81d68a6 Mon Sep 17 00:00:00 2001 From: Pinapelz Date: Wed, 3 Jun 2026 17:22:48 -0700 Subject: create daily/unlimited mode, CDN audio file for daily mode --- src/components/Player/index.styled.ts | 39 ---------- src/components/Player/index.tsx | 137 ++++++++++++++++++++++------------ 2 files changed, 89 insertions(+), 87 deletions(-) delete mode 100644 src/components/Player/index.styled.ts (limited to 'src/components/Player') 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(null); +export function Player({ currentTry }: Props) { + const audioRef = React.useRef(null); const currentPlayTime = playTimes[currentTry]; - const [play, setPlay] = React.useState(false); + const [play, setPlay] = React.useState(false); + const [currentTime, setCurrentTime] = React.useState(0); + const [isReady, setIsReady] = React.useState(false); - const [currentTime, setCurrentTime] = React.useState(0); + const CDN_URL = + import.meta.env.VITE_CDN_URL || "https://yena.pinapelz.com/kheardle"; - const [isReady, setIsReady] = React.useState(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 ( <> - {isReady ? ( <> - {currentTime !== 0 && } - {playTimes.map((playTime) => ( + {currentTime !== 0 && ( + + )} + + {playTimes.map((t) => ( ))} + 1s 16s - {!play && ( + + {!play ? ( - )} - {play && ( + ) : ( )} ) : ( -

Loading player...

+

Loading audio...

)} ); -- cgit v1.2.3