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/YTPlayer/index.styled.ts | 39 +++++++++++++ src/components/YTPlayer/index.tsx | 100 ++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 src/components/YTPlayer/index.styled.ts create mode 100644 src/components/YTPlayer/index.tsx (limited to 'src/components/YTPlayer') diff --git a/src/components/YTPlayer/index.styled.ts b/src/components/YTPlayer/index.styled.ts new file mode 100644 index 0000000..3c98f1e --- /dev/null +++ b/src/components/YTPlayer/index.styled.ts @@ -0,0 +1,39 @@ +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/YTPlayer/index.tsx b/src/components/YTPlayer/index.tsx new file mode 100644 index 0000000..1aac9ac --- /dev/null +++ b/src/components/YTPlayer/index.tsx @@ -0,0 +1,100 @@ +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"; + +interface Props { + id: string; + currentTry: number; +} + +export function Player({ id, currentTry }: Props) { + const opts = { + width: "0", + height: "0", + }; + + // react-youtube doesn't export types for this + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const playerRef = 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); + + React.useEffect(() => { + setInterval(() => { + playerRef.current?.internalPlayer + .getCurrentTime() + .then((time: number) => { + setCurrentTime(time); + }); + }, 250); + }, []); + + React.useEffect(() => { + if (play) { + if (currentTime * 1000 >= currentPlayTime) { + playerRef.current?.internalPlayer.pauseVideo(); + playerRef.current?.internalPlayer.seekTo(0); + setPlay(false); + } + } + }, [play, currentTime]); + + // don't call play video each time currentTime changes + const startPlayback = React.useCallback(() => { + playerRef.current?.internalPlayer.playVideo(); + setPlay(true); + }, []); + + const setReady = React.useCallback(() => { + setIsReady(true); + }, []); + + return ( + <> + + {isReady ? ( + <> + + {currentTime !== 0 && } + {playTimes.map((playTime) => ( + + ))} + + + 1s + 16s + + {!play && ( + + )} + {play && ( + + )} + + ) : ( +

Loading player...

+ )} + + ); +} -- cgit v1.2.3