From eafa2caa277809c6922eca8cd30ff94ea33c4f18 Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Mon, 9 Mar 2026 17:49:05 +0100 Subject: [PATCH] pwa: fix offline caching for prayer/faith routes The glob in sync.ts targeted a nonexistent /src/routes/glaube/ directory instead of the actual [faithLang=faithLang] parameterized route. This meant zero prayer pages were ever precached for offline use. - Fix glob to match [faithLang=faithLang] and expand param segments to both language variants (glaube/faith, gebete/prayers, rosenkranz/rosary) - Extract validPrayerSlugs to shared module for build-time route enumeration - Add faith to service worker cacheable route regex --- src/lib/data/prayerSlugs.ts | 24 +++++++ src/lib/offline/sync.ts | 63 +++++++++++++++---- .../[prayer]/+page.server.ts | 27 +------- src/service-worker.ts | 4 +- 4 files changed, 79 insertions(+), 39 deletions(-) create mode 100644 src/lib/data/prayerSlugs.ts diff --git a/src/lib/data/prayerSlugs.ts b/src/lib/data/prayerSlugs.ts new file mode 100644 index 0000000..0fcd824 --- /dev/null +++ b/src/lib/data/prayerSlugs.ts @@ -0,0 +1,24 @@ +// Valid prayer slugs (both languages) — single source of truth +// Used by the [prayer] route for validation and by sync.ts for offline precaching +export const validPrayerSlugs = new Set([ + 'das-heilige-kreuzzeichen', 'the-sign-of-the-cross', + 'gloria-patri', + 'paternoster', 'our-father', + 'credo', 'nicene-creed', + 'ave-maria', 'hail-mary', + 'salve-regina', + 'das-fatimagebet', 'fatima-prayer', + 'gloria', + 'gebet-zum-hl-erzengel-michael', 'prayer-to-st-michael-the-archangel', + 'bruder-klaus-gebet', 'prayer-of-st-nicholas-of-flue', + 'josephgebet-des-hl-papst-pius-x', 'prayer-to-st-joseph-by-pope-st-pius-x', + 'das-confiteor', 'the-confiteor', + 'postcommunio', + 'anima-christi', + 'prayer-before-a-crucifix', 'gebet-vor-einem-kruzifix', + 'schutzengel-gebet', 'guardian-angel-prayer', + 'apostolisches-glaubensbekenntnis', 'apostles-creed', + 'tantum-ergo', + 'angelus', + 'regina-caeli', +]); diff --git a/src/lib/offline/sync.ts b/src/lib/offline/sync.ts index 04c293f..3f39a92 100644 --- a/src/lib/offline/sync.ts +++ b/src/lib/offline/sync.ts @@ -1,16 +1,55 @@ import { saveAllRecipes } from './db'; import type { BriefRecipeType, RecipeModelType } from '$types/types'; +import { validPrayerSlugs } from '$lib/data/prayerSlugs'; -// Discover glaube routes at build time using Vite's glob import -const glaubePageModules = import.meta.glob('/src/routes/glaube/**/+page.svelte'); -const glaubeRoutes = Object.keys(glaubePageModules).map(path => { - // Convert file path to route path - // /src/routes/glaube/+page.svelte -> /glaube - // /src/routes/glaube/angelus/+page.svelte -> /glaube/angelus - return path - .replace('/src/routes', '') - .replace('/+page.svelte', '') || '/glaube'; -}); +// Discover faith routes at build time using Vite's glob import +// The actual directory is [faithLang=faithLang] with parameterized sub-dirs +const faithPageModules = import.meta.glob('/src/routes/\\[faithLang=faithLang\\]/**/+page.svelte'); + +// Convert file paths to actual route URLs for both language variants +// e.g. /src/routes/[faithLang=faithLang]/[prayers=prayersLang]/+page.svelte +// -> /glaube/gebete, /faith/prayers +const paramMap: Record = { + '[faithLang=faithLang]': ['glaube', 'faith'], + '[prayers=prayersLang]': ['gebete', 'prayers'], + '[rosary=rosaryLang]': ['rosenkranz', 'rosary'], +}; + +function expandFaithRoutes(): string[] { + const routes: string[] = []; + for (const filePath of Object.keys(faithPageModules)) { + // Strip prefix and suffix: /src/routes/[faithLang=faithLang]/angelus/+page.svelte -> [faithLang=faithLang]/angelus + let route = filePath.replace('/src/routes/', '').replace('/+page.svelte', ''); + + // Skip routes with dynamic [prayer] segment — those need explicit slug enumeration + if (route.includes('[prayer]')) continue; + + // Generate both language variants by replacing all param segments + const segments = route.split('/'); + const deSegments: string[] = []; + const enSegments: string[] = []; + for (const seg of segments) { + if (paramMap[seg]) { + deSegments.push(paramMap[seg][0]); + enSegments.push(paramMap[seg][1]); + } else { + deSegments.push(seg); + enSegments.push(seg); + } + } + routes.push('/' + deSegments.join('/')); + routes.push('/' + enSegments.join('/')); + } + return routes; +} + +const faithRoutes = expandFaithRoutes(); + +// Add individual prayer pages (dynamic [prayer] slug, resolved at build time) +for (const slug of validPrayerSlugs) { + faithRoutes.push(`/glaube/gebete/${slug}`); + faithRoutes.push(`/faith/prayers/${slug}`); +} export type SyncProgress = { phase: 'recipes' | 'pages' | 'data' | 'images'; @@ -133,8 +172,8 @@ async function precacheMainPages(_fetchFn: typeof fetch): Promise { '/recipes/favorites/__data.json' ]; - // Add dynamically discovered glaube routes (HTML and __data.json) - for (const route of glaubeRoutes) { + // Add dynamically discovered faith routes (HTML and __data.json) + for (const route of faithRoutes) { pagesToCache.push(route); pagesToCache.push(`${route}/__data.json`); } diff --git a/src/routes/[faithLang=faithLang]/[prayers=prayersLang]/[prayer]/+page.server.ts b/src/routes/[faithLang=faithLang]/[prayers=prayersLang]/[prayer]/+page.server.ts index fa966f2..424c496 100644 --- a/src/routes/[faithLang=faithLang]/[prayers=prayersLang]/[prayer]/+page.server.ts +++ b/src/routes/[faithLang=faithLang]/[prayers=prayersLang]/[prayer]/+page.server.ts @@ -1,32 +1,9 @@ import type { PageServerLoad } from "./$types"; import { error } from "@sveltejs/kit"; - -// Valid prayer slugs (both languages) -const validSlugs = new Set([ - 'das-heilige-kreuzzeichen', 'the-sign-of-the-cross', - 'gloria-patri', - 'paternoster', 'our-father', - 'credo', 'nicene-creed', - 'ave-maria', 'hail-mary', - 'salve-regina', - 'das-fatimagebet', 'fatima-prayer', - 'gloria', - 'gebet-zum-hl-erzengel-michael', 'prayer-to-st-michael-the-archangel', - 'bruder-klaus-gebet', 'prayer-of-st-nicholas-of-flue', - 'josephgebet-des-hl-papst-pius-x', 'prayer-to-st-joseph-by-pope-st-pius-x', - 'das-confiteor', 'the-confiteor', - 'postcommunio', - 'anima-christi', - 'prayer-before-a-crucifix', 'gebet-vor-einem-kruzifix', - 'schutzengel-gebet', 'guardian-angel-prayer', - 'apostolisches-glaubensbekenntnis', 'apostles-creed', - 'tantum-ergo', - 'angelus', - 'regina-caeli', -]); +import { validPrayerSlugs } from '$lib/data/prayerSlugs'; export const load: PageServerLoad = async ({ params, url }) => { - if (!validSlugs.has(params.prayer)) { + if (!validPrayerSlugs.has(params.prayer)) { throw error(404, 'Prayer not found'); } diff --git a/src/service-worker.ts b/src/service-worker.ts index cb36bcc..75fdfa8 100644 --- a/src/service-worker.ts +++ b/src/service-worker.ts @@ -73,7 +73,7 @@ sw.addEventListener('fetch', (event) => { // Handle SvelteKit __data.json requests for cacheable routes (recipes, glaube, root) // Cache successful responses, serve from cache when offline const isCacheableDataRoute = url.pathname.includes('__data.json') && - (url.pathname.match(/^\/(rezepte|recipes|glaube)(\/|$)/) || url.pathname === '/__data.json'); + (url.pathname.match(/^\/(rezepte|recipes|glaube|faith)(\/|$)/) || url.pathname === '/__data.json'); if (isCacheableDataRoute) { event.respondWith( @@ -193,7 +193,7 @@ sw.addEventListener('fetch', (event) => { // Cache successful HTML responses for cacheable pages (using pathname as key) const isCacheablePage = response.ok && ( - url.pathname.match(/^\/(rezepte|recipes|glaube)(\/|$)/) || + url.pathname.match(/^\/(rezepte|recipes|glaube|faith)(\/|$)/) || url.pathname === '/' ); if (isCacheablePage) {