Oglasi: prazne enote se ne izrisejo - brez okvirja, napisa Anzeige in rezerviranega prostora, kadar oglasa ni

This commit is contained in:
FOLX Bot 2026-08-16 21:36:58 +02:00
parent 0b7cf9e5f5
commit 9d20f0da8e
3 changed files with 77 additions and 38 deletions

View File

@ -36,29 +36,31 @@ export default function AdSense({
} catch (e) {
}
if (onAdStatus) {
const checkAd = () => {
if (adRef.current) {
const ins = adRef.current;
const status = ins.getAttribute("data-ad-status");
if (status === "filled") {
onAdStatus(true);
return;
}
if (status === "unfilled") {
onAdStatus(false);
return;
}
if (ins.clientHeight > 10) {
onAdStatus(true);
return;
}
}
};
const t1 = setTimeout(checkAd, 2000);
const t2 = setTimeout(checkAd, 5000);
return () => { clearTimeout(t1); clearTimeout(t2); };
}
if (!onAdStatus) return;
const ins = adRef.current;
if (!ins) return;
// Google postavi data-ad-status na "filled", "unfilled" ali "unfill-optimized".
// Ce statusa ni, se zanesemo na dejansko visino. Odlocimo se enkrat.
let decided = false;
const decide = (filled: boolean) => {
if (decided) return;
decided = true;
obs.disconnect();
clearTimeout(timer);
onAdStatus(filled);
};
const check = () => {
const st = ins.getAttribute("data-ad-status") || "";
if (st === "filled") return decide(true);
if (st.startsWith("unfill")) return decide(false);
if (ins.clientHeight > 10) return decide(true);
};
const obs = new MutationObserver(check);
obs.observe(ins, { attributes: true, attributeFilter: ["data-ad-status", "style"] });
const timer = setTimeout(() => decide(ins.clientHeight > 10), 6000);
check();
return () => { obs.disconnect(); clearTimeout(timer); };
}, []);
return (
@ -82,6 +84,8 @@ let adSeedCounter = 1;
export function ArticleCardAd() {
const seed = useMemo(() => adSeedCounter++, []);
const [filled, setFilled] = useState<boolean | null>(null);
if (filled === false) return null;
return (
<ArtistPatternBg className="rounded-md border border-card-border h-full bg-card" seed={seed}>
<AdSense
@ -89,21 +93,33 @@ export function ArticleCardAd() {
format="fluid"
layoutKey="-6r+cy-10+8a-3"
style={{ display: "block" }}
onAdStatus={setFilled}
/>
</ArtistPatternBg>
);
}
export function TopBannerAd() {
const [filled, setFilled] = useState<boolean | null>(null);
if (filled === false) return null;
const shown = filled === true;
return (
<div className="rounded-md overflow-hidden bg-card border border-card-border mb-4 flex flex-col items-center" data-testid="ad-top-banner">
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest w-full">Anzeige</div>
<div
className={shown
? "rounded-md overflow-hidden bg-card border border-card-border mb-4 flex flex-col items-center"
: "flex flex-col items-center"}
data-testid="ad-top-banner"
>
{shown && (
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest w-full">Anzeige</div>
)}
<AdSense
slot="4154017639"
format="horizontal"
className="w-full"
style={{ display: "block", width: "100%", height: "90px", textAlign: "center", margin: "0 auto" }}
responsive={false}
onAdStatus={setFilled}
/>
</div>
);
@ -111,29 +127,41 @@ export function TopBannerAd() {
export function InArticleAd() {
// Prava AdSense In-article enota (fluid + data-ad-layout="in-article").
// Prilagodi se tipografiji clanka in ima obcutno visji CPM kot display enota,
// ki je tu stala prej.
// Kadar oglasa ni, se okvir in rezerviran prostor ne izriseta.
const [filled, setFilled] = useState<boolean | null>(null);
if (filled === false) return null;
const shown = filled === true;
return (
<div className="my-6 rounded-md overflow-hidden bg-card border border-card-border" style={{ minHeight: "260px" }}>
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
<div
className={shown ? "my-6 rounded-md overflow-hidden bg-card border border-card-border" : ""}
style={shown ? { minHeight: "260px" } : undefined}
>
{shown && (
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
)}
<AdSense
slot="7672178246"
format="fluid"
layout="in-article"
style={{ display: "block", textAlign: "center" }}
onAdStatus={setFilled}
/>
</div>
);
}
export function MultiplexAd() {
const [filled, setFilled] = useState<boolean | null>(null);
if (filled === false) return null;
const shown = filled === true;
return (
<div className="bg-card rounded-md border border-card-border overflow-hidden">
<div className={shown ? "bg-card rounded-md border border-card-border overflow-hidden" : ""}>
<AdSense
slot="4593001335"
format="autorelaxed"
style={{ display: "block" }}
className="w-full min-h-[250px]"
className={shown ? "w-full min-h-[250px]" : "w-full"}
onAdStatus={setFilled}
/>
</div>
);

View File

@ -7,6 +7,7 @@ const DELAY_MS = 3000;
export default function InterstitialAd({ forceShow }: { forceShow?: boolean } = {}) {
const [visible, setVisible] = useState(false);
const [filled, setFilled] = useState<boolean | null>(null);
useEffect(() => {
if (forceShow) {
@ -27,19 +28,21 @@ export default function InterstitialAd({ forceShow }: { forceShow?: boolean } =
}, [forceShow]);
useEffect(() => {
if (visible) {
if (visible && filled !== false) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}
return () => {
document.body.style.overflow = "";
};
}, [visible]);
}, [visible, filled]);
if (!visible) return null;
if (!visible || filled === false) return null;
return (
<div
className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/70 animate-in fade-in duration-300"
className={`fixed inset-0 z-[9999] flex items-center justify-center bg-black/70 animate-in fade-in duration-300`}
onClick={() => setVisible(false)}
data-testid="interstitial-ad-overlay"
>
@ -63,6 +66,7 @@ export default function InterstitialAd({ forceShow }: { forceShow?: boolean } =
format="rectangle"
responsive={false}
style={{ display: "inline-block", width: "300px", height: "250px" }}
onAdStatus={setFilled}
/>
</div>
</div>

View File

@ -1,19 +1,26 @@
import { useState } from "react";
import AdSense from "./adsense";
export default function ParallaxAd() {
const [filled, setFilled] = useState<boolean | null>(null);
if (filled === false) return null;
const shown = filled === true;
return (
<div className="relative" data-testid="parallax-ad-wrapper">
<div className="sticky top-0 h-[350px] flex items-center justify-center bg-background z-0">
<div className={shown ? "sticky top-0 h-[350px] flex items-center justify-center bg-background z-0" : ""}>
<div className="text-center w-full max-w-lg mx-auto">
<div className="text-[10px] text-muted-foreground/40 text-center mb-2 uppercase tracking-widest">Anzeige</div>
{shown && (
<div className="text-[10px] text-muted-foreground/40 text-center mb-2 uppercase tracking-widest">Anzeige</div>
)}
<AdSense
slot="2398082838"
format="rectangle"
style={{ display: "block", minHeight: "250px" }}
onAdStatus={setFilled}
/>
</div>
</div>
<div className="h-[350px]" aria-hidden="true" />
{shown && <div className="h-[350px]" aria-hidden="true" />}
</div>
);
}