modules_Videos_VidyardVideo_index.js

import { classnames, PropTypes, React } from '@gravityforms/libraries';
import { useScript } from '@gravityforms/react-utils';
import { trigger } from '@gravityforms/utils';
import Video from '../Video';

const { forwardRef, useRef, useEffect, useState } = React;

const VIDYARD_LIBRARY_SRC = 'https://play.vidyard.com/embed/v4.js';
const videoDefaultOptions = {
	vidyard: {
		aspect: '16:10',
		type: 'inline',
	},
};

/**
 * @module VidyardVideo
 * @description A vidyard video player component.
 *
 * @since 1.2.2
 *
 * @param {object}              props                  Component props.
 * @param {object}              props.customAttributes Custom attributes for the component.
 * @param {string|Array|object} props.customClasses    Custom classes for the component.
 * @param {object}              props.videoOptions     Video options for player type from consumer. See https://knowledge.vidyard.com/hc/en-us/articles/360009879754-Use-query-strings-to-override-player-settings.
 * @param {object|null}         ref                    Ref to the component.
 *
 * @return {JSX.Element} A vidyard video component.
 *
 * @example
 * import VidyardVideo from '@gravityforms/components/react/admin/modules/Videos/VidyardVideo';
 *
 * return (
 *      <VidyardVideo
 *          customClasses={ [ 'example-class' ] }
 *          videoOptions={ { uuid: 'example-id' } }
 *      />
 * );
 *
 */
const VidyardVideo = forwardRef( ( {
	customAttributes = {},
	customClasses = [],
	videoCustomAttributes = {},
	videoOptions = {},
}, ref ) => {
	const videoRef = useRef( null );
	const [ loading, error ] = useScript( { src: VIDYARD_LIBRARY_SRC } );
	const [ rendered, setRendered ] = useState( false );

	useEffect( () => {
		// if we are loading, errored, don't have a vidref, or weren't supplied uuid for video, no point in going on.
		if ( loading || error || ! videoRef.current || ! videoOptions.uuid ) {
			return;
		}
		// get the vidyard api from the loaded script.
		const { api: vidyardApi } = window.VidyardV4;
		// create the final video options.
		const options = Object.assign( {}, videoDefaultOptions.vidyard, videoOptions, {
			container: videoRef.current,
		} );

		/**
		 * @function controlPlayer
		 * @description Listen on document for custom events to either play or pause the player using the vidyard api.
		 *
		 * @since 1.2.2
		 *
		 * @param {CustomEvent} e The event object.
		 *
		 * @return {void}
		 */
		const controlPlayer = ( e ) => {
			if ( e.type !== 'gform/video/pauseAll' && e.detail.uuid !== videoOptions.uuid ) {
				return;
			}
			const player = vidyardApi.getPlayersByUUID( videoOptions.uuid )[ 0 ];
			let eventType = '';
			if ( e.type === 'gform/video/play' ) {
				player.play();
				eventType = 'playing';
			} else if ( e.type === 'gform/video/pause' || e.type === 'gform/video/pauseAll' ) {
				player.pause();
				eventType = 'paused';
			}
			trigger( { event: `gform/video/${ eventType }`, native: false, data: {
				options,
				player,
			} } );
		};

		// render the player into the dom on the react ref.
		vidyardApi
			.renderPlayer( options ).then( ( player ) => {
				// set rendered state
				setRendered( true );
				// after player render bind listeners in promise.
				document.addEventListener( 'gform/video/pause', controlPlayer );
				document.addEventListener( 'gform/video/pauseAll', controlPlayer );
				document.addEventListener( 'gform/video/play', controlPlayer );
				// trigger an event for consumers with instance.
				trigger( { event: 'gform/video/rendered', native: false, data: {
					options,
					player,
				} } );
			} );

		return () => {
			// on unmount clean up listeners and destroy player.
			const player = vidyardApi.getPlayersByUUID( videoOptions.uuid )[ 0 ];
			vidyardApi.destroyPlayer( player );
			document.removeEventListener( 'gform/video/pause', controlPlayer );
			document.removeEventListener( 'gform/video/pauseAll', controlPlayer );
			document.removeEventListener( 'gform/video/play', controlPlayer );
		};
	}, [ loading, error, videoOptions ] );

	if ( loading ) {
		return <h3>Loading Vidyard API...</h3>;
	}
	if ( error ) {
		return <h3>Failed to load Vidyard API: { error.message }</h3>;
	}

	const style = {
		display: rendered ? 'block' : 'none',
	};

	const componentProps = {
		className: classnames( {
			'gform-video': true,
			'gform-video--type-vidyard': true,
		}, customClasses ),
		...customAttributes,
		style,
	};

	const player = <div ref={ videoRef } { ...componentProps } />;

	const videoProps = {
		...videoCustomAttributes,
		player,
		placeholderButtonProps: {
			...videoCustomAttributes.placeholderButtonProps,
			onClick: () => {
				if ( typeof videoCustomAttributes.placeholderButtonProps.onClick === 'function' ) {
					videoCustomAttributes.placeholderButtonProps.onClick();
				}
				trigger( { event: 'gform/video/play', native: false, data: { uuid: videoOptions.uuid } } );
			},
		},
	};

	return <Video { ...videoProps } ref={ ref } />;
} );

VidyardVideo.propTypes = {
	customAttributes: PropTypes.object,
	customClasses: PropTypes.oneOfType( [
		PropTypes.string,
		PropTypes.array,
		PropTypes.object,
	] ),
	videoCustomAttributes: PropTypes.object,
	videoOptions: PropTypes.object,
};

VidyardVideo.displayName = 'Videos/VidyardVideo';

export default VidyardVideo;