Media Viewer: Fix video controls (#1997)
This commit is contained in:
parent
b7be72f5d9
commit
c54e01b8e0
@ -33,8 +33,8 @@ type OwnProps = {
|
||||
animationLevel: 0 | 1 | 2;
|
||||
onClose: () => void;
|
||||
onFooterClick: () => void;
|
||||
setIsFooterHidden?: (isHidden: boolean) => void;
|
||||
isFooterHidden?: boolean;
|
||||
setControlsVisible?: (isVisible: boolean) => void;
|
||||
areControlsVisible: boolean;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
@ -62,14 +62,14 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
||||
message,
|
||||
origin,
|
||||
animationLevel,
|
||||
isFooterHidden,
|
||||
areControlsVisible,
|
||||
isProtected,
|
||||
volume,
|
||||
playbackRate,
|
||||
isMuted,
|
||||
onClose,
|
||||
onFooterClick,
|
||||
setIsFooterHidden,
|
||||
setControlsVisible,
|
||||
} = props;
|
||||
|
||||
const isGhostAnimation = animationLevel === 2;
|
||||
@ -94,16 +94,8 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
||||
const isOpen = Boolean(avatarOwner || mediaId);
|
||||
|
||||
const toggleControls = useCallback((isVisible) => {
|
||||
setIsFooterHidden?.(!isVisible);
|
||||
}, [setIsFooterHidden]);
|
||||
|
||||
const handleMouseMove = useCallback(() => {
|
||||
toggleControls(true);
|
||||
}, [toggleControls]);
|
||||
|
||||
const handleMouseOut = useCallback(() => {
|
||||
toggleControls(false);
|
||||
}, [toggleControls]);
|
||||
setControlsVisible?.(isVisible);
|
||||
}, [setControlsVisible]);
|
||||
|
||||
if (avatarOwner) {
|
||||
if (!isVideoAvatar) {
|
||||
@ -129,7 +121,7 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
||||
loadProgress={loadProgress}
|
||||
fileSize={videoSize!}
|
||||
isMediaViewerOpen={isOpen && isActive}
|
||||
areControlsVisible={!isFooterHidden}
|
||||
areControlsVisible={areControlsVisible}
|
||||
toggleControls={toggleControls}
|
||||
isProtected={isProtected}
|
||||
noPlay={!isActive}
|
||||
@ -150,8 +142,6 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
||||
return (
|
||||
<div
|
||||
className={buildClassName('MediaViewerContent', hasFooter && 'has-footer')}
|
||||
onMouseMove={!isGif && !IS_TOUCH_ENV ? handleMouseMove : undefined}
|
||||
onMouseOut={!isGif && !IS_TOUCH_ENV ? handleMouseOut : undefined}
|
||||
>
|
||||
{isPhoto && renderPhoto(
|
||||
localBlobUrl || fullMediaBlobUrl || previewBlobUrl || pictogramBlobUrl,
|
||||
@ -173,8 +163,8 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
||||
posterSize={message && calculateMediaViewerDimensions(dimensions!, hasFooter, true)}
|
||||
loadProgress={loadProgress}
|
||||
fileSize={videoSize!}
|
||||
areControlsVisible={areControlsVisible}
|
||||
isMediaViewerOpen={isOpen && isActive}
|
||||
areControlsVisible={!isFooterHidden}
|
||||
toggleControls={toggleControls}
|
||||
noPlay={!isActive}
|
||||
onClose={onClose}
|
||||
@ -189,7 +179,7 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
||||
text={textParts}
|
||||
onClick={onFooterClick}
|
||||
isProtected={isProtected}
|
||||
isHidden={isFooterHidden}
|
||||
isHidden={IS_TOUCH_ENV ? !areControlsVisible : false}
|
||||
isForVideo={isVideo && !isGif}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -97,7 +97,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
const prevZoomLevelChange = usePrevious(zoomLevelChange);
|
||||
const hasZoomChanged = prevZoomLevelChange !== undefined && prevZoomLevelChange !== zoomLevelChange;
|
||||
const forceUpdate = useForceUpdate();
|
||||
const [isFooterHidden, setIsFooterHidden] = useState(true);
|
||||
const [areControlsVisible, setControlsVisible] = useState(false);
|
||||
const [isMouseDown, setIsMouseDown] = useState(false);
|
||||
const { height: windowHeight, width: windowWidth, isResizing } = useWindowSize();
|
||||
const { onClose } = rest;
|
||||
@ -121,15 +121,15 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
const shouldCloseOnVideo = isGif && !IS_IOS;
|
||||
const clickXThreshold = IS_TOUCH_ENV ? 40 : windowWidth / 10;
|
||||
|
||||
const handleToggleFooterVisibility = useCallback((e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
const handleControlsVisibility = useCallback((e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
if (!IS_TOUCH_ENV) return;
|
||||
const isFooter = windowHeight - e.pageY < CLICK_Y_THRESHOLD;
|
||||
if (!isFooter && e.pageX < clickXThreshold) return;
|
||||
if (!isFooter && e.pageX > windowWidth - clickXThreshold) return;
|
||||
setIsFooterHidden(!isFooterHidden);
|
||||
}, [clickXThreshold, isFooterHidden, windowHeight, windowWidth]);
|
||||
setControlsVisible(!areControlsVisible);
|
||||
}, [clickXThreshold, areControlsVisible, windowHeight, windowWidth]);
|
||||
|
||||
useTimeout(() => setIsFooterHidden(false), ANIMATION_DURATION - 150);
|
||||
useTimeout(() => setControlsVisible(true), ANIMATION_DURATION + 100);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current || activeMediaId === undefined) {
|
||||
@ -665,7 +665,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
/* eslint-disable-next-line react/jsx-props-no-spreading */
|
||||
{...rest}
|
||||
animationLevel={animationLevel}
|
||||
isFooterHidden={isFooterHidden}
|
||||
areControlsVisible={areControlsVisible}
|
||||
mediaId={prevMediaId}
|
||||
/>
|
||||
</div>
|
||||
@ -676,7 +676,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
'MediaViewerSlide--active',
|
||||
isMouseDown && scale > 1 && 'MediaViewerSlide--moving',
|
||||
)}
|
||||
onClick={handleToggleFooterVisibility}
|
||||
onClick={handleControlsVisibility}
|
||||
ref={activeSlideRef}
|
||||
style={getAnimationStyle(offsetX, offsetY, scale)}
|
||||
>
|
||||
@ -686,8 +686,8 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
mediaId={activeMediaId}
|
||||
animationLevel={animationLevel}
|
||||
isActive={isActiveRef.current}
|
||||
setIsFooterHidden={setIsFooterHidden}
|
||||
isFooterHidden={isFooterHidden || scale !== 1}
|
||||
setControlsVisible={setControlsVisible}
|
||||
areControlsVisible={areControlsVisible && scale === 1}
|
||||
/>
|
||||
</div>
|
||||
{hasNext && scale === 1 && !isResizing && (
|
||||
@ -696,7 +696,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
/* eslint-disable-next-line react/jsx-props-no-spreading */
|
||||
{...rest}
|
||||
animationLevel={animationLevel}
|
||||
isFooterHidden={isFooterHidden}
|
||||
areControlsVisible={areControlsVisible}
|
||||
mediaId={nextMediaId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -29,11 +29,11 @@ type OwnProps = {
|
||||
fileSize: number;
|
||||
isMediaViewerOpen?: boolean;
|
||||
noPlay?: boolean;
|
||||
areControlsVisible: boolean;
|
||||
volume: number;
|
||||
isMuted: boolean;
|
||||
playbackRate: number;
|
||||
isProtected?: boolean;
|
||||
areControlsVisible: boolean;
|
||||
toggleControls: (isVisible: boolean) => void;
|
||||
onClose: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||
};
|
||||
@ -66,9 +66,20 @@ const VideoPlayer: FC<OwnProps> = ({
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const [isPlayed, setIsPlayed] = useState(!IS_TOUCH_ENV || !IS_IOS);
|
||||
const [currentTime, setCurrentTime] = useState(0);
|
||||
|
||||
const [isFullscreen, setFullscreen, exitFullscreen] = useFullscreenStatus(videoRef, setIsPlayed);
|
||||
|
||||
const handleVideoMove = useCallback(() => {
|
||||
toggleControls(true);
|
||||
}, [toggleControls]);
|
||||
|
||||
const handleVideoLeave = useCallback((e) => {
|
||||
const bounds = videoRef.current?.getBoundingClientRect();
|
||||
if (!bounds) return;
|
||||
if (e.clientX < bounds.left || e.clientX > bounds.right || e.clientY < bounds.top || e.clientY > bounds.bottom) {
|
||||
toggleControls(false);
|
||||
}
|
||||
}, [toggleControls]);
|
||||
|
||||
const {
|
||||
isBuffered, bufferedRanges, bufferingHandlers, bufferedProgress,
|
||||
} = useBuffering();
|
||||
@ -178,6 +189,8 @@ const VideoPlayer: FC<OwnProps> = ({
|
||||
return (
|
||||
<div
|
||||
className="VideoPlayer"
|
||||
onMouseMove={!IS_TOUCH_ENV ? handleVideoMove : undefined}
|
||||
onMouseOut={!IS_TOUCH_ENV ? handleVideoLeave : undefined}
|
||||
>
|
||||
<div
|
||||
style={wrapperStyle}
|
||||
|
||||
@ -5,7 +5,7 @@ import React, {
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
|
||||
import useFlag from '../../hooks/useFlag';
|
||||
import { IS_IOS, IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment';
|
||||
import { IS_IOS, IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../util/environment';
|
||||
import { formatMediaDuration } from '../../util/dateFormat';
|
||||
import { formatFileSize } from '../../util/textFormat';
|
||||
import useLang from '../../hooks/useLang';
|
||||
@ -54,7 +54,7 @@ const PLAYBACK_RATES = [
|
||||
2,
|
||||
];
|
||||
|
||||
const HIDE_CONTROLS_TIMEOUT_MS = 1500;
|
||||
const HIDE_CONTROLS_TIMEOUT_MS = 3000;
|
||||
|
||||
const VideoPlayerControls: FC<OwnProps> = ({
|
||||
bufferedRanges,
|
||||
@ -86,6 +86,7 @@ const VideoPlayerControls: FC<OwnProps> = ({
|
||||
const isSeeking = isSeekingRef.current;
|
||||
|
||||
useEffect(() => {
|
||||
if (!IS_TOUCH_ENV) return undefined;
|
||||
let timeout: number | undefined;
|
||||
if (!isVisible || !isPlayed || isSeeking || isPlaybackMenuOpen) {
|
||||
if (timeout) window.clearTimeout(timeout);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user