Add podcast platform badges and Headliner widget
This commit is contained in:
+23
-1
@@ -31,7 +31,29 @@
|
||||
"studyGuideUrl": "https://a.co/d/01sG2tOJ",
|
||||
"shareHeading": "Help one more person hear the Word this week.",
|
||||
"shareP": "Scan the QR code or text the show link to a friend who needs encouragement today.",
|
||||
"customLinks": [],
|
||||
"customLinks": [
|
||||
{
|
||||
"id": "pocket-casts",
|
||||
"label": "Pocket Casts",
|
||||
"url": "https://pca.st/76jisz3d",
|
||||
"placement": "platforms",
|
||||
"imageUrl": "/images/listen-on-pocket-casts.svg"
|
||||
},
|
||||
{
|
||||
"id": "castbox",
|
||||
"label": "Castbox",
|
||||
"url": "https://castbox.fm/channel/id7140701?country=us",
|
||||
"placement": "platforms",
|
||||
"imageUrl": "/images/listen-on-castbox.svg"
|
||||
},
|
||||
{
|
||||
"id": "goodpods",
|
||||
"label": "Goodpods",
|
||||
"url": "https://goodpods.com/podcasts/verse-by-verse-with-nate-732127",
|
||||
"placement": "platforms",
|
||||
"imageUrl": "https://storage.googleapis.com/goodpods-images-bucket/badges/generic-badge-3.svg"
|
||||
}
|
||||
],
|
||||
"customBlocks": [],
|
||||
"archivedSeries": [],
|
||||
"redirects": [
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="210" height="48" viewBox="0 0 210 48">
|
||||
<rect width="210" height="48" rx="6" fill="#131921"/>
|
||||
<!-- Amazon Music icon: headphone/music note style - simple "a" with music mark -->
|
||||
<g transform="translate(11,11)">
|
||||
<!-- Amazon arrow swoosh shape -->
|
||||
<circle cx="13" cy="13" r="12" fill="#00A8E1"/>
|
||||
<!-- Simplified headphone icon in white -->
|
||||
<path fill="#ffffff" d="M13 4C8.03 4 4 8.03 4 13v1h2v-1c0-3.87 3.13-7 7-7s7 3.13 7 7v1h2v-1c0-4.97-4.03-9-9-9zm-3 10H8c-.55 0-1 .45-1 1v3c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-3c0-.55-.45-1-1-1zm6 0h-2c-.55 0-1 .45-1 1v3c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-3c0-.55-.45-1-1-1z"/>
|
||||
</g>
|
||||
<text font-family="Helvetica Neue, Helvetica, Arial, sans-serif" fill="#b3b3b3" font-size="9.5" x="52" y="19">Listen on</text>
|
||||
<text font-family="Helvetica Neue, Helvetica, Arial, sans-serif" fill="#ffffff" font-size="15" font-weight="bold" x="52" y="36">Amazon Music</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 972 B |
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="183" height="48" viewBox="0 0 183 48">
|
||||
<rect width="183" height="48" rx="6" fill="#1a1a1a"/>
|
||||
<!-- Castbox icon: headphones/podcast icon in orange -->
|
||||
<g transform="translate(11,11)">
|
||||
<circle cx="13" cy="13" r="12" fill="#F55223"/>
|
||||
<!-- Headphones shape in white -->
|
||||
<path fill="#ffffff" d="M13 5C8.58 5 5 8.58 5 13v.5H7V13c0-3.31 2.69-6 6-6s6 2.69 6 6v.5h2V13c0-4.42-3.58-8-8-8zm-3.5 9H7c-.55 0-1 .45-1 1v2.5c0 .55.45 1 1 1h2.5c.55 0 1-.45 1-1V15c0-.55-.45-1-1-1zm7 0h-2.5c-.55 0-1 .45-1 1v2.5c0 .55.45 1 1 1H17c.55 0 1-.45 1-1V15c0-.55-.45-1-1-1z"/>
|
||||
</g>
|
||||
<text font-family="Helvetica Neue, Helvetica, Arial, sans-serif" fill="#b3b3b3" font-size="9.5" x="52" y="19">Listen on</text>
|
||||
<text font-family="Helvetica Neue, Helvetica, Arial, sans-serif" fill="#ffffff" font-size="16" font-weight="bold" x="52" y="36">Castbox</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 904 B |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="195" height="48" viewBox="0 0 195 48">
|
||||
<rect width="195" height="48" rx="6" fill="#F43E37"/>
|
||||
<!-- Pocket Casts logo: circle with play-in-wave icon -->
|
||||
<g transform="translate(11,11)">
|
||||
<circle cx="13" cy="13" r="12" fill="rgba(0,0,0,0.25)"/>
|
||||
<!-- Outer arc -->
|
||||
<path fill="#ffffff" d="M13 2C7 2 2 7 2 13s5 11 11 11 11-5 11-11S19 2 13 2zm0 19c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/>
|
||||
<!-- Inner arc -->
|
||||
<path fill="#ffffff" d="M13 6c-3.87 0-7 3.13-7 7s3.13 7 7 7 7-3.13 7-7-3.13-7-7-7zm0 11c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"/>
|
||||
<!-- Center dot -->
|
||||
<circle cx="13" cy="13" r="2.5" fill="#ffffff"/>
|
||||
</g>
|
||||
<text font-family="Helvetica Neue, Helvetica, Arial, sans-serif" fill="rgba(255,255,255,0.8)" font-size="9.5" x="52" y="19">Listen on</text>
|
||||
<text font-family="Helvetica Neue, Helvetica, Arial, sans-serif" fill="#ffffff" font-size="14.5" font-weight="bold" x="52" y="36">Pocket Casts</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1019 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="48" viewBox="0 0 180 48">
|
||||
<rect width="180" height="48" rx="6" fill="#191414"/>
|
||||
<!-- Spotify icon (24x24 viewBox path, scaled to fit 26x26 at offset 11,11) -->
|
||||
<g transform="translate(11,11) scale(1.083)">
|
||||
<path fill="#1DB954" d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z"/>
|
||||
</g>
|
||||
<text font-family="Helvetica Neue, Helvetica, Arial, sans-serif" fill="#b3b3b3" font-size="9.5" x="48" y="19">Listen on</text>
|
||||
<text font-family="Helvetica Neue, Helvetica, Arial, sans-serif" fill="#ffffff" font-size="16" font-weight="bold" x="48" y="36">Spotify</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="190" height="48" viewBox="0 0 190 48">
|
||||
<rect width="190" height="48" rx="6" fill="#0f0f0f"/>
|
||||
<!-- YouTube icon: red rounded rect with white play triangle -->
|
||||
<g transform="translate(11,11)">
|
||||
<!-- YouTube red background rect -->
|
||||
<rect x="1" y="4.5" width="24" height="17" rx="4" fill="#FF0000"/>
|
||||
<!-- White play triangle -->
|
||||
<polygon points="10,9 20,13 10,17" fill="#ffffff"/>
|
||||
</g>
|
||||
<text font-family="Helvetica Neue, Helvetica, Arial, sans-serif" fill="#b3b3b3" font-size="9.5" x="52" y="19">Watch on</text>
|
||||
<text font-family="Helvetica Neue, Helvetica, Arial, sans-serif" fill="#ffffff" font-size="16" font-weight="bold" x="52" y="36">YouTube</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 732 B |
+32
@@ -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
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user