11import { faMagnifyingGlass , faTimes } from "@fortawesome/free-solid-svg-icons"
22import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
3+ import ShareIcon from "@mui/icons-material/Share"
4+ import ShareOutlinedIcon from "@mui/icons-material/ShareOutlined"
5+ import { useRouter } from "next/router"
36import { useTranslation } from "next-i18next"
47import React , { forwardRef , useEffect , useRef , useState } from "react"
58import styled from "styled-components"
69import { Col , Container , Row } from "../bootstrap"
7- import { Paragraph , formatMilliseconds } from "./hearing"
10+ import {
11+ Paragraph ,
12+ convertToString ,
13+ formatMilliseconds ,
14+ formatTotalSeconds
15+ } from "./hearing"
16+ import { ShareLinkButton } from "components/buttons"
817
918const ClearButton = styled ( FontAwesomeIcon ) `
1019 position: absolute;
@@ -116,11 +125,13 @@ const TranscriptRow = styled(Row)`
116125`
117126
118127export const Transcriptions = ( {
128+ hearingId,
119129 transcriptData,
120130 setCurTimeVideo,
121131 videoLoaded,
122132 videoRef
123133} : {
134+ hearingId : string
124135 transcriptData : Paragraph [ ]
125136 setCurTimeVideo : any
126137 videoLoaded : boolean
@@ -132,11 +143,40 @@ export const Transcriptions = ({
132143 const transcriptRefs = useRef ( new Map ( ) )
133144 const [ searchTerm , setSearchTerm ] = useState ( "" )
134145 const [ filteredData , setFilteredData ] = useState < Paragraph [ ] > ( [ ] )
146+ const [ initialScrollTarget , setInitialScrollTarget ] = useState < number | null > (
147+ null
148+ )
149+ const hasScrolledToInitial = useRef ( false )
135150
136151 const handleClearInput = ( ) => {
137152 setSearchTerm ( "" )
138153 }
139154
155+ // Shared function to scroll to a transcript index
156+ const scrollToTranscript = ( index : number ) => {
157+ const container = containerRef . current
158+ const elem = transcriptRefs . current . get ( index )
159+
160+ if ( elem && container ) {
161+ const elemTop = elem . offsetTop - container . offsetTop
162+ const elemBottom = elemTop + elem . offsetHeight
163+ const viewTop = container . scrollTop
164+ const viewBottom = viewTop + container . clientHeight
165+
166+ if ( elemTop < viewTop ) {
167+ container . scrollTo ( {
168+ top : elemTop ,
169+ behavior : "smooth"
170+ } )
171+ } else if ( elemBottom > viewBottom ) {
172+ container . scrollTo ( {
173+ top : elemBottom - container . clientHeight ,
174+ behavior : "smooth"
175+ } )
176+ }
177+ }
178+ }
179+
140180 useEffect ( ( ) => {
141181 setFilteredData (
142182 transcriptData . filter ( el =>
@@ -145,32 +185,51 @@ export const Transcriptions = ({
145185 )
146186 } , [ transcriptData , searchTerm ] )
147187
188+ const router = useRouter ( )
189+ const startTime = router . query . t
190+ const resultString : string = convertToString ( startTime )
191+
192+ let currentIndex = transcriptData . findIndex (
193+ element => parseInt ( resultString , 10 ) <= element . end / 1000
194+ )
195+
196+ // Set the initial scroll target when we have a startTime and transcripts
197+ useEffect ( ( ) => {
198+ if (
199+ startTime &&
200+ transcriptData . length > 0 &&
201+ currentIndex !== - 1 &&
202+ ! hasScrolledToInitial . current
203+ ) {
204+ setInitialScrollTarget ( currentIndex )
205+ }
206+ } , [ startTime , transcriptData , currentIndex ] )
207+
208+ // Scroll to the initial target when the ref becomes available
209+ useEffect ( ( ) => {
210+ if ( initialScrollTarget !== null && ! searchTerm ) {
211+ const elem = transcriptRefs . current . get ( initialScrollTarget )
212+
213+ if ( elem ) {
214+ setHighlightedId ( initialScrollTarget )
215+ scrollToTranscript ( initialScrollTarget )
216+ hasScrolledToInitial . current = true
217+ setInitialScrollTarget ( null )
218+ }
219+ }
220+ } , [ initialScrollTarget , transcriptRefs . current . size , searchTerm ] )
221+
148222 useEffect ( ( ) => {
149223 const handleTimeUpdate = ( ) => {
150- const currentIndex = transcriptData . findIndex (
151- element => videoRef . current . currentTime <= element . end / 1000
152- )
224+ videoLoaded
225+ ? ( currentIndex = transcriptData . findIndex (
226+ element => videoRef . current . currentTime <= element . end / 1000
227+ ) )
228+ : null
153229 if ( containerRef . current && currentIndex !== highlightedId ) {
154230 setHighlightedId ( currentIndex )
155231 if ( currentIndex !== - 1 && ! searchTerm ) {
156- const container = containerRef . current
157- const elem = transcriptRefs . current . get ( currentIndex )
158- const elemTop = elem . offsetTop - container . offsetTop
159- const elemBottom = elemTop + elem . offsetHeight
160- const viewTop = container . scrollTop
161- const viewBottom = viewTop + container . clientHeight
162-
163- if ( elemTop < viewTop ) {
164- container . scrollTo ( {
165- top : elemTop ,
166- behavior : "smooth"
167- } )
168- } else if ( elemBottom > viewBottom ) {
169- container . scrollTo ( {
170- top : elemBottom - container . clientHeight ,
171- behavior : "smooth"
172- } )
173- }
232+ scrollToTranscript ( currentIndex )
174233 }
175234 }
176235 }
@@ -217,6 +276,7 @@ export const Transcriptions = ({
217276 < TranscriptItem
218277 key = { index }
219278 element = { element }
279+ hearingId = { hearingId }
220280 highlightedId = { highlightedId }
221281 index = { index }
222282 ref = { elem => {
@@ -249,12 +309,14 @@ export const Transcriptions = ({
249309const TranscriptItem = forwardRef ( function TranscriptItem (
250310 {
251311 element,
312+ hearingId,
252313 highlightedId,
253314 index,
254315 setCurTimeVideo,
255316 searchTerm
256317 } : {
257318 element : Paragraph
319+ hearingId : string
258320 highlightedId : number
259321 index : number
260322 setCurTimeVideo : any
@@ -275,6 +337,7 @@ const TranscriptItem = forwardRef(function TranscriptItem(
275337 const isHighlighted = ( index : number ) : boolean => {
276338 return index === highlightedId
277339 }
340+
278341 const highlightText = ( text : string , term : string ) => {
279342 if ( ! term ) return text
280343 const escaped = term . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, "\\$&" )
@@ -290,6 +353,8 @@ const TranscriptItem = forwardRef(function TranscriptItem(
290353 )
291354 }
292355
356+ const [ isHovered , setIsHovered ] = useState ( false )
357+
293358 return (
294359 < TranscriptRow
295360 className = {
@@ -316,6 +381,26 @@ const TranscriptItem = forwardRef(function TranscriptItem(
316381 </ Row >
317382 </ TimestampCol >
318383 < Col className = { `pt-1` } > { highlightText ( element . text , searchTerm ) } </ Col >
384+ < Col xs = "1" className = { `my-1 px-0` } >
385+ { isHighlighted ( index ) ? (
386+ < >
387+ < ShareLinkButton
388+ key = "copy"
389+ text = { `http://localhost:3000/hearing/${ hearingId } ?t=${ formatTotalSeconds (
390+ element . start
391+ ) } `}
392+ className = { `copy my-1 px-1 py-0` }
393+ format = "text/plain"
394+ onMouseEnter = { ( ) => setIsHovered ( true ) }
395+ onMouseLeave = { ( ) => setIsHovered ( false ) }
396+ >
397+ { isHovered ? < ShareIcon /> : < ShareOutlinedIcon /> }
398+ </ ShareLinkButton >
399+ </ >
400+ ) : (
401+ < > </ >
402+ ) }
403+ </ Col >
319404 </ TranscriptRow >
320405 )
321406} )
0 commit comments