fix(feed): unblock UI during fetch-more and drop indicator label text

loadMoreEpisodesForFeed's hot-cache path ran fully synchronously (no
await between getFeed and setFeeds), so the isLoadingMore spinner never
painted and keyboard input froze through every feed in a loadMoreAllFeeds
batch. Add await yieldToUI() before the setFeeds so the renderer gets a
macrotask turn to paint and process input between feeds.

Drop the "Loading…/Refreshing…/Downloading N" label text from the global
activity indicator — render the braille spinner only, per request.
This commit is contained in:
2026-08-12 10:47:02 -04:00
parent ed75c2fff7
commit 0b4a551744
3 changed files with 12 additions and 28 deletions

View File

@@ -25,28 +25,10 @@ export function GlobalActivityIndicator() {
downloadStore.getActiveCount() + downloadStore.getQueue().length > 0 ||
activity.isActive();
/** Label priority: downloads in flight > latest tracked activity >
* generic loading (only reachable when an isLoading/isSearching flag is
* on but nothing else is). */
const label = () => {
const activeCount = downloadStore.getActiveCount();
const queueLength = downloadStore.getQueue().length;
if (activeCount + queueLength > 0) {
return `Downloading ${activeCount}${
queueLength > 0 ? ` +${queueLength} queued` : ""
}`;
}
if (activity.isActive()) {
const latest = activity.labels().at(-1);
return `${latest ?? "Loading"}`;
}
return "Loading…";
};
return (
<Show when={isActive()}>
<box position="absolute" top={0} right={0} paddingRight={1}>
<LoadingIndicator label={label()} />
<LoadingIndicator />
</box>
</Show>
);

View File

@@ -781,6 +781,13 @@ function createFeedStore() {
episodeLoadCount.set(feedId, newCount);
const episodes = cached.slice(0, newCount);
// Yield a real macrotask turn before the sync state update so the
// renderer paints the spinner and processes keyboard input before the
// (potentially large, per-feed in loadMoreAllFeeds) setFeeds + sort
// runs. Without this, the whole body executes in one microtask batch
// and the UI freezes through every feed in the batch.
await yieldToUI();
setFeeds((prev) => {
const updated = prev.map((f) =>
f.id === feedId ? { ...f, episodes } : f,