From 3338e56553c9512e4c62d5804dab1e6b5638dc1b Mon Sep 17 00:00:00 2001 From: David Montero Date: Tue, 11 Aug 2026 20:40:46 +0200 Subject: [PATCH] fix(news): pro builds wait for the overlay source before fetching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The announcement fetch now fires 250ms after editor mount — early enough to race the dynamically imported pro overlay chunk that registers the real news source. Losing the race silently fell back to the OSS self-host proxy, which on the cloud deployment returns [] (its hairpin fetch of its own public feed does not survive the round trip), so cold-cache visitors — every incognito window — got no announcement at all. getNextNews() in a pro build now waits, bounded at 8s, for registerNewsSource() before picking a source; OSS builds resolve the gate immediately and behave exactly as before. --- frontend/src/lib/newsSource.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/frontend/src/lib/newsSource.ts b/frontend/src/lib/newsSource.ts index e93b34d3..9097e17d 100644 --- a/frontend/src/lib/newsSource.ts +++ b/frontend/src/lib/newsSource.ts @@ -32,9 +32,30 @@ export interface NewsPost { type NewsSource = () => Promise; let _source: NewsSource | null = null; +let _sourceRegistered: (() => void) | null = null; + +// In a pro build the real source arrives via registerNewsSource() from a +// DYNAMICALLY IMPORTED overlay chunk, and the announcement fetch fires a +// mere 250ms after editor mount — early enough to race that import. If +// the race is lost the OSS fallback runs instead, which on the cloud +// deployment queries its own (empty) self-host proxy and silently shows +// nothing. So: in pro builds getNextNews() waits — bounded — for the +// registration before picking a source. OSS builds resolve immediately +// and never wait. +const PRO_BUILD = Boolean(import.meta.env.VITE_PRO_BUILD); +const _sourceReady: Promise = PRO_BUILD + ? new Promise((resolve) => { + _sourceRegistered = resolve; + }) + : Promise.resolve(); +/** How long a pro build waits for the overlay chunk before degrading to + * the OSS source. Generous: a lost overlay means no announcement at all + * on the cloud, while the wait only delays a modal nobody is blocked on. */ +const SOURCE_WAIT_MS = 8000; export function registerNewsSource(source: NewsSource): void { _source = source; + _sourceRegistered?.(); } const SEEN_KEY = 'velxio_news_seen'; @@ -91,6 +112,12 @@ async function ossFeedNext(): Promise { */ export async function getNextNews(): Promise { try { + if (PRO_BUILD && !_source) { + await Promise.race([ + _sourceReady, + new Promise((resolve) => setTimeout(resolve, SOURCE_WAIT_MS)), + ]); + } return _source ? await _source() : await ossFeedNext(); } catch (err) { // eslint-disable-next-line no-console