use color_eyre::Result; use crossterm::event::{KeyCode, KeyEvent}; use ratatui::{layout::Rect, Frame}; use tokio::sync::mpsc::UnboundedSender; use super::{ generic::{Activatable, PartialComponent}, Component, }; use crate::{action::Action, app::Mode, config::Config}; pub mod layouts; mod latest_block; mod finalized_block; mod block_ticker; mod current_era; mod current_epoch; mod extrinsics_chart; mod block_explorer; mod extrinsic_explorer; mod log_explorer; use latest_block::LatestBlock; use finalized_block::FinalizedBlock; use block_ticker::BlockTicker; use current_era::CurrentEra; use current_epoch::CurrentEpoch; use extrinsics_chart::ExtrinsicsChart; use block_explorer::BlockExplorer; use extrinsic_explorer::ExtrinsicExplorer; use log_explorer::LogExplorer; #[derive(Debug, Clone, Copy, PartialEq)] pub enum CurrentTab { Nothing, Blocks, Extrinsics, } pub struct Explorer { is_active: bool, current_tab: CurrentTab, components: Vec>> } impl Default for Explorer { fn default() -> Self { Self { is_active: false, current_tab: CurrentTab::Nothing, components: vec![ Box::new(BlockTicker::default()), Box::new(LatestBlock::default()), Box::new(FinalizedBlock::default()), Box::new(CurrentEra::default()), Box::new(CurrentEpoch::default()), Box::new(ExtrinsicsChart::default()), Box::new(BlockExplorer::default()), Box::new(ExtrinsicExplorer::default()), Box::new(LogExplorer::default()), ] } } } impl Explorer { fn move_left(&mut self) { if let CurrentTab::Extrinsics = self.current_tab { self.current_tab = CurrentTab::Blocks; } } fn move_right(&mut self) { match self.current_tab { CurrentTab::Nothing => self.current_tab = CurrentTab::Blocks, CurrentTab::Blocks => self.current_tab = CurrentTab::Extrinsics, _ => {} } } } impl Activatable for Explorer { fn is_active(&self) -> bool { self.is_active } fn is_inactive(&self) -> bool { !self.is_active } fn set_inactive(&mut self) { self.is_active = false; } fn set_active(&mut self) { self.is_active = true; } } impl PartialComponent for Explorer {} impl Component for Explorer { fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { for component in self.components.iter_mut() { component.register_action_handler(tx.clone())?; } Ok(()) } fn register_config_handler(&mut self, config: Config) -> Result<()> { if let Some(_) = config.styles.get(&crate::app::Mode::Explorer) { for component in self.components.iter_mut() { component.register_config_handler(config.clone())?; } } Ok(()) } fn handle_key_event(&mut self, key: KeyEvent) -> Result> { if self.is_inactive() { return Ok(None); } match key.code { KeyCode::Esc => { self.set_inactive(); self.current_tab = CurrentTab::Nothing; for component in self.components.iter_mut() { component.set_active_tab(self.current_tab); } return Ok(Some(Action::SetActiveScreen(Mode::Menu))); }, KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => { self.move_right(); for component in self.components.iter_mut() { component.set_active_tab(self.current_tab); } }, KeyCode::Char('h') | KeyCode::Left => { self.move_left(); for component in self.components.iter_mut() { component.set_active_tab(self.current_tab); } }, _ => { for component in self.components.iter_mut() { let maybe_action = component.handle_key_event(key); if let Ok(Some(_)) = maybe_action { return maybe_action; } } }, } Ok(None) } fn update(&mut self, action: Action) -> Result> { if let Action::SetActiveScreen(Mode::Explorer) = action { self.set_active(); self.current_tab = CurrentTab::Blocks; for component in self.components.iter_mut() { component.set_active_tab(self.current_tab); } } for component in self.components.iter_mut() { component.update(action.clone())?; } Ok(None) } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { let screen = super::layouts::screen_layout(area); for component in self.components.iter_mut() { component.draw(frame, screen)?; } Ok(()) } }