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