/** * DiscoverPage component - Main discover/browse interface for PodTUI */ import { createSignal } from "solid-js"; import { useKeyboard } from "@opentui/solid"; import { useDiscoverStore, DISCOVER_CATEGORIES } from "@/stores/discover"; import { CategoryFilter } from "./CategoryFilter"; import { TrendingShows } from "./TrendingShows"; type DiscoverPageProps = { focused: boolean; onExit?: () => void; }; type FocusArea = "categories" | "shows"; export function DiscoverPage(props: DiscoverPageProps) { const discoverStore = useDiscoverStore(); const [focusArea, setFocusArea] = createSignal("shows"); const [showIndex, setShowIndex] = createSignal(0); const [categoryIndex, setCategoryIndex] = createSignal(0); // Keyboard navigation useKeyboard((key) => { if (!props.focused) return; const area = focusArea(); // Tab switches focus between categories and shows if (key.name === "tab") { if (key.shift) { setFocusArea((a) => (a === "categories" ? "shows" : "categories")); } else { setFocusArea((a) => (a === "categories" ? "shows" : "categories")); } return; } if ( (key.name === "return" || key.name === "enter") && area === "categories" ) { setFocusArea("shows"); return; } // Category navigation if (area === "categories") { if (key.name === "left" || key.name === "h") { const nextIndex = Math.max(0, categoryIndex() - 1); setCategoryIndex(nextIndex); const cat = DISCOVER_CATEGORIES[nextIndex]; if (cat) discoverStore.setSelectedCategory(cat.id); setShowIndex(0); return; } if (key.name === "right" || key.name === "l") { const nextIndex = Math.min( DISCOVER_CATEGORIES.length - 1, categoryIndex() + 1, ); setCategoryIndex(nextIndex); const cat = DISCOVER_CATEGORIES[nextIndex]; if (cat) discoverStore.setSelectedCategory(cat.id); setShowIndex(0); return; } if (key.name === "return" || key.name === "enter") { // Select category and move to shows setFocusArea("shows"); return; } if (key.name === "down" || key.name === "j") { setFocusArea("shows"); return; } } // Shows navigation if (area === "shows") { const shows = discoverStore.filteredPodcasts(); if (key.name === "down" || key.name === "j") { if (shows.length === 0) return; setShowIndex((i) => Math.min(i + 1, shows.length - 1)); return; } if (key.name === "up" || key.name === "k") { if (shows.length === 0) { setFocusArea("categories"); return; } const newIndex = showIndex() - 1; if (newIndex < 0) { setFocusArea("categories"); } else { setShowIndex(newIndex); } return; } if (key.name === "return" || key.name === "enter") { // Subscribe/unsubscribe const podcast = shows[showIndex()]; if (podcast) { discoverStore.toggleSubscription(podcast.id); } return; } } if (key.name === "escape") { if (area === "shows") { setFocusArea("categories"); key.stopPropagation(); } else { props.onExit?.(); } return; } // Refresh with 'r' if (key.name === "r") { discoverStore.refresh(); return; } }); const handleCategorySelect = (categoryId: string) => { discoverStore.setSelectedCategory(categoryId); const index = DISCOVER_CATEGORIES.findIndex((c) => c.id === categoryId); if (index >= 0) setCategoryIndex(index); setShowIndex(0); }; const handleShowSelect = (index: number) => { setShowIndex(index); setFocusArea("shows"); }; const handleSubscribe = (podcast: { id: string }) => { discoverStore.toggleSubscription(podcast.id); }; return ( {/* Header */} Discover Podcasts {discoverStore.filteredPodcasts().length} shows discoverStore.refresh()}> [R] Refresh {/* Category Filter */} Categories: {/* Trending Shows */} Trending in{" "} {DISCOVER_CATEGORIES.find( (c) => c.id === discoverStore.selectedCategory(), )?.name ?? "All"} {/* Footer Hints */} [Tab] Switch focus [j/k] Navigate [Enter] Subscribe [Esc] Up [R] Refresh ); }