use hac_core::{collection::Collection, command::Command};
use crate::event_pool::{Event, EventPool};
use crate::pages::{Eventful, Renderable};
use crate::screen_manager::ScreenManager;
use std::io::Stdout;
use ratatui::{backend::CrosstermBackend, Terminal};
use tokio::sync::mpsc;
pub struct App<'app> {
event_pool: EventPool,
terminal: Terminal<CrosstermBackend<Stdout>>,
should_quit: bool,
screen_manager: ScreenManager<'app>,
}
impl<'app> App<'app> {
pub fn new(
colors: &'app hac_colors::Colors,
collections: Vec<Collection>,
config: &'app hac_config::Config,
dry_run: bool,
) -> anyhow::Result<Self> {
let terminal = Terminal::new(CrosstermBackend::new(std::io::stdout()))?;
Ok(Self {
screen_manager: ScreenManager::new(
terminal.size()?,
colors,
collections,
config,
dry_run,
)?,
event_pool: EventPool::new(60f64, 30f64),
should_quit: false,
terminal,
})
}
pub async fn run(&mut self) -> anyhow::Result<()> {
let (command_tx, mut command_rx) = mpsc::unbounded_channel();
self.event_pool.start();
startup()?;
self.screen_manager
.register_command_handler(command_tx.clone())?;
loop {
{
while let Ok(command) = command_rx.try_recv() {
match command {
Command::Quit => self.should_quit = true,
_ => self.screen_manager.handle_command(command),
}
}
}
if let Some(event) = self.event_pool.next().await {
match event {
Event::Tick => self.screen_manager.handle_tick()?,
Event::Resize(new_size) => self.screen_manager.resize(new_size),
Event::Render => {
self.terminal.draw(|f| {
let result = self.screen_manager.draw(f, f.size());
if let Err(e) = result {
command_tx
.send(Command::Error(format!("Failed to draw: {:?}", e)))
.expect("failed to send command through channel");
}
})?;
}
event => {
if let Some(command) =
self.screen_manager.handle_event(Some(event.clone()))?
{
command_tx
.send(command)
.expect("failed to send command through channel")
}
}
};
}
if self.should_quit {
break;
}
}
shutdown()?;
Ok(())
}
}
fn startup() -> anyhow::Result<()> {
crossterm::terminal::enable_raw_mode()?;
crossterm::execute!(std::io::stdout(), crossterm::terminal::EnterAlternateScreen)?;
Ok(())
}
fn shutdown() -> anyhow::Result<()> {
crossterm::terminal::disable_raw_mode()?;
crossterm::execute!(std::io::stdout(), crossterm::terminal::LeaveAlternateScreen)?;
Ok(())
}