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:
parent
5fc28baa45
commit
0f1a0222a0
@ -9,6 +9,7 @@
|
|||||||
<meta property="og:description" content="Aktuelle Nachrichten aus der Welt der Volksmusik und des Schlagers." />
|
<meta property="og:description" content="Aktuelle Nachrichten aus der Welt der Volksmusik und des Schlagers." />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<link rel="icon" type="image/png" href="/favicon.png" />
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||||
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4465464714854276" crossorigin="anonymous"></script>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<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">
|
<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">
|
||||||
|
|||||||
89
client/src/components/adsense.tsx
Normal file
89
client/src/components/adsense.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import Header from "@/components/header";
|
import Header from "@/components/header";
|
||||||
import Footer from "@/components/footer";
|
import Footer from "@/components/footer";
|
||||||
|
import { InArticleAd } from "@/components/adsense";
|
||||||
import DOMPurify from "dompurify";
|
import DOMPurify from "dompurify";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
@ -214,6 +215,8 @@ export default function ArticlePage() {
|
|||||||
data-testid="article-content"
|
data-testid="article-content"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<InArticleAd />
|
||||||
|
|
||||||
<RelatedArticles currentSlug={slug || ""} />
|
<RelatedArticles currentSlug={slug || ""} />
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { Skeleton } from "@/components/ui/skeleton";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import Header from "@/components/header";
|
import Header from "@/components/header";
|
||||||
import Footer from "@/components/footer";
|
import Footer from "@/components/footer";
|
||||||
|
import { ArticleCardAd, SidebarAd, InArticleAd } from "@/components/adsense";
|
||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
|
|
||||||
function FeaturedSection({ articles }: { articles: Article[] }) {
|
function FeaturedSection({ articles }: { articles: Article[] }) {
|
||||||
@ -251,9 +252,18 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
{(articles || []).map((article) => (
|
{(articles || []).flatMap((article, index) => {
|
||||||
<ArticleCard key={article.id} article={article} />
|
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>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -262,6 +272,7 @@ export default function Home() {
|
|||||||
{popular && popular.length > 0 && (
|
{popular && popular.length > 0 && (
|
||||||
<PopularSidebar articles={popular} />
|
<PopularSidebar articles={popular} />
|
||||||
)}
|
)}
|
||||||
|
<SidebarAd />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { Play, ArrowLeft } from "lucide-react";
|
|||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import Header from "@/components/header";
|
import Header from "@/components/header";
|
||||||
import Footer from "@/components/footer";
|
import Footer from "@/components/footer";
|
||||||
|
import { ArticleCardAd } from "@/components/adsense";
|
||||||
|
|
||||||
function VideoCard({ article }: { article: Article }) {
|
function VideoCard({ article }: { article: Article }) {
|
||||||
const thumbSrc = article.coverImage
|
const thumbSrc = article.coverImage
|
||||||
@ -83,9 +84,15 @@ export default function VideosPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : articles && articles.length > 0 ? (
|
) : articles && articles.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-5">
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-5">
|
||||||
{articles.map((article) => (
|
{articles.flatMap((article, index) => {
|
||||||
<VideoCard key={article.id} article={article} />
|
const items = [
|
||||||
))}
|
<VideoCard key={article.id} article={article} />,
|
||||||
|
];
|
||||||
|
if (index === 2) {
|
||||||
|
items.push(<ArticleCardAd key="ad-video-1" />);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center py-16">
|
<div className="text-center py-16">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user