@@ -8,10 +8,10 @@ import {
88 faLock ,
99} from "@fortawesome/free-solid-svg-icons" ;
1010import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" ;
11- import { Check , Copy , Globe2 } from "lucide-react" ;
11+ import { Check , Clock , Copy , Globe2 } from "lucide-react" ;
1212import moment from "moment" ;
1313import { useRouter } from "next/navigation" ;
14- import { useEffect , useState } from "react" ;
14+ import { useEffect , useRef , useState } from "react" ;
1515import { toast } from "sonner" ;
1616import { editTitle } from "@/actions/videos/edit-title" ;
1717import { useDashboardContext } from "@/app/(org)/dashboard/Contexts" ;
@@ -58,6 +58,23 @@ export const ShareHeader = ({
5858 const [ upgradeModalOpen , setUpgradeModalOpen ] = useState ( false ) ;
5959 const [ isSharingDialogOpen , setIsSharingDialogOpen ] = useState ( false ) ;
6060 const [ linkCopied , setLinkCopied ] = useState ( false ) ;
61+ const [ showCopyOptions , setShowCopyOptions ] = useState ( false ) ;
62+ const [ capturedTime , setCapturedTime ] = useState ( 0 ) ;
63+ const copyOptionsRef = useRef < HTMLDivElement > ( null ) ;
64+
65+ useEffect ( ( ) => {
66+ if ( ! showCopyOptions ) return ;
67+ const handler = ( e : MouseEvent ) => {
68+ if (
69+ copyOptionsRef . current &&
70+ ! copyOptionsRef . current . contains ( e . target as Node )
71+ ) {
72+ setShowCopyOptions ( false ) ;
73+ }
74+ } ;
75+ document . addEventListener ( "mousedown" , handler ) ;
76+ return ( ) => document . removeEventListener ( "mousedown" , handler ) ;
77+ } , [ showCopyOptions ] ) ;
6178
6279 const contextData = useDashboardContext ( ) ;
6380 const contextSharedSpaces = contextData ?. sharedSpaces || null ;
@@ -130,6 +147,39 @@ export const ShareHeader = ({
130147 }
131148 } ;
132149
150+ const formatTimestamp = ( seconds : number ) : string => {
151+ const h = Math . floor ( seconds / 3600 ) ;
152+ const m = Math . floor ( ( seconds % 3600 ) / 60 ) ;
153+ const s = seconds % 60 ;
154+ if ( h > 0 )
155+ return `${ h } :${ String ( m ) . padStart ( 2 , "0" ) } :${ String ( s ) . padStart ( 2 , "0" ) } ` ;
156+ return `${ m } :${ String ( s ) . padStart ( 2 , "0" ) } ` ;
157+ } ;
158+
159+ const handleCopyClick = ( ) => {
160+ const video = document . querySelector ( "video" ) ;
161+ const currentTime = video ? Math . floor ( video . currentTime ) : 0 ;
162+
163+ if ( currentTime > 3 ) {
164+ setCapturedTime ( currentTime ) ;
165+ setShowCopyOptions ( true ) ;
166+ } else {
167+ navigator . clipboard . writeText ( getVideoLink ( ) ) ;
168+ setLinkCopied ( true ) ;
169+ setTimeout ( ( ) => setLinkCopied ( false ) , 2000 ) ;
170+ }
171+ } ;
172+
173+ const handleCopyLink = ( withTimestamp : boolean ) => {
174+ const link = withTimestamp
175+ ? `${ getVideoLink ( ) } ?t=${ capturedTime } `
176+ : getVideoLink ( ) ;
177+ navigator . clipboard . writeText ( link ) ;
178+ setShowCopyOptions ( false ) ;
179+ setLinkCopied ( true ) ;
180+ setTimeout ( ( ) => setLinkCopied ( false ) , 2000 ) ;
181+ } ;
182+
133183 const handleSharingUpdated = ( ) => {
134184 refresh ( ) ;
135185 } ;
@@ -268,23 +318,36 @@ export const ShareHeader = ({
268318 icon = { faLock }
269319 />
270320 ) }
271- < Button
272- variant = "white"
273- onClick = { ( ) => {
274- navigator . clipboard . writeText ( getVideoLink ( ) ) ;
275- setLinkCopied ( true ) ;
276- setTimeout ( ( ) => {
277- setLinkCopied ( false ) ;
278- } , 2000 ) ;
279- } }
280- >
281- { getDisplayLink ( ) }
282- { linkCopied ? (
283- < Check className = "ml-2 w-4 h-4 svgpathanimation" />
284- ) : (
285- < Copy className = "ml-2 w-4 h-4" />
321+ < div className = "relative" ref = { copyOptionsRef } >
322+ < Button variant = "white" onClick = { handleCopyClick } >
323+ { getDisplayLink ( ) }
324+ { linkCopied ? (
325+ < Check className = "ml-2 w-4 h-4 svgpathanimation" />
326+ ) : (
327+ < Copy className = "ml-2 w-4 h-4" />
328+ ) }
329+ </ Button >
330+ { showCopyOptions && (
331+ < div className = "absolute right-0 top-full z-50 mt-1 min-w-full w-max overflow-hidden rounded-lg border border-gray-6 bg-white shadow-lg" >
332+ < button
333+ type = "button"
334+ className = "flex w-full items-center gap-2 px-3 py-2 text-sm text-gray-12 transition-colors hover:bg-gray-3"
335+ onClick = { ( ) => handleCopyLink ( false ) }
336+ >
337+ < Copy className = "w-3.5 h-3.5 shrink-0" />
338+ Copy link
339+ </ button >
340+ < button
341+ type = "button"
342+ className = "flex w-full items-center gap-2 px-3 py-2 text-sm text-gray-12 transition-colors hover:bg-gray-3"
343+ onClick = { ( ) => handleCopyLink ( true ) }
344+ >
345+ < Clock className = "w-3.5 h-3.5 shrink-0" />
346+ Copy link at { formatTimestamp ( capturedTime ) }
347+ </ button >
348+ </ div >
286349 ) }
287- </ Button >
350+ </ div >
288351 </ div >
289352 { userIsOwnerAndNotPro && (
290353 < button
0 commit comments