Media Viewer: Fix volume control for narrow video (#2837)

This commit is contained in:
Alexander Zinchuk 2023-03-30 18:25:17 -05:00
parent 1ad2e816a2
commit aa1cd0a3b9
6 changed files with 53 additions and 28 deletions

View File

@ -57,6 +57,7 @@ type StateProps = {
};
const ANIMATION_DURATION = 350;
const MOBILE_VERSION_CONTROL_WIDTH = 350;
const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
const {
@ -105,6 +106,10 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
setControlsVisible?.(isVisible);
}, [setControlsVisible]);
const toggleControlsOnMove = useCallback(() => {
toggleControls(true);
}, [toggleControls]);
if (avatarOwner || actionPhoto) {
if (!isVideoAvatar) {
return (
@ -149,21 +154,25 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (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 (
<div
className={buildClassName('MediaViewerContent', hasFooter && 'has-footer')}
onMouseMove={isForceMobileVersion && !IS_TOUCH_ENV ? toggleControlsOnMove : undefined}
>
{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<OwnProps & StateProps> = (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<OwnProps & StateProps> = (props) => {
onClose={onClose}
isMuted={isMuted}
isHidden={isHidden}
isForceMobileVersion={isForceMobileVersion}
isProtected={isProtected}
volume={volume}
isClickDisabled={isMoving}
@ -193,6 +203,7 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (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
</div>
);
}
function shouldForceMobileVersion(posterSize?: { width: number; height: number }) {
if (!posterSize) return false;
return posterSize.width < MOBILE_VERSION_CONTROL_WIDTH;
}

View File

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

View File

@ -17,11 +17,12 @@ type OwnProps = {
onClick: () => void;
isHidden?: boolean;
isForVideo: boolean;
isForceMobileVersion?: boolean;
isProtected?: boolean;
};
const MediaViewerFooter: FC<OwnProps> = ({
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<OwnProps> = ({
isForVideo && 'is-for-video',
isHidden && 'is-hidden',
isProtected && 'is-protected',
isForceMobileVersion && 'mobile',
);
return (

View File

@ -38,12 +38,12 @@ type OwnProps = {
isProtected?: boolean;
areControlsVisible: boolean;
shouldCloseOnClick?: boolean;
isForceMobileVersion?: boolean;
toggleControls: (isVisible: boolean) => void;
onClose: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void;
isClickDisabled?: boolean;
};
const MOBILE_VERSION_CONTROL_WIDTH = 400;
const MAX_LOOP_DURATION = 30; // Seconds
const VideoPlayer: FC<OwnProps> = ({
@ -59,6 +59,7 @@ const VideoPlayer: FC<OwnProps> = ({
isMuted,
playbackRate,
onClose,
isForceMobileVersion,
toggleControls,
areControlsVisible,
shouldCloseOnClick,
@ -226,13 +227,15 @@ const VideoPlayer: FC<OwnProps> = ({
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
<div
className="VideoPlayer"
onMouseMove={!IS_TOUCH_ENV ? handleVideoMove : undefined}
onMouseOut={!IS_TOUCH_ENV ? handleVideoLeave : undefined}
onMouseMove={shouldToggleControls ? handleVideoMove : undefined}
onMouseOut={shouldToggleControls ? handleVideoLeave : undefined}
>
<div
style={wrapperStyle}
@ -301,7 +304,7 @@ const VideoPlayer: FC<OwnProps> = ({
duration={duration}
isVisible={areControlsVisible}
setVisibility={toggleControls}
isForceMobileVersion={posterSize && posterSize.width < MOBILE_VERSION_CONTROL_WIDTH}
isForceMobileVersion={isForceMobileVersion}
onSeek={handleSeek}
onChangeFullscreen={handleFullscreenChange}
onPictureInPictureChange={enterPictureInPicture}

View File

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

View File

@ -94,7 +94,7 @@ const VideoPlayerControls: FC<OwnProps> = ({
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<OwnProps> = ({
return () => {
if (timeout) window.clearTimeout(timeout);
};
}, [isPlaying, isVisible, isSeeking, setVisibility, isPlaybackMenuOpen]);
}, [isPlaying, isVisible, isSeeking, setVisibility, isPlaybackMenuOpen, isForceMobileVersion]);
useEffect(() => {
if (isVisible) {