diff --git a/frontend/src/lib/components/NewsFeed.svelte b/frontend/src/lib/components/NewsFeed.svelte index bc7d81d..a1c8318 100644 --- a/frontend/src/lib/components/NewsFeed.svelte +++ b/frontend/src/lib/components/NewsFeed.svelte @@ -18,7 +18,7 @@ import { track } from '$lib/analytics.js'; import { pwa, installApp, dismissPwa } from '$lib/pwa.svelte.js'; import { ritualState, markBriefSeen } from '$lib/ritual.js'; - import { feedBase, parseView, viewUrl } from '$lib/feednav.js'; + import { feedBase, defaultView, parseView, viewUrl } from '$lib/feednav.js'; // Which top bar to wear: 'legacy' = the feed's own Header (the interim `/` mount, kept // unchanged), 'hub' = the shared editorial HubBar (the /news mount, part of the new family). @@ -36,8 +36,18 @@ // so `/` behaves exactly as today and `/news` is self-coherent. At cutover, `/` becomes // the hub and only `/news` renders the feed. function base() { return feedBase($page.url.pathname); } - function urlForView(key) { return viewUrl(base(), key); } - let selected = $derived(parseView($page.url)); + function urlForView(key) { return viewUrl(base(), key, defaultView(base())); } + let selected = $derived(parseView($page.url, defaultView(feedBase($page.url.pathname)))); + // Latest is local-first only on the frozen `/`; on /news it's the pure chronological + // firehose — the geo scope dial belongs to Highlights, not Latest (Codex). + let latestGeo = $derived(chrome !== 'hub'); + // Back is for genuine drill-ins. On /news suppress it for the top-level lanes (Latest is + // the default there); on the frozen `/` keep the original "anything but Highlights" rule. + let showBack = $derived( + chrome === 'hub' + ? (selected.startsWith('tag:') || selected.startsWith('source:') || selected === 'search') + : selected !== 'today' + ); let searchQuery = $derived(($page.url.searchParams.get('q') || '').trim()); let searchOpen = $state(false); let searchText = $state(''); @@ -69,7 +79,7 @@ // Scope dial: the reader's "emotional radius" — nearby | region | country | world. // Persisted; default nearby. 'world' = the global brief/feed (no geo lead). let homeScope = $state('nearby'); - const homeActive = () => selected === 'latest' && !!homeValue; + const homeActive = () => latestGeo && selected === 'latest' && !!homeValue; let showSignIn = $state(false); let showSaved = $state(false); // Saved flyout let loading = $state(true); @@ -299,8 +309,8 @@ } if (key === 'latest') { const q = P.param(prefs.data); - // Closer to Home lives on the all-news browse lane (Latest). - const homeq = homeValue ? `&home=${encodeURIComponent(homeValue)}` : ''; + // Closer to Home lives on Highlights now; Latest stays pure chronological on /news. + const homeq = latestGeo && homeValue ? `&home=${encodeURIComponent(homeValue)}` : ''; return `/api/feed?sort=latest&limit=${PAGE}&offset=${offset}${homeq}${q ? '&' + q : ''}${exq}`; } if (key === 'following') { @@ -338,7 +348,7 @@ let appNavDepth = 0; function goBack() { if (appNavDepth > 0 && typeof history !== 'undefined') history.back(); - else goto(urlForView('today')); // landed here directly → app-safe Highlights + else goto(urlForView(defaultView(base()))); // landed directly → the base's default view } // Load the data for a view. Called by afterNavigate (URL-driven: in-app goto, @@ -359,7 +369,7 @@ feed = items; feedNextOffset = resp.next_offset ?? null; // Home lane pages by the API's world cursor; other lanes by simple length. - feedDone = (key === 'latest' && homeValue) ? feedNextOffset == null : items.length < PAGE; + feedDone = (key === 'latest' && latestGeo && homeValue) ? feedNextOffset == null : items.length < PAGE; markDisplayed(feed); if (key.startsWith('source:') && items[0]) { sourceNames = { ...sourceNames, [key.slice(7)]: items[0].source }; @@ -687,11 +697,7 @@ {isFollowing(followTarget.kind, followTarget.value) ? '✓ Following' : 'Follow ' + followTarget.noun} {/if} - - {#if selected !== 'today'} + {#if showBack}