kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Update video feature
rodzic
3e93ce2281
commit
37fb14816d
|
@ -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;
|
Ładowanie…
Reference in New Issue