fix(ads): Fix logic for checking ad content presence in Ezoic components

This commit is contained in:
Fatih Kadir Akın
2026-03-18 18:19:42 +03:00
parent 0b207d2157
commit f6dbe4c44c
2 changed files with 44 additions and 4 deletions
+35 -2
View File
@@ -1,6 +1,6 @@
"use client";
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { useTranslations } from "next-intl";
import { runEzoic } from "@/lib/ezoic";
@@ -11,6 +11,8 @@ interface EzoicPlaceholderProps {
export function EzoicPlaceholder({ id }: EzoicPlaceholderProps) {
const t = useTranslations("common");
const [isRendered, setIsRendered] = useState(false);
const [hasAdContent, setHasAdContent] = useState(true);
const placeholderRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// Render the placeholder div first, then tell Ezoic to fill it.
@@ -25,14 +27,45 @@ export function EzoicPlaceholder({ id }: EzoicPlaceholderProps) {
});
});
// Observe the placeholder for ad content injection by Ezoic.
// If nothing is injected after a timeout, hide the container
// to avoid showing an empty white card.
const el = document.getElementById(`ezoic-pub-ad-placeholder-${id}`);
let observer: MutationObserver | undefined;
let timeoutId: ReturnType<typeof setTimeout> | undefined;
if (el) {
let filled = false;
observer = new MutationObserver(() => {
if (el.childNodes.length > 0 || el.innerHTML.trim().length > 0) {
filled = true;
setHasAdContent(true);
}
});
observer.observe(el, { childList: true, subtree: true });
// If Ezoic doesn't fill the placeholder within 5s, hide it
timeoutId = setTimeout(() => {
if (!filled && el.childNodes.length === 0) {
setHasAdContent(false);
}
}, 5000);
}
return () => {
cancelAnimationFrame(rafId);
observer?.disconnect();
if (timeoutId) clearTimeout(timeoutId);
runEzoic(() => {
window.ezstandalone?.destroyPlaceholders(id);
});
};
}, [id]);
if (!hasAdContent) return null;
return (
<div className="ezoic-ad-container group border rounded-[var(--radius)] overflow-hidden hover:border-foreground/20 transition-colors flex flex-col">
<div className="px-3 py-1.5 border-b bg-muted/50">
@@ -41,7 +74,7 @@ export function EzoicPlaceholder({ id }: EzoicPlaceholderProps) {
</span>
</div>
<div className="ezoic-ad-content overflow-hidden flex items-center justify-center">
{isRendered && <div id={`ezoic-pub-ad-placeholder-${id}`} />}
{isRendered && <div id={`ezoic-pub-ad-placeholder-${id}`} ref={placeholderRef} />}
</div>
</div>
);
+9 -2
View File
@@ -1,6 +1,6 @@
"use client";
import { useEffect } from "react";
import { useEffect, useRef } from "react";
import Script from "next/script";
import { usePathname } from "next/navigation";
import { runEzoic } from "@/lib/ezoic";
@@ -18,10 +18,17 @@ declare global {
export function EzoicAds() {
const pathname = usePathname();
const isFirstRender = useRef(true);
// Re-trigger ads on SPA route changes
// Per Ezoic docs: destroyPlaceholders first, then showAds in next frame
// Per Ezoic docs: destroyPlaceholders first, then showAds in next frame.
// Skip the first render to avoid racing with individual EzoicPlaceholder
// components that call showAds(id) on mount.
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
runEzoic(() => {
window.ezstandalone?.destroyPlaceholders();
requestAnimationFrame(() => {