Clanek: napis pod sliko stoji tik pod naslovnico, ne za prvim oglasom

This commit is contained in:
folx bot
2026-08-29 21:32:01 +02:00
parent b9b2a6c1f2
commit fd9121e8db
+36 -11
View File
@@ -28,6 +28,17 @@ const ALLOWED_IFRAME_DOMAINS = [
"player.vimeo.com", "player.vimeo.com",
]; ];
/** Napis pod sliko (»Auf dem Bild: …« / »Foto: …«) locimo od besedila, da stoji
* tik pod naslovnico in ne sele za prvim oglasom. */
function splitCaption(html: string): { caption: string; body: string } {
const re = /<p[^>]*>\s*(?:<em>)?\s*(?:Auf dem Bild|Foto)\s*:[\s\S]*?<\/p>/gi;
const found = html.match(re);
if (!found || found.length === 0) return { caption: "", body: html };
let body = html;
for (const f of found) body = body.replace(f, "");
return { caption: found.join(" "), body };
}
function sanitizeContent(html: string): string { function sanitizeContent(html: string): string {
return DOMPurify.sanitize(html, { return DOMPurify.sanitize(html, {
ADD_TAGS: ["iframe", "blockquote"], ADD_TAGS: ["iframe", "blockquote"],
@@ -138,6 +149,11 @@ export default function ArticlePage() {
if (targetUrl) navigate(targetUrl, { replace: true }); if (targetUrl) navigate(targetUrl, { replace: true });
}, [targetUrl, navigate]); }, [targetUrl, navigate]);
const { caption: coverCaption, body: articleBody } = useMemo(
() => splitCaption(article?.content || ""),
[article?.content],
);
const articleUrl = article ? `${window.location.origin}/article/${article.slug}` : ""; const articleUrl = article ? `${window.location.origin}/article/${article.slug}` : "";
const articleImage = article?.coverImage const articleImage = article?.coverImage
? (article.coverImage.startsWith("http") ? article.coverImage : `${window.location.origin}${article.coverImage}`) ? (article.coverImage.startsWith("http") ? article.coverImage : `${window.location.origin}${article.coverImage}`)
@@ -338,16 +354,25 @@ export default function ArticlePage() {
</Link> </Link>
{article.coverImage && ( {article.coverImage && (
<div className="relative overflow-hidden rounded-md mb-5 max-h-[420px]"> <figure className="mb-5">
<img <div className="relative overflow-hidden rounded-md max-h-[420px]">
src={article.coverImage} <img
alt={article.title} src={article.coverImage}
className="w-full h-full object-cover" alt={article.title}
style={{ maxHeight: "420px", objectPosition: objectPositionFor(article.coverImage, focalPoints) }} className="w-full h-full object-cover"
data-testid="img-article-cover" style={{ maxHeight: "420px", objectPosition: objectPositionFor(article.coverImage, focalPoints) }}
/> data-testid="img-article-cover"
<div className="absolute inset-0 bg-gradient-to-t from-black/40 to-transparent" /> />
</div> <div className="absolute inset-0 bg-gradient-to-t from-black/40 to-transparent" />
</div>
{coverCaption && (
<figcaption
className="mt-2 text-xs text-muted-foreground leading-snug [&_p]:m-0 [&_em]:not-italic"
data-testid="text-cover-caption"
dangerouslySetInnerHTML={{ __html: sanitizeContent(coverCaption) }}
/>
)}
</figure>
)} )}
<div className="mb-5"> <div className="mb-5">
@@ -388,7 +413,7 @@ export default function ArticlePage() {
<InContentDisplayAd /> <InContentDisplayAd />
{(() => { {(() => {
const sanitized = sanitizeContent(article.content); const sanitized = sanitizeContent(articleBody || article.content);
const blocks = sanitized.split(/(?=<(?:p|h[2-4]|div)[\s>])/i).filter(Boolean); const blocks = sanitized.split(/(?=<(?:p|h[2-4]|div)[\s>])/i).filter(Boolean);
const AD_INTERVAL = isMobile ? 2 : 3; const AD_INTERVAL = isMobile ? 2 : 3;
const segments: string[] = []; const segments: string[] = [];