Media Viewer: Fix video controls (#1997)

This commit is contained in:
Alexander Zinchuk 2022-08-17 10:38:18 +02:00
parent b7be72f5d9
commit c54e01b8e0
4 changed files with 37 additions and 33 deletions

View File

@ -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}
/>
)}

View File

@ -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>

View File

@ -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}

View File

@ -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);