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 Compose from './compose';
import Drafts from './drafts';
import EmbedModal from './embed-modal';
import GenericAccounts from './generic-accounts';
import MediaAltModal from './media-alt-modal';
import MediaModal from './media-modal';
@ -200,6 +201,21 @@ export default function Modals() {
/>
</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;
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))) {
const domain = new URL(url).hostname
.replace(/^www\./, '')
@ -2016,6 +2030,7 @@ function Card({ card, selfReferential, instance }) {
'--average-color':
rgbAverageColor && `rgb(${rgbAverageColor.join(',')})`,
}}
onClick={handleClick}
>
<div class="card-image">
<img
@ -2054,6 +2069,7 @@ function Card({ card, selfReferential, instance }) {
target="_blank"
rel="nofollow noopener noreferrer"
class="card photo"
onClick={handleClick}
>
<img
src={embedUrl}
@ -2068,42 +2084,46 @@ function Card({ card, selfReferential, instance }) {
/>
</a>
);
} else if (type === 'video') {
if (/youtube/i.test(providerName)) {
// Get ID from e.g. https://www.youtube.com/watch?v=[VIDEO_ID]
const videoID = url.match(/watch\?v=([^&]+)/)?.[1];
if (videoID) {
return <lite-youtube videoid={videoID} nocookie></lite-youtube>;
} else {
if (type === 'video') {
if (/youtube/i.test(providerName)) {
// Get ID from e.g. https://www.youtube.com/watch?v=[VIDEO_ID]
const videoID = url.match(/watch\?v=([^&]+)/)?.[1];
if (videoID) {
return <lite-youtube videoid={videoID} nocookie></lite-youtube>;
}
}
// return (
// <div
// class="card video"
// style={{
// aspectRatio: `${width}/${height}`,
// }}
// dangerouslySetInnerHTML={{ __html: html }}
// />
// );
}
if (hasText && !image) {
const domain = new URL(url).hostname.replace(/^www\./, '');
return (
<a
href={cardStatusURL || url}
target={cardStatusURL ? null : '_blank'}
rel="nofollow noopener noreferrer"
class={`card link no-image`}
lang={language}
onClick={handleClick}
>
<div class="meta-container">
<p class="meta domain">
<Icon icon="link" size="s" /> <span>{domain}</span>
</p>
<p class="title">{title}</p>
<p class="meta">{description || providerName || authorName}</p>
</div>
</a>
);
}
return (
<div
class="card video"
style={{
aspectRatio: `${width}/${height}`,
}}
dangerouslySetInnerHTML={{ __html: html }}
/>
);
} else if (hasText && !image) {
const domain = new URL(url).hostname.replace(/^www\./, '');
return (
<a
href={cardStatusURL || url}
target={cardStatusURL ? null : '_blank'}
rel="nofollow noopener noreferrer"
class={`card link no-image`}
lang={language}
>
<div class="meta-container">
<p class="meta domain">
<Icon icon="link" size="s" /> <span>{domain}</span>
</p>
<p class="title">{title}</p>
<p class="meta">{description || providerName || authorName}</p>
</div>
</a>
);
}
}

View file

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