Add podcast platform badges and Headliner widget

This commit is contained in:
nmemmert
2026-05-06 10:54:38 -04:00
parent 475abff3cf
commit 42bac71fd6
8 changed files with 198 additions and 15 deletions
+32
View File
@@ -460,6 +460,25 @@
align-items: center;
}
.headliner-widget-wrap {
margin-top: 2rem;
}
.headliner-widget-frame {
display: block;
width: 100%;
min-height: 580px;
border: 0;
border-radius: 12px;
}
.headliner-widget-empty {
margin: 0;
text-align: center;
color: rgba(255, 255, 255, 0.72);
font-size: 0.95rem;
}
.platform-btn {
display: inline-flex;
align-items: center;
@@ -495,6 +514,19 @@
display: block;
}
.platform-btn-image {
background: transparent;
padding: 0;
border-radius: 0;
}
.platform-badge {
display: block;
width: 150px;
max-width: 100%;
height: auto;
}
/* ── About ── */
.section-about {
background: #0d0d0d;
+84 -14
View File
@@ -11,6 +11,69 @@ import './App.css'
const SPOTIFY_EMBED_URL =
'https://open.spotify.com/embed/show/0Gq1TzoJOdReSZ1gYQi8Xl?utm_source=generator&theme=0'
const CONSENT_KEY = 'vbn_analytics_consent_choice'
const HEADLINER_WIDGET_ID = 'WI_cmou3b4q7000701p0o9qmmcfj'
function HeadlinerWidget() {
const [iframeSrc, setIframeSrc] = useState('')
const [status, setStatus] = useState<'loading' | 'ready' | 'empty'>('loading')
useEffect(() => {
const getDiscoUrl = () => {
const canonicalHref = document.querySelector('link[rel="canonical"]')?.getAttribute('href')
const ogUrl = document.querySelector('meta[property="og:url"]')?.getAttribute('content')
const base = canonicalHref || ogUrl
if (!base) return window.location.href
try {
const parsed = new URL(base)
return `${parsed.origin}${window.location.pathname}`
} catch {
return window.location.href
}
}
const discoUrl = getDiscoUrl()
const discoTitle = document.querySelector('meta[property="og:title"]')?.getAttribute('content') || document.title
const src = `https://disco.headliner.link/d/web/widget.html?widgetId=${encodeURIComponent(HEADLINER_WIDGET_ID)}&url=${encodeURIComponent(discoUrl)}&title=${encodeURIComponent(discoTitle)}`
const sessionId = `SS_siteforge_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`
const query = new URLSearchParams({
sessionId,
url: discoUrl,
widgetId: HEADLINER_WIDGET_ID,
})
fetch(`https://api.headliner.link/api/v1/widget/widget-request?${query.toString()}`)
.then(r => (r.ok ? r.json() : Promise.reject(new Error('widget request failed'))))
.then((data: { results?: unknown[] }) => {
if (Array.isArray(data.results) && data.results.length > 0) {
setIframeSrc(src)
setStatus('ready')
return
}
setStatus('empty')
})
.catch(() => {
// If API probing fails, still attempt iframe rendering.
setIframeSrc(src)
setStatus('ready')
})
}, [])
return (
status === 'empty' ? null : (
<div className="headliner-widget-wrap" aria-label="Featured listening widget">
{status === 'ready' && iframeSrc && (
<iframe
src={iframeSrc}
title="Featured listening widget"
loading="lazy"
className="headliner-widget-frame"
/>
)}
</div>
)
)
}
function StudyDownloadForm({ buttonText = 'Download Guide' }: { buttonText?: string }) {
const [fields, setFields] = useState({ firstName: '', lastName: '', email: '' })
@@ -421,28 +484,34 @@ function PlatformButtons({ content }: { content: SiteContent }) {
return (
<div className="platform-buttons">
<a href={spotifyUrl} target="_blank" rel="noreferrer" className="platform-btn spotify-btn">
<SpotifyIcon />
Listen on Spotify
<a href={appleUrl} target="_blank" rel="noreferrer" className="platform-btn platform-btn-image">
<img src="/images/apple-podcasts-badge.svg" alt="Listen on Apple Podcasts" className="platform-badge" />
</a>
<a href={youtubeUrl} target="_blank" rel="noreferrer" className="platform-btn youtube-btn">
<YouTubeIcon />
YouTube
<a href={spotifyUrl} target="_blank" rel="noreferrer" className="platform-btn platform-btn-image">
<img src="/images/listen-on-spotify.svg" alt="Listen on Spotify" className="platform-badge" />
</a>
<a href={amazonUrl} target="_blank" rel="noreferrer" className="platform-btn amazon-btn">
<AmazonMusicIcon />
Amazon Music
<a href={youtubeUrl} target="_blank" rel="noreferrer" className="platform-btn platform-btn-image">
<img src="/images/watch-on-youtube.svg" alt="Watch on YouTube" className="platform-badge" />
</a>
<a href={amazonUrl} target="_blank" rel="noreferrer" className="platform-btn platform-btn-image">
<img src="/images/listen-on-amazon-music.svg" alt="Listen on Amazon Music" className="platform-badge" />
</a>
<a href={facebookUrl} target="_blank" rel="noreferrer" className="platform-btn facebook-btn">
<FacebookIcon />
Facebook
</a>
<a href={appleUrl} target="_blank" rel="noreferrer" className="platform-btn apple-btn">
<img src="/images/apple-podcasts-badge.svg" alt="Listen on Apple Podcasts" className="apple-badge" />
</a>
{(content.customLinks ?? []).filter(l => l.placement === 'platforms').map(l => (
<a key={l.id} href={l.url} target="_blank" rel="noreferrer" className="platform-btn custom-btn">
{l.label}
<a
key={l.id}
href={l.url}
target="_blank"
rel="noreferrer"
className={`platform-btn ${l.imageUrl ? 'platform-btn-image' : 'custom-btn'}`}
aria-label={`Listen on ${l.label}`}
>
{l.imageUrl
? <img src={l.imageUrl} alt={`Listen on ${l.label}`} className="platform-badge" />
: l.label}
</a>
))}
</div>
@@ -1100,6 +1169,7 @@ function EpisodesPage({ content }: { content: SiteContent }) {
? spotifyFallback
: renderEpisodeCards(currentEpisodes)}
<PlatformButtons content={content} />
<HeadlinerWidget />
</div>
</section>