aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/Player
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2026-06-03 17:22:48 -0700
committerPinapelz <yukais@pinapelz.com>2026-06-03 17:22:48 -0700
commit14172f9dd64ce91ba5cf51f82c53deb6a81d68a6 (patch)
tree5e12ce4e30ecaed9a2aac48d2959d99a4d8b4ef7 /src/components/Player
parent818db3ef4aadf489dba5ba8ba4f3bb4e150f0b22 (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.ts39
-rw-r--r--src/components/Player/index.tsx137
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>
)}
</>
);
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage