feat(feed): bound feed lifecycle to 30-day window with nonblocking refresh

Persisted feeds keep only episodes from the last 30 days (plus completed
downloads); older episodes live in volatile memory and survive refreshes
via union merge, with per-feed in-memory caches capped at 500. Refresh
batches run at FETCH_CONCURRENCY=4 with per-feed incremental apply (no
Promise.all barrier), config.json writes are trailing-edge debounced
(250ms, immediate flushPendingSave for unsubscribes), and cold
fetch-more refetches abort at FETCH_TIMEOUT_MS. A shared activity store
powers a global top-right indicator covering refresh, fetch-more,
subscribe, search, and downloads.

Also includes the in-flight incremental RSS parsing (chunked with
event-loop yields) and refresh spinner work this tree already carried.
This commit is contained in:
2026-08-12 10:13:20 -04:00
parent e09ae15e32
commit deac6081ca
23 changed files with 2226 additions and 169 deletions

View File

@@ -236,7 +236,7 @@ function FeedPage() {
<Show
when={episodes().length > 0}
fallback={
<box padding={1}>
<box padding={1} alignItems="center">
<Show
when={feedStore.isLoadingFeeds()}
fallback={
@@ -355,7 +355,7 @@ function FeedPage() {
</box>
</Show>
<Show when={feedStore.isLoadingFeeds()}>
<box paddingLeft={2} paddingTop={1}>
<box alignItems="center" paddingTop={1}>
<LoadingIndicator label="Refreshing…" />
</box>
</Show>

View File

@@ -133,6 +133,7 @@ export function PlayerPage() {
currentLabel="Player"
panes={2}
focused={isActive}
currentBorder={["left"]}
/>
);
}