Skip to content

Commit

Permalink
feat: Fetch VideoPress metadata in edit component
Browse files Browse the repository at this point in the history
The Video block's is refactored to fetch VideoPress metadata, therefore enabling support for v5.
  • Loading branch information
Siobhan committed Feb 13, 2024
1 parent ea3a0a9 commit f5e9233
Showing 1 changed file with 101 additions and 46 deletions.
147 changes: 101 additions & 46 deletions projects/plugins/jetpack/extensions/blocks/videopress/edit.native.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
import apiFetch from '@wordpress/api-fetch';
import {
BlockCaption,
MediaPlaceholder,
Expand All @@ -17,7 +15,7 @@ import {
store as blockEditorStore,
} from '@wordpress/block-editor';
import { Icon, ToolbarButton, ToolbarGroup, PanelBody } from '@wordpress/components';
import { withPreferredColorScheme, compose } from '@wordpress/compose';
import { withPreferredColorScheme, compose, createHigherOrderComponent } from '@wordpress/compose';
import { withDispatch, withSelect } from '@wordpress/data';
import { Component } from '@wordpress/element';
import { doAction, hasAction } from '@wordpress/hooks';
Expand All @@ -30,28 +28,35 @@ import {
requestImageUploadCancelDialog,
} from '@wordpress/react-native-bridge';
import { isURL, getProtocol } from '@wordpress/url';
import { View, TouchableWithoutFeedback, Text } from 'react-native';
/**
* External dependencies
*/
import { ActivityIndicator, View, TouchableWithoutFeedback, Text } from 'react-native';
/**
* Internal dependencies
*/
import { createUpgradedEmbedBlock } from '../embed/util';
import VideoCommonSettings from './edit-common-settings';
import SvgIconRetry from './icon-retry';
import style from './style.scss';
import { pickGUIDFromUrl } from './utils';

const ICON_TYPE = {
PLACEHOLDER: 'placeholder',
RETRY: 'retry',
UPLOAD: 'upload',
};

class VideoEdit extends Component {
class VideoPressEdit extends Component {
constructor( props ) {
super( props );

this.state = {
isCaptionSelected: false,
videoContainerHeight: 0,
isUploadInProgress: false,
isUploadFailed: false,
isLoadingMetadata: false,
metadata: {},
};

this.mediaUploadStateReset = this.mediaUploadStateReset.bind( this );
Expand All @@ -65,11 +70,20 @@ class VideoEdit extends Component {
this.onFocusCaption = this.onFocusCaption.bind( this );
}

componentDidMount() {
async componentDidMount() {
const { attributes } = this.props;
const { guid } = attributes;
if ( attributes.id && getProtocol( attributes.src ) === 'file:' ) {
mediaUploadSync();
}

// Try to infer the VideoPress ID from the source upon component mount.
// If the ID already exists, fetch the metadata to get the video URL.
if ( ! guid ) {
await this.setGuid();
} else {
await this.fetchMetadata( guid );
}
}

componentWillUnmount() {
Expand All @@ -80,13 +94,40 @@ class VideoEdit extends Component {
}

static getDerivedStateFromProps( props, state ) {
// Avoid a UI flicker in the toolbar by insuring that isCaptionSelected
// is updated immediately any time the isSelected prop becomes false.
return {
// Avoid a UI flicker in the toolbar by insuring that isCaptionSelected
// is updated immediately any time the isSelected prop becomes false.
isCaptionSelected: props.isSelected && state.isCaptionSelected,
// Reset metadata when "guid" attribute is not defined.
metadata: props.attributes?.guid ? state.metadata : {},
};
}

async setGuid( value ) {
const { setAttributes, attributes } = this.props;
const { src } = attributes;
// If no value is passed, we try to extract the VideoPress ID from the source.
const guid = value ?? pickGUIDFromUrl( src ) ?? undefined;
setAttributes( { guid } );
if ( guid ) {
await this.fetchMetadata( guid );
}
}

async fetchMetadata( guid ) {
this.setState( { isLoadingMetadata: true } );
try {
const metadata = await apiFetch( {
path: `/rest/v1.1/videos/${ guid }`,
} );
this.setState( { metadata, isLoadingMetadata: false } );
} catch ( error ) {
// eslint-disable-next-line no-console
console.error( `Couldn't fetch metadata of VideoPress video with ID = ${ guid }`, error );
this.setState( { isLoadingMetadata: false } );
}
}

onVideoPressed() {
const { attributes } = this.props;

Expand Down Expand Up @@ -119,8 +160,11 @@ class VideoEdit extends Component {

finishMediaUploadWithSuccess( payload ) {
const { setAttributes } = this.props;
setAttributes( { src: payload.mediaUrl, id: payload.mediaServerId } );
const { mediaUrl, mediaServerId, metadata = {} } = payload;
const { videopressGUID } = metadata;
setAttributes( { src: mediaUrl, id: mediaServerId } );
this.setState( { isUploadInProgress: false } );
this.setGuid( videopressGUID );
}

finishMediaUploadWithFailure( payload ) {
Expand All @@ -131,29 +175,23 @@ class VideoEdit extends Component {

mediaUploadStateReset() {
const { setAttributes } = this.props;
setAttributes( { id: null, src: null } );
setAttributes( { id: null, src: null, guid: null } );
this.setState( { isUploadInProgress: false } );
}

onSelectMediaUploadOption( { id, url } ) {
onSelectMediaUploadOption( payload ) {
const { setAttributes } = this.props;
const { id, url, metadata = {} } = payload;
const { videopressGUID } = metadata;
setAttributes( { id, src: url } );
this.setGuid( videopressGUID );
}

onSelectURL( url ) {
const { createErrorNotice, onReplace, setAttributes } = this.props;
const { createErrorNotice, setAttributes } = this.props;

if ( isURL( url ) && /^https?:/.test( getProtocol( url ) ) ) {
// Check if there's an embed block that handles this URL.
const embedBlock = createUpgradedEmbedBlock( {
attributes: { url },
} );
if ( undefined !== embedBlock ) {
onReplace( embedBlock );
return;
}

setAttributes( { src: url, id: undefined, poster: undefined } );
if ( isURL( url ) ) {
setAttributes( { id: url, src: url } );
} else {
createErrorNotice( __( 'Invalid URL.', 'jetpack' ) );
}
Expand Down Expand Up @@ -186,10 +224,19 @@ class VideoEdit extends Component {
return <Icon icon={ SvgIcon } { ...iconStyle } />;
}

getVideoURL() {
const { attributes } = this.props;
const { src, guid } = attributes;
const { metadata = {} } = this.state;

return metadata.original || `https://videopress.com/v/${ guid }` || src;
}

render() {
const { setAttributes, attributes, isSelected, wasBlockJustInserted } = this.props;
const { id, src, guid } = attributes;
const { videoContainerHeight } = this.state;
const { isLoadingMetadata, isUploadInProgress, isUploadFailed, videoContainerHeight } =
this.state;

const toolbarEditButton = (
<MediaUpload
Expand All @@ -212,8 +259,6 @@ class VideoEdit extends Component {
></MediaUpload>
);

// NOTE: `guid` is not part of the block's attribute definition. This case
// handled here is a temporary fix until a we find a better approach.
const isSourcePresent = src || ( guid && id );
if ( ! isSourcePresent ) {
return (
Expand Down Expand Up @@ -251,8 +296,13 @@ class VideoEdit extends Component {
onFinishMediaUploadWithFailure={ this.finishMediaUploadWithFailure }
onUpdateMediaProgress={ this.updateMediaProgress }
onMediaUploadStateReset={ this.mediaUploadStateReset }
renderContent={ ( { isUploadInProgress, isUploadFailed, retryMessage } ) => {
const showVideo = isURL( src ) && ! isUploadInProgress && ! isUploadFailed;
renderContent={ ( { retryMessage } ) => {
const videoURL = this.getVideoURL();
const showVideo =
isURL( videoURL ) &&
! isUploadInProgress &&
! isUploadFailed &&
! isLoadingMetadata;
const icon = this.getIcon( isUploadFailed ? ICON_TYPE.RETRY : ICON_TYPE.UPLOAD );
const styleIconContainer = isUploadFailed ? style.modalIconRetry : style.modalIcon;

Expand All @@ -273,7 +323,7 @@ class VideoEdit extends Component {
<VideoPlayer
isSelected={ isSelected && ! this.state.isCaptionSelected }
style={ videoStyle }
source={ { uri: src } }
source={ { uri: videoURL } }
paused={ true }
/>
</View>
Expand All @@ -289,7 +339,8 @@ class VideoEdit extends Component {
),
} }
>
{ videoContainerHeight > 0 && iconContainer }
{ videoContainerHeight > 0 &&
( isLoadingMetadata ? <ActivityIndicator /> : iconContainer ) }
{ isUploadFailed && (
<Text style={ style.uploadFailedText }>{ retryMessage }</Text>
) }
Expand Down Expand Up @@ -322,17 +373,21 @@ class VideoEdit extends Component {
}
}

export default compose( [
withSelect( ( select, { clientId } ) => ( {
wasBlockJustInserted: select( blockEditorStore ).wasBlockJustInserted(
clientId,
'inserter_menu'
),
} ) ),
withDispatch( dispatch => {
const { createErrorNotice } = dispatch( noticesStore );

return { createErrorNotice };
} ),
withPreferredColorScheme,
] )( VideoEdit );
export default CoreVideoEdit =>
compose( [
withSelect( ( select, { clientId } ) => ( {
wasBlockJustInserted: select( blockEditorStore ).wasBlockJustInserted(
clientId,
'inserter_menu'
),
} ) ),
withDispatch( dispatch => {
const { createErrorNotice } = dispatch( noticesStore );

return { createErrorNotice };
} ),
withPreferredColorScheme,
createHigherOrderComponent( WrappedComponent => props => {
return <WrappedComponent originalEdit={ CoreVideoEdit } { ...props } />;
} ),
] )( VideoPressEdit );

0 comments on commit f5e9233

Please sign in to comment.