Keep AdSense rendering limited to anonymous visitors.

Show guest-only ad units on public pages and skip the AdSense script entirely for signed-in users.
This commit is contained in:
2026-09-20 14:49:34 +02:00
parent 822b96e92a
commit 04a60a981e
8 changed files with 135 additions and 8 deletions
+5
View File
@@ -9,6 +9,7 @@ import ArtworkTags from '../components/artwork/ArtworkTags'
import ArtworkDescription from '../components/artwork/ArtworkDescription'
import ArtworkEvolutionPanel from '../components/artwork/ArtworkEvolutionPanel'
import ArtworkComments from '../components/artwork/ArtworkComments'
import AdSenseUnit from '../components/ads/AdSenseUnit'
import ArtworkActionBar from '../components/artwork/ArtworkActionBar'
import ArtworkDetailsPanel from '../components/artwork/ArtworkDetailsPanel'
import CreatorSpotlight from '../components/artwork/CreatorSpotlight'
@@ -319,6 +320,10 @@ function publisherToGroupSummary(publisher) {
{/* Tags & categories */}
<ArtworkTags artwork={artwork} />
{!isAuthenticated ? (
<AdSenseUnit key={artwork.id} slot="4398418697" />
) : null}
{/* Comments */}
<ArtworkComments
artworkId={artwork.id}
@@ -0,0 +1,45 @@
import React, { useEffect, useRef } from 'react'
export default function AdSenseUnit({
slot,
client = 'ca-pub-6457864535683080',
format = 'auto',
className = '',
enabled = true,
}) {
const pushed = useRef(false)
useEffect(() => {
if (!enabled || !slot || pushed.current) {
return undefined
}
pushed.current = true
try {
window.adsbygoogle = window.adsbygoogle || []
window.adsbygoogle.push({})
} catch {
// AdSense may be blocked or not yet loaded (consent not granted).
}
return undefined
}, [enabled, slot])
if (!enabled || !slot) {
return null
}
return (
<div className={['flex w-full justify-center', className].filter(Boolean).join(' ')}>
<ins
className="adsbygoogle w-full max-w-3xl"
style={{ display: 'block', textAlign: 'center' }}
data-ad-client={client}
data-ad-slot={slot}
data-ad-format={format}
data-full-width-responsive="true"
/>
</div>
)
}
@@ -0,0 +1,43 @@
import React from 'react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, render } from '@testing-library/react'
import AdSenseUnit from './AdSenseUnit'
describe('AdSenseUnit', () => {
afterEach(() => {
cleanup()
vi.restoreAllMocks()
delete window.adsbygoogle
})
it('renders the dashboard horizontal slot and pushes once', () => {
window.adsbygoogle = []
const { container } = render(<AdSenseUnit slot="8766048277" />)
const unit = container.querySelector('ins.adsbygoogle')
expect(unit).not.toBeNull()
expect(container.firstChild.className).toContain('justify-center')
expect(unit.className).toContain('max-w-3xl')
expect(unit.getAttribute('data-ad-client')).toBe('ca-pub-6457864535683080')
expect(unit.getAttribute('data-ad-slot')).toBe('8766048277')
expect(unit.getAttribute('data-ad-format')).toBe('auto')
expect(unit.getAttribute('data-full-width-responsive')).toBe('true')
expect(window.adsbygoogle).toHaveLength(1)
})
it('renders nothing without a slot', () => {
const { container } = render(<AdSenseUnit />)
expect(container.querySelector('ins.adsbygoogle')).toBeNull()
})
it('renders nothing when disabled for authenticated users', () => {
window.adsbygoogle = []
const { container } = render(<AdSenseUnit slot="8766048277" enabled={false} />)
expect(container.querySelector('ins.adsbygoogle')).toBeNull()
expect(window.adsbygoogle).toHaveLength(0)
})
})
@@ -209,6 +209,7 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
const [reported, setReported] = useState(false)
const [reportOpen, setReportOpen] = useState(false)
const isLoggedIn = Boolean(artwork?.viewer?.is_authenticated)
const downloadUnavailable = ['missing', 'pending'].includes(String(artwork?.download_status || '').toLowerCase())
useEffect(() => {
setFavorited(Boolean(artwork?.viewer?.is_favorited))
}, [artwork?.id, artwork?.viewer?.is_favorited])
@@ -404,7 +405,7 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
</button>
{/* Download button */}
<button
{!downloadUnavailable && <button
type="button"
aria-label="Download artwork"
onClick={handleDownload}
@@ -413,7 +414,7 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
>
<DownloadArrowIcon />
{downloading ? 'Downloading…' : 'Download'}
</button>
</button>}
</div>
{/* ── Mobile fixed bottom bar ─────────────────────────────────── */}
@@ -491,7 +492,7 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
<FlagIcon />
</button>
<button
{!downloadUnavailable && <button
type="button"
aria-label="Download artwork"
onClick={handleDownload}
@@ -500,7 +501,7 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
>
<DownloadArrowIcon />
{downloading ? '…' : 'Download'}
</button>
</button>}
</div>
</div>