From 3670a01397def7ca8359603a7d9803081ead5947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Tue, 3 Dec 2024 19:20:04 +0300 Subject: [PATCH] feat: updated timestamps ui --- .../mindset/components/MediaPlayer/index.tsx | 9 +- .../Sidebar/Transcript/Viewer/index.tsx | 92 ++++++++++++++----- .../components/Sidebar/Transcript/index.tsx | 24 ++++- 3 files changed, 94 insertions(+), 31 deletions(-) diff --git a/src/components/mindset/components/MediaPlayer/index.tsx b/src/components/mindset/components/MediaPlayer/index.tsx index 46e373678..58a8eacee 100644 --- a/src/components/mindset/components/MediaPlayer/index.tsx +++ b/src/components/mindset/components/MediaPlayer/index.tsx @@ -116,22 +116,18 @@ const MediaPlayerComponent = ({ mediaUrl }: Props) => { if (!isSeeking) { const currentTime = progress.playedSeconds - setPlayingTime(currentTime) - - return - const edge = findCurrentEdge(edges, currentTime) if (edge) { setActiveEdge(edge) + } else { + setActiveEdge(null) } // find playing link and set it to state } } - console.log(handleProgress) - const handleReady = () => { if (playerRef) { setStatus('ready') @@ -167,6 +163,7 @@ const MediaPlayerComponent = ({ mediaUrl }: Props) => { onError={handleError} onPause={handlePause} onPlay={handlePlay} + onProgress={handleProgress} onReady={handleReady} playing={isPlaying} url={mediaUrl || ''} diff --git a/src/components/mindset/components/Sidebar/Transcript/Viewer/index.tsx b/src/components/mindset/components/Sidebar/Transcript/Viewer/index.tsx index c02049605..39abaafd6 100644 --- a/src/components/mindset/components/Sidebar/Transcript/Viewer/index.tsx +++ b/src/components/mindset/components/Sidebar/Transcript/Viewer/index.tsx @@ -1,6 +1,7 @@ import clsx from 'clsx' -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import styled from 'styled-components' +import { Flex } from '~/components/common/Flex' import { usePlayerStore } from '~/stores/usePlayerStore' import { colors } from '~/utils' import { secondsToMediaTime } from '~/utils/secondsToMediaTime' @@ -32,10 +33,14 @@ type TranscriptData = { export const Viewer = ({ transcriptString }: Props) => { const [currentTime, setCurrentTime] = useState(0) + const [userScrolling, setUserScrolling] = useState(false) const { playerRef } = usePlayerStore((s) => s) const cleaned = transcriptString.replace(/^["']|["']$/g, '') const transcriptData: TranscriptData[] = JSON.parse(cleaned) + const activeRef = useRef(null) + const wrapperRef = useRef(null) + const scrollTimeout = useRef(null) useEffect(() => { const interval = setInterval(() => { @@ -49,26 +54,59 @@ export const Viewer = ({ transcriptString }: Props) => { return () => clearInterval(interval) }, [playerRef, setCurrentTime]) + useEffect(() => { + const handleScroll = () => { + setUserScrolling(true) + + if (scrollTimeout.current) { + clearTimeout(scrollTimeout.current) + } + + // Re-enable automatic scrolling after 1 second of no user scroll + scrollTimeout.current = setTimeout(() => { + setUserScrolling(false) + }, 1000) + } + + const wrapper = wrapperRef.current + + if (wrapper) { + wrapper.addEventListener('scroll', handleScroll) + } + + return () => { + if (wrapper) { + wrapper.removeEventListener('scroll', handleScroll) + } + } + }, []) + + useEffect(() => { + if (!userScrolling && activeRef.current) { + activeRef.current.scrollIntoView({ + behavior: 'smooth', + block: 'center', // Centers the active paragraph + }) + } + }, [currentTime, userScrolling]) + return ( - + {transcriptData.map((i) => { const start = secondsToMediaTime(i.start) - const end = secondsToMediaTime(i.end) + + const isActive = i.start < currentTime && currentTime < i.end return ( - - - {start}:{end} - - {i.words.map((word) => { - const isActive = word.start < currentTime && currentTime < word.end - - return ( - - {word.word} - - ) - })} + + {start} + + {i.text} + ) })} @@ -76,9 +114,14 @@ export const Viewer = ({ transcriptString }: Props) => { ) } -const Paragraph = styled.div` +const Paragraph = styled(Flex)` + flex-direction: row; + align-items: flex-start; font-size: 14px; - word-break: break-word; + padding: 8px 24px; + &.active { + background: ${colors.AI_HIGHLIGHT}; + } ` const Wrapper = styled.div` @@ -88,12 +131,19 @@ const Wrapper = styled.div` padding-right: 4px; ` -const Start = styled.span`` +const Start = styled.span` + background: ${colors.lightBlue100}; + color: ${colors.lightBlue500}; + padding: 2px; + margin-right: 8px; + border-radius: 4px; +` -const Word = styled.span` +const Words = styled.div` margin: 0 2px; + word-break: break-word; &.active { - background: ${colors.lightBlue300}; + background: ${colors.AI_HIGHLIGHT}; } ` diff --git a/src/components/mindset/components/Sidebar/Transcript/index.tsx b/src/components/mindset/components/Sidebar/Transcript/index.tsx index 9e38a1053..568d5b23f 100644 --- a/src/components/mindset/components/Sidebar/Transcript/index.tsx +++ b/src/components/mindset/components/Sidebar/Transcript/index.tsx @@ -10,7 +10,8 @@ import { Viewer } from './Viewer' export const Transcript = () => { const { selectedEpisodeId } = useMindsetStore((s) => s) - const { playingTime, duration } = usePlayerStore((s) => s) + const { playerRef } = usePlayerStore((s) => s) + const [currentTime, setCurrentTime] = useState(0) const [clips, setClips] = useState([]) @@ -32,6 +33,18 @@ export const Transcript = () => { } }, [selectedEpisodeId]) + useEffect(() => { + const interval = setInterval(() => { + if (playerRef && setCurrentTime) { + const time = playerRef.getCurrentTime() + + setCurrentTime(time) + } + }, 100) + + return () => clearInterval(interval) + }, [playerRef, setCurrentTime]) + return ( Transcript @@ -40,9 +53,9 @@ export const Transcript = () => { const [start, end] = timestamp ? (timestamp as string).split('-').map(Number) // Directly convert to numbers - : [0, duration] + : [0, 0] - if (start <= playingTime * 1000 && playingTime * 1000 < end) { + if (start <= currentTime * 1000 && currentTime * 1000 < end) { // Multiply playingTime by 1000 to match millisecond format return ( @@ -61,7 +74,7 @@ export const Transcript = () => { const Wrapper = styled(Flex)` .heading { font-weight: 700; - font-size: 12px; + font-size: 16px; margin-bottom: 16px; } @@ -71,9 +84,12 @@ const Wrapper = styled(Flex)` padding: 24px; overflow-y: auto; flex: 1 1 100%; + max-height: 50%; ` const TranscriptWrapper = styled(Flex)` flex-wrap: wrap; flex: 1 1 100%; + margin-left: -24px; + margin-right: -24px; `