Fresh server data overrides a pinned brief; pin holds otherwise
Per the agreed model: the brief is server-authoritative and a client Replace is a soft override that yields when genuinely new data arrives. - build_daily_brief is now idempotent: if the composed selection is unchanged it leaves the brief (and its created_at) alone, so the timer's 15-min rebuilds are no-ops when no new data landed. - /api/brief exposes generated_at (the brief's created_at = a content-change stamp). The client pins its view against generated_at and keeps it across plain refreshes, but drops it and shows the fresh server brief when generated_at advances. Missed stories remain in the mood feeds. Tests: idempotent rebuild (no-op vs content change) — 93 total. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -76,14 +76,16 @@
|
||||
const ex = Array.from(dismissed).join(',');
|
||||
const fetched = await getJSON(`/api/brief?limit=7${q ? '&' + q : ''}${ex ? '&exclude=' + ex : ''}`);
|
||||
const view = P.loadJSON(BRIEF_VIEW_KEY, null);
|
||||
// On a plain (re)load, keep the reader's curated view for the same day — so
|
||||
// swaps and the hero hold steady. Re-fetch fresh on a new day's brief, or
|
||||
// when forced (e.g. a boundary changed and must be re-applied).
|
||||
if (!fresh && view && view.date === fetched.brief_date && Array.isArray(view.items) && view.items.length) {
|
||||
brief = { brief_date: view.date, title: fetched.title, items: view.items };
|
||||
// Keep the reader's curated view (swaps + hero) across plain refreshes — but
|
||||
// only while the server's brief is unchanged. When genuinely fresh data
|
||||
// arrives (generated_at advances), the server wins and the pin is dropped.
|
||||
const sameServerBrief =
|
||||
view && view.generated_at && fetched.generated_at && view.generated_at === fetched.generated_at;
|
||||
if (!fresh && sameServerBrief && Array.isArray(view.items) && view.items.length) {
|
||||
brief = { ...fetched, items: view.items };
|
||||
} else {
|
||||
brief = fetched;
|
||||
P.saveJSON(BRIEF_VIEW_KEY, { date: fetched.brief_date, items: fetched.items });
|
||||
P.saveJSON(BRIEF_VIEW_KEY, { generated_at: fetched.generated_at, items: fetched.items });
|
||||
}
|
||||
remember(brief.items);
|
||||
}
|
||||
@@ -155,8 +157,9 @@
|
||||
if (i >= 0) {
|
||||
brief.items[i] = repl;
|
||||
brief = { ...brief, items: [...brief.items] };
|
||||
// Pin the swap so this exact curated brief survives a refresh.
|
||||
P.saveJSON(BRIEF_VIEW_KEY, { date: brief.brief_date, items: brief.items });
|
||||
// Pin the swap against the current server brief; it holds until fresh
|
||||
// server data arrives (then the server's version takes over).
|
||||
P.saveJSON(BRIEF_VIEW_KEY, { generated_at: brief.generated_at, items: brief.items });
|
||||
}
|
||||
} else {
|
||||
const i = feed.findIndex((a) => a.id === article.id);
|
||||
|
||||
Reference in New Issue
Block a user