Media Viewer: Fix volume control for narrow video (#2837)
This commit is contained in:
parent
1ad2e816a2
commit
aa1cd0a3b9
@ -57,6 +57,7 @@ type StateProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ANIMATION_DURATION = 350;
|
const ANIMATION_DURATION = 350;
|
||||||
|
const MOBILE_VERSION_CONTROL_WIDTH = 350;
|
||||||
|
|
||||||
const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
@ -105,6 +106,10 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
|||||||
setControlsVisible?.(isVisible);
|
setControlsVisible?.(isVisible);
|
||||||
}, [setControlsVisible]);
|
}, [setControlsVisible]);
|
||||||
|
|
||||||
|
const toggleControlsOnMove = useCallback(() => {
|
||||||
|
toggleControls(true);
|
||||||
|
}, [toggleControls]);
|
||||||
|
|
||||||
if (avatarOwner || actionPhoto) {
|
if (avatarOwner || actionPhoto) {
|
||||||
if (!isVideoAvatar) {
|
if (!isVideoAvatar) {
|
||||||
return (
|
return (
|
||||||
@ -149,21 +154,25 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
|||||||
const textParts = message.content.action?.type === 'suggestProfilePhoto'
|
const textParts = message.content.action?.type === 'suggestProfilePhoto'
|
||||||
? lang('Conversation.SuggestedPhotoTitle')
|
? lang('Conversation.SuggestedPhotoTitle')
|
||||||
: renderMessageText(message);
|
: renderMessageText(message);
|
||||||
|
|
||||||
const hasFooter = Boolean(textParts);
|
const hasFooter = Boolean(textParts);
|
||||||
|
const posterSize = message && calculateMediaViewerDimensions(dimensions!, hasFooter, isVideo);
|
||||||
|
const isForceMobileVersion = isMobile || shouldForceMobileVersion(posterSize);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={buildClassName('MediaViewerContent', hasFooter && 'has-footer')}
|
className={buildClassName('MediaViewerContent', hasFooter && 'has-footer')}
|
||||||
|
onMouseMove={isForceMobileVersion && !IS_TOUCH_ENV ? toggleControlsOnMove : undefined}
|
||||||
>
|
>
|
||||||
{isPhoto && renderPhoto(
|
{isPhoto && renderPhoto(
|
||||||
bestData,
|
bestData,
|
||||||
message && calculateMediaViewerDimensions(dimensions!, hasFooter),
|
posterSize,
|
||||||
!isMobile && !isProtected,
|
!isMobile && !isProtected,
|
||||||
isProtected,
|
isProtected,
|
||||||
)}
|
)}
|
||||||
{isVideo && (!isActive ? renderVideoPreview(
|
{isVideo && (!isActive ? renderVideoPreview(
|
||||||
bestImageData,
|
bestImageData,
|
||||||
message && calculateMediaViewerDimensions(dimensions!, hasFooter, true),
|
posterSize,
|
||||||
!isMobile && !isProtected,
|
!isMobile && !isProtected,
|
||||||
isProtected,
|
isProtected,
|
||||||
) : (
|
) : (
|
||||||
@ -172,7 +181,7 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
|||||||
url={bestData}
|
url={bestData}
|
||||||
isGif={isGif}
|
isGif={isGif}
|
||||||
posterData={bestImageData}
|
posterData={bestImageData}
|
||||||
posterSize={message && calculateMediaViewerDimensions(dimensions!, hasFooter, true)}
|
posterSize={posterSize}
|
||||||
loadProgress={loadProgress}
|
loadProgress={loadProgress}
|
||||||
fileSize={videoSize!}
|
fileSize={videoSize!}
|
||||||
areControlsVisible={areControlsVisible}
|
areControlsVisible={areControlsVisible}
|
||||||
@ -182,6 +191,7 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
isMuted={isMuted}
|
isMuted={isMuted}
|
||||||
isHidden={isHidden}
|
isHidden={isHidden}
|
||||||
|
isForceMobileVersion={isForceMobileVersion}
|
||||||
isProtected={isProtected}
|
isProtected={isProtected}
|
||||||
volume={volume}
|
volume={volume}
|
||||||
isClickDisabled={isMoving}
|
isClickDisabled={isMoving}
|
||||||
@ -193,6 +203,7 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
|||||||
text={textParts}
|
text={textParts}
|
||||||
onClick={onFooterClick}
|
onClick={onFooterClick}
|
||||||
isProtected={isProtected}
|
isProtected={isProtected}
|
||||||
|
isForceMobileVersion={isForceMobileVersion}
|
||||||
isHidden={IS_TOUCH_ENV ? !areControlsVisible : false}
|
isHidden={IS_TOUCH_ENV ? !areControlsVisible : false}
|
||||||
isForVideo={isVideo && !isGif}
|
isForVideo={isVideo && !isGif}
|
||||||
/>
|
/>
|
||||||
@ -342,3 +353,8 @@ function renderVideoPreview(blobUrl?: string, imageSize?: ApiDimensions, canDrag
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldForceMobileVersion(posterSize?: { width: number; height: number }) {
|
||||||
|
if (!posterSize) return false;
|
||||||
|
return posterSize.width < MOBILE_VERSION_CONTROL_WIDTH;
|
||||||
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
padding: 0.5rem 0 0;
|
padding: 0.5rem 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
&.mobile {
|
||||||
background: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0) 100%);
|
background: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
|
||||||
&.is-for-video {
|
&.is-for-video {
|
||||||
@ -26,6 +26,14 @@
|
|||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.media-viewer-footer-content {
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.media-text.multiline::before {
|
||||||
|
display: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body.ghost-animating & {
|
body.ghost-animating & {
|
||||||
@ -43,10 +51,6 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-hidden {
|
&.is-hidden {
|
||||||
@ -84,11 +88,6 @@
|
|||||||
background: rgba(0, 0, 0, 0.75);
|
background: rgba(0, 0, 0, 0.75);
|
||||||
border-radius: var(--border-radius-default);
|
border-radius: var(--border-radius-default);
|
||||||
z-index: var(--z-below);
|
z-index: var(--z-below);
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
display: none;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,11 +17,12 @@ type OwnProps = {
|
|||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
isHidden?: boolean;
|
isHidden?: boolean;
|
||||||
isForVideo: boolean;
|
isForVideo: boolean;
|
||||||
|
isForceMobileVersion?: boolean;
|
||||||
isProtected?: boolean;
|
isProtected?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MediaViewerFooter: FC<OwnProps> = ({
|
const MediaViewerFooter: FC<OwnProps> = ({
|
||||||
text = '', isHidden, isForVideo, onClick, isProtected,
|
text = '', isHidden, isForVideo, onClick, isProtected, isForceMobileVersion,
|
||||||
}) => {
|
}) => {
|
||||||
const [isMultiline, setIsMultiline] = useState(false);
|
const [isMultiline, setIsMultiline] = useState(false);
|
||||||
const { isMobile } = useAppLayout();
|
const { isMobile } = useAppLayout();
|
||||||
@ -58,6 +59,7 @@ const MediaViewerFooter: FC<OwnProps> = ({
|
|||||||
isForVideo && 'is-for-video',
|
isForVideo && 'is-for-video',
|
||||||
isHidden && 'is-hidden',
|
isHidden && 'is-hidden',
|
||||||
isProtected && 'is-protected',
|
isProtected && 'is-protected',
|
||||||
|
isForceMobileVersion && 'mobile',
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -38,12 +38,12 @@ type OwnProps = {
|
|||||||
isProtected?: boolean;
|
isProtected?: boolean;
|
||||||
areControlsVisible: boolean;
|
areControlsVisible: boolean;
|
||||||
shouldCloseOnClick?: boolean;
|
shouldCloseOnClick?: boolean;
|
||||||
|
isForceMobileVersion?: boolean;
|
||||||
toggleControls: (isVisible: boolean) => void;
|
toggleControls: (isVisible: boolean) => void;
|
||||||
onClose: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
onClose: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||||
isClickDisabled?: boolean;
|
isClickDisabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MOBILE_VERSION_CONTROL_WIDTH = 400;
|
|
||||||
const MAX_LOOP_DURATION = 30; // Seconds
|
const MAX_LOOP_DURATION = 30; // Seconds
|
||||||
|
|
||||||
const VideoPlayer: FC<OwnProps> = ({
|
const VideoPlayer: FC<OwnProps> = ({
|
||||||
@ -59,6 +59,7 @@ const VideoPlayer: FC<OwnProps> = ({
|
|||||||
isMuted,
|
isMuted,
|
||||||
playbackRate,
|
playbackRate,
|
||||||
onClose,
|
onClose,
|
||||||
|
isForceMobileVersion,
|
||||||
toggleControls,
|
toggleControls,
|
||||||
areControlsVisible,
|
areControlsVisible,
|
||||||
shouldCloseOnClick,
|
shouldCloseOnClick,
|
||||||
@ -226,13 +227,15 @@ const VideoPlayer: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const wrapperStyle = posterSize && `width: ${posterSize.width}px; height: ${posterSize.height}px`;
|
const wrapperStyle = posterSize && `width: ${posterSize.width}px; height: ${posterSize.height}px`;
|
||||||
const videoStyle = `background-image: url(${posterData})`;
|
const videoStyle = `background-image: url(${posterData})`;
|
||||||
|
const shouldToggleControls = !IS_TOUCH_ENV && !isForceMobileVersion;
|
||||||
const duration = videoRef.current?.duration || 0;
|
const duration = videoRef.current?.duration || 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
// eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
|
||||||
<div
|
<div
|
||||||
className="VideoPlayer"
|
className="VideoPlayer"
|
||||||
onMouseMove={!IS_TOUCH_ENV ? handleVideoMove : undefined}
|
onMouseMove={shouldToggleControls ? handleVideoMove : undefined}
|
||||||
onMouseOut={!IS_TOUCH_ENV ? handleVideoLeave : undefined}
|
onMouseOut={shouldToggleControls ? handleVideoLeave : undefined}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={wrapperStyle}
|
style={wrapperStyle}
|
||||||
@ -301,7 +304,7 @@ const VideoPlayer: FC<OwnProps> = ({
|
|||||||
duration={duration}
|
duration={duration}
|
||||||
isVisible={areControlsVisible}
|
isVisible={areControlsVisible}
|
||||||
setVisibility={toggleControls}
|
setVisibility={toggleControls}
|
||||||
isForceMobileVersion={posterSize && posterSize.width < MOBILE_VERSION_CONTROL_WIDTH}
|
isForceMobileVersion={isForceMobileVersion}
|
||||||
onSeek={handleSeek}
|
onSeek={handleSeek}
|
||||||
onChangeFullscreen={handleFullscreenChange}
|
onChangeFullscreen={handleFullscreenChange}
|
||||||
onPictureInPictureChange={enterPictureInPicture}
|
onPictureInPictureChange={enterPictureInPicture}
|
||||||
|
|||||||
@ -18,10 +18,16 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
&.mobile {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
padding: 2.25rem 0.5rem 0.75rem;
|
padding: 2.25rem 0.5rem 0.75rem;
|
||||||
background: none;
|
background: none;
|
||||||
|
.player-seekline {
|
||||||
|
top: 1rem;
|
||||||
|
}
|
||||||
|
.playback-rate-menu .bubble {
|
||||||
|
bottom: 4.6875rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
@ -44,6 +50,9 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0.25rem;
|
margin: 0.25rem;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
|
@media (max-width: 320px) {
|
||||||
|
margin: 0.125rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.volume-slider {
|
.volume-slider {
|
||||||
@ -91,6 +100,9 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
margin-left: 0.5rem;
|
margin-left: 0.5rem;
|
||||||
|
@media (max-width: 320px) {
|
||||||
|
margin: 0.25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-seekline {
|
.player-seekline {
|
||||||
@ -102,10 +114,6 @@
|
|||||||
touch-action: none;
|
touch-action: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-track {
|
&-track {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@ -155,9 +163,6 @@
|
|||||||
min-width: 3.5rem;
|
min-width: 3.5rem;
|
||||||
margin-right: 5.8125rem;
|
margin-right: 5.8125rem;
|
||||||
bottom: 4.1875rem;
|
bottom: 4.1875rem;
|
||||||
@media (max-width: 600px) {
|
|
||||||
bottom: 4.6875rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.no-fullscreen, &.no-pip {
|
&.no-fullscreen, &.no-pip {
|
||||||
|
|||||||
@ -94,7 +94,7 @@ const VideoPlayerControls: FC<OwnProps> = ({
|
|||||||
const { isMobile } = useAppLayout();
|
const { isMobile } = useAppLayout();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!IS_TOUCH_ENV) return undefined;
|
if (!IS_TOUCH_ENV && !isForceMobileVersion) return undefined;
|
||||||
let timeout: number | undefined;
|
let timeout: number | undefined;
|
||||||
if (!isVisible || !isPlaying || isSeeking || isPlaybackMenuOpen) {
|
if (!isVisible || !isPlaying || isSeeking || isPlaybackMenuOpen) {
|
||||||
if (timeout) window.clearTimeout(timeout);
|
if (timeout) window.clearTimeout(timeout);
|
||||||
@ -106,7 +106,7 @@ const VideoPlayerControls: FC<OwnProps> = ({
|
|||||||
return () => {
|
return () => {
|
||||||
if (timeout) window.clearTimeout(timeout);
|
if (timeout) window.clearTimeout(timeout);
|
||||||
};
|
};
|
||||||
}, [isPlaying, isVisible, isSeeking, setVisibility, isPlaybackMenuOpen]);
|
}, [isPlaying, isVisible, isSeeking, setVisibility, isPlaybackMenuOpen, isForceMobileVersion]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user