index.tsx raw

   1  import { cn, isInViewport } from '@/lib/utils'
   2  import { useContentPolicy } from '@/providers/ContentPolicyProvider'
   3  import { useUserPreferences } from '@/providers/UserPreferencesProvider'
   4  import mediaManager from '@/services/media-manager.service'
   5  import { useEffect, useRef, useState } from 'react'
   6  import ExternalLink from '../ExternalLink'
   7  
   8  export default function VideoPlayer({ src, className }: { src: string; className?: string }) {
   9    const { autoplay } = useContentPolicy()
  10    const { muteMedia, updateMuteMedia } = useUserPreferences()
  11    const [error, setError] = useState(false)
  12    const videoRef = useRef<HTMLVideoElement>(null)
  13    const containerRef = useRef<HTMLDivElement>(null)
  14  
  15    useEffect(() => {
  16      const video = videoRef.current
  17      const container = containerRef.current
  18  
  19      if (!video || !container || error) return
  20  
  21      const observer = new IntersectionObserver(
  22        ([entry]) => {
  23          if (entry.isIntersecting && autoplay) {
  24            setTimeout(() => {
  25              if (isInViewport(container)) {
  26                mediaManager.autoPlay(video)
  27              }
  28            }, 200)
  29          }
  30  
  31          if (!entry.isIntersecting) {
  32            mediaManager.pause(video)
  33          }
  34        },
  35        { threshold: 1 }
  36      )
  37  
  38      observer.observe(container)
  39  
  40      return () => {
  41        observer.unobserve(container)
  42      }
  43    }, [autoplay, error])
  44  
  45    useEffect(() => {
  46      if (!videoRef.current) return
  47  
  48      const video = videoRef.current
  49  
  50      const handleVolumeChange = () => {
  51        updateMuteMedia(video.muted)
  52      }
  53  
  54      video.addEventListener('volumechange', handleVolumeChange)
  55  
  56      return () => {
  57        video.removeEventListener('volumechange', handleVolumeChange)
  58      }
  59    }, [])
  60  
  61    useEffect(() => {
  62      const video = videoRef.current
  63      if (!video || video.muted === muteMedia) return
  64  
  65      if (muteMedia) {
  66        video.muted = true
  67      } else {
  68        video.muted = false
  69      }
  70    }, [muteMedia])
  71  
  72    if (error) {
  73      return <ExternalLink url={src} />
  74    }
  75  
  76    return (
  77      <div ref={containerRef}>
  78        <video
  79          ref={videoRef}
  80          controls
  81          playsInline
  82          className={cn('rounded-xl max-h-[80vh] sm:max-h-[60vh] border', className)}
  83          src={src}
  84          onClick={(e) => e.stopPropagation()}
  85          onPlay={(event) => {
  86            mediaManager.play(event.currentTarget)
  87          }}
  88          muted={muteMedia}
  89          onError={() => setError(true)}
  90        />
  91      </div>
  92    )
  93  }
  94