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:
@@ -9,6 +9,7 @@ import ArtworkTags from '../components/artwork/ArtworkTags'
|
|||||||
import ArtworkDescription from '../components/artwork/ArtworkDescription'
|
import ArtworkDescription from '../components/artwork/ArtworkDescription'
|
||||||
import ArtworkEvolutionPanel from '../components/artwork/ArtworkEvolutionPanel'
|
import ArtworkEvolutionPanel from '../components/artwork/ArtworkEvolutionPanel'
|
||||||
import ArtworkComments from '../components/artwork/ArtworkComments'
|
import ArtworkComments from '../components/artwork/ArtworkComments'
|
||||||
|
import AdSenseUnit from '../components/ads/AdSenseUnit'
|
||||||
import ArtworkActionBar from '../components/artwork/ArtworkActionBar'
|
import ArtworkActionBar from '../components/artwork/ArtworkActionBar'
|
||||||
import ArtworkDetailsPanel from '../components/artwork/ArtworkDetailsPanel'
|
import ArtworkDetailsPanel from '../components/artwork/ArtworkDetailsPanel'
|
||||||
import CreatorSpotlight from '../components/artwork/CreatorSpotlight'
|
import CreatorSpotlight from '../components/artwork/CreatorSpotlight'
|
||||||
@@ -319,6 +320,10 @@ function publisherToGroupSummary(publisher) {
|
|||||||
{/* Tags & categories */}
|
{/* Tags & categories */}
|
||||||
<ArtworkTags artwork={artwork} />
|
<ArtworkTags artwork={artwork} />
|
||||||
|
|
||||||
|
{!isAuthenticated ? (
|
||||||
|
<AdSenseUnit key={artwork.id} slot="4398418697" />
|
||||||
|
) : null}
|
||||||
|
|
||||||
{/* Comments */}
|
{/* Comments */}
|
||||||
<ArtworkComments
|
<ArtworkComments
|
||||||
artworkId={artwork.id}
|
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 [reported, setReported] = useState(false)
|
||||||
const [reportOpen, setReportOpen] = useState(false)
|
const [reportOpen, setReportOpen] = useState(false)
|
||||||
const isLoggedIn = Boolean(artwork?.viewer?.is_authenticated)
|
const isLoggedIn = Boolean(artwork?.viewer?.is_authenticated)
|
||||||
|
const downloadUnavailable = ['missing', 'pending'].includes(String(artwork?.download_status || '').toLowerCase())
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFavorited(Boolean(artwork?.viewer?.is_favorited))
|
setFavorited(Boolean(artwork?.viewer?.is_favorited))
|
||||||
}, [artwork?.id, artwork?.viewer?.is_favorited])
|
}, [artwork?.id, artwork?.viewer?.is_favorited])
|
||||||
@@ -404,7 +405,7 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Download button */}
|
{/* Download button */}
|
||||||
<button
|
{!downloadUnavailable && <button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Download artwork"
|
aria-label="Download artwork"
|
||||||
onClick={handleDownload}
|
onClick={handleDownload}
|
||||||
@@ -413,7 +414,7 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
|
|||||||
>
|
>
|
||||||
<DownloadArrowIcon />
|
<DownloadArrowIcon />
|
||||||
{downloading ? 'Downloading…' : 'Download'}
|
{downloading ? 'Downloading…' : 'Download'}
|
||||||
</button>
|
</button>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Mobile fixed bottom bar ─────────────────────────────────── */}
|
{/* ── Mobile fixed bottom bar ─────────────────────────────────── */}
|
||||||
@@ -491,7 +492,7 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
|
|||||||
<FlagIcon />
|
<FlagIcon />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
{!downloadUnavailable && <button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Download artwork"
|
aria-label="Download artwork"
|
||||||
onClick={handleDownload}
|
onClick={handleDownload}
|
||||||
@@ -500,7 +501,7 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
|
|||||||
>
|
>
|
||||||
<DownloadArrowIcon />
|
<DownloadArrowIcon />
|
||||||
{downloading ? '…' : 'Download'}
|
{downloading ? '…' : 'Download'}
|
||||||
</button>
|
</button>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,14 @@
|
|||||||
|
|
||||||
Renders nothing when:
|
Renders nothing when:
|
||||||
- GOOGLE_ADSENSE_PUBLISHER_ID is not set in .env
|
- GOOGLE_ADSENSE_PUBLISHER_ID is not set in .env
|
||||||
|
- The visitor is authenticated
|
||||||
- User has not given consent (handled client-side via CSS class .ads-disabled)
|
- User has not given consent (handled client-side via CSS class .ads-disabled)
|
||||||
--}}
|
--}}
|
||||||
@php
|
@php
|
||||||
$publisherId = config('services.google_adsense.publisher_id');
|
$publisherId = config('services.google_adsense.publisher_id');
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
@if($publisherId)
|
@if($publisherId && ! auth()->check())
|
||||||
<div class="ad-unit-wrapper {{ $attributes->get('class', '') }}">
|
<div class="ad-unit-wrapper {{ $attributes->get('class', '') }}">
|
||||||
<ins class="adsbygoogle"
|
<ins class="adsbygoogle"
|
||||||
style="display:block"
|
style="display:block"
|
||||||
|
|||||||
@@ -261,8 +261,8 @@
|
|||||||
@inertiaHead
|
@inertiaHead
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if(config('services.google_adsense.publisher_id'))
|
@if(auth()->guest() && config('services.google_adsense.publisher_id'))
|
||||||
{{-- Google AdSense — consent-gated loader --}}
|
{{-- Google AdSense — guests only, consent-gated loader --}}
|
||||||
{{-- Script is only injected after the user accepts all cookies. --}}
|
{{-- Script is only injected after the user accepts all cookies. --}}
|
||||||
{{-- If consent was given on a previous visit it fires on page load. --}}
|
{{-- If consent was given on a previous visit it fires on page load. --}}
|
||||||
<script>
|
<script>
|
||||||
@@ -297,6 +297,10 @@
|
|||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
@endif
|
@endif
|
||||||
|
@guest
|
||||||
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6457864535683080" crossorigin="anonymous"></script>
|
||||||
|
@endguest
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
@php
|
@php
|
||||||
$authBgRoutes = [
|
$authBgRoutes = [
|
||||||
|
|||||||
@@ -100,8 +100,20 @@
|
|||||||
@include('web.home.sections.news', ['items' => $props['news'] ?? []])
|
@include('web.home.sections.news', ['items' => $props['news'] ?? []])
|
||||||
@include('web.home.sections.cta', ['isLoggedIn' => true])
|
@include('web.home.sections.cta', ['isLoggedIn' => true])
|
||||||
@else
|
@else
|
||||||
@foreach ($guestArtworkSections as $section)
|
@foreach ($guestArtworkSections as $index => $section)
|
||||||
@include('web.home.sections.artwork-section', $section)
|
@include('web.home.sections.artwork-section', $section)
|
||||||
|
@if($index === 0)
|
||||||
|
{{-- Skinbase Dashboard Horizontal — guests only --}}
|
||||||
|
<div class="mt-14 flex justify-center px-4 sm:px-6 lg:px-8">
|
||||||
|
<ins class="adsbygoogle w-full max-w-3xl"
|
||||||
|
style="display:block;text-align:center"
|
||||||
|
data-ad-client="ca-pub-6457864535683080"
|
||||||
|
data-ad-slot="8766048277"
|
||||||
|
data-ad-format="auto"
|
||||||
|
data-full-width-responsive="true"></ins>
|
||||||
|
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
@include('web.home.sections.collections', ['collections' => $props['collections_trending'] ?: ($props['collections_featured'] ?: ($props['collections_editorial'] ?? []))])
|
@include('web.home.sections.collections', ['collections' => $props['collections_trending'] ?: ($props['collections_featured'] ?: ($props['collections_editorial'] ?? []))])
|
||||||
@include('web.home.sections.world-spotlight', ['world' => $props['world_spotlight'] ?? null])
|
@include('web.home.sections.world-spotlight', ['world' => $props['world_spotlight'] ?? null])
|
||||||
|
|||||||
@@ -64,6 +64,22 @@ it('authenticated home page sends private cache headers', function () {
|
|||||||
->toContain('no-store');
|
->toContain('no-store');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('authenticated home page does not render ads', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->get('/')
|
||||||
|
->assertOk()
|
||||||
|
->assertDontSee('data-ad-slot="8766048277"', false)
|
||||||
|
->assertDontSee('pagead2.googlesyndication.com', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('guest home page renders the dashboard horizontal ad', function () {
|
||||||
|
$this->get('/')
|
||||||
|
->assertOk()
|
||||||
|
->assertSee('data-ad-slot="8766048277"', false);
|
||||||
|
});
|
||||||
|
|
||||||
it('home page includes a canonical link tag', function () {
|
it('home page includes a canonical link tag', function () {
|
||||||
$this->get('/')
|
$this->get('/')
|
||||||
->assertStatus(200)
|
->assertStatus(200)
|
||||||
|
|||||||
Reference in New Issue
Block a user