diff --git a/extensions/blocks/calendly/edit.js b/extensions/blocks/calendly/edit.js
index b0e994ee0386..7db04d81371f 100644
--- a/extensions/blocks/calendly/edit.js
+++ b/extensions/blocks/calendly/edit.js
@@ -15,11 +15,12 @@ import {
Notice,
PanelBody,
Placeholder,
+ Spinner,
ToggleControl,
Toolbar,
withNotices,
} from '@wordpress/components';
-import { useState } from '@wordpress/element';
+import { useEffect, useState } from '@wordpress/element';
import { __, _x } from '@wordpress/i18n';
import { getBlockDefaultClassName } from '@wordpress/blocks';
@@ -34,6 +35,8 @@ import { getValidatedAttributes } from '../../shared/get-validated-attributes';
import SubmitButton from '../../shared/submit-button';
import { getAttributesFromEmbedCode } from './utils';
import BlockStylesSelector from '../../shared/components/block-styles-selector';
+import { CALENDLY_EXAMPLE_URL } from './';
+import testEmbedUrl from '../../shared/test-embed-url';
function CalendlyEdit( props ) {
const {
@@ -62,6 +65,7 @@ function CalendlyEdit( props ) {
url,
} = validatedAttributes;
const [ embedCode, setEmbedCode ] = useState( '' );
+ const [ isResolvingUrl, setIsResolvingUrl ] = useState( false );
const setErrorNotice = () => {
noticeOperations.removeAllNotices();
@@ -70,6 +74,16 @@ function CalendlyEdit( props ) {
);
};
+ useEffect( () => {
+ if ( ! url || CALENDLY_EXAMPLE_URL === url || 'link' === style ) {
+ return;
+ }
+ testEmbedUrl( url, setIsResolvingUrl ).catch( () => {
+ setAttributes( { url: undefined } );
+ setErrorNotice();
+ } );
+ }, [] );
+
const parseEmbedCode = event => {
if ( ! event ) {
setErrorNotice();
@@ -84,10 +98,16 @@ function CalendlyEdit( props ) {
return;
}
- const newValidatedAttributes = getValidatedAttributes( attributeDetails, newAttributes );
-
- setAttributes( newValidatedAttributes );
- noticeOperations.removeAllNotices();
+ testEmbedUrl( newAttributes.url, setIsResolvingUrl )
+ .then( () => {
+ const newValidatedAttributes = getValidatedAttributes( attributeDetails, newAttributes );
+ setAttributes( newValidatedAttributes );
+ noticeOperations.removeAllNotices();
+ } )
+ .catch( () => {
+ setAttributes( { url: undefined } );
+ setErrorNotice();
+ } );
};
const embedCodeForm = (
@@ -115,6 +135,13 @@ function CalendlyEdit( props ) {
>
);
+ const blockEmbedding = (
+
+
+
{ __( 'Embedding…', 'jetpack' ) }
+
+ );
+
const blockPlaceholder = (
);
+ if ( isResolvingUrl ) {
+ return blockEmbedding;
+ }
+
const classes = `${ className } calendly-style-${ style }`;
return (
diff --git a/extensions/blocks/calendly/index.js b/extensions/blocks/calendly/index.js
index ae56a7f4b34a..243d598a9a0e 100644
--- a/extensions/blocks/calendly/index.js
+++ b/extensions/blocks/calendly/index.js
@@ -17,6 +17,8 @@ import { getAttributesFromEmbedCode, REGEX } from './utils';
*/
import './editor.scss';
+export const CALENDLY_EXAMPLE_URL = 'https://calendly.com/wordpresscom/jetpack-block-example';
+
export const name = 'calendly';
export const title = __( 'Calendly', 'jetpack' );
export const settings = {
@@ -44,7 +46,7 @@ export const settings = {
submitButtonText: __( 'Schedule time with me', 'jetpack' ),
hideEventTypeDetails: false,
style: 'inline',
- url: 'https://calendly.com/wordpresscom/jetpack-block-example',
+ url: CALENDLY_EXAMPLE_URL,
},
},
transforms: {
diff --git a/extensions/shared/test-embed-url.js b/extensions/shared/test-embed-url.js
new file mode 100644
index 000000000000..b594f84b7c7d
--- /dev/null
+++ b/extensions/shared/test-embed-url.js
@@ -0,0 +1,41 @@
+/**
+ * External dependencies
+ */
+import { noop } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import apiFetch from '@wordpress/api-fetch';
+
+/**
+ * Test if a URL is accessible and respond with status code < 400.
+ *
+ * @example
+ * const [ isResolvingUrl, setIsResolvingUrl ] = useState();
+ * testEmbedUrl( url, setIsResolvingUrl )
+ * .then( () => setAttributes( url ) )
+ * .catch( () => setErrorNotice() );
+ *
+ * @param {String} url The URL to test.
+ * @param {Function} [setIsResolvingUrl=noop] An optional function to track the resolving state. Typically used to update the calling component's state.
+ * @returns {Promise} Resolve if the URL is valid, reject otherwise.
+ */
+export default function testEmbedUrl( url, setIsResolvingUrl = noop ) {
+ setIsResolvingUrl( true );
+ return new Promise( async ( resolve, reject ) => {
+ try {
+ const response = await apiFetch( { path: `/wpcom/v2/resolve-redirect/${ url }` } );
+ setIsResolvingUrl( false );
+ const responseStatusCode = response.status ? parseInt( response.status, 10 ) : null;
+ if ( responseStatusCode && responseStatusCode >= 400 ) {
+ reject();
+ } else {
+ resolve();
+ }
+ } catch ( error ) {
+ setIsResolvingUrl( false );
+ reject();
+ }
+ } );
+}