Media Viewer: Various fixes (#2075)
This commit is contained in:
parent
eb69f55f29
commit
5de819826e
@ -130,6 +130,7 @@ const MediaViewerContent: FC<OwnProps & StateProps> = (props) => {
|
|||||||
noPlay={!isActive}
|
noPlay={!isActive}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
isMuted
|
isMuted
|
||||||
|
shouldCloseOnClick
|
||||||
volume={0}
|
volume={0}
|
||||||
playbackRate={1}
|
playbackRate={1}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import usePrevious from '../../hooks/usePrevious';
|
|||||||
import useTimeout from '../../hooks/useTimeout';
|
import useTimeout from '../../hooks/useTimeout';
|
||||||
import useWindowSize from '../../hooks/useWindowSize';
|
import useWindowSize from '../../hooks/useWindowSize';
|
||||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
import { useFullscreenStatus } from '../../hooks/useFullscreen';
|
||||||
|
|
||||||
import MediaViewerContent from './MediaViewerContent';
|
import MediaViewerContent from './MediaViewerContent';
|
||||||
|
|
||||||
@ -101,6 +102,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
const hasZoomChanged = prevZoomLevelChange !== undefined && prevZoomLevelChange !== zoomLevelChange;
|
const hasZoomChanged = prevZoomLevelChange !== undefined && prevZoomLevelChange !== zoomLevelChange;
|
||||||
const forceUpdate = useForceUpdate();
|
const forceUpdate = useForceUpdate();
|
||||||
const [areControlsVisible, setControlsVisible] = useState(false);
|
const [areControlsVisible, setControlsVisible] = useState(false);
|
||||||
|
const isFullscreen = useFullscreenStatus();
|
||||||
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;
|
||||||
@ -145,7 +147,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
}, [mediaId]);
|
}, [mediaId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!containerRef.current || activeMediaId === undefined || isHidden) {
|
if (!containerRef.current || activeMediaId === undefined || isHidden || isFullscreen) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
let lastTransform = lastTransformRef.current;
|
let lastTransform = lastTransformRef.current;
|
||||||
@ -631,10 +633,11 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
animationLevel,
|
animationLevel,
|
||||||
setIsMouseDown,
|
setIsMouseDown,
|
||||||
isHidden,
|
isHidden,
|
||||||
|
isFullscreen,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!containerRef.current || !hasZoomChanged || isHidden) return;
|
if (!containerRef.current || !hasZoomChanged || isHidden || isFullscreen) return;
|
||||||
const { scale } = transformRef.current;
|
const { scale } = transformRef.current;
|
||||||
const dir = zoomLevelChange > 0 ? -1 : +1;
|
const dir = zoomLevelChange > 0 ? -1 : +1;
|
||||||
const minZoom = MIN_ZOOM * 0.6;
|
const minZoom = MIN_ZOOM * 0.6;
|
||||||
@ -662,7 +665,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
containerRef.current.dispatchEvent(wheelEvent);
|
containerRef.current.dispatchEvent(wheelEvent);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [zoomLevelChange, hasZoomChanged, isHidden]);
|
}, [zoomLevelChange, hasZoomChanged, isHidden, isFullscreen]);
|
||||||
|
|
||||||
if (activeMediaId === undefined) return undefined;
|
if (activeMediaId === undefined) return undefined;
|
||||||
|
|
||||||
|
|||||||
@ -7,11 +7,13 @@ import { getActions } from '../../global';
|
|||||||
import type { ApiDimensions } from '../../api/types';
|
import type { ApiDimensions } from '../../api/types';
|
||||||
|
|
||||||
import useBuffering from '../../hooks/useBuffering';
|
import useBuffering from '../../hooks/useBuffering';
|
||||||
import useFullscreenStatus from '../../hooks/useFullscreen';
|
import useFullscreen from '../../hooks/useFullscreen';
|
||||||
import usePictureInPicture from '../../hooks/usePictureInPicture';
|
import usePictureInPicture from '../../hooks/usePictureInPicture';
|
||||||
import useShowTransition from '../../hooks/useShowTransition';
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
import useVideoCleanup from '../../hooks/useVideoCleanup';
|
import useVideoCleanup from '../../hooks/useVideoCleanup';
|
||||||
import { IS_IOS, IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../util/environment';
|
import {
|
||||||
|
IS_IOS, IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV, IS_YA_BROWSER,
|
||||||
|
} from '../../util/environment';
|
||||||
import safePlay from '../../util/safePlay';
|
import safePlay from '../../util/safePlay';
|
||||||
import stopEvent from '../../util/stopEvent';
|
import stopEvent from '../../util/stopEvent';
|
||||||
|
|
||||||
@ -36,6 +38,7 @@ type OwnProps = {
|
|||||||
playbackRate: number;
|
playbackRate: number;
|
||||||
isProtected?: boolean;
|
isProtected?: boolean;
|
||||||
areControlsVisible: boolean;
|
areControlsVisible: boolean;
|
||||||
|
shouldCloseOnClick?: boolean;
|
||||||
toggleControls: (isVisible: boolean) => void;
|
toggleControls: (isVisible: boolean) => void;
|
||||||
onClose: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
onClose: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||||
};
|
};
|
||||||
@ -58,6 +61,7 @@ const VideoPlayer: FC<OwnProps> = ({
|
|||||||
onClose,
|
onClose,
|
||||||
toggleControls,
|
toggleControls,
|
||||||
areControlsVisible,
|
areControlsVisible,
|
||||||
|
shouldCloseOnClick,
|
||||||
isProtected,
|
isProtected,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
@ -70,10 +74,18 @@ const VideoPlayer: FC<OwnProps> = ({
|
|||||||
const videoRef = useRef<HTMLVideoElement>(null);
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
const [isPlaying, setIsPlaying] = useState(!IS_TOUCH_ENV || !IS_IOS);
|
const [isPlaying, setIsPlaying] = useState(!IS_TOUCH_ENV || !IS_IOS);
|
||||||
const [currentTime, setCurrentTime] = useState(0);
|
const [currentTime, setCurrentTime] = useState(0);
|
||||||
const [isFullscreen, setFullscreen, exitFullscreen] = useFullscreenStatus(videoRef, setIsPlaying);
|
const [isFullscreen, setFullscreen, exitFullscreen] = useFullscreen(videoRef, setIsPlaying);
|
||||||
|
|
||||||
const handleEnterFullscreen = useCallback(() => setMediaViewerHidden(true), [setMediaViewerHidden]);
|
const handleEnterFullscreen = useCallback(() => {
|
||||||
const handleLeaveFullscreen = useCallback(() => setMediaViewerHidden(false), [setMediaViewerHidden]);
|
// Yandex browser doesn't support PIP when video is hidden
|
||||||
|
if (IS_YA_BROWSER) return;
|
||||||
|
setMediaViewerHidden(true);
|
||||||
|
}, [setMediaViewerHidden]);
|
||||||
|
|
||||||
|
const handleLeaveFullscreen = useCallback(() => {
|
||||||
|
if (IS_YA_BROWSER) return;
|
||||||
|
setMediaViewerHidden(false);
|
||||||
|
}, [setMediaViewerHidden]);
|
||||||
|
|
||||||
const [
|
const [
|
||||||
isPictureInPictureSupported,
|
isPictureInPictureSupported,
|
||||||
@ -143,6 +155,14 @@ const VideoPlayer: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
}, [isPlaying]);
|
}, [isPlaying]);
|
||||||
|
|
||||||
|
const handleClick = useCallback((e: React.MouseEvent<HTMLVideoElement, MouseEvent>) => {
|
||||||
|
if (shouldCloseOnClick) {
|
||||||
|
onClose(e);
|
||||||
|
} else {
|
||||||
|
togglePlayState(e);
|
||||||
|
}
|
||||||
|
}, [onClose, shouldCloseOnClick, togglePlayState]);
|
||||||
|
|
||||||
useVideoCleanup(videoRef, []);
|
useVideoCleanup(videoRef, []);
|
||||||
|
|
||||||
const handleTimeUpdate = useCallback((e: React.SyntheticEvent<HTMLVideoElement>) => {
|
const handleTimeUpdate = useCallback((e: React.SyntheticEvent<HTMLVideoElement>) => {
|
||||||
@ -231,7 +251,7 @@ const VideoPlayer: FC<OwnProps> = ({
|
|||||||
style={videoStyle}
|
style={videoStyle}
|
||||||
onPlay={() => setIsPlaying(true)}
|
onPlay={() => setIsPlaying(true)}
|
||||||
onEnded={handleEnded}
|
onEnded={handleEnded}
|
||||||
onClick={!IS_SINGLE_COLUMN_LAYOUT ? togglePlayState : undefined}
|
onClick={!IS_SINGLE_COLUMN_LAYOUT ? handleClick : undefined}
|
||||||
onDoubleClick={!IS_TOUCH_ENV ? handleFullscreenChange : undefined}
|
onDoubleClick={!IS_TOUCH_ENV ? handleFullscreenChange : undefined}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...bufferingHandlers}
|
{...bufferingHandlers}
|
||||||
|
|||||||
@ -82,6 +82,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.player-time {
|
.player-time {
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,12 +150,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.playback-rate-menu {
|
.playback-rate-menu {
|
||||||
|
color: var(--color-text);
|
||||||
.bubble {
|
.bubble {
|
||||||
min-width: 3.5rem;
|
min-width: 3.5rem;
|
||||||
margin-right: 3.5rem;
|
margin-right: 5.8125rem;
|
||||||
bottom: 4.1875rem
|
bottom: 4.1875rem;
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
bottom: 4.6875rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.no-fullscreen {
|
|
||||||
|
&.no-fullscreen, &.no-pip {
|
||||||
|
.bubble {
|
||||||
|
margin-right: 3.3125rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.no-fullscreen.no-pip {
|
||||||
.bubble {
|
.bubble {
|
||||||
margin-right: 0.8125rem;
|
margin-right: 0.8125rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -235,7 +235,11 @@ const VideoPlayerControls: FC<OwnProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<Menu
|
<Menu
|
||||||
isOpen={isPlaybackMenuOpen}
|
isOpen={isPlaybackMenuOpen}
|
||||||
className={buildClassName('playback-rate-menu', !isFullscreenSupported && 'no-fullscreen')}
|
className={buildClassName(
|
||||||
|
'playback-rate-menu',
|
||||||
|
!isFullscreenSupported && 'no-fullscreen',
|
||||||
|
!isPictureInPictureSupported && 'no-pip',
|
||||||
|
)}
|
||||||
positionX="right"
|
positionX="right"
|
||||||
positionY="bottom"
|
positionY="bottom"
|
||||||
autoClose
|
autoClose
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useLayoutEffect, useState } from '../lib/teact/teact';
|
import { useLayoutEffect, useState, useEffect } from '../lib/teact/teact';
|
||||||
import { IS_IOS } from '../util/environment';
|
import { IS_IOS } from '../util/environment';
|
||||||
|
|
||||||
type RefType = {
|
type RefType = {
|
||||||
@ -10,7 +10,7 @@ type CallbackType = (isPlayed: boolean) => void;
|
|||||||
|
|
||||||
const prop = getBrowserFullscreenElementProp();
|
const prop = getBrowserFullscreenElementProp();
|
||||||
|
|
||||||
export default function useFullscreenStatus(elRef: RefType, setIsPlayed: CallbackType): ReturnType {
|
export default function useFullscreen(elRef: RefType, setIsPlayed: CallbackType): ReturnType {
|
||||||
const [isFullscreen, setIsFullscreen] = useState(Boolean(prop && document[prop]));
|
const [isFullscreen, setIsFullscreen] = useState(Boolean(prop && document[prop]));
|
||||||
|
|
||||||
const setFullscreen = () => {
|
const setFullscreen = () => {
|
||||||
@ -30,13 +30,18 @@ export default function useFullscreenStatus(elRef: RefType, setIsPlayed: Callbac
|
|||||||
};
|
};
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const listener = () => { setIsFullscreen(Boolean(prop && document[prop])); };
|
const video = elRef.current;
|
||||||
|
const listener = () => {
|
||||||
|
const isEnabled = Boolean(prop && document[prop]);
|
||||||
|
setIsFullscreen(isEnabled);
|
||||||
|
// In Firefox fullscreen video controls are not visible by default, so we force them manually
|
||||||
|
video!.controls = isEnabled;
|
||||||
|
};
|
||||||
const listenerEnter = () => { setIsFullscreen(true); };
|
const listenerEnter = () => { setIsFullscreen(true); };
|
||||||
const listenerExit = () => {
|
const listenerExit = () => {
|
||||||
setIsFullscreen(false);
|
setIsFullscreen(false);
|
||||||
setIsPlayed(false);
|
setIsPlayed(false);
|
||||||
};
|
};
|
||||||
const video = elRef.current;
|
|
||||||
|
|
||||||
document.addEventListener('fullscreenchange', listener, false);
|
document.addEventListener('fullscreenchange', listener, false);
|
||||||
document.addEventListener('webkitfullscreenchange', listener, false);
|
document.addEventListener('webkitfullscreenchange', listener, false);
|
||||||
@ -66,6 +71,28 @@ export default function useFullscreenStatus(elRef: RefType, setIsPlayed: Callbac
|
|||||||
return [isFullscreen, setFullscreen, exitFullscreen];
|
return [isFullscreen, setFullscreen, exitFullscreen];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useFullscreenStatus = () => {
|
||||||
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const listener = () => {
|
||||||
|
setIsFullscreen(checkIfFullscreen());
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('fullscreenchange', listener, false);
|
||||||
|
document.addEventListener('webkitfullscreenchange', listener, false);
|
||||||
|
document.addEventListener('mozfullscreenchange', listener, false);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('fullscreenchange', listener, false);
|
||||||
|
document.removeEventListener('webkitfullscreenchange', listener, false);
|
||||||
|
document.removeEventListener('mozfullscreenchange', listener, false);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return isFullscreen;
|
||||||
|
};
|
||||||
|
|
||||||
function getBrowserFullscreenElementProp() {
|
function getBrowserFullscreenElementProp() {
|
||||||
if (typeof document.fullscreenElement !== 'undefined') {
|
if (typeof document.fullscreenElement !== 'undefined') {
|
||||||
return 'fullscreenElement';
|
return 'fullscreenElement';
|
||||||
@ -74,10 +101,14 @@ function getBrowserFullscreenElementProp() {
|
|||||||
} else if (typeof document.webkitFullscreenElement !== 'undefined') {
|
} else if (typeof document.webkitFullscreenElement !== 'undefined') {
|
||||||
return 'webkitFullscreenElement';
|
return 'webkitFullscreenElement';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function checkIfFullscreen() {
|
||||||
|
const fullscreenProp = getBrowserFullscreenElementProp();
|
||||||
|
return Boolean(fullscreenProp && document[fullscreenProp]);
|
||||||
|
}
|
||||||
|
|
||||||
export function safeRequestFullscreen(video: HTMLVideoElement) {
|
export function safeRequestFullscreen(video: HTMLVideoElement) {
|
||||||
if (video.requestFullscreen) {
|
if (video.requestFullscreen) {
|
||||||
video.requestFullscreen();
|
video.requestFullscreen();
|
||||||
|
|||||||
@ -44,6 +44,8 @@ export const IS_MAC_OS = PLATFORM_ENV === 'macOS';
|
|||||||
export const IS_IOS = PLATFORM_ENV === 'iOS';
|
export const IS_IOS = PLATFORM_ENV === 'iOS';
|
||||||
export const IS_ANDROID = PLATFORM_ENV === 'Android';
|
export const IS_ANDROID = PLATFORM_ENV === 'Android';
|
||||||
export const IS_SAFARI = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
export const IS_SAFARI = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||||
|
export const IS_YA_BROWSER = navigator.userAgent.includes('YaBrowser');
|
||||||
|
|
||||||
export const IS_PWA = (
|
export const IS_PWA = (
|
||||||
window.matchMedia('(display-mode: standalone)').matches
|
window.matchMedia('(display-mode: standalone)').matches
|
||||||
|| (window.navigator as any).standalone
|
|| (window.navigator as any).standalone
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user