diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index fd25d75..b11cd05 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -236,9 +236,13 @@ if (source) sourceNames = { ...sourceNames, [source.id]: source.name }; navigate(key); } + // How many forward in-app navigations deep we are from the landing entry, so + // the in-page Back stays INSIDE the app even when someone deep-links in from + // email/social/an article (history.length can't tell us that). + let appNavDepth = 0; function goBack() { - if (typeof history !== 'undefined' && history.length > 1) history.back(); - else goto(urlForView('today')); + if (appNavDepth > 0 && typeof history !== 'undefined') history.back(); + else goto(urlForView('today')); // landed here directly → app-safe Highlights } // Load the data for a view. Called by afterNavigate (URL-driven: in-app goto, @@ -251,6 +255,7 @@ try { if (key === 'today') { await loadToday(fresh); + if (seq !== loadSeq) return; // a newer navigation superseded this one } else { const items = (await getJSON(feedUrl(key, 0))).items; if (seq !== loadSeq) return; @@ -264,13 +269,17 @@ } catch (e) { if (seq === loadSeq) error = 'Something went quiet — could not reach the feed.'; } - if (typeof window !== 'undefined') window.scrollTo({ top: 0, behavior: 'smooth' }); + if (seq === loadSeq && typeof window !== 'undefined') window.scrollTo({ top: 0, behavior: 'smooth' }); } // Every URL change (in-app goto, browser back/forward, link) reloads the view. // The initial 'enter' is handled by onMount, after its data deps are fetched. afterNavigate((nav) => { if (nav.type === 'enter') return; + // Forward in-app nav deepens the stack; back/forward (popstate) unwinds it. + // Clamped at 0, so an out-of-app landing keeps Back app-safe (→ Highlights). + if (nav.type === 'goto' || nav.type === 'link') appNavDepth += 1; + else if (nav.type === 'popstate') appNavDepth = Math.max(0, appNavDepth - 1); loadView(selected); });