Skip to content

Commit

Permalink
feat: add videoIframe prop to media block and component (#999)
Browse files Browse the repository at this point in the history
* feat: add videoIframe prop to media block and component

* fix: review fixes
  • Loading branch information
benax-se authored Sep 6, 2024
1 parent 9ddfac3 commit 13528be
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 47 deletions.
1 change: 1 addition & 0 deletions .storybook/stories/documentation/Types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { Meta } from '@storybook/blocks';
- `parallax?: boolean` — Enable/disable the parallax effect
- [`video?: Video` — Video](#Video)
- `youtube?: url` — Link to a video on YouTube
- `videoIframe?: url` — Link to a video iframe
- `height?: number` — Block height
- `previewImg?: string`
- `dataLens?: string |` [DataLens](#DataLens)
Expand Down
9 changes: 6 additions & 3 deletions src/components/Media/Media.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, {ReactElement, useMemo, useState} from 'react';

import {MediaProps, QAProps} from '../../models';
import {block, getQaAttrubutes} from '../../utils';
import YoutubeBlock from '../VideoBlock/VideoBlock';
import IframeVideoBlock from '../VideoBlock/VideoBlock';

import DataLens from './DataLens/DataLens';
import FullscreenVideo from './FullscreenVideo/FullscreenVideo';
Expand All @@ -26,6 +26,7 @@ export const Media = (props: MediaAllProps) => {
image,
video,
youtube,
videoIframe,
dataLens,
color,
height,
Expand Down Expand Up @@ -99,11 +100,12 @@ export const Media = (props: MediaAllProps) => {
}
}

if (youtube) {
if (youtube || videoIframe) {
result = (
<YoutubeBlock
<IframeVideoBlock
className={b('youtube', youtubeClassName)}
record={youtube}
videoIframe={videoIframe}
attributes={{color: 'white', rel: '0'}}
previewImg={previewImg}
height={height}
Expand All @@ -127,6 +129,7 @@ export const Media = (props: MediaAllProps) => {
}, [
image,
video,
videoIframe,
youtube,
dataLens,
iframe,
Expand Down
84 changes: 41 additions & 43 deletions src/components/VideoBlock/VideoBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
// TODO fix in https://github.com/gravity-ui/page-constructor/issues/965

import React, {useCallback, useEffect, useRef, useState} from 'react';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';

import {PlayFill} from '@gravity-ui/icons';
import {Icon} from '@gravity-ui/uikit';
Expand All @@ -28,10 +28,13 @@ export const AUTOPLAY_ATTRIBUTES = {
autoplay: 1,
mute: 1,
};
const NO_AUTOPLAY_ATTRIBUTES = {
autoplay: 0,
};

const b = block('VideoBlock');

function getVideoSrc(stream?: string, record?: string) {
function getYoutubeVideoSrc(stream?: string, record?: string) {
if (!stream && !record) {
return null;
}
Expand All @@ -57,6 +60,7 @@ export interface VideoBlockProps extends AnalyticsEventsBase {
id?: string;
stream?: string;
record?: string;
videoIframe?: string;
attributes?: Record<string, string>;
className?: string;
previewImg?: string;
Expand All @@ -71,6 +75,7 @@ const VideoBlock = (props: VideoBlockProps) => {
const {
stream,
record,
videoIframe,
attributes,
className,
id,
Expand All @@ -84,25 +89,29 @@ const VideoBlock = (props: VideoBlockProps) => {
} = props;
const handleAnalytics = useAnalytics(DefaultEventNames.VideoPreview);

const src = getVideoSrc(stream, record);
const src = videoIframe ? videoIframe : getYoutubeVideoSrc(stream, record);
const ref = useRef<HTMLDivElement>(null);
const iframeRef = useRef<HTMLIFrameElement>();
const [hidePreview, setHidePreview] = useState(false);
const norender = (!stream && !record) || !src;
const [currentHeight, setCurrentHeight] = useState(height || undefined);
const fullId = id || uuidv4();
const fullId = useMemo(() => id || uuidv4(), [id]);

const [isPlaying, setIsPlaying] = useState(!previewImg);

const iframeSrc =
src && isPlaying
? `${src}?${getPageSearchParams({
...(attributes || {}),
...(autoplay ? AUTOPLAY_ATTRIBUTES : NO_AUTOPLAY_ATTRIBUTES),
})}`
: undefined;

const onPreviewClick = useCallback(() => {
handleAnalytics(analyticsEvents);

if (iframeRef.current) {
iframeRef.current.src = `${src}?${getPageSearchParams({
...AUTOPLAY_ATTRIBUTES,
...(attributes || {}),
})}`;
}
setIsPlaying(true);

setTimeout(() => setHidePreview(true), AUTOPLAY_DELAY);
}, [handleAnalytics, analyticsEvents, src, attributes]);
}, [handleAnalytics, analyticsEvents]);

useEffect(() => {
const updateSize = debounce(() => {
Expand All @@ -118,44 +127,33 @@ const VideoBlock = (props: VideoBlockProps) => {
};
}, [height]);

useEffect(() => {
if (norender) {
return;
}

if (ref.current && !iframeRef.current) {
const iframe = document.createElement('iframe');
iframe.id = fullId;

if (!previewImg) {
iframe.src = `${src}?${getPageSearchParams({
...(attributes || {}),
...(autoplay ? AUTOPLAY_ATTRIBUTES : {}),
})}`;
}

iframe.width = '100%';
iframe.height = '100%';
iframe.title = i18n('iframe-title');
iframe.frameBorder = '0';
iframe.setAttribute('allowfullscreen', 'true');
iframe.setAttribute('allow', 'autoplay');
iframe.setAttribute('loading', 'lazy');
ref.current.appendChild(iframe);
iframeRef.current = iframe;
}
}, [stream, record, norender, src, fullId, attributes, iframeRef, previewImg, autoplay]);
const iframeContent = useMemo(() => {
return (
<iframe
id={fullId}
src={iframeSrc}
width="100%"
height="100%"
title={i18n('iframe-title')}
frameBorder="0"
allowFullScreen={true}
allow="autoplay; fullscreen; encrypted-media; accelerometer; gyroscope; picture-in-picture; clipboard-write; web-share; screen-wake-lock"
loading="lazy"
/>
);
}, [fullId, iframeSrc]);

useEffect(() => {
setHidePreview(false);
}, [src, setHidePreview]);
}, [src]);

if (norender) {
if (!src) {
return null;
}

return (
<div className={b(null, className)} ref={ref} style={{height: currentHeight}}>
<div className={b(null, className)} style={{height: currentHeight}} ref={ref}>
{iframeContent}
{previewImg && !hidePreview && !fullscreen && (
<div className={b('preview')} onClick={onPreviewClick}>
<Image
Expand Down
5 changes: 5 additions & 0 deletions src/models/constructor-items/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ export interface MediaComponentVideoProps extends AnalyticsEventsBase {
previewImg?: string;
}

export interface MediaComponentVideoIframeProps {
videoIframe: string;
}

export interface MediaComponentYoutubeProps {
youtube: string;
previewImg?: string;
Expand Down Expand Up @@ -279,6 +283,7 @@ export interface MediaProps
extends Animatable,
Partial<MediaComponentDataLensProps>,
Partial<MediaComponentYoutubeProps>,
Partial<MediaComponentVideoIframeProps>,
Partial<MediaComponentImageProps>,
Partial<MediaComponentIframeProps>,
Partial<MediaComponentVideoProps> {
Expand Down
3 changes: 3 additions & 0 deletions src/schema/validators/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,9 @@ export const MediaProps = {
youtube: {
type: 'string',
},
videoIframe: {
type: 'string',
},
parallax: {
type: 'boolean',
},
Expand Down
2 changes: 1 addition & 1 deletion src/sub-blocks/LayoutItem/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ export const hasFullscreen = ({dataLens, image}: MediaProps) => {
return !(dataLens || Array.isArray(image));
};

export const showFullscreenIcon = ({youtube}: MediaProps) => !youtube;
export const showFullscreenIcon = ({youtube, videoIframe}: MediaProps) => !(youtube || videoIframe);

0 comments on commit 13528be

Please sign in to comment.