channel list
This commit is contained in:
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user