'use client' import { cx } from 'class-variance-authority' import { useCallback, useEffect, useRef, useState } from 'react' import { languages } from '@scandic-hotels/common/constants/language' import { useIntl } from 'react-intl' import Image from '../Image' import { VideoPlayerButton } from './Button' import { VideoPlayerProps } from './types' import { useVideoDimensions } from './useVideoDimensions' import { getVideoPropsByVariant } from './utils' import { variants } from './variants' import styles from './videoPlayer.module.css' export function VideoPlayer({ sources, captions, focalPoint = { x: 50, y: 50 }, className, variant = 'inline', poster, autoPlay, hasOverlay, }: VideoPlayerProps) { const intl = useIntl() const videoRef = useRef(null) const shouldAutoPlay = (variant === 'hero' && (autoPlay ?? true)) || !!autoPlay const [hasManuallyPlayed, setHasManuallyPlayed] = useState(false) const [hasToggledMute, setHasToggledMute] = useState(false) const [isPlaying, setIsPlaying] = useState(shouldAutoPlay) const [isMuted, setIsMuted] = useState(true) const [userPaused, setUserPaused] = useState(false) const [showPoster, setShowPoster] = useState(!shouldAutoPlay) const { containerRef, handleMetadataLoaded, containerWidth, hasError, handleError, } = useVideoDimensions() const defaultProps = getVideoPropsByVariant( variant, hasManuallyPlayed, shouldAutoPlay ) const classNames = variants({ className, variant, }) const showPlayButton = !hasError && (variant === 'hero' || (variant === 'inline' && !hasManuallyPlayed)) const showMuteButton = !hasError && variant === 'inline' && hasManuallyPlayed && !hasToggledMute const handleIntersection = useCallback( (entries: IntersectionObserverEntry[]) => { entries.forEach((entry) => { if (entry.intersectionRatio >= 0.1 && !userPaused) { videoRef.current?.play() } else if (entry.intersectionRatio < 0.1) { videoRef.current?.pause() } }) }, [userPaused] ) function togglePlay() { const videoElement = videoRef.current if (videoElement) { if (variant === 'hero') { if (videoElement.paused) { setUserPaused(false) videoElement.play() } else { setUserPaused(true) videoElement.pause() } } else { setHasManuallyPlayed(true) videoElement.play() } } } function handleMuteToggle() { const videoElement = videoRef.current if (videoElement) { const currentlyMuted = videoElement.muted videoElement.muted = !currentlyMuted setIsMuted(!currentlyMuted) setHasToggledMute(true) } } function handleVolumeChangeEvent(event: React.UIEvent) { if (event.currentTarget.muted && !isMuted) { setIsMuted(true) } else if (!event.currentTarget.muted && isMuted) { setIsMuted(false) } } function handlePlay() { setShowPoster(false) setIsPlaying(true) } useEffect(() => { const videoElement = videoRef.current if (!videoElement || variant !== 'hero') { return } const observer = new IntersectionObserver(handleIntersection, { // Play video when at least 10% of it is visible threshold: [0, 0.1, 1], }) observer.observe(videoElement) return () => { observer.disconnect() } }, [variant, handleIntersection]) if (!sources.length) { return null } // Sort sources to prioritize WebM format for better compression const sortedSources = [...sources].sort((a, b) => { const aIsWebM = a.type.includes('webm') const bIsWebM = b.type.includes('webm') return aIsWebM === bIsWebM ? 0 : aIsWebM ? -1 : 1 }) return (
{(showPoster || hasError) && poster ? ( ) : null} {showPlayButton ? ( ) : null} {showMuteButton ? ( ) : null}
) }