use std::time::Duration; use crossterm::event::{self, Event as CrosstermEvent, KeyEvent}; use tokio::sync::mpsc; #[derive(Debug)] pub enum AppEvent { Key(KeyEvent), Tick, ApiResult(ApiResult), } #[derive(Debug)] pub enum ApiResult { MediaList(Vec), SearchResults(crate::client::SearchResponse), AllTags(Vec), Collections(Vec), ImportDone(crate::client::ImportResponse), ScanDone(Vec), AuditLog(Vec), Duplicates(Vec), DatabaseStats(crate::client::DatabaseStatsResponse), Statistics(crate::client::LibraryStatisticsResponse), ScheduledTasks(Vec), BooksList(Vec), BookSeries(Vec), BookAuthors(Vec), MediaUpdated, ReadingProgressUpdated, Error(String), } pub struct EventHandler { tx: mpsc::UnboundedSender, rx: mpsc::UnboundedReceiver, } impl EventHandler { pub fn new(tick_rate: Duration) -> Self { let (tx, rx) = mpsc::unbounded_channel(); let event_tx = tx.clone(); std::thread::spawn(move || { loop { match event::poll(tick_rate) { Ok(true) => { if let Ok(CrosstermEvent::Key(key)) = event::read() && event_tx.send(AppEvent::Key(key)).is_err() { break; } }, Ok(false) => { if event_tx.send(AppEvent::Tick).is_err() { break; } }, Err(e) => { tracing::warn!(error = %e, "event poll failed"); }, } } }); Self { tx, rx } } pub fn sender(&self) -> mpsc::UnboundedSender { self.tx.clone() } pub async fn next(&mut self) -> Option { self.rx.recv().await } }