Loading indicators: the braille spinner now carries a contextual label (Refreshing…, Fetching…, Loading more…, Discovering…, Searching…) and is shown in every loading state that previously rendered nothing — Discover results, Search results fallback, and the empty Feed list. Feed pagination: a focusable "[Fetch More]" row at the bottom of the flat feed list advances every feed's loaded window by 50 episodes via the new loadMoreAllFeeds/hasMoreAcrossAll store API. Behavior is a setting (Fetch More: manual|auto, default manual) persisted in config.json; auto fetches when focus reaches the bottom row. The button row is excluded from episode focus so no episode is double-highlighted while it is active.
31 lines
963 B
TypeScript
31 lines
963 B
TypeScript
import { createSignal, createMemo, Show, onCleanup } from "solid-js";
|
|
import { useTheme } from "@/context/ThemeContext";
|
|
|
|
const spinnerChars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
|
|
/**
|
|
* Animated braille spinner with an optional label (e.g. "Refreshing…").
|
|
* The spinner is rendered in the theme primary color; the label in muted.
|
|
*/
|
|
export function LoadingIndicator(props: { label?: string }) {
|
|
const { theme } = useTheme();
|
|
const [index, setIndex] = createSignal(0);
|
|
|
|
const interval = setInterval(() => {
|
|
setIndex((i) => (i + 1) % spinnerChars.length);
|
|
}, 65);
|
|
|
|
onCleanup(() => clearInterval(interval));
|
|
|
|
const currentChar = createMemo(() => spinnerChars[index()]);
|
|
|
|
return (
|
|
<box flexDirection="row" gap={1} alignItems="flex-start">
|
|
<text fg={theme.primary} content={currentChar()} />
|
|
<Show when={props.label}>
|
|
<text fg={theme.muted || theme.text} content={props.label} />
|
|
</Show>
|
|
</box>
|
|
);
|
|
}
|