Handle cards with iframe embeds

This commit is contained in:
Lim Chee Aun 2024-01-06 16:46:45 +08:00
parent 16e2ac9bce
commit 147a12cbcb
5 changed files with 127 additions and 34 deletions

View file

@ -0,0 +1,27 @@
.embed-modal-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
pointer-events: none;
.top-controls {
padding: 16px;
display: flex;
gap: 8px;
justify-content: space-between;
pointer-events: auto;
}
.embed-content {
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
iframe {
pointer-events: auto;
max-width: 100%;
}
}
}

View file

@ -0,0 +1,28 @@
import './embed-modal.css';
import Icon from './icon';
function EmbedModal({ html, url, onClose = () => {} }) {
return (
<div class="embed-modal-container">
<div class="top-controls">
<button type="button" class="light" onClick={() => onClose()}>
<Icon icon="x" />
</button>
{url && (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
class="button plain"
>
<span>Open link</span> <Icon icon="external" />
</a>
)}
</div>
<div class="embed-content" dangerouslySetInnerHTML={{ __html: html }} />
</div>
);
}
export default EmbedModal;

View file

@ -10,6 +10,7 @@ import states from '../utils/states';
import AccountSheet from './account-sheet'; import AccountSheet from './account-sheet';
import Compose from './compose'; import Compose from './compose';
import Drafts from './drafts'; import Drafts from './drafts';
import EmbedModal from './embed-modal';
import GenericAccounts from './generic-accounts'; import GenericAccounts from './generic-accounts';
import MediaAltModal from './media-alt-modal'; import MediaAltModal from './media-alt-modal';
import MediaModal from './media-modal'; import MediaModal from './media-modal';
@ -200,6 +201,21 @@ export default function Modals() {
/> />
</Modal> </Modal>
)} )}
{!!snapStates.showEmbedModal && (
<Modal
onClose={() => {
states.showEmbedModal = false;
}}
>
<EmbedModal
html={snapStates.showEmbedModal.html}
url={snapStates.showEmbedModal.url}
onClose={() => {
states.showEmbedModal = false;
}}
/>
</Modal>
)}
</> </>
); );
} }

View file

@ -1984,6 +1984,20 @@ function Card({ card, selfReferential, instance }) {
if (snapStates.unfurledLinks[url]) return null; if (snapStates.unfurledLinks[url]) return null;
const hasIframeHTML = /<iframe/i.test(html);
const handleClick = useCallback(
(e) => {
if (hasIframeHTML) {
e.preventDefault();
states.showEmbedModal = {
html,
url: url || embedUrl,
};
}
},
[hasIframeHTML],
);
if (hasText && (image || (type === 'photo' && blurhash))) { if (hasText && (image || (type === 'photo' && blurhash))) {
const domain = new URL(url).hostname const domain = new URL(url).hostname
.replace(/^www\./, '') .replace(/^www\./, '')
@ -2016,6 +2030,7 @@ function Card({ card, selfReferential, instance }) {
'--average-color': '--average-color':
rgbAverageColor && `rgb(${rgbAverageColor.join(',')})`, rgbAverageColor && `rgb(${rgbAverageColor.join(',')})`,
}} }}
onClick={handleClick}
> >
<div class="card-image"> <div class="card-image">
<img <img
@ -2054,6 +2069,7 @@ function Card({ card, selfReferential, instance }) {
target="_blank" target="_blank"
rel="nofollow noopener noreferrer" rel="nofollow noopener noreferrer"
class="card photo" class="card photo"
onClick={handleClick}
> >
<img <img
src={embedUrl} src={embedUrl}
@ -2068,7 +2084,8 @@ function Card({ card, selfReferential, instance }) {
/> />
</a> </a>
); );
} else if (type === 'video') { } else {
if (type === 'video') {
if (/youtube/i.test(providerName)) { if (/youtube/i.test(providerName)) {
// Get ID from e.g. https://www.youtube.com/watch?v=[VIDEO_ID] // Get ID from e.g. https://www.youtube.com/watch?v=[VIDEO_ID]
const videoID = url.match(/watch\?v=([^&]+)/)?.[1]; const videoID = url.match(/watch\?v=([^&]+)/)?.[1];
@ -2076,16 +2093,17 @@ function Card({ card, selfReferential, instance }) {
return <lite-youtube videoid={videoID} nocookie></lite-youtube>; return <lite-youtube videoid={videoID} nocookie></lite-youtube>;
} }
} }
return ( // return (
<div // <div
class="card video" // class="card video"
style={{ // style={{
aspectRatio: `${width}/${height}`, // aspectRatio: `${width}/${height}`,
}} // }}
dangerouslySetInnerHTML={{ __html: html }} // dangerouslySetInnerHTML={{ __html: html }}
/> // />
); // );
} else if (hasText && !image) { }
if (hasText && !image) {
const domain = new URL(url).hostname.replace(/^www\./, ''); const domain = new URL(url).hostname.replace(/^www\./, '');
return ( return (
<a <a
@ -2094,6 +2112,7 @@ function Card({ card, selfReferential, instance }) {
rel="nofollow noopener noreferrer" rel="nofollow noopener noreferrer"
class={`card link no-image`} class={`card link no-image`}
lang={language} lang={language}
onClick={handleClick}
> >
<div class="meta-container"> <div class="meta-container">
<p class="meta domain"> <p class="meta domain">
@ -2105,6 +2124,7 @@ function Card({ card, selfReferential, instance }) {
</a> </a>
); );
} }
}
} }
function EditedAtModal({ function EditedAtModal({

View file

@ -50,6 +50,7 @@ const states = proxy({
showKeyboardShortcutsHelp: false, showKeyboardShortcutsHelp: false,
showGenericAccounts: false, showGenericAccounts: false,
showMediaAlt: false, showMediaAlt: false,
showEmbedModal: false,
// Shortcuts // Shortcuts
shortcuts: [], shortcuts: [],
// Settings // Settings
@ -151,6 +152,7 @@ export function hideAllModals() {
states.showKeyboardShortcutsHelp = false; states.showKeyboardShortcutsHelp = false;
states.showGenericAccounts = false; states.showGenericAccounts = false;
states.showMediaAlt = false; states.showMediaAlt = false;
states.showEmbedModal = false;
} }
export function statusKey(id, instance) { export function statusKey(id, instance) {