diff --git a/src/App.tsx b/src/App.tsx index 985478b..6494ea0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,9 @@ import { createSignal, createMemo, ErrorBoundary, Accessor } from "solid-js"; import { useSelectionHandler } from "@opentui/solid"; import { TabNavigation } from "./components/TabNavigation"; -import { FeedPage } from "@/tabs/Feed/FeedPage"; -import { MyShowsPage } from "@/tabs/MyShows/MyShowsPage"; -import { LoginScreen } from "@/tabs/Settings/LoginScreen"; + import { CodeValidation } from "@/components/CodeValidation"; -import { OAuthPlaceholder } from "@/tabs/Settings/OAuthPlaceholder"; -import { SyncProfile } from "@/tabs/Settings/SyncProfile"; -import { SearchPage } from "@/tabs/Search/SearchPage"; -import { DiscoverPage } from "@/tabs/Discover/DiscoverPage"; -import { SettingsScreen } from "@/tabs/Settings/SettingsScreen"; + import { useAuthStore } from "@/stores/auth"; import { useFeedStore } from "@/stores/feed"; import { useAudio } from "@/hooks/useAudio"; @@ -21,8 +15,7 @@ import { useToast } from "@/ui/toast"; import { useRenderer } from "@opentui/solid"; import type { AuthScreen } from "@/types/auth"; import type { Episode } from "@/types/episode"; -import { DIRECTION } from "./types/navigation"; -import { LayerGraph, TABS } from "./utils/navigation"; +import { DIRECTION, LayerGraph, TABS } from "./utils/navigation"; import { useTheme } from "./context/ThemeContext"; export interface PageProps { @@ -119,6 +112,7 @@ export function App() { > {LayerGraph[activeTab()]({ depth: activeDepth })} + {/**TODO: Contextual controls based on tab/depth**/} ); diff --git a/src/pages/Feed/FeedPage.tsx b/src/pages/Feed/FeedPage.tsx index b6e2c9e..255b037 100644 --- a/src/pages/Feed/FeedPage.tsx +++ b/src/pages/Feed/FeedPage.tsx @@ -11,6 +11,9 @@ import type { Feed } from "@/types/feed"; import { useTheme } from "@/context/ThemeContext"; import { PageProps } from "@/App"; +enum FeedPaneType { + FEED = 1, +} export const FeedPaneCount = 1; export function FeedPage(props: PageProps) { @@ -60,7 +63,7 @@ export function FeedPage(props: PageProps) { } > {/**TODO: figure out wtf to do here **/} - + {(item, index) => ( ("shows"); const [showIndex, setShowIndex] = createSignal(0); const [episodeIndex, setEpisodeIndex] = createSignal(0); const [isRefreshing, setIsRefreshing] = createSignal(false); @@ -128,8 +127,8 @@ export function MyShowsPage(props: PageProps) { setEpisodeIndex(0); }; - return { - showsPanel: () => ( + return ( + Refreshing... @@ -144,7 +143,10 @@ export function MyShowsPage(props: PageProps) { } > - + {(feed, index) => ( - ), - - episodesPanel: () => ( {(episode, index) => ( @@ -252,9 +251,6 @@ export function MyShowsPage(props: PageProps) { - ), - - focusPane, - selectedShow, - }; + + ); } diff --git a/src/pages/Player/PlayerPage.tsx b/src/pages/Player/PlayerPage.tsx index 5815faa..96b9d95 100644 --- a/src/pages/Player/PlayerPage.tsx +++ b/src/pages/Player/PlayerPage.tsx @@ -1,9 +1,15 @@ +import { PageProps } from "@/App"; import { PlaybackControls } from "./PlaybackControls"; import { RealtimeWaveform } from "./RealtimeWaveform"; import { useAudio } from "@/hooks/useAudio"; import { useAppStore } from "@/stores/app"; -export function PlayerPage() { +enum PlayerPaneType { + PLAYER = 1, +} +export const PlayerPaneCount = 1; + +export function PlayerPage(props: PageProps) { const audio = useAudio(); const progressPercent = () => { @@ -63,11 +69,6 @@ export function PlayerPage() { onSpeedChange={(s: number) => audio.setSpeed(s)} onVolumeChange={(v: number) => audio.setVolume(v)} /> - - - Space play/pause | Left/Right seek 10s | Up/Down volume | S speed | Esc - back - ); } diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index eeeecd7..f5d0efd 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -8,35 +8,35 @@ import { useSearchStore } from "@/stores/search"; import { SearchResults } from "./SearchResults"; import { SearchHistory } from "./SearchHistory"; import type { SearchResult } from "@/types/source"; +import { PageProps } from "@/App"; +import { MyShowsPage } from "../MyShows/MyShowsPage"; -type SearchPageProps = { - focused: boolean; - onSubscribe?: (result: SearchResult) => void; - onInputFocusChange?: (focused: boolean) => void; - onExit?: () => void; -}; +enum SearchPaneType { + INPUT = 1, + RESULTS = 2, + HISTORY = 3, +} +export const SearchPaneCount = 3; -type FocusArea = "input" | "results" | "history"; - -export function SearchPage(props: SearchPageProps) { +export function SearchPage(props: PageProps) { const searchStore = useSearchStore(); - const [focusArea, setFocusArea] = createSignal("input"); const [inputValue, setInputValue] = createSignal(""); const [resultIndex, setResultIndex] = createSignal(0); const [historyIndex, setHistoryIndex] = createSignal(0); // Keep parent informed about input focus state - createEffect(() => { - const isInputFocused = props.focused && focusArea() === "input"; - props.onInputFocusChange?.(isInputFocused); - }); + // TODO: have a global input focused prop in useKeyboard hook + //createEffect(() => { + //const isInputFocused = props.focused && focusArea() === "input"; + //props.onInputFocusChange?.(isInputFocused); + //}); const handleSearch = async () => { const query = inputValue().trim(); if (query) { await searchStore.search(query); if (searchStore.results().length > 0) { - setFocusArea("results"); + //setFocusArea("results"); //TODO: move level setResultIndex(0); } } @@ -46,120 +46,16 @@ export function SearchPage(props: SearchPageProps) { setInputValue(query); await searchStore.search(query); if (searchStore.results().length > 0) { - setFocusArea("results"); + //setFocusArea("results"); //TODO: move level setResultIndex(0); } }; const handleResultSelect = (result: SearchResult) => { - props.onSubscribe?.(result); + //props.onSubscribe?.(result); searchStore.markSubscribed(result.podcast.id); }; - // Keyboard navigation - useKeyboard((key) => { - if (!props.focused) return; - - const area = focusArea(); - - // Enter to search from input - if (key.name === "return" && area === "input") { - handleSearch(); - return; - } - - // Tab to cycle focus areas - if (key.name === "tab" && !key.shift) { - if (area === "input") { - if (searchStore.results().length > 0) { - setFocusArea("results"); - } else if (searchStore.history().length > 0) { - setFocusArea("history"); - } - } else if (area === "results") { - if (searchStore.history().length > 0) { - setFocusArea("history"); - } else { - setFocusArea("input"); - } - } else { - setFocusArea("input"); - } - return; - } - - if (key.name === "tab" && key.shift) { - if (area === "input") { - if (searchStore.history().length > 0) { - setFocusArea("history"); - } else if (searchStore.results().length > 0) { - setFocusArea("results"); - } - } else if (area === "history") { - if (searchStore.results().length > 0) { - setFocusArea("results"); - } else { - setFocusArea("input"); - } - } else { - setFocusArea("input"); - } - return; - } - - // Up/Down for results and history - if (area === "results") { - const results = searchStore.results(); - if (key.name === "down" || key.name === "j") { - setResultIndex((i) => Math.min(i + 1, results.length - 1)); - return; - } - if (key.name === "up" || key.name === "k") { - setResultIndex((i) => Math.max(i - 1, 0)); - return; - } - if (key.name === "return" || key.name === "enter") { - const result = results[resultIndex()]; - if (result) handleResultSelect(result); - return; - } - } - - if (area === "history") { - const history = searchStore.history(); - if (key.name === "down" || key.name === "j") { - setHistoryIndex((i) => Math.min(i + 1, history.length - 1)); - return; - } - if (key.name === "up" || key.name === "k") { - setHistoryIndex((i) => Math.max(i - 1, 0)); - return; - } - if (key.name === "return" || key.name === "enter") { - const query = history[historyIndex()]; - if (query) handleHistorySelect(query); - return; - } - } - - // Escape goes back to input or up one level - if (key.name === "escape") { - if (area === "input") { - props.onExit?.(); - } else { - setFocusArea("input"); - key.stopPropagation(); - } - return; - } - - // "/" focuses search input - if (key.name === "/" && area !== "input") { - setFocusArea("input"); - return; - } - }); - return ( {/* Search Header */} @@ -177,7 +73,7 @@ export function SearchPage(props: SearchPageProps) { setInputValue(value); }} placeholder="Enter podcast name, topic, or author..." - focused={props.focused && focusArea() === "input"} + focused={props.depth() === SearchPaneType.INPUT} width={50} /> - + Results ({searchStore.results().length}) @@ -224,7 +122,7 @@ export function SearchPage(props: SearchPageProps) { - + History - - {/* Footer Hints */} - - [Tab] Switch focus - [/] Focus search - [Enter] Select - [Esc] Up - ); } diff --git a/src/pages/Settings/SettingsPage.tsx b/src/pages/Settings/SettingsPage.tsx index d57160f..3d5240b 100644 --- a/src/pages/Settings/SettingsPage.tsx +++ b/src/pages/Settings/SettingsPage.tsx @@ -5,65 +5,33 @@ import { useTheme } from "@/context/ThemeContext"; import { PreferencesPanel } from "./PreferencesPanel"; import { SyncPanel } from "./SyncPanel"; import { VisualizerSettings } from "./VisualizerSettings"; +import { PageProps } from "@/App"; -type SettingsScreenProps = { - accountLabel: string; - accountStatus: "signed-in" | "signed-out"; - onOpenAccount?: () => void; - onExit?: () => void; -}; +enum SettingsPaneType { + SYNC = 1, + SOURCES = 2, + PREFERENCES = 3, + VISUALIZER = 4, + ACCOUNT = 5, +} +export const SettingsPaneCount = 5; -type SectionId = "sync" | "sources" | "preferences" | "visualizer" | "account"; - -const SECTIONS: Array<{ id: SectionId; label: string }> = [ - { id: "sync", label: "Sync" }, - { id: "sources", label: "Sources" }, - { id: "preferences", label: "Preferences" }, - { id: "visualizer", label: "Visualizer" }, - { id: "account", label: "Account" }, +const SECTIONS: Array<{ id: SettingsPaneType; label: string }> = [ + { id: SettingsPaneType.SYNC, label: "Sync" }, + { id: SettingsPaneType.SOURCES, label: "Sources" }, + { id: SettingsPaneType.PREFERENCES, label: "Preferences" }, + { id: SettingsPaneType.VISUALIZER, label: "Visualizer" }, + { id: SettingsPaneType.ACCOUNT, label: "Account" }, ]; -export function SettingsPage(props: SettingsScreenProps) { +export function SettingsPage(props: PageProps) { const { theme } = useTheme(); - const [activeSection, setActiveSection] = createSignal("sync"); - - useKeyboard((key) => { - if (key.name === "escape") { - props.onExit?.(); - return; - } - - if (key.name === "tab") { - const idx = SECTIONS.findIndex((s) => s.id === activeSection()); - const next = key.shift - ? (idx - 1 + SECTIONS.length) % SECTIONS.length - : (idx + 1) % SECTIONS.length; - setActiveSection(SECTIONS[next].id); - return; - } - - if (key.name === "1") setActiveSection("sync"); - if (key.name === "2") setActiveSection("sources"); - if (key.name === "3") setActiveSection("preferences"); - if (key.name === "4") setActiveSection("visualizer"); - if (key.name === "5") setActiveSection("account"); - }); + const [activeSection, setActiveSection] = createSignal( + SettingsPaneType.SYNC, + ); return ( - - - Settings - - - [Tab] Switch section | 1-5 jump | Esc up - - - {(section, index) => ( @@ -88,33 +56,22 @@ export function SettingsPage(props: SettingsScreenProps) { - {activeSection() === "sync" && } - {activeSection() === "sources" && } - {activeSection() === "preferences" && } - {activeSection() === "visualizer" && } - {activeSection() === "account" && ( + {activeSection() === SettingsPaneType.SYNC && } + {activeSection() === SettingsPaneType.SOURCES && ( + + )} + {activeSection() === SettingsPaneType.PREFERENCES && ( + + )} + {activeSection() === SettingsPaneType.VISUALIZER && ( + + )} + {activeSection() === SettingsPaneType.ACCOUNT && ( Account - - Status: - - {props.accountLabel} - - - props.onOpenAccount?.()}> - [A] Manage Account - )} - - Enter to dive | Esc up ); } diff --git a/src/types/navigation.ts b/src/types/navigation.ts deleted file mode 100644 index 54ec36d..0000000 --- a/src/types/navigation.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum DIRECTION { - Increment, - Decrement, -} diff --git a/src/utils/navigation.ts b/src/utils/navigation.ts index 3170706..36b06cd 100644 --- a/src/utils/navigation.ts +++ b/src/utils/navigation.ts @@ -1,9 +1,14 @@ -import { DiscoverPage } from "@/pages/Discover/DiscoverPage"; +import { DiscoverPage, DiscoverPaneCount } from "@/pages/Discover/DiscoverPage"; import { FeedPage, FeedPaneCount } from "@/pages/Feed/FeedPage"; import { MyShowsPage, MyShowsPaneCount } from "@/pages/MyShows/MyShowsPage"; -import { PlayerPage } from "@/pages/Player/PlayerPage"; -import { SearchPage } from "@/pages/Search/SearchPage"; -import { SettingsPage } from "@/pages/Settings/SettingsPage"; +import { PlayerPage, PlayerPaneCount } from "@/pages/Player/PlayerPage"; +import { SearchPage, SearchPaneCount } from "@/pages/Search/SearchPage"; +import { SettingsPage, SettingsPaneCount } from "@/pages/Settings/SettingsPage"; + +export enum DIRECTION { + Increment, + Decrement, +} export enum TABS { FEED, @@ -28,5 +33,5 @@ export const LayerDepths = { [TABS.DISCOVER]: DiscoverPaneCount, [TABS.SEARCH]: SearchPaneCount, [TABS.PLAYER]: PlayerPaneCount, - [TABS.SETTINGS]: SettingPaneCount, + [TABS.SETTINGS]: SettingsPaneCount, };