channel list
This commit is contained in:
@@ -5,6 +5,7 @@ use iradio::radio::{app::App, tui};
|
||||
async fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let mut terminal = tui::init()?;
|
||||
terminal.clear()?;
|
||||
let mut app = App::default();
|
||||
app.load_stations().await?;
|
||||
let app_result = app.run(&mut terminal);
|
||||
|
||||
+44
-32
@@ -7,20 +7,33 @@ use radiobrowser::RadioBrowserAPI;
|
||||
use ratatui::{
|
||||
DefaultTerminal, Frame,
|
||||
layout::{Constraint, Layout},
|
||||
style::Stylize,
|
||||
widgets::{Block, Paragraph},
|
||||
};
|
||||
use std::io::{self};
|
||||
|
||||
use crate::radio::station::StationInfo;
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::widgets::{List, ListItem, ListState};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug)]
|
||||
pub struct App {
|
||||
exit: bool,
|
||||
stations: Vec<StationInfo>,
|
||||
selected_index: usize,
|
||||
scroll_offset: usize,
|
||||
station_list_state: ListState,
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -44,33 +57,36 @@ impl App {
|
||||
Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(right);
|
||||
|
||||
// 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
|
||||
.iter()
|
||||
.skip(self.scroll_offset)
|
||||
.take(15) // max visible
|
||||
.map(|s| ListItem::new(s.name.clone()))
|
||||
.map(|s| {
|
||||
let name = if s.name.len() > max_name_len {
|
||||
format!("{}...", &s.name[..max_name_len.saturating_sub(3)])
|
||||
} else {
|
||||
s.name.clone()
|
||||
};
|
||||
ListItem::new(name.trim().to_owned())
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut list_state = ListState::default();
|
||||
list_state.select(Some(self.selected_index - self.scroll_offset));
|
||||
let stations_list = List::new(station_items)
|
||||
.block(Block::bordered().title("Station Selection"))
|
||||
.highlight_symbol(">>")
|
||||
.highlight_style(ratatui::style::Style::default().reversed());
|
||||
|
||||
let list = List::new(items)
|
||||
.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_stateful_widget(stations_list, left, &mut self.station_list_state.clone());
|
||||
// frame.render_widget(Block::bordered().title("Station Selection"), left);
|
||||
|
||||
// Render the top-right block (e.g., song history)
|
||||
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(
|
||||
station
|
||||
.map(|s| format!("Now selected: {}\n{}", s.name, s.url))
|
||||
@@ -98,19 +114,15 @@ impl App {
|
||||
match key_event.code {
|
||||
KeyCode::Char('q') => self.exit(),
|
||||
KeyCode::Down | KeyCode::Char('j') => {
|
||||
if self.selected_index + 1 < self.stations.len() {
|
||||
self.selected_index += 1;
|
||||
if self.selected_index >= self.scroll_offset + 15 {
|
||||
self.scroll_offset += 1;
|
||||
}
|
||||
if let Some(i) = self.station_list_state.selected() {
|
||||
let next = (i + 1).min(self.stations.len().saturating_sub(1));
|
||||
self.station_list_state.select(Some(next));
|
||||
}
|
||||
}
|
||||
KeyCode::Up | KeyCode::Char('k') => {
|
||||
if self.selected_index > 0 {
|
||||
self.selected_index -= 1;
|
||||
if self.selected_index < self.scroll_offset {
|
||||
self.scroll_offset = self.scroll_offset.saturating_sub(1);
|
||||
}
|
||||
if let Some(i) = self.station_list_state.selected() {
|
||||
let prev = i.saturating_sub(1);
|
||||
self.station_list_state.select(Some(prev));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -127,7 +139,7 @@ impl App {
|
||||
.map_err(|e| eyre!("Failed to create RadioBrowserAPI: {e}"))?;
|
||||
let stations = api
|
||||
.get_stations()
|
||||
.country("DE")
|
||||
.country("Germany")
|
||||
.limit("50")
|
||||
.send()
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user