Add various ad placements throughout the website to monetize content

Integrates AdSense for ads on the homepage, article pages, and video pages, including in-feed ads styled as article cards.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 413891e8-d784-4bea-b9f5-91a5a68316b4
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: b587b746-f67b-4539-9958-86999aee56de
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/413891e8-d784-4bea-b9f5-91a5a68316b4/igVW4lQ
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
sebastjanartic
2026-02-28 18:01:39 +00:00
parent 5fc28baa45
commit 0f1a0222a0
5 changed files with 117 additions and 6 deletions
+89
View File
@@ -0,0 +1,89 @@
import { useEffect, useRef } from "react";
type AdFormat = "auto" | "fluid" | "rectangle" | "horizontal" | "vertical";
interface AdSenseProps {
slot: string;
format?: AdFormat;
responsive?: boolean;
className?: string;
style?: Record<string, string>;
layout?: string;
layoutKey?: string;
}
export default function AdSense({
slot,
format = "auto",
responsive = true,
className = "",
style,
layout,
layoutKey,
}: AdSenseProps) {
const adRef = useRef<HTMLModElement>(null);
const pushed = useRef(false);
useEffect(() => {
if (pushed.current) return;
try {
const adsbygoogle = (window as any).adsbygoogle || [];
adsbygoogle.push({});
pushed.current = true;
} catch (e) {
}
}, []);
return (
<div className={`ad-container ${className}`} data-testid={`ad-slot-${slot}`}>
<ins
ref={adRef}
className="adsbygoogle"
style={style || { display: "block" }}
data-ad-client="ca-pub-4465464714854276"
data-ad-slot={slot}
data-ad-format={format}
data-full-width-responsive={responsive ? "true" : undefined}
{...(layout ? { "data-ad-layout": layout } : {})}
{...(layoutKey ? { "data-ad-layout-key": layoutKey } : {})}
/>
</div>
);
}
export function ArticleCardAd() {
return (
<div className="bg-card rounded-md border border-card-border h-full flex flex-col overflow-hidden">
<AdSense
slot="auto"
format="fluid"
layoutKey="-6t+ed+2i-1n-4w"
style={{ display: "block" }}
className="flex-1 min-h-[280px]"
/>
</div>
);
}
export function InArticleAd() {
return (
<AdSense
slot="auto"
format="fluid"
layout="in-article"
style={{ display: "block", textAlign: "center" }}
className="my-8"
/>
);
}
export function SidebarAd() {
return (
<div className="bg-card rounded-md border border-card-border p-4 mt-6">
<AdSense
slot="auto"
format="auto"
/>
</div>
);
}
+3
View File
@@ -8,6 +8,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 } from "@/components/adsense";
import DOMPurify from "dompurify";
import { useEffect } from "react";
@@ -214,6 +215,8 @@ export default function ArticlePage() {
data-testid="article-content"
/>
<InArticleAd />
<RelatedArticles currentSlug={slug || ""} />
</main>
<Footer />
+14 -3
View File
@@ -8,6 +8,7 @@ import { Skeleton } from "@/components/ui/skeleton";
import { Button } from "@/components/ui/button";
import Header from "@/components/header";
import Footer from "@/components/footer";
import { ArticleCardAd, SidebarAd, InArticleAd } from "@/components/adsense";
import { useState, useEffect, useCallback } from "react";
function FeaturedSection({ articles }: { articles: Article[] }) {
@@ -251,9 +252,18 @@ export default function Home() {
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{(articles || []).map((article) => (
<ArticleCard key={article.id} article={article} />
))}
{(articles || []).flatMap((article, index) => {
const items = [
<ArticleCard key={article.id} article={article} />,
];
if (index === 1) {
items.push(<ArticleCardAd key="ad-feed-1" />);
}
if (index === 4) {
items.push(<ArticleCardAd key="ad-feed-2" />);
}
return items;
})}
</div>
)}
</div>
@@ -262,6 +272,7 @@ export default function Home() {
{popular && popular.length > 0 && (
<PopularSidebar articles={popular} />
)}
<SidebarAd />
</div>
</div>
</main>
+10 -3
View File
@@ -7,6 +7,7 @@ import { Play, ArrowLeft } from "lucide-react";
import { Skeleton } from "@/components/ui/skeleton";
import Header from "@/components/header";
import Footer from "@/components/footer";
import { ArticleCardAd } from "@/components/adsense";
function VideoCard({ article }: { article: Article }) {
const thumbSrc = article.coverImage
@@ -83,9 +84,15 @@ export default function VideosPage() {
</div>
) : articles && articles.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-5">
{articles.map((article) => (
<VideoCard key={article.id} article={article} />
))}
{articles.flatMap((article, index) => {
const items = [
<VideoCard key={article.id} article={article} />,
];
if (index === 2) {
items.push(<ArticleCardAd key="ad-video-1" />);
}
return items;
})}
</div>
) : (
<div className="text-center py-16">