You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

76 lines
2.1 KiB

import { cn, isInViewport } from '@/lib/utils'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import mediaManager from '@/services/media-manager.service'
import { useEffect, useRef, useState } from 'react'
import ExternalLink from '../ExternalLink'
import { MediaErrorBoundary } from '../MediaErrorBoundary'
export default function VideoPlayer({ src, className }: { src: string; className?: string }) {
const { autoplay } = useContentPolicy()
const [error, setError] = useState(false)
const videoRef = useRef<HTMLVideoElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!autoplay) return
const video = videoRef.current
const container = containerRef.current
if (!video || !container) return
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setTimeout(() => {
if (isInViewport(container)) {
mediaManager.autoPlay(video)
}
}, 200)
} else {
mediaManager.pause(video)
}
},
{ threshold: 1 }
)
observer.observe(container)
return () => {
observer.unobserve(container)
}
}, [autoplay])
if (error) {
return <ExternalLink url={src} />
}
return (
<MediaErrorBoundary
fallback={<ExternalLink url={src} />}
onError={(error) => {
// Don't log expected media errors
if (error.name !== 'AbortError' && !error.message.includes('play() request was interrupted')) {
console.warn('Video player error:', error)
}
setError(true)
}}
>
<div ref={containerRef}>
<video
ref={videoRef}
controls
playsInline
className={cn('rounded-lg max-h-[80vh] sm:max-h-[60vh] border', className)}
src={src}
onClick={(e) => e.stopPropagation()}
onPlay={(event) => {
mediaManager.play(event.currentTarget)
}}
muted
onError={() => setError(true)}
/>
</div>
</MediaErrorBoundary>
)
}