use color_eyre::Report; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use ratatui::{prelude::CrosstermBackend, widgets::ListState, DefaultTerminal, Terminal}; use ratatui_form::{Email, Form}; use std::{io, path::Path}; use crate::{ event::{AppEvent, Event, EventHandler}, form::FormState, manifest::{load_manifest, Manifest}, }; // #[derive(Debug)] pub struct App { pub running: bool, pub list_state: ListState, pub events: EventHandler, pub isloadingmanifest: bool, pub manifest: Option, } impl App { pub fn new() -> Self { Self { running: true, list_state: ListState::default().with_selected(Some(0)), events: EventHandler::new(), isloadingmanifest: false, manifest: None, } } pub async fn run( mut self, terminal: &mut Terminal>, ) -> color_eyre::Result<()> { let project_root = Path::new("/home/nikkuss/dotfiles-new"); self.events.send(AppEvent::LoadManifest); while self.running { terminal.draw(|frame| frame.render_widget(&mut self, frame.area()))?; match self.events.next().await? { Event::Tick => self.tick(), Event::Crossterm(event) => match event { crossterm::event::Event::Key(key_event) if key_event.kind == crossterm::event::KeyEventKind::Press => { self.handle_key_events(key_event)? } _ => {} }, Event::App(app_event) => match app_event { AppEvent::LoadManifest => { if !self.isloadingmanifest { let tx = self.events.clone_sender(); tokio::spawn(async move { let result = load_manifest(&project_root).await; let _ = tx.send(Event::App(AppEvent::ManifestLoaded(result))); }); } } AppEvent::ManifestLoaded(manifest) => { self.isloadingmanifest = false; match manifest { Ok(m) => { self.manifest = Some(m); } Err(e) => { self.display_error(e); } } } AppEvent::SelectionUp => self.selection_up(), AppEvent::SelectionDown => self.selection_down(), AppEvent::Quit => self.quit(), }, } } Ok(()) } /// Handles the key events and updates the state of [`App`]. pub fn handle_key_events(&mut self, key_event: KeyEvent) -> color_eyre::Result<()> { match key_event.code { KeyCode::Esc | KeyCode::Char('q') => self.events.send(AppEvent::Quit), KeyCode::Char('c' | 'C') if key_event.modifiers == KeyModifiers::CONTROL => { self.events.send(AppEvent::Quit) } KeyCode::Up => self.events.send(AppEvent::SelectionUp), KeyCode::Down => self.events.send(AppEvent::SelectionDown), KeyCode::Char('a') => self.create_secret(), // Other handlers you could add here. _ => {} } Ok(()) } pub fn selected_secret_name(&self) -> Option { let selected = self.list_state.selected()?; if let Some(manifest) = &self.manifest { return manifest.secrets.keys().nth(selected).cloned(); } None } /// Handles the tick event of the terminal. /// /// The tick event is where you can update the state of your application with any logic that /// needs to be updated at a fixed frame rate. E.g. polling a server, updating an animation. pub fn tick(&self) {} /// Set running to false to quit the application. pub fn quit(&mut self) { self.running = false; } pub fn selection_down(&mut self) { self.list_state.select_next(); } pub fn selection_up(&mut self) { self.list_state.select_previous(); } pub fn display_error(&mut self, display_error: Report) { // somehow display the error? } pub fn create_secret(&mut self) {} }