Episode highlights per series: tag by lane, group on Episodes page, move to Finished Book when series ends; v2.9.1
Publish Container / build (push) Successful in 15m21s
Publish Container / build (push) Successful in 15m21s
- Highlights get a Series field (Lane A / Lane B / General); legacy highlights count as Lane A - Episodes page groups highlights under each series' title - Ending a series (scheduled or manual) moves only that series' highlights onto its Finished Book page; other lane and General stay - Archived highlights keep working: detail pages, sitemap, scripture index and search include them Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ddcaec0ddf
commit
652b003bd9
9 files changed
+133
-57
No files matched your search
+58
-45
@@ -6,8 +6,8 @@ import QASection from './components/QASection'
|
||||
import ContactForm from './components/ContactForm'
|
||||
import { ColossiansStudyIndexPage, ColossiansStudyNotesPage, ColossiansStudySectionPage, StudyLandingPage, StudySignupPage, StudyAccountPage, StudyCommunityPage, StudyDiscussionHubPage, StudyQuizPage, StudyResetPasswordPage, StudyVerifyEmailPage } from './studyPages'
|
||||
import { SpotifyIcon } from './icons'
|
||||
import type { SiteContent, StudyProgram, Testimonial } from './content'
|
||||
import { DEFAULTS } from './content'
|
||||
import type { SiteContent, StudyProgram, Testimonial, PodcastFeaturedLink } from './content'
|
||||
import { DEFAULTS, getAllHighlights } from './content'
|
||||
import { usePageMeta } from './hooks/usePageMeta'
|
||||
import { useGlobalSearch } from './hooks/useGlobalSearch'
|
||||
import { GlobalSearch } from './components/GlobalSearch'
|
||||
@@ -715,10 +715,49 @@ function ContactSection({ content }: { content: SiteContent }) {
|
||||
)
|
||||
}
|
||||
|
||||
function HighlightCard({ link }: { link: PodcastFeaturedLink }) {
|
||||
const hasRichContent = !!(
|
||||
link.embedUrl
|
||||
|| isLikelySpotifyEpisodeUrl(link.url)
|
||||
|| (link.discussionQuestions ?? []).length > 0
|
||||
|| link.showNotes
|
||||
|| link.showNotesSummary
|
||||
|| (link.showNotesThemes ?? []).length > 0
|
||||
|| (link.showNotesScripture ?? []).length > 0
|
||||
|| (link.showNotesQuotes ?? []).length > 0
|
||||
|| (link.showNotesResources ?? []).length > 0
|
||||
)
|
||||
const inner = (
|
||||
<>
|
||||
<div className="episode-card-meta">
|
||||
{link.episodeNumber && <span className="episode-number">Ep. {link.episodeNumber}</span>}
|
||||
</div>
|
||||
<h3 className="episode-title">{link.title || link.url}</h3>
|
||||
{link.summary && <p className="episode-desc">{link.summary}</p>}
|
||||
<span className="episode-listen-cta">Listen →</span>
|
||||
</>
|
||||
)
|
||||
if (!hasRichContent && link.url) {
|
||||
return <a href={link.url} target="_blank" rel="noreferrer" className="episode-card">{inner}</a>
|
||||
}
|
||||
return <Link to={`/episodes/${link.id}`} className="episode-card">{inner}</Link>
|
||||
}
|
||||
|
||||
// Highlights are grouped by the series they belong to, so each book being studied gets its own row.
|
||||
function PodcastHighlightsSection({ content }: { content: SiteContent }) {
|
||||
const links = [...(content.podcastFeaturedLinks ?? [])].reverse()
|
||||
if (links.length === 0) return null
|
||||
|
||||
const laneOf = (link: PodcastFeaturedLink) => (link.lane === 'second' ? 'second' : link.lane === 'general' ? 'general' : 'primary')
|
||||
const secondTitle = content.secondSeries?.title?.trim()
|
||||
const groups = [
|
||||
{ key: 'primary', title: content.seriesTitle, links: links.filter(link => laneOf(link) === 'primary') },
|
||||
{ key: 'second', title: secondTitle, links: links.filter(link => laneOf(link) === 'second') },
|
||||
{ key: 'general', title: 'More Highlights', links: links.filter(link => laneOf(link) === 'general') },
|
||||
].filter(group => group.links.length > 0)
|
||||
// Only label the rows when there is more than one to tell apart
|
||||
const showGroupTitles = groups.length > 1
|
||||
|
||||
return (
|
||||
<section className="section-resources" aria-label="Podcast highlights">
|
||||
<div className="section-inner">
|
||||
@@ -726,48 +765,14 @@ function PodcastHighlightsSection({ content }: { content: SiteContent }) {
|
||||
<span className="ornament">✦</span> Podcast Highlights{' '}
|
||||
<span className="ornament">✦</span>
|
||||
</h2>
|
||||
<div className="episode-list">
|
||||
{links.map(link => {
|
||||
const hasRichContent = !!(
|
||||
link.embedUrl
|
||||
|| isLikelySpotifyEpisodeUrl(link.url)
|
||||
|| (link.discussionQuestions ?? []).length > 0
|
||||
|| link.showNotes
|
||||
|| link.showNotesSummary
|
||||
|| (link.showNotesThemes ?? []).length > 0
|
||||
|| (link.showNotesScripture ?? []).length > 0
|
||||
|| (link.showNotesQuotes ?? []).length > 0
|
||||
|| (link.showNotesResources ?? []).length > 0
|
||||
)
|
||||
const dest = (!hasRichContent && link.url)
|
||||
? { external: link.url }
|
||||
: { internal: `/episodes/${link.id}` }
|
||||
|
||||
const inner = (
|
||||
<>
|
||||
<div className="episode-card-meta">
|
||||
{link.episodeNumber && <span className="episode-number">Ep. {link.episodeNumber}</span>}
|
||||
</div>
|
||||
<h3 className="episode-title">{link.title || link.url}</h3>
|
||||
{link.summary && <p className="episode-desc">{link.summary}</p>}
|
||||
<span className="episode-listen-cta">Listen →</span>
|
||||
</>
|
||||
)
|
||||
|
||||
if ('external' in dest) {
|
||||
return (
|
||||
<a key={link.id} href={dest.external} target="_blank" rel="noreferrer" className="episode-card">
|
||||
{inner}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Link key={link.id} to={dest.internal} className="episode-card">
|
||||
{inner}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{groups.map(group => (
|
||||
<div key={group.key} className="highlight-group">
|
||||
{showGroupTitles && <h3 className="highlight-group-title">{group.title}</h3>}
|
||||
<div className="episode-list">
|
||||
{group.links.map(link => <HighlightCard key={link.id} link={link} />)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
@@ -1424,7 +1429,7 @@ function LandingPage({ content }: { content: SiteContent }) {
|
||||
|
||||
function EpisodeDetailPage({ content }: { content: SiteContent }) {
|
||||
const { id } = useParams<{ id: string }>()
|
||||
const episode = (content.podcastFeaturedLinks ?? []).find(e => e.id === id)
|
||||
const episode = getAllHighlights(content).find(e => e.id === id)
|
||||
const [resolvedEmbedUrl, setResolvedEmbedUrl] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
@@ -1751,6 +1756,14 @@ function FinishedSeriesPage({ content }: { content: SiteContent }) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{(book.highlights ?? []).length > 0 && (
|
||||
<>
|
||||
<h2 className="finished-series-playlist-heading">Episode Highlights</h2>
|
||||
<div className="episode-list">
|
||||
{[...(book.highlights ?? [])].reverse().map(link => <HighlightCard key={link.id} link={link} />)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<h2 id="playlist" className="finished-series-playlist-heading">Episode Playlist</h2>
|
||||
{loading ? (
|
||||
<p className="finished-books-empty">Loading episodes…</p>
|
||||
|
||||
Reference in new issue
Block a user