use crate::Result;
use crate::event::{Tx, new_channel};
use crate::exec::cli::CliArgs;
use crate::exec::{ExecActionEvent, ExecutorTx};
use crate::hub::{HubEvent, get_hub};
use crate::term::safer_println;
use crate::tui_v1::hub_event_handler::handle_hub_event;
use crate::tui_v1::in_reader::InReader;
use crossterm::cursor::MoveUp;
use crossterm::event::{KeyCode, KeyEventKind, KeyModifiers};
use crossterm::execute;
use crossterm::terminal::{Clear, ClearType};
use derive_more::{Deref, From};
#[derive(Debug)]
pub struct TuiAppV1 {
executor_tx: ExecutorTx,
}
impl TuiAppV1 {
pub fn new(executor_tx: ExecutorTx) -> Self {
Self { executor_tx }
}
}
impl TuiAppV1 {
fn executor_tx(&self) -> ExecutorTx {
self.executor_tx.clone()
}
}
#[derive(Clone, From, Deref)]
pub struct ExitTx(Tx<()>);
impl TuiAppV1 {
pub async fn start_with_args(self, cli_args: CliArgs) -> Result<()> {
let (exit_tx, exit_rx) = new_channel::<()>("exit_term");
let interactive = cli_args.cmd.is_interactive();
let in_reader = self.start_app(exit_tx.into(), interactive)?;
let exec_cmd: ExecActionEvent = cli_args.cmd.into();
let executor_tx = self.executor_tx();
tokio::spawn(async move {
let _ = executor_tx.send(exec_cmd).await;
});
let _ = exit_rx.recv().await;
if let Some(in_reader) = in_reader {
in_reader.close()
}
Ok(())
}
fn start_app(&self, exit_tx: ExitTx, interactive: bool) -> Result<Option<InReader>> {
self.run_handle_hub_event(exit_tx.clone(), interactive)?;
let in_reader = self.run_handle_in_event(exit_tx, interactive);
Ok(in_reader)
}
}
impl TuiAppV1 {
fn run_handle_in_event(&self, _exit_tx: ExitTx, interactive: bool) -> Option<InReader> {
if interactive {
let (in_reader, in_rx) = InReader::new_and_rx();
in_reader.start();
let exec_tx = self.executor_tx();
tokio::spawn(async move {
let hub = get_hub();
while let Ok(key_event) = in_rx.recv_async().await {
match key_event.code {
KeyCode::Char('r') => {
if key_event.kind == KeyEventKind::Press {
safer_println("\n-- R pressed - Redo\n", interactive);
exec_tx.send(ExecActionEvent::Redo).await;
}
}
KeyCode::Char('q') => {
if key_event.kind == KeyEventKind::Press {
hub.publish(HubEvent::Quit).await
}
}
KeyCode::Char('a') => {
if key_event.kind == KeyEventKind::Press {
exec_tx.send(ExecActionEvent::OpenAgent).await;
}
}
KeyCode::Char('c')
if key_event.modifiers.contains(KeyModifiers::CONTROL)
&& key_event.kind == KeyEventKind::Press =>
{
hub.publish(HubEvent::Quit).await;
}
_ => (),
}
}
});
Some(in_reader)
} else {
None
}
}
fn run_handle_hub_event(&self, exit_tx: ExitTx, interactive: bool) -> Result<()> {
let exec_tx = self.executor_tx();
let hub_rx = get_hub().take_rx()?;
tokio::spawn(async move {
loop {
let evt_res = hub_rx.recv().await;
match evt_res {
Ok(event) => {
if let Err(err) = handle_hub_event(event, &exec_tx, &exit_tx, interactive).await {
println!("Tui ERROR while handling handle_hub_event. Cause {err}")
}
}
Err(err) => {
println!("TuiApp handle_hub_event event error: {err}");
break;
}
}
}
});
Ok(())
}
}
#[allow(unused)]
fn clear_last_n_lines(n: u16) {
let mut stdout = std::io::stdout();
execute!(stdout, MoveUp(n)).expect("Cannot MoveUp Cursor");
for _ in 0..n {
execute!(stdout, Clear(ClearType::CurrentLine)).expect("Cannot Clear Current Line");
}
}