temp keyboard handling
This commit is contained in:
@@ -2,13 +2,14 @@
|
|||||||
* DiscoverPage component - Main discover/browse interface for PodTUI
|
* DiscoverPage component - Main discover/browse interface for PodTUI
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createSignal, For, Show } from "solid-js";
|
import { createSignal, For, Show, onMount } from "solid-js";
|
||||||
import { useKeyboard } from "@opentui/solid";
|
import { useKeyboard } from "@opentui/solid";
|
||||||
import { useDiscoverStore, DISCOVER_CATEGORIES } from "@/stores/discover";
|
import { useDiscoverStore, DISCOVER_CATEGORIES } from "@/stores/discover";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
import { PodcastCard } from "./PodcastCard";
|
import { PodcastCard } from "./PodcastCard";
|
||||||
import { SelectableBox, SelectableText } from "@/components/Selectable";
|
import { SelectableBox, SelectableText } from "@/components/Selectable";
|
||||||
import { useNavigation } from "@/context/NavigationContext";
|
import { useNavigation } from "@/context/NavigationContext";
|
||||||
|
import { KeybindProvider, useKeybinds } from "@/context/KeybindContext";
|
||||||
|
|
||||||
enum DiscoverPagePaneType {
|
enum DiscoverPagePaneType {
|
||||||
CATEGORIES = 1,
|
CATEGORIES = 1,
|
||||||
@@ -21,6 +22,36 @@ export function DiscoverPage() {
|
|||||||
const [showIndex, setShowIndex] = createSignal(0);
|
const [showIndex, setShowIndex] = createSignal(0);
|
||||||
const [categoryIndex, setCategoryIndex] = createSignal(0);
|
const [categoryIndex, setCategoryIndex] = createSignal(0);
|
||||||
const nav = useNavigation();
|
const nav = useNavigation();
|
||||||
|
const keybind = useKeybinds();
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
useKeyboard(
|
||||||
|
(keyEvent: any) => {
|
||||||
|
const isDown = keybind.match("down", keyEvent);
|
||||||
|
const isUp = keybind.match("up", keyEvent);
|
||||||
|
const isEnter = keyEvent.name === "Enter" || keyEvent.name === " ";
|
||||||
|
const isSpace = keyEvent.name === " ";
|
||||||
|
|
||||||
|
if (isEnter || isSpace) {
|
||||||
|
const filteredPodcasts = discoverStore.filteredPodcasts();
|
||||||
|
if (filteredPodcasts.length > 0 && showIndex() < filteredPodcasts.length) {
|
||||||
|
setShowIndex(showIndex() + 1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredPodcasts = discoverStore.filteredPodcasts();
|
||||||
|
if (filteredPodcasts.length === 0) return;
|
||||||
|
|
||||||
|
if (isDown && showIndex() < filteredPodcasts.length - 1) {
|
||||||
|
setShowIndex(showIndex() + 1);
|
||||||
|
} else if (isUp && showIndex() > 0) {
|
||||||
|
setShowIndex(showIndex() - 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ release: false },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const handleCategorySelect = (categoryId: string) => {
|
const handleCategorySelect = (categoryId: string) => {
|
||||||
discoverStore.setSelectedCategory(categoryId);
|
discoverStore.setSelectedCategory(categoryId);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Reverse chronological order, grouped by date
|
* Reverse chronological order, grouped by date
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createSignal, For, Show } from "solid-js";
|
import { createSignal, For, Show, onMount } from "solid-js";
|
||||||
import { useFeedStore } from "@/stores/feed";
|
import { useFeedStore } from "@/stores/feed";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import type { Episode } from "@/types/episode";
|
import type { Episode } from "@/types/episode";
|
||||||
@@ -13,6 +13,8 @@ import { SelectableBox, SelectableText } from "@/components/Selectable";
|
|||||||
import { useNavigation } from "@/context/NavigationContext";
|
import { useNavigation } from "@/context/NavigationContext";
|
||||||
import { LoadingIndicator } from "@/components/LoadingIndicator";
|
import { LoadingIndicator } from "@/components/LoadingIndicator";
|
||||||
import { TABS } from "@/utils/navigation";
|
import { TABS } from "@/utils/navigation";
|
||||||
|
import { useKeyboard } from "@opentui/solid";
|
||||||
|
import { KeybindProvider, useKeybinds } from "@/context/KeybindContext";
|
||||||
|
|
||||||
enum FeedPaneType {
|
enum FeedPaneType {
|
||||||
FEED = 1,
|
FEED = 1,
|
||||||
@@ -29,6 +31,37 @@ export function FeedPage() {
|
|||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
const allEpisodes = () => feedStore.getAllEpisodesChronological();
|
const allEpisodes = () => feedStore.getAllEpisodesChronological();
|
||||||
|
const keybind = useKeybinds();
|
||||||
|
const [focusedIndex, setFocusedIndex] = createSignal(0);
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
useKeyboard(
|
||||||
|
(keyEvent: any) => {
|
||||||
|
const isDown = keybind.match("down", keyEvent);
|
||||||
|
const isUp = keybind.match("up", keyEvent);
|
||||||
|
const isEnter = keyEvent.name === "Enter" || keyEvent.name === " ";
|
||||||
|
const isSpace = keyEvent.name === " ";
|
||||||
|
|
||||||
|
if (isEnter || isSpace) {
|
||||||
|
const episodes = allEpisodes();
|
||||||
|
if (episodes.length > 0 && episodes[focusedIndex()]) {
|
||||||
|
setSelectedEpisodeID(episodes[focusedIndex()].episode.id);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const episodes = allEpisodes();
|
||||||
|
if (episodes.length === 0) return;
|
||||||
|
|
||||||
|
if (isDown && focusedIndex() < episodes.length - 1) {
|
||||||
|
setFocusedIndex(focusedIndex() + 1);
|
||||||
|
} else if (isUp && focusedIndex() > 0) {
|
||||||
|
setFocusedIndex(focusedIndex() - 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ release: false },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const formatDate = (date: Date): string => {
|
const formatDate = (date: Date): string => {
|
||||||
return format(date, "MMM d, yyyy");
|
return format(date, "MMM d, yyyy");
|
||||||
@@ -105,6 +138,13 @@ export function FeedPage() {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
const isFocused = () => {
|
||||||
|
const episodes = allEpisodes();
|
||||||
|
const currentIndex = episodes.findIndex(
|
||||||
|
(e: any) => e.episode.id === item.episode.id,
|
||||||
|
);
|
||||||
|
return currentIndex === focusedIndex();
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<SelectableBox
|
<SelectableBox
|
||||||
selected={isSelected}
|
selected={isSelected}
|
||||||
@@ -115,7 +155,11 @@ export function FeedPage() {
|
|||||||
paddingTop={0}
|
paddingTop={0}
|
||||||
paddingBottom={0}
|
paddingBottom={0}
|
||||||
onMouseDown={() => {
|
onMouseDown={() => {
|
||||||
// Selection is handled by App's keyboard navigation
|
setSelectedEpisodeID(item.episode.id);
|
||||||
|
const episodes = allEpisodes();
|
||||||
|
setFocusedIndex(
|
||||||
|
episodes.findIndex((e: any) => e.episode.id === item.episode.id),
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectableText selected={isSelected} primary>
|
<SelectableText selected={isSelected} primary>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
* Right panel: episodes for the selected show
|
* Right panel: episodes for the selected show
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createSignal, For, Show, createMemo, createEffect } from "solid-js";
|
import { createSignal, For, Show, createMemo, createEffect, onMount } from "solid-js";
|
||||||
|
import { useKeyboard } from "@opentui/solid";
|
||||||
import { useFeedStore } from "@/stores/feed";
|
import { useFeedStore } from "@/stores/feed";
|
||||||
import { useDownloadStore } from "@/stores/download";
|
import { useDownloadStore } from "@/stores/download";
|
||||||
import { DownloadStatus } from "@/types/episode";
|
import { DownloadStatus } from "@/types/episode";
|
||||||
@@ -32,6 +33,50 @@ export function MyShowsPage() {
|
|||||||
const mutedColor = () => theme.muted || theme.text;
|
const mutedColor = () => theme.muted || theme.text;
|
||||||
const nav = useNavigation();
|
const nav = useNavigation();
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
useKeyboard(
|
||||||
|
(keyEvent: any) => {
|
||||||
|
const isDown =
|
||||||
|
keyEvent.key === "j" || keyEvent.key === "ArrowDown";
|
||||||
|
const isUp =
|
||||||
|
keyEvent.key === "k" || keyEvent.key === "ArrowUp";
|
||||||
|
const isSelect =
|
||||||
|
keyEvent.key === "Enter" || keyEvent.key === " ";
|
||||||
|
|
||||||
|
const shows = feedStore.getFilteredFeeds();
|
||||||
|
const episodesList = episodes();
|
||||||
|
const selected = selectedShow();
|
||||||
|
|
||||||
|
if (isSelect) {
|
||||||
|
if (shows.length > 0 && showIndex() < shows.length) {
|
||||||
|
setShowIndex(showIndex() + 1);
|
||||||
|
}
|
||||||
|
if (episodesList.length > 0 && episodeIndex() < episodesList.length) {
|
||||||
|
setEpisodeIndex(episodeIndex() + 1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shows.length > 0) {
|
||||||
|
if (isDown && showIndex() < shows.length - 1) {
|
||||||
|
setShowIndex(showIndex() + 1);
|
||||||
|
} else if (isUp && showIndex() > 0) {
|
||||||
|
setShowIndex(showIndex() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (episodesList.length > 0) {
|
||||||
|
if (isDown && episodeIndex() < episodesList.length - 1) {
|
||||||
|
setEpisodeIndex(episodeIndex() + 1);
|
||||||
|
} else if (isUp && episodeIndex() > 0) {
|
||||||
|
setEpisodeIndex(episodeIndex() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ release: false },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
/** Threshold: load more when within this many items of the end */
|
/** Threshold: load more when within this many items of the end */
|
||||||
const LOAD_MORE_THRESHOLD = 5;
|
const LOAD_MORE_THRESHOLD = 5;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { useAudio } from "@/hooks/useAudio";
|
|||||||
import { useAppStore } from "@/stores/app";
|
import { useAppStore } from "@/stores/app";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
import { useNavigation } from "@/context/NavigationContext";
|
import { useNavigation } from "@/context/NavigationContext";
|
||||||
|
import { useKeyboard } from "@opentui/solid";
|
||||||
|
import { onMount } from "solid-js";
|
||||||
|
|
||||||
enum PlayerPaneType {
|
enum PlayerPaneType {
|
||||||
PLAYER = 1,
|
PLAYER = 1,
|
||||||
@@ -15,6 +17,32 @@ export function PlayerPage() {
|
|||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const nav = useNavigation();
|
const nav = useNavigation();
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
useKeyboard(
|
||||||
|
(keyEvent: any) => {
|
||||||
|
const isNext = keyEvent.key === "l" || keyEvent.key === "ArrowRight";
|
||||||
|
const isPrev = keyEvent.key === "h" || keyEvent.key === "ArrowLeft";
|
||||||
|
const isPlayPause = keyEvent.key === " " || keyEvent.key === "Enter";
|
||||||
|
|
||||||
|
if (isPlayPause) {
|
||||||
|
audio.togglePlayback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNext) {
|
||||||
|
audio.seek(audio.currentEpisode()?.duration ?? 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPrev) {
|
||||||
|
audio.seek(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ release: false },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const progressPercent = () => {
|
const progressPercent = () => {
|
||||||
const d = audio.duration();
|
const d = audio.duration();
|
||||||
if (d <= 0) return 0;
|
if (d <= 0) return 0;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* SearchPage component - Main search interface for PodTUI
|
* SearchPage component - Main search interface for PodTUI
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createSignal, createEffect, Show } from "solid-js";
|
import { createSignal, createEffect, Show, onMount } from "solid-js";
|
||||||
import { useKeyboard } from "@opentui/solid";
|
import { useKeyboard } from "@opentui/solid";
|
||||||
import { useSearchStore } from "@/stores/search";
|
import { useSearchStore } from "@/stores/search";
|
||||||
import { SearchResults } from "./SearchResults";
|
import { SearchResults } from "./SearchResults";
|
||||||
@@ -27,6 +27,37 @@ export function SearchPage() {
|
|||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const nav = useNavigation();
|
const nav = useNavigation();
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
useKeyboard(
|
||||||
|
(keyEvent: any) => {
|
||||||
|
const isDown =
|
||||||
|
keyEvent.key === "j" || keyEvent.key === "ArrowDown";
|
||||||
|
const isUp =
|
||||||
|
keyEvent.key === "k" || keyEvent.key === "ArrowUp";
|
||||||
|
const isSelect =
|
||||||
|
keyEvent.key === "Enter" || keyEvent.key === " ";
|
||||||
|
|
||||||
|
if (isSelect) {
|
||||||
|
const results = searchStore.results();
|
||||||
|
if (results.length > 0 && resultIndex() < results.length) {
|
||||||
|
setResultIndex(resultIndex() + 1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = searchStore.results();
|
||||||
|
if (results.length === 0) return;
|
||||||
|
|
||||||
|
if (isDown && resultIndex() < results.length - 1) {
|
||||||
|
setResultIndex(resultIndex() + 1);
|
||||||
|
} else if (isUp && resultIndex() > 0) {
|
||||||
|
setResultIndex(resultIndex() - 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ release: false },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
const query = inputValue().trim();
|
const query = inputValue().trim();
|
||||||
if (query) {
|
if (query) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createSignal, For } from "solid-js";
|
import { createSignal, For, onMount } from "solid-js";
|
||||||
import { useKeyboard } from "@opentui/solid";
|
import { useKeyboard } from "@opentui/solid";
|
||||||
import { SourceManager } from "./SourceManager";
|
import { SourceManager } from "./SourceManager";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
@@ -33,6 +33,31 @@ export function SettingsPage() {
|
|||||||
return nav.activeDepth() === depth;
|
return nav.activeDepth() === depth;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
useKeyboard(
|
||||||
|
(keyEvent: any) => {
|
||||||
|
const isDown =
|
||||||
|
keyEvent.key === "j" || keyEvent.key === "ArrowDown";
|
||||||
|
const isUp =
|
||||||
|
keyEvent.key === "k" || keyEvent.key === "ArrowUp";
|
||||||
|
const isSelect =
|
||||||
|
keyEvent.key === "Enter" || keyEvent.key === " ";
|
||||||
|
|
||||||
|
if (isSelect) {
|
||||||
|
nav.setActiveDepth((nav.activeDepth() % SettingsPaneCount) + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextDepth = isDown
|
||||||
|
? (nav.activeDepth() % SettingsPaneCount) + 1
|
||||||
|
: (nav.activeDepth() - 2 + SettingsPaneCount) % SettingsPaneCount + 1;
|
||||||
|
|
||||||
|
nav.setActiveDepth(nextDepth);
|
||||||
|
},
|
||||||
|
{ release: false },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<box flexDirection="column" gap={1} height="100%" width="100%">
|
<box flexDirection="column" gap={1} height="100%" width="100%">
|
||||||
<box flexDirection="row" gap={1}>
|
<box flexDirection="row" gap={1}>
|
||||||
|
|||||||
Reference in New Issue
Block a user