From aa1cd0a3b985f742aa76f385e7c21b86aae7d50d Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Thu, 30 Mar 2023 18:25:17 -0500 Subject: [PATCH] Media Viewer: Fix volume control for narrow video (#2837) --- .../mediaViewer/MediaViewerContent.tsx | 22 ++++++++++++++++--- .../mediaViewer/MediaViewerFooter.scss | 19 ++++++++-------- .../mediaViewer/MediaViewerFooter.tsx | 4 +++- src/components/mediaViewer/VideoPlayer.tsx | 11 ++++++---- .../mediaViewer/VideoPlayerControls.scss | 21 +++++++++++------- .../mediaViewer/VideoPlayerControls.tsx | 4 ++-- 6 files changed, 53 insertions(+), 28 deletions(-) diff --git a/src/components/mediaViewer/MediaViewerContent.tsx b/src/components/mediaViewer/MediaViewerContent.tsx index 7fa4f8832..3600759af 100644 --- a/src/components/mediaViewer/MediaViewerContent.tsx +++ b/src/components/mediaViewer/MediaViewerContent.tsx @@ -57,6 +57,7 @@ type StateProps = { }; const ANIMATION_DURATION = 350; +const MOBILE_VERSION_CONTROL_WIDTH = 350; const MediaViewerContent: FC = (props) => { const { @@ -105,6 +106,10 @@ const MediaViewerContent: FC = (props) => { setControlsVisible?.(isVisible); }, [setControlsVisible]); + const toggleControlsOnMove = useCallback(() => { + toggleControls(true); + }, [toggleControls]); + if (avatarOwner || actionPhoto) { if (!isVideoAvatar) { return ( @@ -149,21 +154,25 @@ const MediaViewerContent: FC = (props) => { const textParts = message.content.action?.type === 'suggestProfilePhoto' ? lang('Conversation.SuggestedPhotoTitle') : renderMessageText(message); + const hasFooter = Boolean(textParts); + const posterSize = message && calculateMediaViewerDimensions(dimensions!, hasFooter, isVideo); + const isForceMobileVersion = isMobile || shouldForceMobileVersion(posterSize); return (
{isPhoto && renderPhoto( bestData, - message && calculateMediaViewerDimensions(dimensions!, hasFooter), + posterSize, !isMobile && !isProtected, isProtected, )} {isVideo && (!isActive ? renderVideoPreview( bestImageData, - message && calculateMediaViewerDimensions(dimensions!, hasFooter, true), + posterSize, !isMobile && !isProtected, isProtected, ) : ( @@ -172,7 +181,7 @@ const MediaViewerContent: FC = (props) => { url={bestData} isGif={isGif} posterData={bestImageData} - posterSize={message && calculateMediaViewerDimensions(dimensions!, hasFooter, true)} + posterSize={posterSize} loadProgress={loadProgress} fileSize={videoSize!} areControlsVisible={areControlsVisible} @@ -182,6 +191,7 @@ const MediaViewerContent: FC = (props) => { onClose={onClose} isMuted={isMuted} isHidden={isHidden} + isForceMobileVersion={isForceMobileVersion} isProtected={isProtected} volume={volume} isClickDisabled={isMoving} @@ -193,6 +203,7 @@ const MediaViewerContent: FC = (props) => { text={textParts} onClick={onFooterClick} isProtected={isProtected} + isForceMobileVersion={isForceMobileVersion} isHidden={IS_TOUCH_ENV ? !areControlsVisible : false} isForVideo={isVideo && !isGif} /> @@ -342,3 +353,8 @@ function renderVideoPreview(blobUrl?: string, imageSize?: ApiDimensions, canDrag
); } + +function shouldForceMobileVersion(posterSize?: { width: number; height: number }) { + if (!posterSize) return false; + return posterSize.width < MOBILE_VERSION_CONTROL_WIDTH; +} diff --git a/src/components/mediaViewer/MediaViewerFooter.scss b/src/components/mediaViewer/MediaViewerFooter.scss index da84ee56a..ece0e37ae 100644 --- a/src/components/mediaViewer/MediaViewerFooter.scss +++ b/src/components/mediaViewer/MediaViewerFooter.scss @@ -13,7 +13,7 @@ padding: 0.5rem 0 0; } - @media (max-width: 600px) { + &.mobile { background: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0) 100%); &.is-for-video { @@ -26,6 +26,14 @@ pointer-events: auto; } } + .media-viewer-footer-content { + opacity: 1; + z-index: 1; + } + .media-text.multiline::before { + display: none; + background: none; + } } body.ghost-animating & { @@ -43,10 +51,6 @@ &:hover { opacity: 1; } - - @media (max-width: 600px) { - opacity: 1; - } } &.is-hidden { @@ -84,11 +88,6 @@ background: rgba(0, 0, 0, 0.75); border-radius: var(--border-radius-default); z-index: var(--z-below); - - @media (max-width: 600px) { - display: none; - background: none; - } } } } diff --git a/src/components/mediaViewer/MediaViewerFooter.tsx b/src/components/mediaViewer/MediaViewerFooter.tsx index 2e294183a..596acfa80 100644 --- a/src/components/mediaViewer/MediaViewerFooter.tsx +++ b/src/components/mediaViewer/MediaViewerFooter.tsx @@ -17,11 +17,12 @@ type OwnProps = { onClick: () => void; isHidden?: boolean; isForVideo: boolean; + isForceMobileVersion?: boolean; isProtected?: boolean; }; const MediaViewerFooter: FC = ({ - text = '', isHidden, isForVideo, onClick, isProtected, + text = '', isHidden, isForVideo, onClick, isProtected, isForceMobileVersion, }) => { const [isMultiline, setIsMultiline] = useState(false); const { isMobile } = useAppLayout(); @@ -58,6 +59,7 @@ const MediaViewerFooter: FC = ({ isForVideo && 'is-for-video', isHidden && 'is-hidden', isProtected && 'is-protected', + isForceMobileVersion && 'mobile', ); return ( diff --git a/src/components/mediaViewer/VideoPlayer.tsx b/src/components/mediaViewer/VideoPlayer.tsx index fc02f8889..240f8ecf9 100644 --- a/src/components/mediaViewer/VideoPlayer.tsx +++ b/src/components/mediaViewer/VideoPlayer.tsx @@ -38,12 +38,12 @@ type OwnProps = { isProtected?: boolean; areControlsVisible: boolean; shouldCloseOnClick?: boolean; + isForceMobileVersion?: boolean; toggleControls: (isVisible: boolean) => void; onClose: (e: React.MouseEvent) => void; isClickDisabled?: boolean; }; -const MOBILE_VERSION_CONTROL_WIDTH = 400; const MAX_LOOP_DURATION = 30; // Seconds const VideoPlayer: FC = ({ @@ -59,6 +59,7 @@ const VideoPlayer: FC = ({ isMuted, playbackRate, onClose, + isForceMobileVersion, toggleControls, areControlsVisible, shouldCloseOnClick, @@ -226,13 +227,15 @@ const VideoPlayer: FC = ({ const wrapperStyle = posterSize && `width: ${posterSize.width}px; height: ${posterSize.height}px`; const videoStyle = `background-image: url(${posterData})`; + const shouldToggleControls = !IS_TOUCH_ENV && !isForceMobileVersion; const duration = videoRef.current?.duration || 0; return ( + // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
= ({ duration={duration} isVisible={areControlsVisible} setVisibility={toggleControls} - isForceMobileVersion={posterSize && posterSize.width < MOBILE_VERSION_CONTROL_WIDTH} + isForceMobileVersion={isForceMobileVersion} onSeek={handleSeek} onChangeFullscreen={handleFullscreenChange} onPictureInPictureChange={enterPictureInPicture} diff --git a/src/components/mediaViewer/VideoPlayerControls.scss b/src/components/mediaViewer/VideoPlayerControls.scss index 473d57e64..141454f8b 100644 --- a/src/components/mediaViewer/VideoPlayerControls.scss +++ b/src/components/mediaViewer/VideoPlayerControls.scss @@ -18,10 +18,16 @@ display: none; } - @media (max-width: 600px) { + &.mobile { position: fixed; padding: 2.25rem 0.5rem 0.75rem; background: none; + .player-seekline { + top: 1rem; + } + .playback-rate-menu .bubble { + bottom: 4.6875rem; + } } &.active { @@ -44,6 +50,9 @@ padding: 0; margin: 0.25rem; height: 2rem; + @media (max-width: 320px) { + margin: 0.125rem; + } } .volume-slider { @@ -91,6 +100,9 @@ overflow: hidden; text-overflow: ellipsis; margin-left: 0.5rem; + @media (max-width: 320px) { + margin: 0.25rem; + } } .player-seekline { @@ -102,10 +114,6 @@ touch-action: none; cursor: pointer; - @media (max-width: 600px) { - top: 1rem; - } - &-track { position: absolute; top: 50%; @@ -155,9 +163,6 @@ min-width: 3.5rem; margin-right: 5.8125rem; bottom: 4.1875rem; - @media (max-width: 600px) { - bottom: 4.6875rem; - } } &.no-fullscreen, &.no-pip { diff --git a/src/components/mediaViewer/VideoPlayerControls.tsx b/src/components/mediaViewer/VideoPlayerControls.tsx index 0fdc76448..75f086aa2 100644 --- a/src/components/mediaViewer/VideoPlayerControls.tsx +++ b/src/components/mediaViewer/VideoPlayerControls.tsx @@ -94,7 +94,7 @@ const VideoPlayerControls: FC = ({ const { isMobile } = useAppLayout(); useEffect(() => { - if (!IS_TOUCH_ENV) return undefined; + if (!IS_TOUCH_ENV && !isForceMobileVersion) return undefined; let timeout: number | undefined; if (!isVisible || !isPlaying || isSeeking || isPlaybackMenuOpen) { if (timeout) window.clearTimeout(timeout); @@ -106,7 +106,7 @@ const VideoPlayerControls: FC = ({ return () => { if (timeout) window.clearTimeout(timeout); }; - }, [isPlaying, isVisible, isSeeking, setVisibility, isPlaybackMenuOpen]); + }, [isPlaying, isVisible, isSeeking, setVisibility, isPlaybackMenuOpen, isForceMobileVersion]); useEffect(() => { if (isVisible) {