- Second series gets its own guide (Resources page card, download page, admin fields) - When a series ends (scheduled or manual) its guide moves onto its Finished Book, which shows a guide button; the main series' guide is cleared unless the launch supplies a new one - Lane B launch schedule can supply the incoming series' guide - Download lookup covers second-series and finished-book guides; guide URLs are sanitized on save and on use Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
39691aedb3
commit
e2150e1cd7
7 files changed
+244
-4
No files matched your search
@@ -297,6 +297,22 @@ function sanitizeIsoDate(value) {
|
||||
return Number.isFinite(time) ? new Date(time).toISOString() : ''
|
||||
}
|
||||
|
||||
function sanitizeStudyGuide(value) {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined
|
||||
const str = (v, max) => (typeof v === 'string' ? v.trim().slice(0, max) : '')
|
||||
const title = str(value.title, 140)
|
||||
if (!title) return undefined
|
||||
return {
|
||||
title,
|
||||
description: str(value.description, 4000),
|
||||
downloadUrl: sanitizeUrl(value.downloadUrl),
|
||||
amazonUrl: sanitizeUrl(value.amazonUrl),
|
||||
amazonLabel: str(value.amazonLabel, 80),
|
||||
appleBooksUrl: sanitizeUrl(value.appleBooksUrl),
|
||||
appleBooksLabel: str(value.appleBooksLabel, 80),
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeSecondSeries(value) {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined
|
||||
const str = (v, max) => (typeof v === 'string' ? v.trim().slice(0, max) : '')
|
||||
@@ -310,6 +326,7 @@ function sanitizeSecondSeries(value) {
|
||||
listenBtnLabel: str(value.listenBtnLabel, 80),
|
||||
studySlug: str(value.studySlug, 120).toLowerCase().replace(/[^a-z0-9-]/g, '-'),
|
||||
...(season ? { season } : {}),
|
||||
...(sanitizeStudyGuide(value.guide) ? { guide: sanitizeStudyGuide(value.guide) } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,6 +427,9 @@ export function sanitizeSiteContent(siteContent) {
|
||||
archivedSeries: sanitizeArchivedSeries(siteContent.archivedSeries),
|
||||
studies: sanitizeStudies(siteContent.studies),
|
||||
secondSeries: sanitizeSecondSeries(siteContent.secondSeries),
|
||||
finishedBooks: Array.isArray(siteContent.finishedBooks)
|
||||
? siteContent.finishedBooks.map(book => (book && typeof book === 'object' ? { ...book, guide: sanitizeStudyGuide(book.guide) } : book))
|
||||
: siteContent.finishedBooks,
|
||||
colossiansStudySections: sanitizeColossiansStudySections(siteContent.colossiansStudySections),
|
||||
redirects: sanitizeRedirectRules(siteContent.redirects),
|
||||
podcastFeaturedLinks: sanitizeFeaturedLinks(siteContent.podcastFeaturedLinks ?? DEFAULT_PODCAST_FEATURED_LINKS),
|
||||
|
||||
@@ -66,11 +66,24 @@ function readCurrentSeries(content, lane) {
|
||||
return {
|
||||
title: s.title ?? '', description: s.description ?? '', imageUrl: s.imageUrl ?? '',
|
||||
season: s.season, studySlug: s.studySlug ?? '',
|
||||
guide: s.guide?.title ? s.guide : undefined,
|
||||
}
|
||||
}
|
||||
return {
|
||||
title: content.seriesTitle ?? '', description: content.seriesDescription ?? '', imageUrl: content.seriesImageUrl ?? '',
|
||||
season: content.seriesSeason, studySlug: content.seriesStudySlug ?? '',
|
||||
// The main series' guide lives in the flat studyGuide* fields
|
||||
guide: content.studyGuideTitle?.trim()
|
||||
? {
|
||||
title: content.studyGuideTitle,
|
||||
description: content.studyGuideDescription ?? '',
|
||||
downloadUrl: content.studyGuideDownloadUrl ?? '',
|
||||
amazonUrl: content.studyGuideUrl ?? '',
|
||||
amazonLabel: content.studyGuideAmazonButtonLabel ?? '',
|
||||
appleBooksUrl: content.studyGuideAppleBooksUrl ?? '',
|
||||
appleBooksLabel: content.studyGuideAppleBooksLabel ?? '',
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +109,7 @@ function applySeriesTransition(content, sched, lane = 'primary') {
|
||||
imageUrl: current.imageUrl,
|
||||
season: current.season ?? ((newSeason ?? 2) - 1 || 1),
|
||||
...(current.studySlug ? { studySlug: current.studySlug } : {}),
|
||||
...(current.guide ? { guide: current.guide } : {}),
|
||||
...(endingHighlights.length > 0 ? { highlights: endingHighlights.map(({ lane: _lane, ...link }) => link) } : {}),
|
||||
}
|
||||
: null
|
||||
@@ -142,6 +156,8 @@ function applySeriesTransition(content, sched, lane = 'primary') {
|
||||
description: sched.newDescription?.trim() ?? '',
|
||||
imageUrl: sched.newImageUrl?.trim() || content.secondSeries?.imageUrl || '',
|
||||
studySlug: newStudySlug,
|
||||
// The ending series' guide moved onto its Finished Book; the incoming series brings its own
|
||||
guide: sched.newGuide?.title?.trim() ? sched.newGuide : undefined,
|
||||
...(newSeason ? { season: newSeason } : {}),
|
||||
},
|
||||
}
|
||||
@@ -152,6 +168,13 @@ function applySeriesTransition(content, sched, lane = 'primary') {
|
||||
seriesLabel: 'Now Playing',
|
||||
seriesStudySlug: newStudySlug,
|
||||
...(newSeason ? { seriesSeason: newSeason } : {}),
|
||||
// The ending series' guide moved onto its Finished Book; unless the launch brings a new one, clear it
|
||||
...(current.guide && !sched.newStudyGuideTitle?.trim()
|
||||
? {
|
||||
studyGuideTitle: '', studyGuideDescription: '', studyGuideDownloadUrl: '', studyGuideUrl: '',
|
||||
studyGuideAmazonButtonLabel: '', studyGuideAppleBooksUrl: '', studyGuideAppleBooksLabel: '',
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
|
||||
const next = sanitizeSiteContent({
|
||||
|
||||
@@ -145,6 +145,18 @@ export function register(app) {
|
||||
const customResources = Array.isArray(siteContent.customLinks)
|
||||
? siteContent.customLinks.filter(link => link?.placement === 'resources')
|
||||
: []
|
||||
// Companion study guides: the second series' guide, and guides kept on finished books
|
||||
if (id === 'guide:second') {
|
||||
const guide = siteContent.secondSeries?.guide
|
||||
const guideUrl = sanitizeUrl(guide?.downloadUrl)
|
||||
return guideUrl ? { label: guide.title, url: guideUrl } : null
|
||||
}
|
||||
if (id.startsWith('guide:book:')) {
|
||||
const bookId = id.slice('guide:book:'.length)
|
||||
const book = Array.isArray(siteContent.finishedBooks) ? siteContent.finishedBooks.find(item => item?.id === bookId) : null
|
||||
const bookGuideUrl = sanitizeUrl(book?.guide?.downloadUrl)
|
||||
return bookGuideUrl ? { label: book.guide.title, url: bookGuideUrl } : null
|
||||
}
|
||||
if (id.startsWith('custom:')) {
|
||||
const customId = id.slice('custom:'.length)
|
||||
const match = customResources.find(link => link.id === customId)
|
||||
|
||||
Reference in new issue
Block a user