feat: added adsense on /free page

This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-05-21 21:01:10 -07:00
parent 2e589091d8
commit 2eaf4fbce1
6 changed files with 149 additions and 1 deletions

View file

@ -0,0 +1,78 @@
"use client";
import type { CSSProperties } from "react";
import { useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
const ADSENSE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID;
declare global {
interface Window {
adsbygoogle?: Record<string, unknown>[];
}
}
interface AdUnitProps {
/** AdSense ad slot ID from your AdSense dashboard. */
slot: string;
/** AdSense ad format. Defaults to "auto" for responsive display ads. */
format?: "auto" | "fluid" | "rectangle" | "vertical" | "horizontal";
/** Optional layout (e.g. "in-article"). */
layout?: string;
/** Optional layout key (required for in-feed ads). */
layoutKey?: string;
/** Full-width responsive on mobile. Defaults to true. */
responsive?: boolean;
className?: string;
style?: CSSProperties;
}
/**
* Renders a Google AdSense ad unit. Requires <AdSenseScript /> to be mounted
* on the same page. Renders nothing if NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID
* is unset or if `slot` is empty (so missing-slot env vars stay invisible).
*/
export function AdUnit({
slot,
format = "auto",
layout,
layoutKey,
responsive = true,
className,
style,
}: AdUnitProps) {
const insRef = useRef<HTMLModElement>(null);
useEffect(() => {
if (!ADSENSE_CLIENT_ID || !slot) return;
const el = insRef.current;
if (!el) return;
// Guard against duplicate pushes (React StrictMode dev double-invoke,
// client-side navigation back to this page, or HMR remounts). AdSense
// sets data-adsbygoogle-status="done" once it has filled a slot.
if (el.getAttribute("data-adsbygoogle-status")) return;
try {
(window.adsbygoogle = window.adsbygoogle || []).push({});
} catch {
// AdSense throws if pushed before the script has loaded or on
// duplicate pushes. The script processes pending pushes when it
// finishes loading, so we can safely swallow this.
}
}, [slot]);
if (!ADSENSE_CLIENT_ID || !slot) return null;
return (
<ins
ref={insRef}
className={cn("adsbygoogle block", className)}
style={{ display: "block", ...style }}
data-ad-client={ADSENSE_CLIENT_ID}
data-ad-slot={slot}
data-ad-format={format}
data-ad-layout={layout}
data-ad-layout-key={layoutKey}
data-full-width-responsive={responsive ? "true" : "false"}
/>
);
}

View file

@ -0,0 +1,13 @@
/**
* Centralized AdSense ad slot IDs.
*
* After creating ad units in your AdSense dashboard (Ads By ad unit), paste
* the numeric slot IDs into the corresponding env vars below. Empty slot IDs
* render nothing (see <AdUnit />), so partial rollout is safe.
*/
export const ADSENSE_SLOTS = {
/** /free hub: between the model table and "Why SurfSense" section. */
freeHubInContent: process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_SLOT_FREE_HUB_IN_CONTENT ?? "",
/** /free hub: between the CTA and the FAQ section. */
freeHubBeforeFaq: process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_SLOT_FREE_HUB_BEFORE_FAQ ?? "",
} as const;

View file

@ -0,0 +1,27 @@
"use client";
import Script from "next/script";
const ADSENSE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID;
/**
* Loads the Google AdSense library (adsbygoogle.js). Mount this once on any
* route that renders <AdUnit /> instances. Scoped per-route (not in the root
* layout) so the third-party script is not shipped on unrelated pages.
*
* Renders nothing if NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID is unset, so dev and
* preview deployments without the env var stay ad-free.
*/
export function AdSenseScript() {
if (!ADSENSE_CLIENT_ID) return null;
return (
<Script
id="adsbygoogle-init"
async
strategy="afterInteractive"
src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${ADSENSE_CLIENT_ID}`}
crossOrigin="anonymous"
/>
);
}