Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PLANET-6198 Convert SocialMedia block to WYSIWYG #623

Merged
merged 3 commits into from
Aug 18, 2021
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
3 changes: 1 addition & 2 deletions assets/src/blocks/Media/MediaEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { MediaPlaceholder, InspectorControls } from '@wordpress/block-editor';
import { TextControl } from '@wordpress/components';
import { debounce } from 'lodash';

import { MediaEmbedPreview } from "./MediaEmbedPreview";
import { MediaElementVideo } from './MediaElementVideo';
import { useSelect } from '@wordpress/data';
import { lacksAttributes } from './MediaBlock';
Expand Down Expand Up @@ -71,7 +70,7 @@ const renderView = (attributes, toAttribute) => {

const VideoComponent = media_url?.endsWith('.mp4')
? <MediaElementVideo videoURL={ media_url } videoPoster={ poster_url } />
: <MediaEmbedPreview html={ embed_html || null } />;
: <div dangerouslySetInnerHTML={{ __html: embed_html || null }} />;

const ErrorMessage = <div className="block-edit-mode-warning components-notice is-error">
{ __( 'The video URL could not be parsed.', 'planet4-blocks-backend' ) }
Expand Down
1 change: 0 additions & 1 deletion assets/src/blocks/Media/MediaEmbedPreview.js

This file was deleted.

3 changes: 1 addition & 2 deletions assets/src/blocks/Media/MediaFrontend.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { MediaEmbedPreview } from './MediaEmbedPreview';
import { MediaElementVideo } from './MediaElementVideo';

const wrapEmbedHTML = embed_html => {
Expand Down Expand Up @@ -47,7 +46,7 @@ export const MediaFrontend = ( attributes ) => {
{
media_url && media_url.endsWith('.mp4')
? <MediaElementVideo videoURL={ media_url } videoPoster={ poster_url } />
: <MediaEmbedPreview html={ wrapEmbedHTML(embed_html) || null } />
: <div dangerouslySetInnerHTML={{ __html: wrapEmbedHTML(embed_html) || null }} />
}
</div>
</section>
Expand Down
50 changes: 50 additions & 0 deletions assets/src/blocks/SocialMedia/SocialMediaBlock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { SocialMediaEditor } from './SocialMediaEditorScript.js';
import { socialMediaV1 } from './deprecated/socialMediaV1';
import { OEMBED_EMBED_TYPE, FACEBOOK_PAGE_TAB_TIMELINE } from './SocialMediaConstants.js';
import { SocialMediaFrontend } from './SocialMediaFrontend.js';

const { registerBlockType } = wp.blocks;
const { __ } = wp.i18n;

const BLOCK_NAME = 'planet4-blocks/social-media';

export const registerSocialMediaBlock = () => registerBlockType(BLOCK_NAME, {
title: __('Social Media', 'planet4-blocks-backend'),
icon: 'share',
category: 'planet4-blocks',
attributes: {
title: {
type: 'string',
default: '',
},
description: {
type: 'string',
default: '',
},
embed_type: {
type: 'string',
default: OEMBED_EMBED_TYPE,
},
facebook_page_tab: {
type: 'string',
default: FACEBOOK_PAGE_TAB_TIMELINE,
},
social_media_url: {
type: 'string',
default: ''
},
alignment_class: {
type: 'string',
default: ''
},
embed_code: {
type: 'string',
default: '',
},
},
edit: SocialMediaEditor,
save: ({ attributes }) => <SocialMediaFrontend {...attributes} />,
deprecated: [
socialMediaV1,
],
});
12 changes: 12 additions & 0 deletions assets/src/blocks/SocialMedia/SocialMediaConstants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const OEMBED_EMBED_TYPE = 'oembed';
export const FACEBOOK_EMBED_TYPE = 'facebook_page';

export const FACEBOOK_PAGE_TAB_TIMELINE = 'timeline';
export const FACEBOOK_PAGE_TAB_EVENTS = 'events';
export const FACEBOOK_PAGE_TAB_MESSAGES = 'messages';

export const ALLOWED_OEMBED_PROVIDERS = [
'twitter',
'facebook',
'instagram',
];
248 changes: 248 additions & 0 deletions assets/src/blocks/SocialMedia/SocialMediaEditorScript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import { useEffect } from '@wordpress/element';
import { InspectorControls } from '@wordpress/block-editor';
import {
RadioControl,
SelectControl,
PanelBody,
} from '@wordpress/components';
import { SocialMediaEmbed } from './SocialMediaEmbed';
import { URLInput } from '../../components/URLInput/URLInput';
import { HTMLSidebarHelp } from '../../components/HTMLSidebarHelp/HTMLSidebarHelp';
import {
OEMBED_EMBED_TYPE,
FACEBOOK_EMBED_TYPE,
FACEBOOK_PAGE_TAB_TIMELINE,
FACEBOOK_PAGE_TAB_EVENTS,
FACEBOOK_PAGE_TAB_MESSAGES,
ALLOWED_OEMBED_PROVIDERS,
} from './SocialMediaConstants.js';

const { RichText } = wp.blockEditor;
const { __ } = wp.i18n;
const { apiFetch } = wp;
const { addQueryArgs } = wp.url;

const loadScriptAsync = uri => {
mleray marked this conversation as resolved.
Show resolved Hide resolved
return new Promise((resolve, reject) => {
let tag = document.createElement('script');
tag.src = uri;
tag.async = true;
tag.onload = () => {
resolve();
};
const body = document.getElementsByTagName('body')[0];
body.appendChild(tag);
});
};

const initializeTwitterEmbeds = () => {
setTimeout(() => {
if ('undefined' !== window.twttr) {
window.twttr.widgets.load();
}
}, 2000);
mleray marked this conversation as resolved.
Show resolved Hide resolved
}

const initializeInstagramEmbeds = () => {
setTimeout(() => {
if ('undefined' !== window.instgrm) {
window.instgrm.Embeds.process();
}
}, 3000);
}

const initializeFacebookEmbeds = () => {
setTimeout(() => {
if ('undefined' !== window.FB) {
window.FB.XFBML.parse();
}
}, 3000);
}

const PROVIDER_SCRIPT_DATA = {
twitter: {
script: 'https://platform.twitter.com/widgets.js',
initFunction: initializeTwitterEmbeds,
},
facebook: {
script: 'https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v9.0',
initFunction: initializeFacebookEmbeds,
},
instagram: {
script: 'https://www.instagram.com/embed.js',
initFunction: initializeInstagramEmbeds,
},
};

export const SocialMediaEditor = ({
attributes,
isSelected,
setAttributes,
}) => {
const {
social_media_url,
facebook_page_tab,
description,
title,
embed_type,
alignment_class,
embed_code,
className,
} = attributes;

const toAttribute = attributeName => value => setAttributes({
[attributeName]: value
});

/**
* Check if social media corresponding embeds script is loaded and initiliaze it.
* Can be used for Facebook, Twitter and Instagram depending on the parameter.
*/
const checkProviderScript = async provider => {
const providerData = PROVIDER_SCRIPT_DATA[provider];
const script = document.querySelector(`body > script[src="${providerData.script}"]`);
if (script === null) {
mleray marked this conversation as resolved.
Show resolved Hide resolved
await loadScriptAsync(providerData.script);
}
providerData.initFunction();
}

const updateEmbed = async (url, provider) => {
if (!url) {
setAttributes({ embed_code: '' });
return;
}

let embedCode;
try {
if (provider === 'twitter') {
const twitterEmbedData = await apiFetch({ path: addQueryArgs('/oembed/1.0/proxy', { url }) });
embedCode = twitterEmbedData ? twitterEmbedData.html : '';
} else if (provider === 'instagram') {
const instagramEmbedData = await apiFetch({ path: addQueryArgs('planet4/v1/get-instagram-embed', { url }) });

if (instagramEmbedData) {

// WordPress automatically adds rel="noopener" to links that have _blank target.
// The Instagram embed HTML doesn't, so in order to avoid block validation errors we need to add it ourselves.
embedCode = instagramEmbedData.replaceAll(`target="_blank"`, `target="_blank" rel="noopener noreferrer"`);
}
}
} catch (error) {
embedCode = '';
}
setAttributes({ embed_code: embedCode });
};

useEffect(() => {
const provider = ALLOWED_OEMBED_PROVIDERS.find(allowedProvider => social_media_url.includes(allowedProvider));

if (!provider) {
setAttributes({ embed_code: '' });
return;
}

(async () => {
await checkProviderScript(provider);

// For Facebook we don't need the embed HTML code since we use an iframe
if (provider !== 'facebook') {
updateEmbed(social_media_url, provider);
}
})();
}, [social_media_url]);

const embed_type_help = __('Select oEmbed for the following types of social media<br>- Twitter: tweet, profile, list, collection, likes, moment<br>- Facebook: post, activity, photo, video, media, question, note<br>- Instagram: image', 'planet4-blocks-backend');

const renderEditInPlace = () => (
<>
<header>
<RichText
tagName='h2'
className='page-section-header'
placeholder={__('Enter title', 'planet4-blocks-backend')}
value={title}
onChange={toAttribute('title')}
keepPlaceholderOnFocus={true}
withoutInteractiveFormatting
multiline='false'
allowedFormats={[]}
/>
</header>
<RichText
tagName='p'
className='page-section-description'
placeholder={__('Enter description', 'planet4-blocks-backend')}
value={description}
onChange={toAttribute('description')}
keepPlaceholderOnFocus={true}
withoutInteractiveFormatting
allowedFormats={['core/bold', 'core/italic']}
/>
</>
);

const renderSidebar = () => (
<InspectorControls>
<PanelBody title={__('Setting', 'planet4-blocks-backend')}>
<RadioControl
label={__('Embed type', 'planet4-blocks-backend')}
options={[
{ label: __('oEmbed', 'planet4-blocks-backend'), value: OEMBED_EMBED_TYPE },
{ label: __('Facebook page', 'planet4-blocks-backend'), value: FACEBOOK_EMBED_TYPE },
]}
selected={embed_type}
onChange={toAttribute('embed_type')}
/>
<HTMLSidebarHelp>{embed_type_help}</HTMLSidebarHelp>
{embed_type === FACEBOOK_EMBED_TYPE &&
<>
<label>
{__('What Facebook page content would you like to display?', 'planet4-blocks-backend')}
</label>
<SelectControl
value={facebook_page_tab}
options={[
{ label: __('Timeline', 'planet4-blocks-backend'), value: FACEBOOK_PAGE_TAB_TIMELINE },
{ label: __('Events', 'planet4-blocks-backend'), value: FACEBOOK_PAGE_TAB_EVENTS },
{ label: __('Messages', 'planet4-blocks-backend'), value: FACEBOOK_PAGE_TAB_MESSAGES },
]}
onChange={toAttribute('facebook_page_tab')}
/>
</>
}
<URLInput
label={__('URL', 'planet4-blocks-backend')}
placeholder={__('Enter URL', 'planet4-blocks-backend')}
value={social_media_url}
onChange={toAttribute('social_media_url')}
/>
<SelectControl
label={__('Alignment', 'planet4-blocks-backend')}
value={alignment_class}
options={[
{ label: __('None', 'planet4-blocks-backend'), value: '' },
{ label: __('Left', 'planet4-blocks-backend'), value: 'alignleft' },
{ label: __('Center', 'planet4-blocks-backend'), value: 'aligncenter' },
{ label: __('Right', 'planet4-blocks-backend'), value: 'alignright' },
]}
onChange={toAttribute('alignment_class')}
/>
</PanelBody>
</InspectorControls>
);

return (
<section className={`block social-media-block ${className ?? ''}`}>
{isSelected && renderSidebar()}
{renderEditInPlace()}
<SocialMediaEmbed
embedCode={embed_code || ''}
facebookPageTab={facebook_page_tab}
facebookPageUrl={social_media_url}
alignmentClass={alignment_class}
embedType={embed_type}
/>
</section>
);
}
42 changes: 42 additions & 0 deletions assets/src/blocks/SocialMedia/SocialMediaEmbed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { OEMBED_EMBED_TYPE, FACEBOOK_EMBED_TYPE } from './SocialMediaConstants.js';

export const SocialMediaEmbed = ({
alignmentClass,
embedCode,
facebookPageTab,
facebookPageUrl,
embedType,
}) => {
if (
(embedType === OEMBED_EMBED_TYPE && !embedCode) ||
(embedType === FACEBOOK_EMBED_TYPE && !facebookPageUrl)
) {
return null;
}

return (
<div className={`social-media-embed ${alignmentClass ?? ''}`}>
{(embedType === OEMBED_EMBED_TYPE && embedCode) ?
<div dangerouslySetInnerHTML={{ __html: embedCode }} /> :
<>
<iframe
className="social-media-embed-facebook social-media-embed-facebook--small"
src={`https://www.facebook.com/plugins/page.php?href=${encodeURIComponent(facebookPageUrl)}&tabs=${facebookPageTab}&width=240&height=500&small_header=false&adapt_container_width=true&hide_cover=false&show_facepile=true`}
scrolling="no"
frameBorder="0"
allowtransparency="true"
allow="encrypted-media"
/>
<iframe
className="social-media-embed-facebook social-media-embed-facebook--large"
src={`https://www.facebook.com/plugins/page.php?href=${encodeURIComponent(facebookPageUrl)}&tabs=${facebookPageTab}&width=500&height=500&small_header=false&adapt_container_width=true&hide_cover=false&show_facepile=true`}
scrolling="no"
frameBorder="0"
allowtransparency="true"
allow="encrypted-media"
/>
</>
}
</div>
);
}
Loading