use std::sync::Mutex; use std::sync::Arc; use crossterm::event::{Event, self, KeyCode}; use tokio; use ratatui::widgets::ListState; use crossbeam_channel::Receiver; use log::warn; use crate::db::DB; use crate::uis::history::HistoryState; use crate::uis::new_transaction::NewTransactionTabState; use crate::uis::navigation_frame::NavigationState; use crate::db::transaction::TransactionRecord; pub type AppResult = std::result::Result>; pub struct StatefulList { pub state: ListState, pub items: Vec, } impl StatefulList { pub fn with_items(items: Vec) -> StatefulList { StatefulList { state: ListState::default(), items, } } pub fn next(&mut self) { let i = match self.state.selected() { Some(i) => { if i >= self.items.len() - 1 { 0 } else { i + 1 } } None => 0, }; self.state.select(Some(i)); } pub fn previous(&mut self) { let i = match self.state.selected() { Some(i) => { if i == 0 { self.items.len() - 1 } else { i - 1 } } None => 0, }; self.state.select(Some(i)); } pub fn unselect(&mut self) { self.state.select(None); } } pub enum InputMode { Insert, Normal, } pub struct App<'a> { pub running: bool, pub states: States<'a>, pub input_mode: InputMode, pub db: Arc>, pub records: Arc>>, } impl<'a> App<'a> { pub fn new(db: DB, records: Arc>>, r: Receiver) -> App<'a> { App { running: true, states: States::new(r), input_mode: InputMode::Normal, db: Arc::new(tokio::sync::Mutex::new(db)), records, } } pub fn poll_events(&mut self) -> AppResult<()> { if let Event::Key(key) = event::read()? { // 'q' needs to be handled at the top level so it can't be // accidentally handed to a dead end if let KeyCode::Char('q') = key.code { if let Some(_) = self.states.nav_state.message.clone() { self.states.nav_state.message = None; } else { if let ActiveFrame::Navigation = self.states.active_frame { self.running = false; } else { self.states.active_frame = ActiveFrame::Navigation; } } } else if let KeyCode::Esc = key.code { if let Some(_) = self.states.nav_state.message.clone() { self.states.nav_state.message = None; } else { if let ActiveFrame::Navigation = self.states.active_frame { self.running = false; } else { self.states.active_frame = ActiveFrame::Navigation; } } } if let KeyCode::Char('r') = key.code { self.refresh(); } match self.states.active_frame { ActiveFrame::Navigation => { NavigationState::handle_event(key, self); } ActiveFrame::History => { HistoryState::handle_event(key, self); } ActiveFrame::NewTransaction => { NewTransactionTabState::handle_event(key, self); } } } return Ok(()) } pub fn refresh(&mut self) { let fut = Arc::clone(&self.db); tokio::spawn(async move { let res = fut.lock().await.get_all_records().await; match res { Ok(_) => {}, Err(e) => warn!("{}", e) } }); } } pub enum ActiveFrame { Navigation, NewTransaction, History, } pub struct States<'a> { pub nav_state: NavigationState<'a>, pub transactions: NewTransactionTabState<'a>, pub history: HistoryState, pub new_data: Receiver, pub active_frame: ActiveFrame, } impl<'a> States<'a> { pub fn new(r: Receiver) -> States<'a> { States { nav_state: NavigationState::new(), transactions: NewTransactionTabState::new(), history: HistoryState::new(), new_data: r, active_frame: ActiveFrame::Navigation, } } }