fix(news): follow-up review of the fixes

Deleted World News API news is not restored by a refresh that fails; a
refund goes only to the day the read was counted in, and not to a request
that timed out; a cockpit brief from another place is dropped, not kept for
a language change; a feed list started over lets scrolling ask again.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
This commit is contained in:
Mazeyar Moeini FeizabadiandClaude Fable 5.2 committed 2026-11-10 21:34:58 +01:00
1 parent bb7e7ecad6
commit 671a256a6a
6 files changed
+102 -30

No files matched your search

+11 -3
View File
@@ -370,7 +370,13 @@ export function createKeyedNews({
(value) => ({ key, value, readAt: now() }),
(error) => {
logFailure(section, what, key, error);
return { ...prior, failedAt: now(), error };
// What is held now: news deleted meanwhile stays deleted.
const held = store.get(id);
return {
...(held?.key === key ? held : { key }),
failedAt: now(),
error,
};
},
)
.then((entry) => {
@@ -720,6 +726,7 @@ export function createKeyedNews({
throw new UpstreamError("World News API's reads for today spent", {
quota: true,
});
const { day } = budget;
try {
const reports = (
await fetchWorldNews({
@@ -735,8 +742,9 @@ export function createKeyedNews({
} catch (error) {
// Out of points: nothing more is asked today.
if (error.status === 402) budget.spent = true;
// Never reached (offline, timed out): no read of the day's.
if (error.unreached && budget.reads > 0) budget.reads -= 1;
// Never reached (offline): no read of the day it was counted in.
if (error.unreached && budget.day === day && budget.reads > 0)
budget.reads -= 1;
throw error;
}
},
+5 -2
View File
@@ -1229,8 +1229,11 @@ test('a World News API read that never reached it is not counted against the day
// Back online: the day's reads are all still there.
answers.worldnews = WORLD_NEWS;
assert.equal((await paris()).status, 200);
// An answer, even a failing one, is a read: two of them spend the day.
answers.worldnews = 500;
// An answer, even a failing one, is a read, and so is a request that
// timed out, which it may have read: two of them spend the day.
answers.worldnews = () => {
throw new DOMException('timed out', 'TimeoutError');
};
const berlin = () => ask('/feed', 'hotspot=all&at=52.52,13.4');
assert.deepEqual(await berlin(), UPSTREAM_ERROR);
clock.now += 2 * MINUTE;
+4 -4
View File
@@ -115,11 +115,11 @@ async function getText(
: AbortSignal.timeout(TIMEOUT_MS),
});
} catch (cause) {
// A request that timed out may have been read; one refused was not.
const timedOut = cause?.name === 'TimeoutError';
throw new UpstreamError(
cause?.name === 'TimeoutError'
? `${what} timed out`
: `${what} unreachable`,
{ unreached: true },
timedOut ? `${what} timed out` : `${what} unreachable`,
{ unreached: !timedOut },
);
}
if (!response.ok) {