Add Instagram embeds to blog posts to display directly on the page

Update the article page to load the Instagram embed script and sanitize Instagram blockquotes, allowing embeds to render correctly on the page.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 413891e8-d784-4bea-b9f5-91a5a68316b4
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: a886b5f4-4c46-4c56-840e-56e53401d608
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/413891e8-d784-4bea-b9f5-91a5a68316b4/kmpcO4B
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
sebastjanartic 2026-02-28 17:11:30 +00:00
parent 20f703b0b5
commit 072a47a69a
2 changed files with 30 additions and 3 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

View File

@ -9,6 +9,7 @@ import { Skeleton } from "@/components/ui/skeleton";
import Header from "@/components/header"; import Header from "@/components/header";
import Footer from "@/components/footer"; import Footer from "@/components/footer";
import DOMPurify from "dompurify"; import DOMPurify from "dompurify";
import { useEffect } from "react";
const ALLOWED_IFRAME_DOMAINS = [ const ALLOWED_IFRAME_DOMAINS = [
"iframe.mediadelivery.net", "iframe.mediadelivery.net",
@ -23,8 +24,8 @@ const ALLOWED_IFRAME_DOMAINS = [
function sanitizeContent(html: string): string { function sanitizeContent(html: string): string {
return DOMPurify.sanitize(html, { return DOMPurify.sanitize(html, {
ADD_TAGS: ["iframe"], ADD_TAGS: ["iframe", "blockquote"],
ADD_ATTR: ["allow", "allowfullscreen", "frameborder", "scrolling", "src", "loading", "style"], ADD_ATTR: ["allow", "allowfullscreen", "frameborder", "scrolling", "src", "loading", "style", "class", "data-instgrm-permalink", "data-instgrm-version", "data-instgrm-captioned", "cite"],
ALLOW_UNKNOWN_PROTOCOLS: false, ALLOW_UNKNOWN_PROTOCOLS: false,
}); });
} }
@ -96,6 +97,31 @@ export default function ArticlePage() {
queryKey: ["/api/articles", slug], queryKey: ["/api/articles", slug],
}); });
useEffect(() => {
if (!article?.content) return;
if (article.content.includes("instagram.com")) {
const existing = document.querySelector('script[src*="instagram.com/embed.js"]');
if (existing) existing.remove();
const script = document.createElement("script");
script.src = "https://www.instagram.com/embed.js";
script.async = true;
document.body.appendChild(script);
script.onload = () => {
if ((window as any).instgrm) {
(window as any).instgrm.Embeds.process();
}
};
}
if (article.content.includes("tiktok.com")) {
if (!document.querySelector('script[src*="tiktok.com/embed.js"]')) {
const script = document.createElement("script");
script.src = "https://www.tiktok.com/embed.js";
script.async = true;
document.body.appendChild(script);
}
}
}, [article?.content]);
if (isLoading) { if (isLoading) {
return ( return (
<div className="min-h-screen bg-background"> <div className="min-h-screen bg-background">
@ -178,7 +204,8 @@ export default function ArticlePage() {
prose-img:rounded-md prose-img:w-full prose-img:object-cover prose-img:rounded-md prose-img:w-full prose-img:object-cover
[&_iframe]:rounded-md [&_iframe]:my-6 [&_iframe]:max-w-full [&_iframe]:rounded-md [&_iframe]:my-6 [&_iframe]:max-w-full
[&_div[style]]:flex [&_div[style]]:justify-center [&_div[style]]:flex [&_div[style]]:justify-center
[&_blockquote]:border-l-primary [&_blockquote]:bg-accent/50 [&_blockquote]:rounded-r-md [&_blockquote]:py-1" [&_blockquote:not(.instagram-media)]:border-l-primary [&_blockquote:not(.instagram-media)]:bg-accent/50 [&_blockquote:not(.instagram-media)]:rounded-r-md [&_blockquote:not(.instagram-media)]:py-1
[&_.instagram-media]:!bg-transparent [&_.instagram-media]:!border-0 [&_.instagram-media]:!shadow-none [&_.instagram-media]:!p-0 [&_.instagram-media]:mx-auto"
dangerouslySetInnerHTML={{ __html: sanitizeContent(article.content) }} dangerouslySetInnerHTML={{ __html: sanitizeContent(article.content) }}
data-testid="article-content" data-testid="article-content"
/> />