Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 28 additions & 17 deletions src/components/DefaultVideo/DefaultVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ interface DefaultVideoProps {
export const DefaultVideo = React.forwardRef<DefaultVideoRefType, DefaultVideoProps>(
(props, ref) => {
const {video, qa, customBarControlsClassName} = props;
const {controls, customControlsOptions, muted: initiallyMuted = true, onVideoEnd} = video;
const {
controls,
customControlsOptions,
muted: initiallyMuted = true,
onVideoEnd,
loop,
} = video;
const {
muteButtonShown,
positioning,
Expand All @@ -45,22 +51,6 @@ export const DefaultVideo = React.forwardRef<DefaultVideoRefType, DefaultVideoPr
return videoRef.current;
}, [videoRef]);

React.useEffect(() => {
const videoElement = videoRef.current;
if (!videoElement || !onVideoEnd) {
return undefined;
}

const handleVideoEnd = () => {
onVideoEnd?.();
};

videoElement.addEventListener('ended', handleVideoEnd);
return () => {
videoElement.removeEventListener('ended', handleVideoEnd);
};
}, [videoRef, onVideoEnd]);

// to guarantee setting a muted attribute in HTML. https://github.com/facebook/react/issues/10389
React.useEffect(() => {
const videoElement = videoRef.current;
Expand All @@ -81,6 +71,7 @@ export const DefaultVideo = React.forwardRef<DefaultVideoRefType, DefaultVideoPr
return !value;
});
}, [videoRef]);

const onMuteToggle = React.useCallback(() => {
setIsMuted((value) => !value);
}, []);
Expand All @@ -91,6 +82,25 @@ export const DefaultVideo = React.forwardRef<DefaultVideoRefType, DefaultVideoPr
}
}, [onPlayToggle, customControlsType]);

const onEnded = React.useCallback(() => {
const videoElement = videoRef.current;
if (!videoElement) {
return;
}

if (loop) {
const {start = 0, end = videoElement.duration} =
typeof loop === 'boolean' ? {} : loop;

if (videoElement.currentTime >= end) {
videoElement.currentTime = start;
videoElement.play();
}
}

onVideoEnd?.();
}, [loop, onVideoEnd]);

return (
<React.Fragment>
<video
Expand All @@ -105,6 +115,7 @@ export const DefaultVideo = React.forwardRef<DefaultVideoRefType, DefaultVideoPr
muted={isMuted}
aria-label={video.ariaLabel}
onClick={onClick}
onEnded={onEnded}
>
{getVideoTypesWithPriority(video.src).map(({src, type}, index) => (
<source key={index} src={src} type={type} data-qa={qa} />
Expand Down
22 changes: 1 addition & 21 deletions src/components/Media/Video/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,6 @@ const Video = (props: VideoAllProps) => {

React.useEffect(() => {
if (ref && ref.current) {
const {loop} = video;

if (loop && typeof loop !== 'boolean') {
const {start = 0, end} = loop;

ref.current.addEventListener(
'timeupdate',
() => {
const videoRef = ref.current;
const endTime = end || (videoRef && videoRef.duration);

if (videoRef && videoRef.currentTime === endTime) {
videoRef.currentTime = start;
videoRef.play().catch(() => setHasVideoFallback(true));
}
},
{passive: true},
);
}

if (playVideo) {
ref.current.play().catch(() => setHasVideoFallback(true));
}
Expand All @@ -96,7 +76,7 @@ const Video = (props: VideoAllProps) => {
className={b('react-player', videoClassName)}
src={src}
previewImgUrl={previewImg}
loop={Boolean(loop)}
loop={loop}
controls={controls}
muted={muted}
autoplay={autoplay && playVideo}
Expand Down
47 changes: 30 additions & 17 deletions src/components/ReactPlayer/ReactPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {PlayFill} from '@gravity-ui/icons';
import {Icon} from '@gravity-ui/uikit';
import debounce from 'lodash/debounce';
import _ReactPlayer from 'react-player';
import type {ReactPlayerProps} from 'react-player';

import {MobileContext} from '../../context/mobileContext';
import {VideoContext} from '../../context/videoContext';
Expand Down Expand Up @@ -41,11 +42,10 @@ const ReactPlayer =
: _ReactPlayer;

export interface ReactPlayerBlockProps
extends Omit<MediaVideoProps, 'loop' | 'src' | 'ref'>,
extends Omit<MediaVideoProps, 'src' | 'ref'>,
ClassNameProps {
src: string | string[];
previewImgUrl?: string;
loop?: boolean;
customBarControlsClassName?: string;
showPreview?: boolean;
onClickPreview?: () => void;
Expand All @@ -57,6 +57,7 @@ export interface ReactPlayerBlockProps

interface PlayerPropgress {
played: number;
playedSeconds: number;
}

// eslint-disable-next-line react/display-name
Expand Down Expand Up @@ -108,6 +109,7 @@ export const ReactPlayerBlock = React.forwardRef<ReactPlayerBlockHandler, ReactP
const [isPlaying, setIsPlaying] = React.useState(autoPlay);
const [playedPercent, setPlayedPercent] = React.useState<number>(0);
const [currentHeight, setCurrentHeight] = React.useState(height);
const [duration, setDuration] = React.useState<null | number>(null);
const [width, setWidth] = React.useState<number>(0);
const [actualRatio, setActualRatio] = React.useState<number>();
const [muted, setMuted] = React.useState<boolean>(mute);
Expand Down Expand Up @@ -325,26 +327,36 @@ export const ReactPlayerBlock = React.forwardRef<ReactPlayerBlockHandler, ReactP
}
}, []);

const onProgress = React.useCallback((progress: PlayerPropgress) => {
setPlayedPercent(progress.played);
const onProgress: ReactPlayerProps['onProgress'] = React.useCallback(
({played, playedSeconds}: PlayerPropgress) => {
setPlayedPercent(played);

if (progress.played === 1) {
setMuted(true);
}
if (loop) {
const {start = 0, end = duration} = typeof loop === 'boolean' ? {} : loop;

// Youtube videos not muted after finishing playing and start again.
// 'onEnded' does not fire when 'loop' is set to true.
// It is custom loop with muted sound after finishing playing and start again.
if (end !== null && playedSeconds >= end) {
setIsPlaying(true);
playerRef?.seekTo(start);
}
}

if (played === 1) {
setMuted(true);
}
},
[duration, loop, playerRef],
);

const onDuration = React.useCallback((currentDuration: number) => {
setDuration(currentDuration);
}, []);

const onEnded = React.useCallback(() => {
// Youtube videos not muted after finishing playing and start again.
// 'onEnded' does not fire when 'loop' is set to true.
// It is custom loop with muted sound after finishing playing and start again.
if (loop) {
setPlayedPercent(0);
setIsPlaying(true);
playerRef?.seekTo(0);
}

setEnded(true);
}, [loop, playerRef]);
}, []);

const onPlayClick = React.useCallback(() => {
if (isPlaying) {
Expand Down Expand Up @@ -422,6 +434,7 @@ export const ReactPlayerBlock = React.forwardRef<ReactPlayerBlockHandler, ReactP
} // to prevent pause icon flickering when autoplayed video ends
onProgress={onProgress}
onEnded={onEnded}
onDuration={onDuration}
aria-label={ariaLabel}
previewTabIndex={-1}
config={{
Expand Down