EmbedModal: abstract embed code to new SafeEmbed component

This commit is contained in:
Alex Gleason 2022-08-21 12:22:06 -04:00
parent 33e13aa6e2
commit 0dd0742752
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
2 changed files with 60 additions and 27 deletions

View File

@ -0,0 +1,49 @@
import React, { useEffect, useRef } from 'react';
interface ISafeEmbed {
/** Styles for the outer frame element. */
className?: string,
/** Space-separate list of restrictions to ALLOW for the iframe. */
sandbox?: string,
/** Unique title for the iframe. */
title: string,
/** HTML body to embed. */
html?: string,
}
/** Safely embeds arbitrary HTML content on the page (by putting it in an iframe). */
const SafeEmbed: React.FC<ISafeEmbed> = ({
className,
sandbox,
title,
html,
}) => {
const iframe = useRef<HTMLIFrameElement>(null);
useEffect(() => {
const iframeDocument = iframe.current?.contentWindow?.document;
if (iframeDocument && html) {
iframeDocument.open();
iframeDocument.write(html);
iframeDocument.close();
iframeDocument.body.style.margin = '0';
const innerFrame = iframeDocument.querySelector('iframe');
if (innerFrame) {
innerFrame.width = '100%';
}
}
}, [iframe.current, html]);
return (
<iframe
ref={iframe}
className={className}
sandbox={sandbox}
title={title}
/>
);
};
export default SafeEmbed;

View File

@ -1,7 +1,8 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import api from 'soapbox/api'; import api from 'soapbox/api';
import SafeEmbed from 'soapbox/components/safe-embed';
import { Modal, Stack, Text, Input } from 'soapbox/components/ui'; import { Modal, Stack, Text, Input } from 'soapbox/components/ui';
import { useAppDispatch } from 'soapbox/hooks'; import { useAppDispatch } from 'soapbox/hooks';
@ -20,33 +21,17 @@ interface IEmbedModal {
const EmbedModal: React.FC<IEmbedModal> = ({ url, onError }) => { const EmbedModal: React.FC<IEmbedModal> = ({ url, onError }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [html, setHtml] = useState('');
const iframe = useRef<HTMLIFrameElement>(null);
const [oembed, setOembed] = useState<any>(null);
useEffect(() => { useEffect(() => {
dispatch(fetchEmbed(url)).then(({ data }) => { dispatch(fetchEmbed(url)).then(({ data }) => {
if (!iframe.current?.contentWindow) return; if (data?.html) {
setOembed(data); setHtml(data.html);
const iframeDocument = iframe.current.contentWindow.document;
iframeDocument.open();
iframeDocument.write(data.html);
iframeDocument.close();
const innerFrame = iframeDocument.querySelector('iframe');
iframeDocument.body.style.margin = '0';
if (innerFrame) {
innerFrame.width = '100%';
} }
}).catch(error => { }).catch(error => {
onError(error); onError(error);
}); });
}, [!!iframe.current]); }, []);
const handleInputClick: React.MouseEventHandler<HTMLInputElement> = (e) => { const handleInputClick: React.MouseEventHandler<HTMLInputElement> = (e) => {
e.currentTarget.select(); e.currentTarget.select();
@ -63,21 +48,20 @@ const EmbedModal: React.FC<IEmbedModal> = ({ url, onError }) => {
<Input <Input
type='text' type='text'
readOnly readOnly
value={oembed?.html || ''} value={html}
onClick={handleInputClick} onClick={handleInputClick}
/> />
</Stack> </Stack>
<iframe <SafeEmbed
className='inline-flex rounded-xl overflow-hidden max-w-full' className='inline-flex rounded-xl overflow-hidden max-w-full'
frameBorder='0'
ref={iframe}
sandbox='allow-same-origin' sandbox='allow-same-origin'
title='preview' title='embedded-status'
html={html}
/> />
</Stack> </Stack>
</Modal> </Modal>
); );
}; };
export default EmbedModal; export default EmbedModal;