nonworking indicator, sort broken

This commit is contained in:
2026-02-19 17:52:57 -05:00
parent 1c65c85d02
commit d1e1dd28b4
6 changed files with 116 additions and 41 deletions

View File

@@ -2,6 +2,7 @@ import { createMemo, ErrorBoundary, Accessor } from "solid-js";
import { useKeyboard, useSelectionHandler } from "@opentui/solid";
import { TabNavigation } from "./components/TabNavigation";
import { CodeValidation } from "@/components/CodeValidation";
import { LoadingIndicator } from "@/components/LoadingIndicator";
import { useAuthStore } from "@/stores/auth";
import { useFeedStore } from "@/stores/feed";
import { useAudio } from "@/hooks/useAudio";
@@ -89,10 +90,13 @@ export function App() {
audioSeekBackward: isSeekBackward,
quit: isQuit,
});
if (isCycle) {
}
// only handling top
// only handling top navigation here, cycle through tabs, just to high priority(player) all else to be handled in each tab
if (nav.activeDepth == 0) {
if (isCycle) {
nav.nextTab();
}
}
},
{ release: false },
);
@@ -109,6 +113,7 @@ export function App() {
</box>
)}
>
<LoadingIndicator isLoading={feedStore.isLoadingFeeds()} />
{DEBUG && (
<box flexDirection="row" width="100%" height={1}>
<text fg={theme.primary}></text>
@@ -149,7 +154,6 @@ export function App() {
onTabSelect={nav.setActiveTab}
/>
{LayerGraph[nav.activeTab]()}
{/** TODO: Contextual controls based on tab/depth**/}
</box>
</ErrorBoundary>
);

View File

@@ -0,0 +1,36 @@
/**
* Loading indicator component
* Displays an animated sliding bar at the top of the screen
*/
import { For } from "solid-js";
import { useTheme } from "@/context/ThemeContext";
interface LoadingIndicatorProps {
isLoading: boolean;
}
export function LoadingIndicator(props: LoadingIndicatorProps) {
const { theme } = useTheme();
if (!props.isLoading) return null;
return (
<box
flexDirection="row"
width="100%"
height={1}
backgroundColor={theme.background}
>
<For each={Array.from({ length: 10 })}>
{(_, index) => (
<box
width={2}
backgroundColor={theme.primary}
style={{ opacity: 0.1 + index() * 0.1 }}
/>
)}
</For>
</box>
);
}

View File

@@ -9,6 +9,7 @@
"inverse": ["shift"],
"leader": ":", // will not trigger while focused on input
"quit": ["<leader>q"],
"refresh": ["<leader>r"],
"audio-toggle": ["<leader>p"],
"audio-pause": [],
"audio-play": [],

View File

@@ -60,6 +60,7 @@ export const { use: useKeybinds, provider: KeybindProvider } =
inverse: [],
leader: "",
quit: [],
refresh: [],
"audio-toggle": [],
"audio-pause": [],
"audio-play": [],
@@ -95,9 +96,6 @@ export const { use: useKeybinds, provider: KeybindProvider } =
for (const key of keys) {
if (evt.name === key) return true;
if (evt.shift && key.toLowerCase() !== key) return false;
if (evt.ctrl && !key.toLowerCase().includes("ctrl")) return false;
if (evt.meta && !key.toLowerCase().includes("meta")) return false;
}
return false;
}

View File

@@ -1,14 +1,33 @@
import { createSignal } from "solid-js";
import { createSimpleContext } from "./helper";
import { TABS } from "../utils/navigation";
import { TABS, TabsCount } from "@/utils/navigation";
export const { use: useNavigation, provider: NavigationProvider } = createSimpleContext({
export const { use: useNavigation, provider: NavigationProvider } =
createSimpleContext({
name: "Navigation",
init: () => {
const [activeTab, setActiveTab] = createSignal<TABS>(TABS.FEED);
const [activeDepth, setActiveDepth] = createSignal(0);
const [inputFocused, setInputFocused] = createSignal(false);
//conveniences
const nextTab = () => {
if (activeTab() >= TabsCount) {
setActiveTab(1);
return;
}
setActiveTab(activeTab() + 1);
};
const prevTab = () => {
if (activeTab() <= 1) {
setActiveTab(TabsCount);
return;
}
setActiveTab(activeTab() - 1);
};
return {
get activeTab() {
return activeTab();
@@ -22,6 +41,8 @@ export const { use: useNavigation, provider: NavigationProvider } = createSimple
setActiveTab,
setActiveDepth,
setInputFocused,
nextTab,
prevTab,
};
},
});
});

View File

@@ -48,13 +48,6 @@ export function createFeedStore() {
const [sources, setSources] = createSignal<PodcastSource[]>([
...DEFAULT_SOURCES,
]);
(async () => {
const loadedFeeds = await loadFeedsFromFile();
if (loadedFeeds.length > 0) setFeeds(loadedFeeds);
const loadedSources = await loadSourcesFromFile<PodcastSource>();
if (loadedSources && loadedSources.length > 0) setSources(loadedSources);
})();
const [filter, setFilter] = createSignal<FeedFilter>({
visibility: "all",
sortBy: "updated" as FeedSortField,
@@ -62,6 +55,7 @@ export function createFeedStore() {
});
const [selectedFeedId, setSelectedFeedId] = createSignal<string | null>(null);
const [isLoadingMore, setIsLoadingMore] = createSignal(false);
const [isLoadingFeeds, setIsLoadingFeeds] = createSignal(false);
/** Get filtered and sorted feeds */
const getFilteredFeeds = (): Feed[] => {
@@ -148,6 +142,13 @@ export function createFeedStore() {
return allEpisodes;
};
/** Sort episodes in reverse chronological order (newest first) */
const sortEpisodesReverseChronological = (episodes: Episode[]): Episode[] => {
return [...episodes].sort(
(a, b) => b.pubDate.getTime() - a.pubDate.getTime(),
);
};
/** Fetch latest episodes from an RSS feed URL, caching all parsed episodes */
const fetchEpisodes = async (
feedUrl: string,
@@ -164,7 +165,7 @@ export function createFeedStore() {
if (!response.ok) return [];
const xml = await response.text();
const parsed = parseRSSFeed(xml, feedUrl);
const allEpisodes = parsed.episodes;
const allEpisodes = sortEpisodesReverseChronological(parsed.episodes);
// Cache all parsed episodes for pagination
if (feedId) {
@@ -264,12 +265,25 @@ export function createFeedStore() {
/** Refresh all feeds */
const refreshAllFeeds = async () => {
setIsLoadingFeeds(true);
try {
const currentFeeds = feeds();
for (const feed of currentFeeds) {
await refreshFeed(feed.id);
}
} finally {
setIsLoadingFeeds(false);
}
};
(async () => {
const loadedFeeds = await loadFeedsFromFile();
if (loadedFeeds.length > 0) setFeeds(loadedFeeds);
const loadedSources = await loadSourcesFromFile<PodcastSource>();
if (loadedSources && loadedSources.length > 0) setSources(loadedSources);
await refreshAllFeeds();
})();
/** Remove a feed */
const removeFeed = (feedId: string) => {
fullEpisodeCache.delete(feedId);
@@ -445,6 +459,7 @@ export function createFeedStore() {
getFeed,
getSelectedFeed,
hasMoreEpisodes,
isLoadingFeeds,
// Actions
setFilter,