From d6df2d385a17e23d82eaa22b3cdfbb544fd75f28 Mon Sep 17 00:00:00 2001 From: Doxa Life Date: Fri, 2 Oct 2026 16:17:21 +0000 Subject: [PATCH] Maps: each page asks for a fresh copy of its dx-maps script before adding it A returning reader's browser may keep /dx-maps/ files for hours, as no cache rule is sent for them. Before adding a dx-maps script (Home, Pray, Adopt, Research, Deep Research, Posterverse and the regions' country posters), the page requests it once with cache 'no-cache': a 304 when unchanged, while a changed file replaces the stale copy. The script is then added at its plain address, so DX Maps' gallery still recognises it and never runs it twice. --- app/components/PosterSlot.vue | 12 ++++++++++-- app/pages/adopt/index.vue | 12 ++++++++++++ app/pages/deep-research/index.vue | 21 +++++++++++++++++++-- app/pages/index.vue | 12 ++++++++++++ app/pages/posterverse/index.vue | 21 +++++++++++++++++++-- app/pages/pray.vue | 12 ++++++++++++ app/pages/research/index.vue | 12 +++++++++++- 7 files changed, 95 insertions(+), 7 deletions(-) diff --git a/app/components/PosterSlot.vue b/app/components/PosterSlot.vue index 6a6f5346..fa1b978c 100644 --- a/app/components/PosterSlot.vue +++ b/app/components/PosterSlot.vue @@ -2,10 +2,18 @@ // The posters' script is shared by every PosterSlot on the page: it loads once, on the first opening anywhere. const POSTERS_TAG = 'dxm-poster-section' const POSTERS_SCRIPT = '/dx-maps/country-posters/country-posters.js' +// FRESH, NOT STALE: a returning reader's browser may keep /dx-maps/ files for hours (no cache rule is sent for them), so +// the script is asked for once with cache 'no-cache' before it is added (a 304 when unchanged; a changed file replaces the +// stale copy), then added at its PLAIN address, so DX Maps' gallery still recognises it and never runs it twice. +function revalidate(src: string): Promise { + return fetch(src, { cache: 'no-cache', credentials: 'same-origin' }) + .then((r) => { if (r.status === 200 && r.body) r.body.cancel() }) + .catch(() => {}) +} let postersLoad: Promise | null = null function loadPosters(prayBaseUrl: string): Promise { if (customElements.get(POSTERS_TAG)) return Promise.resolve() - postersLoad ??= new Promise((resolve, reject) => { + postersLoad ??= revalidate(POSTERS_SCRIPT).then(() => new Promise((resolve, reject) => { // the prayer API the posters read is set before the script runs if (prayBaseUrl) window.MAP_APP_API_URL = prayBaseUrl const script = document.createElement('script') @@ -13,7 +21,7 @@ function loadPosters(prayBaseUrl: string): Promise { script.onload = () => customElements.get(POSTERS_TAG) ? resolve() : reject(new Error('the posters bundle did not register')) script.onerror = () => reject(new Error('the posters bundle failed to load')) document.head.appendChild(script) - }).catch((err) => { + })).catch((err) => { postersLoad = null // a later opening tries again throw err }) diff --git a/app/pages/adopt/index.vue b/app/pages/adopt/index.vue index 81612b5a..40a680a5 100644 --- a/app/pages/adopt/index.vue +++ b/app/pages/adopt/index.vue @@ -48,7 +48,19 @@ const adoptMapConfig = computed(() => JSON.stringify({ // away again, so a later page tries afresh; the page then shows without the map. const MAP_TAG = 'dxm-simple-map' const MAP_SCRIPT = '/dx-maps/doxa-simple-map/doxa-simple-map.js' +// FRESH, NOT STALE: a returning reader's browser may keep /dx-maps/ files for hours (no cache rule is sent for them), so +// the script is asked for once with cache 'no-cache' before it is added (a 304 when unchanged; a changed file replaces the +// stale copy), then added at its PLAIN address, so DX Maps' gallery still recognises it and never runs it twice. +function revalidate(src: string): Promise { + return fetch(src, { cache: 'no-cache', credentials: 'same-origin' }) + .then((r) => { if (r.status === 200 && r.body) r.body.cancel() }) + .catch(() => {}) +} function loadMapBundle(): Promise { + if (customElements.get(MAP_TAG)) return Promise.resolve() + return revalidate(MAP_SCRIPT).then(() => addMapBundle()) +} +function addMapBundle(): Promise { if (customElements.get(MAP_TAG)) return Promise.resolve() const pending = document.querySelector('script[data-dxl-map="' + MAP_TAG + '"]') if (pending) { diff --git a/app/pages/deep-research/index.vue b/app/pages/deep-research/index.vue index c370ae6a..fcb0e0af 100644 --- a/app/pages/deep-research/index.vue +++ b/app/pages/deep-research/index.vue @@ -44,10 +44,18 @@ const homeHref = () => `https://doxa.life${localePath('/')}` // The gallery comes from this site's own /dx-maps/ copy; the script and every embed URL come from it. const GALLERY_SCRIPT = '/dx-maps/doxa-maps-spa/doxa-maps-spa.js' +// FRESH, NOT STALE: a returning reader's browser may keep /dx-maps/ files for hours (no cache rule is sent for them), so +// the script is asked for once with cache 'no-cache' before it is added (a 304 when unchanged; a changed file replaces the +// stale copy), then added at its PLAIN address, so DX Maps' gallery still recognises it and never runs it twice. +function revalidate(src: string): Promise { + return fetch(src, { cache: 'no-cache', credentials: 'same-origin' }) + .then((r) => { if (r.status === 200 && r.body) r.body.cancel() }) + .catch(() => {}) +} + useHead({ title: 'Deep Research', - meta: [{ name: 'robots', content: 'noindex' }], - script: [{ src: GALLERY_SCRIPT, defer: true, key: 'research-gallery' }] + meta: [{ name: 'robots', content: 'noindex' }] }) // With no layout, this page sets itself, as the public layout does for every other @@ -113,6 +121,15 @@ onMounted(() => { gallery.addEventListener('tab-change', onTabChange) gallery.addEventListener('doxa-language-request', onLanguageRequest) host.value?.appendChild(gallery) + // its script, revalidated (above) and then added at its plain address, once per page load + if (!customElements.get('doxa-maps-spa') && !document.querySelector(`script[src="${GALLERY_SCRIPT}"]`)) { + revalidate(GALLERY_SCRIPT).then(() => { + if (document.querySelector(`script[src="${GALLERY_SCRIPT}"]`)) return + const script = document.createElement('script') + script.src = GALLERY_SCRIPT + document.head.appendChild(script) + }) + } }) watch(locale, (lang) => { diff --git a/app/pages/index.vue b/app/pages/index.vue index 3522d8a4..ebfdbe37 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -70,7 +70,19 @@ const homeMapConfig = computed(() => JSON.stringify({ // away again, so a later page tries afresh; the page then shows without the map. const MAP_TAG = 'dxm-simple-map' const MAP_SCRIPT = '/dx-maps/doxa-simple-map/doxa-simple-map.js' +// FRESH, NOT STALE: a returning reader's browser may keep /dx-maps/ files for hours (no cache rule is sent for them), so +// the script is asked for once with cache 'no-cache' before it is added (a 304 when unchanged; a changed file replaces the +// stale copy), then added at its PLAIN address, so DX Maps' gallery still recognises it and never runs it twice. +function revalidate(src: string): Promise { + return fetch(src, { cache: 'no-cache', credentials: 'same-origin' }) + .then((r) => { if (r.status === 200 && r.body) r.body.cancel() }) + .catch(() => {}) +} function loadMapBundle(): Promise { + if (customElements.get(MAP_TAG)) return Promise.resolve() + return revalidate(MAP_SCRIPT).then(() => addMapBundle()) +} +function addMapBundle(): Promise { if (customElements.get(MAP_TAG)) return Promise.resolve() const pending = document.querySelector('script[data-dxl-map="' + MAP_TAG + '"]') if (pending) { diff --git a/app/pages/posterverse/index.vue b/app/pages/posterverse/index.vue index 0d585fcf..8dc50b4e 100644 --- a/app/pages/posterverse/index.vue +++ b/app/pages/posterverse/index.vue @@ -43,10 +43,18 @@ const homeHref = () => `https://doxa.life${localePath('/')}` // The shell comes from this site's own /dx-maps/ copy; the script and every embed URL come from it. const POSTERVERSE_SCRIPT = '/dx-maps/posterverse/posterverse.js' +// FRESH, NOT STALE: a returning reader's browser may keep /dx-maps/ files for hours (no cache rule is sent for them), so +// the script is asked for once with cache 'no-cache' before it is added (a 304 when unchanged; a changed file replaces the +// stale copy), then added at its PLAIN address, so DX Maps' gallery still recognises it and never runs it twice. +function revalidate(src: string): Promise { + return fetch(src, { cache: 'no-cache', credentials: 'same-origin' }) + .then((r) => { if (r.status === 200 && r.body) r.body.cancel() }) + .catch(() => {}) +} + useHead({ title: 'Posterverse', - meta: [{ name: 'robots', content: 'noindex' }], - script: [{ src: POSTERVERSE_SCRIPT, defer: true, key: 'posterverse' }] + meta: [{ name: 'robots', content: 'noindex' }] }) // With no layout, this page sets itself, as the public layout does for every other @@ -110,6 +118,15 @@ onMounted(() => { shell.addEventListener('tab-change', onTabChange) shell.addEventListener('doxa-language-request', onLanguageRequest) host.value?.appendChild(shell) + // its script, revalidated (above) and then added at its plain address, once per page load + if (!customElements.get('posterverse-app') && !document.querySelector(`script[src="${POSTERVERSE_SCRIPT}"]`)) { + revalidate(POSTERVERSE_SCRIPT).then(() => { + if (document.querySelector(`script[src="${POSTERVERSE_SCRIPT}"]`)) return + const script = document.createElement('script') + script.src = POSTERVERSE_SCRIPT + document.head.appendChild(script) + }) + } }) watch(locale, (lang) => { diff --git a/app/pages/pray.vue b/app/pages/pray.vue index 040a0498..5efcfb3e 100644 --- a/app/pages/pray.vue +++ b/app/pages/pray.vue @@ -45,7 +45,19 @@ const prayMapConfig = computed(() => JSON.stringify({ // away again, so a later page tries afresh; the page then shows without the map. const MAP_TAG = 'dxm-simple-map' const MAP_SCRIPT = '/dx-maps/doxa-simple-map/doxa-simple-map.js' +// FRESH, NOT STALE: a returning reader's browser may keep /dx-maps/ files for hours (no cache rule is sent for them), so +// the script is asked for once with cache 'no-cache' before it is added (a 304 when unchanged; a changed file replaces the +// stale copy), then added at its PLAIN address, so DX Maps' gallery still recognises it and never runs it twice. +function revalidate(src: string): Promise { + return fetch(src, { cache: 'no-cache', credentials: 'same-origin' }) + .then((r) => { if (r.status === 200 && r.body) r.body.cancel() }) + .catch(() => {}) +} function loadMapBundle(): Promise { + if (customElements.get(MAP_TAG)) return Promise.resolve() + return revalidate(MAP_SCRIPT).then(() => addMapBundle()) +} +function addMapBundle(): Promise { if (customElements.get(MAP_TAG)) return Promise.resolve() const pending = document.querySelector('script[data-dxl-map="' + MAP_TAG + '"]') if (pending) { diff --git a/app/pages/research/index.vue b/app/pages/research/index.vue index 6964d428..50c06fbf 100644 --- a/app/pages/research/index.vue +++ b/app/pages/research/index.vue @@ -60,10 +60,20 @@ const researchMapSlot = ref(null) let unlockHeight: (() => void) | null = null onMounted(() => { if (researchMapSlot.value) unlockHeight = lockPhoneHeight(researchMapSlot.value) }) onBeforeUnmount(() => unlockHeight?.()) +// FRESH, NOT STALE: a returning reader's browser may keep /dx-maps/ files for hours (no cache rule is sent for them), so +// the script is asked for once with cache 'no-cache' before it is added (a 304 when unchanged; a changed file replaces the +// stale copy), then added at its PLAIN address, so DX Maps' gallery still recognises it and never runs it twice. +function revalidate(src: string): Promise { + return fetch(src, { cache: 'no-cache', credentials: 'same-origin' }) + .then((r) => { if (r.status === 200 && r.body) r.body.cancel() }) + .catch(() => {}) +} // The prayer API the map reads is set before its script runs, and the script is added once per visit. -onMounted(() => { +onMounted(async () => { if (runtimeConfig.public.prayBaseUrl) window.MAP_APP_API_URL = runtimeConfig.public.prayBaseUrl as string const src = '/dx-maps/doxa-research-map/doxa-research-map.js' + if (customElements.get('doxa-research-map') || document.querySelector(`script[src="${src}"]`)) return + await revalidate(src) if (!document.querySelector(`script[src="${src}"]`)) { const script = document.createElement('script') script.src = src