oglasi in CMP nazaj na stanje pred 28. 8. 2026 (Discover padec)
- index.html: Googlovo CMP sporocilo spet zaduseno (controlledMessagingFunction + skrivanje fc-*), kot do 28. 8. - adsense.tsx in mobile-sticky-ad.tsx: stara verzija (stare enote 4154017639, 7672178246, 4593001335, 3854634730, 2398082838; in-article okvir se izrise sele ob polnem oglasu; sticky 320x50) - clanek: vrhnji pas nad naslovnico, brez display oglasa pred besedilom, najvec dva in-article oglasa - naslovnica: postavitev ostane, GridBannerAd na stari enoti 4154017639 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViFFkiQmVM2yhrd6pU1UBQ
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
a31797a807
commit
bc275e272f
@@ -35,6 +35,32 @@
|
||||
<link rel="apple-touch-icon" href="/favicon.png" />
|
||||
<link rel="shortcut icon" href="/favicon.png" />
|
||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4465464714854276" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
window.googlefc = window.googlefc || {};
|
||||
window.googlefc.ccpa = window.googlefc.ccpa || {};
|
||||
window.googlefc.callbackQueue = window.googlefc.callbackQueue || [];
|
||||
window.googlefc.controlledMessagingFunction = function(message) { message.proceed(false); };
|
||||
(function hideGoogleCMP() {
|
||||
var style = document.createElement('style');
|
||||
style.textContent = '.fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,div[id^="fc-"],div[class^="fc-"]{display:none!important;visibility:hidden!important}';
|
||||
document.head.appendChild(style);
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(m) {
|
||||
m.addedNodes.forEach(function(node) {
|
||||
if (node.nodeType === 1) {
|
||||
var id = node.id || '';
|
||||
var cls = node.className || '';
|
||||
if (id.indexOf('fc-') === 0 || (typeof cls === 'string' && cls.indexOf('fc-') >= 0)) {
|
||||
node.style.display = 'none';
|
||||
node.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
observer.observe(document.documentElement, { childList: true, subtree: true });
|
||||
})();
|
||||
</script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Architects+Daughter&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Fira+Code:wght@300..700&family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=Lora:ital,wght@0,400..700;1,400..700&family=Merriweather:ital,opsz,wght@0,18..144,300..900;1,18..144,300..900&family=Montserrat:ital,wght@0,100..900;1,100..900&family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Outfit:wght@100..900&family=Oxanium:wght@200..800&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto:ital,wght@0,100..900;1,100..900&family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
||||
|
||||
@@ -11,7 +11,6 @@ interface AdSenseProps {
|
||||
style?: Record<string, string>;
|
||||
layout?: string;
|
||||
layoutKey?: string;
|
||||
eager?: boolean;
|
||||
onAdStatus?: (filled: boolean) => void;
|
||||
}
|
||||
|
||||
@@ -23,11 +22,6 @@ export default function AdSense({
|
||||
style,
|
||||
layout,
|
||||
layoutKey,
|
||||
// Privzeto zahtevamo takoj. Odlozena zahteva dvigne vidnost, a znizza stevilo
|
||||
// prikazov pri enotah globlje na strani - in prav te (in-article, multiplex)
|
||||
// nosijo prihodek. Lazy pot ostaja na voljo prek eager={false}, a je nikjer
|
||||
// ne uporabljamo, dokler ucinka ne moremo izmeriti v AdSense.
|
||||
eager = true,
|
||||
onAdStatus,
|
||||
}: AdSenseProps) {
|
||||
const adRef = useRef<HTMLModElement>(null);
|
||||
@@ -35,100 +29,44 @@ export default function AdSense({
|
||||
|
||||
useEffect(() => {
|
||||
if (pushed.current) return;
|
||||
const ins = adRef.current;
|
||||
try {
|
||||
const adsbygoogle = (window as any).adsbygoogle || [];
|
||||
adsbygoogle.push({});
|
||||
pushed.current = true;
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
let io: IntersectionObserver | null = null;
|
||||
let obs: MutationObserver | null = null;
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
let decided = false;
|
||||
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.
|
||||
// AdSense odzivni enoti vnaprej doloci visino po sirini zaslona. Ce je
|
||||
// postrezena kreativa nizja, ostane pod njo prazen rezerviran pas znotraj
|
||||
// okvirja. Visino <ins> zato poravnamo na dejansko visino oglasnega okvirja.
|
||||
// Nic se ne odreze - visina je natanko taksna kot oglas.
|
||||
let fitTimers: ReturnType<typeof setTimeout>[] = [];
|
||||
const fitToCreative = () => {
|
||||
// Enota s trdno visino v style (fiksne Display enote) se ne prilagaja
|
||||
// kreativi, sicer nam Google 300x400 razpotegne na 300x600.
|
||||
if (style && typeof (style as any).height === "string") return;
|
||||
const f = ins?.querySelector("iframe") as HTMLIFrameElement | null;
|
||||
if (!ins || !f) return;
|
||||
const h = f.getBoundingClientRect().height;
|
||||
if (h > 10 && Math.abs(ins.getBoundingClientRect().height - h) > 2) {
|
||||
ins.style.height = `${Math.ceil(h)}px`;
|
||||
}
|
||||
};
|
||||
const scheduleFit = () => {
|
||||
fitTimers.forEach(clearTimeout);
|
||||
fitTimers = [0, 300, 1000, 2500].map((d) => setTimeout(fitToCreative, d));
|
||||
};
|
||||
|
||||
let decided = false;
|
||||
const decide = (filled: boolean) => {
|
||||
if (decided) return;
|
||||
decided = true;
|
||||
obs?.disconnect();
|
||||
if (timer) clearTimeout(timer);
|
||||
if (filled) scheduleFit();
|
||||
onAdStatus?.(filled);
|
||||
obs.disconnect();
|
||||
clearTimeout(timer);
|
||||
onAdStatus(filled);
|
||||
};
|
||||
// Visina samega <ins> ni dokaz - nekatere enote imajo rezerviran prostor.
|
||||
// Napolnjen oglas ima notri okvir (iframe) z dejansko visino.
|
||||
const hasFrame = () => {
|
||||
const f = ins?.querySelector("iframe");
|
||||
const f = ins.querySelector("iframe");
|
||||
return !!f && (f as HTMLIFrameElement).clientHeight > 10;
|
||||
};
|
||||
const check = () => {
|
||||
const st = ins?.getAttribute("data-ad-status") || "";
|
||||
const st = ins.getAttribute("data-ad-status") || "";
|
||||
if (st === "filled") return decide(true);
|
||||
if (st.startsWith("unfill")) return decide(false);
|
||||
if (hasFrame()) return decide(true);
|
||||
};
|
||||
|
||||
// Oglas zahtevamo sele, ko se enota priblizuje vidnemu polju (200 px prej).
|
||||
// Enote globoko pod pregibom so se prej zahtevale takoj ob nalaganju strani in
|
||||
// vecine uporabnik ni nikoli videl - nizka vidnost pomeni nizjo ceno.
|
||||
// Odlocitev o praznosti steje sele od zahteve naprej, ne od mounta.
|
||||
const push = () => {
|
||||
if (pushed.current) return;
|
||||
try {
|
||||
const adsbygoogle = (window as any).adsbygoogle || [];
|
||||
adsbygoogle.push({});
|
||||
pushed.current = true;
|
||||
} catch (e) {
|
||||
}
|
||||
if (!ins) return;
|
||||
// Opazujemo tudi pri enotah brez onAdStatus - potrebujemo jih za poravnavo
|
||||
// visine na kreativo.
|
||||
obs = new MutationObserver(() => { check(); fitToCreative(); });
|
||||
obs.observe(ins, { attributes: true, childList: true, subtree: true, attributeFilter: ["data-ad-status", "style"] });
|
||||
timer = setTimeout(() => decide(hasFrame()), 3000);
|
||||
window.addEventListener("resize", scheduleFit);
|
||||
check();
|
||||
scheduleFit();
|
||||
};
|
||||
|
||||
if (eager || !ins || typeof IntersectionObserver === "undefined") {
|
||||
push();
|
||||
} else {
|
||||
io = new IntersectionObserver((entries) => {
|
||||
if (entries.some((e) => e.isIntersecting)) {
|
||||
io?.disconnect();
|
||||
io = null;
|
||||
push();
|
||||
}
|
||||
}, { rootMargin: "200px 0px" });
|
||||
io.observe(ins);
|
||||
}
|
||||
|
||||
return () => {
|
||||
io?.disconnect();
|
||||
obs?.disconnect();
|
||||
if (timer) clearTimeout(timer);
|
||||
fitTimers.forEach(clearTimeout);
|
||||
window.removeEventListener("resize", scheduleFit);
|
||||
};
|
||||
const obs = new MutationObserver(check);
|
||||
obs.observe(ins, { attributes: true, childList: true, subtree: true, attributeFilter: ["data-ad-status", "style"] });
|
||||
const timer = setTimeout(() => decide(hasFrame()), 3000);
|
||||
check();
|
||||
return () => { obs.disconnect(); clearTimeout(timer); };
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -139,8 +77,8 @@ export default function AdSense({
|
||||
style={style || { display: "block" }}
|
||||
data-ad-client="ca-pub-4465464714854276"
|
||||
data-ad-slot={slot}
|
||||
{...(format ? { "data-ad-format": format } : {})}
|
||||
data-full-width-responsive={responsive ? "true" : "false"}
|
||||
data-ad-format={format}
|
||||
data-full-width-responsive={responsive ? "true" : undefined}
|
||||
{...(layout ? { "data-ad-layout": layout } : {})}
|
||||
{...(layoutKey ? { "data-ad-layout-key": layoutKey } : {})}
|
||||
/>
|
||||
@@ -150,54 +88,20 @@ export default function AdSense({
|
||||
|
||||
let adSeedCounter = 1;
|
||||
|
||||
// Vodoravni pas med vrsticami mreze. Oglas v celici je vrstico raztegnil na
|
||||
// svojo visino (546 px proti 399 px kartice) in pod izvlecki je ostalo prazno.
|
||||
// Pas cez vso sirino na visino kartic ne vpliva in ima boljso zapolnjenost.
|
||||
export function GridBannerAd() {
|
||||
export function ArticleCardAd() {
|
||||
const seed = useMemo(() => adSeedCounter++, []);
|
||||
const [filled, setFilled] = useState<boolean | null>(null);
|
||||
if (filled === false) return null;
|
||||
return (
|
||||
// Pas cez vso sirino je bil visok 248 do 280 px in je bralca prekinil bolj,
|
||||
// kot je prinesel. Zozimo ga na 728 px: pri tej sirini Google servira
|
||||
// leaderboard 728x90, na telefonu pa 320x100. Rezanja ne uporabljamo,
|
||||
// ker je obrezan oglas krsitev pravil.
|
||||
<div className="my-2 mx-auto w-full max-w-[728px] min-h-[110px]" data-testid="ad-grid-banner">
|
||||
<div className="text-[9px] text-muted-foreground/40 text-center pb-1 uppercase tracking-widest">
|
||||
Anzeige
|
||||
</div>
|
||||
{/* FOLX 2026 - Leaderboard */}
|
||||
<ArtistPatternBg className="rounded-md border border-card-border h-full bg-card" seed={seed}>
|
||||
<AdSense
|
||||
slot="2757497569"
|
||||
format="auto"
|
||||
responsive={true}
|
||||
slot="3854634730"
|
||||
format="fluid"
|
||||
layoutKey="-6r+cy-10+8a-3"
|
||||
style={{ display: "block" }}
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ArticleCardAd() {
|
||||
const [filled, setFilled] = useState<boolean | null>(null);
|
||||
if (filled === false) return null;
|
||||
// 31. 8. 2026: oglas mora biti enak kartici, torej cela celica in ne nizji
|
||||
// blok z luknjo pod njim. Okvir je zato h-full in prevzame visino vrstice,
|
||||
// oglas pa je v njem centriran. Enota 9916590834 je fiksna 300x400: fiksne
|
||||
// mere Googlova skripta ne prevaja, zato se ne razteguje kot responsive.
|
||||
return (
|
||||
<div
|
||||
className="h-full w-full flex items-center justify-center rounded-md border border-card-border bg-card overflow-hidden"
|
||||
data-testid="ad-card"
|
||||
>
|
||||
{/* FOLX 2026 - Grid, fiksna Display enota 300x600. Fiksna mera pomeni,
|
||||
da Googlova skripta visine ne prevaja; stara 9916590834 je bila
|
||||
Responsiv in se je razteznila na 548 px. */}
|
||||
<AdSense
|
||||
slot="4126524750"
|
||||
responsive={false}
|
||||
style={{ display: "inline-block", width: "300px", height: "250px" }}
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
</div>
|
||||
</ArtistPatternBg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -215,71 +119,36 @@ export function TopBannerAd() {
|
||||
{shown && (
|
||||
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest w-full">Anzeige</div>
|
||||
)}
|
||||
{/* format="auto" + full-width-responsive: Google dobi celo sirino in
|
||||
izbere kreativo, ki jo zapolni. Pri "horizontal" s trdo visino je
|
||||
vracal 320x50 in okrog je ostal prazen rob. */}
|
||||
{/* FOLX 2026 - Leaderboard */}
|
||||
<AdSense
|
||||
slot="2757497569"
|
||||
format="auto"
|
||||
slot="4154017639"
|
||||
format="horizontal"
|
||||
className="w-full"
|
||||
style={{ display: "block" }}
|
||||
style={{ display: "block", width: "100%", height: "90px", textAlign: "center", margin: "0 auto" }}
|
||||
responsive={false}
|
||||
eager
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Navaden odzivni display v toku besedila. Za razliko od fluid in-article enote
|
||||
// dobi Google celotno sirino in lahko postrezhe visoko kreativo (336x280, 300x600),
|
||||
// torej en cel oglas, mimo katerega bralec odskrola - namesto vec drobnih.
|
||||
export function InContentDisplayAd() {
|
||||
const [filled, setFilled] = useState<boolean | null>(null);
|
||||
if (filled === false) return null;
|
||||
const shown = filled === true;
|
||||
return (
|
||||
<div
|
||||
className={shown ? "my-6 rounded-md overflow-hidden bg-card border border-card-border" : ""}
|
||||
data-testid="ad-in-content-display"
|
||||
>
|
||||
{shown && (
|
||||
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
|
||||
)}
|
||||
{/* Sirina 728 px: pri njej Google servira leaderboard 728x90, na telefonu
|
||||
320x100. Pri polni sirini clanka je servirala velik nativni oglas
|
||||
visok 250 px in bralcu je zakrila zacetek besedila. */}
|
||||
<div className="mx-auto w-full max-w-[728px]">
|
||||
<AdSense
|
||||
slot="2757497569"
|
||||
format="auto"
|
||||
responsive={true}
|
||||
style={{ display: "block" }}
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function InArticleAd() {
|
||||
// Prava AdSense In-article enota (fluid + data-ad-layout="in-article").
|
||||
// Kadar oglasa ni, se okvir in rezerviran prostor ne izriseta.
|
||||
const [filled, setFilled] = useState<boolean | null>(null);
|
||||
// 1. 9. 2026: prej se je okvir dodal sele po nalozitvi oglasa in stran je
|
||||
// poskocila - meritev je pokazala CLS 0,10 na clanku in 0,15 na naslovnici,
|
||||
// Search Console pa 225 naslovov cez prag. Zdaj je prostor rezerviran
|
||||
// vnaprej in se po nalozitvi nic ne premakne.
|
||||
if (filled === false) return null;
|
||||
const shown = filled === true;
|
||||
return (
|
||||
<div className="my-6 rounded-md overflow-hidden bg-card border border-card-border min-h-[280px]">
|
||||
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
|
||||
{/* FOLX 2026 - In-Article */}
|
||||
<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="6313599193"
|
||||
slot="7672178246"
|
||||
format="fluid"
|
||||
layout="in-article"
|
||||
responsive={false}
|
||||
style={{ display: "block", textAlign: "center" }}
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
@@ -294,60 +163,39 @@ export function MultiplexAd() {
|
||||
return (
|
||||
<div className={shown ? "bg-card rounded-md border border-card-border overflow-hidden" : ""}>
|
||||
<AdSense
|
||||
slot="8995602853"
|
||||
slot="4593001335"
|
||||
format="autorelaxed"
|
||||
responsive={false}
|
||||
style={{ display: "block" }}
|
||||
className="w-full"
|
||||
className={shown ? "w-full min-h-[250px]" : "w-full"}
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Oba stolpca ostaneta (nic se ne odvzame), a se prilagodita sirini zaslona:
|
||||
// 160x600 od 1536 px, 300x600 od 1700 px naprej, kjer je prostora dovolj.
|
||||
// Sirino dolocz CSS, AdSense jo prebere ob zahtevi. Dodan onAdStatus, da prazna
|
||||
// enota ne pusti luknje - prej je ni imela.
|
||||
function SideRail({ side, offset }: { side: "left" | "right"; offset: number }) {
|
||||
const [filled, setFilled] = useState<boolean | null>(null);
|
||||
if (filled === false) return null;
|
||||
return (
|
||||
<div
|
||||
className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none"
|
||||
style={side === "left"
|
||||
? { left: `calc(50% - ${offset}px - var(--rail-w))` }
|
||||
: { right: `calc(50% - ${offset}px - var(--rail-w))` }}
|
||||
data-testid={`ad-${side}-sidebar`}
|
||||
>
|
||||
<div className="pointer-events-auto w-full">
|
||||
{/* Trdna mera 160x600 (skyscraper). Prej je bil pas na sirokih zaslonih
|
||||
300 px in z responsive vertical si je Google visino racunal sam,
|
||||
zato se je raztegoval cez pol zaslona. */}
|
||||
{/* FOLX 2026 - Skyscraper, fiksna Display enota 160x600. */}
|
||||
<AdSense
|
||||
slot="4349780836"
|
||||
format=""
|
||||
responsive={false}
|
||||
style={{ display: "inline-block", width: "160px", height: "600px" }}
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function PageSideAds({ contentHalfWidth = 640 }: { contentHalfWidth?: number }) {
|
||||
// 31. 8. 2026: pasova sta bila prilepljena na rob zaslona, ker je bila
|
||||
// spremenljivka --rail-w nad 1700 px se vedno 300, enota pa je zdaj fiksna
|
||||
// 160 px - odmik se je racunal s 140 px prevelike sirine. Zdaj je povsod
|
||||
// 160, razmik do vsebine pa majhen, da sta pasova tik ob clanku.
|
||||
const gap = 8;
|
||||
const gap = 10;
|
||||
const offset = contentHalfWidth + gap;
|
||||
return (
|
||||
<div className="relative page-side-rails">
|
||||
<SideRail side="left" offset={offset} />
|
||||
<SideRail side="right" offset={offset} />
|
||||
<div className="relative">
|
||||
<div className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none" style={{ left: `calc(50% - ${offset}px - 160px)` }} data-testid="ad-left-sidebar">
|
||||
<div className="pointer-events-auto">
|
||||
<AdSense
|
||||
slot="2398082838"
|
||||
format="vertical"
|
||||
style={{ display: "block", width: "160px", height: "600px" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none" style={{ right: `calc(50% - ${offset}px - 160px)` }} data-testid="ad-right-sidebar">
|
||||
<div className="pointer-events-auto">
|
||||
<AdSense
|
||||
slot="2398082838"
|
||||
format="vertical"
|
||||
style={{ display: "block", width: "160px", height: "600px" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -358,14 +206,41 @@ export function SidebarAd() {
|
||||
return (
|
||||
<div className={`sticky top-4 ${visible ? "" : "hidden"}`}>
|
||||
<div className="bg-card rounded-md border border-card-border p-4 mt-4">
|
||||
{/* FOLX 2026 - Sidebar, fiksna Display enota 300x600. */}
|
||||
<AdSense
|
||||
slot="9953566507"
|
||||
responsive={false}
|
||||
style={{ display: "inline-block", width: "300px", height: "600px" }}
|
||||
slot="2398082838"
|
||||
format="auto"
|
||||
onAdStatus={(filled) => setVisible(filled)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Vodoravni pas med vrsticami mreze na naslovnici. 4. 9. 2026: nazaj na staro
|
||||
// enoto 4154017639 (horizontal), enako kot vrhnji pas pred 28. 8.
|
||||
export function GridBannerAd() {
|
||||
const [filled, setFilled] = useState<boolean | null>(null);
|
||||
if (filled === false) return null;
|
||||
const shown = filled === true;
|
||||
return (
|
||||
<div className="my-2 mx-auto w-full max-w-[728px]" data-testid="ad-grid-banner">
|
||||
<div
|
||||
className={shown
|
||||
? "rounded-md overflow-hidden bg-card border border-card-border flex flex-col items-center"
|
||||
: "flex flex-col items-center"}
|
||||
>
|
||||
{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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ const DISMISS_KEY = "folx-sticky-ad-dismissed";
|
||||
export default function MobileStickyAd() {
|
||||
const isMobile = useIsMobile();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [filled, setFilled] = useState<boolean | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMobile) return;
|
||||
@@ -24,37 +23,26 @@ export default function MobileStickyAd() {
|
||||
sessionStorage.setItem(DISMISS_KEY, "1");
|
||||
};
|
||||
|
||||
if (!isMobile || !visible || filled === false) return null;
|
||||
const shown = filled === true;
|
||||
if (!isMobile || !visible) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-0 left-0 right-0 z-[100] animate-in slide-in-from-bottom duration-300" data-testid="mobile-sticky-ad">
|
||||
<div className={shown ? "relative bg-card border-t border-card-border shadow-2xl" : "relative"}>
|
||||
{shown && (
|
||||
<button
|
||||
onClick={close}
|
||||
className="absolute -top-8 right-2 w-7 h-7 flex items-center justify-center rounded-full bg-card border border-card-border shadow-lg text-muted-foreground hover:text-foreground transition-colors"
|
||||
data-testid="button-sticky-ad-close"
|
||||
aria-label="Schließen"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
{shown && (
|
||||
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
|
||||
)}
|
||||
{/* Vodoravni format brez full-width-responsive: enota se drzi sirine
|
||||
svojega vsebnika in da 320x50 ali 320x100. Z vklopljenim
|
||||
full-width-responsive je Google vracal 390x390 kvadrat. */}
|
||||
<div className="w-full pb-1">
|
||||
<div className="relative bg-card border-t border-card-border shadow-2xl">
|
||||
<button
|
||||
onClick={close}
|
||||
className="absolute -top-8 right-2 w-7 h-7 flex items-center justify-center rounded-full bg-card border border-card-border shadow-lg text-muted-foreground hover:text-foreground transition-colors"
|
||||
data-testid="button-sticky-ad-close"
|
||||
aria-label="Schließen"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
|
||||
<div className="flex justify-center pb-1">
|
||||
<AdSense
|
||||
slot="4154017639"
|
||||
format="horizontal"
|
||||
className="w-full"
|
||||
style={{ display: "block" }}
|
||||
style={{ display: "inline-block", width: "320px", height: "50px" }}
|
||||
responsive={false}
|
||||
eager
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import Header from "@/components/header";
|
||||
import Footer from "@/components/footer";
|
||||
import { InArticleAd, InContentDisplayAd, PageSideAds, MultiplexAd } from "@/components/adsense";
|
||||
import { InArticleAd, PageSideAds, MultiplexAd, TopBannerAd } from "@/components/adsense";
|
||||
import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import DOMPurify from "dompurify";
|
||||
import ShareButtons from "@/components/share-buttons";
|
||||
@@ -344,8 +344,7 @@ export default function ArticlePage() {
|
||||
<Header />
|
||||
<PageSideAds contentHalfWidth={480} />
|
||||
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
{/* Zgornjega pasu na clanku namenoma ni: potiskal je naslovnico in naslov
|
||||
pod pregib, placan pa je najslabse. Prvi oglas pride sele v besedilu. */}
|
||||
<TopBannerAd />
|
||||
<Link href="/">
|
||||
<Button variant="ghost" size="sm" className="mb-3 gap-2" data-testid="button-back">
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
@@ -407,11 +406,7 @@ export default function ArticlePage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enota "Folx News - In-Article" (4154017639) je bila prej vrhnji pas nad
|
||||
naslovnico. Ostane na clanku, a pod naslovom in tik pred besedilom:
|
||||
isti prikazi, boljse mesto, naslov ne pade pod pregib. */}
|
||||
<InContentDisplayAd />
|
||||
|
||||
|
||||
{(() => {
|
||||
const sanitized = sanitizeContent(articleBody || article.content);
|
||||
const blocks = sanitized.split(/(?=<(?:p|h[2-4]|div)[\s>])/i).filter(Boolean);
|
||||
@@ -435,13 +430,8 @@ export default function ArticlePage() {
|
||||
// Vsi so fluid in-article enota 7672178246 (najvecji zasluzkar).
|
||||
|
||||
const last = segments.length - 1;
|
||||
const extra = Math.min(3, Math.max(0, Math.floor((blocks.length - 10) / 5)));
|
||||
const adCount = last <= 0 ? 0 : (last === 1 ? 1 : Math.min(2 + extra, last));
|
||||
const AD_AFTER: number[] = [];
|
||||
for (let i = 0; i < adCount; i++) {
|
||||
const pos = i === 0 ? 0 : Math.round((i * last) / adCount);
|
||||
if (pos < last && !AD_AFTER.includes(pos)) AD_AFTER.push(pos);
|
||||
}
|
||||
// Najvec dva oglasa v besedilu: po prvem odstavku in na sredini (stanje pred 28. 8. 2026).
|
||||
const AD_AFTER = last <= 0 ? [] : (last === 1 ? [0] : [0, Math.floor(last / 2)]);
|
||||
return (
|
||||
<div ref={(el) => {
|
||||
if (!el) return;
|
||||
@@ -462,9 +452,6 @@ export default function ArticlePage() {
|
||||
dangerouslySetInnerHTML={{ __html: html }}
|
||||
data-testid={idx === 0 ? "article-content" : `article-content-${idx}`}
|
||||
/>
|
||||
{/* VSA mesta v besedilu so 7672178246 - to je enota z najvecjim
|
||||
prihodkom (98,93 EUR v 7 dneh). Prej sta bili tam dve, zdaj
|
||||
dve do pet. Enota 4154017639 ima svoje mesto pred besedilom. */}
|
||||
{AD_AFTER.includes(idx) && <InArticleAd />}
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user