import useUserVideos, { useMutationLikeAVideo } from '@/web/api/hooks/useUserVideos' import player from '@/web/states/player' import uiStates from '@/web/states/uiStates' import { formatDuration } from '@/web/utils/common' import { css, cx } from '@emotion/css' import { motion, useAnimationControls } from 'framer-motion' import React, { useEffect, useMemo, useRef } from 'react' import Icon from '../Icon' import Slider from '../Slider' import { proxy, useSnapshot } from 'valtio' import { throttle } from 'lodash-es' const videoStates = proxy({ currentTime: 0, duration: 0, isPaused: true, isFullscreen: false, }) const VideoInstance = ({ src, poster }: { src: string; poster: string }) => { const videoRef = useRef(null) const videoContainerRef = useRef(null) const video = videoRef.current const { isPaused, isFullscreen } = useSnapshot(videoStates) useEffect(() => { if (!video || !src) return const handleDurationChange = () => (videoStates.duration = video.duration * 1000) const handleTimeUpdate = () => (videoStates.currentTime = video.currentTime * 1000) const handleFullscreenChange = () => (videoStates.isFullscreen = !!document.fullscreenElement) const handlePause = () => (videoStates.isPaused = true) const handlePlay = () => (videoStates.isPaused = false) video.addEventListener('timeupdate', handleTimeUpdate) video.addEventListener('durationchange', handleDurationChange) document.addEventListener('fullscreenchange', handleFullscreenChange) video.addEventListener('pause', handlePause) video.addEventListener('play', handlePlay) return () => { video.removeEventListener('timeupdate', handleTimeUpdate) video.removeEventListener('durationchange', handleDurationChange) document.removeEventListener('fullscreenchange', handleFullscreenChange) video.removeEventListener('pause', handlePause) video.removeEventListener('play', handlePlay) } }) // if video is playing, pause music useEffect(() => { if (!isPaused) player.pause() }, [isPaused]) const togglePlay = () => { videoStates.isPaused ? videoRef.current?.play() : videoRef.current?.pause() } const toggleFullscreen = async () => { if (document.fullscreenElement) { document.exitFullscreen() videoStates.isFullscreen = false } else { if (videoContainerRef.current) { videoContainerRef.current.requestFullscreen() videoStates.isFullscreen = true } } } // reset video state when src changes useEffect(() => { videoStates.currentTime = 0 videoStates.duration = 0 videoStates.isPaused = true videoStates.isFullscreen = false }, [src]) // animation controls const animationControls = useAnimationControls() const controlsTimestamp = useRef(0) const isControlsVisible = useRef(false) const isMouseOverControls = useRef(false) // hide controls after 2 seconds const showControls = () => { isControlsVisible.current = true controlsTimestamp.current = Date.now() animationControls.start('visible') } const hideControls = () => { isControlsVisible.current = false animationControls.start('hidden') } useEffect(() => { if (!isFullscreen) return const interval = setInterval(() => { if ( isControlsVisible.current && Date.now() - controlsTimestamp.current > 2000 && !isMouseOverControls.current ) { hideControls() } }, 300) return () => clearInterval(interval) }, [isFullscreen]) if (!src) return null return ( !isControlsVisible.current && isFullscreen && showControls()} > ) } const Controls = ({ videoRef, toggleFullscreen, togglePlay, onMouseOver, onMouseOut, }: { videoRef: React.RefObject toggleFullscreen: () => void togglePlay: () => void onMouseOver: () => void onMouseOut: () => void }) => { const video = videoRef.current const { playingVideoID } = useSnapshot(uiStates) const { currentTime, duration, isPaused, isFullscreen } = useSnapshot(videoStates) const { data: likedVideos } = useUserVideos() const isLiked = useMemo(() => { return !!likedVideos?.data?.find(video => String(video.vid) === String(playingVideoID)) }, [likedVideos]) const likeAVideo = useMutationLikeAVideo() const onLike = async () => { if (playingVideoID) likeAVideo.mutateAsync(playingVideoID) } // keyboard shortcuts useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { switch (e.key) { case 'Enter': toggleFullscreen() break case ' ': togglePlay() break } } document.addEventListener('keydown', handleKeyDown) return () => document.removeEventListener('keydown', handleKeyDown) }, []) const animationVariants = { hidden: { y: '48px', opacity: 0 }, visible: { y: 0, opacity: 1 }, } const animationTransition = { type: 'spring', bounce: 0.4, duration: 0.5 } return (
e.stopPropagation()} onMouseOver={onMouseOver} onMouseOut={onMouseOut}> {/* Current Time */} {formatDuration(currentTime || 0, 'en-US', 'hh:mm:ss')} {/* Controls */} {/* Slider */}
video?.currentTime && (video.currentTime = value)} onlyCallOnChangeAfterDragEnded={true} />
{/* Duration */} {formatDuration(duration || 0, 'en-US', 'hh:mm:ss')}
) } export default VideoInstance