feat(footer): let the reader check for new data

A reload can be served the browser's cached feed for minutes. The button
beside the data's age asks the server past that copy, swaps in a newer
feed without reloading, and says honestly what came back. Newer is
judged on events and sources, never the build stamp. The age now says
"just now" rather than "ended ago" when a fresh feed is a moment ahead
of the page's clock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-13 14:17:20 +02:00
1 parent bcbd3cc188
commit f7ba86e6e5
5 files changed
+240 -6

No files matched your search

+17 -1
View File
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { fetchFeed, type FeedState } from "./api.ts";
import { fetchFeed, refreshOutcome, type FeedState, type RefreshOutcome } from "./api.ts";
import { Controls } from "./components/Controls.tsx";
import { Dailies } from "./components/Dailies.tsx";
import { GameFocus } from "./components/GameFocus.tsx";
@@ -809,6 +809,22 @@ export function App() {
sources={state.feed.sources}
now={now}
hiddenGames={prefs.hiddenGames}
online={online}
onRefresh={async (): Promise<RefreshOutcome> => {
// The newest published feed, past the browser's copy. Swapped in only
// when it carries something new, so a check that found nothing leaves
// the page exactly as it was. Never reloads: the reader may be
// halfway through typing an event in.
const before = state.feed;
try {
const next = await fetchFeed(undefined, { fresh: true });
const outcome = refreshOutcome(before, next);
if (outcome === "updated") setState({ status: "ready", feed: next });
return outcome;
} catch (err) {
return refreshOutcome(before, err instanceof Error ? err : new Error(String(err)));
}
}}
/>
{lastIgnored !== null && (