diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 378fad3..7c50a6c 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -12,39 +12,52 @@ let feed = $state([]); let userPrefs = $state(P.blank()); let showBoundaries = $state(false); + let showHistory = $state(false); let loading = $state(true); let error = $state(''); + // Session memory (this tab only — cleared on close, no account): + // every article shown is remembered so Replace never recycles something + // already seen, and so a replaced-away story stays recoverable in History. + const seenIds = new Set(); + let history = $state([]); + function remember(items) { + for (const a of items || []) { + if (a && !seenIds.has(a.id)) { + seenIds.add(a.id); + history.unshift(a); + } + } + } + let filtersOn = $derived(P.active(userPrefs)); let current = $derived(moods.find((m) => m.key === selected)); - let viewLabel = $derived(current?.label ?? 'Today'); + let viewLabel = $derived(selected === 'today' ? 'Highlights from Today' : (current?.label ?? '')); let viewSubtitle = $derived( - selected === 'today' - ? brief?.brief_date - ? `Five good things · ${brief.brief_date}` - : 'Five good things today' - : (current?.description ?? '') + selected === 'today' ? (brief?.brief_date ?? '') : (current?.description ?? '') ); - function feedUrl(moodKey, limit) { - const mood = moods.find((m) => m.key === moodKey); - const merged = P.merge(userPrefs, mood?.filter ?? {}); - const q = P.param(merged); - return `/api/feed?limit=${limit}${q ? '&' + q : ''}`; - } - function briefUrl() { - const q = P.param(userPrefs); - return `/api/brief?limit=5${q ? '&' + q : ''}`; + function mergedParam() { + const merged = P.merge(userPrefs, current?.filter ?? {}); + return P.param(merged); } async function select(key) { selected = key; error = ''; try { - // Today = just the day's highlights. Other moods reveal that category only - // when chosen (categories live behind their selection, not on the home). - if (key === 'today') brief = await getJSON(briefUrl()); - else feed = (await getJSON(feedUrl(key, 24))).items; + // Today = the day's highlights (hero + six). Other moods reveal that + // category only when chosen. + if (key === 'today') { + const q = P.param(userPrefs); + brief = await getJSON(`/api/brief?limit=7${q ? '&' + q : ''}`); + remember(brief.items); + } else { + const mood = moods.find((m) => m.key === key); + const q = P.param(P.merge(userPrefs, mood?.filter ?? {})); + feed = (await getJSON(`/api/feed?limit=24${q ? '&' + q : ''}`)).items; + remember(feed); + } } catch (e) { error = 'Something went quiet — could not reach the feed.'; } @@ -70,11 +83,11 @@ async function replaceArticle(article) { const list = selected === 'today' ? brief?.items : feed; if (!list) return; - const shown = list.map((a) => a.id).join(','); const isHero = selected === 'today' && list[0]?.id === article.id; - const merged = P.merge(userPrefs, current?.filter ?? {}); - const q = P.param(merged); - const url = `/api/replacement?exclude=${shown}&avoid_paywall=true${isHero ? '&gentle=true' : ''}${q ? '&' + q : ''}`; + // Exclude everything seen this session, so Replace never cycles back. + const exclude = Array.from(seenIds).join(','); + const q = mergedParam(); + const url = `/api/replacement?exclude=${exclude}&avoid_paywall=true${isHero ? '&gentle=true' : ''}${q ? '&' + q : ''}`; let repl; try { repl = await getJSON(url); @@ -83,9 +96,10 @@ return; } if (!repl) { - flash('Nothing else to swap in right now — try easing a boundary.'); + flash("That's everything fresh for now — nothing new to swap in."); return; } + remember([repl]); if (selected === 'today') { const i = brief.items.findIndex((a) => a.id === article.id); if (i >= 0) { @@ -118,7 +132,8 @@ {/if}
Everything you've seen this visit, including stories you swapped away. Kept on this device for this tab only — it clears when you close it. (Saved history & favorites come with sign-in, later.)
+ {#if history.length} +Nothing yet — your seen stories will appear here.
+ {/if} +{notice}
{/if} @@ -156,7 +193,7 @@✦ that's the good news for today ✦
{:else} -No brief yet today — try a calmer filter, or check back soon.
+No highlights yet today — try a calmer filter, or check back soon.
{/if} {:else if feed.length}