Remove Ezoic ad integration

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Fatih Kadir Akın
2026-04-23 00:22:56 +03:00
parent a4632f1a55
commit dbd2eed8bc
13 changed files with 1 additions and 333 deletions
+1 -3
View File
@@ -65,6 +65,4 @@ CRON_SECRET="your-secret-key-here"
# SENTRY_AUTH_TOKEN=sentry-auth-token
# GOOGLE_ADSENSE_ACCOUNT=ca-pub-xxxxxxxxxxxxxxxx
# NEXT_PUBLIC_EZOIC_ENABLED=true
# EZOIC_SITE_DOMAIN=prompts.chat
# GOOGLE_ADSENSE_ACCOUNT=ca-pub-xxxxxxxxxxxxxxxx
-17
View File
@@ -1,17 +0,0 @@
import { NextResponse } from "next/server";
export async function GET() {
const domain = process.env.EZOIC_SITE_DOMAIN;
if (!domain) {
return new NextResponse("# ads.txt not configured\n", {
status: 200,
headers: { "Content-Type": "text/plain" },
});
}
return NextResponse.redirect(
`https://srv.adstxtmanager.com/19390/${domain}`,
301
);
}
-70
View File
@@ -802,73 +802,3 @@
padding: 0;
}
/* Ezoic privacy policy container styling */
#ezoic-privacy-policy-container {
margin-top: 1rem;
color: var(--muted-foreground);
}
#ezoic-privacy-policy-container h2,
#ezoic-privacy-policy-container h3,
#ezoic-privacy-policy-container h4 {
font-size: 1rem;
font-weight: 600;
margin-top: 1rem;
margin-bottom: 0.5rem;
color: var(--foreground);
}
#ezoic-privacy-policy-container p {
margin-top: 0.75rem;
margin-bottom: 0.75rem;
line-height: 1.7;
color: var(--muted-foreground);
}
#ezoic-privacy-policy-container a {
color: var(--primary);
text-decoration: underline;
text-underline-offset: 2px;
}
#ezoic-privacy-policy-container a:hover {
opacity: 0.8;
}
#ezoic-privacy-policy-container ul,
#ezoic-privacy-policy-container ol {
margin-top: 0.75rem;
margin-bottom: 0.75rem;
padding-left: 1.5rem;
color: var(--muted-foreground);
}
#ezoic-privacy-policy-container li {
margin-top: 0.25rem;
margin-bottom: 0.25rem;
}
#ezoic-privacy-policy-container table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
font-size: 0.875rem;
}
#ezoic-privacy-policy-container th,
#ezoic-privacy-policy-container td {
border: 1px solid var(--border);
padding: 0.5rem 0.75rem;
text-align: left;
}
#ezoic-privacy-policy-container th {
background: var(--muted);
font-weight: 600;
}
/* Ezoic ad content: center and constrain injected ad creatives */
.ezoic-ad-content * {
margin-left: auto !important;
margin-right: auto !important;
}
-3
View File
@@ -7,7 +7,6 @@ import { Header } from "@/components/layout/header";
import { Footer } from "@/components/layout/footer";
import { CookieConsentBanner } from "@/components/layout/cookie-consent";
import { Analytics } from "@/components/layout/analytics";
import { EzoicScripts, EzoicRouteHandler } from "@/components/layout/ezoic-ads";
import { WebsiteStructuredData } from "@/components/seo/structured-data";
import { AppBanner } from "@/components/layout/app-banner";
import { LocaleDetector } from "@/components/providers/locale-detector";
@@ -185,13 +184,11 @@ export default async function RootLayout({
<meta name="google-adsense-account" content={process.env.GOOGLE_ADSENSE_ACCOUNT} />
)}
<WebsiteStructuredData />
{process.env.NEXT_PUBLIC_EZOIC_ENABLED === "true" && <EzoicScripts />}
</head>
<body className={`${fontClasses} antialiased`}>
{process.env.GOOGLE_ANALYTICS_ID && (
<Analytics gaId={process.env.GOOGLE_ANALYTICS_ID} />
)}
{process.env.NEXT_PUBLIC_EZOIC_ENABLED === "true" && <EzoicRouteHandler />}
<Providers locale={locale} messages={messages} theme={config.theme} branding={{ ...config.branding, useCloneBranding: config.homepage?.useCloneBranding }}>
{isEmbedRoute || isKidsRoute ? (
children
-21
View File
@@ -97,27 +97,6 @@ export default function PrivacyPage() {
</p>
</section>
{process.env.NEXT_PUBLIC_EZOIC_ENABLED === "true" && (
<section className="space-y-3">
<h2 className="text-lg font-semibold">Advertising & Ezoic</h2>
<p className="text-muted-foreground">
We use Ezoic to provide personalized ads and analytics on this site. Ezoic and its partners
may collect and use data about your visit to this and other websites to provide relevant
advertisements. For full details on how Ezoic and its partners use your information, including
a list of known cookies used on this site, please see the{" "}
<a
href="http://g.ezoic.net/privacy/prompts.chat"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
Ezoic privacy disclosures
</a>.
</p>
<span id="ezoic-privacy-policy-embed"></span>
</section>
)}
<section className="space-y-3">
<h2 className="text-lg font-semibold">Your Rights & CC0 License</h2>
<p className="text-muted-foreground">
-4
View File
@@ -33,7 +33,6 @@ import { AddToCollectionButton } from "@/components/prompts/add-to-collection-bu
import { getConfig } from "@/lib/config";
import { StructuredData } from "@/components/seo/structured-data";
import { AI_MODELS } from "@/lib/works-best-with";
import { EzoicAd } from "@/components/ads/ezoic-ad";
interface PromptPageProps {
params: Promise<{ id: string }>;
@@ -710,9 +709,6 @@ export default async function PromptPage({ params }: PromptPageProps) {
locale={locale}
/>
)}
{/* Ad Placement */}
{process.env.NEXT_PUBLIC_EZOIC_ENABLED === "true" && <EzoicAd id={201} />}
</TabsContent>
<TabsContent value="versions" className="mt-0">
-18
View File
@@ -1,18 +0,0 @@
"use client";
import { EzoicPlaceholder } from "./ezoic-placeholder";
interface EzoicAdProps {
id: number;
}
/**
* Client-only wrapper for EzoicPlaceholder.
* Renders the placeholder div after mount so it's in the DOM
* when Ezoic's showAds() runs.
*
* @see https://docs.ezoic.com/docs/ezoicadsadvanced/nextjs/
*/
export function EzoicAd({ id }: EzoicAdProps) {
return <EzoicPlaceholder id={id} />;
}
-43
View File
@@ -1,43 +0,0 @@
"use client";
import { useEffect } from "react";
import { useTranslations } from "next-intl";
import { runEzoic } from "@/lib/ezoic";
interface EzoicPlaceholderProps {
id: number;
}
/**
* Manages the lifecycle of a single Ezoic ad slot.
* The placeholder div is always in the DOM so Ezoic can find it
* when showAds() runs. Destroys the placeholder on unmount.
*
* @see https://docs.ezoic.com/docs/ezoicadsadvanced/nextjs/
*/
export function EzoicPlaceholder({ id }: EzoicPlaceholderProps) {
const t = useTranslations("common");
useEffect(() => {
runEzoic(() => {
window.ezstandalone?.showAds(id);
});
return () => {
runEzoic(() => {
window.ezstandalone?.destroyPlaceholders(id);
});
};
}, [id]);
return (
<div className="ezoic-ad-container my-2 overflow-hidden">
<div className="flex items-center justify-center">
<span className="text-[10px] font-medium uppercase tracking-widest text-muted-foreground/50 select-none">
{t("ad")}
</span>
</div>
<div id={`ezoic-pub-ad-placeholder-${id}`} className="overflow-hidden" />
</div>
);
}
-88
View File
@@ -1,88 +0,0 @@
"use client";
import { useEffect } from "react";
import Script from "next/script";
import { usePathname } from "next/navigation";
import { runEzoic } from "@/lib/ezoic";
declare global {
interface Window {
ezstandalone?: {
cmd: Array<() => void>;
showAds: (...ids: number[]) => void;
destroyAll: () => void;
destroyPlaceholders: (...ids: number[]) => void;
[key: string]: unknown;
};
}
}
/**
* Ezoic script loader — renders all required <Script> tags.
* Place inside <head> in layout.tsx per official docs.
*
* @see https://docs.ezoic.com/docs/ezoicadsadvanced/nextjs/
*/
export function EzoicScripts() {
return (
<>
{/* Ezoic Privacy / Consent Management */}
<Script
id="ezoic-cmp"
src="https://cmp.gatekeeperconsent.com/min.js"
strategy="beforeInteractive"
data-cfasync="false"
/>
<Script
id="ezoic-cmp-2"
src="https://the.gatekeeperconsent.com/cmp.min.js"
strategy="beforeInteractive"
data-cfasync="false"
/>
{/* Ezoic standalone script */}
<Script
id="ezoic-sa"
src="//www.ezojs.com/ezoic/sa.min.js"
strategy="afterInteractive"
/>
{/* Ezoic init — cmd queue must exist before any runEzoic() calls */}
<Script id="ezoic-init" strategy="afterInteractive">
{`
window.ezstandalone = window.ezstandalone || {};
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
`}
</Script>
{/* Ezoic Analytics */}
<Script
id="ezoic-analytics"
src="//ezoicanalytics.com/analytics.js"
strategy="afterInteractive"
/>
</>
);
}
/**
* Handles SPA route changes for Ezoic.
* Destroys stale placeholders and re-scans the DOM on each navigation.
*
* @see https://docs.ezoic.com/docs/ezoicadsadvanced/nextjs/
*/
export function EzoicRouteHandler() {
const pathname = usePathname();
useEffect(() => {
runEzoic(() => {
window.ezstandalone?.destroyPlaceholders();
requestAnimationFrame(() => {
window.ezstandalone?.showAds();
});
});
}, [pathname]);
return null;
}
@@ -5,7 +5,6 @@ import { db } from "@/lib/db";
import { Button } from "@/components/ui/button";
import { Masonry } from "@/components/ui/masonry";
import { PromptCard } from "@/components/prompts/prompt-card";
import { EzoicAd } from "@/components/ads/ezoic-ad";
interface DiscoveryPromptsProps {
isHomepage?: boolean;
@@ -158,15 +157,6 @@ export async function DiscoveryPrompts({ isHomepage = false }: DiscoveryPromptsP
</section>
)}
{/* Ad Placement - after featured */}
{isHomepage && process.env.NEXT_PUBLIC_EZOIC_ENABLED === "true" && (
<section className="py-8 border-b">
<div className="container max-w-2xl">
<EzoicAd id={202} />
</div>
</section>
)}
{/* Today's Most Upvoted Section */}
{todaysMostUpvoted.length > 0 && (
<section className={isHomepage ? "py-12 border-b" : "pb-8 mb-8 border-b"}>
@@ -217,15 +207,6 @@ export async function DiscoveryPrompts({ isHomepage = false }: DiscoveryPromptsP
</section>
)}
{/* Ad Placement - after latest */}
{isHomepage && process.env.NEXT_PUBLIC_EZOIC_ENABLED === "true" && (
<section className="py-8 border-b">
<div className="container max-w-2xl">
<EzoicAd id={203} />
</div>
</section>
)}
{/* Recently Updated Section */}
{recentlyUpdated.length > 0 && (
<section className={isHomepage ? "py-12 border-b" : "pb-8 mb-8 border-b"}>
-13
View File
@@ -1,13 +0,0 @@
/**
* Safely push a function to the Ezoic command queue.
* Initializes window.ezstandalone and cmd array if they don't exist yet,
* preventing errors when components mount before the Ezoic script loads.
*
* @see https://docs.ezoic.com/docs/ezoicadsadvanced/nextjs/
*/
export function runEzoic(fn: () => void) {
if (typeof window === "undefined") return;
const ez = (window.ezstandalone ??= {} as NonNullable<typeof window.ezstandalone>);
ez.cmd = ez.cmd || [];
ez.cmd.push(fn);
}
-32
View File
@@ -1,32 +0,0 @@
import { EzoicPlaceholder } from "@/components/ads/ezoic-placeholder";
import type { WidgetPlugin } from "./types";
// Placeholder IDs must match those created in the Ezoic dashboard.
// Each repeated instance uses a unique ID (101, 102, 103, ...) to avoid
// "unpredictable ad behaviour" per Ezoic infinite scroll docs.
const EZOIC_FEED_BASE_ID = 101;
export const ezoicWidget: WidgetPlugin = {
id: "ezoic",
name: "Ezoic Ads",
prompts: [
{
id: "ezoic-feed-ad",
slug: "ezoic-feed-ad",
title: "",
description: "",
content: "",
type: "TEXT",
positioning: {
position: 4,
mode: "repeat",
repeatEvery: 12,
maxCount: 20,
},
shouldInject: () => process.env.NEXT_PUBLIC_EZOIC_ENABLED === "true",
render: (instanceIndex: number) => (
<EzoicPlaceholder id={EZOIC_FEED_BASE_ID + instanceIndex} />
),
},
],
};
-2
View File
@@ -3,7 +3,6 @@ import { coderabbitWidget } from "./coderabbit";
import { bookWidget } from "./book";
import { textreamWidget } from "./textream";
import { commandcodeWidget } from "./commandcode";
import { ezoicWidget } from "./ezoic";
export * from "./types";
@@ -13,7 +12,6 @@ const widgetPlugins: WidgetPlugin[] = [
bookWidget,
textreamWidget,
commandcodeWidget,
ezoicWidget,
];
/**