diff --git a/Cargo.toml b/Cargo.toml index 4bc883f..67b989d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "ghost-eye" authors = ["str3tch "] description = "Application for interacting with Casper/Ghost nodes that are exposing RPC only to the localhost" -version = "0.3.67" +version = "0.3.68" edition = "2021" homepage = "https://git.ghostchain.io/ghostchain" repository = "https://git.ghostchain.io/ghostchain/ghost-eye" diff --git a/src/components/empty.rs b/src/components/empty.rs index 745c4a2..5ccbf71 100644 --- a/src/components/empty.rs +++ b/src/components/empty.rs @@ -1,5 +1,4 @@ use color_eyre::Result; -use tokio::sync::mpsc::UnboundedSender; use crossterm::event::{KeyEvent, KeyCode}; use ratatui::{ layout::{Alignment, Rect}, @@ -10,13 +9,20 @@ use ratatui::{ use super::Component; use crate::{ + components::generic::Activatable, action::Action, app::Mode }; #[derive(Debug, Clone, Default)] pub struct Empty { is_active: bool, - action_tx: Option>, +} + +impl Activatable for Empty { + 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 Empty { @@ -88,20 +94,15 @@ impl Empty { } fn move_out(&mut self) -> Result> { - self.is_active = false; + self.set_inactive(); Ok(Some(Action::SetActiveScreen(Mode::Menu))) } } impl Component for Empty { - fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { - self.action_tx = Some(tx); - Ok(()) - } - fn update(&mut self, action: Action) -> Result> { match action { - Action::SetActiveScreen(Mode::Empty) => self.is_active = true, + Action::SetActiveScreen(Mode::Empty) => self.set_active(), _ => {} }; Ok(None) @@ -109,15 +110,20 @@ impl Component for Empty { fn handle_key_event(&mut self, key: KeyEvent) -> Result> { match key.code { - KeyCode::Esc if self.is_active => self.move_out(), + KeyCode::Esc if self.is_active() => self.move_out(), _ => Ok(None), } } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let screen = super::screen_layout(area); + let screen = super::layouts::screen_layout(area); + + let lines = if self.is_active() { + self.prepare_active_text() + } else { + self.prepare_inactive_text() + }; - let lines = if self.is_active { self.prepare_active_text() } else { self.prepare_inactive_text() }; let lines_len = lines.len() as u16; let padding_top = screen .as_size() diff --git a/src/components/explorer/block_explorer.rs b/src/components/explorer/block_explorer.rs index 690762a..23155e6 100644 --- a/src/components/explorer/block_explorer.rs +++ b/src/components/explorer/block_explorer.rs @@ -1,5 +1,5 @@ use color_eyre::Result; -use crossterm::event::{KeyCode, KeyEvent}; +use crossterm::event::KeyEvent; use ratatui::layout::{Constraint, Margin}; use ratatui::widgets::{Padding, Scrollbar, ScrollbarOrientation}; use ratatui::{ @@ -9,13 +9,11 @@ use ratatui::{ Frame }; use subxt::utils::H256; -use tokio::sync::mpsc::UnboundedSender; -use super::{Component, PartialComponent, CurrentTab}; +use super::{Component, CurrentTab}; use crate::{ - action::Action, - config::Config, - palette::StylePalette, + components::generic::{Activatable, Scrollable, PartialComponent}, + action::Action, config::Config, palette::StylePalette, }; #[derive(Debug, Default)] @@ -26,7 +24,6 @@ struct BlockInfo { pub struct BlockExplorer { is_active: bool, - action_tx: Option>, blocks: std::collections::VecDeque, block_headers: std::collections::HashMap, block_authors: std::collections::HashMap, @@ -41,6 +38,49 @@ impl Default for BlockExplorer { } } +impl Activatable for BlockExplorer { + 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 Scrollable for BlockExplorer { + type IndexType = usize; + + fn selected_index(&self) -> Option { + self.table_state.selected() + } + + fn items_length(&self) -> Self::IndexType { + self.blocks.len() + } + + fn apply_next_row(&mut self, new_index: Self::IndexType) -> Result> { + self.table_state.select(Some(new_index)); + self.scroll_state = self.scroll_state.position(new_index); + self.send_used_explorer_block(new_index) + } + + fn apply_prev_row(&mut self, new_index: Self::IndexType) -> Result> { + self.apply_next_row(new_index) + } + + fn apply_first_row(&mut self) -> Result> { + match self.items_length() > 0 { + true => self.apply_next_row(0), + false => Ok(None), + } + } + + fn apply_last_row(&mut self) -> Result> { + match self.items_length().checked_sub(1) { + Some(last_idx) => self.apply_next_row(last_idx), + None => Ok(None), + } + } +} + impl BlockExplorer { const MAX_BLOCKS: usize = 50; @@ -48,7 +88,6 @@ impl BlockExplorer { Self { is_active: false, blocks: Default::default(), - action_tx: None, block_authors: Default::default(), block_headers: Default::default(), scroll_state: ScrollbarState::new(0), @@ -61,7 +100,7 @@ impl BlockExplorer { &mut self, hash: H256, block_number: u32, - ) { + ) -> Result> { let front_block_number = self.blocks .front() .map(|block| block.block_number) @@ -74,7 +113,7 @@ impl BlockExplorer { }); self.block_headers.insert(block_number, hash); - if self.blocks.len() > Self::MAX_BLOCKS { + if self.items_length() > Self::MAX_BLOCKS { if let Some(block) = self.blocks.pop_back() { if let Some(hash) = self.block_headers.remove(&block.block_number) { self.block_authors.remove(&hash); @@ -82,19 +121,21 @@ impl BlockExplorer { } } - self.scroll_state = self.scroll_state.content_length(self.blocks.len()); - if self.table_state.selected().is_some() { - self.next_row(); + self.scroll_state = self.scroll_state.content_length(self.items_length()); + return match self.table_state.selected() { + Some(_) => self.next_row(), + None => Ok(None), } } + Ok(None) } fn update_finalized_block_info( &mut self, header: H256, block_number: u32, - ) { - for idx in 0..self.blocks.len() { + ) -> Result> { + for idx in 0..self.items_length() { if self.blocks[idx].finalized { break; } else if self.blocks[idx].block_number > block_number { continue; } else { @@ -102,86 +143,30 @@ impl BlockExplorer { self.blocks[idx].finalized = true; } } + Ok(None) } - fn send_used_explorer_block(&mut self, index: usize) { - if let Some(action_tx) = &self.action_tx { - let maybe_block_number = self.blocks.get(index).map(|info| info.block_number); - let _ = action_tx.send(Action::UsedExplorerBlock(maybe_block_number)); - } - } - - fn first_row(&mut self) { - if self.blocks.len() > 0 { - self.table_state.select(Some(0)); - self.scroll_state = self.scroll_state.position(0); - self.send_used_explorer_block(0); - } - } - - fn next_row(&mut self) { - let i = match self.table_state.selected() { - Some(i) => { - if i >= self.blocks.len() - 1 { - i - } else { - i + 1 - } - }, - None => 0, - }; - self.table_state.select(Some(i)); - self.scroll_state = self.scroll_state.position(i); - self.send_used_explorer_block(i); - } - - fn last_row(&mut self) { - if self.blocks.len() > 0 { - let last = self.blocks.len() - 1; - self.table_state.select(Some(last)); - self.scroll_state = self.scroll_state.position(last); - self.send_used_explorer_block(last); - } - } - - fn previous_row(&mut self) { - let i = match self.table_state.selected() { - Some(i) => { - if i == 0 { - 0 - } else { - i - 1 - } - }, - None => 0 - }; - self.table_state.select(Some(i)); - self.scroll_state = self.scroll_state.position(i); - self.send_used_explorer_block(i); + fn send_used_explorer_block(&mut self, index: usize) -> Result> { + let maybe_block_number = self.blocks.get(index).map(|info| info.block_number); + Ok(Some(Action::UsedExplorerBlock(maybe_block_number))) } } -impl PartialComponent for BlockExplorer { - fn set_active(&mut self, current_tab: CurrentTab) { +impl PartialComponent for BlockExplorer { + fn set_active_tab(&mut self, current_tab: CurrentTab) { match current_tab { - CurrentTab::Blocks => self.is_active = true, - CurrentTab::Extrinsics => self.is_active = false, + CurrentTab::Blocks => self.set_active(), + CurrentTab::Extrinsics => self.set_inactive(), CurrentTab::Nothing => { - self.is_active = false; + self.set_inactive(); self.table_state.select(None); self.scroll_state = self.scroll_state.position(0); - self.send_used_explorer_block(usize::MAX); }, } } } impl Component for BlockExplorer { - fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { - self.action_tx = Some(tx); - Ok(()) - } - fn register_config_handler(&mut self, config: Config) -> Result<()> { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { self.palette.with_normal_style(style.get("normal_style").copied()); @@ -202,28 +187,23 @@ impl Component for BlockExplorer { Action::FinalizedBlockInformation(header, block_number) => self.update_finalized_block_info(header, block_number), Action::SetBlockAuthor(header, author) => { let _ = self.block_authors.insert(header, author); + Ok(None) }, - _ => {}, - }; - Ok(None) + _ => Ok(None), + } } fn handle_key_event(&mut self, key: KeyEvent) -> Result> { - match key.code { - KeyCode::Char('k') | KeyCode::Up if self.is_active => self.previous_row(), - KeyCode::Char('j') | KeyCode::Down if self.is_active => self.next_row(), - KeyCode::Char('g') if self.is_active => self.first_row(), - KeyCode::Char('G') if self.is_active => self.last_row(), - _ => {}, - }; - - Ok(None) + match self.is_active() { + true => self.handle_scrollable_key_codes(key.code), + false => Ok(None), + } } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [place, _] = super::explorer_scrollbars_layout(area); + let [place, _] = super::layouts::explorer_scrollbars_layout(area); - let (border_style, border_type) = self.palette.create_border_style(self.is_active); + let (border_style, border_type) = self.palette.create_border_style(self.is_active()); let default_hash = H256::repeat_byte(69u8); let default_author = "...".to_string(); let rows = self.blocks diff --git a/src/components/explorer/block_ticker.rs b/src/components/explorer/block_ticker.rs index 21ff9c0..899b10f 100644 --- a/src/components/explorer/block_ticker.rs +++ b/src/components/explorer/block_ticker.rs @@ -7,12 +7,11 @@ use ratatui::{ Frame }; -use super::{Component, PartialComponent, CurrentTab}; +use super::{Component, CurrentTab}; use crate::{ - config::Config, + components::generic::PartialComponent, + config::Config, action::Action, palette::StylePalette, widgets::{BigText, PixelSize}, - action::Action, - palette::StylePalette, }; #[derive(Debug)] @@ -37,18 +36,15 @@ impl BlockTicker { } } - fn block_found(&mut self, block: u32) -> Result<()> { + fn block_found(&mut self, block: u32) { if self.last_block < block { self.last_block_time = Instant::now(); self.last_block = block; } - Ok(()) } } -impl PartialComponent for BlockTicker { - fn set_active(&mut self, _current_tab: CurrentTab) {} -} +impl PartialComponent for BlockTicker {} impl Component for BlockTicker { fn register_config_handler(&mut self, config: Config) -> Result<()> { @@ -65,14 +61,14 @@ impl Component for BlockTicker { fn update(&mut self, action: Action) -> Result> { match action { - Action::BestBlockUpdated(block) => self.block_found(block)?, + Action::BestBlockUpdated(block) => self.block_found(block), _ => {} }; Ok(None) } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [place, _, _] = super::explorer_block_info_layout(area); + let [place, _, _] = super::layouts::explorer_block_info_layout(area); let passed = (Instant::now() - self.last_block_time).as_secs_f64(); let text = if passed < 60.0 { diff --git a/src/components/explorer/current_epoch.rs b/src/components/explorer/current_epoch.rs index 66d14e7..eb1790b 100644 --- a/src/components/explorer/current_epoch.rs +++ b/src/components/explorer/current_epoch.rs @@ -6,11 +6,10 @@ use ratatui::{ Frame, }; -use super::{Component, PartialComponent, CurrentTab}; +use super::{Component, CurrentTab}; use crate::{ - config::Config, - action::Action, - palette::StylePalette, + components::generic::PartialComponent, + config::Config, action::Action, palette::StylePalette, widgets::{PixelSize, BigText}, }; @@ -26,17 +25,9 @@ impl CurrentEpoch { const SESSION_LENGTH: u64 = 2_400; const SECONDS_IN_DAY: u64 = 86_400; const SECONDS_IN_HOUR: u64 = 3_600; - - fn update_epoch_progress(&mut self, number: u64, progress: u64) -> Result<()> { - self.number = number; - self.progress = progress; - Ok(()) - } } -impl PartialComponent for CurrentEpoch { - fn set_active(&mut self, _current_tab: CurrentTab) {} -} +impl PartialComponent for CurrentEpoch {} impl Component for CurrentEpoch { fn register_config_handler(&mut self, config: Config) -> Result<()> { @@ -53,15 +44,17 @@ impl Component for CurrentEpoch { fn update(&mut self, action: Action) -> Result> { match action { - Action::SetEpochProgress(number, progress) => - self.update_epoch_progress(number, progress)?, + Action::SetEpochProgress(number, progress) => { + self.number = number; + self.progress = progress; + }, _ => {} }; Ok(None) } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [place, _] = super::explorer_era_info_layout(area); + let [place, _] = super::layouts::explorer_era_info_layout(area); let seconds_to_next = Self::SESSION_LENGTH.saturating_sub(self.progress) * Self::SECONDS_IN_BLOCK; let hours = (seconds_to_next % Self::SECONDS_IN_DAY) / Self::SECONDS_IN_HOUR; diff --git a/src/components/explorer/current_era.rs b/src/components/explorer/current_era.rs index be71fd0..8684ae0 100644 --- a/src/components/explorer/current_era.rs +++ b/src/components/explorer/current_era.rs @@ -6,13 +6,10 @@ use ratatui::{ Frame, }; -use super::{Component, PartialComponent, CurrentTab}; +use super::{Component, CurrentTab}; use crate::{ - config::Config, - action::Action, - palette::StylePalette, - types::EraInfo, - widgets::{PixelSize, BigText}, + config::Config, action::Action, palette::StylePalette, types::EraInfo, + components::generic::PartialComponent, widgets::{PixelSize, BigText}, }; #[derive(Debug, Default)] @@ -28,16 +25,9 @@ impl CurrentEra { const MILLIS_IN_DAY: u64 = 86_400_000; const MILLIS_IN_HOUR: u64 = 3_600_000; const MILLIS_IN_MINUTE: u64 = 60_000; - - fn update_era(&mut self, era_info: EraInfo) -> Result<()> { - self.era = era_info; - Ok(()) - } } -impl PartialComponent for CurrentEra { - fn set_active(&mut self, _current_tab: CurrentTab) {} -} +impl PartialComponent for CurrentEra {} impl Component for CurrentEra { fn register_config_handler(&mut self, config: Config) -> Result<()> { @@ -54,14 +44,14 @@ impl Component for CurrentEra { fn update(&mut self, action: Action) -> Result> { match action { - Action::SetActiveEra(era_info) => self.update_era(era_info)?, - _ => {} + Action::SetActiveEra(era_info) => self.era = era_info, + _ => {}, }; Ok(None) } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [_, place] = super::explorer_era_info_layout(area); + let [_, place] = super::layouts::explorer_era_info_layout(area); let current_time = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) diff --git a/src/components/explorer/extrinsic_explorer.rs b/src/components/explorer/extrinsic_explorer.rs index 06bfa7a..b7a1d16 100644 --- a/src/components/explorer/extrinsic_explorer.rs +++ b/src/components/explorer/extrinsic_explorer.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, VecDeque}; use std::usize; use color_eyre::Result; -use crossterm::event::{KeyCode, KeyEvent}; +use crossterm::event::KeyEvent; use ratatui::layout::{Constraint, Margin}; use ratatui::widgets::{Padding, Scrollbar, ScrollbarOrientation}; use ratatui::{ @@ -10,19 +10,16 @@ use ratatui::{ widgets::{Block, ScrollbarState, Cell, Row, Table, TableState}, Frame }; -use tokio::sync::mpsc::UnboundedSender; -use super::{Component, CurrentTab, PartialComponent}; +use super::{Component, CurrentTab}; use crate::{ - types::CasperExtrinsicDetails, - action::Action, - config::Config, - palette::StylePalette, + components::generic::{Activatable, Scrollable, PartialComponent}, + types::CasperExtrinsicDetails, + action::Action, config::Config, palette::StylePalette, }; pub struct ExtrinsicExplorer { is_active: bool, - action_tx: Option>, extrinsics: HashMap>, current_extrinsics: Option>, block_numbers: VecDeque, @@ -37,13 +34,58 @@ impl Default for ExtrinsicExplorer { } } +impl Activatable for ExtrinsicExplorer { + 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 Scrollable for ExtrinsicExplorer { + type IndexType = usize; + + fn selected_index(&self) -> Option { + self.table_state.selected() + } + + fn items_length(&self) -> Self::IndexType { + self.current_extrinsics + .as_ref() + .map(|exts| exts.len()) + .unwrap_or_default() + } + + fn apply_next_row(&mut self, new_index: Self::IndexType) -> Result> { + self.table_state.select(Some(new_index)); + self.scroll_state = self.scroll_state.position(new_index); + self.send_used_explorer_log(new_index) + } + + fn apply_prev_row(&mut self, new_index: Self::IndexType) -> Result> { + self.apply_next_row(new_index) + } + + fn apply_first_row(&mut self) -> Result> { + match self.items_length() > 0 { + true => self.apply_next_row(0), + false => Ok(None), + } + } + + fn apply_last_row(&mut self) -> Result> { + match self.items_length().checked_sub(1) { + Some(last_idx) => self.apply_next_row(last_idx), + None => Ok(None), + } + } +} + impl ExtrinsicExplorer { const MAX_BLOCKS: usize = 50; pub fn new() -> Self { Self { is_active: false, - action_tx: None, current_extrinsics: None, extrinsics: Default::default(), block_numbers: Default::default(), @@ -53,6 +95,20 @@ impl ExtrinsicExplorer { } } + fn update_used_explorer_block(&mut self, block_number: u32) { + let maybe_exts = self.extrinsics + .get(&block_number) + .map(|exts| exts.to_vec()); + let exts_length = self.extrinsics + .get(&block_number) + .map(|exts| exts.len()) + .unwrap_or_default(); + + self.current_extrinsics = maybe_exts; + self.scroll_state = self.scroll_state.content_length(exts_length); + } + + fn update_extrinsics_for_header( &mut self, block_number: u32, @@ -68,109 +124,30 @@ impl ExtrinsicExplorer { } } - fn send_used_explorer_log(&mut self, index: usize) { - if let Some(action_tx) = &self.action_tx { - let maybe_log = self.current_extrinsics - .as_ref() - .map(|ext| { - ext.get(index).map(|ext| { - hex::encode(&ext.field_bytes.clone()) - }) - }) - .flatten(); - let _ = action_tx.send(Action::UsedExplorerLog(maybe_log.clone())); - } - } - - fn first_row(&mut self) { - match &self.current_extrinsics { - Some(exts) if exts.len() > 0 => self.table_state.select(Some(0)), - _ => self.table_state.select(None), - } - self.scroll_state = self.scroll_state.position(0); - self.send_used_explorer_log(0); - } - - fn next_row(&mut self) { - match &self.current_extrinsics { - Some(exts) => { - let i = match self.table_state.selected() { - Some(i) => { - if i >= exts.len() - 1 { - i - } else { - i + 1 - } - }, - None => 0, - }; - self.table_state.select(Some(i)); - self.scroll_state = self.scroll_state.position(i); - self.send_used_explorer_log(i); - }, - None => { - self.table_state.select(None); - self.scroll_state = self.scroll_state.position(0); - self.send_used_explorer_log(0); - } - } - } - - fn last_row(&mut self) { - match &self.current_extrinsics { - Some(exts) => { - let last = exts.len().saturating_sub(1); - self.table_state.select(Some(last)); - self.scroll_state = self.scroll_state.position(last); - self.send_used_explorer_log(last); - }, - None => { - self.table_state.select(None); - self.scroll_state = self.scroll_state.position(0); - self.send_used_explorer_log(0); - } - } - } - - fn previous_row(&mut self) { - match &self.current_extrinsics { - Some(_) => { - let i = self.table_state - .selected() - .map(|i| i.saturating_sub(1)) - .unwrap_or_default(); - self.table_state.select(Some(i)); - self.scroll_state = self.scroll_state.position(i); - self.send_used_explorer_log(i); - }, - None => { - self.table_state.select(None); - self.scroll_state = self.scroll_state.position(0); - self.send_used_explorer_log(0); - } - } + fn send_used_explorer_log(&mut self, index: usize) -> Result> { + let maybe_log = self.current_extrinsics + .as_ref() + .map(|ext| ext.get(index).map(|ext| { + hex::encode(&ext.field_bytes.clone()) + })) + .flatten(); + Ok(Some(Action::UsedExplorerLog(maybe_log.clone()))) } } -impl PartialComponent for ExtrinsicExplorer { - fn set_active(&mut self, current_tab: CurrentTab) { +impl PartialComponent for ExtrinsicExplorer { + fn set_active_tab(&mut self, current_tab: CurrentTab) { if current_tab == CurrentTab::Extrinsics { - self.is_active = true; + self.set_active(); } else { - self.is_active = false; + self.set_inactive(); self.table_state.select(None); self.scroll_state = self.scroll_state.position(0); - self.send_used_explorer_log(usize::MAX); } } } impl Component for ExtrinsicExplorer { - fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { - self.action_tx = Some(tx); - Ok(()) - } - fn register_config_handler(&mut self, config: Config) -> Result<()> { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { self.palette.with_normal_style(style.get("normal_style").copied()); @@ -187,16 +164,8 @@ impl Component for ExtrinsicExplorer { fn update(&mut self, action: Action) -> Result> { match action { - Action::UsedExplorerBlock(maybe_block_number) => { - let block_number = maybe_block_number.unwrap_or_default(); - if let Some(exts) = self.extrinsics.get(&block_number) { - self.current_extrinsics = Some(exts.to_vec()); - self.scroll_state = self.scroll_state.content_length(exts.len()); - } else { - self.current_extrinsics = None; - self.scroll_state = self.scroll_state.content_length(0); - } - }, + Action::UsedExplorerBlock(maybe_block_number) => + self.update_used_explorer_block(maybe_block_number.unwrap_or_default()), Action::ExtrinsicsForBlock(block_number, extrinsics) => self.update_extrinsics_for_header(block_number, extrinsics), _ => {}, @@ -205,21 +174,15 @@ impl Component for ExtrinsicExplorer { } fn handle_key_event(&mut self, key: KeyEvent) -> Result> { - match key.code { - KeyCode::Char('k') | KeyCode::Up if self.is_active => self.previous_row(), - KeyCode::Char('j') | KeyCode::Down if self.is_active => self.next_row(), - KeyCode::Char('g') if self.is_active => self.first_row(), - KeyCode::Char('G') if self.is_active => self.last_row(), - _ => {}, - }; - - Ok(None) + match self.is_active() { + true => self.handle_scrollable_key_codes(key.code), + false => Ok(None), + } } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [_, place] = super::explorer_scrollbars_layout(area); - - let (border_style, border_type) = self.palette.create_border_style(self.is_active); + let [_, place] = super::layouts::explorer_scrollbars_layout(area); + let (border_style, border_type) = self.palette.create_border_style(self.is_active()); let mut longest_pallet_name_length = 0; let mut longest_variant_name_length = 0; diff --git a/src/components/explorer/extrinsics_chart.rs b/src/components/explorer/extrinsics_chart.rs index e187ae7..54a21bc 100644 --- a/src/components/explorer/extrinsics_chart.rs +++ b/src/components/explorer/extrinsics_chart.rs @@ -7,8 +7,11 @@ use ratatui::{ Frame, }; -use super::{Component, PartialComponent, CurrentTab}; -use crate::{palette::StylePalette, config::Config, action::Action}; +use super::{Component, CurrentTab}; +use crate::{ + components::generic::PartialComponent, + palette::StylePalette, config::Config, action::Action, +}; #[derive(Debug, Default)] pub struct ExtrinsicsChart { @@ -70,9 +73,7 @@ impl ExtrinsicsChart { } } -impl PartialComponent for ExtrinsicsChart { - fn set_active(&mut self, _current_tab: CurrentTab) {} -} +impl PartialComponent for ExtrinsicsChart {} impl Component for ExtrinsicsChart { fn register_config_handler(&mut self, config: Config) -> Result<()> { @@ -96,7 +97,7 @@ impl Component for ExtrinsicsChart { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [_, place] = super::explorer_header_layout(area); + let [_, place] = super::layouts::explorer_header_layout(area); frame.render_widget(self.extrinsics_bar_chart(place.as_size().width), place); Ok(()) } diff --git a/src/components/explorer/finalized_block.rs b/src/components/explorer/finalized_block.rs index 0e51386..0c85c59 100644 --- a/src/components/explorer/finalized_block.rs +++ b/src/components/explorer/finalized_block.rs @@ -5,11 +5,10 @@ use ratatui::{ Frame, }; -use super::{Component, PartialComponent, CurrentTab}; +use super::{Component, CurrentTab}; use crate::{ - config::Config, - action::Action, - palette::StylePalette, + components::generic::PartialComponent, + config::Config, action::Action, palette::StylePalette, widgets::{PixelSize, BigText}, }; @@ -26,9 +25,7 @@ impl FinalizedBlock { } } -impl PartialComponent for FinalizedBlock { - fn set_active(&mut self, _current_tab: CurrentTab) {} -} +impl PartialComponent for FinalizedBlock {} impl Component for FinalizedBlock { fn register_config_handler(&mut self, config: Config) -> Result<()> { @@ -52,7 +49,7 @@ impl Component for FinalizedBlock { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [_, _, place] = super::explorer_block_info_layout(area); + let [_, _, place] = super::layouts::explorer_block_info_layout(area); let text = self.number.to_string(); let (border_style, border_type) = self.palette.create_border_style(false); diff --git a/src/components/explorer/latest_block.rs b/src/components/explorer/latest_block.rs index 0a4058a..fcca5fc 100644 --- a/src/components/explorer/latest_block.rs +++ b/src/components/explorer/latest_block.rs @@ -5,11 +5,10 @@ use ratatui::{ Frame, }; -use super::{Component, PartialComponent, CurrentTab}; +use super::{Component, CurrentTab}; use crate::{ - config::Config, - action::Action, - palette::StylePalette, + components::generic::PartialComponent, + config::Config, action::Action, palette::StylePalette, widgets::{PixelSize, BigText}, }; @@ -26,9 +25,7 @@ impl LatestBlock { } } -impl PartialComponent for LatestBlock { - fn set_active(&mut self, _current_tab: CurrentTab) {} -} +impl PartialComponent for LatestBlock {} impl Component for LatestBlock { fn register_config_handler(&mut self, config: Config) -> Result<()> { @@ -52,7 +49,7 @@ impl Component for LatestBlock { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [_, place, _] = super::explorer_block_info_layout(area); + let [_, place, _] = super::layouts::explorer_block_info_layout(area); let text = self.number.to_string(); let (border_style, border_type) = self.palette.create_border_style(false); diff --git a/src/components/explorer/layouts.rs b/src/components/explorer/layouts.rs new file mode 100644 index 0000000..fe3125d --- /dev/null +++ b/src/components/explorer/layouts.rs @@ -0,0 +1,50 @@ +use ratatui::layout::{Constraint, Flex, Layout, Rect}; + +pub fn explorer_layout(area: Rect) -> [Rect; 3] { + Layout::vertical([ + Constraint::Percentage(30), + Constraint::Percentage(40), + Constraint::Percentage(30), + ]).areas(area) +} + +pub fn explorer_header_layout(area: Rect) -> [Rect; 2] { + let [header, _, _] = explorer_layout(area); + Layout::horizontal([ + Constraint::Percentage(50), + Constraint::Percentage(50), + ]).areas(header) +} + +pub fn explorer_info_layout(area: Rect) -> [Rect; 2] { + let [info, _] = explorer_header_layout(area); + Layout::vertical([ + Constraint::Percentage(100), + Constraint::Percentage(100), + ]).areas(info) +} + +pub fn explorer_block_info_layout(area: Rect) -> [Rect; 3] { + let [blocks, _] = explorer_info_layout(area); + Layout::horizontal([ + Constraint::Percentage(33), + Constraint::Percentage(33), + Constraint::Percentage(33), + ]).flex(Flex::SpaceBetween).areas(blocks) +} + +pub fn explorer_era_info_layout(area: Rect) -> [Rect; 2] { + let [_, blocks] = explorer_info_layout(area); + Layout::horizontal([ + Constraint::Percentage(50), + Constraint::Percentage(50), + ]).flex(Flex::SpaceBetween).areas(blocks) +} + +pub fn explorer_scrollbars_layout(area: Rect) -> [Rect; 2] { + let [_, place, _] = explorer_layout(area); + Layout::horizontal([ + Constraint::Percentage(50), + Constraint::Percentage(50), + ]).areas(place) +} diff --git a/src/components/explorer/log_explorer.rs b/src/components/explorer/log_explorer.rs index 8b9bc5f..8072705 100644 --- a/src/components/explorer/log_explorer.rs +++ b/src/components/explorer/log_explorer.rs @@ -6,38 +6,19 @@ use ratatui::{ Frame }; -use super::{Component, CurrentTab, PartialComponent}; +use super::{Component, CurrentTab}; use crate::{ - action::Action, - config::Config, - palette::StylePalette, + components::generic::PartialComponent, + action::Action, config::Config, palette::StylePalette }; +#[derive(Default)] pub struct LogExplorer { - is_active: bool, maybe_log: Option, palette: StylePalette, } -impl Default for LogExplorer { - fn default() -> Self { - Self::new() - } -} - -impl LogExplorer { - pub fn new() -> Self { - Self { - is_active: false, - maybe_log: None, - palette: StylePalette::default(), - } - } -} - -impl PartialComponent for LogExplorer { - fn set_active(&mut self, _current_tab: CurrentTab) {} -} +impl PartialComponent for LogExplorer {} impl Component for LogExplorer { fn register_config_handler(&mut self, config: Config) -> Result<()> { @@ -58,9 +39,9 @@ impl Component for LogExplorer { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [_, _, place] = super::explorer_layout(area); + let [_, _, place] = super::layouts::explorer_layout(area); - let (border_style, border_type) = self.palette.create_border_style(self.is_active); + let (border_style, border_type) = self.palette.create_border_style(false); let line = match &self.maybe_log { Some(log) => Line::from(hex::encode(log)), diff --git a/src/components/explorer/mod.rs b/src/components/explorer/mod.rs index 1348272..733730d 100644 --- a/src/components/explorer/mod.rs +++ b/src/components/explorer/mod.rs @@ -1,14 +1,15 @@ use color_eyre::Result; use crossterm::event::{KeyCode, KeyEvent}; -use ratatui::{ - layout::{Constraint, Flex, Layout, Rect}, - Frame, -}; +use ratatui::{layout::Rect, Frame}; use tokio::sync::mpsc::UnboundedSender; -use super::Component; +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; @@ -29,21 +30,17 @@ use block_explorer::BlockExplorer; use extrinsic_explorer::ExtrinsicExplorer; use log_explorer::LogExplorer; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum CurrentTab { Nothing, Blocks, Extrinsics, } -pub trait PartialComponent: Component { - fn set_active(&mut self, current_tab: CurrentTab); -} - pub struct Explorer { is_active: bool, current_tab: CurrentTab, - components: Vec> + components: Vec>> } impl Default for Explorer { @@ -82,10 +79,15 @@ impl Explorer { } } -impl PartialComponent for Explorer { - fn set_active(&mut self, _current_tab: CurrentTab) {} +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() { @@ -104,31 +106,35 @@ impl Component for Explorer { } fn handle_key_event(&mut self, key: KeyEvent) -> Result> { - if !self.is_active { return Ok(None); } + if self.is_inactive() { return Ok(None); } + match key.code { KeyCode::Esc => { - self.is_active = false; + self.set_inactive(); self.current_tab = CurrentTab::Nothing; for component in self.components.iter_mut() { - component.set_active(self.current_tab.clone()); + 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(self.current_tab.clone()); + 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(self.current_tab.clone()); + component.set_active_tab(self.current_tab); } }, _ => { for component in self.components.iter_mut() { - component.handle_key_event(key)?; + let maybe_action = component.handle_key_event(key); + if let Ok(Some(_)) = maybe_action { + return maybe_action; + } } }, } @@ -137,10 +143,10 @@ impl Component for Explorer { fn update(&mut self, action: Action) -> Result> { if let Action::SetActiveScreen(Mode::Explorer) = action { - self.is_active = true; + self.set_active(); self.current_tab = CurrentTab::Blocks; for component in self.components.iter_mut() { - component.set_active(self.current_tab.clone()); + component.set_active_tab(self.current_tab); } } for component in self.components.iter_mut() { @@ -150,59 +156,10 @@ impl Component for Explorer { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let screen = super::screen_layout(area); + let screen = super::layouts::screen_layout(area); for component in self.components.iter_mut() { component.draw(frame, screen)?; } Ok(()) } } - -pub fn explorer_layout(area: Rect) -> [Rect; 3] { - Layout::vertical([ - Constraint::Percentage(30), - Constraint::Percentage(40), - Constraint::Percentage(30), - ]).areas(area) -} - -pub fn explorer_header_layout(area: Rect) -> [Rect; 2] { - let [header, _, _] = explorer_layout(area); - Layout::horizontal([ - Constraint::Percentage(50), - Constraint::Percentage(50), - ]).areas(header) -} - -pub fn explorer_info_layout(area: Rect) -> [Rect; 2] { - let [info, _] = explorer_header_layout(area); - Layout::vertical([ - Constraint::Percentage(100), - Constraint::Percentage(100), - ]).areas(info) -} - -pub fn explorer_block_info_layout(area: Rect) -> [Rect; 3] { - let [blocks, _] = explorer_info_layout(area); - Layout::horizontal([ - Constraint::Percentage(33), - Constraint::Percentage(33), - Constraint::Percentage(33), - ]).flex(Flex::SpaceBetween).areas(blocks) -} - -pub fn explorer_era_info_layout(area: Rect) -> [Rect; 2] { - let [_, blocks] = explorer_info_layout(area); - Layout::horizontal([ - Constraint::Percentage(50), - Constraint::Percentage(50), - ]).flex(Flex::SpaceBetween).areas(blocks) -} - -pub fn explorer_scrollbars_layout(area: Rect) -> [Rect; 2] { - let [_, place, _] = explorer_layout(area); - Layout::horizontal([ - Constraint::Percentage(50), - Constraint::Percentage(50), - ]).areas(place) -} diff --git a/src/components/fps.rs b/src/components/fps.rs index 81fb401..2523f2a 100644 --- a/src/components/fps.rs +++ b/src/components/fps.rs @@ -75,7 +75,7 @@ impl Component for FpsCounter { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [_, place] = super::header_layout(area); + let [_, place] = super::layouts::header_layout(area); let message = format!( " {:.2} ticks/sec | {:.2} FPS", diff --git a/src/components/generic/mod.rs b/src/components/generic/mod.rs index ac13d13..e157cbd 100644 --- a/src/components/generic/mod.rs +++ b/src/components/generic/mod.rs @@ -1,7 +1,9 @@ mod activatable; mod helpable; mod scrollable; +mod partial; pub use activatable::Activatable; pub use helpable::Helpable; pub use scrollable::Scrollable; +pub use partial::PartialComponent; diff --git a/src/components/generic/partial.rs b/src/components/generic/partial.rs new file mode 100644 index 0000000..17a02fd --- /dev/null +++ b/src/components/generic/partial.rs @@ -0,0 +1,8 @@ +use crate::components::Component; + +pub trait PartialComponent: Component +where + TabType: std::fmt::Debug + Clone + Copy + PartialEq, +{ + fn set_active_tab(&mut self, _current_tab: TabType) {} +} diff --git a/src/components/generic/scrollable.rs b/src/components/generic/scrollable.rs index d550f91..15a88d1 100644 --- a/src/components/generic/scrollable.rs +++ b/src/components/generic/scrollable.rs @@ -1,6 +1,7 @@ use std::{ cmp::{PartialEq, PartialOrd}, ops::{Add, Sub} }; +use crossterm::event::KeyCode; use color_eyre::Result; use crate::action::Action; @@ -13,12 +14,15 @@ pub trait Scrollable { + From + Copy; - fn apply_next_row(&mut self, new_index: Self::IndexType) -> Result>; - fn apply_prev_row(&mut self, new_index: Self::IndexType) -> Result>; - fn selected_index(&self) -> Option; fn items_length(&self) -> Self::IndexType; + fn apply_next_row(&mut self, _new_index: Self::IndexType) -> Result> { Ok(None) } + fn apply_prev_row(&mut self, _new_index: Self::IndexType) -> Result> { Ok(None) } + fn apply_first_row(&mut self) -> Result> { Ok(None) } + fn apply_last_row(&mut self) -> Result> { Ok(None) } + + fn next_row(&mut self) -> Result> { let length = self.items_length(); if length == 0.into() { @@ -55,4 +59,28 @@ pub trait Scrollable { }; self.apply_prev_row(new_index) } + + fn first_row(&mut self) -> Result> { + if self.items_length() == 0.into() { + return Ok(None); + } + self.apply_first_row() + } + + fn last_row(&mut self) -> Result> { + if self.items_length() == 0.into() { + return Ok(None); + } + self.apply_last_row() + } + + fn handle_scrollable_key_codes(&mut self, key_code: KeyCode) -> Result> { + match key_code { + KeyCode::Char('k') | KeyCode::Up => self.prev_row(), + KeyCode::Char('j') | KeyCode::Down => self.next_row(), + KeyCode::Char('g') => self.first_row(), + KeyCode::Char('G') => self.last_row(), + _ => Ok(None), + } + } } diff --git a/src/components/health.rs b/src/components/health.rs index fd63b15..a70f6c4 100644 --- a/src/components/health.rs +++ b/src/components/health.rs @@ -84,7 +84,7 @@ impl Component for Health { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [place, _] = super::header_layout(area); + let [place, _] = super::layouts::header_layout(area); let message = format!("{:^12} | tx.pool: {:^3} | peers: {:^3} | {:^9} | validators {:^4} | nominators {:^4} |", self.name_as_string(), diff --git a/src/components/layouts.rs b/src/components/layouts.rs new file mode 100644 index 0000000..7e9e8b0 --- /dev/null +++ b/src/components/layouts.rs @@ -0,0 +1,37 @@ +use ratatui::layout::{Constraint, Layout, Rect}; + +pub fn global_layout(area: Rect) -> [Rect; 2] { + Layout::vertical([ + Constraint::Length(1), + Constraint::Fill(1), + ]).areas(area) +} + +pub fn header_layout(area: Rect) -> [Rect; 2] { + let [header, _] = global_layout(area); + Layout::horizontal([ + Constraint::Fill(1), + Constraint::Length(27), + ]).areas(header) +} + +pub fn main_layout(area: Rect) -> [Rect; 2] { + let [_, main] = global_layout(area); + Layout::horizontal([ + Constraint::Max(30), + Constraint::Fill(1), + ]).areas(main) +} + +pub fn menu_layout(area: Rect) -> [Rect; 2] { + let [menu, _] = main_layout(area); + Layout::vertical([ + Constraint::Min(0), + Constraint::Length(5), + ]).areas(menu) +} + +pub fn screen_layout(area: Rect) -> Rect { + let [_, screen] = main_layout(area); + screen +} diff --git a/src/components/menu.rs b/src/components/menu.rs index 8f27a35..bc0f9af 100644 --- a/src/components/menu.rs +++ b/src/components/menu.rs @@ -118,7 +118,7 @@ impl Component for Menu { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [menu, _] = super::menu_layout(area); + let [menu, _] = super::layouts::menu_layout(area); let (color, border_type) = self.palette.create_border_style(self.is_active()); let block = Block::bordered() diff --git a/src/components/mod.rs b/src/components/mod.rs index f99d43e..c89b63b 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,7 +1,7 @@ use color_eyre::Result; use crossterm::event::{KeyEvent, MouseEvent}; use ratatui::{ - layout::{Constraint, Layout, Rect, Size}, + layout::{Rect, Size}, Frame, }; use tokio::sync::mpsc::UnboundedSender; @@ -9,6 +9,7 @@ use std::sync::mpsc::Sender; use crate::{palette, action::Action, config::Config, tui::Event}; +pub mod layouts; pub mod fps; pub mod health; pub mod menu; @@ -67,39 +68,3 @@ pub trait Component { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()>; } - -pub fn global_layout(area: Rect) -> [Rect; 2] { - Layout::vertical([ - Constraint::Length(1), - Constraint::Fill(1), - ]).areas(area) -} - -pub fn header_layout(area: Rect) -> [Rect; 2] { - let [header, _] = global_layout(area); - Layout::horizontal([ - Constraint::Fill(1), - Constraint::Length(27), - ]).areas(header) -} - -pub fn main_layout(area: Rect) -> [Rect; 2] { - let [_, main] = global_layout(area); - Layout::horizontal([ - Constraint::Max(30), - Constraint::Fill(1), - ]).areas(main) -} - -pub fn menu_layout(area: Rect) -> [Rect; 2] { - let [menu, _] = main_layout(area); - Layout::vertical([ - Constraint::Min(0), - Constraint::Length(5), - ]).areas(menu) -} - -pub fn screen_layout(area: Rect) -> Rect { - let [_, screen] = main_layout(area); - screen -} diff --git a/src/components/validator/mod.rs b/src/components/validator/mod.rs index 54bbd7c..6541766 100644 --- a/src/components/validator/mod.rs +++ b/src/components/validator/mod.rs @@ -277,7 +277,7 @@ impl Component for Validator { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let screen = super::screen_layout(area); + let screen = super::layouts::screen_layout(area); for component in self.components.iter_mut() { component.draw(frame, screen)?; } diff --git a/src/components/version.rs b/src/components/version.rs index 6ee3341..e652634 100644 --- a/src/components/version.rs +++ b/src/components/version.rs @@ -52,7 +52,7 @@ impl Component for Version { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [_, version] = super::menu_layout(area); + let [_, version] = super::layouts::menu_layout(area); let text_style = self.palette.create_basic_style(false); let (border_style, border_type) = self.palette.create_border_style(false); diff --git a/src/components/wallet/mod.rs b/src/components/wallet/mod.rs index 070d5fa..c60ea1d 100644 --- a/src/components/wallet/mod.rs +++ b/src/components/wallet/mod.rs @@ -239,7 +239,7 @@ impl Component for Wallet { } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let screen = super::screen_layout(area); + let screen = super::layouts::screen_layout(area); for component in self.components.iter_mut() { component.draw(frame, screen)?; }