diff --git a/src/App.tsx b/src/App.tsx
index 629a5f2..a5a24d9 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -113,47 +113,48 @@ export function App() {
)}
>
-
- {DEBUG && (
-
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
- █
-
- )}
-
-
- {LayerGraph[nav.activeTab]()}
+
+ {DEBUG && (
+
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+ █
+
+ )}
+
+
+ {LayerGraph[nav.activeTab]()}
+
);
diff --git a/src/components/LoadingIndicator.tsx b/src/components/LoadingIndicator.tsx
index 85a0a8d..ad4a4a4 100644
--- a/src/components/LoadingIndicator.tsx
+++ b/src/components/LoadingIndicator.tsx
@@ -1,36 +1,24 @@
-/**
- * Loading indicator component
- * Displays an animated sliding bar at the top of the screen
- */
-
-import { For } from "solid-js";
+import { createSignal, createMemo, onCleanup } from "solid-js";
import { useTheme } from "@/context/ThemeContext";
-interface LoadingIndicatorProps {
- isLoading: boolean;
-}
+const spinnerChars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
-export function LoadingIndicator(props: LoadingIndicatorProps) {
+//TODO: Watch for actual loading state (fetching feeds)
+export function LoadingIndicator() {
const { theme } = useTheme();
+ const [index, setIndex] = createSignal(0);
- if (!props.isLoading) return null;
+ const interval = setInterval(() => {
+ setIndex((i) => (i + 1) % spinnerChars.length);
+ }, 65);
+
+ onCleanup(() => clearInterval(interval));
+
+ const currentChar = createMemo(() => spinnerChars[index()]);
return (
-
-
- {(_, index) => (
-
- )}
-
+
+
);
}
diff --git a/src/pages/Feed/FeedPage.tsx b/src/pages/Feed/FeedPage.tsx
index f27203d..4f1520d 100644
--- a/src/pages/Feed/FeedPage.tsx
+++ b/src/pages/Feed/FeedPage.tsx
@@ -1,18 +1,17 @@
/**
* FeedPage - Shows latest episodes across all subscribed shows
- * Reverse chronological order, like an inbox/timeline
+ * Reverse chronological order, grouped by date
*/
import { createSignal, For, Show } from "solid-js";
import { useFeedStore } from "@/stores/feed";
-import { useKeyboard } from "@opentui/solid";
import { format } from "date-fns";
import type { Episode } from "@/types/episode";
import type { Feed } from "@/types/feed";
import { useTheme } from "@/context/ThemeContext";
import { SelectableBox, SelectableText } from "@/components/Selectable";
-import { se } from "date-fns/locale";
import { useNavigation } from "@/context/NavigationContext";
+import { LoadingIndicator } from "@/components/LoadingIndicator";
enum FeedPaneType {
FEED = 1,
@@ -31,22 +30,25 @@ export function FeedPage() {
const allEpisodes = () => feedStore.getAllEpisodesChronological();
- const formatDate = (date: Date): string => {
- return format(date, "MMM d, yyyy");
- };
-
const paginatedEpisodes = () => {
const episodes = allEpisodes();
return episodes.slice(0, loadedEpisodesCount());
};
- const episodesByDate = () => {
- const groups: Record = {};
- const sortedEpisodes = paginatedEpisodes();
+ const formatDate = (date: Date): string => {
+ return format(date, "MMM d, yyyy");
+ };
- for (const episode of sortedEpisodes) {
- const dateKey = formatDate(new Date(episode.episode.pubDate));
- groups[dateKey] = episode;
+ const groupEpisodesByDate = () => {
+ const groups: Record> = {};
+ const episodes = paginatedEpisodes();
+
+ for (const item of episodes) {
+ const dateKey = formatDate(new Date(item.episode.pubDate));
+ if (!groups[dateKey]) {
+ groups[dateKey] = [];
+ }
+ groups[dateKey].push(item);
}
return groups;
@@ -59,12 +61,6 @@ export function FeedPage() {
return `${mins}m`;
};
- const handleRefresh = async () => {
- setIsRefreshing(true);
- await feedStore.refreshAllFeeds();
- setIsRefreshing(false);
- };
-
const { theme } = useTheme();
return (
-
- b.localeCompare(a),
+ b.localeCompare(a))}>
+ {([date, episodes]) => (
+
+ false} primary>
+ {date}
+
+
+ {(item) => (
+ false}
+ flexDirection="column"
+ gap={0}
+ paddingLeft={1}
+ paddingRight={1}
+ paddingTop={0}
+ paddingBottom={0}
+ onMouseDown={() => {
+ // Selection is handled by App's keyboard navigation
+ }}
+ >
+ false} primary>
+ {item.episode.title}
+
+
+ false} primary>
+ {item.feed.podcast.title}
+
+ false} tertiary>
+ {formatDuration(item.episode.duration)}
+
+
+
+ )}
+
+
)}
- >
- {([date, episode], groupIndex) => {
- const selected = () => groupIndex() === 1; // TODO: Manage selections locally
- return (
- <>
-
- false} primary>
- {date}
-
-
- {
- // Selection is handled by App's keyboard navigation
- }}
- >
-
- {selected() ? ">" : " "}
-
-
- {episode.episode.title}
-
-
-
- {episode.feed.podcast.title}
-
-
- {formatDate(episode.episode.pubDate)}
-
-
- {formatDuration(episode.episode.duration)}
-
-
-
- >
- );
- }}
{/* Loading indicator */}
- Loading more episodes...
+
diff --git a/src/pages/MyShows/MyShowsPage.tsx b/src/pages/MyShows/MyShowsPage.tsx
index 5bed1d3..032055b 100644
--- a/src/pages/MyShows/MyShowsPage.tsx
+++ b/src/pages/MyShows/MyShowsPage.tsx
@@ -12,6 +12,7 @@ import { format } from "date-fns";
import { useTheme } from "@/context/ThemeContext";
import { useAudioNavStore, AudioSource } from "@/stores/audio-nav";
import { useNavigation } from "@/context/NavigationContext";
+import { LoadingIndicator } from "@/components/LoadingIndicator";
enum MyShowsPaneType {
SHOWS = 1,
@@ -245,7 +246,7 @@ export function MyShowsPage() {
- Loading more episodes...
+