rustfmt whole project

Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
Uncle Stretch 2025-11-13 17:10:08 +03:00
parent 565758bb84
commit bae21bb505
Signed by: str3tch
GPG Key ID: 84F3190747EE79AA
101 changed files with 4661 additions and 3256 deletions

View File

@ -2,7 +2,7 @@
name = "ghost-eye" name = "ghost-eye"
authors = ["str3tch <stretch@ghostchain.io>"] authors = ["str3tch <stretch@ghostchain.io>"]
description = "Application for interacting with Casper/Ghost nodes that are exposing RPC only to the localhost" description = "Application for interacting with Casper/Ghost nodes that are exposing RPC only to the localhost"
version = "0.3.75" version = "0.3.76"
edition = "2021" edition = "2021"
homepage = "https://git.ghostchain.io/ghostchain" homepage = "https://git.ghostchain.io/ghostchain"
repository = "https://git.ghostchain.io/ghostchain/ghost-eye" repository = "https://git.ghostchain.io/ghostchain/ghost-eye"

View File

@ -1,13 +1,13 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum::Display; use strum::Display;
use subxt::utils::H256;
use subxt::config::substrate::DigestItem; use subxt::config::substrate::DigestItem;
use subxt::utils::H256;
use crate::types::{ use crate::types::{
ActionLevel, ActionTarget, CasperExtrinsicDetails, EraInfo, EraRewardPoints, ActionLevel, ActionTarget, BlockRange, CasperExtrinsicDetails, EraInfo, EraRewardPoints,
Nominator, Nominations, PeerInformation, SessionKeyInfo, UnlockChunk, SystemAccount, Gatekeeper, Nominations, Nominator, PeerInformation, RewardDestination, SessionKeyInfo,
RewardDestination, Gatekeeper, BlockRange, SystemAccount, UnlockChunk,
}; };
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]

View File

@ -1,19 +1,18 @@
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
use ratatui::prelude::Rect; use ratatui::prelude::Rect;
use tokio::sync::mpsc::{UnboundedSender, UnboundedReceiver};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
use tracing::info; use tracing::info;
use crate::{ use crate::{
action::Action, action::Action,
components::{
empty::Empty, explorer::Explorer, fps::FpsCounter, health::Health, help::Help, menu::Menu,
validator::Validator, version::Version, wallet::Wallet, Component,
},
config::Config, config::Config,
tui::{Event, Tui}, tui::{Event, Tui},
components::{
menu::Menu, version::Version, explorer::Explorer, wallet::Wallet,
validator::Validator, empty::Empty, help::Help, health::Health,
fps::FpsCounter, Component,
},
}; };
pub use crate::modes::Mode; pub use crate::modes::Mode;
@ -190,7 +189,9 @@ impl App {
fn handle_actions(&mut self, tui: &mut Tui) -> Result<()> { fn handle_actions(&mut self, tui: &mut Tui) -> Result<()> {
while let Ok(action) = self.action_rx.try_recv() { while let Ok(action) = self.action_rx.try_recv() {
match action { match action {
Action::Tick => { self.last_tick_key_events.drain(..); }, Action::Tick => {
self.last_tick_key_events.drain(..);
}
Action::Quit => self.should_quite = true, Action::Quit => self.should_quite = true,
Action::Suspend => self.should_suspend = true, Action::Suspend => self.should_suspend = true,
Action::Resume => self.should_suspend = false, Action::Resume => self.should_suspend = false,
@ -226,7 +227,7 @@ impl App {
.send(Action::Error(format!("failed to draw: {:?}", err))); .send(Action::Error(format!("failed to draw: {:?}", err)));
} }
} }
}, }
Mode::Wallet => { Mode::Wallet => {
if let Some(component) = self.components.get_mut(6) { if let Some(component) = self.components.get_mut(6) {
if let Err(err) = component.draw(frame, frame.area()) { if let Err(err) = component.draw(frame, frame.area()) {
@ -235,7 +236,7 @@ impl App {
.send(Action::Error(format!("failed to draw: {:?}", err))); .send(Action::Error(format!("failed to draw: {:?}", err)));
} }
} }
}, }
Mode::Validator => { Mode::Validator => {
if let Some(component) = self.components.get_mut(7) { if let Some(component) = self.components.get_mut(7) {
if let Err(err) = component.draw(frame, frame.area()) { if let Err(err) = component.draw(frame, frame.area()) {
@ -244,7 +245,7 @@ impl App {
.send(Action::Error(format!("failed to draw: {:?}", err))); .send(Action::Error(format!("failed to draw: {:?}", err)));
} }
} }
}, }
_ => { _ => {
if let Some(component) = self.components.last_mut() { if let Some(component) = self.components.last_mut() {
if let Err(err) = component.draw(frame, frame.area()) { if let Err(err) = component.draw(frame, frame.area()) {
@ -253,7 +254,7 @@ impl App {
.send(Action::Error(format!("failed to draw: {:?}", err))); .send(Action::Error(format!("failed to draw: {:?}", err)));
} }
} }
}, }
} }
for component in self.components.iter_mut().take(5) { for component in self.components.iter_mut().take(5) {

View File

@ -1,8 +1,10 @@
//! Casper specific configuration //! Casper specific configuration
use subxt::{ use subxt::{
Config, blocks::Block, client::OnlineClient, blocks::Block,
config::{DefaultExtrinsicParamsBuilder, DefaultExtrinsicParams, SubstrateConfig}, client::OnlineClient,
config::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder, SubstrateConfig},
Config,
}; };
/// Default set of commonly used type by Casper nodes. /// Default set of commonly used type by Casper nodes.

View File

@ -1,5 +1,5 @@
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyEvent, KeyCode}; use crossterm::event::{KeyCode, KeyEvent};
use ratatui::{ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Line, text::Line,
@ -8,10 +8,7 @@ use ratatui::{
}; };
use super::Component; use super::Component;
use crate::{ use crate::{action::Action, app::Mode, components::generic::Activatable};
components::generic::Activatable,
action::Action, app::Mode
};
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Empty { pub struct Empty {
@ -19,10 +16,18 @@ pub struct Empty {
} }
impl Activatable for Empty { impl Activatable for Empty {
fn is_active(&self) -> bool { self.is_active } fn is_active(&self) -> bool {
fn is_inactive(&self) -> bool { !self.is_active } self.is_active
fn set_inactive(&mut self) { self.is_active = false; } }
fn set_active(&mut self) { self.is_active = true; } 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 { impl Empty {
@ -125,10 +130,7 @@ impl Component for Empty {
}; };
let lines_len = lines.len() as u16; let lines_len = lines.len() as u16;
let padding_top = screen let padding_top = screen.as_size().height.saturating_sub(lines_len) / 2;
.as_size()
.height
.saturating_sub(lines_len) / 2;
let paragraph = Paragraph::new(lines) let paragraph = Paragraph::new(lines)
.block(Block::bordered().padding(Padding::new(0, 0, padding_top / 2, 0))) .block(Block::bordered().padding(Padding::new(0, 0, padding_top / 2, 0)))

View File

@ -3,17 +3,19 @@ use crossterm::event::KeyEvent;
use ratatui::layout::{Constraint, Margin}; use ratatui::layout::{Constraint, Margin};
use ratatui::widgets::{Padding, Scrollbar, ScrollbarOrientation}; use ratatui::widgets::{Padding, Scrollbar, ScrollbarOrientation};
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
widgets::{Block, ScrollbarState, Cell, Row, Table, TableState}, text::Text,
Frame widgets::{Block, Cell, Row, ScrollbarState, Table, TableState},
Frame,
}; };
use subxt::utils::H256; use subxt::utils::H256;
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
components::generic::{Activatable, Scrollable, PartialComponent}, action::Action,
action::Action, config::Config, palette::StylePalette, components::generic::{Activatable, PartialComponent, Scrollable},
config::Config,
palette::StylePalette,
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -39,10 +41,18 @@ impl Default for BlockExplorer {
} }
impl Activatable for BlockExplorer { impl Activatable for BlockExplorer {
fn is_active(&self) -> bool { self.is_active } fn is_active(&self) -> bool {
fn is_inactive(&self) -> bool { !self.is_active } self.is_active
fn set_inactive(&mut self) { self.is_active = false; } }
fn set_active(&mut self) { self.is_active = true; } 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 { impl Scrollable for BlockExplorer {
@ -101,7 +111,8 @@ impl BlockExplorer {
hash: H256, hash: H256,
block_number: u32, block_number: u32,
) -> Result<Option<Action>> { ) -> Result<Option<Action>> {
let front_block_number = self.blocks let front_block_number = self
.blocks
.front() .front()
.map(|block| block.block_number) .map(|block| block.block_number)
.unwrap_or_default(); .unwrap_or_default();
@ -125,7 +136,7 @@ impl BlockExplorer {
return match self.table_state.selected() { return match self.table_state.selected() {
Some(_) => self.next_row(), Some(_) => self.next_row(),
None => Ok(None), None => Ok(None),
} };
} }
Ok(None) Ok(None)
} }
@ -136,9 +147,11 @@ impl BlockExplorer {
block_number: u32, block_number: u32,
) -> Result<Option<Action>> { ) -> Result<Option<Action>> {
for idx in 0..self.items_length() { for idx in 0..self.items_length() {
if self.blocks[idx].finalized { break; } if self.blocks[idx].finalized {
else if self.blocks[idx].block_number > block_number { continue; } break;
else { } else if self.blocks[idx].block_number > block_number {
continue;
} else {
self.block_headers.insert(block_number, header); self.block_headers.insert(block_number, header);
self.blocks[idx].finalized = true; self.blocks[idx].finalized = true;
} }
@ -161,7 +174,7 @@ impl PartialComponent<CurrentTab> for BlockExplorer {
self.set_inactive(); self.set_inactive();
self.table_state.select(None); self.table_state.select(None);
self.scroll_state = self.scroll_state.position(0); self.scroll_state = self.scroll_state.position(0);
}, }
} }
} }
} }
@ -169,26 +182,38 @@ impl PartialComponent<CurrentTab> for BlockExplorer {
impl Component for BlockExplorer { impl Component for BlockExplorer {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::BestBlockInformation(header, block_number) => self.update_latest_block_info(header, block_number), Action::BestBlockInformation(header, block_number) => {
Action::FinalizedBlockInformation(header, block_number) => self.update_finalized_block_info(header, block_number), self.update_latest_block_info(header, block_number)
}
Action::FinalizedBlockInformation(header, block_number) => {
self.update_finalized_block_info(header, block_number)
}
Action::SetBlockAuthor(header, author) => { Action::SetBlockAuthor(header, author) => {
let _ = self.block_authors.insert(header, author); let _ = self.block_authors.insert(header, author);
Ok(None) Ok(None)
}, }
_ => Ok(None), _ => Ok(None),
} }
} }
@ -206,25 +231,30 @@ impl Component for BlockExplorer {
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_hash = H256::repeat_byte(69u8);
let default_author = "...".to_string(); let default_author = "...".to_string();
let rows = self.blocks let rows = self
.blocks
.iter() .iter()
.map(|info| { .map(|info| {
let header = self.block_headers let header = self
.block_headers
.get(&info.block_number) .get(&info.block_number)
.unwrap_or(&default_hash); .unwrap_or(&default_hash);
let author = self.block_authors let author = self.block_authors.get(&header).unwrap_or(&default_author);
.get(&header)
.unwrap_or(&default_author);
if info.finalized { if info.finalized {
Row::new(vec![ Row::new(vec![
Cell::from(Text::from(info.block_number.to_string()).alignment(Alignment::Left)), Cell::from(
Text::from(info.block_number.to_string()).alignment(Alignment::Left),
),
Cell::from(Text::from(header.to_string()).alignment(Alignment::Center)), Cell::from(Text::from(header.to_string()).alignment(Alignment::Center)),
Cell::from(Text::from(author.clone()).alignment(Alignment::Right)), Cell::from(Text::from(author.clone()).alignment(Alignment::Right)),
]).style(self.palette.create_highlight_style()) ])
.style(self.palette.create_highlight_style())
} else { } else {
Row::new(vec![ Row::new(vec![
Cell::from(Text::from(info.block_number.to_string()).alignment(Alignment::Left)), Cell::from(
Text::from(info.block_number.to_string()).alignment(Alignment::Left),
),
Cell::from(Text::from(header.to_string()).alignment(Alignment::Center)), Cell::from(Text::from(header.to_string()).alignment(Alignment::Center)),
Cell::from(Text::from(author.clone()).alignment(Alignment::Right)), Cell::from(Text::from(author.clone()).alignment(Alignment::Right)),
]) ])
@ -232,12 +262,14 @@ impl Component for BlockExplorer {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let max_block_number_length = self.blocks let max_block_number_length = self
.blocks
.front() .front()
.map(|block| block.block_number) .map(|block| block.block_number)
.unwrap_or_default() .unwrap_or_default()
.checked_ilog10() .checked_ilog10()
.unwrap_or(0) as u16 + 1; .unwrap_or(0) as u16
+ 1;
let table = Table::new( let table = Table::new(
rows, rows,
@ -250,13 +282,15 @@ impl Component for BlockExplorer {
.style(self.palette.create_basic_style(false)) .style(self.palette.create_basic_style(false))
.highlight_style(self.palette.create_basic_style(true)) .highlight_style(self.palette.create_basic_style(true))
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.padding(Padding::right(2)) .border_type(border_type)
.title_alignment(Alignment::Right) .padding(Padding::right(2))
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Blocks")); .title_style(self.palette.create_title_style(false))
.title("Blocks"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -267,7 +301,10 @@ impl Component for BlockExplorer {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -4,13 +4,15 @@ use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
widgets::{Block, Padding, Paragraph, Wrap}, widgets::{Block, Padding, Paragraph, Wrap},
Frame Frame,
}; };
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
action::Action,
components::generic::PartialComponent, components::generic::PartialComponent,
config::Config, action::Action, palette::StylePalette, config::Config,
palette::StylePalette,
widgets::{BigText, PixelSize}, widgets::{BigText, PixelSize},
}; };
@ -18,7 +20,7 @@ use crate::{
pub struct BlockTicker { pub struct BlockTicker {
last_block: u32, last_block: u32,
last_block_time: Instant, last_block_time: Instant,
palette: StylePalette palette: StylePalette,
} }
impl Default for BlockTicker { impl Default for BlockTicker {
@ -49,12 +51,18 @@ impl PartialComponent<CurrentTab> for BlockTicker {}
impl Component for BlockTicker { impl Component for BlockTicker {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -88,13 +96,15 @@ impl Component for BlockTicker {
if width < text_width || height < 7 { if width < text_width || height < 7 {
let paragraph = Paragraph::new(text) let paragraph = Paragraph::new(text)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0)) .title_style(self.palette.create_title_style(false))
.title("Passed")) .padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0))
.title("Passed"),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, place); frame.render_widget(paragraph, place);
@ -103,23 +113,27 @@ impl Component for BlockTicker {
.centered() .centered()
.pixel_size(PixelSize::Quadrant) .pixel_size(PixelSize::Quadrant)
.style(self.palette.create_basic_style(false)) .style(self.palette.create_basic_style(false))
.lines(vec![ .lines(vec![text.into()])
text.into(),
])
.build(); .build();
let paragraph = Paragraph::new("") let paragraph = Paragraph::new("")
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Passed")) .title_style(self.palette.create_title_style(false))
.title("Passed"),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, place); frame.render_widget(paragraph, place);
let height_offset = height.saturating_sub(2) / 2; let height_offset = height.saturating_sub(2) / 2;
let place = place.offset(ratatui::layout::Offset { x: 1, y: height_offset as i32 }) let place = place
.offset(ratatui::layout::Offset {
x: 1,
y: height_offset as i32,
})
.intersection(place); .intersection(place);
frame.render_widget(big_text, place); frame.render_widget(big_text, place);
} }

View File

@ -8,9 +8,11 @@ use ratatui::{
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
action::Action,
components::generic::PartialComponent, components::generic::PartialComponent,
config::Config, action::Action, palette::StylePalette, config::Config,
widgets::{PixelSize, BigText}, palette::StylePalette,
widgets::{BigText, PixelSize},
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -22,9 +24,9 @@ pub struct CurrentEpoch {
impl CurrentEpoch { impl CurrentEpoch {
const SECONDS_IN_BLOCK: u64 = 6; const SECONDS_IN_BLOCK: u64 = 6;
const SESSION_LENGTH: u64 = 2_400; const SESSION_LENGTH: u64 = 2_400;
const SECONDS_IN_DAY: u64 = 86_400; const SECONDS_IN_DAY: u64 = 86_400;
const SECONDS_IN_HOUR: u64 = 3_600; const SECONDS_IN_HOUR: u64 = 3_600;
} }
impl PartialComponent<CurrentTab> for CurrentEpoch {} impl PartialComponent<CurrentTab> for CurrentEpoch {}
@ -32,12 +34,18 @@ impl PartialComponent<CurrentTab> for CurrentEpoch {}
impl Component for CurrentEpoch { impl Component for CurrentEpoch {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -47,7 +55,7 @@ impl Component for CurrentEpoch {
Action::SetEpochProgress(number, progress) => { Action::SetEpochProgress(number, progress) => {
self.number = number; self.number = number;
self.progress = progress; self.progress = progress;
}, }
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -56,7 +64,8 @@ impl Component for CurrentEpoch {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [place, _] = super::layouts::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 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; let hours = (seconds_to_next % Self::SECONDS_IN_DAY) / Self::SECONDS_IN_HOUR;
let minutes = (seconds_to_next % Self::SECONDS_IN_HOUR) / 60; let minutes = (seconds_to_next % Self::SECONDS_IN_HOUR) / 60;
let seconds = seconds_to_next % 60; let seconds = seconds_to_next % 60;
@ -73,20 +82,24 @@ impl Component for CurrentEpoch {
if width < text_width || height < 7 { if width < text_width || height < 7 {
let text = vec![ let text = vec![
Line::from(text), Line::from(text),
Line::from(format!("{}{} {}{}", Line::from(format!(
if big_time { hours } else { minutes }, "{}{} {}{}",
if big_time { "hrs" } else { "mins" }, if big_time { hours } else { minutes },
if big_time { minutes } else { seconds }, if big_time { "hrs" } else { "mins" },
if big_time { "mins" } else { "secs" })), if big_time { minutes } else { seconds },
if big_time { "mins" } else { "secs" }
)),
]; ];
let paragraph = Paragraph::new(text) let paragraph = Paragraph::new(text)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.padding(Padding::new(0, 0, height.saturating_sub(3) / 2, 0)) .title_style(self.palette.create_title_style(false))
.title("Epoch")) .padding(Padding::new(0, 0, height.saturating_sub(3) / 2, 0))
.title("Epoch"),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, place); frame.render_widget(paragraph, place);
@ -95,28 +108,36 @@ impl Component for CurrentEpoch {
.centered() .centered()
.pixel_size(PixelSize::Quadrant) .pixel_size(PixelSize::Quadrant)
.style(self.palette.create_basic_style(false)) .style(self.palette.create_basic_style(false))
.lines(vec![ .lines(vec![text.into()])
text.into(),
])
.build(); .build();
let paragraph = Paragraph::new("") let paragraph = Paragraph::new("")
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_style(self.palette.create_title_style(false)) .border_type(border_type)
.title_top(Line::from("Epoch").right_aligned()) .title_style(self.palette.create_title_style(false))
.title_top(Line::from(format!("{}{} {}{}", .title_top(Line::from("Epoch").right_aligned())
.title_top(
Line::from(format!(
"{}{} {}{}",
if big_time { hours } else { minutes }, if big_time { hours } else { minutes },
if big_time { "hrs" } else { "mins" }, if big_time { "hrs" } else { "mins" },
if big_time { minutes } else { seconds }, if big_time { minutes } else { seconds },
if big_time { "mins" } else { "secs" })) if big_time { "mins" } else { "secs" }
.centered())) ))
.centered(),
),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, place); frame.render_widget(paragraph, place);
let height_offset = height.saturating_sub(2) / 2; let height_offset = height.saturating_sub(2) / 2;
let place = place.offset(ratatui::layout::Offset { x: 1, y: height_offset as i32 }) let place = place
.offset(ratatui::layout::Offset {
x: 1,
y: height_offset as i32,
})
.intersection(place); .intersection(place);
frame.render_widget(big_text, place); frame.render_widget(big_text, place);
} }

View File

@ -8,22 +8,26 @@ use ratatui::{
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
config::Config, action::Action, palette::StylePalette, types::EraInfo, action::Action,
components::generic::PartialComponent, widgets::{PixelSize, BigText}, components::generic::PartialComponent,
config::Config,
palette::StylePalette,
types::EraInfo,
widgets::{BigText, PixelSize},
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct CurrentEra{ pub struct CurrentEra {
era: EraInfo, era: EraInfo,
palette: StylePalette, palette: StylePalette,
} }
impl CurrentEra { impl CurrentEra {
const ERA_OFFSET_IN_SLOTS: u64 = 2_400 * 6; const ERA_OFFSET_IN_SLOTS: u64 = 2_400 * 6;
const ERA_OFFSET_IN_MILLIS: u64 = Self::ERA_OFFSET_IN_SLOTS * 6_000; const ERA_OFFSET_IN_MILLIS: u64 = Self::ERA_OFFSET_IN_SLOTS * 6_000;
const MILLIS_IN_DAY: u64 = 86_400_000; const MILLIS_IN_DAY: u64 = 86_400_000;
const MILLIS_IN_HOUR: u64 = 3_600_000; const MILLIS_IN_HOUR: u64 = 3_600_000;
const MILLIS_IN_MINUTE: u64 = 60_000; const MILLIS_IN_MINUTE: u64 = 60_000;
} }
@ -32,12 +36,18 @@ impl PartialComponent<CurrentTab> for CurrentEra {}
impl Component for CurrentEra { impl Component for CurrentEra {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -45,7 +55,7 @@ impl Component for CurrentEra {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetActiveEra(era_info) => self.era = era_info, Action::SetActiveEra(era_info) => self.era = era_info,
_ => {}, _ => {}
}; };
Ok(None) Ok(None)
} }
@ -83,21 +93,25 @@ impl Component for CurrentEra {
if width < text_width || height < 7 { if width < text_width || height < 7 {
let text = vec![ let text = vec![
Line::from(text), Line::from(text),
Line::from(format!("{}{}{} {}{}", Line::from(format!(
reversed_char, "{}{}{} {}{}",
if big_time { hours } else { minutes }, reversed_char,
if big_time { "hrs" } else { "mins" }, if big_time { hours } else { minutes },
if big_time { minutes } else { seconds }, if big_time { "hrs" } else { "mins" },
if big_time { "mins" } else { "secs" })), if big_time { minutes } else { seconds },
if big_time { "mins" } else { "secs" }
)),
]; ];
let paragraph = Paragraph::new(text) let paragraph = Paragraph::new(text)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.padding(Padding::new(0, 0, (height.saturating_sub(3)) / 2, 0)) .title_style(self.palette.create_title_style(false))
.title("Era")) .padding(Padding::new(0, 0, (height.saturating_sub(3)) / 2, 0))
.title("Era"),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, place); frame.render_widget(paragraph, place);
@ -106,28 +120,36 @@ impl Component for CurrentEra {
.centered() .centered()
.pixel_size(PixelSize::Quadrant) .pixel_size(PixelSize::Quadrant)
.style(self.palette.create_basic_style(false)) .style(self.palette.create_basic_style(false))
.lines(vec![ .lines(vec![text.into()])
text.into(),
])
.build(); .build();
let paragraph = Paragraph::new("") let paragraph = Paragraph::new("")
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_style(self.palette.create_title_style(false)) .border_type(border_type)
.title_top(Line::from("Era").right_aligned()) .title_style(self.palette.create_title_style(false))
.title_top(Line::from(format!("{}{} {}{}", .title_top(Line::from("Era").right_aligned())
.title_top(
Line::from(format!(
"{}{} {}{}",
if big_time { hours } else { minutes }, if big_time { hours } else { minutes },
if big_time { "hrs" } else { "mins" }, if big_time { "hrs" } else { "mins" },
if big_time { minutes } else { seconds }, if big_time { minutes } else { seconds },
if big_time { "mins" } else { "secs" })) if big_time { "mins" } else { "secs" }
.centered())) ))
.centered(),
),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, place); frame.render_widget(paragraph, place);
let height_offset = height.saturating_sub(2) / 2; let height_offset = height.saturating_sub(2) / 2;
let place = place.offset(ratatui::layout::Offset { x: 1, y: height_offset as i32 }) let place = place
.offset(ratatui::layout::Offset {
x: 1,
y: height_offset as i32,
})
.intersection(place); .intersection(place);
frame.render_widget(big_text, place); frame.render_widget(big_text, place);
} }

View File

@ -1,21 +1,23 @@
use std::collections::{HashMap, VecDeque};
use std::usize;
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
use ratatui::layout::{Constraint, Margin}; use ratatui::layout::{Constraint, Margin};
use ratatui::widgets::{Padding, Scrollbar, ScrollbarOrientation}; use ratatui::widgets::{Padding, Scrollbar, ScrollbarOrientation};
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
widgets::{Block, ScrollbarState, Cell, Row, Table, TableState}, text::Text,
Frame widgets::{Block, Cell, Row, ScrollbarState, Table, TableState},
Frame,
}; };
use std::collections::{HashMap, VecDeque};
use std::usize;
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
components::generic::{Activatable, Scrollable, PartialComponent}, action::Action,
components::generic::{Activatable, PartialComponent, Scrollable},
config::Config,
palette::StylePalette,
types::CasperExtrinsicDetails, types::CasperExtrinsicDetails,
action::Action, config::Config, palette::StylePalette,
}; };
pub struct ExtrinsicExplorer { pub struct ExtrinsicExplorer {
@ -35,10 +37,18 @@ impl Default for ExtrinsicExplorer {
} }
impl Activatable for ExtrinsicExplorer { impl Activatable for ExtrinsicExplorer {
fn is_active(&self) -> bool { self.is_active } fn is_active(&self) -> bool {
fn is_inactive(&self) -> bool { !self.is_active } self.is_active
fn set_inactive(&mut self) { self.is_active = false; } }
fn set_active(&mut self) { self.is_active = true; } 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 { impl Scrollable for ExtrinsicExplorer {
@ -96,10 +106,9 @@ impl ExtrinsicExplorer {
} }
fn update_used_explorer_block(&mut self, block_number: u32) { fn update_used_explorer_block(&mut self, block_number: u32) {
let maybe_exts = self.extrinsics let maybe_exts = self.extrinsics.get(&block_number).map(|exts| exts.to_vec());
.get(&block_number) let exts_length = self
.map(|exts| exts.to_vec()); .extrinsics
let exts_length = self.extrinsics
.get(&block_number) .get(&block_number)
.map(|exts| exts.len()) .map(|exts| exts.len())
.unwrap_or_default(); .unwrap_or_default();
@ -108,7 +117,6 @@ impl ExtrinsicExplorer {
self.scroll_state = self.scroll_state.content_length(exts_length); self.scroll_state = self.scroll_state.content_length(exts_length);
} }
fn update_extrinsics_for_header( fn update_extrinsics_for_header(
&mut self, &mut self,
block_number: u32, block_number: u32,
@ -125,11 +133,13 @@ impl ExtrinsicExplorer {
} }
fn send_used_explorer_log(&mut self, index: usize) -> Result<Option<Action>> { fn send_used_explorer_log(&mut self, index: usize) -> Result<Option<Action>> {
let maybe_log = self.current_extrinsics let maybe_log = self
.current_extrinsics
.as_ref() .as_ref()
.map(|ext| ext.get(index).map(|ext| { .map(|ext| {
hex::encode(&ext.field_bytes.clone()) ext.get(index)
})) .map(|ext| hex::encode(&ext.field_bytes.clone()))
})
.flatten(); .flatten();
Ok(Some(Action::UsedExplorerLog(maybe_log.clone()))) Ok(Some(Action::UsedExplorerLog(maybe_log.clone())))
} }
@ -150,25 +160,35 @@ impl PartialComponent<CurrentTab> for ExtrinsicExplorer {
impl Component for ExtrinsicExplorer { impl Component for ExtrinsicExplorer {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::UsedExplorerBlock(maybe_block_number) => Action::UsedExplorerBlock(maybe_block_number) => {
self.update_used_explorer_block(maybe_block_number.unwrap_or_default()), self.update_used_explorer_block(maybe_block_number.unwrap_or_default())
Action::ExtrinsicsForBlock(block_number, extrinsics) => }
self.update_extrinsics_for_header(block_number, extrinsics), Action::ExtrinsicsForBlock(block_number, extrinsics) => {
_ => {}, self.update_extrinsics_for_header(block_number, extrinsics)
}
_ => {}
}; };
Ok(None) Ok(None)
} }
@ -192,8 +212,10 @@ impl Component for ExtrinsicExplorer {
.iter() .iter()
.enumerate() .enumerate()
.map(|(idx, ext)| { .map(|(idx, ext)| {
longest_pallet_name_length = longest_pallet_name_length.max(ext.pallet_name.len()); longest_pallet_name_length =
longest_variant_name_length = longest_variant_name_length.max(ext.variant_name.len()); longest_pallet_name_length.max(ext.pallet_name.len());
longest_variant_name_length =
longest_variant_name_length.max(ext.variant_name.len());
Row::new(vec![ Row::new(vec![
Cell::from(Text::from(idx.to_string()).alignment(Alignment::Left)), Cell::from(Text::from(idx.to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(ext.pallet_name.clone()).alignment(Alignment::Right)), Cell::from(Text::from(ext.pallet_name.clone()).alignment(Alignment::Right)),
@ -217,13 +239,15 @@ impl Component for ExtrinsicExplorer {
.style(self.palette.create_basic_style(false)) .style(self.palette.create_basic_style(false))
.highlight_style(self.palette.create_basic_style(true)) .highlight_style(self.palette.create_basic_style(true))
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.padding(Padding::right(2)) .border_type(border_type)
.title_alignment(Alignment::Right) .padding(Padding::right(2))
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Extrinsics")); .title_style(self.palette.create_title_style(false))
.title("Extrinsics"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -234,7 +258,10 @@ impl Component for ExtrinsicExplorer {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,4 +1,3 @@
use std::collections::VecDeque;
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
@ -6,11 +5,11 @@ use ratatui::{
widgets::{Bar, BarChart, BarGroup, Block}, widgets::{Bar, BarChart, BarGroup, Block},
Frame, Frame,
}; };
use std::collections::VecDeque;
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
components::generic::PartialComponent, action::Action, components::generic::PartialComponent, config::Config, palette::StylePalette,
palette::StylePalette, config::Config, action::Action,
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -28,7 +27,8 @@ impl ExtrinsicsChart {
let (border_style, border_type) = self.palette.create_border_style(false); let (border_style, border_type) = self.palette.create_border_style(false);
let length = (width as usize) / (Self::BAR_WIDTH + Self::BAR_GAP); let length = (width as usize) / (Self::BAR_WIDTH + Self::BAR_GAP);
let bars: Vec<Bar> = self.extrinsics let bars: Vec<Bar> = self
.extrinsics
.iter() .iter()
.rev() .rev()
.take(length) .take(length)
@ -37,12 +37,14 @@ impl ExtrinsicsChart {
BarChart::default() BarChart::default()
.data(BarGroup::default().bars(&bars)) .data(BarGroup::default().bars(&bars))
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_style(self.palette.create_title_style(false)) .border_type(border_type)
.title_alignment(Alignment::Right) .title_style(self.palette.create_title_style(false))
.title("Tx. Heat Map")) .title_alignment(Alignment::Right)
.title("Tx. Heat Map"),
)
.bar_width(8) .bar_width(8)
.bar_gap(1) .bar_gap(1)
} }
@ -54,7 +56,11 @@ impl ExtrinsicsChart {
.text_value(ext_len.to_string()) .text_value(ext_len.to_string())
} }
fn update_extrinsics_length(&mut self, block_number: u32, extrinsics_length: usize) -> Result<()> { fn update_extrinsics_length(
&mut self,
block_number: u32,
extrinsics_length: usize,
) -> Result<()> {
match self.extrinsics.back() { match self.extrinsics.back() {
Some(back) => { Some(back) => {
if back.0 < block_number { if back.0 < block_number {
@ -63,7 +69,7 @@ impl ExtrinsicsChart {
let _ = self.extrinsics.pop_front(); let _ = self.extrinsics.pop_front();
} }
} }
}, }
None => { None => {
self.extrinsics.push_back((block_number, extrinsics_length)); self.extrinsics.push_back((block_number, extrinsics_length));
} }
@ -78,19 +84,27 @@ impl PartialComponent<CurrentTab> for ExtrinsicsChart {}
impl Component for ExtrinsicsChart { impl Component for ExtrinsicsChart {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
} }
Ok(()) Ok(())
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::ExtrinsicsLength(block, length) => self.update_extrinsics_length(block, length)?, Action::ExtrinsicsLength(block, length) => {
self.update_extrinsics_length(block, length)?
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)

View File

@ -7,9 +7,11 @@ use ratatui::{
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
action::Action,
components::generic::PartialComponent, components::generic::PartialComponent,
config::Config, action::Action, palette::StylePalette, config::Config,
widgets::{PixelSize, BigText}, palette::StylePalette,
widgets::{BigText, PixelSize},
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -30,12 +32,18 @@ impl PartialComponent<CurrentTab> for FinalizedBlock {}
impl Component for FinalizedBlock { impl Component for FinalizedBlock {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -60,13 +68,15 @@ impl Component for FinalizedBlock {
if width < text_width || height < 7 { if width < text_width || height < 7 {
let paragraph = Paragraph::new(text) let paragraph = Paragraph::new(text)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0)) .title_style(self.palette.create_title_style(false))
.title("Finalized")) .padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0))
.title("Finalized"),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, place); frame.render_widget(paragraph, place);
@ -75,23 +85,27 @@ impl Component for FinalizedBlock {
.centered() .centered()
.pixel_size(PixelSize::Quadrant) .pixel_size(PixelSize::Quadrant)
.style(self.palette.create_basic_style(false)) .style(self.palette.create_basic_style(false))
.lines(vec![ .lines(vec![text.into()])
text.into(),
])
.build(); .build();
let paragraph = Paragraph::new("") let paragraph = Paragraph::new("")
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Finalized")) .title_style(self.palette.create_title_style(false))
.title("Finalized"),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, place); frame.render_widget(paragraph, place);
let height_offset = height.saturating_sub(2) / 2; let height_offset = height.saturating_sub(2) / 2;
let place = place.offset(ratatui::layout::Offset { x: 1, y: height_offset as i32 }) let place = place
.offset(ratatui::layout::Offset {
x: 1,
y: height_offset as i32,
})
.intersection(place); .intersection(place);
frame.render_widget(big_text, place); frame.render_widget(big_text, place);
} }

View File

@ -7,15 +7,17 @@ use ratatui::{
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
action::Action,
components::generic::PartialComponent, components::generic::PartialComponent,
config::Config, action::Action, palette::StylePalette, config::Config,
widgets::{PixelSize, BigText}, palette::StylePalette,
widgets::{BigText, PixelSize},
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct LatestBlock { pub struct LatestBlock {
number: u32, number: u32,
palette: StylePalette palette: StylePalette,
} }
impl LatestBlock { impl LatestBlock {
@ -30,12 +32,18 @@ impl PartialComponent<CurrentTab> for LatestBlock {}
impl Component for LatestBlock { impl Component for LatestBlock {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -60,13 +68,15 @@ impl Component for LatestBlock {
if width < text_width || height < 7 { if width < text_width || height < 7 {
let paragraph = Paragraph::new(text) let paragraph = Paragraph::new(text)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0)) .title_style(self.palette.create_title_style(false))
.title("Latest")) .padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0))
.title("Latest"),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, place); frame.render_widget(paragraph, place);
@ -75,23 +85,27 @@ impl Component for LatestBlock {
.centered() .centered()
.pixel_size(PixelSize::Quadrant) .pixel_size(PixelSize::Quadrant)
.style(self.palette.create_basic_style(false)) .style(self.palette.create_basic_style(false))
.lines(vec![ .lines(vec![text.into()])
text.into(),
])
.build(); .build();
let paragraph = Paragraph::new("") let paragraph = Paragraph::new("")
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Latest")) .title_style(self.palette.create_title_style(false))
.title("Latest"),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, place); frame.render_widget(paragraph, place);
let height_offset = height.saturating_sub(2) / 2; let height_offset = height.saturating_sub(2) / 2;
let place = place.offset(ratatui::layout::Offset { x: 1, y: height_offset as i32 }) let place = place
.offset(ratatui::layout::Offset {
x: 1,
y: height_offset as i32,
})
.intersection(place); .intersection(place);
frame.render_widget(big_text, place); frame.render_widget(big_text, place);
} }

View File

@ -5,46 +5,39 @@ pub fn explorer_layout(area: Rect) -> [Rect; 3] {
Constraint::Percentage(30), Constraint::Percentage(30),
Constraint::Percentage(40), Constraint::Percentage(40),
Constraint::Percentage(30), Constraint::Percentage(30),
]).areas(area) ])
.areas(area)
} }
pub fn explorer_header_layout(area: Rect) -> [Rect; 2] { pub fn explorer_header_layout(area: Rect) -> [Rect; 2] {
let [header, _, _] = explorer_layout(area); let [header, _, _] = explorer_layout(area);
Layout::horizontal([ Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(header)
Constraint::Percentage(50),
Constraint::Percentage(50),
]).areas(header)
} }
pub fn explorer_info_layout(area: Rect) -> [Rect; 2] { pub fn explorer_info_layout(area: Rect) -> [Rect; 2] {
let [info, _] = explorer_header_layout(area); let [info, _] = explorer_header_layout(area);
Layout::vertical([ Layout::vertical([Constraint::Percentage(100), Constraint::Percentage(100)]).areas(info)
Constraint::Percentage(100),
Constraint::Percentage(100),
]).areas(info)
} }
pub fn explorer_block_info_layout(area: Rect) -> [Rect; 3] { pub fn explorer_block_info_layout(area: Rect) -> [Rect; 3] {
let [blocks, _] = explorer_info_layout(area); let [blocks, _] = explorer_info_layout(area);
Layout::horizontal([ Layout::horizontal([
Constraint::Percentage(33), Constraint::Percentage(33),
Constraint::Percentage(33), Constraint::Percentage(33),
Constraint::Percentage(33), Constraint::Percentage(33),
]).flex(Flex::SpaceBetween).areas(blocks) ])
.flex(Flex::SpaceBetween)
.areas(blocks)
} }
pub fn explorer_era_info_layout(area: Rect) -> [Rect; 2] { pub fn explorer_era_info_layout(area: Rect) -> [Rect; 2] {
let [_, blocks] = explorer_info_layout(area); let [_, blocks] = explorer_info_layout(area);
Layout::horizontal([ Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
Constraint::Percentage(50), .flex(Flex::SpaceBetween)
Constraint::Percentage(50), .areas(blocks)
]).flex(Flex::SpaceBetween).areas(blocks)
} }
pub fn explorer_scrollbars_layout(area: Rect) -> [Rect; 2] { pub fn explorer_scrollbars_layout(area: Rect) -> [Rect; 2] {
let [_, place, _] = explorer_layout(area); let [_, place, _] = explorer_layout(area);
Layout::horizontal([ Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(place)
Constraint::Percentage(50),
Constraint::Percentage(50),
]).areas(place)
} }

View File

@ -3,13 +3,12 @@ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Line, text::Line,
widgets::{Block, Paragraph, Wrap}, widgets::{Block, Paragraph, Wrap},
Frame Frame,
}; };
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
components::generic::PartialComponent, action::Action, components::generic::PartialComponent, config::Config, palette::StylePalette,
action::Action, config::Config, palette::StylePalette
}; };
#[derive(Default)] #[derive(Default)]
@ -23,9 +22,12 @@ impl PartialComponent<CurrentTab> for LogExplorer {}
impl Component for LogExplorer { impl Component for LogExplorer {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
.with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -33,7 +35,7 @@ impl Component for LogExplorer {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::UsedExplorerLog(maybe_log) => self.maybe_log = maybe_log, Action::UsedExplorerLog(maybe_log) => self.maybe_log = maybe_log,
_ => {}, _ => {}
}; };
Ok(None) Ok(None)
} }
@ -49,12 +51,14 @@ impl Component for LogExplorer {
}; };
let paragraph = Paragraph::new(line) let paragraph = Paragraph::new(line)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Logs")) .title_style(self.palette.create_title_style(false))
.title("Logs"),
)
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });

View File

@ -9,25 +9,25 @@ use super::{
}; };
use crate::{action::Action, app::Mode, config::Config}; 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 block_explorer;
mod block_ticker;
mod current_epoch;
mod current_era;
mod extrinsic_explorer; mod extrinsic_explorer;
mod extrinsics_chart;
mod finalized_block;
mod latest_block;
pub mod layouts;
mod log_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 block_explorer::BlockExplorer;
use block_ticker::BlockTicker;
use current_epoch::CurrentEpoch;
use current_era::CurrentEra;
use extrinsic_explorer::ExtrinsicExplorer; use extrinsic_explorer::ExtrinsicExplorer;
use extrinsics_chart::ExtrinsicsChart;
use finalized_block::FinalizedBlock;
use latest_block::LatestBlock;
use log_explorer::LogExplorer; use log_explorer::LogExplorer;
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
@ -40,7 +40,7 @@ pub enum CurrentTab {
pub struct Explorer { pub struct Explorer {
is_active: bool, is_active: bool,
current_tab: CurrentTab, current_tab: CurrentTab,
components: Vec<Box<dyn PartialComponent<CurrentTab>>> components: Vec<Box<dyn PartialComponent<CurrentTab>>>,
} }
impl Default for Explorer { impl Default for Explorer {
@ -58,7 +58,7 @@ impl Default for Explorer {
Box::new(BlockExplorer::default()), Box::new(BlockExplorer::default()),
Box::new(ExtrinsicExplorer::default()), Box::new(ExtrinsicExplorer::default()),
Box::new(LogExplorer::default()), Box::new(LogExplorer::default()),
] ],
} }
} }
} }
@ -80,10 +80,18 @@ impl Explorer {
} }
impl Activatable for Explorer { impl Activatable for Explorer {
fn is_active(&self) -> bool { self.is_active } fn is_active(&self) -> bool {
fn is_inactive(&self) -> bool { !self.is_active } self.is_active
fn set_inactive(&mut self) { self.is_active = false; } }
fn set_active(&mut self) { self.is_active = true; } 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<CurrentTab> for Explorer {} impl PartialComponent<CurrentTab> for Explorer {}
@ -106,7 +114,9 @@ impl Component for Explorer {
} }
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> { fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
if self.is_inactive() { return Ok(None); } if self.is_inactive() {
return Ok(None);
}
match key.code { match key.code {
KeyCode::Esc => { KeyCode::Esc => {
@ -116,19 +126,19 @@ impl Component for Explorer {
component.set_active_tab(self.current_tab); component.set_active_tab(self.current_tab);
} }
return Ok(Some(Action::SetActiveScreen(Mode::Menu))); return Ok(Some(Action::SetActiveScreen(Mode::Menu)));
}, }
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => { KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => {
self.move_right(); self.move_right();
for component in self.components.iter_mut() { for component in self.components.iter_mut() {
component.set_active_tab(self.current_tab); component.set_active_tab(self.current_tab);
} }
}, }
KeyCode::Char('h') | KeyCode::Left => { KeyCode::Char('h') | KeyCode::Left => {
self.move_left(); self.move_left();
for component in self.components.iter_mut() { for component in self.components.iter_mut() {
component.set_active_tab(self.current_tab); component.set_active_tab(self.current_tab);
} }
}, }
_ => { _ => {
for component in self.components.iter_mut() { for component in self.components.iter_mut() {
let maybe_action = component.handle_key_event(key); let maybe_action = component.handle_key_event(key);
@ -136,7 +146,7 @@ impl Component for Explorer {
return maybe_action; return maybe_action;
} }
} }
}, }
} }
Ok(None) Ok(None)
} }

View File

@ -79,8 +79,7 @@ impl Component for FpsCounter {
let message = format!( let message = format!(
" {:.2} ticks/sec | {:.2} FPS", " {:.2} ticks/sec | {:.2} FPS",
self.ticks_per_second, self.ticks_per_second, self.frames_per_second
self.frames_per_second
); );
let span = Span::styled(message, Style::new().dim()); let span = Span::styled(message, Style::new().dim());

View File

@ -1,5 +1,5 @@
use color_eyre::Result;
use crate::action::Action; use crate::action::Action;
use color_eyre::Result;
use super::Activatable; use super::Activatable;

View File

@ -1,9 +1,9 @@
mod activatable; mod activatable;
mod helpable; mod helpable;
mod scrollable;
mod partial; mod partial;
mod scrollable;
pub use activatable::Activatable; pub use activatable::Activatable;
pub use helpable::Helpable; pub use helpable::Helpable;
pub use scrollable::Scrollable;
pub use partial::PartialComponent; pub use partial::PartialComponent;
pub use scrollable::Scrollable;

View File

@ -1,10 +1,11 @@
use std::{
cmp::{PartialEq, PartialOrd}, ops::{Add, Sub}
};
use crossterm::event::KeyCode; use crossterm::event::KeyCode;
use std::{
cmp::{PartialEq, PartialOrd},
ops::{Add, Sub},
};
use color_eyre::Result;
use crate::action::Action; use crate::action::Action;
use color_eyre::Result;
pub trait Scrollable { pub trait Scrollable {
type IndexType: Add<Output = Self::IndexType> type IndexType: Add<Output = Self::IndexType>
@ -17,11 +18,18 @@ pub trait Scrollable {
fn selected_index(&self) -> Option<Self::IndexType>; fn selected_index(&self) -> Option<Self::IndexType>;
fn items_length(&self) -> Self::IndexType; fn items_length(&self) -> Self::IndexType;
fn apply_next_row(&mut self, _new_index: Self::IndexType) -> Result<Option<Action>> { Ok(None) } fn apply_next_row(&mut self, _new_index: Self::IndexType) -> Result<Option<Action>> {
fn apply_prev_row(&mut self, _new_index: Self::IndexType) -> Result<Option<Action>> { Ok(None) } Ok(None)
fn apply_first_row(&mut self) -> Result<Option<Action>> { Ok(None) } }
fn apply_last_row(&mut self) -> Result<Option<Action>> { Ok(None) } fn apply_prev_row(&mut self, _new_index: Self::IndexType) -> Result<Option<Action>> {
Ok(None)
}
fn apply_first_row(&mut self) -> Result<Option<Action>> {
Ok(None)
}
fn apply_last_row(&mut self) -> Result<Option<Action>> {
Ok(None)
}
fn next_row(&mut self) -> Result<Option<Action>> { fn next_row(&mut self) -> Result<Option<Action>> {
let length = self.items_length(); let length = self.items_length();
@ -36,7 +44,7 @@ pub trait Scrollable {
} else { } else {
index index
} }
}, }
None => 0.into(), None => 0.into(),
}; };
self.apply_next_row(new_index) self.apply_next_row(new_index)
@ -55,7 +63,7 @@ pub trait Scrollable {
index - 1.into() index - 1.into()
} }
} }
None => 0.into() None => 0.into(),
}; };
self.apply_prev_row(new_index) self.apply_prev_row(new_index)
} }

View File

@ -10,7 +10,7 @@ use ratatui::{
use super::Component; use super::Component;
use crate::{ use crate::{
action::Action, action::Action,
widgets::{DotSpinner, OghamCenter, VerticalBlocks} widgets::{DotSpinner, OghamCenter, VerticalBlocks},
}; };
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -73,7 +73,7 @@ impl Component for Health {
self.peers = peers; self.peers = peers;
self.is_syncing = is_syncing; self.is_syncing = is_syncing;
self.should_have_peers = should_have_peers; self.should_have_peers = should_have_peers;
}, }
Action::SetNodeName(name) => self.name = name, Action::SetNodeName(name) => self.name = name,
Action::SetPendingExtrinsicsLength(length) => self.tx_pool_length = length, Action::SetPendingExtrinsicsLength(length) => self.tx_pool_length = length,
Action::NominatorsNumber(number) => self.nominators_count = number, Action::NominatorsNumber(number) => self.nominators_count = number,

View File

@ -1,18 +1,18 @@
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyEvent, KeyCode}; use crossterm::event::{KeyCode, KeyEvent};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Margin, Rect}, layout::{Alignment, Constraint, Flex, Layout, Margin, Rect},
style::{palette::tailwind, Style, Modifier}, style::{palette::tailwind, Modifier, Style},
text::Text, text::Text,
widgets::{ widgets::{
Block, BorderType, Cell, Clear, HighlightSpacing, Paragraph, Row, Block, BorderType, Cell, Clear, HighlightSpacing, Paragraph, Row, Scrollbar,
Scrollbar, ScrollbarOrientation, ScrollbarState, Table, TableState, ScrollbarOrientation, ScrollbarState, Table, TableState,
}, },
Frame Frame,
}; };
use super::palette::StylePalette;
use super::generic::{Activatable, Helpable, Scrollable}; use super::generic::{Activatable, Helpable, Scrollable};
use super::palette::StylePalette;
use super::Component; use super::Component;
use crate::{action::Action, app::Mode, config::Config}; use crate::{action::Action, app::Mode, config::Config};
@ -49,13 +49,21 @@ impl Default for Help {
} }
impl Activatable for Help { impl Activatable for Help {
fn is_active(&self) -> bool { self.is_active } fn is_active(&self) -> bool {
fn is_inactive(&self) -> bool { !self.is_active } self.is_active
fn set_inactive(&mut self) { self.is_active = false; } }
fn set_active(&mut self) { self.is_active = true; } 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 Helpable for Help { } impl Helpable for Help {}
impl Scrollable for Help { impl Scrollable for Help {
type IndexType = usize; type IndexType = usize;
@ -80,11 +88,16 @@ impl Scrollable for Help {
impl Component for Help { impl Component for Help {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Help) { if let Some(style) = config.styles.get(&crate::app::Mode::Help) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -96,7 +109,9 @@ impl Component for Help {
if self.current_mode != mode { if self.current_mode != mode {
self.current_mode = mode; self.current_mode = mode;
self.table_state.select(None); self.table_state.select(None);
self.scroll_state = ScrollbarState::new((self.current_mode.get_help_data().len() - 1) * ITEM_HEIGHT); self.scroll_state = ScrollbarState::new(
(self.current_mode.get_help_data().len() - 1) * ITEM_HEIGHT,
);
} }
} }
_ => {} _ => {}
@ -111,18 +126,14 @@ impl Component for Help {
KeyCode::Esc if self.is_active() => { KeyCode::Esc if self.is_active() => {
self.set_inactive(); self.set_inactive();
Ok(Some(Action::SetActiveScreen(self.current_mode))) Ok(Some(Action::SetActiveScreen(self.current_mode)))
}, }
_ => Ok(None), _ => Ok(None),
} }
} }
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active() { if self.is_active() {
let highlight_symbol = Text::from(vec![ let highlight_symbol = Text::from(vec!["".into(), "".into(), "".into()]);
"".into(),
"".into(),
"".into(),
]);
let table = Table::new( let table = Table::new(
self.current_mode self.current_mode
@ -135,38 +146,46 @@ impl Component for Help {
_ => tailwind::SLATE.c900, _ => tailwind::SLATE.c900,
}; };
Row::from(content Row::from(
.into_iter() content
.map(|data| Cell::from(Text::from(*data))) .into_iter()
.collect::<Row>() .map(|data| Cell::from(Text::from(*data)))
.style(Style::default().fg(tailwind::BLUE.c200).bg(color)) .collect::<Row>()
.height(ITEM_HEIGHT as u16)) .style(Style::default().fg(tailwind::BLUE.c200).bg(color))
.height(ITEM_HEIGHT as u16),
)
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
self.current_mode.get_help_constraints() self.current_mode.get_help_constraints(),
) )
.header(self.current_mode .header(
.get_help_headers() self.current_mode
.into_iter() .get_help_headers()
.map(|h| Cell::from(Text::from(*h))) .into_iter()
.collect::<Row>() .map(|h| Cell::from(Text::from(*h)))
.style(Style::default() .collect::<Row>()
.fg(tailwind::SLATE.c200) .style(
.bg(tailwind::BLUE.c900) Style::default()
) .fg(tailwind::SLATE.c200)
.height(1) .bg(tailwind::BLUE.c900),
)
.height(1),
)
.highlight_style(
Style::default()
.add_modifier(Modifier::REVERSED)
.fg(tailwind::BLUE.c400)
.bg(tailwind::YELLOW.c200),
) )
.highlight_style(Style::default()
.add_modifier(Modifier::REVERSED)
.fg(tailwind::BLUE.c400)
.bg(tailwind::YELLOW.c200))
.highlight_spacing(HighlightSpacing::Always) .highlight_spacing(HighlightSpacing::Always)
.highlight_symbol(highlight_symbol) .highlight_symbol(highlight_symbol)
.block(Block::default() .block(
.style(Style::default().bg(tailwind::SLATE.c950)) Block::default()
.title_alignment(Alignment::Right) .style(Style::default().bg(tailwind::SLATE.c950))
.title_style(tailwind::YELLOW.c200) .title_alignment(Alignment::Right)
.title(self.current_mode.get_help_title())); .title_style(tailwind::YELLOW.c200)
.title(self.current_mode.get_help_title()),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -174,11 +193,16 @@ impl Component for Help {
.end_symbol(None); .end_symbol(None);
let footer = Paragraph::new(Text::from(self.current_mode.get_help_text())) let footer = Paragraph::new(Text::from(self.current_mode.get_help_text()))
.style(Style::default().fg(tailwind::SLATE.c200).bg(tailwind::SLATE.c950)) .style(
Style::default()
.fg(tailwind::SLATE.c200)
.bg(tailwind::SLATE.c950),
)
.centered() .centered()
.block(Block::bordered() .block(
.border_type(BorderType::Double) Block::bordered()
.border_style(Style::default().fg(tailwind::BLUE.c400)) .border_type(BorderType::Double)
.border_style(Style::default().fg(tailwind::BLUE.c400)),
); );
let v = Layout::vertical([Constraint::Length(23)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Length(23)]).flex(Flex::Center);
@ -187,14 +211,16 @@ impl Component for Help {
let [area] = v.areas(area); let [area] = v.areas(area);
let [area] = h.areas(area); let [area] = h.areas(area);
let [main_area, footer_area] = Layout::vertical([ let [main_area, footer_area] =
Constraint::Fill(1), Layout::vertical([Constraint::Fill(1), Constraint::Length(3)]).areas(area);
Constraint::Length(3),
]).areas(area);
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_stateful_widget(table, main_area, &mut self.table_state); frame.render_stateful_widget(table, main_area, &mut self.table_state);
frame.render_stateful_widget(scrollbar, main_area.inner(Margin::new(1, 2)), &mut self.scroll_state); frame.render_stateful_widget(
scrollbar,
main_area.inner(Margin::new(1, 2)),
&mut self.scroll_state,
);
frame.render_widget(footer, footer_area); frame.render_widget(footer, footer_area);
} }
Ok(()) Ok(())

View File

@ -1,34 +1,22 @@
use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::layout::{Constraint, Layout, Rect};
pub fn global_layout(area: Rect) -> [Rect; 2] { pub fn global_layout(area: Rect) -> [Rect; 2] {
Layout::vertical([ Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).areas(area)
Constraint::Length(1),
Constraint::Fill(1),
]).areas(area)
} }
pub fn header_layout(area: Rect) -> [Rect; 2] { pub fn header_layout(area: Rect) -> [Rect; 2] {
let [header, _] = global_layout(area); let [header, _] = global_layout(area);
Layout::horizontal([ Layout::horizontal([Constraint::Fill(1), Constraint::Length(27)]).areas(header)
Constraint::Fill(1),
Constraint::Length(27),
]).areas(header)
} }
pub fn main_layout(area: Rect) -> [Rect; 2] { pub fn main_layout(area: Rect) -> [Rect; 2] {
let [_, main] = global_layout(area); let [_, main] = global_layout(area);
Layout::horizontal([ Layout::horizontal([Constraint::Max(30), Constraint::Fill(1)]).areas(main)
Constraint::Max(30),
Constraint::Fill(1),
]).areas(main)
} }
pub fn menu_layout(area: Rect) -> [Rect; 2] { pub fn menu_layout(area: Rect) -> [Rect; 2] {
let [menu, _] = main_layout(area); let [menu, _] = main_layout(area);
Layout::vertical([ Layout::vertical([Constraint::Min(0), Constraint::Length(5)]).areas(menu)
Constraint::Min(0),
Constraint::Length(5),
]).areas(menu)
} }
pub fn screen_layout(area: Rect) -> Rect { pub fn screen_layout(area: Rect) -> Rect {

View File

@ -1,10 +1,10 @@
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent};
use ratatui::{prelude::*, widgets::*}; use ratatui::{prelude::*, widgets::*};
use crossterm::event::{KeyEvent, KeyCode};
use super::Component;
use super::palette::StylePalette; use super::palette::StylePalette;
use crate::{config::Config, action::Action, app::Mode}; use super::Component;
use crate::{action::Action, app::Mode, config::Config};
use super::generic::{Activatable, Helpable, Scrollable}; use super::generic::{Activatable, Helpable, Scrollable};
@ -16,17 +16,27 @@ pub struct Menu {
} }
impl Default for Menu { impl Default for Menu {
fn default() -> Self { Menu::new() } fn default() -> Self {
Menu::new()
}
} }
impl Activatable for Menu { impl Activatable for Menu {
fn is_active(&self) -> bool { self.is_active } fn is_active(&self) -> bool {
fn is_inactive(&self) -> bool { !self.is_active } self.is_active
fn set_inactive(&mut self) { self.is_active = false; } }
fn set_active(&mut self) { self.is_active = true; } 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 Helpable for Menu { } impl Helpable for Menu {}
impl Scrollable for Menu { impl Scrollable for Menu {
type IndexType = usize; type IndexType = usize;
@ -62,7 +72,6 @@ impl Menu {
], ],
palette: StylePalette::default(), palette: StylePalette::default(),
is_active: true, is_active: true,
}; };
new_list.list_state.select(Some(0)); new_list.list_state.select(Some(0));
new_list new_list
@ -90,11 +99,16 @@ impl Component for Menu {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Menu) { if let Some(style) = config.styles.get(&crate::app::Mode::Menu) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
.with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
} }
Ok(()) Ok(())
} }
@ -111,7 +125,7 @@ impl Component for Menu {
Some(2) => Ok(Some(Action::SetActiveScreen(Mode::Validator))), Some(2) => Ok(Some(Action::SetActiveScreen(Mode::Validator))),
_ => Ok(Some(Action::SetActiveScreen(Mode::Empty))), _ => Ok(Some(Action::SetActiveScreen(Mode::Empty))),
} }
}, }
KeyCode::Char('?') if self.is_active() => self.open_help_popup(), KeyCode::Char('?') if self.is_active() => self.open_help_popup(),
_ => Ok(None), _ => Ok(None),
} }
@ -126,12 +140,13 @@ impl Component for Menu {
.border_type(border_type); .border_type(border_type);
let list = List::default() let list = List::default()
.items(self.items .items(
.iter() self.items
.map(|item| ListItem::new( .iter()
Line::raw(item.as_str()).alignment(Alignment::Center) .map(|item| {
)) ListItem::new(Line::raw(item.as_str()).alignment(Alignment::Center))
.collect::<Vec<_>>() })
.collect::<Vec<_>>(),
) )
.block(block) .block(block)
.style(self.palette.create_basic_style(false)) .style(self.palette.create_basic_style(false))

View File

@ -4,22 +4,22 @@ use ratatui::{
layout::{Rect, Size}, layout::{Rect, Size},
Frame, Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use crate::{palette, action::Action, config::Config, tui::Event}; use crate::{action::Action, config::Config, palette, tui::Event};
pub mod layouts;
pub mod fps;
pub mod health;
pub mod menu;
pub mod version;
pub mod explorer;
pub mod wallet;
pub mod validator;
pub mod empty; pub mod empty;
pub mod help; pub mod explorer;
pub mod fps;
pub mod generic; pub mod generic;
pub mod health;
pub mod help;
pub mod layouts;
pub mod menu;
pub mod validator;
pub mod version;
pub mod wallet;
pub trait Component { pub trait Component {
fn register_network_handler(&mut self, tx: Sender<Action>) -> Result<()> { fn register_network_handler(&mut self, tx: Sender<Action>) -> Result<()> {

View File

@ -1,14 +1,14 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -27,7 +27,7 @@ pub struct BondPopup {
minimal_bond: u128, minimal_bond: u128,
is_bonded: bool, is_bonded: bool,
amount: Input, amount: Input,
palette: StylePalette palette: StylePalette,
} }
impl Default for BondPopup { impl Default for BondPopup {
@ -60,8 +60,7 @@ impl BondPopup {
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::ValidatorLog));
Action::EventLog(message, level, ActionTarget::ValidatorLog));
} }
} }
@ -78,16 +77,25 @@ impl BondPopup {
let amount = (value * 1_000_000_000_000_000_000.0) as u128; let amount = (value * 1_000_000_000_000_000_000.0) as u128;
let log_target = ActionTarget::ValidatorLog; let log_target = ActionTarget::ValidatorLog;
let _ = if self.is_bonded { let _ = if self.is_bonded {
network_tx.send(Action::BondValidatorExtraFrom(self.stash_secret_seed, amount, log_target)) network_tx.send(Action::BondValidatorExtraFrom(
self.stash_secret_seed,
amount,
log_target,
))
} else { } else {
network_tx.send(Action::BondValidatorFrom(self.stash_secret_seed, amount, log_target)) network_tx.send(Action::BondValidatorFrom(
self.stash_secret_seed,
amount,
log_target,
))
}; };
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
} }
}, }
Err(err) => self.log_event( Err(err) => {
format!("invalid amount, error: {err}"), ActionLevel::Error), self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error)
}
} }
} }
} }
@ -137,11 +145,16 @@ impl Component for BondPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -154,7 +167,7 @@ impl Component for BondPopup {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -162,8 +175,9 @@ impl Component for BondPopup {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetIsBonded(is_bonded, account_id) if self.stash_account_id == account_id => Action::SetIsBonded(is_bonded, account_id) if self.stash_account_id == account_id => {
self.is_bonded = is_bonded, self.is_bonded = is_bonded
}
Action::SetMinValidatorBond(minimal_bond) => self.minimal_bond = minimal_bond, Action::SetMinValidatorBond(minimal_bond) => self.minimal_bond = minimal_bond,
Action::SetStashSecret(secret_seed) => self.stash_secret_seed = secret_seed, Action::SetStashSecret(secret_seed) => self.stash_secret_seed = secret_seed,
Action::SetStashAccount(account_id) => self.stash_account_id = account_id, Action::SetStashAccount(account_id) => self.stash_account_id = account_id,
@ -175,13 +189,14 @@ impl Component for BondPopup {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.amount.value()) let input = Paragraph::new(self.amount.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title(format!("Staking bond amount"))); .title(format!("Staking bond amount")),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
@ -190,8 +205,8 @@ impl Component for BondPopup {
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(input, area); frame.render_widget(input, area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
area.x + self.amount.cursor() as u16 + 1, area.x + self.amount.cursor() as u16 + 1,
area.y + 1 area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -1,19 +1,19 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
palette::StylePalette, palette::StylePalette,
types::{ActionTarget, ActionLevel}, types::{ActionLevel, ActionTarget},
widgets::{Input, InputRequest}, widgets::{Input, InputRequest},
}; };
@ -24,7 +24,7 @@ pub struct ChangeBlocksPopup {
network_tx: Option<Sender<Action>>, network_tx: Option<Sender<Action>>,
chain_id: u64, chain_id: u64,
block_number: Input, block_number: Input,
palette: StylePalette palette: StylePalette,
} }
impl ChangeBlocksPopup { impl ChangeBlocksPopup {
@ -44,9 +44,9 @@ impl ChangeBlocksPopup {
action_tx.send(Action::ClosePopup) action_tx.send(Action::ClosePopup)
} }
Err(_) => action_tx.send(Action::EventLog( Err(_) => action_tx.send(Action::EventLog(
"Incorrect block number format".to_string(), "Incorrect block number format".to_string(),
ActionLevel::Error, ActionLevel::Error,
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
)), )),
}; };
} }
@ -98,11 +98,16 @@ impl Component for ChangeBlocksPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -116,7 +121,7 @@ impl Component for ChangeBlocksPopup {
KeyCode::Backspace => self.delete_char(), KeyCode::Backspace => self.delete_char(),
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -133,13 +138,14 @@ impl Component for ChangeBlocksPopup {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.block_number.value()) let input = Paragraph::new(self.block_number.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title(format!("Bump blocks for #{:?}", self.chain_id))); .title(format!("Bump blocks for #{:?}", self.chain_id)),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(57)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(57)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
@ -148,8 +154,8 @@ impl Component for ChangeBlocksPopup {
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(input, area); frame.render_widget(input, area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
area.x + self.block_number.cursor() as u16 + 1, area.x + self.block_number.cursor() as u16 + 1,
area.y + 1 area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -1,19 +1,15 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)] #[derive(Debug)]
pub struct ChillPopup { pub struct ChillPopup {
@ -21,7 +17,7 @@ pub struct ChillPopup {
action_tx: Option<UnboundedSender<Action>>, action_tx: Option<UnboundedSender<Action>>,
network_tx: Option<Sender<Action>>, network_tx: Option<Sender<Action>>,
secret_seed: [u8; 32], secret_seed: [u8; 32],
palette: StylePalette palette: StylePalette,
} }
impl Default for ChillPopup { impl Default for ChillPopup {
@ -80,11 +76,16 @@ impl Component for ChillPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -93,7 +94,7 @@ impl Component for ChillPopup {
match key.code { match key.code {
KeyCode::Enter => self.submit_message(), KeyCode::Enter => self.submit_message(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -111,12 +112,14 @@ impl Component for ChillPopup {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(" Do you want to chill stash account and stop validation?") let input = Paragraph::new(" Do you want to chill stash account and stop validation?")
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_style(self.palette.create_popup_title_style()) .border_type(border_type)
.title_alignment(Alignment::Right) .title_style(self.palette.create_popup_title_style())
.title(format!("Chill stash"))); .title_alignment(Alignment::Right)
.title(format!("Chill stash")),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(57)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(57)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);

View File

@ -5,15 +5,18 @@ use ratatui::{
style::{Color, Style}, style::{Color, Style},
text::Text, text::Text,
widgets::{ widgets::{
Block, Padding, Cell, Row, Scrollbar, ScrollbarOrientation, Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table,
ScrollbarState, Table, TableState, TableState,
}, },
Frame Frame,
}; };
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, config::Config, palette::StylePalette, types::{ActionLevel, ActionTarget} action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -29,7 +32,7 @@ pub struct EventLogs {
scroll_state: ScrollbarState, scroll_state: ScrollbarState,
table_state: TableState, table_state: TableState,
logs: std::collections::VecDeque<LogDetails>, logs: std::collections::VecDeque<LogDetails>,
palette: StylePalette palette: StylePalette,
} }
impl EventLogs { impl EventLogs {
@ -62,7 +65,7 @@ impl EventLogs {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
@ -85,8 +88,8 @@ impl EventLogs {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i); self.scroll_state = self.scroll_state.position(i);
@ -109,33 +112,42 @@ impl PartialComponent for EventLogs {
impl Component for EventLogs { impl Component for EventLogs {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) { if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> { fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
match key.code { match key.code {
KeyCode::Up | KeyCode::Char('k') if self.is_active => self.previous_row(), KeyCode::Up | KeyCode::Char('k') if self.is_active => self.previous_row(),
KeyCode::Down | KeyCode::Char('j') if self.is_active => self.next_row(), KeyCode::Down | KeyCode::Char('j') 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.first_row(),
KeyCode::Char('G') if self.is_active => self.last_row(), KeyCode::Char('G') if self.is_active => self.last_row(),
_ => {}, _ => {}
}; };
Ok(None) Ok(None)
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::EventLog(message, level, target) if target == ActionTarget::ValidatorLog => Action::EventLog(message, level, target) if target == ActionTarget::ValidatorLog => {
self.add_new_log(message, level), self.add_new_log(message, level)
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -150,33 +162,38 @@ impl Component for EventLogs {
let info_style = Style::new().fg(Color::Green); let info_style = Style::new().fg(Color::Green);
let table = Table::new( let table = Table::new(
self.logs self.logs.iter().map(|log| {
.iter() let style = match log.level {
.map(|log| { ActionLevel::Info => info_style,
let style = match log.level { ActionLevel::Warn => warn_style,
ActionLevel::Info => info_style, ActionLevel::Error => error_style,
ActionLevel::Warn => warn_style, };
ActionLevel::Error => error_style, Row::new(vec![
}; Cell::from(
Row::new(vec![ Text::from(log.time.format("%H:%M:%S").to_string())
Cell::from(Text::from(log.time.format("%H:%M:%S").to_string()).style(style).alignment(Alignment::Left)), .style(style)
Cell::from(Text::from(log.message.clone()).style(style).alignment(Alignment::Left)), .alignment(Alignment::Left),
]) ),
}), Cell::from(
[ Text::from(log.message.clone())
Constraint::Max(8), .style(style)
Constraint::Min(0), .alignment(Alignment::Left),
], ),
])
}),
[Constraint::Max(8), Constraint::Min(0)],
) )
.column_spacing(1) .column_spacing(1)
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.padding(Padding::right(2)) .border_type(border_type)
.title_alignment(Alignment::Right) .padding(Padding::right(2))
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Action Logs")); .title_style(self.palette.create_title_style(false))
.title("Action Logs"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -187,7 +204,10 @@ impl Component for EventLogs {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );
Ok(()) Ok(())

View File

@ -1,20 +1,16 @@
use std::collections::HashMap;
use color_eyre::Result; use color_eyre::Result;
use ratatui::layout::Constraint; use ratatui::layout::Constraint;
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Text,
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame Frame,
}; };
use std::collections::HashMap;
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::BlockRange; use crate::types::BlockRange;
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
struct Details { struct Details {
@ -31,20 +27,28 @@ pub struct GatekeeperDetails {
} }
impl PartialComponent for GatekeeperDetails { impl PartialComponent for GatekeeperDetails {
fn set_active(&mut self, _current_tab: CurrentTab) { } fn set_active(&mut self, _current_tab: CurrentTab) {}
} }
impl Component for GatekeeperDetails { impl Component for GatekeeperDetails {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) { if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
@ -54,12 +58,15 @@ impl Component for GatekeeperDetails {
Action::SetChoosenGatekeeper(chain_id) => self.selected_chain_id = chain_id, Action::SetChoosenGatekeeper(chain_id) => self.selected_chain_id = chain_id,
Action::SetBlockRange(chain_id, block_range) => { Action::SetBlockRange(chain_id, block_range) => {
let _ = self.block_ranges.insert(chain_id, block_range); let _ = self.block_ranges.insert(chain_id, block_range);
}, }
Action::SetGatekeepedNetwork(network) => { Action::SetGatekeepedNetwork(network) => {
self.gatekeeper_details.insert(network.chain_id, Details { self.gatekeeper_details.insert(
incoming_fee: format!("{:.5}%", network.incoming_fee as f64 / 10_000_000.0), network.chain_id,
outgoing_fee: format!("{:.5}%", network.outgoing_fee as f64 / 10_000_000.0), Details {
}); incoming_fee: format!("{:.5}%", network.incoming_fee as f64 / 10_000_000.0),
outgoing_fee: format!("{:.5}%", network.outgoing_fee as f64 / 10_000_000.0),
},
);
} }
_ => {} _ => {}
}; };
@ -70,12 +77,14 @@ impl Component for GatekeeperDetails {
let [_, place] = super::validator_gatekeeped_networks_layout(area); let [_, place] = super::validator_gatekeeped_networks_layout(area);
let (border_style, border_type) = self.palette.create_border_style(false); let (border_style, border_type) = self.palette.create_border_style(false);
let current_gatekeeper_details = self.gatekeeper_details let current_gatekeeper_details = self
.gatekeeper_details
.get(&self.selected_chain_id) .get(&self.selected_chain_id)
.cloned() .cloned()
.unwrap_or_default(); .unwrap_or_default();
let current_block_range = self.block_ranges let current_block_range = self
.block_ranges
.get(&self.selected_chain_id) .get(&self.selected_chain_id)
.copied() .copied()
.unwrap_or_default(); .unwrap_or_default();
@ -84,34 +93,45 @@ impl Component for GatekeeperDetails {
vec![ vec![
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("From block".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("From block".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(current_block_range.from_block.to_string()).alignment(Alignment::Right)), Cell::from(
Text::from(current_block_range.from_block.to_string())
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("To block".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("To block".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(current_block_range.to_block.to_string()).alignment(Alignment::Right)), Cell::from(
Text::from(current_block_range.to_block.to_string())
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Incoming fee".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Incoming fee".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(current_gatekeeper_details.incoming_fee).alignment(Alignment::Right)), Cell::from(
Text::from(current_gatekeeper_details.incoming_fee)
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Outgoing fee".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Outgoing fee".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(current_gatekeeper_details.outgoing_fee).alignment(Alignment::Right)), Cell::from(
Text::from(current_gatekeeper_details.outgoing_fee)
.alignment(Alignment::Right),
),
]), ]),
], ],
[ [Constraint::Length(12), Constraint::Fill(1)],
Constraint::Length(12),
Constraint::Fill(1),
],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title(format!("Chain ID: {}", self.selected_chain_id))); .title_style(self.palette.create_title_style(false))
.title(format!("Chain ID: {}", self.selected_chain_id)),
);
frame.render_widget(table, place); frame.render_widget(table, place);

View File

@ -1,14 +1,24 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, prelude::Margin, style::{Color, Style}, widgets::{Block, BorderType, Cell, Clear, Paragraph, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, TableState}, Frame layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
prelude::Margin,
style::{Color, Style},
widgets::{
Block, BorderType, Cell, Clear, Paragraph, Row, Scrollbar, ScrollbarOrientation,
ScrollbarState, Table, TableState,
},
Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, config::Config, palette::StylePalette, widgets::{Input, InputRequest} action::Action,
config::Config,
palette::StylePalette,
widgets::{Input, InputRequest},
}; };
#[derive(Debug, Default, Eq, PartialEq)] #[derive(Debug, Default, Eq, PartialEq)]
@ -57,8 +67,9 @@ impl GatekeeperEndpoints {
let mut stored_endpoints = self.stored_endpoints.clone(); let mut stored_endpoints = self.stored_endpoints.clone();
stored_endpoints.push(self.rpc_input.value().to_string()); stored_endpoints.push(self.rpc_input.value().to_string());
let _ = network_tx.send(Action::UpdateStoredRpcEndpoints( let _ = network_tx.send(Action::UpdateStoredRpcEndpoints(
self.chain_id, self.chain_id,
stored_endpoints)); stored_endpoints,
));
self.rpc_input = Input::new(String::new()); self.rpc_input = Input::new(String::new());
} }
} }
@ -68,27 +79,26 @@ impl GatekeeperEndpoints {
match self.selected { match self.selected {
Selected::Input => { Selected::Input => {
let _ = self.rpc_input.handle(InputRequest::InsertChar(new_char)); let _ = self.rpc_input.handle(InputRequest::InsertChar(new_char));
}, }
Selected::StoredRpcs if (new_char == 'd' || new_char == 'D') => { Selected::StoredRpcs if (new_char == 'd' || new_char == 'D') => {
if let Some(index) = self.stored_table_state.selected() { if let Some(index) = self.stored_table_state.selected() {
let mut stored_endpoints = self.stored_endpoints.clone(); let mut stored_endpoints = self.stored_endpoints.clone();
if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
stored_endpoints.remove(index); stored_endpoints.remove(index);
let _ = network_tx.send(Action::UpdateStoredRpcEndpoints( let _ = network_tx.send(Action::UpdateStoredRpcEndpoints(
self.chain_id, self.chain_id,
stored_endpoints)); stored_endpoints,
));
} }
} }
},
_ => {
match new_char {
'j' => self.move_cursor_down(),
'k' => self.move_cursor_up(),
'l' => self.move_cursor_right(),
'h' => self.move_cursor_left(),
_ => {}
}
} }
_ => match new_char {
'j' => self.move_cursor_down(),
'k' => self.move_cursor_up(),
'l' => self.move_cursor_right(),
'h' => self.move_cursor_left(),
_ => {}
},
} }
} }
@ -100,7 +110,7 @@ impl GatekeeperEndpoints {
fn move_cursor_up(&mut self) { fn move_cursor_up(&mut self) {
match self.selected { match self.selected {
Selected::Input => {}, Selected::Input => {}
Selected::DefaultRpcs => { Selected::DefaultRpcs => {
let i = match self.default_table_state.selected() { let i = match self.default_table_state.selected() {
Some(i) => { Some(i) => {
@ -109,12 +119,12 @@ impl GatekeeperEndpoints {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.default_table_state.select(Some(i)); self.default_table_state.select(Some(i));
self.default_scroll_state = self.default_scroll_state.position(i); self.default_scroll_state = self.default_scroll_state.position(i);
}, }
Selected::StoredRpcs => { Selected::StoredRpcs => {
let i = match self.stored_table_state.selected() { let i = match self.stored_table_state.selected() {
Some(i) => { Some(i) => {
@ -123,18 +133,18 @@ impl GatekeeperEndpoints {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.stored_table_state.select(Some(i)); self.stored_table_state.select(Some(i));
self.stored_scroll_state = self.stored_scroll_state.position(i); self.stored_scroll_state = self.stored_scroll_state.position(i);
}, }
}; };
} }
fn move_cursor_down(&mut self) { fn move_cursor_down(&mut self) {
match self.selected { match self.selected {
Selected::Input => {}, Selected::Input => {}
Selected::DefaultRpcs => { Selected::DefaultRpcs => {
let i = match self.default_table_state.selected() { let i = match self.default_table_state.selected() {
Some(i) => { Some(i) => {
@ -143,12 +153,12 @@ impl GatekeeperEndpoints {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.default_table_state.select(Some(i)); self.default_table_state.select(Some(i));
self.default_scroll_state = self.default_scroll_state.position(i); self.default_scroll_state = self.default_scroll_state.position(i);
}, }
Selected::StoredRpcs => { Selected::StoredRpcs => {
let i = match self.stored_table_state.selected() { let i = match self.stored_table_state.selected() {
Some(i) => { Some(i) => {
@ -157,12 +167,12 @@ impl GatekeeperEndpoints {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.stored_table_state.select(Some(i)); self.stored_table_state.select(Some(i));
self.stored_scroll_state = self.stored_scroll_state.position(i); self.stored_scroll_state = self.stored_scroll_state.position(i);
}, }
}; };
} }
@ -200,18 +210,20 @@ impl GatekeeperEndpoints {
scrollbar_style: Style, scrollbar_style: Style,
) -> (Table<'a>, Scrollbar<'a>) { ) -> (Table<'a>, Scrollbar<'a>) {
let table = Table::new( let table = Table::new(
rpcs.iter().map(|endpoint| Row::new(vec![ rpcs.iter()
Cell::from(endpoint.as_str()) .map(|endpoint| Row::new(vec![Cell::from(endpoint.as_str())]))
])).collect::<Vec<Row>>(), .collect::<Vec<Row>>(),
[Constraint::Min(1)], [Constraint::Min(1)],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.block(Block::bordered() .block(
Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title(title_name)); .title(title_name),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -230,7 +242,7 @@ impl PartialComponent for GatekeeperEndpoints {
_ => { _ => {
self.is_active = false; self.is_active = false;
self.rpc_input = Input::new(String::new()); self.rpc_input = Input::new(String::new());
}, }
}; };
} }
} }
@ -248,16 +260,26 @@ impl Component for GatekeeperEndpoints {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_title_style(style.get("normal_title_style").copied());
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_popup_style(style.get("popup_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_popup_title_style(style.get("popup_title_style").copied());
self.palette
.with_hover_style(style.get("hover_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
@ -273,7 +295,7 @@ impl Component for GatekeeperEndpoints {
KeyCode::Up => self.move_cursor_up(), KeyCode::Up => self.move_cursor_up(),
KeyCode::Down => self.move_cursor_down(), KeyCode::Down => self.move_cursor_down(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -282,10 +304,12 @@ impl Component for GatekeeperEndpoints {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetChoosenGatekeeper(chain_id) => self.set_chain_id(chain_id), Action::SetChoosenGatekeeper(chain_id) => self.set_chain_id(chain_id),
Action::SetGatekeepedNetwork(network) => Action::SetGatekeepedNetwork(network) => {
self.default_endpoints = network.default_endpoints, self.default_endpoints = network.default_endpoints
Action::SetStoredRpcEndpoints(stored_endpoints) => }
self.stored_endpoints = stored_endpoints, Action::SetStoredRpcEndpoints(stored_endpoints) => {
self.stored_endpoints = stored_endpoints
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -294,7 +318,8 @@ impl Component for GatekeeperEndpoints {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let (selected_border_style, selected_border_type) = self.palette.create_border_style(true); let (selected_border_style, selected_border_type) =
self.palette.create_border_style(true);
let scrollbar_style = self.palette.create_scrollbar_style(); let scrollbar_style = self.palette.create_scrollbar_style();
let (default_border_style, default_border_type) = match self.selected { let (default_border_style, default_border_type) = match self.selected {
@ -327,13 +352,14 @@ impl Component for GatekeeperEndpoints {
scrollbar_style, scrollbar_style,
); );
let input = Paragraph::new(self.rpc_input.value()) let input = Paragraph::new(self.rpc_input.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(input_border_style) .border_style(input_border_style)
.border_type(input_border_type) .border_type(input_border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Input new RPC")); .title("Input new RPC"),
);
let v = Layout::vertical([Constraint::Max(14)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(14)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(80)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(80)]).flex(Flex::Center);
@ -341,34 +367,44 @@ impl Component for GatekeeperEndpoints {
let [area] = h.areas(area); let [area] = h.areas(area);
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
let [tables_area, input_area] = Layout::vertical([ let [tables_area, input_area] =
Constraint::Length(11), Layout::vertical([Constraint::Length(11), Constraint::Length(3)]).areas(area);
Constraint::Length(3),
]).areas(area);
let [default_table_area, stored_table_area] = Layout::horizontal([ let [default_table_area, stored_table_area] =
Constraint::Max(40), Layout::horizontal([Constraint::Max(40), Constraint::Max(40)]).areas(tables_area);
Constraint::Max(40),
]).areas(tables_area);
frame.render_stateful_widget(default_rpcs, default_table_area, &mut self.default_table_state); frame.render_stateful_widget(
default_rpcs,
default_table_area,
&mut self.default_table_state,
);
frame.render_stateful_widget( frame.render_stateful_widget(
default_scrollbar, default_scrollbar,
default_table_area.inner(Margin { vertical: 1, horizontal: 1 }), default_table_area.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.default_scroll_state, &mut self.default_scroll_state,
); );
frame.render_stateful_widget(stored_rpcs, stored_table_area, &mut self.stored_table_state); frame.render_stateful_widget(
stored_rpcs,
stored_table_area,
&mut self.stored_table_state,
);
frame.render_stateful_widget( frame.render_stateful_widget(
stored_scrollbar, stored_scrollbar,
stored_table_area.inner(Margin { vertical: 1, horizontal: 1 }), stored_table_area.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.stored_scroll_state, &mut self.stored_scroll_state,
); );
frame.render_widget(input, input_area); frame.render_widget(input, input_area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
input_area.x + self.rpc_input.cursor() as u16 + 1, input_area.x + self.rpc_input.cursor() as u16 + 1,
input_area.y + 1 input_area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -1,24 +1,17 @@
use std::collections::HashMap;
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent}; use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::Margin; use ratatui::layout::Margin;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
widgets::{ widgets::{Block, List, ListItem, ListState, Scrollbar, ScrollbarOrientation, ScrollbarState},
Block, List, ListState, ListItem, Scrollbar, Frame,
ScrollbarOrientation, ScrollbarState,
},
Frame
}; };
use std::collections::HashMap;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::Gatekeeper; use crate::types::Gatekeeper;
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
struct NetworkDetails { struct NetworkDetails {
chain_id: u64, chain_id: u64,
@ -69,12 +62,10 @@ impl Gatekeepers {
fn change_choosen_gatekeeper(&mut self) { fn change_choosen_gatekeeper(&mut self) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
if let Some(chain_id) = self.list_state if let Some(chain_id) = self
.list_state
.selected() .selected()
.map(|index| self.gatekeepers .map(|index| self.gatekeepers.get(index).map(|data| data.chain_id))
.get(index)
.map(|data| data.chain_id)
)
.flatten() .flatten()
{ {
let _ = action_tx.send(Action::SetChoosenGatekeeper(chain_id)); let _ = action_tx.send(Action::SetChoosenGatekeeper(chain_id));
@ -120,7 +111,7 @@ impl Gatekeepers {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.list_state.select(Some(i)); self.list_state.select(Some(i));
@ -145,8 +136,8 @@ impl Gatekeepers {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.list_state.select(Some(i)); self.list_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i); self.scroll_state = self.scroll_state.position(i);
@ -175,22 +166,29 @@ impl Component for Gatekeepers {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) { if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetGatekeepedNetwork(network) => Action::SetGatekeepedNetwork(network) => self.update_gatekeeped_network(network),
self.update_gatekeeped_network(network),
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -205,7 +203,7 @@ impl Component for Gatekeepers {
KeyCode::Char('G') => self.last_row(), KeyCode::Char('G') => self.last_row(),
KeyCode::Char('O') => self.nullify_evm_blocks(), KeyCode::Char('O') => self.nullify_evm_blocks(),
KeyCode::Enter => self.gatekeeper_endpoints(), KeyCode::Enter => self.gatekeeper_endpoints(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -214,21 +212,21 @@ impl Component for Gatekeepers {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [place, _] = super::validator_gatekeeped_networks_layout(area); let [place, _] = super::validator_gatekeeped_networks_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 list = List::new( let list = List::new(self.gatekeepers.iter().map(|network| {
self.gatekeepers ListItem::new(format!(
.iter() "{} (type {})",
.map(|network| ListItem::new(format!("{} (type {})", network.chain_name, network.chain_type
network.chain_name, ))
network.chain_type)) }))
) .highlight_style(self.palette.create_highlight_style())
) .block(
.highlight_style(self.palette.create_highlight_style()) Block::bordered()
.block(Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title("Gatekeeped Networks")); .title("Gatekeeped Networks"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -239,7 +237,10 @@ impl Component for Gatekeepers {
frame.render_stateful_widget(list, place, &mut self.list_state); frame.render_stateful_widget(list, place, &mut self.list_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,4 +1,4 @@
use std::collections::{HashSet, BTreeMap}; use std::collections::{BTreeMap, HashSet};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use color_eyre::Result; use color_eyre::Result;
@ -6,24 +6,20 @@ use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::{Constraint, Margin}; use ratatui::layout::{Constraint, Margin};
use ratatui::style::Modifier; use ratatui::style::Modifier;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect},
prelude::Stylize, prelude::Stylize,
text::Text, text::Text,
layout::{Alignment, Rect},
widgets::{ widgets::{
Block, Cell, Row, Table, TableState, Scrollbar, Padding, Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table,
ScrollbarOrientation, ScrollbarState, TableState,
}, },
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::{ActionLevel, ActionTarget}; use crate::types::{ActionLevel, ActionTarget};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
struct EraStakingInfo { struct EraStakingInfo {
reward: u128, reward: u128,
@ -66,7 +62,8 @@ impl History {
} }
fn payout_all_available(&mut self) { fn payout_all_available(&mut self) {
let unclaimed_keys = self.rewards let unclaimed_keys = self
.rewards
.iter() .iter()
.filter_map(|(k, v)| (!v.is_claimed && v.reward > 0).then(|| *k)) .filter_map(|(k, v)| (!v.is_claimed && v.reward > 0).then(|| *k))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -74,9 +71,10 @@ impl History {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = if unclaimed_keys.len() == 0 { let _ = if unclaimed_keys.len() == 0 {
action_tx.send(Action::EventLog( action_tx.send(Action::EventLog(
String::from("no available payouts found for current validator"), String::from("no available payouts found for current validator"),
ActionLevel::Warn, ActionLevel::Warn,
ActionTarget::ValidatorLog)) ActionTarget::ValidatorLog,
))
} else { } else {
self.pending_payout.extend(&unclaimed_keys); self.pending_payout.extend(&unclaimed_keys);
action_tx.send(Action::PayoutAllValidatorPopup(unclaimed_keys)) action_tx.send(Action::PayoutAllValidatorPopup(unclaimed_keys))
@ -86,12 +84,11 @@ impl History {
fn payout_by_era_index(&mut self) { fn payout_by_era_index(&mut self) {
if let Some(index) = self.table_state.selected() { if let Some(index) = self.table_state.selected() {
let rev_index = self.rewards.len() let rev_index = self.rewards.len().saturating_sub(index).saturating_sub(1);
.saturating_sub(index)
.saturating_sub(1);
if let Some(era_index) = self.rewards.keys().nth(rev_index) { if let Some(era_index) = self.rewards.keys().nth(rev_index) {
let is_claimed = self.rewards let is_claimed = self
.rewards
.get(&era_index) .get(&era_index)
.map(|x| x.is_claimed) .map(|x| x.is_claimed)
.expect("BTreeMap of rewards is indexed; qed"); .expect("BTreeMap of rewards is indexed; qed");
@ -103,13 +100,18 @@ impl History {
action_tx.send(Action::PayoutValidatorPopup(*era_index)) action_tx.send(Action::PayoutValidatorPopup(*era_index))
} }
(false, true) => action_tx.send(Action::EventLog( (false, true) => action_tx.send(Action::EventLog(
format!("payout for era #{} is in-flight already", era_index), format!("payout for era #{} is in-flight already", era_index),
ActionLevel::Warn, ActionLevel::Warn,
ActionTarget::ValidatorLog)), ActionTarget::ValidatorLog,
)),
(true, _) => action_tx.send(Action::EventLog( (true, _) => action_tx.send(Action::EventLog(
format!("staking rewards for era index #{} already claimed", era_index), format!(
ActionLevel::Warn, "staking rewards for era index #{} already claimed",
ActionTarget::ValidatorLog)), era_index
),
ActionLevel::Warn,
ActionTarget::ValidatorLog,
)),
}; };
} }
} }
@ -131,7 +133,7 @@ impl History {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
@ -154,8 +156,8 @@ impl History {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i); self.scroll_state = self.scroll_state.position(i);
@ -165,11 +167,14 @@ impl History {
match self.rewards.get_mut(&era_index) { match self.rewards.get_mut(&era_index) {
Some(reward_item) => reward_item.reward = reward, Some(reward_item) => reward_item.reward = reward,
None => { None => {
let _ = self.rewards.insert(era_index, EraStakingInfo { let _ = self.rewards.insert(
reward, era_index,
slash: 0u128, EraStakingInfo {
is_claimed: false, reward,
}); slash: 0u128,
is_claimed: false,
},
);
} }
} }
self.scroll_state = self.scroll_state.content_length(self.rewards.len()); self.scroll_state = self.scroll_state.content_length(self.rewards.len());
@ -187,11 +192,14 @@ impl History {
reward_item.is_claimed = is_claimed; reward_item.is_claimed = is_claimed;
} }
None => { None => {
let _ = self.rewards.insert(era_index, EraStakingInfo { let _ = self.rewards.insert(
reward: 0u128, era_index,
slash: 0u128, EraStakingInfo {
is_claimed, reward: 0u128,
}); slash: 0u128,
is_claimed,
},
);
} }
} }
} }
@ -200,11 +208,14 @@ impl History {
match self.rewards.get_mut(&era_index) { match self.rewards.get_mut(&era_index) {
Some(reward_item) => reward_item.slash = slash, Some(reward_item) => reward_item.slash = slash,
None => { None => {
let _ = self.rewards.insert(era_index, EraStakingInfo { let _ = self.rewards.insert(
reward: 0u128, era_index,
slash, EraStakingInfo {
is_claimed: false, reward: 0u128,
}); slash,
is_claimed: false,
},
);
} }
} }
} }
@ -222,7 +233,7 @@ impl PartialComponent for History {
CurrentTab::History => { CurrentTab::History => {
self.is_active = true; self.is_active = true;
self.pending_payout = Default::default(); self.pending_payout = Default::default();
}, }
_ => self.is_active = false, _ => self.is_active = false,
} }
} }
@ -241,22 +252,34 @@ impl Component for History {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) { if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetValidatorEraReward(era_index, reward) => self.update_rewards(era_index, reward), Action::SetValidatorEraReward(era_index, reward) => {
Action::SetValidatorEraClaimed(era_index, is_claimed) => self.update_claims(era_index, is_claimed), self.update_rewards(era_index, reward)
}
Action::SetValidatorEraClaimed(era_index, is_claimed) => {
self.update_claims(era_index, is_claimed)
}
Action::SetValidatorEraSlash(era_index, slash) => self.update_slashes(era_index, slash), Action::SetValidatorEraSlash(era_index, slash) => self.update_slashes(era_index, slash),
_ => {} _ => {}
}; };
@ -272,7 +295,7 @@ impl Component for History {
KeyCode::Char('G') => self.last_row(), KeyCode::Char('G') => self.last_row(),
KeyCode::Char('H') => self.payout_all_available(), KeyCode::Char('H') => self.payout_all_available(),
KeyCode::Enter => self.payout_by_era_index(), KeyCode::Enter => self.payout_by_era_index(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -282,38 +305,38 @@ impl Component for History {
let [_, place, _] = super::validator_statistics_layout(area); let [_, place, _] = super::validator_statistics_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 table = Table::new( let table = Table::new(
self.rewards self.rewards.iter().rev().map(|(key, value)| {
.iter() let mut era_index_text = Text::from(key.to_string()).alignment(Alignment::Left);
.rev() let mut slash_text =
.map(|(key, value)| { Text::from(self.prepare_u128(value.slash)).alignment(Alignment::Center);
let mut era_index_text = Text::from(key.to_string()).alignment(Alignment::Left); let mut reward_text =
let mut slash_text = Text::from(self.prepare_u128(value.slash)).alignment(Alignment::Center); Text::from(self.prepare_u128(value.reward)).alignment(Alignment::Right);
let mut reward_text = Text::from(self.prepare_u128(value.reward)).alignment(Alignment::Right);
if value.is_claimed { if value.is_claimed {
era_index_text = era_index_text.add_modifier(Modifier::CROSSED_OUT); era_index_text = era_index_text.add_modifier(Modifier::CROSSED_OUT);
slash_text = slash_text.add_modifier(Modifier::CROSSED_OUT); slash_text = slash_text.add_modifier(Modifier::CROSSED_OUT);
reward_text = reward_text.add_modifier(Modifier::CROSSED_OUT); reward_text = reward_text.add_modifier(Modifier::CROSSED_OUT);
Row::new(vec![ Row::new(vec![
Cell::from(era_index_text), Cell::from(era_index_text),
Cell::from(slash_text), Cell::from(slash_text),
Cell::from(reward_text), Cell::from(reward_text),
]).style(self.palette.create_highlight_style()) ])
} else { .style(self.palette.create_highlight_style())
if self.pending_payout.contains(key) { } else {
era_index_text = era_index_text.add_modifier(Modifier::SLOW_BLINK); if self.pending_payout.contains(key) {
slash_text = slash_text.add_modifier(Modifier::SLOW_BLINK); era_index_text = era_index_text.add_modifier(Modifier::SLOW_BLINK);
reward_text = reward_text.add_modifier(Modifier::SLOW_BLINK); slash_text = slash_text.add_modifier(Modifier::SLOW_BLINK);
} reward_text = reward_text.add_modifier(Modifier::SLOW_BLINK);
Row::new(vec![
Cell::from(era_index_text),
Cell::from(slash_text),
Cell::from(reward_text),
])
} }
}),
Row::new(vec![
Cell::from(era_index_text),
Cell::from(slash_text),
Cell::from(reward_text),
])
}
}),
[ [
Constraint::Length(4), Constraint::Length(4),
Constraint::Fill(1), Constraint::Fill(1),
@ -322,13 +345,15 @@ impl Component for History {
) )
.highlight_style(self.palette.create_basic_style(true)) .highlight_style(self.palette.create_basic_style(true))
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.padding(Padding::right(2)) .border_type(border_type)
.title_alignment(Alignment::Right) .padding(Padding::right(2))
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Staking history")); .title_style(self.palette.create_title_style(false))
.title("Staking history"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -339,7 +364,10 @@ impl Component for History {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -4,20 +4,13 @@ use ratatui::layout::Margin;
use ratatui::widgets::ListItem; use ratatui::widgets::ListItem;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
widgets::{ widgets::{Block, List, ListState, Scrollbar, ScrollbarOrientation, ScrollbarState},
Block, List, ListState, Scrollbar, Frame,
ScrollbarOrientation, ScrollbarState,
},
Frame
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
pub struct ListenAddresses { pub struct ListenAddresses {
is_active: bool, is_active: bool,
@ -63,7 +56,7 @@ impl ListenAddresses {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.list_state.select(Some(i)); self.list_state.select(Some(i));
@ -86,8 +79,8 @@ impl ListenAddresses {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.list_state.select(Some(i)); self.list_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i); self.scroll_state = self.scroll_state.position(i);
@ -115,14 +108,22 @@ impl Component for ListenAddresses {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
@ -143,7 +144,7 @@ impl Component for ListenAddresses {
KeyCode::Down | KeyCode::Char('j') => self.next_row(), KeyCode::Down | KeyCode::Char('j') => self.next_row(),
KeyCode::Char('g') => self.first_row(), KeyCode::Char('g') => self.first_row(),
KeyCode::Char('G') => self.last_row(), KeyCode::Char('G') => self.last_row(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -155,15 +156,17 @@ impl Component for ListenAddresses {
let list = List::new( let list = List::new(
self.listen_addresses self.listen_addresses
.iter() .iter()
.map(|addr| ListItem::new(addr.clone())) .map(|addr| ListItem::new(addr.clone())),
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.block(Block::bordered() .block(
Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title(self.local_identity.clone())); .title(self.local_identity.clone()),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -174,7 +177,10 @@ impl Component for ListenAddresses {
frame.render_stateful_widget(list, place, &mut self.list_state); frame.render_stateful_widget(list, place, &mut self.list_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -11,55 +11,55 @@ use tokio::sync::mpsc::UnboundedSender;
use super::Component; use super::Component;
use crate::{action::Action, app::Mode, config::Config}; use crate::{action::Action, app::Mode, config::Config};
mod event_log;
mod peers;
mod stash_info;
mod nominators;
mod listen_addresses;
mod gatekeepers;
mod gatekeeper_details;
mod history;
mod withdrawals;
mod stash_details;
mod staking_details;
mod reward_details;
mod bond_popup; mod bond_popup;
mod payout_popup;
mod payout_all_popup;
mod rotate_popup;
mod validate_popup;
mod chill_popup;
mod unbond_popup;
mod rebond_popup;
mod withdraw_popup;
mod payee_popup;
mod change_blocks_popup; mod change_blocks_popup;
mod chill_popup;
mod event_log;
mod gatekeeper_details;
mod gatekeeper_endpoints_popup; mod gatekeeper_endpoints_popup;
mod gatekeepers;
mod history;
mod listen_addresses;
mod nominators;
mod payee_popup;
mod payout_all_popup;
mod payout_popup;
mod peers;
mod rebond_popup;
mod reward_details;
mod rotate_popup;
mod staking_details;
mod stash_details;
mod stash_info;
mod unbond_popup;
mod validate_popup;
mod withdraw_popup;
mod withdrawals;
use stash_details::StashDetails;
use staking_details::StakingDetails;
use reward_details::RewardDetails;
use event_log::EventLogs;
use peers::Peers;
use stash_info::StashInfo;
use listen_addresses::ListenAddresses;
use gatekeepers::Gatekeepers;
use gatekeeper_details::GatekeeperDetails;
use nominators::NominatorsByValidator;
use history::History;
use withdrawals::Withdrawals;
use bond_popup::BondPopup; use bond_popup::BondPopup;
use payout_popup::PayoutPopup;
use payout_all_popup::PayoutAllPopup;
use rotate_popup::RotatePopup;
use validate_popup::ValidatePopup;
use chill_popup::ChillPopup;
use unbond_popup::UnbondPopup;
use rebond_popup::RebondPopup;
use withdraw_popup::WithdrawPopup;
use payee_popup::PayeePopup;
use change_blocks_popup::ChangeBlocksPopup; use change_blocks_popup::ChangeBlocksPopup;
use chill_popup::ChillPopup;
use event_log::EventLogs;
use gatekeeper_details::GatekeeperDetails;
use gatekeeper_endpoints_popup::GatekeeperEndpoints; use gatekeeper_endpoints_popup::GatekeeperEndpoints;
use gatekeepers::Gatekeepers;
use history::History;
use listen_addresses::ListenAddresses;
use nominators::NominatorsByValidator;
use payee_popup::PayeePopup;
use payout_all_popup::PayoutAllPopup;
use payout_popup::PayoutPopup;
use peers::Peers;
use rebond_popup::RebondPopup;
use reward_details::RewardDetails;
use rotate_popup::RotatePopup;
use staking_details::StakingDetails;
use stash_details::StashDetails;
use stash_info::StashInfo;
use unbond_popup::UnbondPopup;
use validate_popup::ValidatePopup;
use withdraw_popup::WithdrawPopup;
use withdrawals::Withdrawals;
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub enum CurrentTab { pub enum CurrentTab {
@ -182,24 +182,26 @@ impl Component for Validator {
} }
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> { fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
if !self.is_active { return Ok(None) } if !self.is_active {
return Ok(None);
}
match self.current_tab { match self.current_tab {
CurrentTab::BondPopup | CurrentTab::BondPopup
CurrentTab::RotatePopup | | CurrentTab::RotatePopup
CurrentTab::ValidatePopup | | CurrentTab::ValidatePopup
CurrentTab::ChillPopup | | CurrentTab::ChillPopup
CurrentTab::UnbondPopup | | CurrentTab::UnbondPopup
CurrentTab::RebondPopup | | CurrentTab::RebondPopup
CurrentTab::WithdrawPopup | | CurrentTab::WithdrawPopup
CurrentTab::PayoutPopup | | CurrentTab::PayoutPopup
CurrentTab::PayoutAllPopup | | CurrentTab::PayoutAllPopup
CurrentTab::ChangeBlocksPopup | | CurrentTab::ChangeBlocksPopup
CurrentTab::GatekeeperEndpoints => { | CurrentTab::GatekeeperEndpoints => {
for component in self.components.iter_mut() { for component in self.components.iter_mut() {
component.handle_key_event(key)?; component.handle_key_event(key)?;
} }
}, }
_ => match key.code { _ => match key.code {
KeyCode::Esc => { KeyCode::Esc => {
self.is_active = false; self.is_active = false;
@ -208,51 +210,51 @@ impl Component for Validator {
component.set_active(self.current_tab.clone()); component.set_active(self.current_tab.clone());
} }
return Ok(Some(Action::SetActiveScreen(Mode::Menu))); return Ok(Some(Action::SetActiveScreen(Mode::Menu)));
}, }
KeyCode::Char('R') => { KeyCode::Char('R') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::RotatePopup; self.current_tab = CurrentTab::RotatePopup;
}, }
KeyCode::Char('V') => { KeyCode::Char('V') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::ValidatePopup; self.current_tab = CurrentTab::ValidatePopup;
}, }
KeyCode::Char('U') => { KeyCode::Char('U') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::UnbondPopup; self.current_tab = CurrentTab::UnbondPopup;
}, }
KeyCode::Char('C') => { KeyCode::Char('C') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::ChillPopup; self.current_tab = CurrentTab::ChillPopup;
}, }
KeyCode::Char('B') => { KeyCode::Char('B') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::BondPopup; self.current_tab = CurrentTab::BondPopup;
}, }
KeyCode::Char('E') => { KeyCode::Char('E') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::RebondPopup; self.current_tab = CurrentTab::RebondPopup;
}, }
KeyCode::Char('W') => { KeyCode::Char('W') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::WithdrawPopup; self.current_tab = CurrentTab::WithdrawPopup;
}, }
KeyCode::Char('L') => { KeyCode::Char('L') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::ListenAddresses; self.current_tab = CurrentTab::ListenAddresses;
}, }
KeyCode::Char('I') => { KeyCode::Char('I') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::PayeePopup; self.current_tab = CurrentTab::PayeePopup;
}, }
KeyCode::Char('l') | KeyCode::Right => self.move_right(), KeyCode::Char('l') | KeyCode::Right => self.move_right(),
KeyCode::Char('h') | KeyCode::Left => self.move_left(), KeyCode::Char('h') | KeyCode::Left => self.move_left(),
_ => { _ => {
for component in self.components.iter_mut() { for component in self.components.iter_mut() {
component.handle_key_event(key)?; component.handle_key_event(key)?;
} }
}, }
} },
} }
Ok(None) Ok(None)
} }
@ -275,17 +277,17 @@ impl Component for Validator {
Action::WithdrawValidatorPopup => { Action::WithdrawValidatorPopup => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::WithdrawPopup; self.current_tab = CurrentTab::WithdrawPopup;
}, }
Action::ChangeBlocksPopup => { Action::ChangeBlocksPopup => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::ChangeBlocksPopup; self.current_tab = CurrentTab::ChangeBlocksPopup;
}, }
Action::GatekeeperEndpoints => { Action::GatekeeperEndpoints => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::GatekeeperEndpoints; self.current_tab = CurrentTab::GatekeeperEndpoints;
}, }
Action::ClosePopup => self.current_tab = self.previous_tab, Action::ClosePopup => self.current_tab = self.previous_tab,
_ => {}, _ => {}
} }
for component in self.components.iter_mut() { for component in self.components.iter_mut() {
component.set_active(self.current_tab.clone()); component.set_active(self.current_tab.clone());
@ -309,15 +311,13 @@ pub fn validator_layout(area: Rect) -> [Rect; 4] {
Constraint::Fill(1), Constraint::Fill(1),
Constraint::Fill(1), Constraint::Fill(1),
Constraint::Percentage(25), Constraint::Percentage(25),
]).areas(area) ])
.areas(area)
} }
pub fn validator_details_layout(area: Rect) -> [Rect; 2] { pub fn validator_details_layout(area: Rect) -> [Rect; 2] {
let [place, _, _, _] = validator_layout(area); let [place, _, _, _] = validator_layout(area);
Layout::horizontal([ Layout::horizontal([Constraint::Length(31), Constraint::Fill(1)]).areas(place)
Constraint::Length(31),
Constraint::Fill(1),
]).areas(place)
} }
pub fn validator_session_and_listen_layout(area: Rect) -> [Rect; 3] { pub fn validator_session_and_listen_layout(area: Rect) -> [Rect; 3] {
@ -326,15 +326,13 @@ pub fn validator_session_and_listen_layout(area: Rect) -> [Rect; 3] {
Constraint::Length(6), Constraint::Length(6),
Constraint::Length(6), Constraint::Length(6),
Constraint::Fill(1), Constraint::Fill(1),
]).areas(place) ])
.areas(place)
} }
pub fn validator_gatekeeped_networks_layout(area: Rect) -> [Rect; 2] { pub fn validator_gatekeeped_networks_layout(area: Rect) -> [Rect; 2] {
let [_, _, place] = validator_session_and_listen_layout(area); let [_, _, place] = validator_session_and_listen_layout(area);
Layout::horizontal([ Layout::horizontal([Constraint::Fill(1), Constraint::Length(30)]).areas(place)
Constraint::Fill(1),
Constraint::Length(30),
]).areas(place)
} }
pub fn validator_statistics_layout(area: Rect) -> [Rect; 3] { pub fn validator_statistics_layout(area: Rect) -> [Rect; 3] {
@ -343,7 +341,8 @@ pub fn validator_statistics_layout(area: Rect) -> [Rect; 3] {
Constraint::Percentage(30), Constraint::Percentage(30),
Constraint::Percentage(40), Constraint::Percentage(40),
Constraint::Percentage(30), Constraint::Percentage(30),
]).areas(place) ])
.areas(place)
} }
pub fn validator_balance_layout(area: Rect) -> [Rect; 3] { pub fn validator_balance_layout(area: Rect) -> [Rect; 3] {
@ -352,5 +351,6 @@ pub fn validator_balance_layout(area: Rect) -> [Rect; 3] {
Constraint::Length(6), Constraint::Length(6),
Constraint::Length(6), Constraint::Length(6),
Constraint::Fill(1), Constraint::Fill(1),
]).areas(place) ])
.areas(place)
} }

View File

@ -2,23 +2,19 @@ use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent}; use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::{Constraint, Margin}; use ratatui::layout::{Constraint, Margin};
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Text,
widgets::{ widgets::{
Block, Cell, Row, Table, TableState, Scrollbar, Padding, Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table,
ScrollbarOrientation, ScrollbarState, TableState,
}, },
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::Nominator; use crate::types::Nominator;
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
pub struct NominatorsByValidator { pub struct NominatorsByValidator {
is_active: bool, is_active: bool,
@ -76,7 +72,7 @@ impl NominatorsByValidator {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
@ -99,8 +95,8 @@ impl NominatorsByValidator {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i); self.scroll_state = self.scroll_state.position(i);
@ -134,14 +130,22 @@ impl Component for NominatorsByValidator {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) { if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
@ -149,8 +153,11 @@ impl Component for NominatorsByValidator {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetStashAccount(stash) => self.stash = stash, Action::SetStashAccount(stash) => self.stash = stash,
Action::SetNominatorsByValidator(nominators, account_id) if self.stash == account_id => Action::SetNominatorsByValidator(nominators, account_id)
self.update_nominators(nominators), if self.stash == account_id =>
{
self.update_nominators(nominators)
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -163,7 +170,7 @@ impl Component for NominatorsByValidator {
KeyCode::Down | KeyCode::Char('j') => self.next_row(), KeyCode::Down | KeyCode::Char('j') => self.next_row(),
KeyCode::Char('g') => self.first_row(), KeyCode::Char('g') => self.first_row(),
KeyCode::Char('G') => self.last_row(), KeyCode::Char('G') => self.last_row(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -173,28 +180,27 @@ impl Component for NominatorsByValidator {
let [place, _, _] = super::validator_statistics_layout(area); let [place, _, _] = super::validator_statistics_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 table = Table::new( let table = Table::new(
self.nominators self.nominators.iter().map(|info| {
.iter() Row::new(vec![
.map(|info| { Cell::from(Text::from(info.address.clone()).alignment(Alignment::Left)),
Row::new(vec![ Cell::from(
Cell::from(Text::from(info.address.clone()).alignment(Alignment::Left)), Text::from(self.prepare_u128(info.value)).alignment(Alignment::Right),
Cell::from(Text::from(self.prepare_u128(info.value)).alignment(Alignment::Right)), ),
]) ])
}), }),
[ [Constraint::Min(0), Constraint::Min(11)],
Constraint::Min(0),
Constraint::Min(11),
],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.padding(Padding::right(2)) .border_type(border_type)
.title_alignment(Alignment::Right) .padding(Padding::right(2))
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("My Nominators")); .title_style(self.palette.create_title_style(false))
.title("My Nominators"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -205,7 +211,10 @@ impl Component for NominatorsByValidator {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,18 +1,16 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
text::Text, text::Text,
widgets::{Block, Cell, Clear, Paragraph, Row, Table, TableState}, widgets::{Block, Cell, Clear, Paragraph, Row, Table, TableState},
Frame, Frame,
}; };
use subxt::ext::sp_core::crypto::{
ByteArray, Ss58Codec, Ss58AddressFormat, AccountId32,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use subxt::ext::sp_core::crypto::{AccountId32, ByteArray, Ss58AddressFormat, Ss58Codec};
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -36,7 +34,7 @@ pub struct PayeePopup {
address: Input, address: Input,
possible_payee_options: &'static [(&'static str, &'static str)], possible_payee_options: &'static [(&'static str, &'static str)],
current_reward_destination: RewardDestination, current_reward_destination: RewardDestination,
palette: StylePalette palette: StylePalette,
} }
impl Default for PayeePopup { impl Default for PayeePopup {
@ -61,9 +59,18 @@ impl PayeePopup {
address: Input::new(String::new()), address: Input::new(String::new()),
current_reward_destination: Default::default(), current_reward_destination: Default::default(),
possible_payee_options: &[ possible_payee_options: &[
("Re-stake", "(pay into the stash account, increasing the amount at stake accordingly)"), (
("Stake", "(pay into the stash account, not increasing the amount at stake)"), "Re-stake",
("Account", "(pay into a specified account different from stash)"), "(pay into the stash account, increasing the amount at stake accordingly)",
),
(
"Stake",
"(pay into the stash account, not increasing the amount at stake)",
),
(
"Account",
"(pay into a specified account different from stash)",
),
("None", "(refuse to receive all rewards from staking)"), ("None", "(refuse to receive all rewards from staking)"),
], ],
palette: StylePalette::default(), palette: StylePalette::default(),
@ -85,7 +92,7 @@ impl PayeePopup {
let address = AccountId32::from(account_id) let address = AccountId32::from(account_id)
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)); .to_ss58check_with_version(Ss58AddressFormat::custom(1996));
(2, address) (2, address)
}, }
RewardDestination::None => (3, Default::default()), RewardDestination::None => (3, Default::default()),
_ => (0, Default::default()), _ => (0, Default::default()),
}; };
@ -106,13 +113,12 @@ impl PayeePopup {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.move_to_row(i); self.move_to_row(i);
} }
fn previous_row(&mut self) { fn previous_row(&mut self) {
let i = match self.table_state.selected() { let i = match self.table_state.selected() {
Some(i) => { Some(i) => {
@ -121,18 +127,15 @@ impl PayeePopup {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.move_to_row(i); self.move_to_row(i);
} }
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::EventLog( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::ValidatorLog));
message,
level,
ActionTarget::ValidatorLog));
} }
} }
@ -145,8 +148,12 @@ impl PayeePopup {
Ok((account_id, format)) => { Ok((account_id, format)) => {
if format != Ss58AddressFormat::custom(1996) { if format != Ss58AddressFormat::custom(1996) {
self.log_event( self.log_event(
format!("provided public address for {} is not part of Casper/Ghost ecosystem", self.address.value()), format!(
ActionLevel::Error); "provided public address for {} is not part of Casper/Ghost ecosystem",
self.address.value()
),
ActionLevel::Error,
);
} }
let seed_vec = account_id.to_raw_vec(); let seed_vec = account_id.to_raw_vec();
let mut account_id = [0u8; 32]; let mut account_id = [0u8; 32];
@ -154,15 +161,18 @@ impl PayeePopup {
self.proposed_account_id = Some(account_id); self.proposed_account_id = Some(account_id);
self.submit_new_payee(); self.submit_new_payee();
}, }
_ => { _ => {
self.log_event( self.log_event(
format!("could not create valid account id from {}", self.address.value()), format!(
ActionLevel::Error); "could not create valid account id from {}",
self.address.value()
),
ActionLevel::Error,
);
self.proposed_account_id = None; self.proposed_account_id = None;
} }
}; };
} }
fn submit_new_payee(&mut self) { fn submit_new_payee(&mut self) {
@ -171,7 +181,8 @@ impl PayeePopup {
0 => RewardDestination::Staked, 0 => RewardDestination::Staked,
1 => RewardDestination::Stash, 1 => RewardDestination::Stash,
2 => { 2 => {
let account_id = self.proposed_account_id let account_id = self
.proposed_account_id
.expect("checked before in submit_new_input; qed"); .expect("checked before in submit_new_input; qed");
RewardDestination::Account(account_id) RewardDestination::Account(account_id)
} }
@ -182,17 +193,20 @@ impl PayeePopup {
if !self.is_bonded { if !self.is_bonded {
self.log_event( self.log_event(
"no bond detected, stake minimum bond amount first".to_string(), "no bond detected, stake minimum bond amount first".to_string(),
ActionLevel::Warn); ActionLevel::Warn,
);
} else if new_destination == self.current_reward_destination { } else if new_destination == self.current_reward_destination {
self.log_event( self.log_event(
"same destination choosen, no need for transaction".to_string(), "same destination choosen, no need for transaction".to_string(),
ActionLevel::Warn); ActionLevel::Warn,
);
} else { } else {
if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::SetPayee( let _ = network_tx.send(Action::SetPayee(
self.stash_secret_seed, self.stash_secret_seed,
new_destination, new_destination,
ActionTarget::ValidatorLog)); ActionTarget::ValidatorLog,
));
} }
} }
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
@ -247,12 +261,18 @@ impl Component for PayeePopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
self.palette.with_highlight_style(style.get("highlight_style").copied()); .with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
} }
Ok(()) Ok(())
} }
@ -267,7 +287,7 @@ impl Component for PayeePopup {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.trigger_address_input(), KeyCode::Esc => self.trigger_address_input(),
_ => {}, _ => {}
}; };
} else { } else {
match key.code { match key.code {
@ -276,7 +296,7 @@ impl Component for PayeePopup {
KeyCode::Up | KeyCode::Char('k') => self.previous_row(), KeyCode::Up | KeyCode::Char('k') => self.previous_row(),
KeyCode::Down | KeyCode::Char('j') => self.next_row(), KeyCode::Down | KeyCode::Char('j') => self.next_row(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
} }
@ -287,9 +307,12 @@ impl Component for PayeePopup {
match action { match action {
Action::SetStashAccount(account_id) => self.stash_account_id = account_id, Action::SetStashAccount(account_id) => self.stash_account_id = account_id,
Action::SetStashSecret(secret_seed) => self.stash_secret_seed = secret_seed, Action::SetStashSecret(secret_seed) => self.stash_secret_seed = secret_seed,
Action::SetIsBonded(is_bonded, account_id) if self.stash_account_id == account_id => Action::SetIsBonded(is_bonded, account_id) if self.stash_account_id == account_id => {
self.is_bonded = is_bonded, self.is_bonded = is_bonded
Action::SetStakingPayee(reward_destination, account_id) if self.stash_account_id == account_id => { }
Action::SetStakingPayee(reward_destination, account_id)
if self.stash_account_id == account_id =>
{
let destination_changed = self.current_reward_destination != reward_destination; let destination_changed = self.current_reward_destination != reward_destination;
self.current_reward_destination = reward_destination; self.current_reward_destination = reward_destination;
if destination_changed || self.table_state.selected().is_none() { if destination_changed || self.table_state.selected().is_none() {
@ -318,16 +341,18 @@ impl Component for PayeePopup {
]) ])
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
[Constraint::Length(8), Constraint::Min(0)] [Constraint::Length(8), Constraint::Min(0)],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_style(self.palette.create_popup_title_style()) .border_type(border_type)
.title_alignment(Alignment::Right) .title_style(self.palette.create_popup_title_style())
.title("Select reward destination")); .title_alignment(Alignment::Right)
.title("Select reward destination"),
);
let v = Layout::vertical([Constraint::Max(6)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(6)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(83)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(83)]).flex(Flex::Center);
@ -338,13 +363,14 @@ impl Component for PayeePopup {
frame.render_stateful_widget(table, area, &mut self.table_state); frame.render_stateful_widget(table, area, &mut self.table_state);
if self.is_input_active { if self.is_input_active {
let input_amount = Paragraph::new(self.address.value()) let input_amount = Paragraph::new(self.address.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Destination account")); .title("Destination account"),
);
let v = Layout::vertical([Constraint::Max(8)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(8)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(51)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(51)]).flex(Flex::Center);
@ -355,8 +381,8 @@ impl Component for PayeePopup {
frame.render_widget(input_amount, input_area); frame.render_widget(input_amount, input_area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
input_area.x + self.address.cursor() as u16 + 1, input_area.x + self.address.cursor() as u16 + 1,
input_area.y + 1 input_area.y + 1,
)); ));
} }
} }

View File

@ -1,19 +1,15 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)] #[derive(Debug)]
pub struct PayoutAllPopup { pub struct PayoutAllPopup {
@ -23,7 +19,7 @@ pub struct PayoutAllPopup {
secret_seed: [u8; 32], secret_seed: [u8; 32],
stash_account_id: [u8; 32], stash_account_id: [u8; 32],
era_indexes: Vec<u32>, era_indexes: Vec<u32>,
palette: StylePalette palette: StylePalette,
} }
impl Default for PayoutAllPopup { impl Default for PayoutAllPopup {
@ -57,9 +53,10 @@ impl PayoutAllPopup {
if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
for era_index in &self.era_indexes { for era_index in &self.era_indexes {
let _ = network_tx.send(Action::PayoutStakers( let _ = network_tx.send(Action::PayoutStakers(
self.secret_seed, self.secret_seed,
self.stash_account_id, self.stash_account_id,
*era_index)); *era_index,
));
} }
} }
self.close_popup(); self.close_popup();
@ -88,11 +85,16 @@ impl Component for PayoutAllPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -102,7 +104,7 @@ impl Component for PayoutAllPopup {
match key.code { match key.code {
KeyCode::Enter => self.start_payout(), KeyCode::Enter => self.start_payout(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -121,17 +123,24 @@ impl Component for PayoutAllPopup {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let popup = Paragraph::new( let popup = Paragraph::new(format!(
format!(" Do payout for {} eras, from {} to {}", " Do payout for {} eras, from {} to {}",
self.era_indexes.len(), self.era_indexes.len(),
self.era_indexes.first().expect("Length of unclaimed indexes always more then one; qed"), self.era_indexes
self.era_indexes.last().expect("Length of unclaimed indexes always more then one; qed"))) .first()
.block(Block::bordered() .expect("Length of unclaimed indexes always more then one; qed"),
self.era_indexes
.last()
.expect("Length of unclaimed indexes always more then one; qed")
))
.block(
Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Enter to proceed / Esc to close")); .title("Enter to proceed / Esc to close"),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);

View File

@ -1,19 +1,15 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)] #[derive(Debug)]
pub struct PayoutPopup { pub struct PayoutPopup {
@ -23,7 +19,7 @@ pub struct PayoutPopup {
secret_seed: [u8; 32], secret_seed: [u8; 32],
stash_account_id: [u8; 32], stash_account_id: [u8; 32],
era_index: u32, era_index: u32,
palette: StylePalette palette: StylePalette,
} }
impl Default for PayoutPopup { impl Default for PayoutPopup {
@ -55,9 +51,10 @@ impl PayoutPopup {
fn start_payout(&mut self) { fn start_payout(&mut self) {
if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::PayoutStakers( let _ = network_tx.send(Action::PayoutStakers(
self.secret_seed, self.secret_seed,
self.stash_account_id, self.stash_account_id,
self.era_index)); self.era_index,
));
} }
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
@ -87,11 +84,16 @@ impl Component for PayoutPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -101,7 +103,7 @@ impl Component for PayoutPopup {
match key.code { match key.code {
KeyCode::Enter => self.start_payout(), KeyCode::Enter => self.start_payout(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -120,13 +122,14 @@ impl Component for PayoutPopup {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let popup = Paragraph::new(format!(" Do payout for era #{}", self.era_index)) let popup = Paragraph::new(format!(" Do payout for era #{}", self.era_index)).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Enter to proceed / Esc to close")); .title("Enter to proceed / Esc to close"),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);

View File

@ -2,22 +2,18 @@ use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent}; use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::{Constraint, Margin}; use ratatui::layout::{Constraint, Margin};
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Text,
widgets::{ widgets::{
Block, Cell, Row, Table, TableState, Scrollbar, Padding, Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table,
ScrollbarOrientation, ScrollbarState, TableState,
}, },
Frame Frame,
}; };
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::PeerInformation; use crate::types::PeerInformation;
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
pub struct Peers { pub struct Peers {
is_active: bool, is_active: bool,
@ -69,7 +65,7 @@ impl Peers {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
@ -92,8 +88,8 @@ impl Peers {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i); self.scroll_state = self.scroll_state.position(i);
@ -116,14 +112,22 @@ impl PartialComponent for Peers {
impl Component for Peers { impl Component for Peers {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) { if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
@ -143,7 +147,7 @@ impl Component for Peers {
KeyCode::Down | KeyCode::Char('j') => self.next_row(), KeyCode::Down | KeyCode::Char('j') => self.next_row(),
KeyCode::Char('g') => self.first_row(), KeyCode::Char('g') => self.first_row(),
KeyCode::Char('G') => self.last_row(), KeyCode::Char('G') => self.last_row(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -153,16 +157,16 @@ impl Component for Peers {
let [_, _, place, _] = super::validator_layout(area); let [_, _, place, _] = super::validator_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 table = Table::new( let table = Table::new(
self.peers self.peers.iter().map(|info| {
.iter() Row::new(vec![
.map(|info| { Cell::from(Text::from(info.peer_id.clone()).alignment(Alignment::Left)),
Row::new(vec![ Cell::from(Text::from(info.roles.clone()).alignment(Alignment::Center)),
Cell::from(Text::from(info.peer_id.clone()).alignment(Alignment::Left)), Cell::from(Text::from(info.best_hash.to_string()).alignment(Alignment::Center)),
Cell::from(Text::from(info.roles.clone()).alignment(Alignment::Center)), Cell::from(
Cell::from(Text::from(info.best_hash.to_string()).alignment(Alignment::Center)), Text::from(info.best_number.to_string()).alignment(Alignment::Right),
Cell::from(Text::from(info.best_number.to_string()).alignment(Alignment::Right)), ),
]) ])
}), }),
[ [
Constraint::Fill(1), Constraint::Fill(1),
Constraint::Length(11), Constraint::Length(11),
@ -172,13 +176,15 @@ impl Component for Peers {
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.padding(Padding::right(2)) .border_type(border_type)
.title_alignment(Alignment::Right) .padding(Padding::right(2))
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("My Peers")); .title_style(self.palette.create_title_style(false))
.title("My Peers"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -189,7 +195,10 @@ impl Component for Peers {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,14 +1,14 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -24,7 +24,7 @@ pub struct RebondPopup {
network_tx: Option<Sender<Action>>, network_tx: Option<Sender<Action>>,
secret_seed: [u8; 32], secret_seed: [u8; 32],
amount: Input, amount: Input,
palette: StylePalette palette: StylePalette,
} }
impl Default for RebondPopup { impl Default for RebondPopup {
@ -54,8 +54,7 @@ impl RebondPopup {
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::ValidatorLog));
Action::EventLog(message, level, ActionTarget::ValidatorLog));
} }
} }
@ -74,9 +73,10 @@ impl RebondPopup {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
} }
}, }
Err(err) => self.log_event( Err(err) => {
format!("invalid amount, error: {err}"), ActionLevel::Error), self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error)
}
} }
} }
} }
@ -126,11 +126,16 @@ impl Component for RebondPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -143,7 +148,7 @@ impl Component for RebondPopup {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -160,13 +165,14 @@ impl Component for RebondPopup {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.amount.value()) let input = Paragraph::new(self.amount.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title(format!("Amount to rebond"))); .title(format!("Amount to rebond")),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
@ -175,8 +181,8 @@ impl Component for RebondPopup {
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(input, area); frame.render_widget(input, area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
area.x + self.amount.cursor() as u16 + 1, area.x + self.amount.cursor() as u16 + 1,
area.y + 1 area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -1,20 +1,16 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::layout::Constraint; use ratatui::layout::Constraint;
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Text,
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame Frame,
}; };
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::EraRewardPoints; use crate::types::EraRewardPoints;
use crate::widgets::DotSpinner; use crate::widgets::DotSpinner;
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
pub struct RewardDetails { pub struct RewardDetails {
palette: StylePalette, palette: StylePalette,
@ -58,9 +54,12 @@ impl RewardDetails {
fn comission_to_string(&self) -> String { fn comission_to_string(&self) -> String {
match self.commission { match self.commission {
Some(commission) => { Some(commission) => {
if self.nominators_blocked { "blocked".to_string() } if self.nominators_blocked {
else { format!("{:.2}%", commission as f64 / 10_000_000.0) } "blocked".to_string()
}, } else {
format!("{:.2}%", commission as f64 / 10_000_000.0)
}
}
None => DotSpinner::default().to_string(), None => DotSpinner::default().to_string(),
} }
} }
@ -77,20 +76,28 @@ impl RewardDetails {
} }
impl PartialComponent for RewardDetails { impl PartialComponent for RewardDetails {
fn set_active(&mut self, _current_tab: CurrentTab) { } fn set_active(&mut self, _current_tab: CurrentTab) {}
} }
impl Component for RewardDetails { impl Component for RewardDetails {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) { if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
@ -98,8 +105,12 @@ impl Component for RewardDetails {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetStashAccount(stash) => self.stash = stash, Action::SetStashAccount(stash) => self.stash = stash,
Action::SetCurrentValidatorEraRewards(_, _, individual) => self.update_individual(&individual), Action::SetCurrentValidatorEraRewards(_, _, individual) => {
Action::SetValidatorPrefs(commission, disabled, account_id) if self.stash == account_id => { self.update_individual(&individual)
}
Action::SetValidatorPrefs(commission, disabled, account_id)
if self.stash == account_id =>
{
self.commission = commission; self.commission = commission;
self.in_staking_validators = commission.is_some(); self.in_staking_validators = commission.is_some();
self.nominators_blocked = disabled; self.nominators_blocked = disabled;
@ -110,7 +121,7 @@ impl Component for RewardDetails {
} else { } else {
self.in_next_keys = !session_key_info.key.is_empty(); self.in_next_keys = !session_key_info.key.is_empty();
} }
}, }
Action::Apy(apy) => self.apy = apy, Action::Apy(apy) => self.apy = apy,
Action::TreasuryApy(apy) => self.treasury_apy = apy, Action::TreasuryApy(apy) => self.treasury_apy = apy,
Action::Inflation(inflation) => self.inflation = inflation, Action::Inflation(inflation) => self.inflation = inflation,
@ -130,8 +141,8 @@ impl Component for RewardDetails {
match (self.in_staking_validators, is_fully_in_session) { match (self.in_staking_validators, is_fully_in_session) {
(true, true) => "Active", (true, true) => "Active",
(true, false) => "Rotating", (true, false) => "Rotating",
(false, true) if self.in_rewards => { "Stopping" } (false, true) if self.in_rewards => "Stopping",
(false, true) if !self.in_rewards => { "Chill" } (false, true) if !self.in_rewards => "Chill",
_ => "Nothing", _ => "Nothing",
} }
}; };
@ -156,19 +167,18 @@ impl Component for RewardDetails {
Cell::from(Text::from(self.inflation.clone()).alignment(Alignment::Right)), Cell::from(Text::from(self.inflation.clone()).alignment(Alignment::Right)),
]), ]),
], ],
[ [Constraint::Min(13), Constraint::Min(0)],
Constraint::Min(13),
Constraint::Min(0),
],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title(staking_status)); .title_style(self.palette.create_title_style(false))
.title(staking_status),
);
frame.render_widget(table, place); frame.render_widget(table, place);

View File

@ -1,20 +1,16 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Rect},
text::Text, text::Text,
widgets::{Block, Cell, Clear, Row, Table}, widgets::{Block, Cell, Clear, Row, Table},
Frame, Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)] #[derive(Debug)]
pub struct RotatePopup { pub struct RotatePopup {
@ -23,7 +19,7 @@ pub struct RotatePopup {
network_tx: Option<Sender<Action>>, network_tx: Option<Sender<Action>>,
cached_keys: String, cached_keys: String,
secret_seed: [u8; 32], secret_seed: [u8; 32],
palette: StylePalette palette: StylePalette,
} }
impl Default for RotatePopup { impl Default for RotatePopup {
@ -55,7 +51,9 @@ impl RotatePopup {
if !self.cached_keys.is_empty() && self.cached_keys.len() == 258 { if !self.cached_keys.is_empty() && self.cached_keys.len() == 258 {
if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::SetSessionKeys( let _ = network_tx.send(Action::SetSessionKeys(
self.secret_seed, self.cached_keys.clone())); self.secret_seed,
self.cached_keys.clone(),
));
} }
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
@ -108,11 +106,16 @@ impl Component for RotatePopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -131,7 +134,7 @@ impl Component for RotatePopup {
match key.code { match key.code {
KeyCode::Enter => self.rotate_keys(), KeyCode::Enter => self.rotate_keys(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -160,20 +163,19 @@ impl Component for RotatePopup {
Cell::from(Text::from("slow".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("slow".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(slow_key).alignment(Alignment::Right)), Cell::from(Text::from(slow_key).alignment(Alignment::Right)),
]), ]),
],
[
Constraint::Length(4),
Constraint::Min(0),
], ],
[Constraint::Length(4), Constraint::Min(0)],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Rotate session keys (Enter to proceed / Esc to close)")); .title_style(self.palette.create_title_style(false))
.title("Rotate session keys (Enter to proceed / Esc to close)"),
);
let v = Layout::vertical([Constraint::Max(6)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(6)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(73)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(73)]).flex(Flex::Center);

View File

@ -1,20 +1,16 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::layout::Constraint; use ratatui::layout::Constraint;
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Text,
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame Frame,
}; };
use subxt::ext::sp_core::crypto::{Ss58Codec, Ss58AddressFormat, AccountId32}; use subxt::ext::sp_core::crypto::{AccountId32, Ss58AddressFormat, Ss58Codec};
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::RewardDestination; use crate::types::RewardDestination;
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
pub struct StakingDetails { pub struct StakingDetails {
palette: StylePalette, palette: StylePalette,
@ -63,27 +59,34 @@ impl StakingDetails {
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)); .to_ss58check_with_version(Ss58AddressFormat::custom(1996));
let tail = address.len().saturating_sub(5); let tail = address.len().saturating_sub(5);
format!("{}..{}", &address[..5], &address[tail..]) format!("{}..{}", &address[..5], &address[tail..])
}, }
} }
} }
} }
impl PartialComponent for StakingDetails { impl PartialComponent for StakingDetails {
fn set_active(&mut self, _current_tab: CurrentTab) { } fn set_active(&mut self, _current_tab: CurrentTab) {}
} }
impl Component for StakingDetails { impl Component for StakingDetails {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) { if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
@ -92,8 +95,9 @@ impl Component for StakingDetails {
match action { match action {
Action::SetStashAccount(account_id) => self.stash = account_id, Action::SetStashAccount(account_id) => self.stash = account_id,
Action::NextReward(next_reward) => self.next_reward = next_reward, Action::NextReward(next_reward) => self.next_reward = next_reward,
Action::SetStakingPayee(destination, account_id) if self.stash == account_id => Action::SetStakingPayee(destination, account_id) if self.stash == account_id => {
self.reward_destination = destination, self.reward_destination = destination
}
Action::SetStakedRatio(total, own, account_id) if self.stash == account_id => { Action::SetStakedRatio(total, own, account_id) if self.stash == account_id => {
self.staked_total = total; self.staked_total = total;
self.staked_own = own; self.staked_own = own;
@ -112,34 +116,45 @@ impl Component for StakingDetails {
vec![ vec![
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Stake value".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Stake value".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.staked_total)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.staked_total))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Own stake".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Own stake".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.staked_own)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.staked_own)).alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Other stake".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Other stake".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.staked_total.saturating_sub(self.staked_own))).alignment(Alignment::Right)), Cell::from(
Text::from(
self.prepare_u128(self.staked_total.saturating_sub(self.staked_own)),
)
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Next reward".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Next reward".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.next_reward)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.next_reward)).alignment(Alignment::Right),
),
]), ]),
], ],
[ [Constraint::Min(11), Constraint::Min(0)],
Constraint::Min(11),
Constraint::Min(0),
],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title(title)); .title_style(self.palette.create_title_style(false))
.title(title),
);
frame.render_widget(table, place); frame.render_widget(table, place);

View File

@ -1,20 +1,16 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::layout::Constraint; use ratatui::layout::Constraint;
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Text,
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame Frame,
}; };
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::widgets::DotSpinner; use crate::widgets::DotSpinner;
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
pub struct StashDetails { pub struct StashDetails {
palette: StylePalette, palette: StylePalette,
@ -56,8 +52,8 @@ impl StashDetails {
let value = value as f64 / 10f64.powi(18); let value = value as f64 / 10f64.powi(18);
let after = Self::DECIMALS; let after = Self::DECIMALS;
format!("{:.after$}{}", value, Self::TICKER) format!("{:.after$}{}", value, Self::TICKER)
}, }
None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER) None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER),
} }
} }
@ -71,7 +67,7 @@ impl StashDetails {
} }
impl PartialComponent for StashDetails { impl PartialComponent for StashDetails {
fn set_active(&mut self, _current_tab: CurrentTab) { } fn set_active(&mut self, _current_tab: CurrentTab) {}
} }
impl Component for StashDetails { impl Component for StashDetails {
@ -82,14 +78,22 @@ impl Component for StashDetails {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) { if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
@ -98,22 +102,31 @@ impl Component for StashDetails {
match action { match action {
Action::SetStashSecret(secret) => self.stash_secret = secret, Action::SetStashSecret(secret) => self.stash_secret = secret,
Action::SetStashAccount(account_id) => self.stash_account_id = account_id, Action::SetStashAccount(account_id) => self.stash_account_id = account_id,
Action::SetIsBonded(is_bonded, account_id) if self.stash_account_id == account_id => Action::SetIsBonded(is_bonded, account_id) if self.stash_account_id == account_id => {
self.is_bonded = is_bonded, self.is_bonded = is_bonded
Action::SetStakedAmountRatio(total, active, account_id) if self.stash_account_id == account_id => { }
Action::SetStakedAmountRatio(total, active, account_id)
if self.stash_account_id == account_id =>
{
self.staked_total = total; self.staked_total = total;
self.staked_active = active; self.staked_active = active;
}, }
Action::BalanceResponse(account_id, maybe_balance) if account_id == self.stash_account_id => { Action::BalanceResponse(account_id, maybe_balance)
if account_id == self.stash_account_id =>
{
if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::SetSender( let _ = network_tx.send(Action::SetSender(
hex::encode(self.stash_secret), hex::encode(self.stash_secret),
maybe_balance.clone().map(|b| b.nonce))); maybe_balance.clone().map(|b| b.nonce),
));
} }
self.free_balance = maybe_balance.map(|balance| balance.free self.free_balance = maybe_balance.map(|balance| {
.saturating_sub(balance.frozen) balance
.saturating_sub(balance.reserved)); .free
}, .saturating_sub(balance.frozen)
.saturating_sub(balance.reserved)
});
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -131,30 +144,38 @@ impl Component for StashDetails {
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Free balance".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Free balance".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.free_balance)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.free_balance))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Total staked".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Total staked".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.staked_total)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.staked_total))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Active staked".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Active staked".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.staked_active)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.staked_active))
.alignment(Alignment::Right),
),
]), ]),
], ],
[ [Constraint::Min(14), Constraint::Min(0)],
Constraint::Min(14),
Constraint::Min(0),
],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Stash details")); .title_style(self.palette.create_title_style(false))
.title("Stash details"),
);
frame.render_widget(table, place); frame.render_widget(table, place);

View File

@ -1,37 +1,32 @@
use std::path::PathBuf;
use std::fs::File; use std::fs::File;
use std::io::{Write, BufRead, BufReader}; use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf;
use color_eyre::Result; use color_eyre::Result;
use ratatui::layout::Constraint; use ratatui::layout::Constraint;
use ratatui::style::{Modifier, Stylize}; use ratatui::style::{Modifier, Stylize};
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Text,
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame Frame,
}; };
use std::sync::mpsc::Sender;
use subxt::{ use subxt::{
tx::PairSigner,
ext::sp_core::{ ext::sp_core::{
Pair as PairT, crypto::{AccountId32, Ss58AddressFormat, Ss58Codec},
sr25519::Pair, sr25519::Pair,
crypto::{Ss58Codec, Ss58AddressFormat, AccountId32}, Pair as PairT,
}, },
tx::PairSigner,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::casper::CasperConfig; use crate::casper::CasperConfig;
use crate::types::{ActionLevel, ActionTarget}; use crate::types::{ActionLevel, ActionTarget};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette, types::SessionKeyInfo};
types::SessionKeyInfo,
action::Action,
config::Config,
palette::StylePalette,
};
pub struct StashInfo { pub struct StashInfo {
is_active: bool, is_active: bool,
@ -68,8 +63,7 @@ impl StashInfo {
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::ValidatorLog));
Action::EventLog(message, level, ActionTarget::ValidatorLog));
} }
} }
@ -96,7 +90,8 @@ impl StashInfo {
self.initiate_stash_info(account_id, seed); self.initiate_stash_info(account_id, seed);
self.log_event( self.log_event(
format!("stash key {address} read from disk"), format!("stash key {address} read from disk"),
ActionLevel::Info); ActionLevel::Info,
);
self.stash_address = address; self.stash_address = address;
self.stash_pair = Some(pair_signer); self.stash_pair = Some(pair_signer);
@ -104,16 +99,24 @@ impl StashInfo {
Ok(()) Ok(())
} else { } else {
self.log_event( self.log_event(
format!("file at '{:?}' is empty, trying to create new key", &self.stash_filepath), format!(
ActionLevel::Warn); "file at '{:?}' is empty, trying to create new key",
&self.stash_filepath
),
ActionLevel::Warn,
);
self.generate_and_save_new_key() self.generate_and_save_new_key()
} }
}, }
Err(_) => { Err(_) => {
self.log_event( self.log_event(
format!("file at '{:?}' not found, trying to create new key", &self.stash_filepath), format!(
ActionLevel::Warn); "file at '{:?}' not found, trying to create new key",
&self.stash_filepath
),
ActionLevel::Warn,
);
self.generate_and_save_new_key() self.generate_and_save_new_key()
} }
@ -133,8 +136,12 @@ impl StashInfo {
self.initiate_stash_info(account_id, seed); self.initiate_stash_info(account_id, seed);
self.log_event( self.log_event(
format!("new stash key {} created and stored at {:?}", &address, self.stash_filepath), format!(
ActionLevel::Info); "new stash key {} created and stored at {:?}",
&address, self.stash_filepath
),
ActionLevel::Info,
);
self.stash_address = address; self.stash_address = address;
self.stash_pair = Some(pair_signer); self.stash_pair = Some(pair_signer);
@ -179,7 +186,7 @@ impl StashInfo {
} }
impl PartialComponent for StashInfo { impl PartialComponent for StashInfo {
fn set_active(&mut self, _current_tab: CurrentTab) { } fn set_active(&mut self, _current_tab: CurrentTab) {}
} }
impl Component for StashInfo { impl Component for StashInfo {
@ -195,14 +202,22 @@ impl Component for StashInfo {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
self.read_or_create_stash()?; self.read_or_create_stash()?;
Ok(()) Ok(())
@ -220,37 +235,38 @@ impl Component for StashInfo {
let [place, _, _] = super::validator_session_and_listen_layout(area); let [place, _, _] = super::validator_session_and_listen_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 table = Table::new( let table = Table::new(
self.key_names self.key_names.iter().map(|name| {
.iter() let address_text = match self.session_keys.get(*name) {
.map(|name| { Some(key_info) => {
let address_text = match self.session_keys.get(*name) { let mut address_text =
Some(key_info) => { Text::from(key_info.key.clone()).alignment(Alignment::Center);
let mut address_text = Text::from(key_info.key.clone()).alignment(Alignment::Center); if !key_info.is_stored {
if !key_info.is_stored { address_text = address_text.add_modifier(Modifier::CROSSED_OUT);
address_text = address_text.add_modifier(Modifier::CROSSED_OUT); }
} address_text
address_text }
}, None => Text::from("-").alignment(Alignment::Center),
None => Text::from("-").alignment(Alignment::Center), };
}; let queued_name = format!("q_{}", name);
let queued_name = format!("q_{}", name); let queued_address_text = match self.session_keys.get(&queued_name) {
let queued_address_text = match self.session_keys.get(&queued_name) { Some(key_info) => {
Some(key_info) => { let mut queued_address_text =
let mut queued_address_text = Text::from(key_info.key.clone()).alignment(Alignment::Right); Text::from(key_info.key.clone()).alignment(Alignment::Right);
if !key_info.is_stored { if !key_info.is_stored {
queued_address_text = queued_address_text.add_modifier(Modifier::CROSSED_OUT); queued_address_text =
} queued_address_text.add_modifier(Modifier::CROSSED_OUT);
queued_address_text }
}, queued_address_text
None => Text::from("-").alignment(Alignment::Right), }
}; None => Text::from("-").alignment(Alignment::Right),
Row::new(vec![ };
Cell::from(Text::from(name.to_string()).alignment(Alignment::Left)), Row::new(vec![
Cell::from(address_text), Cell::from(Text::from(name.to_string()).alignment(Alignment::Left)),
Cell::from(Text::from("-->".to_string()).alignment(Alignment::Center)), Cell::from(address_text),
Cell::from(queued_address_text), Cell::from(Text::from("-->".to_string()).alignment(Alignment::Center)),
]) Cell::from(queued_address_text),
}), ])
}),
[ [
Constraint::Length(4), Constraint::Length(4),
Constraint::Min(0), Constraint::Min(0),
@ -260,12 +276,14 @@ impl Component for StashInfo {
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title(self.stash_address.clone())); .title_style(self.palette.create_title_style(false))
.title(self.stash_address.clone()),
);
frame.render_widget(table, place); frame.render_widget(table, place);
Ok(()) Ok(())

View File

@ -1,14 +1,14 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -26,7 +26,7 @@ pub struct UnbondPopup {
stash_account_id: [u8; 32], stash_account_id: [u8; 32],
is_bonded: bool, is_bonded: bool,
amount: Input, amount: Input,
palette: StylePalette palette: StylePalette,
} }
impl Default for UnbondPopup { impl Default for UnbondPopup {
@ -58,8 +58,7 @@ impl UnbondPopup {
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::ValidatorLog));
Action::EventLog(message, level, ActionTarget::ValidatorLog));
} }
} }
@ -75,19 +74,20 @@ impl UnbondPopup {
Ok(value) => { Ok(value) => {
if self.is_bonded { if self.is_bonded {
let amount = (value * 1_000_000_000_000_000_000.0) as u128; let amount = (value * 1_000_000_000_000_000_000.0) as u128;
let _ = network_tx.send(Action::UnbondFrom( let _ = network_tx.send(Action::UnbondFrom(self.stash_secret_seed, amount));
self.stash_secret_seed, amount));
} else { } else {
self.log_event( self.log_event(
format!("current stash doesn't have bond yet"), format!("current stash doesn't have bond yet"),
ActionLevel::Warn); ActionLevel::Warn,
);
} }
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
} }
}, }
Err(err) => self.log_event( Err(err) => {
format!("invalid amount, error: {err}"), ActionLevel::Error), self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error)
}
} }
} }
} }
@ -137,11 +137,16 @@ impl Component for UnbondPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -154,7 +159,7 @@ impl Component for UnbondPopup {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -162,8 +167,9 @@ impl Component for UnbondPopup {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetIsBonded(is_bonded, account_id) if self.stash_account_id == account_id => Action::SetIsBonded(is_bonded, account_id) if self.stash_account_id == account_id => {
self.is_bonded = is_bonded, self.is_bonded = is_bonded
}
Action::SetStashSecret(secret_seed) => self.stash_secret_seed = secret_seed, Action::SetStashSecret(secret_seed) => self.stash_secret_seed = secret_seed,
Action::SetStashAccount(account_id) => self.stash_account_id = account_id, Action::SetStashAccount(account_id) => self.stash_account_id = account_id,
_ => {} _ => {}
@ -174,13 +180,14 @@ impl Component for UnbondPopup {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.amount.value()) let input = Paragraph::new(self.amount.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title(format!("Unbond amount"))); .title(format!("Unbond amount")),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
@ -189,8 +196,8 @@ impl Component for UnbondPopup {
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(input, area); frame.render_widget(input, area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
area.x + self.amount.cursor() as u16 + 1, area.x + self.amount.cursor() as u16 + 1,
area.y + 1 area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -1,14 +1,14 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -24,7 +24,7 @@ pub struct ValidatePopup {
network_tx: Option<Sender<Action>>, network_tx: Option<Sender<Action>>,
secret_seed: [u8; 32], secret_seed: [u8; 32],
amount: Input, amount: Input,
palette: StylePalette palette: StylePalette,
} }
impl Default for ValidatePopup { impl Default for ValidatePopup {
@ -54,8 +54,7 @@ impl ValidatePopup {
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::ValidatorLog));
Action::EventLog(message, level, ActionTarget::ValidatorLog));
} }
} }
@ -74,9 +73,10 @@ impl ValidatePopup {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
} }
}, }
Err(err) => self.log_event( Err(err) => {
format!("invalid amount, error: {err}"), ActionLevel::Error), self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error)
}
} }
} }
} }
@ -126,11 +126,16 @@ impl Component for ValidatePopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -143,7 +148,7 @@ impl Component for ValidatePopup {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -160,13 +165,14 @@ impl Component for ValidatePopup {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.amount.value()) let input = Paragraph::new(self.amount.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title(format!("Commission for nominators"))); .title(format!("Commission for nominators")),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
@ -175,8 +181,8 @@ impl Component for ValidatePopup {
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(input, area); frame.render_widget(input, area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
area.x + self.amount.cursor() as u16 + 1, area.x + self.amount.cursor() as u16 + 1,
area.y + 1 area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -1,14 +1,14 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -27,7 +27,7 @@ pub struct WithdrawPopup {
unlocking_is_empty: bool, unlocking_is_empty: bool,
existential_deposit: u128, existential_deposit: u128,
active_balance: u128, active_balance: u128,
palette: StylePalette palette: StylePalette,
} }
impl Default for WithdrawPopup { impl Default for WithdrawPopup {
@ -68,9 +68,10 @@ impl WithdrawPopup {
let spans_needed = if self.stash_should_be_killed() { let spans_needed = if self.stash_should_be_killed() {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::EventLog( let _ = action_tx.send(Action::EventLog(
"Current stash account will be killed during this transaction".to_string(), "Current stash account will be killed during this transaction".to_string(),
ActionLevel::Warn, ActionLevel::Warn,
ActionTarget::ValidatorLog)); ActionTarget::ValidatorLog,
));
} }
self.slashing_spans_length self.slashing_spans_length
} else { } else {
@ -106,11 +107,16 @@ impl Component for WithdrawPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -120,7 +126,7 @@ impl Component for WithdrawPopup {
match key.code { match key.code {
KeyCode::Enter => self.proceed(), KeyCode::Enter => self.proceed(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -128,15 +134,26 @@ impl Component for WithdrawPopup {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetExistentialDeposit(existential_deposit) => self.existential_deposit = existential_deposit, Action::SetExistentialDeposit(existential_deposit) => {
self.existential_deposit = existential_deposit
}
Action::SetStashAccount(account_id) => self.stash_account = account_id, Action::SetStashAccount(account_id) => self.stash_account = account_id,
Action::SetStashSecret(secret_seed) => self.secret_seed = secret_seed, Action::SetStashSecret(secret_seed) => self.secret_seed = secret_seed,
Action::SetSlashingSpansLength(length, account_id) if self.stash_account == account_id => Action::SetSlashingSpansLength(length, account_id)
self.slashing_spans_length = length as u32, if self.stash_account == account_id =>
Action::SetStakedAmountRatio(_, active_balance, account_id) if self.stash_account == account_id => {
self.active_balance = active_balance.unwrap_or_default(), self.slashing_spans_length = length as u32
Action::SetValidatorEraUnlocking(unlockings, account_id) if self.stash_account == account_id => }
self.unlocking_is_empty = unlockings.is_empty(), Action::SetStakedAmountRatio(_, active_balance, account_id)
if self.stash_account == account_id =>
{
self.active_balance = active_balance.unwrap_or_default()
}
Action::SetValidatorEraUnlocking(unlockings, account_id)
if self.stash_account == account_id =>
{
self.unlocking_is_empty = unlockings.is_empty()
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -150,13 +167,14 @@ impl Component for WithdrawPopup {
} else { } else {
" Do you want to withdraw all unbonded funds?" " Do you want to withdraw all unbonded funds?"
}; };
let input = Paragraph::new(text) let input = Paragraph::new(text).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Enter to proceed / Esc to close")); .title("Enter to proceed / Esc to close"),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);

View File

@ -3,23 +3,18 @@ use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::{Constraint, Margin}; use ratatui::layout::{Constraint, Margin};
use ratatui::style::{Modifier, Stylize}; use ratatui::style::{Modifier, Stylize};
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Text,
widgets::{ widgets::{
Block, Cell, Row, Table, TableState, Scrollbar, Block, Cell, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, TableState,
ScrollbarOrientation, ScrollbarState,
}, },
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::{ActionTarget, ActionLevel, UnlockChunk}; use crate::types::{ActionLevel, ActionTarget, UnlockChunk};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
pub struct Withdrawals { pub struct Withdrawals {
is_active: bool, is_active: bool,
@ -59,12 +54,13 @@ impl Withdrawals {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
if let Some(index) = self.table_state.selected() { if let Some(index) = self.table_state.selected() {
if index == 0 && self.unlockings[0].era < self.current_era { if index == 0 && self.unlockings[0].era < self.current_era {
let _ = action_tx.send(Action::WithdrawValidatorPopup); let _ = action_tx.send(Action::WithdrawValidatorPopup);
} else { } else {
let _ = action_tx.send(Action::EventLog( let _ = action_tx.send(Action::EventLog(
"Nothing to be witdrawn yet on the selected unlocking".to_string(), "Nothing to be witdrawn yet on the selected unlocking".to_string(),
ActionLevel::Info, ActionLevel::Info,
ActionTarget::ValidatorLog)); ActionTarget::ValidatorLog,
));
} }
} }
} }
@ -85,7 +81,7 @@ impl Withdrawals {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
@ -108,8 +104,8 @@ impl Withdrawals {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i); self.scroll_state = self.scroll_state.position(i);
@ -128,8 +124,7 @@ impl Withdrawals {
} }
} }
self.unlockings = updated_unlockings; self.unlockings = updated_unlockings;
self.scroll_state = self.scroll_state self.scroll_state = self.scroll_state.content_length(self.unlockings.len());
.content_length(self.unlockings.len());
} }
fn prepare_u128(&self, value: u128) -> String { fn prepare_u128(&self, value: u128) -> String {
@ -168,14 +163,22 @@ impl Component for Withdrawals {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) { if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
@ -184,8 +187,11 @@ impl Component for Withdrawals {
match action { match action {
Action::SetStashAccount(account_id) => self.stash_account = account_id, Action::SetStashAccount(account_id) => self.stash_account = account_id,
Action::SetActiveEra(era_info) => self.current_era = era_info.index, Action::SetActiveEra(era_info) => self.current_era = era_info.index,
Action::SetValidatorEraUnlocking(unlockings, account_id) if self.stash_account == account_id => Action::SetValidatorEraUnlocking(unlockings, account_id)
self.add_new_unlocking(unlockings), if self.stash_account == account_id =>
{
self.add_new_unlocking(unlockings)
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -199,7 +205,7 @@ impl Component for Withdrawals {
KeyCode::Char('g') => self.first_row(), KeyCode::Char('g') => self.first_row(),
KeyCode::Char('G') => self.last_row(), KeyCode::Char('G') => self.last_row(),
KeyCode::Enter => self.try_open_popup(), KeyCode::Enter => self.try_open_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -209,35 +215,31 @@ impl Component for Withdrawals {
let [_, _, place] = super::validator_statistics_layout(area); let [_, _, place] = super::validator_statistics_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 table = Table::new( let table = Table::new(
self.unlockings self.unlockings.iter().map(|unlock| {
.iter() let mut est_era_text =
.map(|unlock| { Text::from(self.estimate_time(unlock.era)).alignment(Alignment::Center);
let mut est_era_text = Text::from(self.estimate_time(unlock.era)).alignment(Alignment::Center); let mut value_text =
let mut value_text = Text::from(self.prepare_u128(unlock.value)).alignment(Alignment::Right); Text::from(self.prepare_u128(unlock.value)).alignment(Alignment::Right);
if unlock.era > self.current_era { if unlock.era > self.current_era {
est_era_text = est_era_text.add_modifier(Modifier::CROSSED_OUT); est_era_text = est_era_text.add_modifier(Modifier::CROSSED_OUT);
value_text = value_text.add_modifier(Modifier::CROSSED_OUT); value_text = value_text.add_modifier(Modifier::CROSSED_OUT);
} }
Row::new(vec![ Row::new(vec![Cell::from(est_era_text), Cell::from(value_text)])
Cell::from(est_era_text), }),
Cell::from(value_text), [Constraint::Length(7), Constraint::Min(0)],
])
}),
[
Constraint::Length(7),
Constraint::Min(0),
],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Withdrawals")); .title_style(self.palette.create_title_style(false))
.title("Withdrawals"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -248,7 +250,10 @@ impl Component for Withdrawals {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -8,12 +8,7 @@ use ratatui::{
use subxt::utils::H256; use subxt::utils::H256;
use super::Component; use super::Component;
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette, widgets::OghamCenter};
config::Config,
action::Action,
palette::StylePalette,
widgets::OghamCenter,
};
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Version { pub struct Version {
@ -35,8 +30,10 @@ impl Version {
impl Component for Version { impl Component for Version {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Menu) { if let Some(style) = config.styles.get(&crate::app::Mode::Menu) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette
.with_normal_border_style(style.get("normal_border_style").copied());
} }
Ok(()) Ok(())
} }
@ -57,14 +54,25 @@ impl Component for Version {
let text_style = self.palette.create_basic_style(false); let text_style = self.palette.create_basic_style(false);
let (border_style, border_type) = self.palette.create_border_style(false); let (border_style, border_type) = self.palette.create_border_style(false);
let text = vec![ let text = vec![
Line::styled(self.chain_name.clone().unwrap_or(OghamCenter::default().to_string()), text_style), Line::styled(
Line::styled(self.node_version.clone().unwrap_or(OghamCenter::default().to_string()), text_style), self.chain_name
.clone()
.unwrap_or(OghamCenter::default().to_string()),
text_style,
),
Line::styled(
self.node_version
.clone()
.unwrap_or(OghamCenter::default().to_string()),
text_style,
),
Line::styled(self.prepared_genesis_hash(), text_style), Line::styled(self.prepared_genesis_hash(), text_style),
]; ];
let paragraph = Paragraph::new(text) let paragraph = Paragraph::new(text)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.border_type(border_type),
) )
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });

View File

@ -1,20 +1,15 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Cell, Clear, Row, Table},
text::Text, text::Text,
widgets::{Block, Cell, Clear, Row, Table},
Frame, Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette, widgets::DotSpinner};
action::Action,
config::Config,
palette::StylePalette,
widgets::DotSpinner,
};
#[derive(Debug)] #[derive(Debug)]
pub struct AccountDetails { pub struct AccountDetails {
@ -66,8 +61,8 @@ impl AccountDetails {
let value = value as f64 / 10f64.powi(18); let value = value as f64 / 10f64.powi(18);
let after = Self::DECIMALS; let after = Self::DECIMALS;
format!("{:.after$}{}", value, Self::TICKER) format!("{:.after$}{}", value, Self::TICKER)
}, }
None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER) None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER),
} }
} }
} }
@ -99,11 +94,16 @@ impl Component for AccountDetails {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -119,11 +119,12 @@ impl Component for AccountDetails {
self.reserved_balance = Some(account_info.reserved); self.reserved_balance = Some(account_info.reserved);
self.nonce = Some(account_info.nonce); self.nonce = Some(account_info.nonce);
let transferable = account_info.free let transferable = account_info
.free
.saturating_sub(account_info.reserved) .saturating_sub(account_info.reserved)
.saturating_sub(account_info.frozen); .saturating_sub(account_info.frozen);
self.transferable_balance = Some(transferable); self.transferable_balance = Some(transferable);
}, }
None => { None => {
self.transferable_balance = None; self.transferable_balance = None;
self.locked_balance = None; self.locked_balance = None;
@ -142,7 +143,7 @@ impl Component for AccountDetails {
if self.is_active && key.kind == KeyEventKind::Press { if self.is_active && key.kind == KeyEventKind::Press {
match key.code { match key.code {
KeyCode::Esc | KeyCode::Enter => self.close_popup(), KeyCode::Esc | KeyCode::Enter => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -155,39 +156,54 @@ impl Component for AccountDetails {
[ [
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("nonce: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("nonce: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.nonce Cell::from(
.map(|n| n.to_string()) Text::from(
.unwrap_or(DotSpinner::default().to_string()) self.nonce
).alignment(Alignment::Right)), .map(|n| n.to_string())
.unwrap_or(DotSpinner::default().to_string()),
)
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("total: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("total: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.total_balance)).alignment(Alignment::Right)) Cell::from(
Text::from(self.prepare_u128(self.total_balance))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("free: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("free: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.transferable_balance)).alignment(Alignment::Right)) Cell::from(
Text::from(self.prepare_u128(self.transferable_balance))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("locked: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("locked: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.locked_balance)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.locked_balance))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("reserved: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("reserved: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.reserved_balance)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.reserved_balance))
.alignment(Alignment::Right),
),
]), ]),
], ],
[ [Constraint::Max(10), Constraint::Min(14)],
Constraint::Max(10),
Constraint::Min(14),
]
) )
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title(format!("Details for {}", &self.name))); .title_style(self.palette.create_title_style(false))
.title(format!("Details for {}", &self.name)),
);
let v = Layout::vertical([Constraint::Max(7)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(7)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);

View File

@ -1,36 +1,32 @@
use std::path::PathBuf;
use std::fs::File; use std::fs::File;
use std::io::{Write, BufRead, BufReader}; use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent}; use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::{Constraint, Margin}; use ratatui::layout::{Constraint, Margin};
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Text,
widgets::{ widgets::{
Block, Cell, Row, Table, TableState, Scrollbar, Padding, Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table,
ScrollbarOrientation, ScrollbarState, TableState,
}, },
Frame Frame,
}; };
use std::sync::mpsc::Sender;
use subxt::ext::sp_core::{ use subxt::ext::sp_core::{
Pair as PairT, crypto::{AccountId32, Ss58AddressFormat, Ss58Codec},
sr25519::Pair, sr25519::Pair,
crypto::{Ss58Codec, Ss58AddressFormat, AccountId32}, Pair as PairT,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::{ActionLevel, ActionTarget, SystemAccount}; use crate::types::{ActionLevel, ActionTarget, SystemAccount};
use crate::widgets::DotSpinner; use crate::widgets::DotSpinner;
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
struct AccountInfo { struct AccountInfo {
name: String, name: String,
@ -79,9 +75,7 @@ impl Accounts {
if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
let used_seed = self.wallet_keys[index].seed.clone(); let used_seed = self.wallet_keys[index].seed.clone();
let account_id = self.wallet_keys[index].account_id; let account_id = self.wallet_keys[index].account_id;
let used_nonce = self.balances let used_nonce = self.balances.get(&account_id).map(|info| info.nonce);
.get(&account_id)
.map(|info| info.nonce);
let _ = network_tx.send(Action::SetSender(used_seed, used_nonce)); let _ = network_tx.send(Action::SetSender(used_seed, used_nonce));
} }
} }
@ -90,22 +84,17 @@ impl Accounts {
let used_seed = self.wallet_keys[index].seed.clone(); let used_seed = self.wallet_keys[index].seed.clone();
let account_id = self.wallet_keys[index].account_id; let account_id = self.wallet_keys[index].account_id;
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::UsedAccount( let _ = action_tx.send(Action::UsedAccount(account_id, used_seed.clone()));
account_id,
used_seed.clone()));
} }
if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::GetNominatorsByAccount( let _ = network_tx.send(Action::GetNominatorsByAccount(account_id, false));
account_id,
false));
} }
self.set_sender_nonce(index); self.set_sender_nonce(index);
} }
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::WalletLog));
Action::EventLog(message, level, ActionTarget::WalletLog));
} }
} }
@ -119,16 +108,15 @@ impl Accounts {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let account_id = self.wallet_keys[index].account_id; let account_id = self.wallet_keys[index].account_id;
let _ = action_tx.send(Action::BalanceSetActive( let _ = action_tx.send(Action::BalanceSetActive(
self.balances.get(&account_id).cloned())); self.balances.get(&account_id).cloned(),
));
} }
} }
fn update_account_name(&mut self) { fn update_account_name(&mut self) {
if let Some(index) = self.table_state.selected() { if let Some(index) = self.table_state.selected() {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::RenameAccount( let _ = action_tx.send(Action::RenameAccount(self.wallet_keys[index].name.clone()));
self.wallet_keys[index].name.clone()
));
} }
} }
} }
@ -141,19 +129,20 @@ impl Accounts {
.arg("clipboard") .arg("clipboard")
.stdin(std::process::Stdio::piped()) .stdin(std::process::Stdio::piped())
.spawn() .spawn()
.and_then(|child| Ok(child .and_then(|child| {
Ok(child
.stdin .stdin
.and_then(|mut cs| cs .and_then(|mut cs| cs.write_all(address.as_bytes()).ok()))
.write_all(address.as_bytes()) }) {
.ok())
))
{
Ok(Some(())) => self.log_event( Ok(Some(())) => self.log_event(
format!("address {} copied to clipboard", &address), format!("address {} copied to clipboard", &address),
ActionLevel::Warn), ActionLevel::Warn,
),
_ => self.log_event( _ => self.log_event(
"command `xclip` not found, consider installing `xclip` on your machine".to_string(), "command `xclip` not found, consider installing `xclip` on your machine"
ActionLevel::Error), .to_string(),
ActionLevel::Error,
),
} }
} }
} }
@ -166,9 +155,12 @@ impl Accounts {
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)); .to_ss58check_with_version(Ss58AddressFormat::custom(1996));
self.log_event( self.log_event(
format!("new wallet '{}' with public address {} created", format!(
&name, &address), "new wallet '{}' with public address {} created",
ActionLevel::Info); &name, &address
),
ActionLevel::Info,
);
self.send_balance_request(account_id, false); self.send_balance_request(account_id, false);
self.wallet_keys.push(AccountInfo { self.wallet_keys.push(AccountInfo {
@ -185,16 +177,16 @@ impl Accounts {
if let Some(index) = self.table_state.selected() { if let Some(index) = self.table_state.selected() {
let old_name = self.wallet_keys[index].name.clone(); let old_name = self.wallet_keys[index].name.clone();
self.log_event(format!("wallet '{}' renamed to {}", self.log_event(
&new_name, &old_name), format!("wallet '{}' renamed to {}", &new_name, &old_name),
ActionLevel::Info); ActionLevel::Info,
);
self.wallet_keys[index].name = new_name; self.wallet_keys[index].name = new_name;
self.save_to_file(); self.save_to_file();
} }
} }
fn swap_up(&mut self) { fn swap_up(&mut self) {
if let Some(src_index) = self.table_state.selected() { if let Some(src_index) = self.table_state.selected() {
let dst_index = src_index.saturating_sub(1); let dst_index = src_index.saturating_sub(1);
if src_index > dst_index { if src_index > dst_index {
@ -224,9 +216,13 @@ impl Accounts {
self.previous_row(); self.previous_row();
self.save_to_file(); self.save_to_file();
self.log_event(format!("wallet `{}` with public address {} removed", self.log_event(
&wallet.name, &wallet.address), format!(
ActionLevel::Warn); "wallet `{}` with public address {} removed",
&wallet.name, &wallet.address
),
ActionLevel::Warn,
);
} }
} }
} }
@ -262,16 +258,17 @@ impl Accounts {
self.send_balance_request(account_id, false); self.send_balance_request(account_id, false);
self.wallet_keys.push(AccountInfo { self.wallet_keys.push(AccountInfo {
name: wallet_name.to_string(), name: wallet_name.to_string(),
account_id, account_id,
address, address,
seed: wallet_key.to_string(), seed: wallet_key.to_string(),
}); });
} }
self.log_event(format!("read {} wallets from disk", self.log_event(
self.wallet_keys.len()), format!("read {} wallets from disk", self.wallet_keys.len()),
ActionLevel::Info); ActionLevel::Info,
}, );
}
Err(_) => { Err(_) => {
let (pair, seed) = match std::fs::read_to_string("/etc/ghost/wallet-key") { let (pair, seed) = match std::fs::read_to_string("/etc/ghost/wallet-key") {
Ok(content) => { Ok(content) => {
@ -285,7 +282,8 @@ impl Accounts {
self.log_event( self.log_event(
"wallet read from the `/etc/ghost/wallet-key`".to_string(), "wallet read from the `/etc/ghost/wallet-key`".to_string(),
ActionLevel::Warn); ActionLevel::Warn,
);
let pair = Pair::from_seed(&seed); let pair = Pair::from_seed(&seed);
(pair, seed) (pair, seed)
@ -293,7 +291,8 @@ impl Accounts {
Err(_) => { Err(_) => {
self.log_event( self.log_event(
"no wallets found on disk, new wallet created".to_string(), "no wallets found on disk, new wallet created".to_string(),
ActionLevel::Warn); ActionLevel::Warn,
);
let (pair, seed) = Pair::generate(); let (pair, seed) = Pair::generate();
(pair, seed) (pair, seed)
} }
@ -309,10 +308,10 @@ impl Accounts {
self.send_balance_request(account_id, false); self.send_balance_request(account_id, false);
self.wallet_keys.push(AccountInfo { self.wallet_keys.push(AccountInfo {
name: "ghostie".to_string(), name: "ghostie".to_string(),
address, address,
account_id, account_id,
seed: secret_seed, seed: secret_seed,
}); });
} }
}; };
@ -346,11 +345,16 @@ impl Accounts {
if self.wallet_keys.iter().any(|key| key.seed == secret_seed) { if self.wallet_keys.iter().any(|key| key.seed == secret_seed) {
self.log_event( self.log_event(
format!("{} from `{}` already loaded", name_for_key, path_to_key), format!("{} from `{}` already loaded", name_for_key, path_to_key),
ActionLevel::Warn); ActionLevel::Warn,
);
} else { } else {
self.log_event( self.log_event(
format!("{} from `{}` loaded to ghost-eye", name_for_key, path_to_key), format!(
ActionLevel::Warn); "{} from `{}` loaded to ghost-eye",
name_for_key, path_to_key
),
ActionLevel::Warn,
);
self.send_balance_request(account_id, false); self.send_balance_request(account_id, false);
self.wallet_keys.push(AccountInfo { self.wallet_keys.push(AccountInfo {
@ -361,12 +365,12 @@ impl Accounts {
}); });
self.save_to_file(); self.save_to_file();
} }
} }
Err(_) => { Err(_) => {
self.log_event( self.log_event(
format!("nothing found inside `{}`", path_to_key), format!("nothing found inside `{}`", path_to_key),
ActionLevel::Warn); ActionLevel::Warn,
);
} }
}; };
} }
@ -374,12 +378,14 @@ impl Accounts {
fn push_to_address_book(&mut self) { fn push_to_address_book(&mut self) {
if let Some(index) = self.table_state.selected() { if let Some(index) = self.table_state.selected() {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let wallet_key = self.wallet_keys let wallet_key = self
.wallet_keys
.get(index) .get(index)
.expect("wallet key index should be in range; qed"); .expect("wallet key index should be in range; qed");
let _ = action_tx.send(Action::NewAddressBookRecord( let _ = action_tx.send(Action::NewAddressBookRecord(
wallet_key.name.clone(), wallet_key.name.clone(),
wallet_key.address.clone())); wallet_key.address.clone(),
));
} }
} }
} }
@ -401,7 +407,7 @@ impl Accounts {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
@ -428,8 +434,8 @@ impl Accounts {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i); self.scroll_state = self.scroll_state.position(i);
@ -444,7 +450,7 @@ impl Accounts {
let after = Self::DECIMALS; let after = Self::DECIMALS;
format!("{:.after$}{}", value, Self::TICKER) format!("{:.after$}{}", value, Self::TICKER)
} }
None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER) None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER),
} }
} }
} }
@ -471,14 +477,22 @@ impl Component for Accounts {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
let mut wallet_keys_file = config.config.data_dir; let mut wallet_keys_file = config.config.data_dir;
@ -493,7 +507,11 @@ impl Component for Accounts {
Action::NewAccount(name) => self.create_new_account(name), Action::NewAccount(name) => self.create_new_account(name),
Action::UpdateAccountName(new_name) => self.rename_account(new_name), Action::UpdateAccountName(new_name) => self.rename_account(new_name),
Action::BalanceResponse(account_id, maybe_balance) => { Action::BalanceResponse(account_id, maybe_balance) => {
if self.wallet_keys.iter().any(|wallet| wallet.account_id == account_id) { if self
.wallet_keys
.iter()
.any(|wallet| wallet.account_id == account_id)
{
let _ = match maybe_balance { let _ = match maybe_balance {
Some(balance) => self.balances.insert(account_id, balance), Some(balance) => self.balances.insert(account_id, balance),
None => self.balances.remove(&account_id), None => self.balances.remove(&account_id),
@ -505,7 +523,7 @@ impl Component for Accounts {
} }
} }
} }
}, }
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -525,7 +543,7 @@ impl Component for Accounts {
KeyCode::Char('Y') => self.copy_to_clipboard(), KeyCode::Char('Y') => self.copy_to_clipboard(),
KeyCode::Char('L') => self.load_initial_keys(), KeyCode::Char('L') => self.load_initial_keys(),
KeyCode::Char('P') => self.push_to_address_book(), KeyCode::Char('P') => self.push_to_address_book(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -536,33 +554,27 @@ impl Component for Accounts {
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 table = Table::new( let table = Table::new(
self.wallet_keys self.wallet_keys.iter().map(|info| {
.iter() let balance = self.balances.get(&info.account_id).map(|b| b.free);
.map(|info| { Row::new(vec![
let balance = self.balances Cell::from(Text::from(info.name.clone()).alignment(Alignment::Left)),
.get(&info.account_id) Cell::from(Text::from(info.address.clone()).alignment(Alignment::Center)),
.map(|b| b.free); Cell::from(Text::from(self.prepare_u128(balance)).alignment(Alignment::Right)),
Row::new(vec![ ])
Cell::from(Text::from(info.name.clone()).alignment(Alignment::Left)), }),
Cell::from(Text::from(info.address.clone()).alignment(Alignment::Center)), [Constraint::Min(5), Constraint::Max(51), Constraint::Min(11)],
Cell::from(Text::from(self.prepare_u128(balance)).alignment(Alignment::Right)),
])
}),
[
Constraint::Min(5),
Constraint::Max(51),
Constraint::Min(11),
],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.padding(Padding::right(2)) .border_type(border_type)
.title_alignment(Alignment::Right) .padding(Padding::right(2))
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("My Accounts")); .title_style(self.palette.create_title_style(false))
.title("My Accounts"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -573,7 +585,10 @@ impl Component for Accounts {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,18 +1,18 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
widgets::{Input, InputRequest},
action::Action, action::Action,
config::Config, config::Config,
palette::StylePalette, palette::StylePalette,
widgets::{Input, InputRequest},
}; };
#[derive(Debug)] #[derive(Debug)]
@ -20,7 +20,7 @@ pub struct AddAccount {
is_active: bool, is_active: bool,
action_tx: Option<UnboundedSender<Action>>, action_tx: Option<UnboundedSender<Action>>,
name: Input, name: Input,
palette: StylePalette palette: StylePalette,
} }
impl Default for AddAccount { impl Default for AddAccount {
@ -48,8 +48,7 @@ impl AddAccount {
fn submit_message(&mut self) { fn submit_message(&mut self) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::NewAccount( let _ = action_tx.send(Action::NewAccount(self.name.value().to_string()));
self.name.value().to_string()));
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
} }
} }
@ -78,7 +77,7 @@ impl PartialComponent for AddAccount {
_ => { _ => {
self.is_active = false; self.is_active = false;
self.name = Input::new(String::new()); self.name = Input::new(String::new());
}, }
}; };
} }
} }
@ -91,11 +90,16 @@ impl Component for AddAccount {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -109,7 +113,7 @@ impl Component for AddAccount {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -121,13 +125,14 @@ impl Component for AddAccount {
let area = Rect::new(size.width / 2, size.height / 2, 50, 3); let area = Rect::new(size.width / 2, size.height / 2, 50, 3);
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.name.value()) let input = Paragraph::new(self.name.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("New wallet name")); .title("New wallet name"),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
@ -136,8 +141,8 @@ impl Component for AddAccount {
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(input, area); frame.render_widget(input, area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
area.x + self.name.cursor() as u16 + 1, area.x + self.name.cursor() as u16 + 1,
area.y + 1 area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -1,18 +1,18 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
widgets::{Input, InputRequest},
action::Action, action::Action,
config::Config, config::Config,
palette::StylePalette, palette::StylePalette,
widgets::{Input, InputRequest},
}; };
#[derive(Debug)] #[derive(Debug)]
@ -28,7 +28,7 @@ pub struct AddAddressBookRecord {
action_tx: Option<UnboundedSender<Action>>, action_tx: Option<UnboundedSender<Action>>,
name: Input, name: Input,
address: Input, address: Input,
palette: StylePalette palette: StylePalette,
} }
impl Default for AddAddressBookRecord { impl Default for AddAddressBookRecord {
@ -59,11 +59,14 @@ impl AddAddressBookRecord {
fn submit_message(&mut self) { fn submit_message(&mut self) {
match self.name_or_address { match self.name_or_address {
NameOrAddress::Name => self.name_or_address = NameOrAddress::Address, NameOrAddress::Name => self.name_or_address = NameOrAddress::Address,
NameOrAddress::Address => if let Some(action_tx) = &self.action_tx { NameOrAddress::Address => {
let _ = action_tx.send(Action::NewAddressBookRecord( if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::NewAddressBookRecord(
self.name.value().to_string(), self.name.value().to_string(),
self.address.value().to_string())); self.address.value().to_string(),
let _ = action_tx.send(Action::ClosePopup); ));
let _ = action_tx.send(Action::ClosePopup);
}
} }
} }
} }
@ -72,7 +75,7 @@ impl AddAddressBookRecord {
match self.name_or_address { match self.name_or_address {
NameOrAddress::Name => { NameOrAddress::Name => {
let _ = self.name.handle(InputRequest::InsertChar(new_char)); let _ = self.name.handle(InputRequest::InsertChar(new_char));
}, }
NameOrAddress::Address => { NameOrAddress::Address => {
let _ = self.address.handle(InputRequest::InsertChar(new_char)); let _ = self.address.handle(InputRequest::InsertChar(new_char));
} }
@ -83,7 +86,7 @@ impl AddAddressBookRecord {
match self.name_or_address { match self.name_or_address {
NameOrAddress::Name => { NameOrAddress::Name => {
let _ = self.name.handle(InputRequest::DeletePrevChar); let _ = self.name.handle(InputRequest::DeletePrevChar);
}, }
NameOrAddress::Address => { NameOrAddress::Address => {
let _ = self.address.handle(InputRequest::DeletePrevChar); let _ = self.address.handle(InputRequest::DeletePrevChar);
} }
@ -94,7 +97,7 @@ impl AddAddressBookRecord {
match self.name_or_address { match self.name_or_address {
NameOrAddress::Name => { NameOrAddress::Name => {
let _ = self.name.handle(InputRequest::GoToNextChar); let _ = self.name.handle(InputRequest::GoToNextChar);
}, }
NameOrAddress::Address => { NameOrAddress::Address => {
let _ = self.address.handle(InputRequest::GoToNextChar); let _ = self.address.handle(InputRequest::GoToNextChar);
} }
@ -105,7 +108,7 @@ impl AddAddressBookRecord {
match self.name_or_address { match self.name_or_address {
NameOrAddress::Name => { NameOrAddress::Name => {
let _ = self.name.handle(InputRequest::GoToPrevChar); let _ = self.name.handle(InputRequest::GoToPrevChar);
}, }
NameOrAddress::Address => { NameOrAddress::Address => {
let _ = self.address.handle(InputRequest::GoToPrevChar); let _ = self.address.handle(InputRequest::GoToPrevChar);
} }
@ -124,7 +127,7 @@ impl PartialComponent for AddAddressBookRecord {
self.address = Input::new(String::new()); self.address = Input::new(String::new());
self.name_or_address = NameOrAddress::Name; self.name_or_address = NameOrAddress::Name;
} }
}, }
}; };
} }
} }
@ -137,11 +140,16 @@ impl Component for AddAddressBookRecord {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -157,7 +165,7 @@ impl Component for AddAddressBookRecord {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -170,21 +178,23 @@ impl Component for AddAddressBookRecord {
let address_area = Rect::new(size.width / 2, size.height / 2 + 3, 51, 3); let address_area = Rect::new(size.width / 2, size.height / 2 + 3, 51, 3);
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input_name = Paragraph::new(self.name.value()) let input_name = Paragraph::new(self.name.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("New name in Address Book")); .title("New name in Address Book"),
);
let input_address = Paragraph::new(self.address.value()) let input_address = Paragraph::new(self.address.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Address for new name")); .title("Address for new name"),
);
let v = Layout::vertical([Constraint::Length(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Length(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Length(51)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Length(51)]).flex(Flex::Center);
@ -202,14 +212,14 @@ impl Component for AddAddressBookRecord {
match self.name_or_address { match self.name_or_address {
NameOrAddress::Name => { NameOrAddress::Name => {
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
name_area.x + self.name.cursor() as u16 + 1, name_area.x + self.name.cursor() as u16 + 1,
name_area.y + 1 name_area.y + 1,
)); ));
}, }
NameOrAddress::Address => { NameOrAddress::Address => {
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
address_area.x + self.address.cursor() as u16 + 1, address_area.x + self.address.cursor() as u16 + 1,
address_area.y + 1 address_area.y + 1,
)); ));
} }
} }

View File

@ -1,6 +1,6 @@
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::io::{Write, BufRead, BufReader};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent}; use crossterm::event::{KeyCode, KeyEvent};
@ -9,23 +9,17 @@ use ratatui::text::Text;
use ratatui::widgets::{Cell, Padding, Scrollbar, ScrollbarOrientation}; use ratatui::widgets::{Cell, Padding, Scrollbar, ScrollbarOrientation};
use ratatui::{ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
widgets::{Block, ScrollbarState, Row, Table, TableState}, widgets::{Block, Row, ScrollbarState, Table, TableState},
Frame Frame,
}; };
use subxt::ext::sp_core::crypto::{
ByteArray, Ss58Codec, Ss58AddressFormat, AccountId32,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use subxt::ext::sp_core::crypto::{AccountId32, ByteArray, Ss58AddressFormat, Ss58Codec};
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::{ActionLevel, ActionTarget, SystemAccount}; use crate::types::{ActionLevel, ActionTarget, SystemAccount};
use crate::widgets::DotSpinner; use crate::widgets::DotSpinner;
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
struct BookRecord { struct BookRecord {
name: String, name: String,
@ -80,8 +74,8 @@ impl AddressBook {
fn send_transfer_to(&mut self) { fn send_transfer_to(&mut self) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
if let Some(index) = self.table_state.selected() { if let Some(index) = self.table_state.selected() {
let _ = action_tx.send(Action::TransferTo( let _ =
self.address_book[index].address.clone())); action_tx.send(Action::TransferTo(self.address_book[index].address.clone()));
} }
} }
} }
@ -94,8 +88,7 @@ impl AddressBook {
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::WalletLog));
Action::EventLog(message, level, ActionTarget::WalletLog));
} }
} }
@ -129,47 +122,77 @@ impl AddressBook {
} }
self.log_event( self.log_event(
format!("read {} records from address book", self.address_book.len()), format!("read {} records from address book", self.address_book.len()),
ActionLevel::Info) ActionLevel::Info,
}, )
}
Err(_) => { Err(_) => {
let chad_boyz = vec![ let chad_boyz = vec![
("Pierre", "328d3b7c3046ef7700937d99fb2e98ce2591682c2b5dcf3f562e4da157650237"), (
("Ghost_7", "3666e4e19f87bb8680495f31864ce1f1c69d4178002cc01911aef2cc7313f203"), "Pierre",
("Neptune1", "ac871e8bab00dd56ba3a1c0bd289357203dcaf10010b0b04ad7472870cd22a3c"), "328d3b7c3046ef7700937d99fb2e98ce2591682c2b5dcf3f562e4da157650237",
("Neptune2", "425ccd7bda4f5c76788ba23bc0381d7a2e496179c93301208c57501c80a4232a"), ),
("Doctor K", "927a98dcf8f721103005f168476c24b91d7d10d580f457006a908e10e62c7729"), (
("Starman", "ac9e227e30a63ce6eeb55cfbb1fb832aa7e1d3fad2bcb3f663de4a91d744fd50"), "Ghost_7",
("Kitsune1", "46c78fcacffd80abc9cca4917ef8369a37e21a1691ca11e7a3b53f80be745313"), "3666e4e19f87bb8680495f31864ce1f1c69d4178002cc01911aef2cc7313f203",
("Scientio", "fa5e5a295ec74c3dda81118d9240db1552b28f831838465ae0712e97e78a6728"), ),
("Kitsune2", "4078ddb1ba1388f768fe6aa40ba9124a72692ecbcc83dc088fa86c735e4dc128"), (
("Proxmio", "5e1456904c40192cd3a18183df7dffea90d97739830a902cabb702ecdae4f649"), "Neptune1",
"ac871e8bab00dd56ba3a1c0bd289357203dcaf10010b0b04ad7472870cd22a3c",
),
(
"Neptune2",
"425ccd7bda4f5c76788ba23bc0381d7a2e496179c93301208c57501c80a4232a",
),
(
"Doctor K",
"927a98dcf8f721103005f168476c24b91d7d10d580f457006a908e10e62c7729",
),
(
"Starman",
"ac9e227e30a63ce6eeb55cfbb1fb832aa7e1d3fad2bcb3f663de4a91d744fd50",
),
(
"Kitsune1",
"46c78fcacffd80abc9cca4917ef8369a37e21a1691ca11e7a3b53f80be745313",
),
(
"Scientio",
"fa5e5a295ec74c3dda81118d9240db1552b28f831838465ae0712e97e78a6728",
),
(
"Kitsune2",
"4078ddb1ba1388f768fe6aa40ba9124a72692ecbcc83dc088fa86c735e4dc128",
),
(
"Proxmio",
"5e1456904c40192cd3a18183df7dffea90d97739830a902cabb702ecdae4f649",
),
]; ];
let mut new_file = File::create(file_path)?; let mut new_file = File::create(file_path)?;
chad_boyz chad_boyz.iter().for_each(|chad_info| {
.iter() writeln!(new_file, "{}:0x{}", chad_info.0, chad_info.1)
.for_each(|chad_info| { .expect("should write to address book; qed");
writeln!(new_file, "{}:0x{}", chad_info.0, chad_info.1) let chad_account_id: [u8; 32] = hex::decode(&chad_info.1[..])
.expect("should write to address book; qed"); .expect("stored seed is valid hex string; qed")
let chad_account_id: [u8; 32] = hex::decode(&chad_info.1[..]) .as_slice()
.expect("stored seed is valid hex string; qed") .try_into()
.as_slice() .expect("stored seed is valid length; qed");
.try_into() let address = AccountId32::from(chad_account_id)
.expect("stored seed is valid length; qed"); .to_ss58check_with_version(Ss58AddressFormat::custom(1996));
let address = AccountId32::from(chad_account_id)
.to_ss58check_with_version(Ss58AddressFormat::custom(1996));
self.address_book.push(BookRecord { self.address_book.push(BookRecord {
name: chad_info.0.to_string(), name: chad_info.0.to_string(),
address, address,
account_id: chad_account_id, account_id: chad_account_id,
seed: chad_info.1.to_string(), seed: chad_info.1.to_string(),
});
self.send_balance_request(chad_account_id, false);
}); });
self.send_balance_request(chad_account_id, false);
});
self.log_event( self.log_event(
format!("address book is empty, filling it with giga chad boyz as default"), format!("address book is empty, filling it with giga chad boyz as default"),
ActionLevel::Warn) ActionLevel::Warn,
)
} }
}; };
self.scroll_state = self.scroll_state.content_length(self.address_book.len()); self.scroll_state = self.scroll_state.content_length(self.address_book.len());
@ -181,7 +204,8 @@ impl AddressBook {
let old_name = self.address_book[index].name.clone(); let old_name = self.address_book[index].name.clone();
self.log_event( self.log_event(
format!("record renamed from {} to {}", &old_name, &new_name), format!("record renamed from {} to {}", &old_name, &new_name),
ActionLevel::Info); ActionLevel::Info,
);
self.address_book[index].name = new_name; self.address_book[index].name = new_name;
self.save_to_file(); self.save_to_file();
} }
@ -192,21 +216,34 @@ impl AddressBook {
Ok((account_id, format)) => { Ok((account_id, format)) => {
if format != Ss58AddressFormat::custom(1996) { if format != Ss58AddressFormat::custom(1996) {
self.log_event( self.log_event(
format!("provided public address for {} is not part of Casper/Ghost ecosystem", &address), format!(
ActionLevel::Error); "provided public address for {} is not part of Casper/Ghost ecosystem",
&address
),
ActionLevel::Error,
);
} }
match self.address_book.iter().position(|record| record.address == address) { match self
.address_book
.iter()
.position(|record| record.address == address)
{
Some(index) => { Some(index) => {
let record = self.address_book let record = self
.address_book
.get(index) .get(index)
.expect("record should be in range of address book; qed"); .expect("record should be in range of address book; qed");
self.log_event( self.log_event(
format!("record with name `{}` already stores address {}", &record.name, &record.address), format!(
ActionLevel::Warn); "record with name `{}` already stores address {}",
&record.name, &record.address
),
ActionLevel::Warn,
);
self.table_state.select(Some(index)); self.table_state.select(Some(index));
self.scroll_state = self.scroll_state.position(index); self.scroll_state = self.scroll_state.position(index);
}, }
None => { None => {
let seed_vec = account_id.to_raw_vec(); let seed_vec = account_id.to_raw_vec();
let mut account_id = [0u8; 32]; let mut account_id = [0u8; 32];
@ -214,8 +251,12 @@ impl AddressBook {
let seed_str = hex::encode(seed_vec); let seed_str = hex::encode(seed_vec);
self.log_event( self.log_event(
format!("account {} with address {} added to address book", &name, &address), format!(
ActionLevel::Info); "account {} with address {} added to address book",
&name, &address
),
ActionLevel::Info,
);
self.address_book.push(BookRecord { self.address_book.push(BookRecord {
name, name,
@ -224,16 +265,18 @@ impl AddressBook {
seed: seed_str, seed: seed_str,
}); });
self.scroll_state = self.scroll_state.content_length(self.address_book.len()); self.scroll_state =
self.scroll_state.content_length(self.address_book.len());
self.save_to_file(); self.save_to_file();
self.send_balance_request(account_id, false); self.send_balance_request(account_id, false);
self.last_row(); self.last_row();
}, }
} }
}, }
Err(_) => self.log_event( Err(_) => self.log_event(
format!("provided account address {} is invalid", &address), format!("provided account address {} is invalid", &address),
ActionLevel::Error), ActionLevel::Error,
),
} }
} }
@ -241,7 +284,7 @@ impl AddressBook {
if let Some(index) = self.table_state.selected() { if let Some(index) = self.table_state.selected() {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::RenameAddressBookRecord( let _ = action_tx.send(Action::RenameAddressBookRecord(
self.address_book[index].name.clone() self.address_book[index].name.clone(),
)); ));
} }
} }
@ -251,10 +294,10 @@ impl AddressBook {
if let Some(index) = self.table_state.selected() { if let Some(index) = self.table_state.selected() {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::AccountDetailsOf( let _ = action_tx.send(Action::AccountDetailsOf(
self.address_book[index].name.clone(), self.address_book[index].name.clone(),
self.balances self.balances
.get(&self.address_book[index].account_id) .get(&self.address_book[index].account_id)
.map(|data| data.clone()), .map(|data| data.clone()),
)); ));
} }
} }
@ -287,9 +330,13 @@ impl AddressBook {
let record = self.address_book.remove(index); let record = self.address_book.remove(index);
self.previous_row(); self.previous_row();
self.save_to_file(); self.save_to_file();
self.log_event(format!("record `{}` with public address {} removed", self.log_event(
&record.name, &record.address), format!(
ActionLevel::Warn); "record `{}` with public address {} removed",
&record.name, &record.address
),
ActionLevel::Warn,
);
} }
} }
@ -308,7 +355,7 @@ impl AddressBook {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
@ -331,8 +378,8 @@ impl AddressBook {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i); self.scroll_state = self.scroll_state.position(i);
@ -344,8 +391,8 @@ impl AddressBook {
let value = value as f64 / 10f64.powi(18); let value = value as f64 / 10f64.powi(18);
let after = Self::DECIMALS; let after = Self::DECIMALS;
format!("{:.after$}{}", value, Self::TICKER) format!("{:.after$}{}", value, Self::TICKER)
}, }
None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER) None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER),
} }
} }
} }
@ -371,14 +418,22 @@ impl Component for AddressBook {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
let mut address_book_file = config.config.data_dir; let mut address_book_file = config.config.data_dir;
@ -390,24 +445,25 @@ impl Component for AddressBook {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::UpdateAddressBookRecord(new_name) => Action::UpdateAddressBookRecord(new_name) => self.rename_record(new_name),
self.rename_record(new_name), Action::NewAddressBookRecord(name, address) => self.add_new_record(name, address),
Action::NewAddressBookRecord(name, address) =>
self.add_new_record(name, address),
Action::BalanceResponse(account_id, maybe_balance) => { Action::BalanceResponse(account_id, maybe_balance) => {
if self.address_book.iter().any(|record| record.account_id == account_id) { if self
.address_book
.iter()
.any(|record| record.account_id == account_id)
{
let _ = match maybe_balance { let _ = match maybe_balance {
Some(balance) => self.balances.insert(account_id, balance), Some(balance) => self.balances.insert(account_id, balance),
None => self.balances.remove(&account_id), None => self.balances.remove(&account_id),
}; };
} }
}, }
_ => {} _ => {}
}; };
Ok(None) Ok(None)
} }
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> { fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
if self.is_active { if self.is_active {
match key.code { match key.code {
@ -421,7 +477,7 @@ impl Component for AddressBook {
KeyCode::Char('R') => self.update_address_book_record(), KeyCode::Char('R') => self.update_address_book_record(),
KeyCode::Char('I') => self.show_account_details(), KeyCode::Char('I') => self.show_account_details(),
KeyCode::Enter => self.send_transfer_to(), KeyCode::Enter => self.send_transfer_to(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -432,34 +488,31 @@ impl Component for AddressBook {
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 table = Table::new( let table = Table::new(
self.address_book self.address_book.iter().map(|info| {
.iter() let balance = self
.map(|info| { .balances
let balance = self.balances .get(&info.account_id)
.get(&info.account_id) .map(|inner_balance| inner_balance.free);
.map(|inner_balance| inner_balance.free); Row::new(vec![
Row::new(vec![ Cell::from(Text::from(info.name.clone()).alignment(Alignment::Left)),
Cell::from(Text::from(info.name.clone()).alignment(Alignment::Left)), Cell::from(Text::from(info.address.clone()).alignment(Alignment::Center)),
Cell::from(Text::from(info.address.clone()).alignment(Alignment::Center)), Cell::from(Text::from(self.prepare_u128(balance)).alignment(Alignment::Right)),
Cell::from(Text::from(self.prepare_u128(balance)).alignment(Alignment::Right)), ])
]) }),
}), [Constraint::Min(5), Constraint::Max(51), Constraint::Min(11)],
[
Constraint::Min(5),
Constraint::Max(51),
Constraint::Min(11),
],
) )
.style(self.palette.create_basic_style(false)) .style(self.palette.create_basic_style(false))
.highlight_style(self.palette.create_basic_style(true)) .highlight_style(self.palette.create_basic_style(true))
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.padding(Padding::right(2)) .border_type(border_type)
.title_alignment(Alignment::Right) .padding(Padding::right(2))
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Address Book")); .title_style(self.palette.create_title_style(false))
.title("Address Book"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -470,7 +523,10 @@ impl Component for AddressBook {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,18 +1,13 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Constraint, Rect}, layout::{Alignment, Constraint, Rect},
text::Text,
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame Frame,
}; };
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette, widgets::DotSpinner};
widgets::DotSpinner,
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)] #[derive(Debug)]
pub struct Balance { pub struct Balance {
@ -22,7 +17,7 @@ pub struct Balance {
locked_balance: Option<u128>, locked_balance: Option<u128>,
reserved_balance: Option<u128>, reserved_balance: Option<u128>,
nonce: Option<u32>, nonce: Option<u32>,
palette: StylePalette palette: StylePalette,
} }
impl Default for Balance { impl Default for Balance {
@ -53,8 +48,8 @@ impl Balance {
let value = value as f64 / 10f64.powi(18); let value = value as f64 / 10f64.powi(18);
let after = Self::DECIMALS; let after = Self::DECIMALS;
format!("{:.after$}{}", value, Self::TICKER) format!("{:.after$}{}", value, Self::TICKER)
}, }
None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER) None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER),
} }
} }
} }
@ -71,38 +66,43 @@ impl PartialComponent for Balance {
impl Component for Balance { impl Component for Balance {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
} }
Ok(()) Ok(())
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::BalanceSetActive(maybe_balance) => { Action::BalanceSetActive(maybe_balance) => match maybe_balance {
match maybe_balance { Some(balance) => {
Some(balance) => { self.total_balance = Some(balance.free);
self.total_balance = Some(balance.free); self.locked_balance = Some(balance.frozen);
self.locked_balance = Some(balance.frozen); self.reserved_balance = Some(balance.reserved);
self.reserved_balance = Some(balance.reserved); self.nonce = Some(balance.nonce);
self.nonce = Some(balance.nonce);
let transferable = balance.free let transferable = balance
.saturating_sub(balance.reserved) .free
.saturating_sub(balance.frozen); .saturating_sub(balance.reserved)
self.transferable_balance = Some(transferable); .saturating_sub(balance.frozen);
}, self.transferable_balance = Some(transferable);
None => { }
self.transferable_balance = None; None => {
self.locked_balance = None; self.transferable_balance = None;
self.reserved_balance = None; self.locked_balance = None;
self.total_balance = None; self.reserved_balance = None;
self.nonce = None; self.total_balance = None;
} self.nonce = None;
} }
}, },
_ => {} _ => {}
@ -112,46 +112,60 @@ impl Component for Balance {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [_, _, place, _] = super::account_layout(area); let [_, _, place, _] = super::account_layout(area);
let (border_style, border_type) = self.palette let (border_style, border_type) = self.palette.create_border_style(self.is_active);
.create_border_style(self.is_active);
let table = Table::new( let table = Table::new(
[ [
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("nonce: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("nonce: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.nonce Cell::from(
.map(|n| n.to_string()) Text::from(
.unwrap_or(DotSpinner::default().to_string()) self.nonce
).alignment(Alignment::Right)), .map(|n| n.to_string())
.unwrap_or(DotSpinner::default().to_string()),
)
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("account: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("account: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.total_balance)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.total_balance))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("free: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("free: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.transferable_balance)).alignment(Alignment::Right)) Cell::from(
Text::from(self.prepare_u128(self.transferable_balance))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("locked: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("locked: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.locked_balance)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.locked_balance))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("reserved: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("reserved: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.reserved_balance)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.reserved_balance))
.alignment(Alignment::Right),
),
]), ]),
], ],
[ [Constraint::Max(10), Constraint::Min(14)],
Constraint::Max(10),
Constraint::Min(14),
]
) )
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Balance")); .title_style(self.palette.create_title_style(false))
.title("Balance"),
);
frame.render_widget(table, place); frame.render_widget(table, place);
Ok(()) Ok(())

View File

@ -1,14 +1,14 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -27,7 +27,7 @@ pub struct BondPopup {
minimal_bond: u128, minimal_bond: u128,
is_bonded: bool, is_bonded: bool,
amount: Input, amount: Input,
palette: StylePalette palette: StylePalette,
} }
impl Default for BondPopup { impl Default for BondPopup {
@ -53,8 +53,7 @@ impl BondPopup {
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::WalletLog));
Action::EventLog(message, level, ActionTarget::WalletLog));
} }
} }
@ -88,16 +87,25 @@ impl BondPopup {
let amount = (value * 1_000_000_000_000_000_000.0) as u128; let amount = (value * 1_000_000_000_000_000_000.0) as u128;
let log_target = ActionTarget::WalletLog; let log_target = ActionTarget::WalletLog;
let _ = if self.is_bonded { let _ = if self.is_bonded {
network_tx.send(Action::BondValidatorExtraFrom(self.account_secret_seed, amount, log_target)) network_tx.send(Action::BondValidatorExtraFrom(
self.account_secret_seed,
amount,
log_target,
))
} else { } else {
network_tx.send(Action::BondValidatorFrom(self.account_secret_seed, amount, log_target)) network_tx.send(Action::BondValidatorFrom(
self.account_secret_seed,
amount,
log_target,
))
}; };
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
} }
}, }
Err(err) => self.log_event( Err(err) => {
format!("invalid amount, error: {err}"), ActionLevel::Error), self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error)
}
} }
} }
} }
@ -147,11 +155,16 @@ impl Component for BondPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -165,7 +178,7 @@ impl Component for BondPopup {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -173,9 +186,12 @@ impl Component for BondPopup {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetIsBonded(is_bonded, account_id) if self.account_id == account_id => Action::SetIsBonded(is_bonded, account_id) if self.account_id == account_id => {
self.is_bonded = is_bonded, self.is_bonded = is_bonded
Action::UsedAccount(account_id, secret_seed) => self.update_used_account(account_id, secret_seed), }
Action::UsedAccount(account_id, secret_seed) => {
self.update_used_account(account_id, secret_seed)
}
Action::SetMinValidatorBond(minimal_bond) => self.minimal_bond = minimal_bond, Action::SetMinValidatorBond(minimal_bond) => self.minimal_bond = minimal_bond,
_ => {} _ => {}
}; };
@ -185,13 +201,14 @@ impl Component for BondPopup {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.amount.value()) let input = Paragraph::new(self.amount.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title(format!("Staking bond amount"))); .title(format!("Staking bond amount")),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
@ -200,8 +217,8 @@ impl Component for BondPopup {
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(input, area); frame.render_widget(input, area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
area.x + self.amount.cursor() as u16 + 1, area.x + self.amount.cursor() as u16 + 1,
area.y + 1 area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -2,14 +2,14 @@ use std::sync::mpsc::Sender;
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
widgets::Clear,
layout::{Alignment, Constraint, Rect}, layout::{Alignment, Constraint, Rect},
text::Text, text::Text,
widgets::Clear,
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame Frame,
}; };
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{action::Action, config::Config, palette::StylePalette};
#[derive(Debug)] #[derive(Debug)]
@ -86,11 +86,11 @@ impl CurrentValidatorDetails {
Some(commission) => { Some(commission) => {
self.commission = commission as f64 / 10_000_000.0; self.commission = commission as f64 / 10_000_000.0;
self.is_active_validator = true; self.is_active_validator = true;
}, }
None => { None => {
self.commission = 0.0; self.commission = 0.0;
self.is_active_validator = false; self.is_active_validator = false;
}, }
} }
} }
@ -130,29 +130,53 @@ impl Component for CurrentValidatorDetails {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetChoosenValidator(account_id, individual, total) => self.update_choosen_validator(account_id, individual, total), Action::SetChoosenValidator(account_id, individual, total) => {
Action::SetValidatorPrefs(commission, is_disabled, account_id) if self.choosen == account_id => self.update_commission(commission, is_disabled), self.update_choosen_validator(account_id, individual, total)
Action::SetNominatorsByValidator(noms, account_id) if self.choosen == account_id => self.others_len = noms.len(), }
Action::SetStakedAmountRatio(_, active_stake, account_id) if self.choosen == account_id => self.active_stake = active_stake.unwrap_or_default(), Action::SetValidatorPrefs(commission, is_disabled, account_id)
Action::SetValidatorLatestClaim(era_index, account_id) if self.choosen == account_id => self.latest_era_claim = era_index, if self.choosen == account_id =>
{
self.update_commission(commission, is_disabled)
}
Action::SetNominatorsByValidator(noms, account_id) if self.choosen == account_id => {
self.others_len = noms.len()
}
Action::SetStakedAmountRatio(_, active_stake, account_id)
if self.choosen == account_id =>
{
self.active_stake = active_stake.unwrap_or_default()
}
Action::SetValidatorLatestClaim(era_index, account_id)
if self.choosen == account_id =>
{
self.latest_era_claim = era_index
}
Action::SetStakedRatio(total, own, account_id) if self.choosen == account_id => { Action::SetStakedRatio(total, own, account_id) if self.choosen == account_id => {
self.total_balance = total; self.total_balance = total;
self.own_balance = own; self.own_balance = own;
}, }
_ => {} _ => {}
}; };
@ -167,54 +191,85 @@ impl Component for CurrentValidatorDetails {
vec![ vec![
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Forbidden".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Forbidden".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.is_nomination_disabled.to_string()).alignment(Alignment::Right)), Cell::from(
Text::from(self.is_nomination_disabled.to_string())
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Nominators".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Nominators".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.others_len.to_string()).alignment(Alignment::Right)), Cell::from(
Text::from(self.others_len.to_string()).alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Total staked".to_string()).alignment(Alignment::Left)), Cell::from(
Cell::from(Text::from(self.prepare_u128(self.total_balance)).alignment(Alignment::Right)), Text::from("Total staked".to_string()).alignment(Alignment::Left),
),
Cell::from(
Text::from(self.prepare_u128(self.total_balance))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Own stake".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Own stake".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.own_balance)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.own_balance))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Imbalance".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Imbalance".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_stake_imbalance()).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_stake_imbalance()).alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Commission".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Commission".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(format!("{:.4}%", self.commission)).alignment(Alignment::Right)), Cell::from(
Text::from(format!("{:.4}%", self.commission))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Points ratio".to_string()).alignment(Alignment::Left)), Cell::from(
Cell::from(Text::from(format!("{:.4}%", self.points_ratio)).alignment(Alignment::Right)), Text::from("Points ratio".to_string()).alignment(Alignment::Left),
),
Cell::from(
Text::from(format!("{:.4}%", self.points_ratio))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("In next era".to_string()).alignment(Alignment::Left)), Cell::from(
Cell::from(Text::from(self.prepare_state_string()).alignment(Alignment::Right)), Text::from("In next era".to_string()).alignment(Alignment::Left),
),
Cell::from(
Text::from(self.prepare_state_string()).alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("Last payout".to_string()).alignment(Alignment::Left)), Cell::from(
Cell::from(Text::from(format!("{} days ago", self.latest_era_claim)).alignment(Alignment::Right)), Text::from("Last payout".to_string()).alignment(Alignment::Left),
),
Cell::from(
Text::from(format!("{} days ago", self.latest_era_claim))
.alignment(Alignment::Right),
),
]), ]),
], ],
[ [Constraint::Max(12), Constraint::Min(0)],
Constraint::Max(12),
Constraint::Min(0),
],
) )
.column_spacing(1) .column_spacing(1)
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Validator details")); .title_style(self.palette.create_title_style(false))
.title("Validator details"),
);
frame.render_widget(Clear, place); frame.render_widget(Clear, place);
frame.render_widget(table, place); frame.render_widget(table, place);

View File

@ -1,33 +1,29 @@
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::io::{Write, BufRead, BufReader};
use subxt::ext::sp_core::crypto::{AccountId32, Ss58AddressFormat, Ss58Codec}; use subxt::ext::sp_core::crypto::{AccountId32, Ss58AddressFormat, Ss58Codec};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent}; use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::{Constraint, Margin}; use ratatui::layout::{Constraint, Margin};
use ratatui::style::{Stylize, Modifier}; use ratatui::style::{Modifier, Stylize};
use ratatui::widgets::Clear; use ratatui::widgets::Clear;
use ratatui::{ use ratatui::{
text::{Line, Text},
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::{Line, Text},
widgets::{ widgets::{
Block, Cell, Row, Table, TableState, Scrollbar, Padding, Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table,
ScrollbarOrientation, ScrollbarState, TableState,
}, },
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{PartialComponent, Component, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::types::{ActionLevel, Nominations, ActionTarget, EraRewardPoints}; use crate::types::{ActionLevel, ActionTarget, EraRewardPoints, Nominations};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette};
action::Action,
config::Config,
palette::StylePalette,
};
pub struct CurrentValidators { pub struct CurrentValidators {
is_active: bool, is_active: bool,
@ -116,8 +112,8 @@ impl CurrentValidators {
account_id: *account, account_id: *account,
address: AccountId32::from(*account) address: AccountId32::from(*account)
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)), .to_ss58check_with_version(Ss58AddressFormat::custom(1996)),
points: 0, points: 0,
disabled: true, disabled: true,
}); });
} }
} }
@ -127,15 +123,17 @@ impl CurrentValidators {
fn update_choosen_details(&self, index: usize) { fn update_choosen_details(&self, index: usize) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let (selected_account_id, selected_points) = self.filtered_vector let (selected_account_id, selected_points) = self
.filtered_vector
.get(index) .get(index)
.map(|data| (data.account_id, data.points)) .map(|data| (data.account_id, data.points))
.unwrap_or_default(); .unwrap_or_default();
let _ = action_tx.send(Action::SetChoosenValidator( let _ = action_tx.send(Action::SetChoosenValidator(
selected_account_id, selected_account_id,
selected_points, selected_points,
self.total_points)); self.total_points,
));
} }
} }
@ -145,21 +143,24 @@ impl CurrentValidators {
fn choose_current_nominated(&mut self) { fn choose_current_nominated(&mut self) {
self.clear_choosen(); self.clear_choosen();
self.checked_validators.extend(self.my_nominations self.checked_validators.extend(
.get(&self.account_id) self.my_nominations
.map(|nom| nom.targets.clone()) .get(&self.account_id)
.unwrap_or_default()); .map(|nom| nom.targets.clone())
.unwrap_or_default(),
);
} }
fn choose_all_validators(&mut self) { fn choose_all_validators(&mut self) {
self.clear_choosen(); self.clear_choosen();
self.checked_validators.extend(self.individual self.checked_validators
.iter().map(|ind| ind.account_id)); .extend(self.individual.iter().map(|ind| ind.account_id));
} }
fn swap_choosen_filter(&mut self) { fn swap_choosen_filter(&mut self) {
let is_individual = self.filtered_vector.len() == self.individual.len(); let is_individual = self.filtered_vector.len() == self.individual.len();
self.filtered_vector = self.individual self.filtered_vector = self
.individual
.iter() .iter()
.filter_map(|data| { .filter_map(|data| {
let is_good = !is_individual || self.checked_validators.contains(&data.account_id); let is_good = !is_individual || self.checked_validators.contains(&data.account_id);
@ -193,8 +194,8 @@ impl CurrentValidators {
let account_id = self.filtered_vector[index].account_id; let account_id = self.filtered_vector[index].account_id;
let _ = self.known_validators.insert(account_id, new_name); let _ = self.known_validators.insert(account_id, new_name);
let mut file = File::create(&self.known_validators_file) let mut file =
.expect("file should be accessible; qed"); File::create(&self.known_validators_file).expect("file should be accessible; qed");
for (account_id, name) in self.known_validators.iter() { for (account_id, name) in self.known_validators.iter() {
let seed = hex::encode(account_id); let seed = hex::encode(account_id);
@ -246,7 +247,7 @@ impl CurrentValidators {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.move_selected(i); self.move_selected(i);
@ -267,8 +268,8 @@ impl CurrentValidators {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.move_selected(i); self.move_selected(i);
} }
@ -294,7 +295,11 @@ impl CurrentValidators {
} }
fn nominate_choosen(&mut self) { fn nominate_choosen(&mut self) {
if self.my_stash_id.map(|acc| acc == self.account_id).unwrap_or_default() { if self
.my_stash_id
.map(|acc| acc == self.account_id)
.unwrap_or_default()
{
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::EventLog( let _ = action_tx.send(Action::EventLog(
"nomination from stash account will stop node validation, use another account for nomination".to_string(), "nomination from stash account will stop node validation, use another account for nomination".to_string(),
@ -303,12 +308,15 @@ impl CurrentValidators {
} }
} else { } else {
if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
let nominate_targets: Vec<[u8; 32]> = self.checked_validators let nominate_targets: Vec<[u8; 32]> = self
.checked_validators
.clone() .clone()
.into_iter() .into_iter()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let _ = network_tx.send(Action::NominateTargets( let _ = network_tx.send(Action::NominateTargets(
self.account_secret_seed, nominate_targets)); self.account_secret_seed,
nominate_targets,
));
} }
} }
self.close_popup(); self.close_popup();
@ -316,7 +324,8 @@ impl CurrentValidators {
fn prepare_nomination_line(&self) -> String { fn prepare_nomination_line(&self) -> String {
let empty_nominations = Nominations::default(); let empty_nominations = Nominations::default();
let nominations = self.my_nominations let nominations = self
.my_nominations
.get(&self.account_id) .get(&self.account_id)
.unwrap_or(&empty_nominations); .unwrap_or(&empty_nominations);
@ -328,9 +337,9 @@ impl CurrentValidators {
} else { } else {
"Active" "Active"
}; };
format!("Submitted at era #{} | {} ", format!(
nominations.submitted_in, "Submitted at era #{} | {} ",
status, nominations.submitted_in, status,
) )
} }
} }
@ -358,14 +367,22 @@ impl Component for CurrentValidators {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
let mut known_validators_file = config.config.data_dir; let mut known_validators_file = config.config.data_dir;
known_validators_file.push(Self::KNOWN_VALIDATORS_FILE); known_validators_file.push(Self::KNOWN_VALIDATORS_FILE);
@ -376,12 +393,19 @@ impl Component for CurrentValidators {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::UpdateKnownValidator(validator_name) => self.save_validator_name(validator_name), Action::UpdateKnownValidator(validator_name) => {
Action::UsedAccount(account_id, secret_seed) => self.update_used_account(account_id, secret_seed), self.save_validator_name(validator_name)
Action::SetNominatorsByAccount(nominations, account_id) => self.store_nominators(nominations, account_id), }
Action::UsedAccount(account_id, secret_seed) => {
self.update_used_account(account_id, secret_seed)
}
Action::SetNominatorsByAccount(nominations, account_id) => {
self.store_nominators(nominations, account_id)
}
Action::SetStashAccount(account_id) => self.my_stash_id = Some(account_id), Action::SetStashAccount(account_id) => self.my_stash_id = Some(account_id),
Action::SetCurrentValidatorEraRewards(era_index, total_points, individual) => Action::SetCurrentValidatorEraRewards(era_index, total_points, individual) => {
self.update_era_rewards(era_index, total_points, &individual), self.update_era_rewards(era_index, total_points, &individual)
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -404,7 +428,7 @@ impl Component for CurrentValidators {
KeyCode::Char('N') => self.nominate_choosen(), KeyCode::Char('N') => self.nominate_choosen(),
KeyCode::Enter => self.flip_validator_check(), KeyCode::Enter => self.flip_validator_check(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -415,7 +439,11 @@ impl Component for CurrentValidators {
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 [place, _] = super::nominator_layout(area); let [place, _] = super::nominator_layout(area);
let top_title = format!("Validators {} | Total points: {}", self.individual.len(), self.total_points); let top_title = format!(
"Validators {} | Total points: {}",
self.individual.len(),
self.total_points
);
let bottom_title = self.prepare_nomination_line(); let bottom_title = self.prepare_nomination_line();
let table = Table::new( let table = Table::new(
@ -423,14 +451,18 @@ impl Component for CurrentValidators {
.iter() .iter()
.chain(&self.not_active_nominations) .chain(&self.not_active_nominations)
.map(|info| { .map(|info| {
let is_validator_choosen = self.checked_validators.contains(&info.account_id); let is_validator_choosen =
let is_current_nomination = self.my_nominations self.checked_validators.contains(&info.account_id);
let is_current_nomination = self
.my_nominations
.get(&self.account_id) .get(&self.account_id)
.map(|x| x.targets.contains(&info.account_id)) .map(|x| x.targets.contains(&info.account_id))
.unwrap_or_default(); .unwrap_or_default();
let mut address_text = Text::from(info.address.clone()).alignment(Alignment::Center); let mut address_text =
let mut points_text = Text::from(info.points.to_string()).alignment(Alignment::Right); Text::from(info.address.clone()).alignment(Alignment::Center);
let mut points_text =
Text::from(info.points.to_string()).alignment(Alignment::Right);
let (row_style, is_choosen_text) = if is_validator_choosen { let (row_style, is_choosen_text) = if is_validator_choosen {
address_text = address_text.add_modifier(Modifier::ITALIC); address_text = address_text.add_modifier(Modifier::ITALIC);
@ -447,14 +479,14 @@ impl Component for CurrentValidators {
points_text = points_text.add_modifier(Modifier::CROSSED_OUT); points_text = points_text.add_modifier(Modifier::CROSSED_OUT);
} }
let default_name = self.my_stash_id let default_name = self
.map(|account_id| .my_stash_id
account_id.eq(&info.account_id).then(|| "My stash") .map(|account_id| account_id.eq(&info.account_id).then(|| "My stash"))
)
.flatten() .flatten()
.unwrap_or("Ghostie"); .unwrap_or("Ghostie");
let name = self.known_validators let name = self
.known_validators
.get(&info.account_id) .get(&info.account_id)
.cloned() .cloned()
.unwrap_or(default_name.to_string()); .unwrap_or(default_name.to_string());
@ -464,7 +496,8 @@ impl Component for CurrentValidators {
Cell::from(Text::from(name).alignment(Alignment::Left)), Cell::from(Text::from(name).alignment(Alignment::Left)),
Cell::from(address_text), Cell::from(address_text),
Cell::from(points_text), Cell::from(points_text),
]).style(row_style) ])
.style(row_style)
}), }),
[ [
Constraint::Length(1), Constraint::Length(1),
@ -473,16 +506,18 @@ impl Component for CurrentValidators {
Constraint::Length(6), Constraint::Length(6),
], ],
) )
.style(self.palette.create_basic_style(false)) .style(self.palette.create_basic_style(false))
.highlight_style(self.palette.create_basic_style(true)) .highlight_style(self.palette.create_basic_style(true))
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.padding(Padding::right(2)) .padding(Padding::right(2))
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title_bottom(Line::from(bottom_title).left_aligned()) .title_bottom(Line::from(bottom_title).left_aligned())
.title_top(Line::from(top_title).right_aligned())); .title_top(Line::from(top_title).right_aligned()),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -494,7 +529,10 @@ impl Component for CurrentValidators {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );
} }

View File

@ -5,13 +5,13 @@ use ratatui::{
style::{Color, Style}, style::{Color, Style},
text::Text, text::Text,
widgets::{ widgets::{
Block, Padding, Cell, Row, Scrollbar, ScrollbarOrientation, Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table,
ScrollbarState, Table, TableState, TableState,
}, },
Frame Frame,
}; };
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -32,7 +32,7 @@ pub struct EventLogs {
scroll_state: ScrollbarState, scroll_state: ScrollbarState,
table_state: TableState, table_state: TableState,
logs: std::collections::VecDeque<LogDetails>, logs: std::collections::VecDeque<LogDetails>,
palette: StylePalette palette: StylePalette,
} }
impl EventLogs { impl EventLogs {
@ -65,7 +65,7 @@ impl EventLogs {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
@ -88,8 +88,8 @@ impl EventLogs {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.table_state.select(Some(i)); self.table_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i); self.scroll_state = self.scroll_state.position(i);
@ -112,33 +112,42 @@ impl PartialComponent for EventLogs {
impl Component for EventLogs { impl Component for EventLogs {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied()); self.palette
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied()); .with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
self.palette
.with_scrollbar_style(style.get("scrollbar_style").copied());
} }
Ok(()) Ok(())
} }
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> { fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
match key.code { match key.code {
KeyCode::Up | KeyCode::Char('k') if self.is_active => self.previous_row(), KeyCode::Up | KeyCode::Char('k') if self.is_active => self.previous_row(),
KeyCode::Down | KeyCode::Char('j') if self.is_active => self.next_row(), KeyCode::Down | KeyCode::Char('j') 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.first_row(),
KeyCode::Char('G') if self.is_active => self.last_row(), KeyCode::Char('G') if self.is_active => self.last_row(),
_ => {}, _ => {}
}; };
Ok(None) Ok(None)
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::EventLog(message, level, target) if target == ActionTarget::WalletLog => Action::EventLog(message, level, target) if target == ActionTarget::WalletLog => {
self.add_new_log(message, level), self.add_new_log(message, level)
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -153,33 +162,38 @@ impl Component for EventLogs {
let info_style = Style::new().fg(Color::Green); let info_style = Style::new().fg(Color::Green);
let table = Table::new( let table = Table::new(
self.logs self.logs.iter().map(|log| {
.iter() let style = match log.level {
.map(|log| { ActionLevel::Info => info_style,
let style = match log.level { ActionLevel::Warn => warn_style,
ActionLevel::Info => info_style, ActionLevel::Error => error_style,
ActionLevel::Warn => warn_style, };
ActionLevel::Error => error_style, Row::new(vec![
}; Cell::from(
Row::new(vec![ Text::from(log.time.format("%H:%M:%S").to_string())
Cell::from(Text::from(log.time.format("%H:%M:%S").to_string()).style(style).alignment(Alignment::Left)), .style(style)
Cell::from(Text::from(log.message.clone()).style(style).alignment(Alignment::Left)), .alignment(Alignment::Left),
]) ),
}), Cell::from(
[ Text::from(log.message.clone())
Constraint::Max(8), .style(style)
Constraint::Min(0), .alignment(Alignment::Left),
], ),
])
}),
[Constraint::Max(8), Constraint::Min(0)],
) )
.column_spacing(1) .column_spacing(1)
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.padding(Padding::right(2)) .border_type(border_type)
.title_alignment(Alignment::Right) .padding(Padding::right(2))
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Action Logs")); .title_style(self.palette.create_title_style(false))
.title("Action Logs"),
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -190,7 +204,10 @@ impl Component for EventLogs {
frame.render_stateful_widget(table, place, &mut self.table_state); frame.render_stateful_widget(table, place, &mut self.table_state);
frame.render_stateful_widget( frame.render_stateful_widget(
scrollbar, scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }), place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );
Ok(()) Ok(())

View File

@ -8,41 +8,41 @@ use ratatui::{
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
mod balance;
mod transfer;
mod address_book;
mod add_account;
mod rename_account;
mod event_logs;
mod accounts;
mod overview;
mod add_address_book_record;
mod rename_address_book_record;
mod account_details; mod account_details;
mod staking_ledger; mod accounts;
mod add_account;
mod add_address_book_record;
mod address_book;
mod balance;
mod bond_popup; mod bond_popup;
mod payee_popup;
mod current_validators;
mod current_validator_details; mod current_validator_details;
mod current_validators;
mod event_logs;
mod overview;
mod payee_popup;
mod rename_account;
mod rename_address_book_record;
mod rename_known_validator; mod rename_known_validator;
mod staking_ledger;
mod transfer;
use balance::Balance;
use transfer::Transfer;
use address_book::AddressBook;
use add_account::AddAccount;
use rename_account::RenameAccount;
use event_logs::EventLogs;
use accounts::Accounts;
use overview::Overview;
use add_address_book_record::AddAddressBookRecord;
use rename_address_book_record::RenameAddressBookRecord;
use account_details::AccountDetails; use account_details::AccountDetails;
use staking_ledger::StakingLedger; use accounts::Accounts;
use add_account::AddAccount;
use add_address_book_record::AddAddressBookRecord;
use address_book::AddressBook;
use balance::Balance;
use bond_popup::BondPopup; use bond_popup::BondPopup;
use payee_popup::PayeePopup;
use current_validators::CurrentValidators;
use current_validator_details::CurrentValidatorDetails; use current_validator_details::CurrentValidatorDetails;
use current_validators::CurrentValidators;
use event_logs::EventLogs;
use overview::Overview;
use payee_popup::PayeePopup;
use rename_account::RenameAccount;
use rename_address_book_record::RenameAddressBookRecord;
use rename_known_validator::RenameKnownValidator; use rename_known_validator::RenameKnownValidator;
use staking_ledger::StakingLedger;
use transfer::Transfer;
use super::Component; use super::Component;
use crate::{action::Action, app::Mode, config::Config}; use crate::{action::Action, app::Mode, config::Config};
@ -146,61 +146,63 @@ impl Component for Wallet {
} }
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> { fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
if !self.is_active { return Ok(None) } if !self.is_active {
return Ok(None);
}
match self.current_tab { match self.current_tab {
// block the default key handle for popups // block the default key handle for popups
CurrentTab::AddAccount | CurrentTab::AddAccount
CurrentTab::RenameAccount | | CurrentTab::RenameAccount
CurrentTab::RenameAddressBookRecord | | CurrentTab::RenameAddressBookRecord
CurrentTab::Transfer | | CurrentTab::Transfer
CurrentTab::AccountDetails | | CurrentTab::AccountDetails
CurrentTab::BondPopup | | CurrentTab::BondPopup
CurrentTab::PayeePopup | | CurrentTab::PayeePopup
CurrentTab::CurrentValidatorsPopup | | CurrentTab::CurrentValidatorsPopup
CurrentTab::AddAddressBookRecord => { | CurrentTab::AddAddressBookRecord => {
for component in self.components.iter_mut() { for component in self.components.iter_mut() {
component.handle_key_event(key)?; component.handle_key_event(key)?;
} }
}, }
_ => match key.code { _ => match key.code {
KeyCode::Esc => { KeyCode::Esc => {
self.is_active = false; self.is_active = false;
self.current_tab = CurrentTab::Nothing; self.current_tab = CurrentTab::Nothing;
return Ok(Some(Action::SetActiveScreen(Mode::Menu))); return Ok(Some(Action::SetActiveScreen(Mode::Menu)));
}, }
KeyCode::Char('W') => { KeyCode::Char('W') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::AddAccount; self.current_tab = CurrentTab::AddAccount;
}, }
KeyCode::Char('A') => { KeyCode::Char('A') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::AddAddressBookRecord; self.current_tab = CurrentTab::AddAddressBookRecord;
}, }
KeyCode::Char('T') => { KeyCode::Char('T') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::Transfer; self.current_tab = CurrentTab::Transfer;
}, }
KeyCode::Char('B') => { KeyCode::Char('B') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::BondPopup; self.current_tab = CurrentTab::BondPopup;
}, }
KeyCode::Char('O') => { KeyCode::Char('O') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::PayeePopup; self.current_tab = CurrentTab::PayeePopup;
}, }
KeyCode::Char('N') => { KeyCode::Char('N') => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::CurrentValidatorsPopup; self.current_tab = CurrentTab::CurrentValidatorsPopup;
}, }
KeyCode::Char('l') | KeyCode::Right => self.move_right(), KeyCode::Char('l') | KeyCode::Right => self.move_right(),
KeyCode::Char('h') | KeyCode::Left => self.move_left(), KeyCode::Char('h') | KeyCode::Left => self.move_left(),
_ => { _ => {
for component in self.components.iter_mut() { for component in self.components.iter_mut() {
component.handle_key_event(key)?; component.handle_key_event(key)?;
} }
}, }
} },
} }
Ok(None) Ok(None)
} }
@ -211,11 +213,11 @@ impl Component for Wallet {
self.is_active = true; self.is_active = true;
self.current_tab = CurrentTab::Accounts; self.current_tab = CurrentTab::Accounts;
self.previous_tab = CurrentTab::Accounts; self.previous_tab = CurrentTab::Accounts;
}, }
Action::RenameAccount(_) => { Action::RenameAccount(_) => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::RenameAccount; self.current_tab = CurrentTab::RenameAccount;
}, }
Action::RenameAddressBookRecord(_) => { Action::RenameAddressBookRecord(_) => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::RenameAddressBookRecord; self.current_tab = CurrentTab::RenameAddressBookRecord;
@ -223,11 +225,11 @@ impl Component for Wallet {
Action::TransferTo(_) => { Action::TransferTo(_) => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::Transfer; self.current_tab = CurrentTab::Transfer;
}, }
Action::AccountDetailsOf(_, _) => { Action::AccountDetailsOf(_, _) => {
self.previous_tab = self.current_tab; self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::AccountDetails; self.current_tab = CurrentTab::AccountDetails;
}, }
Action::ClosePopup => self.current_tab = self.previous_tab, Action::ClosePopup => self.current_tab = self.previous_tab,
_ => {} _ => {}
} }
@ -248,18 +250,12 @@ impl Component for Wallet {
} }
pub fn wallet_layout(area: Rect) -> [Rect; 2] { pub fn wallet_layout(area: Rect) -> [Rect; 2] {
Layout::vertical([ Layout::vertical([Constraint::Percentage(75), Constraint::Percentage(25)]).areas(area)
Constraint::Percentage(75),
Constraint::Percentage(25),
]).areas(area)
} }
pub fn bars_layout(area: Rect) -> [Rect; 2] { pub fn bars_layout(area: Rect) -> [Rect; 2] {
let [place, _] = wallet_layout(area); let [place, _] = wallet_layout(area);
Layout::horizontal([ Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(place)
Constraint::Percentage(50),
Constraint::Percentage(50),
]).areas(place)
} }
pub fn account_layout(area: Rect) -> [Rect; 4] { pub fn account_layout(area: Rect) -> [Rect; 4] {
@ -269,14 +265,12 @@ pub fn account_layout(area: Rect) -> [Rect; 4] {
Constraint::Min(0), Constraint::Min(0),
Constraint::Max(7), Constraint::Max(7),
Constraint::Max(6), Constraint::Max(6),
]).areas(place) ])
.areas(place)
} }
pub fn nominator_layout(area: Rect) -> [Rect; 2] { pub fn nominator_layout(area: Rect) -> [Rect; 2] {
let v = Layout::vertical([Constraint::Max(11)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(11)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
Layout::horizontal([ Layout::horizontal([Constraint::Percentage(60), Constraint::Percentage(40)]).areas(area)
Constraint::Percentage(60),
Constraint::Percentage(40),
]).areas(area)
} }

View File

@ -1,24 +1,20 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Constraint, Rect}, layout::{Alignment, Constraint, Rect},
text::Text,
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame Frame,
}; };
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{action::Action, config::Config, palette::StylePalette, widgets::DotSpinner};
action::Action,
config::Config,
palette::StylePalette, widgets::DotSpinner,
};
#[derive(Debug)] #[derive(Debug)]
pub struct Overview { pub struct Overview {
is_active: bool, is_active: bool,
existential_balance: Option<u128>, existential_balance: Option<u128>,
total_issuance: Option<u128>, total_issuance: Option<u128>,
palette: StylePalette palette: StylePalette,
} }
impl Default for Overview { impl Default for Overview {
@ -46,8 +42,8 @@ impl Overview {
let value = value as f64 / 10f64.powi(18); let value = value as f64 / 10f64.powi(18);
let after = Self::DECIMALS; let after = Self::DECIMALS;
format!("{:.after$}{}", value, Self::TICKER) format!("{:.after$}{}", value, Self::TICKER)
}, }
None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER) None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER),
} }
} }
} }
@ -64,12 +60,18 @@ impl PartialComponent for Overview {
impl Component for Overview { impl Component for Overview {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -85,31 +87,35 @@ impl Component for Overview {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [place, _, _, _] = super::account_layout(area); let [place, _, _, _] = super::account_layout(area);
let (border_style, border_type) = self.palette let (border_style, border_type) = self.palette.create_border_style(self.is_active);
.create_border_style(self.is_active);
let table = Table::new( let table = Table::new(
[ [
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("total supply: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("total supply: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.total_issuance)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.total_issuance))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("min deposit: ".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("min deposit: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.existential_balance)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.existential_balance))
.alignment(Alignment::Right),
),
]), ]),
], ],
[ [Constraint::Max(15), Constraint::Min(0)],
Constraint::Max(15),
Constraint::Min(0),
]
) )
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Overview")); .title_style(self.palette.create_title_style(false))
.title("Overview"),
);
frame.render_widget(table, place); frame.render_widget(table, place);
Ok(()) Ok(())

View File

@ -1,18 +1,16 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
text::Text, text::Text,
widgets::{Block, Cell, Clear, Paragraph, Row, Table, TableState}, widgets::{Block, Cell, Clear, Paragraph, Row, Table, TableState},
Frame, Frame,
}; };
use subxt::ext::sp_core::crypto::{
ByteArray, Ss58Codec, Ss58AddressFormat, AccountId32,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use subxt::ext::sp_core::crypto::{AccountId32, ByteArray, Ss58AddressFormat, Ss58Codec};
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -36,7 +34,7 @@ pub struct PayeePopup {
address: Input, address: Input,
possible_payee_options: &'static [(&'static str, &'static str)], possible_payee_options: &'static [(&'static str, &'static str)],
current_reward_destination: RewardDestination, current_reward_destination: RewardDestination,
palette: StylePalette palette: StylePalette,
} }
impl Default for PayeePopup { impl Default for PayeePopup {
@ -61,9 +59,18 @@ impl PayeePopup {
address: Input::new(String::new()), address: Input::new(String::new()),
current_reward_destination: Default::default(), current_reward_destination: Default::default(),
possible_payee_options: &[ possible_payee_options: &[
("Re-stake", "(pay into the stash account, increasing the amount at stake accordingly)"), (
("Stake", "(pay into the stash account, not increasing the amount at stake)"), "Re-stake",
("Account", "(pay into a specified account different from stash)"), "(pay into the stash account, increasing the amount at stake accordingly)",
),
(
"Stake",
"(pay into the stash account, not increasing the amount at stake)",
),
(
"Account",
"(pay into a specified account different from stash)",
),
("None", "(refuse to receive all rewards from staking)"), ("None", "(refuse to receive all rewards from staking)"),
], ],
palette: StylePalette::default(), palette: StylePalette::default(),
@ -85,7 +92,7 @@ impl PayeePopup {
let address = AccountId32::from(account_id) let address = AccountId32::from(account_id)
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)); .to_ss58check_with_version(Ss58AddressFormat::custom(1996));
(2, address) (2, address)
}, }
RewardDestination::None => (3, Default::default()), RewardDestination::None => (3, Default::default()),
_ => (0, Default::default()), _ => (0, Default::default()),
}; };
@ -106,13 +113,12 @@ impl PayeePopup {
} else { } else {
i + 1 i + 1
} }
}, }
None => 0, None => 0,
}; };
self.move_to_row(i); self.move_to_row(i);
} }
fn previous_row(&mut self) { fn previous_row(&mut self) {
let i = match self.table_state.selected() { let i = match self.table_state.selected() {
Some(i) => { Some(i) => {
@ -121,18 +127,15 @@ impl PayeePopup {
} else { } else {
i - 1 i - 1
} }
}, }
None => 0 None => 0,
}; };
self.move_to_row(i); self.move_to_row(i);
} }
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::EventLog( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::WalletLog));
message,
level,
ActionTarget::WalletLog));
} }
} }
@ -155,8 +158,12 @@ impl PayeePopup {
Ok((account_id, format)) => { Ok((account_id, format)) => {
if format != Ss58AddressFormat::custom(1996) { if format != Ss58AddressFormat::custom(1996) {
self.log_event( self.log_event(
format!("provided public address for {} is not part of Casper/Ghost ecosystem", self.address.value()), format!(
ActionLevel::Error); "provided public address for {} is not part of Casper/Ghost ecosystem",
self.address.value()
),
ActionLevel::Error,
);
} }
let seed_vec = account_id.to_raw_vec(); let seed_vec = account_id.to_raw_vec();
let mut account_id = [0u8; 32]; let mut account_id = [0u8; 32];
@ -164,15 +171,18 @@ impl PayeePopup {
self.proposed_account_id = Some(account_id); self.proposed_account_id = Some(account_id);
self.submit_new_payee(); self.submit_new_payee();
}, }
_ => { _ => {
self.log_event( self.log_event(
format!("could not create valid account id from {}", self.address.value()), format!(
ActionLevel::Error); "could not create valid account id from {}",
self.address.value()
),
ActionLevel::Error,
);
self.proposed_account_id = None; self.proposed_account_id = None;
} }
}; };
} }
fn submit_new_payee(&mut self) { fn submit_new_payee(&mut self) {
@ -181,7 +191,8 @@ impl PayeePopup {
0 => RewardDestination::Staked, 0 => RewardDestination::Staked,
1 => RewardDestination::Stash, 1 => RewardDestination::Stash,
2 => { 2 => {
let account_id = self.proposed_account_id let account_id = self
.proposed_account_id
.expect("checked before in submit_new_input; qed"); .expect("checked before in submit_new_input; qed");
RewardDestination::Account(account_id) RewardDestination::Account(account_id)
} }
@ -192,17 +203,20 @@ impl PayeePopup {
if !self.is_bonded { if !self.is_bonded {
self.log_event( self.log_event(
"no bond detected, stake minimum bond amount first".to_string(), "no bond detected, stake minimum bond amount first".to_string(),
ActionLevel::Warn); ActionLevel::Warn,
);
} else if new_destination == self.current_reward_destination { } else if new_destination == self.current_reward_destination {
self.log_event( self.log_event(
"same destination choosen, no need for transaction".to_string(), "same destination choosen, no need for transaction".to_string(),
ActionLevel::Warn); ActionLevel::Warn,
);
} else { } else {
if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::SetPayee( let _ = network_tx.send(Action::SetPayee(
self.account_secret_seed, self.account_secret_seed,
new_destination, new_destination,
ActionTarget::WalletLog)); ActionTarget::WalletLog,
));
} }
} }
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
@ -257,12 +271,18 @@ impl Component for PayeePopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
self.palette.with_highlight_style(style.get("highlight_style").copied()); .with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
self.palette
.with_highlight_style(style.get("highlight_style").copied());
} }
Ok(()) Ok(())
} }
@ -277,7 +297,7 @@ impl Component for PayeePopup {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.trigger_address_input(), KeyCode::Esc => self.trigger_address_input(),
_ => {}, _ => {}
}; };
} else { } else {
match key.code { match key.code {
@ -286,7 +306,7 @@ impl Component for PayeePopup {
KeyCode::Up | KeyCode::Char('k') => self.previous_row(), KeyCode::Up | KeyCode::Char('k') => self.previous_row(),
KeyCode::Down | KeyCode::Char('j') => self.next_row(), KeyCode::Down | KeyCode::Char('j') => self.next_row(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
} }
@ -295,10 +315,15 @@ impl Component for PayeePopup {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::UsedAccount(account_id, secret_seed) => self.update_used_account(account_id, secret_seed), Action::UsedAccount(account_id, secret_seed) => {
Action::SetIsBonded(is_bonded, account_id) if self.account_id == account_id => self.update_used_account(account_id, secret_seed)
self.is_bonded = is_bonded, }
Action::SetStakingPayee(reward_destination, account_id) if self.account_id == account_id => { Action::SetIsBonded(is_bonded, account_id) if self.account_id == account_id => {
self.is_bonded = is_bonded
}
Action::SetStakingPayee(reward_destination, account_id)
if self.account_id == account_id =>
{
let destination_changed = self.current_reward_destination != reward_destination; let destination_changed = self.current_reward_destination != reward_destination;
self.current_reward_destination = reward_destination; self.current_reward_destination = reward_destination;
if destination_changed || self.table_state.selected().is_none() { if destination_changed || self.table_state.selected().is_none() {
@ -327,16 +352,18 @@ impl Component for PayeePopup {
]) ])
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
[Constraint::Length(8), Constraint::Min(0)] [Constraint::Length(8), Constraint::Min(0)],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_style(self.palette.create_popup_title_style()) .border_type(border_type)
.title_alignment(Alignment::Right) .title_style(self.palette.create_popup_title_style())
.title("Select reward destination")); .title_alignment(Alignment::Right)
.title("Select reward destination"),
);
let v = Layout::vertical([Constraint::Max(6)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(6)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(83)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(83)]).flex(Flex::Center);
@ -347,13 +374,14 @@ impl Component for PayeePopup {
frame.render_stateful_widget(table, area, &mut self.table_state); frame.render_stateful_widget(table, area, &mut self.table_state);
if self.is_input_active { if self.is_input_active {
let input_amount = Paragraph::new(self.address.value()) let input_amount = Paragraph::new(self.address.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Destination account")); .title("Destination account"),
);
let v = Layout::vertical([Constraint::Max(8)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(8)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(51)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(51)]).flex(Flex::Center);
@ -364,8 +392,8 @@ impl Component for PayeePopup {
frame.render_widget(input_amount, input_area); frame.render_widget(input_amount, input_area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
input_area.x + self.address.cursor() as u16 + 1, input_area.x + self.address.cursor() as u16 + 1,
input_area.y + 1 input_area.y + 1,
)); ));
} }
} }

View File

@ -1,18 +1,18 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
widgets::{Input, InputRequest},
action::Action, action::Action,
config::Config, config::Config,
palette::StylePalette, palette::StylePalette,
widgets::{Input, InputRequest},
}; };
#[derive(Debug)] #[derive(Debug)]
@ -21,7 +21,7 @@ pub struct RenameAccount {
action_tx: Option<UnboundedSender<Action>>, action_tx: Option<UnboundedSender<Action>>,
old_name: String, old_name: String,
name: Input, name: Input,
palette: StylePalette palette: StylePalette,
} }
impl Default for RenameAccount { impl Default for RenameAccount {
@ -50,8 +50,7 @@ impl RenameAccount {
fn submit_message(&mut self) { fn submit_message(&mut self) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::UpdateAccountName( let _ = action_tx.send(Action::UpdateAccountName(self.name.value().to_string()));
self.name.value().to_string()));
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
} }
} }
@ -94,11 +93,16 @@ impl Component for RenameAccount {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -120,7 +124,7 @@ impl Component for RenameAccount {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -129,13 +133,14 @@ impl Component for RenameAccount {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.name.value()) let input = Paragraph::new(self.name.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title(format!("New name for '{}'", self.old_name))); .title(format!("New name for '{}'", self.old_name)),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
@ -144,8 +149,8 @@ impl Component for RenameAccount {
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(input, area); frame.render_widget(input, area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
area.x + self.name.cursor() as u16 + 1, area.x + self.name.cursor() as u16 + 1,
area.y + 1 area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -1,18 +1,18 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
widgets::{Input, InputRequest},
action::Action, action::Action,
config::Config, config::Config,
palette::StylePalette, palette::StylePalette,
widgets::{Input, InputRequest},
}; };
#[derive(Debug)] #[derive(Debug)]
@ -21,7 +21,7 @@ pub struct RenameAddressBookRecord {
action_tx: Option<UnboundedSender<Action>>, action_tx: Option<UnboundedSender<Action>>,
old_name: String, old_name: String,
name: Input, name: Input,
palette: StylePalette palette: StylePalette,
} }
impl Default for RenameAddressBookRecord { impl Default for RenameAddressBookRecord {
@ -51,7 +51,8 @@ impl RenameAddressBookRecord {
fn submit_message(&mut self) { fn submit_message(&mut self) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::UpdateAddressBookRecord( let _ = action_tx.send(Action::UpdateAddressBookRecord(
self.name.value().to_string())); self.name.value().to_string(),
));
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
} }
} }
@ -94,11 +95,16 @@ impl Component for RenameAddressBookRecord {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -120,7 +126,7 @@ impl Component for RenameAddressBookRecord {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -129,13 +135,14 @@ impl Component for RenameAddressBookRecord {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active { if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.name.value()) let input = Paragraph::new(self.name.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title(format!("New name for '{}'", self.old_name))); .title(format!("New name for '{}'", self.old_name)),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
@ -144,8 +151,8 @@ impl Component for RenameAddressBookRecord {
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(input, area); frame.render_widget(input, area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
area.x + self.name.cursor() as u16 + 1, area.x + self.name.cursor() as u16 + 1,
area.y + 1 area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -1,18 +1,18 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
widgets::{Input, InputRequest},
action::Action, action::Action,
config::Config, config::Config,
palette::StylePalette, palette::StylePalette,
widgets::{Input, InputRequest},
}; };
#[derive(Debug)] #[derive(Debug)]
@ -20,7 +20,7 @@ pub struct RenameKnownValidator {
is_active: bool, is_active: bool,
action_tx: Option<UnboundedSender<Action>>, action_tx: Option<UnboundedSender<Action>>,
name: Input, name: Input,
palette: StylePalette palette: StylePalette,
} }
impl Default for RenameKnownValidator { impl Default for RenameKnownValidator {
@ -46,8 +46,7 @@ impl RenameKnownValidator {
fn submit_message(&mut self) { fn submit_message(&mut self) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::UpdateKnownValidator( let _ = action_tx.send(Action::UpdateKnownValidator(self.name.value().to_string()));
self.name.value().to_string()));
} }
self.close_popup(); self.close_popup();
} }
@ -70,7 +69,7 @@ impl RenameKnownValidator {
} }
impl PartialComponent for RenameKnownValidator { impl PartialComponent for RenameKnownValidator {
fn set_active(&mut self, _current_tab: CurrentTab) { } fn set_active(&mut self, _current_tab: CurrentTab) {}
} }
impl Component for RenameKnownValidator { impl Component for RenameKnownValidator {
@ -89,11 +88,16 @@ impl Component for RenameKnownValidator {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -107,7 +111,7 @@ impl Component for RenameKnownValidator {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -119,13 +123,14 @@ impl Component for RenameKnownValidator {
let area = Rect::new(size.width / 2, size.height / 2, 51, 3); let area = Rect::new(size.width / 2, size.height / 2, 51, 3);
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.name.value()) let input = Paragraph::new(self.name.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Know validator name")); .title("Know validator name"),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area); let [area] = v.areas(area);
@ -134,8 +139,8 @@ impl Component for RenameKnownValidator {
frame.render_widget(Clear, area); frame.render_widget(Clear, area);
frame.render_widget(input, area); frame.render_widget(input, area);
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
area.x + self.name.cursor() as u16 + 1, area.x + self.name.cursor() as u16 + 1,
area.y + 1 area.y + 1,
)); ));
} }
Ok(()) Ok(())

View File

@ -1,17 +1,17 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
text::Text,
layout::{Alignment, Constraint, Rect}, layout::{Alignment, Constraint, Rect},
text::Text,
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame Frame,
}; };
use subxt::ext::sp_core::crypto::{Ss58Codec, Ss58AddressFormat, AccountId32};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use subxt::ext::sp_core::crypto::{AccountId32, Ss58AddressFormat, Ss58Codec};
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, config::Config, palette::StylePalette, action::Action, config::Config, palette::StylePalette, types::RewardDestination,
types::RewardDestination, widgets::DotSpinner, widgets::DotSpinner,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -23,7 +23,7 @@ pub struct StakingLedger {
total_staked: Option<u128>, total_staked: Option<u128>,
active_staked: Option<u128>, active_staked: Option<u128>,
reward_destination: RewardDestination, reward_destination: RewardDestination,
palette: StylePalette palette: StylePalette,
} }
impl Default for StakingLedger { impl Default for StakingLedger {
@ -64,8 +64,8 @@ impl StakingLedger {
let value = value as f64 / 10f64.powi(18); let value = value as f64 / 10f64.powi(18);
let after = Self::DECIMALS; let after = Self::DECIMALS;
format!("{:.after$}{}", value, Self::TICKER) format!("{:.after$}{}", value, Self::TICKER)
}, }
None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER) None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER),
} }
} }
@ -80,7 +80,7 @@ impl StakingLedger {
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)); .to_ss58check_with_version(Ss58AddressFormat::custom(1996));
let tail = address.len().saturating_sub(5); let tail = address.len().saturating_sub(5);
format!("{}..{}", &address[..5], &address[tail..]) format!("{}..{}", &address[..5], &address[tail..])
}, }
} }
} }
@ -110,12 +110,18 @@ impl Component for StakingLedger {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_hover_style(style.get("hover_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); self.palette
self.palette.with_hover_border_style(style.get("hover_border_style").copied()); .with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_hover_title_style(style.get("hover_title_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -123,14 +129,20 @@ impl Component for StakingLedger {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::UsedAccount(account_id, _) => self.set_used_account_id(account_id), Action::UsedAccount(account_id, _) => self.set_used_account_id(account_id),
Action::SetIsBonded(is_bonded, account_id) if self.account_id == account_id => Action::SetIsBonded(is_bonded, account_id) if self.account_id == account_id => {
self.is_bonded = is_bonded, self.is_bonded = is_bonded
Action::SetStakingPayee(reward_destination, account_id) if self.account_id == account_id => }
self.reward_destination = reward_destination, Action::SetStakingPayee(reward_destination, account_id)
Action::SetStakedAmountRatio(total, active, account_id) if self.account_id == account_id => { if self.account_id == account_id =>
{
self.reward_destination = reward_destination
}
Action::SetStakedAmountRatio(total, active, account_id)
if self.account_id == account_id =>
{
self.total_staked = total; self.total_staked = total;
self.active_staked = active; self.active_staked = active;
}, }
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -138,8 +150,7 @@ impl Component for StakingLedger {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [_, _, _, place] = super::account_layout(area); let [_, _, _, place] = super::account_layout(area);
let (border_style, border_type) = self.palette let (border_style, border_type) = self.palette.create_border_style(self.is_active);
.create_border_style(self.is_active);
let table = Table::new( let table = Table::new(
[ [
@ -149,28 +160,35 @@ impl Component for StakingLedger {
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("destination:".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("destination:".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.get_reward_destination()).alignment(Alignment::Right)), Cell::from(
Text::from(self.get_reward_destination()).alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("total:".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("total:".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.total_staked)).alignment(Alignment::Right)) Cell::from(
Text::from(self.prepare_u128(self.total_staked))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Text::from("active:".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("active:".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.active_staked)).alignment(Alignment::Right)), Cell::from(
Text::from(self.prepare_u128(self.active_staked))
.alignment(Alignment::Right),
),
]), ]),
], ],
[ [Constraint::Max(12), Constraint::Min(14)],
Constraint::Max(12),
Constraint::Min(14),
]
) )
.block(Block::bordered() .block(
.border_style(border_style) Block::bordered()
.border_type(border_type) .border_style(border_style)
.title_alignment(Alignment::Right) .border_type(border_type)
.title_style(self.palette.create_title_style(false)) .title_alignment(Alignment::Right)
.title("Nomination stake")); .title_style(self.palette.create_title_style(false))
.title("Nomination stake"),
);
frame.render_widget(table, place); frame.render_widget(table, place);
Ok(()) Ok(())

View File

@ -5,14 +5,12 @@ use ratatui::{
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame,
}; };
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use subxt::ext::sp_core::crypto::{ use subxt::ext::sp_core::crypto::{AccountId32, ByteArray, Ss58AddressFormat, Ss58Codec};
ByteArray, Ss58Codec, Ss58AddressFormat, AccountId32,
};
use super::{Component, PartialComponent, CurrentTab}; use super::{Component, CurrentTab, PartialComponent};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -68,49 +66,58 @@ impl Transfer {
fn log_event(&mut self, message: String, level: ActionLevel) { fn log_event(&mut self, message: String, level: ActionLevel) {
if let Some(action_tx) = &self.action_tx { if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send( let _ = action_tx.send(Action::EventLog(message, level, ActionTarget::WalletLog));
Action::EventLog(message, level, ActionTarget::WalletLog));
} }
} }
fn submit_message(&mut self) { fn submit_message(&mut self) {
match self.receiver_or_amount { match self.receiver_or_amount {
ReceiverOrAmount::Receiver => ReceiverOrAmount::Receiver => self.receiver_or_amount = ReceiverOrAmount::Amount,
self.receiver_or_amount = ReceiverOrAmount::Amount, ReceiverOrAmount::Amount => {
ReceiverOrAmount::Amount => if let Some(network_tx) = &self.network_tx { if let Some(network_tx) = &self.network_tx {
match AccountId32::from_ss58check_with_version(self.receiver.value()) { match AccountId32::from_ss58check_with_version(self.receiver.value()) {
Ok((_account_id, format)) if format != Ss58AddressFormat::custom(1996) => { Ok((_account_id, format)) if format != Ss58AddressFormat::custom(1996) => {
self.log_event( self.log_event(
format!("provided public address for {} is not part of Casper/Ghost ecosystem", self.receiver.value()), format!("provided public address for {} is not part of Casper/Ghost ecosystem", self.receiver.value()),
ActionLevel::Error); ActionLevel::Error);
},
Ok((account_id, format)) if format == Ss58AddressFormat::custom(1996) => {
let seed_vec = account_id.to_raw_vec();
let mut account_id = [0u8; 32];
account_id.copy_from_slice(&seed_vec);
let str_amount = self.amount.value();
let str_amount = if str_amount.starts_with('.') {
&format!("0{}", str_amount)[..]
} else {
str_amount
};
match str_amount.parse::<f64>() {
Ok(value) => {
let amount = (value * 1_000_000_000_000_000_000.0) as u128;
let _ = network_tx.send(Action::TransferBalance(
self.sender.clone(), account_id, amount));
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup);
}
},
Err(err) => self.log_event(
format!("invalid amount, error: {err}"), ActionLevel::Error),
} }
}, Ok((account_id, format)) if format == Ss58AddressFormat::custom(1996) => {
_ => self.log_event( let seed_vec = account_id.to_raw_vec();
format!("could not create valid account id from {}", self.receiver.value()), let mut account_id = [0u8; 32];
ActionLevel::Error), account_id.copy_from_slice(&seed_vec);
let str_amount = self.amount.value();
let str_amount = if str_amount.starts_with('.') {
&format!("0{}", str_amount)[..]
} else {
str_amount
};
match str_amount.parse::<f64>() {
Ok(value) => {
let amount = (value * 1_000_000_000_000_000_000.0) as u128;
let _ = network_tx.send(Action::TransferBalance(
self.sender.clone(),
account_id,
amount,
));
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup);
}
}
Err(err) => self.log_event(
format!("invalid amount, error: {err}"),
ActionLevel::Error,
),
}
}
_ => self.log_event(
format!(
"could not create valid account id from {}",
self.receiver.value()
),
ActionLevel::Error,
),
}
} }
} }
} }
@ -120,7 +127,7 @@ impl Transfer {
match self.receiver_or_amount { match self.receiver_or_amount {
ReceiverOrAmount::Receiver => { ReceiverOrAmount::Receiver => {
let _ = self.receiver.handle(InputRequest::InsertChar(new_char)); let _ = self.receiver.handle(InputRequest::InsertChar(new_char));
}, }
ReceiverOrAmount::Amount => { ReceiverOrAmount::Amount => {
let is_separator_needed = !self.amount.value().contains('.') && new_char == '.'; let is_separator_needed = !self.amount.value().contains('.') && new_char == '.';
if new_char.is_digit(10) || is_separator_needed { if new_char.is_digit(10) || is_separator_needed {
@ -134,7 +141,7 @@ impl Transfer {
match self.receiver_or_amount { match self.receiver_or_amount {
ReceiverOrAmount::Receiver => { ReceiverOrAmount::Receiver => {
let _ = self.receiver.handle(InputRequest::DeletePrevChar); let _ = self.receiver.handle(InputRequest::DeletePrevChar);
}, }
ReceiverOrAmount::Amount => { ReceiverOrAmount::Amount => {
let _ = self.amount.handle(InputRequest::DeletePrevChar); let _ = self.amount.handle(InputRequest::DeletePrevChar);
} }
@ -145,7 +152,7 @@ impl Transfer {
match self.receiver_or_amount { match self.receiver_or_amount {
ReceiverOrAmount::Receiver => { ReceiverOrAmount::Receiver => {
let _ = self.receiver.handle(InputRequest::GoToNextChar); let _ = self.receiver.handle(InputRequest::GoToNextChar);
}, }
ReceiverOrAmount::Amount => { ReceiverOrAmount::Amount => {
let _ = self.amount.handle(InputRequest::GoToNextChar); let _ = self.amount.handle(InputRequest::GoToNextChar);
} }
@ -156,7 +163,7 @@ impl Transfer {
match self.receiver_or_amount { match self.receiver_or_amount {
ReceiverOrAmount::Receiver => { ReceiverOrAmount::Receiver => {
let _ = self.receiver.handle(InputRequest::GoToPrevChar); let _ = self.receiver.handle(InputRequest::GoToPrevChar);
}, }
ReceiverOrAmount::Amount => { ReceiverOrAmount::Amount => {
let _ = self.amount.handle(InputRequest::GoToPrevChar); let _ = self.amount.handle(InputRequest::GoToPrevChar);
} }
@ -175,12 +182,11 @@ impl PartialComponent for Transfer {
self.amount = Input::new(String::new()); self.amount = Input::new(String::new());
self.receiver_or_amount = ReceiverOrAmount::Receiver; self.receiver_or_amount = ReceiverOrAmount::Receiver;
} }
}, }
}; };
} }
} }
impl Component for Transfer { impl Component for Transfer {
fn register_network_handler(&mut self, tx: Sender<Action>) -> Result<()> { fn register_network_handler(&mut self, tx: Sender<Action>) -> Result<()> {
self.network_tx = Some(tx); self.network_tx = Some(tx);
@ -194,11 +200,16 @@ impl Component for Transfer {
fn register_config_handler(&mut self, config: Config) -> Result<()> { fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) { if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied()); self.palette
self.palette.with_normal_border_style(style.get("normal_border_style").copied()); .with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied()); self.palette
self.palette.with_popup_style(style.get("popup_style").copied()); .with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied()); self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_popup_style(style.get("popup_style").copied());
self.palette
.with_popup_title_style(style.get("popup_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -214,7 +225,7 @@ impl Component for Transfer {
KeyCode::Left => self.move_cursor_left(), KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(), KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(), KeyCode::Esc => self.close_popup(),
_ => {}, _ => {}
}; };
} }
Ok(None) Ok(None)
@ -239,21 +250,23 @@ impl Component for Transfer {
let amount_area = Rect::new(size.width / 2, size.height / 2 + 3, 51, 3); let amount_area = Rect::new(size.width / 2, size.height / 2 + 3, 51, 3);
let (border_style, border_type) = self.palette.create_popup_style(); let (border_style, border_type) = self.palette.create_popup_style();
let input_receiver = Paragraph::new(self.receiver.value()) let input_receiver = Paragraph::new(self.receiver.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Receiver")); .title("Receiver"),
);
let input_amount = Paragraph::new(self.amount.value()) let input_amount = Paragraph::new(self.amount.value()).block(
.block(Block::bordered() Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type) .border_type(border_type)
.title_style(self.palette.create_popup_title_style()) .title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Amount to send")); .title("Amount to send"),
);
let v = Layout::vertical([Constraint::Length(3)]).flex(Flex::Center); let v = Layout::vertical([Constraint::Length(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Length(51)]).flex(Flex::Center); let h = Layout::horizontal([Constraint::Length(51)]).flex(Flex::Center);
@ -271,14 +284,14 @@ impl Component for Transfer {
match self.receiver_or_amount { match self.receiver_or_amount {
ReceiverOrAmount::Receiver => { ReceiverOrAmount::Receiver => {
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
receiver_area.x + self.receiver.cursor() as u16 + 1, receiver_area.x + self.receiver.cursor() as u16 + 1,
receiver_area.y + 1 receiver_area.y + 1,
)); ));
}, }
ReceiverOrAmount::Amount => { ReceiverOrAmount::Amount => {
frame.set_cursor_position(Position::new( frame.set_cursor_position(Position::new(
amount_area.x + self.amount.cursor() as u16 + 1, amount_area.x + self.amount.cursor() as u16 + 1,
amount_area.y + 1 amount_area.y + 1,
)); ));
} }
} }

View File

@ -93,9 +93,7 @@ impl Config {
for (mode, default_styles) in default_config.styles.iter() { for (mode, default_styles) in default_config.styles.iter() {
let user_styles = cfg.styles.entry(*mode).or_default(); let user_styles = cfg.styles.entry(*mode).or_default();
for (style_key, style) in default_styles.iter() { for (style_key, style) in default_styles.iter() {
user_styles user_styles.entry(style_key.clone()).or_insert(*style);
.entry(style_key.clone())
.or_insert(*style);
} }
} }
@ -132,7 +130,8 @@ pub struct KeyBindings(pub HashMap<Mode, HashMap<Vec<KeyEvent>, Action>>);
impl<'de> Deserialize<'de> for KeyBindings { impl<'de> Deserialize<'de> for KeyBindings {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>, where
D: Deserializer<'de>,
{ {
let parsed_map = HashMap::<Mode, HashMap<String, Action>>::deserialize(deserializer)?; let parsed_map = HashMap::<Mode, HashMap<String, Action>>::deserialize(deserializer)?;
@ -200,7 +199,7 @@ fn parse_key_code_with_modifiers(
"backtab" => { "backtab" => {
modifiers.insert(KeyModifiers::SHIFT); modifiers.insert(KeyModifiers::SHIFT);
KeyCode::BackTab KeyCode::BackTab
}, }
"backspace" => KeyCode::Backspace, "backspace" => KeyCode::Backspace,
"delete" => KeyCode::Delete, "delete" => KeyCode::Delete,
"insert" => KeyCode::Insert, "insert" => KeyCode::Insert,
@ -253,12 +252,12 @@ pub fn key_event_to_string(key_event: &KeyEvent) -> String {
KeyCode::F(c) => { KeyCode::F(c) => {
char = format!("f({c})"); char = format!("f({c})");
&char &char
}, }
KeyCode::Char(' ') => "space", KeyCode::Char(' ') => "space",
KeyCode::Char(c) => { KeyCode::Char(c) => {
char = c.to_string(); char = c.to_string();
&char &char
}, }
KeyCode::Esc => "esc", KeyCode::Esc => "esc",
KeyCode::Null => "", KeyCode::Null => "",
KeyCode::CapsLock => "", KeyCode::CapsLock => "",
@ -327,7 +326,8 @@ pub struct Styles(pub HashMap<Mode, HashMap<String, Style>>);
impl<'de> Deserialize<'de> for Styles { impl<'de> Deserialize<'de> for Styles {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> where
D: Deserializer<'de>,
{ {
let parsed_map = HashMap::<Mode, HashMap<String, String>>::deserialize(deserializer)?; let parsed_map = HashMap::<Mode, HashMap<String, String>>::deserialize(deserializer)?;

View File

@ -6,8 +6,8 @@ use tracing::error;
pub fn init() -> Result<()> { pub fn init() -> Result<()> {
let (_panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default() let (_panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default()
.panic_section(format!( .panic_section(format!(
"This is a bug. Consider reporting it at {}", "This is a bug. Consider reporting it at {}",
env!("CARGO_PKG_REPOSITORY") env!("CARGO_PKG_REPOSITORY")
)) ))
.capture_span_trace_by_default(false) .capture_span_trace_by_default(false)
.display_location_section(false) .display_location_section(false)

View File

@ -16,8 +16,7 @@ pub fn init() -> Result<()> {
let log_path = directory.join(LOG_FILE.clone()); let log_path = directory.join(LOG_FILE.clone());
let log_file = std::fs::File::create(log_path)?; let log_file = std::fs::File::create(log_path)?;
let env_filter = EnvFilter::builder() let env_filter = EnvFilter::builder().with_default_directive(tracing::Level::INFO.into());
.with_default_directive(tracing::Level::INFO.into());
let env_filter = env_filter let env_filter = env_filter
.try_from_env() .try_from_env()
.or_else(|_| env_filter.with_env_var(LOG_ENV.clone()).from_env())?; .or_else(|_| env_filter.with_env_var(LOG_ENV.clone()).from_env())?;

View File

@ -1,24 +1,21 @@
use clap::Parser; use clap::Parser;
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{backend::rpc::RpcClient, OnlineClient};
OnlineClient,
backend::rpc::RpcClient,
};
mod modes;
mod action; mod action;
mod app; mod app;
mod casper;
mod cli; mod cli;
mod components; mod components;
mod config; mod config;
mod errors; mod errors;
mod logging; mod logging;
mod tui; mod modes;
mod network; mod network;
mod widgets;
mod types;
mod palette; mod palette;
mod casper; mod tui;
mod types;
mod widgets;
use casper::{CasperAccountId, CasperConfig}; use casper::{CasperAccountId, CasperConfig};
@ -62,11 +59,7 @@ async fn main() -> Result<()> {
let cloned_action_tx = action_tx.clone(); let cloned_action_tx = action_tx.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
let mut network = network::Network::new( let mut network = network::Network::new(cloned_action_tx, online_client, rpc_client);
cloned_action_tx,
online_client,
rpc_client,
);
start_tokio_action_loop(sync_io_rx, &mut network); start_tokio_action_loop(sync_io_rx, &mut network);
}); });
@ -84,11 +77,8 @@ async fn main() -> Result<()> {
let cloned_action_tx = action_tx.clone(); let cloned_action_tx = action_tx.clone();
let cloned_sync_tx = sync_io_tx.clone(); let cloned_sync_tx = sync_io_tx.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
let mut subscription = network::BestSubscription::new( let mut subscription =
cloned_action_tx, network::BestSubscription::new(cloned_action_tx, cloned_sync_tx, best_blocks_sub);
cloned_sync_tx,
best_blocks_sub,
);
start_tokio_best_subscription(&mut subscription); start_tokio_best_subscription(&mut subscription);
}); });

View File

@ -1,5 +1,5 @@
use ratatui::layout::Constraint; use ratatui::layout::Constraint;
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Mode { pub enum Mode {

View File

@ -1,18 +1,22 @@
use tokio::sync::mpsc::UnboundedSender; use codec::{Decode, Encode};
use color_eyre::Result; use color_eyre::Result;
use subxt::{backend::{legacy::rpc_methods::SystemHealth, rpc::RpcClient}, rpc_params}; use subxt::{
use codec::{Encode, Decode}; backend::{legacy::rpc_methods::SystemHealth, rpc::RpcClient},
rpc_params,
};
use tokio::sync::mpsc::UnboundedSender;
use crate::{action::Action, network::miscellaneous::get_slow_clap_storage_key, types::{ActionLevel, ActionTarget, BlockRange, PeerInformation}}; use crate::{
action::Action,
network::miscellaneous::get_slow_clap_storage_key,
types::{ActionLevel, ActionTarget, BlockRange, PeerInformation},
};
pub async fn get_node_name( pub async fn get_node_name(
action_tx: &UnboundedSender<Action>, action_tx: &UnboundedSender<Action>,
rpc_client: &RpcClient, rpc_client: &RpcClient,
) -> Result<()> { ) -> Result<()> {
let maybe_node_name = rpc_client let maybe_node_name = rpc_client.request("system_name", rpc_params![]).await.ok();
.request("system_name", rpc_params![])
.await
.ok();
action_tx.send(Action::SetNodeName(maybe_node_name))?; action_tx.send(Action::SetNodeName(maybe_node_name))?;
Ok(()) Ok(())
} }
@ -25,15 +29,18 @@ pub async fn get_system_health(
.request("system_health", rpc_params![]) .request("system_health", rpc_params![])
.await .await
.ok() .ok()
.map_or((None, false, false), |health: SystemHealth| ( .map_or((None, false, false), |health: SystemHealth| {
(
Some(health.peers), Some(health.peers),
health.is_syncing, health.is_syncing,
health.should_have_peers, health.should_have_peers,
)); )
});
action_tx.send(Action::SetSystemHealth( action_tx.send(Action::SetSystemHealth(
maybe_peers, maybe_peers,
is_syncing, is_syncing,
should_have_peers))?; should_have_peers,
))?;
Ok(()) Ok(())
} }
@ -42,10 +49,7 @@ pub async fn get_genesis_hash(
rpc_client: &RpcClient, rpc_client: &RpcClient,
) -> Result<()> { ) -> Result<()> {
let params = rpc_params![0u32]; let params = rpc_params![0u32];
let maybe_genesis_hash = rpc_client let maybe_genesis_hash = rpc_client.request("chain_getBlockHash", params).await.ok();
.request("chain_getBlockHash", params)
.await
.ok();
action_tx.send(Action::SetGenesisHash(maybe_genesis_hash))?; action_tx.send(Action::SetGenesisHash(maybe_genesis_hash))?;
Ok(()) Ok(())
} }
@ -54,10 +58,7 @@ pub async fn get_chain_name(
action_tx: &UnboundedSender<Action>, action_tx: &UnboundedSender<Action>,
rpc_client: &RpcClient, rpc_client: &RpcClient,
) -> Result<()> { ) -> Result<()> {
let maybe_chain_name = rpc_client let maybe_chain_name = rpc_client.request("system_chain", rpc_params![]).await.ok();
.request("system_chain", rpc_params![])
.await
.ok();
action_tx.send(Action::SetChainName(maybe_chain_name))?; action_tx.send(Action::SetChainName(maybe_chain_name))?;
Ok(()) Ok(())
} }
@ -137,7 +138,7 @@ pub async fn rotate_keys(
pub async fn get_block_range( pub async fn get_block_range(
action_tx: &UnboundedSender<Action>, action_tx: &UnboundedSender<Action>,
rpc_client: &RpcClient, rpc_client: &RpcClient,
chain_id: u64 chain_id: u64,
) -> Result<()> { ) -> Result<()> {
let chain_id_encoded = chain_id.encode(); let chain_id_encoded = chain_id.encode();
let block_range_key_raw = get_slow_clap_storage_key(b"block-", &chain_id_encoded); let block_range_key_raw = get_slow_clap_storage_key(b"block-", &chain_id_encoded);
@ -146,7 +147,10 @@ pub async fn get_block_range(
block_range_key.push_str(&format!("{:02x}", byte)); block_range_key.push_str(&format!("{:02x}", byte));
} }
let block_range: BlockRange = rpc_client let block_range: BlockRange = rpc_client
.request("offchain_localStorageGet", rpc_params!["PERSISTENT", block_range_key]) .request(
"offchain_localStorageGet",
rpc_params!["PERSISTENT", block_range_key],
)
.await .await
.ok() .ok()
.map(|hex_string: String| { .map(|hex_string: String| {
@ -154,7 +158,10 @@ pub async fn get_block_range(
let mut cursor = &bytes[..]; let mut cursor = &bytes[..];
let from_block: u64 = u64::decode(&mut cursor).expect("first valid"); let from_block: u64 = u64::decode(&mut cursor).expect("first valid");
let to_block: u64 = u64::decode(&mut cursor).expect("second valid"); let to_block: u64 = u64::decode(&mut cursor).expect("second valid");
BlockRange { from_block, to_block } BlockRange {
from_block,
to_block,
}
}) })
.unwrap_or_default(); .unwrap_or_default();
@ -166,7 +173,7 @@ pub async fn nullify_blocks(
action_tx: &UnboundedSender<Action>, action_tx: &UnboundedSender<Action>,
rpc_client: &RpcClient, rpc_client: &RpcClient,
chain_id: u64, chain_id: u64,
new_block: u64 new_block: u64,
) -> Result<()> { ) -> Result<()> {
let chain_id_encoded = chain_id.encode(); let chain_id_encoded = chain_id.encode();
let block_range_key_raw = get_slow_clap_storage_key(b"block-", &chain_id_encoded); let block_range_key_raw = get_slow_clap_storage_key(b"block-", &chain_id_encoded);
@ -182,18 +189,21 @@ pub async fn nullify_blocks(
} }
match rpc_client match rpc_client
.request::<()>("offchain_localStorageSet", rpc_params!["PERSISTENT", block_range_key, zeroes_bytes]) .request::<()>(
"offchain_localStorageSet",
rpc_params!["PERSISTENT", block_range_key, zeroes_bytes],
)
.await .await
{ {
Ok(_) => action_tx.send(Action::EventLog( Ok(_) => action_tx.send(Action::EventLog(
format!("Blocks changed for chain id #{:?}", chain_id), format!("Blocks changed for chain id #{:?}", chain_id),
ActionLevel::Info, ActionLevel::Info,
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
))?, ))?,
Err(err) => action_tx.send(Action::EventLog( Err(err) => action_tx.send(Action::EventLog(
format!("Block changing failed: {:?}", err), format!("Block changing failed: {:?}", err),
ActionLevel::Error, ActionLevel::Error,
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
))?, ))?,
}; };
@ -213,7 +223,10 @@ pub async fn get_stored_rpc_endpoints(
} }
let stored_rpc_endpoints: String = rpc_client let stored_rpc_endpoints: String = rpc_client
.request("offchain_localStorageGet", rpc_params!["PERSISTENT", endpoint_key]) .request(
"offchain_localStorageGet",
rpc_params!["PERSISTENT", endpoint_key],
)
.await .await
.ok() .ok()
.unwrap_or_default(); .unwrap_or_default();
@ -249,7 +262,11 @@ pub async fn set_stored_rpc_endpoints(
encoded_endpoints.push_str(&format!("{:02x}", byte)); encoded_endpoints.push_str(&format!("{:02x}", byte));
} }
match rpc_client.request::<()>("offchain_localStorageSet", rpc_params!["PERSISTENT", endpoint_key, encoded_endpoints]) match rpc_client
.request::<()>(
"offchain_localStorageSet",
rpc_params!["PERSISTENT", endpoint_key, encoded_endpoints],
)
.await .await
{ {
Ok(_) => { Ok(_) => {
@ -261,9 +278,9 @@ pub async fn set_stored_rpc_endpoints(
get_stored_rpc_endpoints(action_tx, rpc_client, chain_id).await?; get_stored_rpc_endpoints(action_tx, rpc_client, chain_id).await?;
} }
Err(err) => action_tx.send(Action::EventLog( Err(err) => action_tx.send(Action::EventLog(
format!("RPC endpoints update failed: {:?}", err), format!("RPC endpoints update failed: {:?}", err),
ActionLevel::Error, ActionLevel::Error,
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
))?, ))?,
}; };

View File

@ -9,38 +9,131 @@ const SLOW_CLAP_DB_PREFIX: &[u8] = b"slow_clap::";
// FALLOFF: u32 = 0_050_000; // FALLOFF: u32 = 0_050_000;
// MAX_PIECE_COUNT: u32 = 40; // MAX_PIECE_COUNT: u32 = 40;
const PIECEWISE_LINEAR_POUNTS: [(Perbill, Perbill); 32] = [ const PIECEWISE_LINEAR_POUNTS: [(Perbill, Perbill); 32] = [
(Perbill::from_parts(0), Perbill::from_parts(6900000)), (Perbill::from_parts(0), Perbill::from_parts(6900000)),
(Perbill::from_parts(690000000), Perbill::from_parts(690000000)), (
(Perbill::from_parts(692740000), Perbill::from_parts(664536000)), Perbill::from_parts(690000000),
(Perbill::from_parts(695588000), Perbill::from_parts(639072000)), Perbill::from_parts(690000000),
(Perbill::from_parts(698554000), Perbill::from_parts(613608000)), ),
(Perbill::from_parts(701647000), Perbill::from_parts(588144000)), (
(Perbill::from_parts(704879000), Perbill::from_parts(562680000)), Perbill::from_parts(692740000),
(Perbill::from_parts(708262000), Perbill::from_parts(537216000)), Perbill::from_parts(664536000),
(Perbill::from_parts(711811000), Perbill::from_parts(511752000)), ),
(Perbill::from_parts(715545000), Perbill::from_parts(486288000)), (
(Perbill::from_parts(719482000), Perbill::from_parts(460824000)), Perbill::from_parts(695588000),
(Perbill::from_parts(723646000), Perbill::from_parts(435360000)), Perbill::from_parts(639072000),
(Perbill::from_parts(728066000), Perbill::from_parts(409896000)), ),
(Perbill::from_parts(732774000), Perbill::from_parts(384432000)), (
(Perbill::from_parts(737811000), Perbill::from_parts(358968000)), Perbill::from_parts(698554000),
(Perbill::from_parts(743227000), Perbill::from_parts(333504000)), Perbill::from_parts(613608000),
(Perbill::from_parts(749083000), Perbill::from_parts(308040000)), ),
(Perbill::from_parts(755456000), Perbill::from_parts(282576000)), (
(Perbill::from_parts(762447000), Perbill::from_parts(257112000)), Perbill::from_parts(701647000),
(Perbill::from_parts(770189000), Perbill::from_parts(231648000)), Perbill::from_parts(588144000),
(Perbill::from_parts(778863000), Perbill::from_parts(206184000)), ),
(Perbill::from_parts(788725000), Perbill::from_parts(180720000)), (
(Perbill::from_parts(800151000), Perbill::from_parts(155256000)), Perbill::from_parts(704879000),
(Perbill::from_parts(813735000), Perbill::from_parts(129792000)), Perbill::from_parts(562680000),
(Perbill::from_parts(830484000), Perbill::from_parts(104328000)), ),
(Perbill::from_parts(852337000), Perbill::from_parts(78864000)), (
(Perbill::from_parts(877801000), Perbill::from_parts(57460000)), Perbill::from_parts(708262000),
(Perbill::from_parts(903265000), Perbill::from_parts(42422000)), Perbill::from_parts(537216000),
(Perbill::from_parts(928728000), Perbill::from_parts(31857000)), ),
(Perbill::from_parts(954189000), Perbill::from_parts(24435000)), (
(Perbill::from_parts(979651000), Perbill::from_parts(19220000)), Perbill::from_parts(711811000),
(Perbill::from_parts(1000000000), Perbill::from_parts(16291000)), Perbill::from_parts(511752000),
),
(
Perbill::from_parts(715545000),
Perbill::from_parts(486288000),
),
(
Perbill::from_parts(719482000),
Perbill::from_parts(460824000),
),
(
Perbill::from_parts(723646000),
Perbill::from_parts(435360000),
),
(
Perbill::from_parts(728066000),
Perbill::from_parts(409896000),
),
(
Perbill::from_parts(732774000),
Perbill::from_parts(384432000),
),
(
Perbill::from_parts(737811000),
Perbill::from_parts(358968000),
),
(
Perbill::from_parts(743227000),
Perbill::from_parts(333504000),
),
(
Perbill::from_parts(749083000),
Perbill::from_parts(308040000),
),
(
Perbill::from_parts(755456000),
Perbill::from_parts(282576000),
),
(
Perbill::from_parts(762447000),
Perbill::from_parts(257112000),
),
(
Perbill::from_parts(770189000),
Perbill::from_parts(231648000),
),
(
Perbill::from_parts(778863000),
Perbill::from_parts(206184000),
),
(
Perbill::from_parts(788725000),
Perbill::from_parts(180720000),
),
(
Perbill::from_parts(800151000),
Perbill::from_parts(155256000),
),
(
Perbill::from_parts(813735000),
Perbill::from_parts(129792000),
),
(
Perbill::from_parts(830484000),
Perbill::from_parts(104328000),
),
(
Perbill::from_parts(852337000),
Perbill::from_parts(78864000),
),
(
Perbill::from_parts(877801000),
Perbill::from_parts(57460000),
),
(
Perbill::from_parts(903265000),
Perbill::from_parts(42422000),
),
(
Perbill::from_parts(928728000),
Perbill::from_parts(31857000),
),
(
Perbill::from_parts(954189000),
Perbill::from_parts(24435000),
),
(
Perbill::from_parts(979651000),
Perbill::from_parts(19220000),
),
(
Perbill::from_parts(1000000000),
Perbill::from_parts(16291000),
),
]; ];
const MAXIMUM_INFLATION: Perbill = Perbill::from_parts(690000000); const MAXIMUM_INFLATION: Perbill = Perbill::from_parts(690000000);
@ -48,23 +141,34 @@ pub fn calculate_for_fraction(n: u128, d: u128) -> (Perbill, Perbill) {
let n = n.min(d.clone()); let n = n.min(d.clone());
if PIECEWISE_LINEAR_POUNTS.is_empty() { if PIECEWISE_LINEAR_POUNTS.is_empty() {
return (MAXIMUM_INFLATION, Perbill::zero()) return (MAXIMUM_INFLATION, Perbill::zero());
} }
let next_point_index = PIECEWISE_LINEAR_POUNTS.iter().position(|p| n < p.0 * d.clone()); let next_point_index = PIECEWISE_LINEAR_POUNTS
.iter()
.position(|p| n < p.0 * d.clone());
let (prev, next) = if let Some(next_point_index) = next_point_index { let (prev, next) = if let Some(next_point_index) = next_point_index {
if let Some(previous_point_index) = next_point_index.checked_sub(1) { if let Some(previous_point_index) = next_point_index.checked_sub(1) {
(PIECEWISE_LINEAR_POUNTS[previous_point_index], PIECEWISE_LINEAR_POUNTS[next_point_index]) (
PIECEWISE_LINEAR_POUNTS[previous_point_index],
PIECEWISE_LINEAR_POUNTS[next_point_index],
)
} else { } else {
// There is no previous points, take first point ordinate // There is no previous points, take first point ordinate
let fraction = PIECEWISE_LINEAR_POUNTS.first().map(|p| p.1).unwrap_or_else(Perbill::zero); let fraction = PIECEWISE_LINEAR_POUNTS
return (MAXIMUM_INFLATION, fraction) .first()
.map(|p| p.1)
.unwrap_or_else(Perbill::zero);
return (MAXIMUM_INFLATION, fraction);
} }
} else { } else {
// There is no next points, take last point ordinate // There is no next points, take last point ordinate
let fraction = PIECEWISE_LINEAR_POUNTS.last().map(|p| p.1).unwrap_or_else(Perbill::zero); let fraction = PIECEWISE_LINEAR_POUNTS
return (MAXIMUM_INFLATION, fraction) .last()
.map(|p| p.1)
.unwrap_or_else(Perbill::zero);
return (MAXIMUM_INFLATION, fraction);
}; };
let delta_y = multiply_by_rational_saturating( let delta_y = multiply_by_rational_saturating(
@ -86,19 +190,19 @@ pub fn calculate_for_fraction(n: u128, d: u128) -> (Perbill, Perbill) {
} }
fn abs_sub<N: Ord + core::ops::Sub<Output = N> + Clone>(a: N, b: N) -> N where { fn abs_sub<N: Ord + core::ops::Sub<Output = N> + Clone>(a: N, b: N) -> N where {
a.clone().max(b.clone()) - a.min(b) a.clone().max(b.clone()) - a.min(b)
} }
fn multiply_by_rational_saturating(value: u128, p: u32, q: u32) -> u128 { fn multiply_by_rational_saturating(value: u128, p: u32, q: u32) -> u128 {
let q = q.max(1); let q = q.max(1);
let result_divisor_part = (value / q as u128).saturating_mul(p as u128); let result_divisor_part = (value / q as u128).saturating_mul(p as u128);
let result_remainder_part = { let result_remainder_part = {
let rem = value % q as u128; let rem = value % q as u128;
let rem_u32 = rem as u32; let rem_u32 = rem as u32;
let rem_part = rem_u32 as u64 * p as u64 / q as u64; let rem_part = rem_u32 as u64 * p as u64 / q as u64;
rem_part as u128 rem_part as u128
}; };
result_divisor_part.saturating_add(result_remainder_part) result_divisor_part.saturating_add(result_remainder_part)
} }
pub fn get_slow_clap_storage_key(first: &[u8], second: &[u8]) -> Vec<u8> { pub fn get_slow_clap_storage_key(first: &[u8], second: &[u8]) -> Vec<u8> {

View File

@ -1,25 +1,28 @@
use tokio::sync::mpsc::UnboundedSender;
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{
backend::rpc::RpcClient, tx::{TxProgress, TxStatus}, utils::H256, OnlineClient backend::rpc::RpcClient,
tx::{TxProgress, TxStatus},
utils::H256,
OnlineClient,
}; };
use tokio::sync::mpsc::UnboundedSender;
mod legacy_rpc_calls; mod legacy_rpc_calls;
mod miscellaneous;
mod predefined_calls; mod predefined_calls;
mod predefined_txs; mod predefined_txs;
mod subscriptions;
mod miscellaneous;
mod raw_calls; mod raw_calls;
mod subscriptions;
pub use miscellaneous::calculate_for_fraction; pub use miscellaneous::calculate_for_fraction;
use crate::{ use crate::{
types::{ActionLevel, ActionTarget},
action::Action, action::Action,
casper::CasperConfig, casper::CasperConfig,
types::{ActionLevel, ActionTarget},
}; };
pub use subscriptions::{FinalizedSubscription, BestSubscription}; pub use subscriptions::{BestSubscription, FinalizedSubscription};
const GATEKEEPED_CHAIN_IDS: [u64; 1] = [ const GATEKEEPED_CHAIN_IDS: [u64; 1] = [
11155111, //Sepolia 11155111, //Sepolia
@ -69,12 +72,12 @@ impl Network {
fn store_stash_or_validator_if_possible(&mut self, account_id: [u8; 32], is_stash: bool) { fn store_stash_or_validator_if_possible(&mut self, account_id: [u8; 32], is_stash: bool) {
if is_stash { if is_stash {
match self.stash_to_watch { match self.stash_to_watch {
Some(stash) if stash == account_id => {}, Some(stash) if stash == account_id => {}
_ => self.stash_to_watch = Some(account_id), _ => self.stash_to_watch = Some(account_id),
} }
} else { } else {
match self.validator_details_to_watch { match self.validator_details_to_watch {
Some(stash) if stash == account_id => {}, Some(stash) if stash == account_id => {}
_ => self.validator_details_to_watch = Some(account_id), _ => self.validator_details_to_watch = Some(account_id),
} }
} }
@ -107,40 +110,138 @@ impl Network {
Action::NewBestHash(hash) => { Action::NewBestHash(hash) => {
self.best_hash = Some(hash); self.best_hash = Some(hash);
Ok(()) Ok(())
}, }
Action::NewFinalizedHash(hash) => { Action::NewFinalizedHash(hash) => {
self.finalized_hash = Some(hash); self.finalized_hash = Some(hash);
if let Some(stash_to_watch) = self.stash_to_watch { if let Some(stash_to_watch) = self.stash_to_watch {
predefined_calls::get_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &stash_to_watch).await?; predefined_calls::get_session_keys(
predefined_calls::get_queued_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &stash_to_watch).await?; &self.action_tx,
predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &stash_to_watch).await?; &self.online_client_api,
predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &stash_to_watch).await?; &self.rpc_client,
predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &stash_to_watch).await?; &stash_to_watch,
predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &stash_to_watch).await?; )
predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &stash_to_watch).await?; .await?;
predefined_calls::get_account_payee(&self.action_tx, &self.online_client_api, &stash_to_watch).await?; predefined_calls::get_queued_session_keys(
predefined_calls::get_slashing_spans(&self.action_tx, &self.online_client_api, &stash_to_watch).await?; &self.action_tx,
&self.online_client_api,
&self.rpc_client,
&stash_to_watch,
)
.await?;
predefined_calls::get_nominators_by_validator(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_validator_prefs(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_staking_value_ratio(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_is_stash_bonded(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_validators_ledger(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_account_payee(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_slashing_spans(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
for era_index in self.eras_to_watch.iter() { for era_index in self.eras_to_watch.iter() {
predefined_calls::get_validator_staking_result(&self.action_tx, &self.online_client_api, &stash_to_watch, *era_index).await?; predefined_calls::get_validator_staking_result(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
*era_index,
)
.await?;
} }
} }
if let Some(validator_details_to_watch) = self.validator_details_to_watch { if let Some(validator_details_to_watch) = self.validator_details_to_watch {
predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?; predefined_calls::get_nominators_by_validator(
predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?; &self.action_tx,
predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?; &self.online_client_api,
predefined_calls::get_validator_latest_claim(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?; &validator_details_to_watch,
predefined_calls::get_account_payee(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?; )
predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?; .await?;
predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?; predefined_calls::get_validator_prefs(
predefined_calls::get_nominators_by_account(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?; &self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_staking_value_ratio(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_validator_latest_claim(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_account_payee(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_validators_ledger(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_is_stash_bonded(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_nominators_by_account(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
} }
for account_id in self.accounts_to_watch.iter() { for account_id in self.accounts_to_watch.iter() {
predefined_calls::get_balance(&self.action_tx, &self.online_client_api, &account_id).await?; predefined_calls::get_balance(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await?;
} }
Ok(()) Ok(())
}, }
Action::CheckPendingTransactions => { Action::CheckPendingTransactions => {
let length = self.transactions_to_watch.len(); let length = self.transactions_to_watch.len();
for i in (0..length).rev() { for i in (0..length).rev() {
@ -148,70 +249,198 @@ impl Network {
let ext_hash = pending_tx.tx_progress.extrinsic_hash(); let ext_hash = pending_tx.tx_progress.extrinsic_hash();
let log_target = pending_tx.target.clone(); let log_target = pending_tx.target.clone();
match (*pending_tx).tx_progress.next().await { match (*pending_tx).tx_progress.next().await {
Some(Ok(status)) => { Some(Ok(status)) => match status {
match status { TxStatus::Validated => self.action_tx.send(Action::EventLog(
TxStatus::Validated => self.action_tx.send(Action::EventLog(format!("transaction {} is part of future queue", ext_hash), ActionLevel::Info, log_target))?, format!("transaction {} is part of future queue", ext_hash),
TxStatus::Broadcasted { num_peers } => self.action_tx.send(Action::EventLog(format!("transaction {} has been broardcasted to {} nodes", ext_hash, num_peers), ActionLevel::Info, log_target))?, ActionLevel::Info,
TxStatus::NoLongerInBestBlock => self.action_tx.send(Action::EventLog(format!("transaction {} is no longer in a best block", ext_hash), ActionLevel::Warn, log_target))?, log_target,
TxStatus::InBestBlock(b) => self.action_tx.send(Action::EventLog(format!("transaction {} included in the block header {}", b.extrinsic_hash(), b.block_hash()), ActionLevel::Info, log_target))?, ))?,
TxStatus::InFinalizedBlock(b) => { TxStatus::Broadcasted { num_peers } => {
self.action_tx.send(Action::EventLog(format!("transaction {} has been finalized in block header {}", b.extrinsic_hash(), b.block_hash()), ActionLevel::Info, log_target))?; self.action_tx.send(Action::EventLog(
self.transactions_to_watch.remove(i); format!(
} "transaction {} has been broardcasted to {} nodes",
TxStatus::Error { message } => { ext_hash, num_peers
self.action_tx.send(Action::EventLog(format!("transaction {} error, something get wrong: {message}", ext_hash), ActionLevel::Error, log_target))?; ),
self.remove_transaction_and_decrement_nonce(i); ActionLevel::Info,
} log_target,
TxStatus::Invalid { message } => { ))?
self.action_tx.send(Action::EventLog(format!("transaction {} invalid: {message}", ext_hash), ActionLevel::Error, log_target))?; }
self.remove_transaction_and_decrement_nonce(i); TxStatus::NoLongerInBestBlock => {
} self.action_tx.send(Action::EventLog(
TxStatus::Dropped { message } => { format!(
self.action_tx.send(Action::EventLog(format!("transaction {} was dropped: {message}", ext_hash), ActionLevel::Error, log_target))?; "transaction {} is no longer in a best block",
self.remove_transaction_and_decrement_nonce(i); ext_hash
} ),
ActionLevel::Warn,
log_target,
))?
}
TxStatus::InBestBlock(b) => self.action_tx.send(Action::EventLog(
format!(
"transaction {} included in the block header {}",
b.extrinsic_hash(),
b.block_hash()
),
ActionLevel::Info,
log_target,
))?,
TxStatus::InFinalizedBlock(b) => {
self.action_tx.send(Action::EventLog(
format!(
"transaction {} has been finalized in block header {}",
b.extrinsic_hash(),
b.block_hash()
),
ActionLevel::Info,
log_target,
))?;
self.transactions_to_watch.remove(i);
}
TxStatus::Error { message } => {
self.action_tx.send(Action::EventLog(
format!(
"transaction {} error, something get wrong: {message}",
ext_hash
),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i);
}
TxStatus::Invalid { message } => {
self.action_tx.send(Action::EventLog(
format!("transaction {} invalid: {message}", ext_hash),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i);
}
TxStatus::Dropped { message } => {
self.action_tx.send(Action::EventLog(
format!("transaction {} was dropped: {message}", ext_hash),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i);
} }
}, },
_ => { _ => {
self.action_tx.send(Action::EventLog(format!("transaction {} was dropped", ext_hash), ActionLevel::Error, log_target))?; self.action_tx.send(Action::EventLog(
format!("transaction {} was dropped", ext_hash),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i); self.remove_transaction_and_decrement_nonce(i);
} }
} }
} }
Ok(()) Ok(())
}, }
Action::GetSystemHealth => legacy_rpc_calls::get_system_health(&self.action_tx, &self.rpc_client).await, Action::GetSystemHealth => {
Action::GetNodeName => legacy_rpc_calls::get_node_name(&self.action_tx, &self.rpc_client).await, legacy_rpc_calls::get_system_health(&self.action_tx, &self.rpc_client).await
Action::GetGenesisHash => legacy_rpc_calls::get_genesis_hash(&self.action_tx, &self.rpc_client).await, }
Action::GetChainName => legacy_rpc_calls::get_chain_name(&self.action_tx, &self.rpc_client).await, Action::GetNodeName => {
Action::GetChainVersion => legacy_rpc_calls::get_system_version(&self.action_tx, &self.rpc_client).await, legacy_rpc_calls::get_node_name(&self.action_tx, &self.rpc_client).await
Action::GetPendingExtrinsics => legacy_rpc_calls::get_pending_extrinsics(&self.action_tx, &self.rpc_client).await, }
Action::GetConnectedPeers => legacy_rpc_calls::get_connected_peers(&self.action_tx, &self.rpc_client).await, Action::GetGenesisHash => {
Action::GetListenAddresses => legacy_rpc_calls::get_listen_addresses(&self.action_tx, &self.rpc_client).await, legacy_rpc_calls::get_genesis_hash(&self.action_tx, &self.rpc_client).await
Action::GetLocalIdentity => legacy_rpc_calls::get_local_identity(&self.action_tx, &self.rpc_client).await, }
Action::GetRpcEndpoints(chain_id) => legacy_rpc_calls::get_stored_rpc_endpoints(&self.action_tx, &self.rpc_client, chain_id).await, Action::GetChainName => {
Action::RotateSessionKeys => legacy_rpc_calls::rotate_keys(&self.action_tx, &self.rpc_client).await, legacy_rpc_calls::get_chain_name(&self.action_tx, &self.rpc_client).await
}
Action::GetChainVersion => {
legacy_rpc_calls::get_system_version(&self.action_tx, &self.rpc_client).await
}
Action::GetPendingExtrinsics => {
legacy_rpc_calls::get_pending_extrinsics(&self.action_tx, &self.rpc_client).await
}
Action::GetConnectedPeers => {
legacy_rpc_calls::get_connected_peers(&self.action_tx, &self.rpc_client).await
}
Action::GetListenAddresses => {
legacy_rpc_calls::get_listen_addresses(&self.action_tx, &self.rpc_client).await
}
Action::GetLocalIdentity => {
legacy_rpc_calls::get_local_identity(&self.action_tx, &self.rpc_client).await
}
Action::GetRpcEndpoints(chain_id) => {
legacy_rpc_calls::get_stored_rpc_endpoints(
&self.action_tx,
&self.rpc_client,
chain_id,
)
.await
}
Action::RotateSessionKeys => {
legacy_rpc_calls::rotate_keys(&self.action_tx, &self.rpc_client).await
}
Action::GetBlockRange => { Action::GetBlockRange => {
for chain_id in GATEKEEPED_CHAIN_IDS { for chain_id in GATEKEEPED_CHAIN_IDS {
legacy_rpc_calls::get_block_range(&self.action_tx, &self.rpc_client, chain_id).await?; legacy_rpc_calls::get_block_range(&self.action_tx, &self.rpc_client, chain_id)
.await?;
} }
Ok(()) Ok(())
} }
Action::GetBlockAuthor(hash, logs) => predefined_calls::get_block_author(&self.action_tx, &self.online_client_api, &logs, &hash).await, Action::GetBlockAuthor(hash, logs) => {
Action::GetActiveEra => predefined_calls::get_active_era(&self.action_tx, &self.online_client_api).await, predefined_calls::get_block_author(
Action::GetCurrentEra => predefined_calls::get_current_era(&self.action_tx, &self.online_client_api).await, &self.action_tx,
Action::GetEpochProgress => predefined_calls::get_epoch_progress(&self.action_tx, &self.online_client_api).await, &self.online_client_api,
Action::GetMinValidatorBond => predefined_calls::get_minimal_validator_bond(&self.action_tx, &self.online_client_api).await, &logs,
Action::GetGatekeepedNetwork(chain_id) => predefined_calls::get_gatekeeped_network(&self.action_tx, &self.online_client_api, chain_id).await, &hash,
)
.await
}
Action::GetActiveEra => {
predefined_calls::get_active_era(&self.action_tx, &self.online_client_api).await
}
Action::GetCurrentEra => {
predefined_calls::get_current_era(&self.action_tx, &self.online_client_api).await
}
Action::GetEpochProgress => {
predefined_calls::get_epoch_progress(&self.action_tx, &self.online_client_api).await
}
Action::GetMinValidatorBond => {
predefined_calls::get_minimal_validator_bond(
&self.action_tx,
&self.online_client_api,
)
.await
}
Action::GetGatekeepedNetwork(chain_id) => {
predefined_calls::get_gatekeeped_network(
&self.action_tx,
&self.online_client_api,
chain_id,
)
.await
}
Action::GetExistentialDeposit => {
Action::GetExistentialDeposit => predefined_calls::get_existential_deposit(&self.action_tx, &self.online_client_api).await, predefined_calls::get_existential_deposit(&self.action_tx, &self.online_client_api)
Action::GetTotalIssuance => predefined_calls::get_total_issuance(&self.action_tx, &self.online_client_api).await, .await
Action::GetValidatorsNumber => predefined_calls::get_validators_number(&self.action_tx, &self.online_client_api).await, }
Action::GetNominatorsNumber => predefined_calls::get_nominators_number(&self.action_tx, &self.online_client_api).await, Action::GetTotalIssuance => {
Action::GetInflation => predefined_calls::get_inflation(&self.action_tx, &self.online_client_api).await, predefined_calls::get_total_issuance(&self.action_tx, &self.online_client_api).await
Action::GetCurrentValidatorEraRewards => predefined_calls::get_current_validator_reward_in_era(&self.action_tx, &self.online_client_api).await, }
Action::GetValidatorsNumber => {
predefined_calls::get_validators_number(&self.action_tx, &self.online_client_api)
.await
}
Action::GetNominatorsNumber => {
predefined_calls::get_nominators_number(&self.action_tx, &self.online_client_api)
.await
}
Action::GetInflation => {
predefined_calls::get_inflation(&self.action_tx, &self.online_client_api).await
}
Action::GetCurrentValidatorEraRewards => {
predefined_calls::get_current_validator_reward_in_era(
&self.action_tx,
&self.online_client_api,
)
.await
}
Action::SetSender(seed, maybe_nonce) => { Action::SetSender(seed, maybe_nonce) => {
self.store_sender_nonce(&seed, maybe_nonce); self.store_sender_nonce(&seed, maybe_nonce);
@ -224,59 +453,126 @@ impl Network {
Action::GetStakingPayee(account_id, is_stash) => { Action::GetStakingPayee(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_account_payee(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_account_payee(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
} }
Action::GetValidatorLatestClaim(account_id, is_stash) => { Action::GetValidatorLatestClaim(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validator_latest_claim(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_validator_latest_claim(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
} }
Action::GetSlashingSpans(account_id, is_stash) => { Action::GetSlashingSpans(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_slashing_spans(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_slashing_spans(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
} }
Action::GetValidatorLedger(account_id, is_stash) => { Action::GetValidatorLedger(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_validators_ledger(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
} }
Action::GetIsStashBonded(account_id, is_stash) => { Action::GetIsStashBonded(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_is_stash_bonded(
}, &self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetErasStakersOverview(account_id, is_stash) => { Action::GetErasStakersOverview(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_staking_value_ratio(
}, &self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetValidatorPrefs(account_id, is_stash) => { Action::GetValidatorPrefs(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_validator_prefs(
}, &self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetNominatorsByValidator(account_id, is_stash) => { Action::GetNominatorsByValidator(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_nominators_by_validator(
}, &self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetNominatorsByAccount(account_id, is_stash) => { Action::GetNominatorsByAccount(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_nominators_by_account(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_nominators_by_account(
}, &self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetValidatorAllRewards(account_id, is_stash) => { Action::GetValidatorAllRewards(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validator_staking_results(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_validator_staking_results(
}, &self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetQueuedSessionKeys(account_id, is_stash) => { Action::GetQueuedSessionKeys(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_queued_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &account_id).await predefined_calls::get_queued_session_keys(
}, &self.action_tx,
&self.online_client_api,
&self.rpc_client,
&account_id,
)
.await
}
Action::GetSessionKeys(account_id, is_stash) => { Action::GetSessionKeys(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash); self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &account_id).await predefined_calls::get_session_keys(
}, &self.action_tx,
&self.online_client_api,
&self.rpc_client,
&account_id,
)
.await
}
Action::BalanceRequest(account_id, remove) => { Action::BalanceRequest(account_id, remove) => {
if remove { if remove {
let _ = self.accounts_to_watch.remove(&account_id); let _ = self.accounts_to_watch.remove(&account_id);
Ok(()) Ok(())
} else { } else {
let _ = self.accounts_to_watch.insert(account_id); let _ = self.accounts_to_watch.insert(account_id);
predefined_calls::get_balance(&self.action_tx, &self.online_client_api, &account_id).await predefined_calls::get_balance(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
} }
} }
Action::TransferBalance(sender, receiver, amount) => { Action::TransferBalance(sender, receiver, amount) => {
@ -296,7 +592,9 @@ impl Network {
&receiver, &receiver,
&amount, &amount,
maybe_nonce, maybe_nonce,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_cloned, sender: sender_cloned,
@ -316,7 +614,9 @@ impl Network {
&amount, &amount,
maybe_nonce, maybe_nonce,
log_target, log_target,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_str, sender: sender_str,
@ -335,7 +635,9 @@ impl Network {
&amount, &amount,
maybe_nonce, maybe_nonce,
log_target, log_target,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_str, sender: sender_str,
@ -354,7 +656,9 @@ impl Network {
&stash, &stash,
era_index, era_index,
maybe_nonce, maybe_nonce,
).await { )
.await
{
self.eras_to_watch.insert(era_index); self.eras_to_watch.insert(era_index);
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
@ -373,7 +677,9 @@ impl Network {
&sender, &sender,
&hashed_keys, &hashed_keys,
maybe_nonce, maybe_nonce,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_str, sender: sender_str,
@ -391,7 +697,9 @@ impl Network {
&sender, &sender,
percent, percent,
maybe_nonce, maybe_nonce,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_str, sender: sender_str,
@ -408,7 +716,9 @@ impl Network {
&self.online_client_api, &self.online_client_api,
&sender, &sender,
maybe_nonce, maybe_nonce,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_str, sender: sender_str,
@ -426,7 +736,9 @@ impl Network {
&sender, &sender,
&amount, &amount,
maybe_nonce, maybe_nonce,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_str, sender: sender_str,
@ -444,7 +756,9 @@ impl Network {
&sender, &sender,
&amount, &amount,
maybe_nonce, maybe_nonce,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_str, sender: sender_str,
@ -462,7 +776,9 @@ impl Network {
&sender, &sender,
&spans, &spans,
maybe_nonce, maybe_nonce,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_str, sender: sender_str,
@ -481,7 +797,9 @@ impl Network {
reward_destination, reward_destination,
maybe_nonce, maybe_nonce,
log_target, log_target,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_str, sender: sender_str,
@ -499,7 +817,9 @@ impl Network {
&sender, &sender,
&nomination_targets, &nomination_targets,
maybe_nonce, maybe_nonce,
).await { )
.await
{
self.transactions_to_watch.push(TxToWatch { self.transactions_to_watch.push(TxToWatch {
tx_progress, tx_progress,
sender: sender_str, sender: sender_str,
@ -514,7 +834,8 @@ impl Network {
&self.rpc_client, &self.rpc_client,
chain_id, chain_id,
new_block, new_block,
).await )
.await
} }
Action::UpdateStoredRpcEndpoints(chain_id, stored_endpoints) => { Action::UpdateStoredRpcEndpoints(chain_id, stored_endpoints) => {
legacy_rpc_calls::set_stored_rpc_endpoints( legacy_rpc_calls::set_stored_rpc_endpoints(
@ -522,9 +843,10 @@ impl Network {
&self.rpc_client, &self.rpc_client,
chain_id, chain_id,
stored_endpoints, stored_endpoints,
).await )
.await
} }
_ => Ok(()) _ => Ok(()),
} }
} }
} }

View File

@ -1,21 +1,24 @@
use tokio::sync::mpsc::UnboundedSender;
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{
backend::rpc::RpcClient, backend::rpc::RpcClient,
client::OnlineClient, client::OnlineClient,
config::substrate::DigestItem, config::substrate::DigestItem,
ext::sp_core::crypto::{ ext::sp_core::crypto::{AccountId32, Ss58AddressFormat, Ss58Codec},
AccountId32, Ss58AddressFormat, Ss58Codec,
},
rpc_params, rpc_params,
utils::H256, utils::H256,
}; };
use tokio::sync::mpsc::UnboundedSender;
use crate::{ use crate::{
action::Action, action::Action,
casper_network::runtime_types::{ghost_networks::NetworkType, pallet_staking::RewardDestination, sp_consensus_slots}, casper_network::runtime_types::{
types::{EraInfo, EraRewardPoints, Gatekeeper, Nominations, Nominator, SessionKeyInfo, SystemAccount, UnlockChunk}, ghost_networks::NetworkType, pallet_staking::RewardDestination, sp_consensus_slots,
CasperAccountId, CasperConfig },
types::{
EraInfo, EraRewardPoints, Gatekeeper, Nominations, Nominator, SessionKeyInfo,
SystemAccount, UnlockChunk,
},
CasperAccountId, CasperConfig,
}; };
pub async fn get_block_author( pub async fn get_block_author(
@ -24,22 +27,29 @@ pub async fn get_block_author(
logs: &Vec<DigestItem>, logs: &Vec<DigestItem>,
at_hash: &H256, at_hash: &H256,
) -> Result<()> { ) -> Result<()> {
use codec::Decode;
use crate::casper_network::runtime_types::sp_consensus_babe::digests::PreDigest; use crate::casper_network::runtime_types::sp_consensus_babe::digests::PreDigest;
use codec::Decode;
let validators = super::raw_calls::session::validators(api, Some(at_hash)) let validators = super::raw_calls::session::validators(api, Some(at_hash))
.await? .await?
.unwrap_or_default(); .unwrap_or_default();
let maybe_author = match logs.iter().find(|item| matches!(item, DigestItem::PreRuntime(..))) { let maybe_author = match logs
.iter()
.find(|item| matches!(item, DigestItem::PreRuntime(..)))
{
Some(DigestItem::PreRuntime(engine, data)) if *engine == [b'B', b'A', b'B', b'E'] => { Some(DigestItem::PreRuntime(engine, data)) if *engine == [b'B', b'A', b'B', b'E'] => {
match PreDigest::decode(&mut &data[..]) { match PreDigest::decode(&mut &data[..]) {
Ok(PreDigest::Primary(primary)) => validators.get(primary.authority_index as usize), Ok(PreDigest::Primary(primary)) => validators.get(primary.authority_index as usize),
Ok(PreDigest::SecondaryPlain(secondary)) => validators.get(secondary.authority_index as usize), Ok(PreDigest::SecondaryPlain(secondary)) => {
Ok(PreDigest::SecondaryVRF(secondary)) => validators.get(secondary.authority_index as usize), validators.get(secondary.authority_index as usize)
}
Ok(PreDigest::SecondaryVRF(secondary)) => {
validators.get(secondary.authority_index as usize)
}
_ => None, _ => None,
} }
}, }
_ => None, _ => None,
}; };
@ -49,7 +59,7 @@ pub async fn get_block_author(
.expect("author should be valid AccountId32; qed"); .expect("author should be valid AccountId32; qed");
let account_id = AccountId32::from(extended_author.0); let account_id = AccountId32::from(extended_author.0);
account_id.to_ss58check_with_version(Ss58AddressFormat::custom(1996)) account_id.to_ss58check_with_version(Ss58AddressFormat::custom(1996))
}, }
None => "...".to_string(), None => "...".to_string(),
}; };
@ -111,8 +121,7 @@ pub async fn get_total_issuance(
action_tx: &UnboundedSender<Action>, action_tx: &UnboundedSender<Action>,
api: &OnlineClient<CasperConfig>, api: &OnlineClient<CasperConfig>,
) -> Result<()> { ) -> Result<()> {
let maybe_total_issuance = super::raw_calls::balances::total_issuance(api, None) let maybe_total_issuance = super::raw_calls::balances::total_issuance(api, None).await?;
.await?;
action_tx.send(Action::SetTotalIssuance(maybe_total_issuance))?; action_tx.send(Action::SetTotalIssuance(maybe_total_issuance))?;
Ok(()) Ok(())
} }
@ -134,12 +143,11 @@ pub async fn get_balance(
let maybe_balance = super::raw_calls::system::balance(api, None, account_id) let maybe_balance = super::raw_calls::system::balance(api, None, account_id)
.await? .await?
.map(|balance| SystemAccount { .map(|balance| SystemAccount {
nonce: balance.nonce, nonce: balance.nonce,
free: balance.data.free, free: balance.data.free,
reserved: balance.data.reserved, reserved: balance.data.reserved,
frozen: balance.data.frozen, frozen: balance.data.frozen,
} });
);
action_tx.send(Action::BalanceResponse(*account_id, maybe_balance))?; action_tx.send(Action::BalanceResponse(*account_id, maybe_balance))?;
Ok(()) Ok(())
} }
@ -185,10 +193,11 @@ pub async fn get_inflation(
let adjusted_issuance = super::raw_calls::networks::bridged_imbalance(api, None) let adjusted_issuance = super::raw_calls::networks::bridged_imbalance(api, None)
.await? .await?
.map(|imbalance| total_issuance .map(|imbalance| {
.saturating_add(imbalance.bridged_out) total_issuance
.saturating_sub(imbalance.bridged_in) .saturating_add(imbalance.bridged_out)
) .saturating_sub(imbalance.bridged_in)
})
.unwrap_or_default(); .unwrap_or_default();
let accumulated_commission = super::raw_calls::networks::accumulated_commission(api, None) let accumulated_commission = super::raw_calls::networks::accumulated_commission(api, None)
@ -230,7 +239,7 @@ pub async fn get_session_keys(
let slow_key = format!("0x{}", hex::encode(session_keys.slow_clap.0)); let slow_key = format!("0x{}", hex::encode(session_keys.slow_clap.0));
(gran_key, babe_key, audi_key, slow_key) (gran_key, babe_key, audi_key, slow_key)
}, }
None => (String::new(), String::new(), String::new(), String::new()), None => (String::new(), String::new(), String::new(), String::new()),
}; };
@ -252,19 +261,17 @@ pub async fn get_queued_session_keys(
let maybe_queued_keys = super::raw_calls::session::queued_keys(api, None).await?; let maybe_queued_keys = super::raw_calls::session::queued_keys(api, None).await?;
let (gran_key, babe_key, audi_key, slow_key) = match maybe_queued_keys { let (gran_key, babe_key, audi_key, slow_key) = match maybe_queued_keys {
Some(session_keys) => { Some(session_keys) => match session_keys.iter().find(|tuple| tuple.0 == account) {
match session_keys.iter().find(|tuple| tuple.0 == account) { Some(keys) => {
Some(keys) => { let session_keys = &keys.1;
let session_keys = &keys.1; let gran_key = format!("0x{}", hex::encode(session_keys.grandpa.0));
let gran_key = format!("0x{}", hex::encode(session_keys.grandpa.0)); let babe_key = format!("0x{}", hex::encode(session_keys.babe.0));
let babe_key = format!("0x{}", hex::encode(session_keys.babe.0)); let audi_key = format!("0x{}", hex::encode(session_keys.authority_discovery.0));
let audi_key = format!("0x{}", hex::encode(session_keys.authority_discovery.0)); let slow_key = format!("0x{}", hex::encode(session_keys.slow_clap.0));
let slow_key = format!("0x{}", hex::encode(session_keys.slow_clap.0));
(gran_key, babe_key, audi_key, slow_key) (gran_key, babe_key, audi_key, slow_key)
},
None => (String::new(), String::new(), String::new(), String::new()),
} }
None => (String::new(), String::new(), String::new(), String::new()),
}, },
None => (String::new(), String::new(), String::new(), String::new()), None => (String::new(), String::new(), String::new(), String::new()),
}; };
@ -293,7 +300,7 @@ async fn check_author_has_key(
.await?; .await?;
let session_key_info = SessionKeyInfo { let session_key_info = SessionKeyInfo {
key: key.to_string(), key: key.to_string(),
is_stored is_stored,
}; };
action_tx.send(Action::SetSessionKey(name.to_string(), session_key_info))?; action_tx.send(Action::SetSessionKey(name.to_string(), session_key_info))?;
Ok(()) Ok(())
@ -342,34 +349,36 @@ pub async fn get_current_validator_reward_in_era(
.await? .await?
.unwrap_or_default(); .unwrap_or_default();
let maybe_era_reward_points = super::raw_calls::staking::eras_reward_points(api, None, era_index) let maybe_era_reward_points =
.await?; super::raw_calls::staking::eras_reward_points(api, None, era_index).await?;
let (total_points, individual) = match maybe_era_reward_points { let (total_points, individual) = match maybe_era_reward_points {
Some(era_reward_points) => { Some(era_reward_points) => (
( era_reward_points.total,
era_reward_points.total, era_reward_points
era_reward_points.individual .individual
.iter() .iter()
.enumerate() .enumerate()
.map(|(index, (account_id, points))| { .map(|(index, (account_id, points))| {
let address = AccountId32::from(account_id.0) let address = AccountId32::from(account_id.0)
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)); .to_ss58check_with_version(Ss58AddressFormat::custom(1996));
EraRewardPoints { EraRewardPoints {
address, address,
account_id: account_id.0, account_id: account_id.0,
points: *points, points: *points,
disabled: disabled_validators.contains(&(index as u32)), disabled: disabled_validators.contains(&(index as u32)),
} }
}) })
.collect(), .collect(),
) ),
},
None => (0, Vec::new()), None => (0, Vec::new()),
}; };
action_tx.send(Action::SetCurrentValidatorEraRewards( action_tx.send(Action::SetCurrentValidatorEraRewards(
era_index, total_points, individual))?; era_index,
total_points,
individual,
))?;
Ok(()) Ok(())
} }
@ -380,12 +389,16 @@ async fn get_validator_reward_in_era(
account_id: &[u8; 32], account_id: &[u8; 32],
era_index: u32, era_index: u32,
) -> Result<()> { ) -> Result<()> {
let maybe_era_reward_points = super::raw_calls::staking::eras_reward_points(api, None, era_index).await?; let maybe_era_reward_points =
let era_reward = super::raw_calls::staking::eras_validator_reward(api, None, era_index).await?.unwrap_or_default(); super::raw_calls::staking::eras_reward_points(api, None, era_index).await?;
let era_reward = super::raw_calls::staking::eras_validator_reward(api, None, era_index)
.await?
.unwrap_or_default();
let my_reward = match maybe_era_reward_points { let my_reward = match maybe_era_reward_points {
Some(era_reward_points) => { Some(era_reward_points) => {
let my_points = era_reward_points.individual let my_points = era_reward_points
.individual
.iter() .iter()
.find(|(acc, _)| acc.0 == *account_id) .find(|(acc, _)| acc.0 == *account_id)
.map(|info| info.1) .map(|info| info.1)
@ -393,7 +406,7 @@ async fn get_validator_reward_in_era(
era_reward era_reward
.saturating_mul(my_points as u128) .saturating_mul(my_points as u128)
.saturating_div(era_reward_points.total as u128) .saturating_div(era_reward_points.total as u128)
}, }
None => 0u128, None => 0u128,
}; };
@ -408,14 +421,11 @@ async fn get_validator_claims_in_era(
account_id: &[u8; 32], account_id: &[u8; 32],
era_index: u32, era_index: u32,
) -> Result<()> { ) -> Result<()> {
let maybe_claimed_rewards = super::raw_calls::staking::claimed_rewards(api, None, era_index, account_id) let maybe_claimed_rewards =
.await?; super::raw_calls::staking::claimed_rewards(api, None, era_index, account_id).await?;
if let Some(claimed_rewards) = maybe_claimed_rewards { if let Some(claimed_rewards) = maybe_claimed_rewards {
let already_claimed = claimed_rewards let already_claimed = claimed_rewards.first().map(|x| *x == 0).unwrap_or(false);
.first()
.map(|x| *x == 0)
.unwrap_or(false);
action_tx.send(Action::SetValidatorEraClaimed(era_index, already_claimed))?; action_tx.send(Action::SetValidatorEraClaimed(era_index, already_claimed))?;
} }
@ -428,8 +438,8 @@ async fn get_validator_slashes_in_era(
account_id: &[u8; 32], account_id: &[u8; 32],
era_index: u32, era_index: u32,
) -> Result<()> { ) -> Result<()> {
let maybe_slash_in_era = super::raw_calls::staking::validator_slash_in_era(api, None, era_index, account_id) let maybe_slash_in_era =
.await?; super::raw_calls::staking::validator_slash_in_era(api, None, era_index, account_id).await?;
if let Some(slash_in_era) = maybe_slash_in_era { if let Some(slash_in_era) = maybe_slash_in_era {
action_tx.send(Action::SetValidatorEraSlash(era_index, slash_in_era.1))?; action_tx.send(Action::SetValidatorEraSlash(era_index, slash_in_era.1))?;
@ -443,18 +453,26 @@ pub async fn get_validators_ledger(
api: &OnlineClient<CasperConfig>, api: &OnlineClient<CasperConfig>,
account_id: &[u8; 32], account_id: &[u8; 32],
) -> Result<()> { ) -> Result<()> {
let maybe_ledger = super::raw_calls::staking::ledger(api, None, account_id) let maybe_ledger = super::raw_calls::staking::ledger(api, None, account_id).await?;
.await?;
match maybe_ledger { match maybe_ledger {
Some(ledger) => { Some(ledger) => {
let chunks = ledger.unlocking.0 let chunks = ledger
.unlocking
.0
.iter() .iter()
.map(|chunk| UnlockChunk { value: chunk.value, era: chunk.era }) .map(|chunk| UnlockChunk {
value: chunk.value,
era: chunk.era,
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
action_tx.send(Action::SetStakedAmountRatio(Some(ledger.total), Some(ledger.active), *account_id))?; action_tx.send(Action::SetStakedAmountRatio(
Some(ledger.total),
Some(ledger.active),
*account_id,
))?;
action_tx.send(Action::SetValidatorEraUnlocking(chunks, *account_id))?; action_tx.send(Action::SetValidatorEraUnlocking(chunks, *account_id))?;
}, }
None => { None => {
action_tx.send(Action::SetStakedAmountRatio(None, None, *account_id))?; action_tx.send(Action::SetStakedAmountRatio(None, None, *account_id))?;
action_tx.send(Action::SetValidatorEraUnlocking(Vec::new(), *account_id))?; action_tx.send(Action::SetValidatorEraUnlocking(Vec::new(), *account_id))?;
@ -472,7 +490,8 @@ pub async fn get_nominators_by_account(
let nominators = super::raw_calls::staking::nominators(api, None, account_id) let nominators = super::raw_calls::staking::nominators(api, None, account_id)
.await? .await?
.map(|n| Nominations { .map(|n| Nominations {
targets: n.targets targets: n
.targets
.0 .0
.into_iter() .into_iter()
.map(|account_id_32| account_id_32.0) .map(|account_id_32| account_id_32.0)
@ -495,32 +514,41 @@ pub async fn get_nominators_by_validator(
.map(|era_info| era_info.index) .map(|era_info| era_info.index)
.unwrap_or_default(); .unwrap_or_default();
let maybe_eras_stakers_overview = super::raw_calls::staking::eras_stakers_overview(api, None, active_era_index, account_id) let maybe_eras_stakers_overview =
.await?; super::raw_calls::staking::eras_stakers_overview(api, None, active_era_index, account_id)
.await?;
let nominators = match maybe_eras_stakers_overview { let nominators = match maybe_eras_stakers_overview {
Some(overview) => { Some(overview) => {
let mut others = Vec::with_capacity(overview.nominator_count as usize); let mut others = Vec::with_capacity(overview.nominator_count as usize);
for page in 0..overview.page_count { for page in 0..overview.page_count {
let page_index = page as u32; let page_index = page as u32;
let nominators = super::raw_calls::staking::eras_stakers_paged(api, None, active_era_index, page_index, account_id) let nominators = super::raw_calls::staking::eras_stakers_paged(
.await?; api,
others.append(&mut nominators None,
.map(|n| n.others active_era_index,
.iter() page_index,
.map(|info| Nominator { account_id,
account_id: info.who.0, )
address: AccountId32::from(info.who.0) .await?;
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)), others.append(
value: info.value, &mut nominators
.map(|n| {
n.others
.iter()
.map(|info| Nominator {
account_id: info.who.0,
address: AccountId32::from(info.who.0)
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)),
value: info.value,
})
.collect::<Vec<_>>()
}) })
.collect::<Vec<_>>() .unwrap_or_default(),
)
.unwrap_or_default()
); );
} }
others others
}, }
None => Vec::new(), None => Vec::new(),
}; };
@ -549,8 +577,9 @@ pub async fn get_staking_value_ratio(
.await? .await?
.map(|era_info| era_info.index) .map(|era_info| era_info.index)
.unwrap_or_default(); .unwrap_or_default();
let maybe_era_stakers_overview = super::raw_calls::staking::eras_stakers_overview(api, None, active_era_index, account_id) let maybe_era_stakers_overview =
.await?; super::raw_calls::staking::eras_stakers_overview(api, None, active_era_index, account_id)
.await?;
let (total, own) = match maybe_era_stakers_overview { let (total, own) = match maybe_era_stakers_overview {
Some(overview) => (overview.total, overview.own), Some(overview) => (overview.total, overview.own),
None => (0, 0), None => (0, 0),
@ -564,8 +593,8 @@ pub async fn get_validator_prefs(
api: &OnlineClient<CasperConfig>, api: &OnlineClient<CasperConfig>,
account_id: &[u8; 32], account_id: &[u8; 32],
) -> Result<()> { ) -> Result<()> {
let maybe_validator_prefs = super::raw_calls::staking::validators(api, None, account_id) let maybe_validator_prefs =
.await?; super::raw_calls::staking::validators(api, None, account_id).await?;
let (commission, blocked) = match maybe_validator_prefs { let (commission, blocked) = match maybe_validator_prefs {
Some(prefs) => (Some(prefs.commission.0), prefs.blocked), Some(prefs) => (Some(prefs.commission.0), prefs.blocked),
None => (None, false), None => (None, false),
@ -594,7 +623,10 @@ pub async fn get_slashing_spans(
.await? .await?
.map(|spans| spans.prior.len().saturating_add(1)) .map(|spans| spans.prior.len().saturating_add(1))
.unwrap_or_default(); .unwrap_or_default();
action_tx.send(Action::SetSlashingSpansLength(slashing_spans_length, *account_id))?; action_tx.send(Action::SetSlashingSpansLength(
slashing_spans_length,
*account_id,
))?;
Ok(()) Ok(())
} }
@ -613,10 +645,11 @@ pub async fn get_validator_latest_claim(
let mut claimed_era = current_era; let mut claimed_era = current_era;
while claimed_era > last_era { while claimed_era > last_era {
let is_claimed = super::raw_calls::staking::claimed_rewards(api, None, claimed_era, account_id) let is_claimed =
.await? super::raw_calls::staking::claimed_rewards(api, None, claimed_era, account_id)
.map(|claimed_rewards| claimed_rewards.len() > 0) .await?
.unwrap_or_default(); .map(|claimed_rewards| claimed_rewards.len() > 0)
.unwrap_or_default();
if is_claimed { if is_claimed {
break; break;
@ -625,7 +658,10 @@ pub async fn get_validator_latest_claim(
claimed_era -= 1; claimed_era -= 1;
} }
action_tx.send(Action::SetValidatorLatestClaim(current_era.saturating_sub(claimed_era), *account_id))?; action_tx.send(Action::SetValidatorLatestClaim(
current_era.saturating_sub(claimed_era),
*account_id,
))?;
Ok(()) Ok(())
} }
@ -640,7 +676,9 @@ pub async fn get_account_payee(
.map(|payee| match payee { .map(|payee| match payee {
RewardDestination::Stash => crate::types::RewardDestination::Stash, RewardDestination::Stash => crate::types::RewardDestination::Stash,
RewardDestination::Staked => crate::types::RewardDestination::Staked, RewardDestination::Staked => crate::types::RewardDestination::Staked,
RewardDestination::Account(account_id_32) => crate::types::RewardDestination::Account(account_id_32.0), RewardDestination::Account(account_id_32) => {
crate::types::RewardDestination::Account(account_id_32.0)
}
RewardDestination::Controller => crate::types::RewardDestination::Controller, RewardDestination::Controller => crate::types::RewardDestination::Controller,
RewardDestination::None => crate::types::RewardDestination::None, RewardDestination::None => crate::types::RewardDestination::None,
}) })
@ -658,19 +696,18 @@ pub async fn get_gatekeeped_network(
.await? .await?
.map(|network| Gatekeeper { .map(|network| Gatekeeper {
chain_id, chain_id,
chain_name: String::from_utf8_lossy(&network.chain_name) chain_name: String::from_utf8_lossy(&network.chain_name).to_string(),
.to_string(),
chain_type: match network.network_type { chain_type: match network.network_type {
NetworkType::Evm => String::from("EVM"), NetworkType::Evm => String::from("EVM"),
NetworkType::Utxo => String::from("UTXO"), NetworkType::Utxo => String::from("UTXO"),
NetworkType::Undefined => String::from("???"), NetworkType::Undefined => String::from("???"),
}, },
default_endpoints: network.default_endpoints default_endpoints: network
.default_endpoints
.iter() .iter()
.map(|endpoint| String::from_utf8_lossy(&endpoint).to_string()) .map(|endpoint| String::from_utf8_lossy(&endpoint).to_string())
.collect(), .collect(),
gatekeeper: String::from_utf8_lossy(&network.gatekeeper) gatekeeper: String::from_utf8_lossy(&network.gatekeeper).to_string(),
.to_string(),
incoming_fee: network.incoming_fee, incoming_fee: network.incoming_fee,
outgoing_fee: network.outgoing_fee, outgoing_fee: network.outgoing_fee,
}) })

View File

@ -22,7 +22,9 @@ pub async fn transfer_balance(
maybe_nonce: Option<&mut u32>, maybe_nonce: Option<&mut u32>,
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> { ) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let receiver_id = subxt::utils::MultiAddress::Id(subxt::utils::AccountId32::from(*receiver)); let receiver_id = subxt::utils::MultiAddress::Id(subxt::utils::AccountId32::from(*receiver));
let transfer_tx = casper_network::tx().balances().transfer_allow_death(receiver_id, *amount); let transfer_tx = casper_network::tx()
.balances()
.transfer_allow_death(receiver_id, *amount);
inner_sign_and_submit_then_watch( inner_sign_and_submit_then_watch(
action_tx, action_tx,
api, api,
@ -31,7 +33,8 @@ pub async fn transfer_balance(
Box::new(transfer_tx), Box::new(transfer_tx),
"transfer", "transfer",
ActionTarget::WalletLog, ActionTarget::WalletLog,
).await )
.await
} }
pub async fn bond_extra( pub async fn bond_extra(
@ -51,7 +54,8 @@ pub async fn bond_extra(
Box::new(bond_extra_tx), Box::new(bond_extra_tx),
"bond extra", "bond extra",
log_target, log_target,
).await )
.await
} }
pub async fn bond( pub async fn bond(
@ -63,8 +67,11 @@ pub async fn bond(
log_target: ActionTarget, log_target: ActionTarget,
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> { ) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
// auto-stake everything by now // auto-stake everything by now
let reward_destination = casper_network::runtime_types::pallet_staking::RewardDestination::Staked; let reward_destination =
let bond_tx = casper_network::tx().staking().bond(*amount, reward_destination); casper_network::runtime_types::pallet_staking::RewardDestination::Staked;
let bond_tx = casper_network::tx()
.staking()
.bond(*amount, reward_destination);
inner_sign_and_submit_then_watch( inner_sign_and_submit_then_watch(
action_tx, action_tx,
api, api,
@ -73,7 +80,8 @@ pub async fn bond(
Box::new(bond_tx), Box::new(bond_tx),
"bond", "bond",
log_target, log_target,
).await )
.await
} }
pub async fn payout_stakers( pub async fn payout_stakers(
@ -85,7 +93,9 @@ pub async fn payout_stakers(
maybe_nonce: Option<&mut u32>, maybe_nonce: Option<&mut u32>,
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> { ) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let stash_id = subxt::utils::AccountId32::from(*stash); let stash_id = subxt::utils::AccountId32::from(*stash);
let payout_stakers_tx = casper_network::tx().staking().payout_stakers(stash_id, era_index); let payout_stakers_tx = casper_network::tx()
.staking()
.payout_stakers(stash_id, era_index);
inner_sign_and_submit_then_watch( inner_sign_and_submit_then_watch(
action_tx, action_tx,
api, api,
@ -94,7 +104,8 @@ pub async fn payout_stakers(
Box::new(payout_stakers_tx), Box::new(payout_stakers_tx),
"payout stakers", "payout stakers",
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
).await )
.await
} }
pub async fn set_keys( pub async fn set_keys(
@ -107,10 +118,26 @@ pub async fn set_keys(
let (gran_key, babe_key, audi_key, slow_key) = { let (gran_key, babe_key, audi_key, slow_key) = {
let s = hashed_keys_str.trim_start_matches("0x"); let s = hashed_keys_str.trim_start_matches("0x");
( (
hex::decode(&s[0..64]).unwrap().as_slice().try_into().unwrap(), hex::decode(&s[0..64])
hex::decode(&s[64..128]).unwrap().as_slice().try_into().unwrap(), .unwrap()
hex::decode(&s[128..192]).unwrap().as_slice().try_into().unwrap(), .as_slice()
hex::decode(&s[192..256]).unwrap().as_slice().try_into().unwrap(), .try_into()
.unwrap(),
hex::decode(&s[64..128])
.unwrap()
.as_slice()
.try_into()
.unwrap(),
hex::decode(&s[128..192])
.unwrap()
.as_slice()
.try_into()
.unwrap(),
hex::decode(&s[192..256])
.unwrap()
.as_slice()
.try_into()
.unwrap(),
) )
}; };
let session_keys = runtime_types::casper_runtime::opaque::SessionKeys { let session_keys = runtime_types::casper_runtime::opaque::SessionKeys {
@ -121,7 +148,9 @@ pub async fn set_keys(
}; };
// it seems like there is no check for the second paramter, that's why // it seems like there is no check for the second paramter, that's why
// we it can be anything. For example empty vector. // we it can be anything. For example empty vector.
let set_keys_tx = casper_network::tx().session().set_keys(session_keys, Vec::new()); let set_keys_tx = casper_network::tx()
.session()
.set_keys(session_keys, Vec::new());
inner_sign_and_submit_then_watch( inner_sign_and_submit_then_watch(
action_tx, action_tx,
api, api,
@ -130,7 +159,8 @@ pub async fn set_keys(
Box::new(set_keys_tx), Box::new(set_keys_tx),
"set keys", "set keys",
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
).await )
.await
} }
pub async fn validate( pub async fn validate(
@ -153,7 +183,8 @@ pub async fn validate(
Box::new(validate_tx), Box::new(validate_tx),
"validate", "validate",
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
).await )
.await
} }
pub async fn chill( pub async fn chill(
@ -171,7 +202,8 @@ pub async fn chill(
Box::new(chill_tx), Box::new(chill_tx),
"chill", "chill",
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
).await )
.await
} }
pub async fn unbond( pub async fn unbond(
@ -190,7 +222,8 @@ pub async fn unbond(
Box::new(unbond_tx), Box::new(unbond_tx),
"unbond", "unbond",
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
).await )
.await
} }
pub async fn rebond( pub async fn rebond(
@ -209,7 +242,8 @@ pub async fn rebond(
Box::new(rebond_tx), Box::new(rebond_tx),
"rebond", "rebond",
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
).await )
.await
} }
pub async fn withdraw_unbonded( pub async fn withdraw_unbonded(
@ -228,7 +262,8 @@ pub async fn withdraw_unbonded(
Box::new(withdraw_unbonded_tx), Box::new(withdraw_unbonded_tx),
"withdraw unbonded", "withdraw unbonded",
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog,
).await )
.await
} }
pub async fn set_payee( pub async fn set_payee(
@ -240,10 +275,18 @@ pub async fn set_payee(
log_target: ActionTarget, log_target: ActionTarget,
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> { ) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let reward_destination = match reward_destination { let reward_destination = match reward_destination {
RewardDestination::Staked => casper_network::runtime_types::pallet_staking::RewardDestination::Staked, RewardDestination::Staked => {
RewardDestination::Stash => casper_network::runtime_types::pallet_staking::RewardDestination::Stash, casper_network::runtime_types::pallet_staking::RewardDestination::Staked
RewardDestination::Controller => casper_network::runtime_types::pallet_staking::RewardDestination::Controller, }
RewardDestination::None => casper_network::runtime_types::pallet_staking::RewardDestination::None, RewardDestination::Stash => {
casper_network::runtime_types::pallet_staking::RewardDestination::Stash
}
RewardDestination::Controller => {
casper_network::runtime_types::pallet_staking::RewardDestination::Controller
}
RewardDestination::None => {
casper_network::runtime_types::pallet_staking::RewardDestination::None
}
RewardDestination::Account(account) => { RewardDestination::Account(account) => {
let account_id = subxt::utils::AccountId32::from(account); let account_id = subxt::utils::AccountId32::from(account);
casper_network::runtime_types::pallet_staking::RewardDestination::Account(account_id) casper_network::runtime_types::pallet_staking::RewardDestination::Account(account_id)
@ -258,7 +301,8 @@ pub async fn set_payee(
Box::new(set_payee_tx), Box::new(set_payee_tx),
"set payee", "set payee",
log_target, log_target,
).await )
.await
} }
pub async fn nominate( pub async fn nominate(
@ -281,7 +325,8 @@ pub async fn nominate(
Box::new(nominate_tx), Box::new(nominate_tx),
"nominate", "nominate",
ActionTarget::WalletLog, ActionTarget::WalletLog,
).await )
.await
} }
async fn inner_sign_and_submit_then_watch( async fn inner_sign_and_submit_then_watch(
@ -300,30 +345,36 @@ async fn inner_sign_and_submit_then_watch(
CasperExtrinsicParamsBuilder::new() CasperExtrinsicParamsBuilder::new()
.nonce(nonce.saturating_sub(1) as u64) .nonce(nonce.saturating_sub(1) as u64)
.build() .build()
}, }
None => CasperExtrinsicParamsBuilder::new().build(), None => CasperExtrinsicParamsBuilder::new().build(),
}; };
match api match api
.tx() .tx()
.sign_and_submit_then_watch(&tx_call, &signer, tx_params) .sign_and_submit_then_watch(&tx_call, &signer, tx_params)
.await { .await
Ok(tx_progress) => { {
action_tx.send(Action::EventLog( Ok(tx_progress) => {
format!("{tx_name} transaction {} sent", tx_progress.extrinsic_hash()), action_tx.send(Action::EventLog(
ActionLevel::Info, format!(
target))?; "{tx_name} transaction {} sent",
Ok(tx_progress) tx_progress.extrinsic_hash()
}, ),
Err(err) => { ActionLevel::Info,
if let Some(ref mut nonce) = maybe_nonce { target,
**nonce = nonce.saturating_sub(1); ))?;
} Ok(tx_progress)
action_tx.send(Action::EventLog(
format!("error during {tx_name} transaction: {err}"),
ActionLevel::Error,
target))?;
Err(err.into())
}
} }
Err(err) => {
if let Some(ref mut nonce) = maybe_nonce {
**nonce = nonce.saturating_sub(1);
}
action_tx.send(Action::EventLog(
format!("error during {tx_name} transaction: {err}"),
ActionLevel::Error,
target,
))?;
Err(err.into())
}
}
} }

View File

@ -1,10 +1,10 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{client::OnlineClient, utils::H256};
utils::H256,
client::OnlineClient,
};
use crate::{casper_network::{self, runtime_types::sp_consensus_slots}, CasperConfig}; use crate::{
casper_network::{self, runtime_types::sp_consensus_slots},
CasperConfig,
};
pub async fn current_slot( pub async fn current_slot(
online_client: &OnlineClient<CasperConfig>, online_client: &OnlineClient<CasperConfig>,
@ -33,9 +33,7 @@ pub async fn genesis_slot(
Ok(maybe_genesis_slot) Ok(maybe_genesis_slot)
} }
pub fn epoch_duration( pub fn epoch_duration(online_client: &OnlineClient<CasperConfig>) -> Result<u64> {
online_client: &OnlineClient<CasperConfig>,
) -> Result<u64> {
let constant_query = casper_network::constants().babe().epoch_duration(); let constant_query = casper_network::constants().babe().epoch_duration();
let epoch_duration = super::do_constant_call(online_client, &constant_query)?; let epoch_duration = super::do_constant_call(online_client, &constant_query)?;
Ok(epoch_duration) Ok(epoch_duration)

View File

@ -1,10 +1,7 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{client::OnlineClient, utils::H256};
utils::H256,
client::OnlineClient,
};
use crate::{CasperConfig, casper_network}; use crate::{casper_network, CasperConfig};
pub async fn total_issuance( pub async fn total_issuance(
online_client: &OnlineClient<CasperConfig>, online_client: &OnlineClient<CasperConfig>,
@ -15,9 +12,7 @@ pub async fn total_issuance(
Ok(maybe_total_issuance) Ok(maybe_total_issuance)
} }
pub fn existential_deposit( pub fn existential_deposit(online_client: &OnlineClient<CasperConfig>) -> Result<u128> {
online_client: &OnlineClient<CasperConfig>,
) -> Result<u128> {
let constant_query = casper_network::constants().balances().existential_deposit(); let constant_query = casper_network::constants().balances().existential_deposit();
let existential_deposit = super::do_constant_call(online_client, &constant_query)?; let existential_deposit = super::do_constant_call(online_client, &constant_query)?;
Ok(existential_deposit) Ok(existential_deposit)

View File

@ -1,18 +1,18 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{
backend::BlockRef, backend::BlockRef,
utils::{Yes, H256, AccountId32},
client::OnlineClient, client::OnlineClient,
utils::{AccountId32, Yes, H256},
}; };
use crate::CasperConfig; use crate::CasperConfig;
pub mod session;
pub mod staking;
pub mod system;
pub mod babe; pub mod babe;
pub mod balances; pub mod balances;
pub mod networks; pub mod networks;
pub mod session;
pub mod staking;
pub mod system;
pub async fn do_storage_call<'address, Addr>( pub async fn do_storage_call<'address, Addr>(
online_client: &OnlineClient<CasperConfig>, online_client: &OnlineClient<CasperConfig>,
@ -24,17 +24,10 @@ where
{ {
let at_hash = match maybe_at_hash { let at_hash = match maybe_at_hash {
Some(at_hash) => BlockRef::from_hash(*at_hash), Some(at_hash) => BlockRef::from_hash(*at_hash),
None => online_client None => online_client.backend().latest_finalized_block_ref().await?,
.backend()
.latest_finalized_block_ref()
.await?,
}; };
online_client online_client.storage().at(at_hash).fetch(storage_key).await
.storage()
.at(at_hash)
.fetch(storage_key)
.await
} }
pub fn do_constant_call<'address, Addr>( pub fn do_constant_call<'address, Addr>(
@ -42,10 +35,12 @@ pub fn do_constant_call<'address, Addr>(
constant_query: &'address Addr, constant_query: &'address Addr,
) -> Result<Addr::Target, subxt::Error> ) -> Result<Addr::Target, subxt::Error>
where where
Addr: subxt::constants::Address + 'address Addr: subxt::constants::Address + 'address,
{ {
let constant_client = online_client.constants(); let constant_client = online_client.constants();
constant_client.validate(constant_query).expect("constant query should be correct; qed"); constant_client
.validate(constant_query)
.expect("constant query should be correct; qed");
constant_client.at(constant_query) constant_client.at(constant_query)
} }

View File

@ -1,8 +1,5 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{client::OnlineClient, utils::H256};
utils::H256,
client::OnlineClient,
};
use crate::{ use crate::{
casper_network::{ casper_network::{
@ -16,8 +13,11 @@ pub async fn bridged_imbalance(
online_client: &OnlineClient<CasperConfig>, online_client: &OnlineClient<CasperConfig>,
at_hash: Option<&H256>, at_hash: Option<&H256>,
) -> Result<Option<BridgeAdjustment<u128>>> { ) -> Result<Option<BridgeAdjustment<u128>>> {
let storage_key = casper_network::storage().ghost_networks().bridged_imbalance(); let storage_key = casper_network::storage()
let maybe_bridged_imbalance = super::do_storage_call(online_client, &storage_key, at_hash).await?; .ghost_networks()
.bridged_imbalance();
let maybe_bridged_imbalance =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_bridged_imbalance) Ok(maybe_bridged_imbalance)
} }
@ -25,8 +25,11 @@ pub async fn accumulated_commission(
online_client: &OnlineClient<CasperConfig>, online_client: &OnlineClient<CasperConfig>,
at_hash: Option<&H256>, at_hash: Option<&H256>,
) -> Result<Option<u128>> { ) -> Result<Option<u128>> {
let storage_key = casper_network::storage().ghost_networks().accumulated_commission(); let storage_key = casper_network::storage()
let maybe_accumulated_commission = super::do_storage_call(online_client, &storage_key, at_hash).await?; .ghost_networks()
.accumulated_commission();
let maybe_accumulated_commission =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_accumulated_commission) Ok(maybe_accumulated_commission)
} }
@ -35,7 +38,9 @@ pub async fn networks(
at_hash: Option<&H256>, at_hash: Option<&H256>,
chain_id: u64, chain_id: u64,
) -> Result<Option<NetworkData>> { ) -> Result<Option<NetworkData>> {
let storage_key = casper_network::storage().ghost_networks().networks(chain_id); let storage_key = casper_network::storage()
.ghost_networks()
.networks(chain_id);
let maybe_network = super::do_storage_call(online_client, &storage_key, at_hash).await?; let maybe_network = super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_network) Ok(maybe_network)
} }

View File

@ -1,10 +1,13 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{
utils::{AccountId32, H256},
client::OnlineClient, client::OnlineClient,
utils::{AccountId32, H256},
}; };
use crate::{casper_network::{self, runtime_types::casper_runtime::opaque}, CasperConfig}; use crate::{
casper_network::{self, runtime_types::casper_runtime::opaque},
CasperConfig,
};
pub async fn validators( pub async fn validators(
online_client: &OnlineClient<CasperConfig>, online_client: &OnlineClient<CasperConfig>,

View File

@ -9,8 +9,8 @@ use crate::{
self, self,
runtime_types::{ runtime_types::{
pallet_staking::{ pallet_staking::{
slashing::SlashingSpans, ActiveEraInfo, EraRewardPoints, slashing::SlashingSpans, ActiveEraInfo, EraRewardPoints, Nominations,
RewardDestination, StakingLedger, ValidatorPrefs, Nominations, RewardDestination, StakingLedger, ValidatorPrefs,
}, },
sp_arithmetic::per_things::Perbill, sp_arithmetic::per_things::Perbill,
sp_staking::{ExposurePage, PagedExposureMetadata}, sp_staking::{ExposurePage, PagedExposureMetadata},
@ -42,7 +42,8 @@ pub async fn counter_for_validators(
at_hash: Option<&H256>, at_hash: Option<&H256>,
) -> Result<Option<u32>> { ) -> Result<Option<u32>> {
let storage_key = casper_network::storage().staking().counter_for_validators(); let storage_key = casper_network::storage().staking().counter_for_validators();
let maybe_counter_for_validators = super::do_storage_call(online_client, &storage_key, at_hash).await?; let maybe_counter_for_validators =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_counter_for_validators) Ok(maybe_counter_for_validators)
} }
@ -51,7 +52,8 @@ pub async fn counter_for_nominators(
at_hash: Option<&H256>, at_hash: Option<&H256>,
) -> Result<Option<u32>> { ) -> Result<Option<u32>> {
let storage_key = casper_network::storage().staking().counter_for_nominators(); let storage_key = casper_network::storage().staking().counter_for_nominators();
let maybe_counter_for_nominators = super::do_storage_call(online_client, &storage_key, at_hash).await?; let maybe_counter_for_nominators =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_counter_for_nominators) Ok(maybe_counter_for_nominators)
} }
@ -62,7 +64,9 @@ pub async fn nominators(
) -> Result<Option<Nominations>> { ) -> Result<Option<Nominations>> {
let account_id = super::convert_array_to_account_id(account); let account_id = super::convert_array_to_account_id(account);
let storage_key = casper_network::storage().staking().nominators(account_id); let storage_key = casper_network::storage().staking().nominators(account_id);
let maybe_nominators = super::do_storage_call(online_client, &storage_key, at_hash).await.unwrap(); let maybe_nominators = super::do_storage_call(online_client, &storage_key, at_hash)
.await
.unwrap();
Ok(maybe_nominators) Ok(maybe_nominators)
} }
@ -71,8 +75,11 @@ pub async fn eras_total_stake(
at_hash: Option<&H256>, at_hash: Option<&H256>,
era_index: u32, era_index: u32,
) -> Result<Option<u128>> { ) -> Result<Option<u128>> {
let storage_key = casper_network::storage().staking().eras_total_stake(era_index); let storage_key = casper_network::storage()
let maybe_eras_total_stake = super::do_storage_call(online_client, &storage_key, at_hash).await?; .staking()
.eras_total_stake(era_index);
let maybe_eras_total_stake =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_eras_total_stake) Ok(maybe_eras_total_stake)
} }
@ -81,8 +88,11 @@ pub async fn eras_validator_reward(
at_hash: Option<&H256>, at_hash: Option<&H256>,
era_index: u32, era_index: u32,
) -> Result<Option<u128>> { ) -> Result<Option<u128>> {
let storage_key = casper_network::storage().staking().eras_validator_reward(era_index); let storage_key = casper_network::storage()
let maybe_eras_validator_reward = super::do_storage_call(online_client, &storage_key, at_hash).await?; .staking()
.eras_validator_reward(era_index);
let maybe_eras_validator_reward =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_eras_validator_reward) Ok(maybe_eras_validator_reward)
} }
@ -91,8 +101,11 @@ pub async fn eras_reward_points(
at_hash: Option<&H256>, at_hash: Option<&H256>,
era_index: u32, era_index: u32,
) -> Result<Option<EraRewardPoints<AccountId32>>> { ) -> Result<Option<EraRewardPoints<AccountId32>>> {
let storage_key = casper_network::storage().staking().eras_reward_points(era_index); let storage_key = casper_network::storage()
let maybe_eras_reward_points = super::do_storage_call(online_client, &storage_key, at_hash).await?; .staking()
.eras_reward_points(era_index);
let maybe_eras_reward_points =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_eras_reward_points) Ok(maybe_eras_reward_points)
} }
@ -103,8 +116,11 @@ pub async fn claimed_rewards(
account: &[u8; 32], account: &[u8; 32],
) -> Result<Option<Vec<u32>>> { ) -> Result<Option<Vec<u32>>> {
let account_id = super::convert_array_to_account_id(account); let account_id = super::convert_array_to_account_id(account);
let storage_key = casper_network::storage().staking().claimed_rewards(era_index, account_id); let storage_key = casper_network::storage()
let maybe_claimed_rewards = super::do_storage_call(online_client, &storage_key, at_hash).await?; .staking()
.claimed_rewards(era_index, account_id);
let maybe_claimed_rewards =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_claimed_rewards) Ok(maybe_claimed_rewards)
} }
@ -115,8 +131,11 @@ pub async fn validator_slash_in_era(
account: &[u8; 32], account: &[u8; 32],
) -> Result<Option<(Perbill, u128)>> { ) -> Result<Option<(Perbill, u128)>> {
let account_id = super::convert_array_to_account_id(account); let account_id = super::convert_array_to_account_id(account);
let storage_key = casper_network::storage().staking().validator_slash_in_era(era_index, account_id); let storage_key = casper_network::storage()
let maybe_validator_slash_in_era = super::do_storage_call(online_client, &storage_key, at_hash).await?; .staking()
.validator_slash_in_era(era_index, account_id);
let maybe_validator_slash_in_era =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_validator_slash_in_era) Ok(maybe_validator_slash_in_era)
} }
@ -139,8 +158,11 @@ pub async fn eras_stakers_paged(
account: &[u8; 32], account: &[u8; 32],
) -> Result<Option<ExposurePage<AccountId32, u128>>> { ) -> Result<Option<ExposurePage<AccountId32, u128>>> {
let account_id = super::convert_array_to_account_id(account); let account_id = super::convert_array_to_account_id(account);
let storage_key = casper_network::storage().staking().eras_stakers_paged(era_index, account_id, page_index); let storage_key = casper_network::storage()
let maybe_eras_stakers_paged = super::do_storage_call(online_client, &storage_key, at_hash).await?; .staking()
.eras_stakers_paged(era_index, account_id, page_index);
let maybe_eras_stakers_paged =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_eras_stakers_paged) Ok(maybe_eras_stakers_paged)
} }
@ -162,8 +184,11 @@ pub async fn eras_stakers_overview(
account: &[u8; 32], account: &[u8; 32],
) -> Result<Option<PagedExposureMetadata<u128>>> { ) -> Result<Option<PagedExposureMetadata<u128>>> {
let account_id = super::convert_array_to_account_id(account); let account_id = super::convert_array_to_account_id(account);
let storage_key = casper_network::storage().staking().eras_stakers_overview(era_index, account_id); let storage_key = casper_network::storage()
let maybe_eras_stakers_overview = super::do_storage_call(online_client, &storage_key, at_hash).await?; .staking()
.eras_stakers_overview(era_index, account_id);
let maybe_eras_stakers_overview =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_eras_stakers_overview) Ok(maybe_eras_stakers_overview)
} }
@ -183,7 +208,8 @@ pub async fn disabled_validators(
at_hash: Option<&H256>, at_hash: Option<&H256>,
) -> Result<Option<Vec<u32>>> { ) -> Result<Option<Vec<u32>>> {
let storage_key = casper_network::storage().staking().disabled_validators(); let storage_key = casper_network::storage().staking().disabled_validators();
let maybe_disabled_validators = super::do_storage_call(online_client, &storage_key, at_hash).await?; let maybe_disabled_validators =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_disabled_validators) Ok(maybe_disabled_validators)
} }
@ -192,7 +218,8 @@ pub async fn min_validator_bond(
at_hash: Option<&H256>, at_hash: Option<&H256>,
) -> Result<Option<u128>> { ) -> Result<Option<u128>> {
let storage_key = casper_network::storage().staking().min_validator_bond(); let storage_key = casper_network::storage().staking().min_validator_bond();
let maybe_min_validator_bond = super::do_storage_call(online_client, &storage_key, at_hash).await?; let maybe_min_validator_bond =
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_min_validator_bond) Ok(maybe_min_validator_bond)
} }
@ -202,7 +229,9 @@ pub async fn slashing_spans(
account: &[u8; 32], account: &[u8; 32],
) -> Result<Option<SlashingSpans>> { ) -> Result<Option<SlashingSpans>> {
let account_id = super::convert_array_to_account_id(account); let account_id = super::convert_array_to_account_id(account);
let storage_key = casper_network::storage().staking().slashing_spans(account_id); let storage_key = casper_network::storage()
.staking()
.slashing_spans(account_id);
let maybe_slashing_spans = super::do_storage_call(online_client, &storage_key, at_hash).await?; let maybe_slashing_spans = super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_slashing_spans) Ok(maybe_slashing_spans)
} }
@ -218,9 +247,7 @@ pub async fn payee(
Ok(maybe_payee) Ok(maybe_payee)
} }
pub fn history_depth( pub fn history_depth(online_client: &OnlineClient<CasperConfig>) -> Result<u32> {
online_client: &OnlineClient<CasperConfig>,
) -> Result<u32> {
let constant_query = casper_network::constants().staking().history_depth(); let constant_query = casper_network::constants().staking().history_depth();
let history_depth = super::do_constant_call(online_client, &constant_query)?; let history_depth = super::do_constant_call(online_client, &constant_query)?;
Ok(history_depth) Ok(history_depth)

View File

@ -1,8 +1,5 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{client::OnlineClient, utils::H256};
utils::H256,
client::OnlineClient,
};
use crate::{ use crate::{
casper_network::{ casper_network::{

View File

@ -1,8 +1,4 @@
use crate::{ use crate::{action::Action, casper::CasperBlock, types::CasperExtrinsicDetails};
types::CasperExtrinsicDetails,
action::Action,
casper::CasperBlock,
};
use color_eyre::Result; use color_eyre::Result;
use super::GATEKEEPED_CHAIN_IDS; use super::GATEKEEPED_CHAIN_IDS;
@ -19,7 +15,11 @@ impl FinalizedSubscription {
network_tx: std::sync::mpsc::Sender<Action>, network_tx: std::sync::mpsc::Sender<Action>,
finalized_blocks_sub: subxt::backend::StreamOfResults<CasperBlock>, finalized_blocks_sub: subxt::backend::StreamOfResults<CasperBlock>,
) -> Self { ) -> Self {
Self { action_tx, network_tx, finalized_blocks_sub } Self {
action_tx,
network_tx,
finalized_blocks_sub,
}
} }
pub async fn subscribe_finalized_blocks(&mut self) -> Result<()> { pub async fn subscribe_finalized_blocks(&mut self) -> Result<()> {
@ -52,12 +52,18 @@ impl FinalizedSubscription {
)); ));
} }
self.action_tx.send(Action::FinalizedBlockInformation(block_hash, block_number))?; self.action_tx
self.action_tx.send(Action::ExtrinsicsForBlock(block_number, extrinsic_details))?; .send(Action::FinalizedBlockInformation(block_hash, block_number))?;
self.action_tx.send(Action::NewFinalizedBlock(block_number))?; self.action_tx
.send(Action::ExtrinsicsForBlock(block_number, extrinsic_details))?;
self.action_tx
.send(Action::NewFinalizedBlock(block_number))?;
self.network_tx.send(Action::NewFinalizedHash(block_hash))?; self.network_tx.send(Action::NewFinalizedHash(block_hash))?;
self.network_tx.send(Action::GetBlockAuthor(block_hash, block.header().digest.logs.clone()))?; self.network_tx.send(Action::GetBlockAuthor(
block_hash,
block.header().digest.logs.clone(),
))?;
} }
Ok(()) Ok(())
} }
@ -75,7 +81,11 @@ impl BestSubscription {
network_tx: std::sync::mpsc::Sender<Action>, network_tx: std::sync::mpsc::Sender<Action>,
best_blocks_sub: subxt::backend::StreamOfResults<CasperBlock>, best_blocks_sub: subxt::backend::StreamOfResults<CasperBlock>,
) -> Self { ) -> Self {
Self { action_tx, network_tx, best_blocks_sub } Self {
action_tx,
network_tx,
best_blocks_sub,
}
} }
pub async fn subscribe_best_blocks(&mut self) -> Result<()> { pub async fn subscribe_best_blocks(&mut self) -> Result<()> {
@ -109,14 +119,21 @@ impl BestSubscription {
)); ));
} }
self.action_tx.send(Action::BestBlockInformation(block_hash, block_number))?; self.action_tx
self.action_tx.send(Action::ExtrinsicsForBlock(block_number, extrinsic_details))?; .send(Action::BestBlockInformation(block_hash, block_number))?;
self.action_tx.send(Action::BestBlockUpdated(block_number))?; self.action_tx
.send(Action::ExtrinsicsForBlock(block_number, extrinsic_details))?;
self.action_tx
.send(Action::BestBlockUpdated(block_number))?;
self.action_tx.send(Action::NewBestBlock(block_number))?; self.action_tx.send(Action::NewBestBlock(block_number))?;
self.action_tx.send(Action::ExtrinsicsLength(block_number, extrinsics_length))?; self.action_tx
.send(Action::ExtrinsicsLength(block_number, extrinsics_length))?;
self.network_tx.send(Action::NewBestHash(block_hash))?; self.network_tx.send(Action::NewBestHash(block_hash))?;
self.network_tx.send(Action::GetBlockAuthor(block_hash, block.header().digest.logs.clone()))?; self.network_tx.send(Action::GetBlockAuthor(
block_hash,
block.header().digest.logs.clone(),
))?;
self.network_tx.send(Action::GetActiveEra)?; self.network_tx.send(Action::GetActiveEra)?;
self.network_tx.send(Action::GetCurrentEra)?; self.network_tx.send(Action::GetCurrentEra)?;
self.network_tx.send(Action::GetEpochProgress)?; self.network_tx.send(Action::GetEpochProgress)?;
@ -124,11 +141,13 @@ impl BestSubscription {
self.network_tx.send(Action::GetValidatorsNumber)?; self.network_tx.send(Action::GetValidatorsNumber)?;
self.network_tx.send(Action::GetNominatorsNumber)?; self.network_tx.send(Action::GetNominatorsNumber)?;
self.network_tx.send(Action::GetInflation)?; self.network_tx.send(Action::GetInflation)?;
self.network_tx.send(Action::GetCurrentValidatorEraRewards)?; self.network_tx
.send(Action::GetCurrentValidatorEraRewards)?;
self.network_tx.send(Action::GetMinValidatorBond)?; self.network_tx.send(Action::GetMinValidatorBond)?;
for chain_id in GATEKEEPED_CHAIN_IDS { for chain_id in GATEKEEPED_CHAIN_IDS {
self.network_tx.send(Action::GetGatekeepedNetwork(chain_id))?; self.network_tx
.send(Action::GetGatekeepedNetwork(chain_id))?;
} }
} }
Ok(()) Ok(())

View File

@ -1,4 +1,4 @@
use ratatui::style::{Style, Color}; use ratatui::style::{Color, Style};
use ratatui::widgets::block::BorderType; use ratatui::widgets::block::BorderType;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -108,12 +108,18 @@ impl StylePalette {
pub fn create_border_style(&self, active: bool) -> (Color, BorderType) { pub fn create_border_style(&self, active: bool) -> (Color, BorderType) {
if active { if active {
( (
self.hover_border_style.map(|style| style.fg).flatten().unwrap_or_default(), self.hover_border_style
.map(|style| style.fg)
.flatten()
.unwrap_or_default(),
self.hover_border_type, self.hover_border_type,
) )
} else { } else {
( (
self.normal_border_style.map(|style| style.fg).flatten().unwrap_or_default(), self.normal_border_style
.map(|style| style.fg)
.flatten()
.unwrap_or_default(),
self.normal_border_type, self.normal_border_type,
) )
} }
@ -129,7 +135,10 @@ impl StylePalette {
pub fn create_popup_style(&self) -> (Color, BorderType) { pub fn create_popup_style(&self) -> (Color, BorderType) {
( (
self.popup_style.map(|style| style.fg).flatten().unwrap_or_default(), self.popup_style
.map(|style| style.fg)
.flatten()
.unwrap_or_default(),
self.popup_border_type, self.popup_border_type,
) )
} }

View File

@ -8,15 +8,14 @@ use color_eyre::Result;
use crossterm::{ use crossterm::{
cursor, cursor,
event::{ event::{
DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture,
EnableMouseCapture, Event as CrosstermEvent, EventStream, KeyEvent, Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind, MouseEvent,
KeyEventKind, MouseEvent,
}, },
terminal::{EnterAlternateScreen, LeaveAlternateScreen}, terminal::{EnterAlternateScreen, LeaveAlternateScreen},
}; };
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use ratatui::backend::CrosstermBackend as Backend; use ratatui::backend::CrosstermBackend as Backend;
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
use tokio::{ use tokio::{
sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
task::JoinHandle, task::JoinHandle,

View File

@ -1,5 +1,5 @@
use codec::Decode; use codec::Decode;
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct SystemAccount { pub struct SystemAccount {

View File

@ -1,5 +1,5 @@
use codec::Decode; use codec::Decode;
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct EraInfo { pub struct EraInfo {

View File

@ -1,4 +1,4 @@
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
use subxt::utils::H256; use subxt::utils::H256;
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,23 +1,23 @@
mod account;
mod era; mod era;
mod extrinsics; mod extrinsics;
mod log; mod log;
mod account; mod networks;
mod nominator;
mod peer; mod peer;
mod session; mod session;
mod nominator;
mod staking; mod staking;
mod networks;
pub use account::SystemAccount;
pub use era::{EraInfo, EraRewardPoints};
pub use extrinsics::CasperExtrinsicDetails; pub use extrinsics::CasperExtrinsicDetails;
pub use era::{EraRewardPoints, EraInfo};
pub use log::ActionLevel; pub use log::ActionLevel;
pub use log::ActionTarget; pub use log::ActionTarget;
pub use account::SystemAccount; pub use networks::BlockRange;
pub use networks::Gatekeeper;
pub use nominator::Nominations;
pub use nominator::Nominator;
pub use peer::PeerInformation; pub use peer::PeerInformation;
pub use session::SessionKeyInfo; pub use session::SessionKeyInfo;
pub use nominator::Nominator;
pub use nominator::Nominations;
pub use staking::UnlockChunk;
pub use staking::RewardDestination; pub use staking::RewardDestination;
pub use networks::Gatekeeper; pub use staking::UnlockChunk;
pub use networks::BlockRange;

View File

@ -1,5 +1,5 @@
use codec::Decode; use codec::Decode;
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct Gatekeeper { pub struct Gatekeeper {

View File

@ -1,5 +1,5 @@
use codec::Decode; use codec::Decode;
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct Nominator { pub struct Nominator {

View File

@ -1,6 +1,6 @@
use subxt::utils::H256;
use codec::Decode; use codec::Decode;
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
use subxt::utils::H256;
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]

View File

@ -1,5 +1,5 @@
use codec::Decode; use codec::Decode;
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct SessionKeyInfo { pub struct SessionKeyInfo {

View File

@ -1,6 +1,6 @@
use codec::Decode; use codec::Decode;
use serde::{Deserialize, Serialize};
use strum::Display; use strum::Display;
use serde::{Serialize, Deserialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct UnlockChunk { pub struct UnlockChunk {

View File

@ -1,5 +1,6 @@
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] #[derive(
#[derive(serde::Serialize, serde::Deserialize)] Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize,
)]
pub enum InputRequest { pub enum InputRequest {
SetCursor(usize), SetCursor(usize),
InsertChar(char), InsertChar(char),
@ -17,8 +18,9 @@ pub enum InputRequest {
DeleteTillEnd, DeleteTillEnd,
} }
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] #[derive(
#[derive(serde::Serialize, serde::Deserialize)] Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct StateChanged { pub struct StateChanged {
pub value: bool, pub value: bool,
pub cursor: bool, pub cursor: bool,
@ -26,8 +28,7 @@ pub struct StateChanged {
pub type InputResponse = Option<StateChanged>; pub type InputResponse = Option<StateChanged>;
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Input { pub struct Input {
value: String, value: String,
cursor: usize, cursor: usize,
@ -78,10 +79,7 @@ impl Input {
.value .value
.chars() .chars()
.take(self.cursor) .take(self.cursor)
.chain( .chain(std::iter::once(c).chain(self.value.chars().skip(self.cursor)))
std::iter::once(c)
.chain(self.value.chars().skip(self.cursor)),
)
.collect(); .collect();
} }
self.cursor += 1; self.cursor += 1;

View File

@ -1,14 +1,14 @@
mod big_text;
mod dot_spinner; mod dot_spinner;
mod input;
mod ogham; mod ogham;
mod vertical_block; mod vertical_block;
mod big_text;
mod input;
pub use dot_spinner::DotSpinner;
pub use vertical_block::VerticalBlocks;
pub use ogham::OghamCenter;
pub use big_text::BigText; pub use big_text::BigText;
pub use big_text::PixelSize; pub use big_text::PixelSize;
pub use dot_spinner::DotSpinner;
pub use input::{Input, InputRequest}; pub use input::{Input, InputRequest};
pub use ogham::OghamCenter;
pub use vertical_block::VerticalBlocks;
const CYCLE: i64 = 1560; const CYCLE: i64 = 1560;

Some files were not shown because too many files have changed in this diff Show More