channel list

This commit is contained in:
2025-08-03 21:58:03 +02:00
parent 3e36f25582
commit 6a15a5de74
2 changed files with 45 additions and 32 deletions
+1
View File
@@ -5,6 +5,7 @@ use iradio::radio::{app::App, tui};
async fn main() -> Result<()> { async fn main() -> Result<()> {
color_eyre::install()?; color_eyre::install()?;
let mut terminal = tui::init()?; let mut terminal = tui::init()?;
terminal.clear()?;
let mut app = App::default(); let mut app = App::default();
app.load_stations().await?; app.load_stations().await?;
let app_result = app.run(&mut terminal); let app_result = app.run(&mut terminal);
+44 -32
View File
@@ -7,20 +7,33 @@ use radiobrowser::RadioBrowserAPI;
use ratatui::{ use ratatui::{
DefaultTerminal, Frame, DefaultTerminal, Frame,
layout::{Constraint, Layout}, layout::{Constraint, Layout},
style::Stylize,
widgets::{Block, Paragraph}, widgets::{Block, Paragraph},
}; };
use std::io::{self}; use std::io::{self};
use crate::radio::station::StationInfo; use crate::radio::station::StationInfo;
use ratatui::style::{Color, Modifier, Style};
use ratatui::widgets::{List, ListItem, ListState}; use ratatui::widgets::{List, ListItem, ListState};
#[derive(Debug, Default)] #[derive(Debug)]
pub struct App { pub struct App {
exit: bool, exit: bool,
stations: Vec<StationInfo>, stations: Vec<StationInfo>,
selected_index: usize, station_list_state: ListState,
scroll_offset: usize, }
impl Default for App {
fn default() -> Self {
Self {
exit: false,
stations: Vec::new(),
station_list_state: {
let mut state = ListState::default();
state.select(Some(0));
state
},
}
}
} }
impl App { impl App {
@@ -44,33 +57,36 @@ impl App {
Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(right); Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(right);
// Render the left block (e.g., station selection) // Render the left block (e.g., station selection)
let items: Vec<ListItem> = self let max_name_len = left.width.saturating_sub(4) as usize;
let station_items: Vec<ListItem> = self
.stations .stations
.iter() .iter()
.skip(self.scroll_offset) .map(|s| {
.take(15) // max visible let name = if s.name.len() > max_name_len {
.map(|s| ListItem::new(s.name.clone())) format!("{}...", &s.name[..max_name_len.saturating_sub(3)])
} else {
s.name.clone()
};
ListItem::new(name.trim().to_owned())
})
.collect(); .collect();
let mut list_state = ListState::default(); let stations_list = List::new(station_items)
list_state.select(Some(self.selected_index - self.scroll_offset)); .block(Block::bordered().title("Station Selection"))
.highlight_symbol(">>")
.highlight_style(ratatui::style::Style::default().reversed());
let list = List::new(items) frame.render_stateful_widget(stations_list, left, &mut self.station_list_state.clone());
.block(Block::bordered().title("Stations"))
.highlight_style(
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
)
.highlight_symbol("");
frame.render_stateful_widget(list, left, &mut list_state);
// frame.render_widget(Block::bordered().title("Station Selection"), left); // frame.render_widget(Block::bordered().title("Station Selection"), left);
// Render the top-right block (e.g., song history) // Render the top-right block (e.g., song history)
frame.render_widget(Block::bordered().title("Played Songs"), top_right); frame.render_widget(Block::bordered().title("Played Songs"), top_right);
let station = self.stations.get(self.selected_index); let station = self
.station_list_state
.selected()
.and_then(|i| self.stations.get(i));
let paragraph = Paragraph::new( let paragraph = Paragraph::new(
station station
.map(|s| format!("Now selected: {}\n{}", s.name, s.url)) .map(|s| format!("Now selected: {}\n{}", s.name, s.url))
@@ -98,19 +114,15 @@ impl App {
match key_event.code { match key_event.code {
KeyCode::Char('q') => self.exit(), KeyCode::Char('q') => self.exit(),
KeyCode::Down | KeyCode::Char('j') => { KeyCode::Down | KeyCode::Char('j') => {
if self.selected_index + 1 < self.stations.len() { if let Some(i) = self.station_list_state.selected() {
self.selected_index += 1; let next = (i + 1).min(self.stations.len().saturating_sub(1));
if self.selected_index >= self.scroll_offset + 15 { self.station_list_state.select(Some(next));
self.scroll_offset += 1;
}
} }
} }
KeyCode::Up | KeyCode::Char('k') => { KeyCode::Up | KeyCode::Char('k') => {
if self.selected_index > 0 { if let Some(i) = self.station_list_state.selected() {
self.selected_index -= 1; let prev = i.saturating_sub(1);
if self.selected_index < self.scroll_offset { self.station_list_state.select(Some(prev));
self.scroll_offset = self.scroll_offset.saturating_sub(1);
}
} }
} }
_ => {} _ => {}
@@ -127,7 +139,7 @@ impl App {
.map_err(|e| eyre!("Failed to create RadioBrowserAPI: {e}"))?; .map_err(|e| eyre!("Failed to create RadioBrowserAPI: {e}"))?;
let stations = api let stations = api
.get_stations() .get_stations()
.country("DE") .country("Germany")
.limit("50") .limit("50")
.send() .send()
.await .await