use color_eyre::Result;
use crossterm::cursor;
use crossterm::execute;
use crossterm::terminal;
use crossterm::{cursor::MoveTo, ExecutableCommand};
use mal::api::model::RankingType;
use mal::handlers::common;
use mal::logging::initialize_logging;
use ratatui::prelude::CrosstermBackend;
use ratatui::Terminal;
use std::sync::Arc;
use std::{
io::{self}, panic,
};
use tokio::sync::Mutex;
use mal::app::*;
use mal::auth::OAuth;
use mal::config::{app_config::AppConfig, oauth_config::AuthConfig};
use mal::event;
use mal::event::key::Key;
use mal::handlers;
use mal::network::{IoEvent, Network};
use mal::ui;
fn setup_terminal() -> Result<()> {
let mut stdout = io::stdout();
execute!(stdout, terminal::EnterAlternateScreen)?;
execute!(stdout, cursor::Hide)?;
execute!(stdout, terminal::Clear(terminal::ClearType::All))?;
execute!(stdout, crossterm::event::EnableMouseCapture)?;
terminal::enable_raw_mode()?;
Ok(())
}
fn cleanup_terminal() -> Result<()> {
let mut stdout = io::stdout();
execute!(stdout, crossterm::event::DisableMouseCapture)?;
execute!(stdout, cursor::MoveTo(0, 0))?;
execute!(stdout, terminal::Clear(terminal::ClearType::All))?;
execute!(stdout, terminal::LeaveAlternateScreen)?;
execute!(stdout, cursor::Show)?;
terminal::disable_raw_mode()?;
Ok(())
}
fn setup_panic_hook() {
panic::set_hook(Box::new(|panic_info| {
cleanup_terminal().unwrap();
better_panic::Settings::auto().create_panic_handler()(panic_info);
}));
}
#[tokio::main]
async fn main() -> Result<()> {
better_panic::install();
setup_panic_hook();
let exit = mal::cli::handle_args();
if exit {
return Ok(());
}
initialize_logging();
println!("==> Loading Configiration");
let app_config = AppConfig::load()?;
println!("==> Auth Configuration Loading");
let auth_config = AuthConfig::load()?;
println!("==> Refreshing Token");
let oauth = OAuth::get_auth_async(auth_config).await?;
let (sync_io_tx, sync_io_rx) = std::sync::mpsc::channel::<IoEvent>();
let app = Arc::new(Mutex::new(App::new(sync_io_tx, app_config.clone())));
let cloned_app = Arc::clone(&app);
std::thread::spawn(move || {
let mut network = Network::new(oauth, &app, app_config.search_limit);
start_network(sync_io_rx, &mut network);
});
start_ui(app_config, &cloned_app).await?;
Ok(())
}
#[tokio::main]
async fn start_network(io_rx: std::sync::mpsc::Receiver<IoEvent>, network: &mut Network) {
while let Ok(io_event) = io_rx.recv() {
network.handle_network_event(io_event).await;
}
}
async fn start_ui(app_config: AppConfig, app: &Arc<Mutex<App>>) -> Result<()> {
let backend = CrosstermBackend::new(io::stdout());
let mut terminal = Terminal::new(backend)?;
setup_terminal()?;
let events = event::Events::new(app_config.behavior.tick_rate_milliseconds);
{
let mut app = app.lock().await;
app.active_top_three = TopThreeBlock::Loading(RankingType::AnimeRankingType(
app_config.top_three_anime_types[0].clone(),
));
app.active_top_three_anime = Some(app_config.top_three_anime_types[0].clone());
app.active_top_three_manga = Some(app_config.top_three_manga_types[0].clone());
app.dispatch(IoEvent::GetTopThree(TopThreeBlock::Anime(
app_config.top_three_anime_types[0].clone(),
)));
}
loop {
let mut app = app.lock().await;
if app.exit_flag {
break;
}
let current_block = app.active_block;
terminal.draw(|f| ui::draw_main_layout(f, &mut app))?;
if current_block == ActiveBlock::Input {
terminal.show_cursor()?;
} else {
terminal.hide_cursor()?;
}
let cursor_offset = if app.size.height > ui::util::SMALL_TERMINAL_HEIGHT {
2
} else {
1
};
terminal.backend_mut().execute(MoveTo(
cursor_offset + app.input_cursor_position,
cursor_offset,
))?;
if let event::Event::Input(key) = events.next()? {
let key = common::get_lowercase_key(key);
let active_block = app.active_block;
if key == Key::Tab {
handlers::handle_tab(&mut app);
} else if key == Key::BackTab {
handlers::handle_back_tab(&mut app);
} else if active_block == ActiveBlock::Input {
handlers::input_handler(key, &mut app);
} else if common::quit_event(key) {
app.exit_confirmation_popup = true;
} else if key == app.app_config.keys.back {
if app.active_block != ActiveBlock::Input {
app.load_previous_route();
break;
}
} else {
handlers::handle_app(key, &mut app);
}
}
}
cleanup_terminal()?;
Ok(())
}