Update video feature

environments/review-update-vid-g70vyz/deployments/5013
danidfra 2024-10-31 17:55:09 -03:00
rodzic 3e93ce2281
commit 37fb14816d
1 zmienionych plików z 56 dodań i 8 usunięć

Wyświetl plik

@ -6,6 +6,7 @@ import { defineMessages, useIntl } from 'react-intl';
import Blurhash from 'soapbox/components/blurhash'; import Blurhash from 'soapbox/components/blurhash';
import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
import { useIsMobile } from 'soapbox/hooks/useIsMobile';
import { isPanoramic, isPortrait, minimumAspectRatio, maximumAspectRatio } from 'soapbox/utils/media-aspect-ratio'; import { isPanoramic, isPortrait, minimumAspectRatio, maximumAspectRatio } from 'soapbox/utils/media-aspect-ratio';
import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen'; import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen';
@ -129,11 +130,13 @@ const Video: React.FC<IVideo> = ({
blurhash, blurhash,
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const isMobile = useIsMobile();
const player = useRef<HTMLDivElement>(null); const player = useRef<HTMLDivElement>(null);
const video = useRef<HTMLVideoElement>(null); const video = useRef<HTMLVideoElement>(null);
const seek = useRef<HTMLDivElement>(null); const seek = useRef<HTMLDivElement>(null);
const slider = useRef<HTMLDivElement>(null); const slider = useRef<HTMLDivElement>(null);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const [currentTime, setCurrentTime] = useState(0); const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0); const [duration, setDuration] = useState(0);
@ -144,6 +147,7 @@ const Video: React.FC<IVideo> = ({
const [containerWidth, setContainerWidth] = useState(width); const [containerWidth, setContainerWidth] = useState(width);
const [fullscreen, setFullscreen] = useState(false); const [fullscreen, setFullscreen] = useState(false);
const [hovered, setHovered] = useState(false); const [hovered, setHovered] = useState(false);
const [volumeHovered, setVolumeHovered] = useState(false);
const [seekHovered, setSeekHovered] = useState(false); const [seekHovered, setSeekHovered] = useState(false);
const [muted, setMuted] = useState(false); const [muted, setMuted] = useState(false);
const [buffer, setBuffer] = useState(0); const [buffer, setBuffer] = useState(0);
@ -397,6 +401,36 @@ const Video: React.FC<IVideo> = ({
setSeekHovered(false); setSeekHovered(false);
}; };
const handleVolumeEnter = () => {
setVolumeHovered(true);
};
const handleVolumeLeave = () => {
setVolumeHovered(false);
};
const handleClickStart = () => {
setHovered(true);
setHovered(true);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
timeoutRef.current = setTimeout(() => {
setHovered(false);
}, 2 * 1000);
};
const handleOnMouseMove = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
handleClickStart();
};
const toggleMute = () => { const toggleMute = () => {
if (video.current) { if (video.current) {
const muted = !video.current.muted; const muted = !video.current.muted;
@ -434,9 +468,17 @@ const Video: React.FC<IVideo> = ({
} }
}; };
const handleTogglePlay = () => {
if (!isMobile) togglePlay();
};
const progress = (currentTime / duration) * 100; const progress = (currentTime / duration) * 100;
const playerStyle: React.CSSProperties = {}; const playerStyle: React.CSSProperties = {};
const startTimeout = () => {
timeoutRef.current = setTimeout(() => setHovered(false), 1000);
};
if (inline && containerWidth) { if (inline && containerWidth) {
width = containerWidth; width = containerWidth;
const minSize = containerWidth / (16 / 9); const minSize = containerWidth / (16 / 9);
@ -481,10 +523,13 @@ const Video: React.FC<IVideo> = ({
return ( return (
<div <div
role='menuitem' role='menuitem'
className={clsx('relative box-border max-w-full overflow-hidden rounded-[10px] bg-black text-white focus:outline-0', { detailed, 'w-full h-full m-0': fullscreen })} className={clsx('relative box-border flex max-w-full overflow-hidden rounded-[10px] bg-black text-white focus:outline-0', { 'w-full h-full m-0': fullscreen })}
style={playerStyle} style={playerStyle}
ref={player} ref={player}
onClick={handleClickRoot} onClick={handleClickRoot}
onMouseMove={handleOnMouseMove}
onMouseOut={startTimeout}
onBlur={startTimeout}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
tabIndex={0} tabIndex={0}
> >
@ -506,7 +551,7 @@ const Video: React.FC<IVideo> = ({
})} })}
width={width} width={width}
height={height || DEFAULT_HEIGHT} height={height || DEFAULT_HEIGHT}
onClick={togglePlay} onClick={handleTogglePlay}
onKeyDown={handleVideoKeyDown} onKeyDown={handleVideoKeyDown}
onPlay={handlePlay} onPlay={handlePlay}
onPause={handlePause} onPause={handlePause}
@ -516,7 +561,10 @@ const Video: React.FC<IVideo> = ({
onVolumeChange={handleVolumeChange} onVolumeChange={handleVolumeChange}
/> />
<div className={clsx('absolute inset-x-0 bottom-0 z-20 box-border bg-gradient-to-t from-black/70 to-transparent px-[15px] opacity-0 transition-opacity duration-100 ease-linear', { 'opacity-100': paused || hovered })}> <div
className={clsx('absolute inset-x-0 bottom-0 z-20 box-border bg-gradient-to-t from-black/70 to-transparent px-[15px] opacity-0 transition-opacity duration-100 ease-linear', { 'opacity-100': paused || hovered })}
// onClick={handleClickStart}
>
<div className='relative h-6 cursor-pointer' onMouseDown={handleMouseDown} onMouseEnter={handleSeekEnter} onMouseLeave={handleSeekLeave} ref={seek}> <div className='relative h-6 cursor-pointer' onMouseDown={handleMouseDown} onMouseEnter={handleSeekEnter} onMouseLeave={handleSeekLeave} ref={seek}>
<div <div
style={{ style={{
@ -569,9 +617,9 @@ const Video: React.FC<IVideo> = ({
</button> </button>
<div <div
className={clsx('relative inline-flex h-6 flex-none cursor-pointer overflow-hidden transition-all duration-100 ease-linear', { 'overflow-visible w-[50px] mr-[16px]': hovered })} onMouseDown={handleVolumeMouseDown} ref={slider} className={clsx('relative inline-flex h-6 flex-none cursor-pointer overflow-hidden transition-all duration-100 ease-linear', { 'overflow-visible w-[50px] mr-[16px]': volumeHovered })} onMouseDown={handleVolumeMouseDown} ref={slider}
onMouseEnter={handleMouseEnter} onMouseEnter={handleVolumeEnter}
onMouseLeave={handleMouseLeave} onMouseLeave={handleVolumeLeave}
> >
<div <div
className={clsx({ 'bottom-[27px]': fullscreen || detailed })} className={clsx({ 'bottom-[27px]': fullscreen || detailed })}
@ -590,7 +638,7 @@ const Video: React.FC<IVideo> = ({
/> />
<div className={clsx('absolute left-0 top-1/2 block h-1 -translate-y-1/2 rounded-md bg-accent-500', { 'bottom-[27px]': fullscreen || detailed })} style={{ width: `${volume * 100}%` }} /> <div className={clsx('absolute left-0 top-1/2 block h-1 -translate-y-1/2 rounded-md bg-accent-500', { 'bottom-[27px]': fullscreen || detailed })} style={{ width: `${volume * 100}%` }} />
<span <span
className={clsx('absolute left-0 top-1/2 z-30 -ml-1.5 size-3 -translate-y-1/2 rounded-full bg-accent-500 opacity-0 shadow-[1px_2px_6px_rgba(0,0,0,0.3)] transition-opacity duration-100', { 'opacity-100': hovered, 'bottom-[23px]': fullscreen || detailed })} className={clsx('absolute left-0 top-1/2 z-30 -ml-1.5 size-3 -translate-y-1/2 rounded-full bg-accent-500 opacity-0 shadow-[1px_2px_6px_rgba(0,0,0,0.3)] transition-opacity duration-100', { 'opacity-100': volumeHovered, 'bottom-[23px]': fullscreen || detailed })}
tabIndex={0} tabIndex={0}
style={{ left: `${volume * 100}%` }} style={{ left: `${volume * 100}%` }}
/> />
@ -628,4 +676,4 @@ const Video: React.FC<IVideo> = ({
); );
}; };
export default Video; export default Video;