Compare commits

..

No commits in common. "bae21bb50529ff3fa4fab08eea23b3b7dfa9dab2" and "718ae51deea72bae91ff9a4df9740beecce27e01" have entirely different histories.

101 changed files with 3194 additions and 5057 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.76" version = "0.3.73"
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::config::substrate::DigestItem;
use subxt::utils::H256; use subxt::utils::H256;
use subxt::config::substrate::DigestItem;
use crate::types::{ use crate::types::{
ActionLevel, ActionTarget, BlockRange, CasperExtrinsicDetails, EraInfo, EraRewardPoints, ActionLevel, ActionTarget, CasperExtrinsicDetails, EraInfo, EraRewardPoints,
Gatekeeper, Nominations, Nominator, PeerInformation, RewardDestination, SessionKeyInfo, Nominator, Nominations, PeerInformation, SessionKeyInfo, UnlockChunk, SystemAccount,
SystemAccount, UnlockChunk, RewardDestination, Gatekeeper, BlockRange,
}; };
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
@ -39,7 +39,6 @@ pub enum Action {
PayoutAllValidatorPopup(Vec<u32>), PayoutAllValidatorPopup(Vec<u32>),
WithdrawValidatorPopup, WithdrawValidatorPopup,
ChangeBlocksPopup, ChangeBlocksPopup,
GatekeeperEndpoints,
BalanceRequest([u8; 32], bool), BalanceRequest([u8; 32], bool),
BalanceResponse([u8; 32], Option<SystemAccount>), BalanceResponse([u8; 32], Option<SystemAccount>),
@ -119,7 +118,6 @@ pub enum Action {
GetStakingPayee([u8; 32], bool), GetStakingPayee([u8; 32], bool),
GetValidatorIsDisabled([u8; 32], bool), GetValidatorIsDisabled([u8; 32], bool),
GetCurrentValidatorEraRewards, GetCurrentValidatorEraRewards,
GetRpcEndpoints(u64),
SetNodeName(Option<String>), SetNodeName(Option<String>),
SetSystemHealth(Option<usize>, bool, bool), SetSystemHealth(Option<usize>, bool, bool),
@ -132,8 +130,6 @@ pub enum Action {
SetChoosenValidator([u8; 32], u32, u32), SetChoosenValidator([u8; 32], u32, u32),
SetChoosenGatekeeper(u64), SetChoosenGatekeeper(u64),
SetSlashingSpansLength(usize, [u8; 32]), SetSlashingSpansLength(usize, [u8; 32]),
SetStoredRpcEndpoints(Vec<String>),
UpdateStoredRpcEndpoints(u64, Vec<String>),
BestBlockInformation(H256, u32), BestBlockInformation(H256, u32),
FinalizedBlockInformation(H256, u32), FinalizedBlockInformation(H256, u32),

View File

@ -1,18 +1,19 @@
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;
@ -189,9 +190,7 @@ 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 => { Action::Tick => { self.last_tick_key_events.drain(..); },
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,
@ -227,7 +226,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()) {
@ -236,7 +235,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()) {
@ -245,7 +244,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()) {
@ -254,7 +253,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,10 +1,8 @@
//! Casper specific configuration //! Casper specific configuration
use subxt::{ use subxt::{
blocks::Block, Config, blocks::Block, client::OnlineClient,
client::OnlineClient, config::{DefaultExtrinsicParamsBuilder, DefaultExtrinsicParams, SubstrateConfig},
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::{KeyCode, KeyEvent}; use crossterm::event::{KeyEvent, KeyCode};
use ratatui::{ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
text::Line, text::Line,
@ -8,7 +8,10 @@ use ratatui::{
}; };
use super::Component; use super::Component;
use crate::{action::Action, app::Mode, components::generic::Activatable}; use crate::{
components::generic::Activatable,
action::Action, app::Mode
};
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Empty { pub struct Empty {
@ -16,18 +19,10 @@ pub struct Empty {
} }
impl Activatable for Empty { impl Activatable for Empty {
fn is_active(&self) -> bool { fn is_active(&self) -> bool { self.is_active }
self.is_active fn is_inactive(&self) -> bool { !self.is_active }
} fn set_inactive(&mut self) { self.is_active = false; }
fn is_inactive(&self) -> bool { fn set_active(&mut self) { self.is_active = true; }
!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 {
@ -130,7 +125,10 @@ impl Component for Empty {
}; };
let lines_len = lines.len() as u16; let lines_len = lines.len() as u16;
let padding_top = screen.as_size().height.saturating_sub(lines_len) / 2; let padding_top = screen
.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,19 +3,17 @@ 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::{
layout::{Alignment, Rect},
text::Text, text::Text,
widgets::{Block, Cell, Row, ScrollbarState, Table, TableState}, layout::{Alignment, Rect},
Frame, widgets::{Block, ScrollbarState, Cell, Row, Table, TableState},
Frame
}; };
use subxt::utils::H256; use subxt::utils::H256;
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
action::Action, components::generic::{Activatable, Scrollable, PartialComponent},
components::generic::{Activatable, PartialComponent, Scrollable}, action::Action, config::Config, palette::StylePalette,
config::Config,
palette::StylePalette,
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -41,18 +39,10 @@ impl Default for BlockExplorer {
} }
impl Activatable for BlockExplorer { impl Activatable for BlockExplorer {
fn is_active(&self) -> bool { fn is_active(&self) -> bool { self.is_active }
self.is_active fn is_inactive(&self) -> bool { !self.is_active }
} fn set_inactive(&mut self) { self.is_active = false; }
fn is_inactive(&self) -> bool { fn set_active(&mut self) { self.is_active = true; }
!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 {
@ -111,8 +101,7 @@ impl BlockExplorer {
hash: H256, hash: H256,
block_number: u32, block_number: u32,
) -> Result<Option<Action>> { ) -> Result<Option<Action>> {
let front_block_number = self let front_block_number = self.blocks
.blocks
.front() .front()
.map(|block| block.block_number) .map(|block| block.block_number)
.unwrap_or_default(); .unwrap_or_default();
@ -136,7 +125,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)
} }
@ -147,11 +136,9 @@ 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 { if self.blocks[idx].finalized { break; }
break; else if self.blocks[idx].block_number > block_number { continue; }
} else if self.blocks[idx].block_number > block_number { else {
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;
} }
@ -174,7 +161,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);
} },
} }
} }
} }
@ -182,38 +169,26 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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) => { Action::BestBlockInformation(header, block_number) => self.update_latest_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::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),
} }
} }
@ -231,30 +206,25 @@ 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 let rows = self.blocks
.blocks
.iter() .iter()
.map(|info| { .map(|info| {
let header = self let header = self.block_headers
.block_headers
.get(&info.block_number) .get(&info.block_number)
.unwrap_or(&default_hash); .unwrap_or(&default_hash);
let author = self.block_authors.get(&header).unwrap_or(&default_author); let author = self.block_authors
.get(&header)
.unwrap_or(&default_author);
if info.finalized { if info.finalized {
Row::new(vec![ Row::new(vec![
Cell::from( Cell::from(Text::from(info.block_number.to_string()).alignment(Alignment::Left)),
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( Cell::from(Text::from(info.block_number.to_string()).alignment(Alignment::Left)),
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)),
]) ])
@ -262,14 +232,12 @@ impl Component for BlockExplorer {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let max_block_number_length = self let max_block_number_length = self.blocks
.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 .unwrap_or(0) as u16 + 1;
+ 1;
let table = Table::new( let table = Table::new(
rows, rows,
@ -282,15 +250,13 @@ 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(Block::bordered()
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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title("Blocks"), .title("Blocks"));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -301,10 +267,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -4,15 +4,13 @@ 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, config::Config, action::Action, palette::StylePalette,
palette::StylePalette,
widgets::{BigText, PixelSize}, widgets::{BigText, PixelSize},
}; };
@ -20,7 +18,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 {
@ -51,18 +49,12 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_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(())
} }
@ -96,15 +88,13 @@ 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(Block::bordered()
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))
.padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0)) .padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0))
.title("Passed"), .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);
@ -113,27 +103,23 @@ 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![text.into()]) .lines(vec![
text.into(),
])
.build(); .build();
let paragraph = Paragraph::new("") let paragraph = Paragraph::new("")
.block( .block(Block::bordered()
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("Passed"), .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 let place = place.offset(ratatui::layout::Offset { x: 1, y: height_offset as i32 })
.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,11 +8,9 @@ use ratatui::{
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
action::Action,
components::generic::PartialComponent, components::generic::PartialComponent,
config::Config, config::Config, action::Action, palette::StylePalette,
palette::StylePalette, widgets::{PixelSize, BigText},
widgets::{BigText, PixelSize},
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -34,18 +32,12 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_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(())
} }
@ -55,7 +47,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)
@ -64,8 +56,7 @@ 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 = let seconds_to_next = Self::SESSION_LENGTH.saturating_sub(self.progress) * Self::SECONDS_IN_BLOCK;
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;
@ -82,24 +73,20 @@ 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 { 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" })),
)),
]; ];
let paragraph = Paragraph::new(text) let paragraph = 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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.padding(Padding::new(0, 0, height.saturating_sub(3) / 2, 0)) .padding(Padding::new(0, 0, height.saturating_sub(3) / 2, 0))
.title("Epoch"), .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);
@ -108,36 +95,28 @@ 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![text.into()]) .lines(vec![
text.into(),
])
.build(); .build();
let paragraph = Paragraph::new("") let paragraph = Paragraph::new("")
.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_title_style(false)) .title_style(self.palette.create_title_style(false))
.title_top(Line::from("Epoch").right_aligned()) .title_top(Line::from("Epoch").right_aligned())
.title_top( .title_top(Line::from(format!("{}{} {}{}",
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 let place = place.offset(ratatui::layout::Offset { x: 1, y: height_offset as i32 })
.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,16 +8,12 @@ use ratatui::{
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
action::Action, config::Config, action::Action, palette::StylePalette, types::EraInfo,
components::generic::PartialComponent, components::generic::PartialComponent, widgets::{PixelSize, BigText},
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,
} }
@ -36,18 +32,12 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_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(())
} }
@ -55,7 +45,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)
} }
@ -93,25 +83,21 @@ 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, reversed_char,
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" })),
)),
]; ];
let paragraph = Paragraph::new(text) let paragraph = 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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.padding(Padding::new(0, 0, (height.saturating_sub(3)) / 2, 0)) .padding(Padding::new(0, 0, (height.saturating_sub(3)) / 2, 0))
.title("Era"), .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);
@ -120,36 +106,28 @@ 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![text.into()]) .lines(vec![
text.into(),
])
.build(); .build();
let paragraph = Paragraph::new("") let paragraph = Paragraph::new("")
.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_title_style(false)) .title_style(self.palette.create_title_style(false))
.title_top(Line::from("Era").right_aligned()) .title_top(Line::from("Era").right_aligned())
.title_top( .title_top(Line::from(format!("{}{} {}{}",
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 let place = place.offset(ratatui::layout::Offset { x: 1, y: height_offset as i32 })
.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,23 +1,21 @@
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::{
layout::{Alignment, Rect},
text::Text, text::Text,
widgets::{Block, Cell, Row, ScrollbarState, Table, TableState}, layout::{Alignment, Rect},
Frame, widgets::{Block, ScrollbarState, Cell, Row, Table, TableState},
Frame
}; };
use std::collections::{HashMap, VecDeque};
use std::usize;
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
action::Action, components::generic::{Activatable, Scrollable, PartialComponent},
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 {
@ -37,18 +35,10 @@ impl Default for ExtrinsicExplorer {
} }
impl Activatable for ExtrinsicExplorer { impl Activatable for ExtrinsicExplorer {
fn is_active(&self) -> bool { fn is_active(&self) -> bool { self.is_active }
self.is_active fn is_inactive(&self) -> bool { !self.is_active }
} fn set_inactive(&mut self) { self.is_active = false; }
fn is_inactive(&self) -> bool { fn set_active(&mut self) { self.is_active = true; }
!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 {
@ -106,9 +96,10 @@ 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.get(&block_number).map(|exts| exts.to_vec()); let maybe_exts = self.extrinsics
let exts_length = self .get(&block_number)
.extrinsics .map(|exts| exts.to_vec());
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();
@ -117,6 +108,7 @@ 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,
@ -133,13 +125,11 @@ 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 let maybe_log = self.current_extrinsics
.current_extrinsics
.as_ref() .as_ref()
.map(|ext| { .map(|ext| ext.get(index).map(|ext| {
ext.get(index) hex::encode(&ext.field_bytes.clone())
.map(|ext| hex::encode(&ext.field_bytes.clone())) }))
})
.flatten(); .flatten();
Ok(Some(Action::UsedExplorerLog(maybe_log.clone()))) Ok(Some(Action::UsedExplorerLog(maybe_log.clone())))
} }
@ -160,35 +150,25 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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) =>
Action::ExtrinsicsForBlock(block_number, extrinsics) => { self.update_extrinsics_for_header(block_number, extrinsics),
self.update_extrinsics_for_header(block_number, extrinsics) _ => {},
}
_ => {}
}; };
Ok(None) Ok(None)
} }
@ -212,10 +192,8 @@ impl Component for ExtrinsicExplorer {
.iter() .iter()
.enumerate() .enumerate()
.map(|(idx, ext)| { .map(|(idx, ext)| {
longest_pallet_name_length = longest_pallet_name_length = longest_pallet_name_length.max(ext.pallet_name.len());
longest_pallet_name_length.max(ext.pallet_name.len()); longest_variant_name_length = longest_variant_name_length.max(ext.variant_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)),
@ -239,15 +217,13 @@ 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(Block::bordered()
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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title("Extrinsics"), .title("Extrinsics"));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -258,10 +234,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,3 +1,4 @@
use std::collections::VecDeque;
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
@ -5,11 +6,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::{
action::Action, components::generic::PartialComponent, config::Config, palette::StylePalette, components::generic::PartialComponent,
palette::StylePalette, config::Config, action::Action,
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -27,8 +28,7 @@ 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 let bars: Vec<Bar> = self.extrinsics
.extrinsics
.iter() .iter()
.rev() .rev()
.take(length) .take(length)
@ -37,14 +37,12 @@ impl ExtrinsicsChart {
BarChart::default() BarChart::default()
.data(BarGroup::default().bars(&bars)) .data(BarGroup::default().bars(&bars))
.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_title_style(false)) .title_style(self.palette.create_title_style(false))
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title("Tx. Heat Map"), .title("Tx. Heat Map"))
)
.bar_width(8) .bar_width(8)
.bar_gap(1) .bar_gap(1)
} }
@ -56,11 +54,7 @@ impl ExtrinsicsChart {
.text_value(ext_len.to_string()) .text_value(ext_len.to_string())
} }
fn update_extrinsics_length( fn update_extrinsics_length(&mut self, block_number: u32, extrinsics_length: usize) -> Result<()> {
&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 {
@ -69,7 +63,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));
} }
@ -84,27 +78,19 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_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) => { Action::ExtrinsicsLength(block, length) => self.update_extrinsics_length(block, length)?,
self.update_extrinsics_length(block, length)?
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)

View File

@ -7,11 +7,9 @@ use ratatui::{
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
action::Action,
components::generic::PartialComponent, components::generic::PartialComponent,
config::Config, config::Config, action::Action, palette::StylePalette,
palette::StylePalette, widgets::{PixelSize, BigText},
widgets::{BigText, PixelSize},
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -32,18 +30,12 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_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(())
} }
@ -68,15 +60,13 @@ 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(Block::bordered()
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))
.padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0)) .padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0))
.title("Finalized"), .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);
@ -85,27 +75,23 @@ 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![text.into()]) .lines(vec![
text.into(),
])
.build(); .build();
let paragraph = Paragraph::new("") let paragraph = Paragraph::new("")
.block( .block(Block::bordered()
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("Finalized"), .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 let place = place.offset(ratatui::layout::Offset { x: 1, y: height_offset as i32 })
.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,17 +7,15 @@ use ratatui::{
use super::{Component, CurrentTab}; use super::{Component, CurrentTab};
use crate::{ use crate::{
action::Action,
components::generic::PartialComponent, components::generic::PartialComponent,
config::Config, config::Config, action::Action, palette::StylePalette,
palette::StylePalette, widgets::{PixelSize, BigText},
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 {
@ -32,18 +30,12 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_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(())
} }
@ -68,15 +60,13 @@ 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(Block::bordered()
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))
.padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0)) .padding(Padding::new(0, 0, height.saturating_sub(2) / 2, 0))
.title("Latest"), .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);
@ -85,27 +75,23 @@ 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![text.into()]) .lines(vec![
text.into(),
])
.build(); .build();
let paragraph = Paragraph::new("") let paragraph = Paragraph::new("")
.block( .block(Block::bordered()
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("Latest"), .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 let place = place.offset(ratatui::layout::Offset { x: 1, y: height_offset as i32 })
.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,18 +5,23 @@ 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([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(header) Layout::horizontal([
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([Constraint::Percentage(100), Constraint::Percentage(100)]).areas(info) Layout::vertical([
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] {
@ -25,19 +30,21 @@ pub fn explorer_block_info_layout(area: Rect) -> [Rect; 3] {
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([Constraint::Percentage(50), Constraint::Percentage(50)]) Layout::horizontal([
.flex(Flex::SpaceBetween) Constraint::Percentage(50),
.areas(blocks) Constraint::Percentage(50),
]).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([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(place) Layout::horizontal([
Constraint::Percentage(50),
Constraint::Percentage(50),
]).areas(place)
} }

View File

@ -3,12 +3,13 @@ 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::{
action::Action, components::generic::PartialComponent, config::Config, palette::StylePalette, components::generic::PartialComponent,
action::Action, config::Config, palette::StylePalette
}; };
#[derive(Default)] #[derive(Default)]
@ -22,12 +23,9 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
} }
Ok(()) Ok(())
} }
@ -35,7 +33,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)
} }
@ -51,14 +49,12 @@ impl Component for LogExplorer {
}; };
let paragraph = Paragraph::new(line) let paragraph = Paragraph::new(line)
.block( .block(Block::bordered()
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("Logs"), .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};
mod block_explorer;
mod block_ticker;
mod current_epoch;
mod current_era;
mod extrinsic_explorer;
mod extrinsics_chart;
mod finalized_block;
mod latest_block;
pub mod layouts; pub mod layouts;
mod latest_block;
mod finalized_block;
mod block_ticker;
mod current_era;
mod current_epoch;
mod extrinsics_chart;
mod block_explorer;
mod extrinsic_explorer;
mod log_explorer; mod log_explorer;
use block_explorer::BlockExplorer;
use block_ticker::BlockTicker;
use current_epoch::CurrentEpoch;
use current_era::CurrentEra;
use extrinsic_explorer::ExtrinsicExplorer;
use extrinsics_chart::ExtrinsicsChart;
use finalized_block::FinalizedBlock;
use latest_block::LatestBlock; use latest_block::LatestBlock;
use finalized_block::FinalizedBlock;
use block_ticker::BlockTicker;
use current_era::CurrentEra;
use current_epoch::CurrentEpoch;
use extrinsics_chart::ExtrinsicsChart;
use block_explorer::BlockExplorer;
use extrinsic_explorer::ExtrinsicExplorer;
use log_explorer::LogExplorer; 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,18 +80,10 @@ impl Explorer {
} }
impl Activatable for Explorer { impl Activatable for Explorer {
fn is_active(&self) -> bool { fn is_active(&self) -> bool { self.is_active }
self.is_active fn is_inactive(&self) -> bool { !self.is_active }
} fn set_inactive(&mut self) { self.is_active = false; }
fn is_inactive(&self) -> bool { fn set_active(&mut self) { self.is_active = true; }
!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 {}
@ -114,9 +106,7 @@ 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() { if self.is_inactive() { return Ok(None); }
return Ok(None);
}
match key.code { match key.code {
KeyCode::Esc => { KeyCode::Esc => {
@ -126,19 +116,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);
@ -146,7 +136,7 @@ impl Component for Explorer {
return maybe_action; return maybe_action;
} }
} }
} },
} }
Ok(None) Ok(None)
} }

View File

@ -79,7 +79,8 @@ 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.frames_per_second self.ticks_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 crate::action::Action;
use color_eyre::Result; use color_eyre::Result;
use crate::action::Action;
use super::Activatable; use super::Activatable;

View File

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

View File

@ -1,11 +1,10 @@
use crossterm::event::KeyCode;
use std::{ use std::{
cmp::{PartialEq, PartialOrd}, cmp::{PartialEq, PartialOrd}, ops::{Add, Sub}
ops::{Add, Sub},
}; };
use crossterm::event::KeyCode;
use crate::action::Action;
use color_eyre::Result; use color_eyre::Result;
use crate::action::Action;
pub trait Scrollable { pub trait Scrollable {
type IndexType: Add<Output = Self::IndexType> type IndexType: Add<Output = Self::IndexType>
@ -18,18 +17,11 @@ 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>> { fn apply_next_row(&mut self, _new_index: Self::IndexType) -> Result<Option<Action>> { Ok(None) }
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_prev_row(&mut self, _new_index: Self::IndexType) -> Result<Option<Action>> { fn apply_last_row(&mut self) -> 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 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();
@ -44,7 +36,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)
@ -63,7 +55,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::{KeyCode, KeyEvent}; use crossterm::event::{KeyEvent, KeyCode};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Margin, Rect}, layout::{Alignment, Constraint, Flex, Layout, Margin, Rect},
style::{palette::tailwind, Modifier, Style}, style::{palette::tailwind, Style, Modifier},
text::Text, text::Text,
widgets::{ widgets::{
Block, BorderType, Cell, Clear, HighlightSpacing, Paragraph, Row, Scrollbar, Block, BorderType, Cell, Clear, HighlightSpacing, Paragraph, Row,
ScrollbarOrientation, ScrollbarState, Table, TableState, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, TableState,
}, },
Frame, Frame
}; };
use super::generic::{Activatable, Helpable, Scrollable};
use super::palette::StylePalette; use super::palette::StylePalette;
use super::generic::{Activatable, Helpable, Scrollable};
use super::Component; use super::Component;
use crate::{action::Action, app::Mode, config::Config}; use crate::{action::Action, app::Mode, config::Config};
@ -49,21 +49,13 @@ impl Default for Help {
} }
impl Activatable for Help { impl Activatable for Help {
fn is_active(&self) -> bool { fn is_active(&self) -> bool { self.is_active }
self.is_active fn is_inactive(&self) -> bool { !self.is_active }
} fn set_inactive(&mut self) { self.is_active = false; }
fn is_inactive(&self) -> bool { fn set_active(&mut self) { self.is_active = true; }
!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;
@ -88,16 +80,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -109,9 +96,7 @@ 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.scroll_state = ScrollbarState::new((self.current_mode.get_help_data().len() - 1) * ITEM_HEIGHT);
(self.current_mode.get_help_data().len() - 1) * ITEM_HEIGHT,
);
} }
} }
_ => {} _ => {}
@ -126,14 +111,18 @@ 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!["".into(), "".into(), "".into()]); let highlight_symbol = Text::from(vec![
"".into(),
"".into(),
"".into(),
]);
let table = Table::new( let table = Table::new(
self.current_mode self.current_mode
@ -146,46 +135,38 @@ impl Component for Help {
_ => tailwind::SLATE.c900, _ => tailwind::SLATE.c900,
}; };
Row::from( Row::from(content
content
.into_iter() .into_iter()
.map(|data| Cell::from(Text::from(*data))) .map(|data| Cell::from(Text::from(*data)))
.collect::<Row>() .collect::<Row>()
.style(Style::default().fg(tailwind::BLUE.c200).bg(color)) .style(Style::default().fg(tailwind::BLUE.c200).bg(color))
.height(ITEM_HEIGHT as u16), .height(ITEM_HEIGHT as u16))
)
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
self.current_mode.get_help_constraints(), self.current_mode.get_help_constraints()
) )
.header( .header(self.current_mode
self.current_mode
.get_help_headers() .get_help_headers()
.into_iter() .into_iter()
.map(|h| Cell::from(Text::from(*h))) .map(|h| Cell::from(Text::from(*h)))
.collect::<Row>() .collect::<Row>()
.style( .style(Style::default()
Style::default()
.fg(tailwind::SLATE.c200) .fg(tailwind::SLATE.c200)
.bg(tailwind::BLUE.c900), .bg(tailwind::BLUE.c900)
) )
.height(1), .height(1)
) )
.highlight_style( .highlight_style(Style::default()
Style::default()
.add_modifier(Modifier::REVERSED) .add_modifier(Modifier::REVERSED)
.fg(tailwind::BLUE.c400) .fg(tailwind::BLUE.c400)
.bg(tailwind::YELLOW.c200), .bg(tailwind::YELLOW.c200))
)
.highlight_spacing(HighlightSpacing::Always) .highlight_spacing(HighlightSpacing::Always)
.highlight_symbol(highlight_symbol) .highlight_symbol(highlight_symbol)
.block( .block(Block::default()
Block::default()
.style(Style::default().bg(tailwind::SLATE.c950)) .style(Style::default().bg(tailwind::SLATE.c950))
.title_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(tailwind::YELLOW.c200) .title_style(tailwind::YELLOW.c200)
.title(self.current_mode.get_help_title()), .title(self.current_mode.get_help_title()));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -193,16 +174,11 @@ 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(Style::default().fg(tailwind::SLATE.c200).bg(tailwind::SLATE.c950))
Style::default()
.fg(tailwind::SLATE.c200)
.bg(tailwind::SLATE.c950),
)
.centered() .centered()
.block( .block(Block::bordered()
Block::bordered()
.border_type(BorderType::Double) .border_type(BorderType::Double)
.border_style(Style::default().fg(tailwind::BLUE.c400)), .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);
@ -211,16 +187,14 @@ 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] = let [main_area, footer_area] = Layout::vertical([
Layout::vertical([Constraint::Fill(1), Constraint::Length(3)]).areas(area); Constraint::Fill(1),
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( frame.render_stateful_widget(scrollbar, main_area.inner(Margin::new(1, 2)), &mut self.scroll_state);
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,22 +1,34 @@
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([Constraint::Length(1), Constraint::Fill(1)]).areas(area) Layout::vertical([
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([Constraint::Fill(1), Constraint::Length(27)]).areas(header) Layout::horizontal([
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([Constraint::Max(30), Constraint::Fill(1)]).areas(main) Layout::horizontal([
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([Constraint::Min(0), Constraint::Length(5)]).areas(menu) Layout::vertical([
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::palette::StylePalette;
use super::Component; use super::Component;
use crate::{action::Action, app::Mode, config::Config}; use super::palette::StylePalette;
use crate::{config::Config, action::Action, app::Mode};
use super::generic::{Activatable, Helpable, Scrollable}; use super::generic::{Activatable, Helpable, Scrollable};
@ -16,27 +16,17 @@ pub struct Menu {
} }
impl Default for Menu { impl Default for Menu {
fn default() -> Self { fn default() -> Self { Menu::new() }
Menu::new()
}
} }
impl Activatable for Menu { impl Activatable for Menu {
fn is_active(&self) -> bool { fn is_active(&self) -> bool { self.is_active }
self.is_active fn is_inactive(&self) -> bool { !self.is_active }
} fn set_inactive(&mut self) { self.is_active = false; }
fn is_inactive(&self) -> bool { fn set_active(&mut self) { self.is_active = true; }
!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;
@ -72,6 +62,7 @@ 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
@ -99,16 +90,11 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_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_highlight_style(style.get("highlight_style").copied());
} }
Ok(()) Ok(())
} }
@ -125,7 +111,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),
} }
@ -140,13 +126,12 @@ impl Component for Menu {
.border_type(border_type); .border_type(border_type);
let list = List::default() let list = List::default()
.items( .items(self.items
self.items
.iter() .iter()
.map(|item| { .map(|item| ListItem::new(
ListItem::new(Line::raw(item.as_str()).alignment(Alignment::Center)) 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 std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use crate::{action::Action, config::Config, palette, tui::Event}; use crate::{palette, action::Action, config::Config, tui::Event};
pub mod empty;
pub mod explorer;
pub mod fps;
pub mod generic;
pub mod health;
pub mod help;
pub mod layouts; pub mod layouts;
pub mod fps;
pub mod health;
pub mod menu; pub mod menu;
pub mod validator;
pub mod version; pub mod version;
pub mod explorer;
pub mod wallet; pub mod wallet;
pub mod validator;
pub mod empty;
pub mod help;
pub mod generic;
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 color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame
}; };
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,7 +60,8 @@ 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(Action::EventLog(message, level, ActionTarget::ValidatorLog)); let _ = action_tx.send(
Action::EventLog(message, level, ActionTarget::ValidatorLog));
} }
} }
@ -77,25 +78,16 @@ 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( network_tx.send(Action::BondValidatorExtraFrom(self.stash_secret_seed, amount, log_target))
self.stash_secret_seed,
amount,
log_target,
))
} else { } else {
network_tx.send(Action::BondValidatorFrom( network_tx.send(Action::BondValidatorFrom(self.stash_secret_seed, amount, log_target))
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) => { Err(err) => self.log_event(
self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error) format!("invalid amount, error: {err}"), ActionLevel::Error),
}
} }
} }
} }
@ -145,16 +137,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -167,7 +154,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)
@ -175,9 +162,8 @@ 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,
@ -189,14 +175,13 @@ 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()).block( let input = Paragraph::new(self.amount.value())
Block::bordered() .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(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);
@ -206,7 +191,7 @@ impl Component for BondPopup {
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 color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, 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, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
palette::StylePalette, palette::StylePalette,
types::{ActionLevel, ActionTarget}, types::{ActionTarget, ActionLevel},
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 {
@ -98,16 +98,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -121,7 +116,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)
@ -138,14 +133,13 @@ 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()).block( let input = Paragraph::new(self.block_number.value())
Block::bordered() .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(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);
@ -155,7 +149,7 @@ impl Component for ChangeBlocksPopup {
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,15 +1,19 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
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, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)] #[derive(Debug)]
pub struct ChillPopup { pub struct ChillPopup {
@ -17,7 +21,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 {
@ -76,16 +80,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -94,7 +93,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)
@ -112,14 +111,12 @@ 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(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!("Chill stash")), .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,18 +5,15 @@ use ratatui::{
style::{Color, Style}, style::{Color, Style},
text::Text, text::Text,
widgets::{ widgets::{
Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, Block, Padding, Cell, Row, Scrollbar, ScrollbarOrientation,
TableState, ScrollbarState, Table, TableState,
}, },
Frame, Frame
}; };
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{ use crate::{
action::Action, action::Action, config::Config, palette::StylePalette, types::{ActionLevel, ActionTarget}
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -32,7 +29,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 +62,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 +85,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,22 +109,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
} }
@ -138,16 +127,15 @@ impl Component for EventLogs {
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)
@ -162,38 +150,33 @@ 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.iter().map(|log| { self.logs
.iter()
.map(|log| {
let style = match log.level { let style = match log.level {
ActionLevel::Info => info_style, ActionLevel::Info => info_style,
ActionLevel::Warn => warn_style, ActionLevel::Warn => warn_style,
ActionLevel::Error => error_style, ActionLevel::Error => error_style,
}; };
Row::new(vec![ Row::new(vec![
Cell::from( Cell::from(Text::from(log.time.format("%H:%M:%S").to_string()).style(style).alignment(Alignment::Left)),
Text::from(log.time.format("%H:%M:%S").to_string()) Cell::from(Text::from(log.message.clone()).style(style).alignment(Alignment::Left)),
.style(style)
.alignment(Alignment::Left),
),
Cell::from(
Text::from(log.message.clone())
.style(style)
.alignment(Alignment::Left),
),
]) ])
}), }),
[Constraint::Max(8), Constraint::Min(0)], [
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(Block::bordered()
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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title("Action Logs"), .title("Action Logs"));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -204,10 +187,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );
Ok(()) Ok(())

View File

@ -1,16 +1,20 @@
use std::collections::HashMap;
use color_eyre::Result; use color_eyre::Result;
use ratatui::layout::Constraint; use ratatui::layout::Constraint;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect},
text::Text, text::Text,
layout::{Alignment, Rect},
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame, Frame
}; };
use std::collections::HashMap;
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::types::BlockRange; use crate::types::BlockRange;
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
struct Details { struct Details {
@ -27,28 +31,20 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
} }
@ -58,15 +54,12 @@ 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( self.gatekeeper_details.insert(network.chain_id, Details {
network.chain_id,
Details {
incoming_fee: format!("{:.5}%", network.incoming_fee as f64 / 10_000_000.0), 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), outgoing_fee: format!("{:.5}%", network.outgoing_fee as f64 / 10_000_000.0),
}, });
);
} }
_ => {} _ => {}
}; };
@ -77,14 +70,12 @@ 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 let current_gatekeeper_details = self.gatekeeper_details
.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 let current_block_range = self.block_ranges
.block_ranges
.get(&self.selected_chain_id) .get(&self.selected_chain_id)
.copied() .copied()
.unwrap_or_default(); .unwrap_or_default();
@ -93,45 +84,34 @@ 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( Cell::from(Text::from(current_block_range.from_block.to_string()).alignment(Alignment::Right)),
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( Cell::from(Text::from(current_block_range.to_block.to_string()).alignment(Alignment::Right)),
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( Cell::from(Text::from(current_gatekeeper_details.incoming_fee).alignment(Alignment::Right)),
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( Cell::from(Text::from(current_gatekeeper_details.outgoing_fee).alignment(Alignment::Right)),
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(Block::bordered()
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(format!("Chain ID: {}", self.selected_chain_id)), .title(format!("Chain ID: {}", self.selected_chain_id)));
);
frame.render_widget(table, place); frame.render_widget(table, place);

View File

@ -1,412 +0,0 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
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,
};
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
widgets::{Input, InputRequest},
};
#[derive(Debug, Default, Eq, PartialEq)]
enum Selected {
#[default]
Input,
DefaultRpcs,
StoredRpcs,
}
#[derive(Debug, Default)]
pub struct GatekeeperEndpoints {
is_active: bool,
selected: Selected,
chain_id: u64,
rpc_input: Input,
action_tx: Option<UnboundedSender<Action>>,
network_tx: Option<Sender<Action>>,
default_endpoints: Vec<String>,
stored_endpoints: Vec<String>,
default_scroll_state: ScrollbarState,
default_table_state: TableState,
stored_scroll_state: ScrollbarState,
stored_table_state: TableState,
palette: StylePalette,
}
impl GatekeeperEndpoints {
fn close_popup(&mut self) {
self.is_active = false;
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup);
}
}
fn set_chain_id(&mut self, chain_id: u64) {
self.chain_id = chain_id;
if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::GetRpcEndpoints(chain_id));
}
}
fn submit_message(&mut self) {
if let Some(network_tx) = &self.network_tx {
if self.selected == Selected::Input {
let mut stored_endpoints = self.stored_endpoints.clone();
stored_endpoints.push(self.rpc_input.value().to_string());
let _ = network_tx.send(Action::UpdateStoredRpcEndpoints(
self.chain_id,
stored_endpoints,
));
self.rpc_input = Input::new(String::new());
}
}
}
fn enter_char(&mut self, new_char: char) {
match self.selected {
Selected::Input => {
let _ = self.rpc_input.handle(InputRequest::InsertChar(new_char));
}
Selected::StoredRpcs if (new_char == 'd' || new_char == 'D') => {
if let Some(index) = self.stored_table_state.selected() {
let mut stored_endpoints = self.stored_endpoints.clone();
if let Some(network_tx) = &self.network_tx {
stored_endpoints.remove(index);
let _ = network_tx.send(Action::UpdateStoredRpcEndpoints(
self.chain_id,
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(),
_ => {}
},
}
}
fn delete_char(&mut self) {
if self.selected == Selected::Input {
let _ = self.rpc_input.handle(InputRequest::DeletePrevChar);
}
}
fn move_cursor_up(&mut self) {
match self.selected {
Selected::Input => {}
Selected::DefaultRpcs => {
let i = match self.default_table_state.selected() {
Some(i) => {
if i == 0 {
0
} else {
i - 1
}
}
None => 0,
};
self.default_table_state.select(Some(i));
self.default_scroll_state = self.default_scroll_state.position(i);
}
Selected::StoredRpcs => {
let i = match self.stored_table_state.selected() {
Some(i) => {
if i == 0 {
0
} else {
i - 1
}
}
None => 0,
};
self.stored_table_state.select(Some(i));
self.stored_scroll_state = self.stored_scroll_state.position(i);
}
};
}
fn move_cursor_down(&mut self) {
match self.selected {
Selected::Input => {}
Selected::DefaultRpcs => {
let i = match self.default_table_state.selected() {
Some(i) => {
if i >= self.default_endpoints.len() - 1 {
i
} else {
i + 1
}
}
None => 0,
};
self.default_table_state.select(Some(i));
self.default_scroll_state = self.default_scroll_state.position(i);
}
Selected::StoredRpcs => {
let i = match self.stored_table_state.selected() {
Some(i) => {
if i >= self.stored_endpoints.len() - 1 {
i
} else {
i + 1
}
}
None => 0,
};
self.stored_table_state.select(Some(i));
self.stored_scroll_state = self.stored_scroll_state.position(i);
}
};
}
fn move_cursor_right(&mut self) {
self.selected = match self.selected {
Selected::Input => Selected::StoredRpcs,
Selected::DefaultRpcs => Selected::Input,
Selected::StoredRpcs => Selected::StoredRpcs,
};
self.update_selected_state();
}
fn move_cursor_left(&mut self) {
self.selected = match self.selected {
Selected::Input => Selected::DefaultRpcs,
Selected::StoredRpcs => Selected::Input,
Selected::DefaultRpcs => Selected::DefaultRpcs,
};
self.update_selected_state();
}
fn update_selected_state(&mut self) {
self.stored_table_state.select(None);
self.default_table_state.select(None);
self.stored_scroll_state = self.stored_scroll_state.position(0);
self.default_scroll_state = self.default_scroll_state.position(0);
}
fn prepare_table<'a>(
&self,
rpcs: &'a Vec<String>,
title_name: &'a str,
border_style: Color,
border_type: BorderType,
scrollbar_style: Style,
) -> (Table<'a>, Scrollbar<'a>) {
let table = Table::new(
rpcs.iter()
.map(|endpoint| Row::new(vec![Cell::from(endpoint.as_str())]))
.collect::<Vec<Row>>(),
[Constraint::Min(1)],
)
.highlight_style(self.palette.create_highlight_style())
.block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title(title_name),
);
let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight)
.begin_symbol(None)
.end_symbol(None)
.style(scrollbar_style);
(table, scrollbar)
}
}
impl PartialComponent for GatekeeperEndpoints {
fn set_active(&mut self, current_tab: CurrentTab) {
match current_tab {
CurrentTab::GatekeeperEndpoints => self.is_active = true,
_ => {
self.is_active = false;
self.rpc_input = Input::new(String::new());
}
};
}
}
impl Component for GatekeeperEndpoints {
fn register_network_handler(&mut self, tx: Sender<Action>) -> Result<()> {
self.network_tx = Some(tx);
Ok(())
}
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
self.action_tx = Some(tx);
Ok(())
}
fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette
.with_normal_style(style.get("normal_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());
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_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(())
}
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
if self.is_active && key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Enter => self.submit_message(),
KeyCode::Char(to_insert) => self.enter_char(to_insert),
KeyCode::Backspace => self.delete_char(),
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
KeyCode::Up => self.move_cursor_up(),
KeyCode::Down => self.move_cursor_down(),
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
}
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::SetChoosenGatekeeper(chain_id) => self.set_chain_id(chain_id),
Action::SetGatekeepedNetwork(network) => {
self.default_endpoints = network.default_endpoints
}
Action::SetStoredRpcEndpoints(stored_endpoints) => {
self.stored_endpoints = stored_endpoints
}
_ => {}
};
Ok(None)
}
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style();
let (selected_border_style, selected_border_type) =
self.palette.create_border_style(true);
let scrollbar_style = self.palette.create_scrollbar_style();
let (default_border_style, default_border_type) = match self.selected {
Selected::DefaultRpcs => (selected_border_style, selected_border_type),
_ => (border_style, border_type),
};
let (stored_border_style, stored_border_type) = match self.selected {
Selected::StoredRpcs => (selected_border_style, selected_border_type),
_ => (border_style, border_type),
};
let (input_border_style, input_border_type) = match self.selected {
Selected::Input => (selected_border_style, selected_border_type),
_ => (border_style, border_type),
};
let (default_rpcs, default_scrollbar) = self.prepare_table(
&self.default_endpoints,
"Default RPCs",
default_border_style,
default_border_type,
scrollbar_style,
);
let (stored_rpcs, stored_scrollbar) = self.prepare_table(
&self.stored_endpoints,
"Stored RPCs",
stored_border_style,
stored_border_type,
scrollbar_style,
);
let input = Paragraph::new(self.rpc_input.value()).block(
Block::bordered()
.border_style(input_border_style)
.border_type(input_border_type)
.title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right)
.title("Input new RPC"),
);
let v = Layout::vertical([Constraint::Max(14)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(80)]).flex(Flex::Center);
let [area] = v.areas(area);
let [area] = h.areas(area);
frame.render_widget(Clear, area);
let [tables_area, input_area] =
Layout::vertical([Constraint::Length(11), Constraint::Length(3)]).areas(area);
let [default_table_area, stored_table_area] =
Layout::horizontal([Constraint::Max(40), 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_scrollbar,
default_table_area.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.default_scroll_state,
);
frame.render_stateful_widget(
stored_rpcs,
stored_table_area,
&mut self.stored_table_state,
);
frame.render_stateful_widget(
stored_scrollbar,
stored_table_area.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.stored_scroll_state,
);
frame.render_widget(input, input_area);
frame.set_cursor_position(Position::new(
input_area.x + self.rpc_input.cursor() as u16 + 1,
input_area.y + 1,
));
}
Ok(())
}
}

View File

@ -1,17 +1,24 @@
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::{Block, List, ListItem, ListState, Scrollbar, ScrollbarOrientation, ScrollbarState}, widgets::{
Frame, Block, List, ListState, ListItem, Scrollbar,
ScrollbarOrientation, ScrollbarState,
},
Frame
}; };
use std::collections::HashMap;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::types::Gatekeeper; use crate::types::Gatekeeper;
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
struct NetworkDetails { struct NetworkDetails {
chain_id: u64, chain_id: u64,
@ -54,18 +61,14 @@ impl Gatekeepers {
} }
} }
fn gatekeeper_endpoints(&mut self) {
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::GatekeeperEndpoints);
}
}
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 if let Some(chain_id) = self.list_state
.list_state
.selected() .selected()
.map(|index| self.gatekeepers.get(index).map(|data| data.chain_id)) .map(|index| self.gatekeepers
.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));
@ -111,7 +114,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));
@ -136,8 +139,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);
@ -166,29 +169,22 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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) => self.update_gatekeeped_network(network), Action::SetGatekeepedNetwork(network) =>
self.update_gatekeeped_network(network),
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -202,8 +198,7 @@ impl Component for Gatekeepers {
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::Char('O') => self.nullify_evm_blocks(), KeyCode::Char('O') => self.nullify_evm_blocks(),
KeyCode::Enter => self.gatekeeper_endpoints(), _ => {},
_ => {}
}; };
} }
Ok(None) Ok(None)
@ -212,21 +207,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(self.gatekeepers.iter().map(|network| { let list = List::new(
ListItem::new(format!( self.gatekeepers
"{} (type {})", .iter()
network.chain_name, network.chain_type .map(|network| ListItem::new(format!("{} (type {})",
)) network.chain_name,
})) network.chain_type))
)
)
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.block( .block(Block::bordered()
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)
@ -237,10 +232,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,4 +1,4 @@
use std::collections::{BTreeMap, HashSet}; use std::collections::{HashSet, BTreeMap};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use color_eyre::Result; use color_eyre::Result;
@ -6,20 +6,24 @@ 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, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, Block, Cell, Row, Table, TableState, Scrollbar, Padding,
TableState, ScrollbarOrientation, ScrollbarState,
}, },
Frame, Frame
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::types::{ActionLevel, ActionTarget}; use crate::types::{ActionLevel, ActionTarget};
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
struct EraStakingInfo { struct EraStakingInfo {
reward: u128, reward: u128,
@ -62,8 +66,7 @@ impl History {
} }
fn payout_all_available(&mut self) { fn payout_all_available(&mut self) {
let unclaimed_keys = self let unclaimed_keys = self.rewards
.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<_>>();
@ -73,8 +76,7 @@ impl History {
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))
@ -84,11 +86,12 @@ 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().saturating_sub(index).saturating_sub(1); let rev_index = self.rewards.len()
.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 let is_claimed = self.rewards
.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");
@ -102,16 +105,11 @@ impl History {
(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!( format!("staking rewards for era index #{} already claimed", era_index),
"staking rewards for era index #{} already claimed",
era_index
),
ActionLevel::Warn, ActionLevel::Warn,
ActionTarget::ValidatorLog, ActionTarget::ValidatorLog)),
)),
}; };
} }
} }
@ -133,7 +131,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));
@ -156,8 +154,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);
@ -167,14 +165,11 @@ 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( let _ = self.rewards.insert(era_index, EraStakingInfo {
era_index,
EraStakingInfo {
reward, reward,
slash: 0u128, slash: 0u128,
is_claimed: false, 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());
@ -192,14 +187,11 @@ impl History {
reward_item.is_claimed = is_claimed; reward_item.is_claimed = is_claimed;
} }
None => { None => {
let _ = self.rewards.insert( let _ = self.rewards.insert(era_index, EraStakingInfo {
era_index,
EraStakingInfo {
reward: 0u128, reward: 0u128,
slash: 0u128, slash: 0u128,
is_claimed, is_claimed,
}, });
);
} }
} }
} }
@ -208,14 +200,11 @@ 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( let _ = self.rewards.insert(era_index, EraStakingInfo {
era_index,
EraStakingInfo {
reward: 0u128, reward: 0u128,
slash, slash,
is_claimed: false, is_claimed: false,
}, });
);
} }
} }
} }
@ -233,7 +222,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,
} }
} }
@ -252,34 +241,22 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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) => { Action::SetValidatorEraReward(era_index, reward) => self.update_rewards(era_index, reward),
self.update_rewards(era_index, reward) Action::SetValidatorEraClaimed(era_index, is_claimed) => self.update_claims(era_index, is_claimed),
}
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),
_ => {} _ => {}
}; };
@ -295,7 +272,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)
@ -305,12 +282,13 @@ 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.iter().rev().map(|(key, value)| { self.rewards
.iter()
.rev()
.map(|(key, value)| {
let mut era_index_text = Text::from(key.to_string()).alignment(Alignment::Left); let mut era_index_text = Text::from(key.to_string()).alignment(Alignment::Left);
let mut slash_text = let mut slash_text = Text::from(self.prepare_u128(value.slash)).alignment(Alignment::Center);
Text::from(self.prepare_u128(value.slash)).alignment(Alignment::Center); let mut reward_text = 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);
@ -321,8 +299,7 @@ impl Component for History {
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())
.style(self.palette.create_highlight_style())
} else { } else {
if self.pending_payout.contains(key) { if self.pending_payout.contains(key) {
era_index_text = era_index_text.add_modifier(Modifier::SLOW_BLINK); era_index_text = era_index_text.add_modifier(Modifier::SLOW_BLINK);
@ -345,15 +322,13 @@ 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(Block::bordered()
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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title("Staking history"), .title("Staking history"));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -364,10 +339,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -4,13 +4,20 @@ use ratatui::layout::Margin;
use ratatui::widgets::ListItem; use ratatui::widgets::ListItem;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
widgets::{Block, List, ListState, Scrollbar, ScrollbarOrientation, ScrollbarState}, widgets::{
Frame, Block, List, ListState, Scrollbar,
ScrollbarOrientation, ScrollbarState,
},
Frame
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
pub struct ListenAddresses { pub struct ListenAddresses {
is_active: bool, is_active: bool,
@ -56,7 +63,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));
@ -79,8 +86,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);
@ -108,22 +115,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
} }
@ -144,7 +143,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)
@ -156,17 +155,15 @@ 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(Block::bordered()
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)
@ -177,10 +174,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -11,55 +11,53 @@ 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 bond_popup;
mod change_blocks_popup;
mod chill_popup;
mod event_log; mod event_log;
mod gatekeeper_details;
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 peers;
mod rebond_popup;
mod reward_details;
mod rotate_popup;
mod staking_details;
mod stash_details;
mod stash_info; mod stash_info;
mod unbond_popup; mod nominators;
mod validate_popup; mod listen_addresses;
mod withdraw_popup; mod gatekeepers;
mod gatekeeper_details;
mod history;
mod withdrawals; mod withdrawals;
mod stash_details;
mod staking_details;
mod reward_details;
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;
use bond_popup::BondPopup;
use change_blocks_popup::ChangeBlocksPopup;
use chill_popup::ChillPopup;
use event_log::EventLogs;
use gatekeeper_details::GatekeeperDetails;
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_details::StashDetails;
use staking_details::StakingDetails;
use reward_details::RewardDetails;
use event_log::EventLogs;
use peers::Peers;
use stash_info::StashInfo; use stash_info::StashInfo;
use unbond_popup::UnbondPopup; use listen_addresses::ListenAddresses;
use validate_popup::ValidatePopup; use gatekeepers::Gatekeepers;
use withdraw_popup::WithdrawPopup; use gatekeeper_details::GatekeeperDetails;
use nominators::NominatorsByValidator;
use history::History;
use withdrawals::Withdrawals; use withdrawals::Withdrawals;
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;
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub enum CurrentTab { pub enum CurrentTab {
@ -82,7 +80,6 @@ pub enum CurrentTab {
WithdrawPopup, WithdrawPopup,
PayeePopup, PayeePopup,
ChangeBlocksPopup, ChangeBlocksPopup,
GatekeeperEndpoints,
} }
pub trait PartialComponent: Component { pub trait PartialComponent: Component {
@ -126,7 +123,6 @@ impl Default for Validator {
Box::new(WithdrawPopup::default()), Box::new(WithdrawPopup::default()),
Box::new(PayeePopup::default()), Box::new(PayeePopup::default()),
Box::new(ChangeBlocksPopup::default()), Box::new(ChangeBlocksPopup::default()),
Box::new(GatekeeperEndpoints::default()),
], ],
} }
} }
@ -182,26 +178,23 @@ 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 { if !self.is_active { return Ok(None) }
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 => {
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;
@ -210,52 +203,52 @@ 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)
} }
@ -277,17 +270,13 @@ 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 => {
self.previous_tab = self.current_tab;
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());
@ -311,13 +300,15 @@ 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([Constraint::Length(31), Constraint::Fill(1)]).areas(place) Layout::horizontal([
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,13 +317,15 @@ 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([Constraint::Fill(1), Constraint::Length(30)]).areas(place) Layout::horizontal([
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] {
@ -341,8 +334,7 @@ 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] {
@ -351,6 +343,5 @@ 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,19 +2,23 @@ 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::{
layout::{Alignment, Rect},
text::Text, text::Text,
layout::{Alignment, Rect},
widgets::{ widgets::{
Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, Block, Cell, Row, Table, TableState, Scrollbar, Padding,
TableState, ScrollbarOrientation, ScrollbarState,
}, },
Frame, Frame
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::types::Nominator; use crate::types::Nominator;
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
pub struct NominatorsByValidator { pub struct NominatorsByValidator {
is_active: bool, is_active: bool,
@ -72,7 +76,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));
@ -95,8 +99,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);
@ -130,22 +134,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
} }
@ -153,11 +149,8 @@ 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) Action::SetNominatorsByValidator(nominators, account_id) if self.stash == account_id =>
if self.stash == account_id => self.update_nominators(nominators),
{
self.update_nominators(nominators)
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -170,7 +163,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)
@ -180,27 +173,28 @@ 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.iter().map(|info| { self.nominators
.iter()
.map(|info| {
Row::new(vec![ Row::new(vec![
Cell::from(Text::from(info.address.clone()).alignment(Alignment::Left)), Cell::from(Text::from(info.address.clone()).alignment(Alignment::Left)),
Cell::from( Cell::from(Text::from(self.prepare_u128(info.value)).alignment(Alignment::Right)),
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(Block::bordered()
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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title("My Nominators"), .title("My Nominators"));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -211,10 +205,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,16 +1,18 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
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 std::sync::mpsc::Sender; use subxt::ext::sp_core::crypto::{
use subxt::ext::sp_core::crypto::{AccountId32, ByteArray, Ss58AddressFormat, Ss58Codec}; ByteArray, Ss58Codec, Ss58AddressFormat, AccountId32,
};
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -34,7 +36,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 {
@ -59,18 +61,9 @@ 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)"),
"Re-stake", ("Stake", "(pay into the stash account, not increasing the amount at stake)"),
"(pay into the stash account, increasing the amount at stake accordingly)", ("Account", "(pay into a specified account different from stash)"),
),
(
"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(),
@ -92,7 +85,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()),
}; };
@ -113,12 +106,13 @@ 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) => {
@ -127,15 +121,18 @@ 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(message, level, ActionTarget::ValidatorLog)); let _ = action_tx.send(Action::EventLog(
message,
level,
ActionTarget::ValidatorLog));
} }
} }
@ -148,12 +145,8 @@ 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!( format!("provided public address for {} is not part of Casper/Ghost ecosystem", self.address.value()),
"provided public address for {} is not part of Casper/Ghost ecosystem", ActionLevel::Error);
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];
@ -161,18 +154,15 @@ 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!( format!("could not create valid account id from {}", self.address.value()),
"could not create valid account id from {}", ActionLevel::Error);
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,8 +171,7 @@ impl PayeePopup {
0 => RewardDestination::Staked, 0 => RewardDestination::Staked,
1 => RewardDestination::Stash, 1 => RewardDestination::Stash,
2 => { 2 => {
let account_id = self let account_id = self.proposed_account_id
.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)
} }
@ -193,20 +182,17 @@ 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 {
@ -261,18 +247,12 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_style").copied());
.with_normal_title_style(style.get("normal_title_style").copied()); self.palette.with_highlight_style(style.get("highlight_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(())
} }
@ -287,7 +267,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 {
@ -296,7 +276,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(),
_ => {} _ => {},
}; };
} }
} }
@ -307,12 +287,9 @@ 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() {
@ -341,18 +318,16 @@ 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(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("Select reward destination"), .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);
@ -363,14 +338,13 @@ 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()).block( let input_amount = Paragraph::new(self.address.value())
Block::bordered() .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("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);
@ -382,7 +356,7 @@ impl Component for PayeePopup {
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,15 +1,19 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
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 tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)] #[derive(Debug)]
pub struct PayoutAllPopup { pub struct PayoutAllPopup {
@ -19,7 +23,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 {
@ -55,8 +59,7 @@ impl PayoutAllPopup {
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();
@ -85,16 +88,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -104,7 +102,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)
@ -123,24 +121,17 @@ 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(format!( let popup = Paragraph::new(
" Do payout for {} eras, from {} to {}", format!(" Do payout for {} eras, from {} to {}",
self.era_indexes.len(), self.era_indexes.len(),
self.era_indexes self.era_indexes.first().expect("Length of unclaimed indexes always more then one; qed"),
.first() self.era_indexes.last().expect("Length of unclaimed indexes always more then one; qed")))
.expect("Length of unclaimed indexes always more then one; qed"), .block(Block::bordered()
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,15 +1,19 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
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 tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)] #[derive(Debug)]
pub struct PayoutPopup { pub struct PayoutPopup {
@ -19,7 +23,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 {
@ -53,8 +57,7 @@ impl PayoutPopup {
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);
@ -84,16 +87,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -103,7 +101,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)
@ -122,14 +120,13 @@ 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)).block( let popup = Paragraph::new(format!(" Do payout for era #{}", self.era_index))
Block::bordered() .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

@ -2,18 +2,22 @@ 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::{
layout::{Alignment, Rect},
text::Text, text::Text,
layout::{Alignment, Rect},
widgets::{ widgets::{
Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, Block, Cell, Row, Table, TableState, Scrollbar, Padding,
TableState, ScrollbarOrientation, ScrollbarState,
}, },
Frame, Frame
}; };
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::types::PeerInformation; use crate::types::PeerInformation;
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
pub struct Peers { pub struct Peers {
is_active: bool, is_active: bool,
@ -65,7 +69,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));
@ -88,8 +92,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);
@ -112,22 +116,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
} }
@ -147,7 +143,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)
@ -157,14 +153,14 @@ 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.iter().map(|info| { self.peers
.iter()
.map(|info| {
Row::new(vec![ Row::new(vec![
Cell::from(Text::from(info.peer_id.clone()).alignment(Alignment::Left)), Cell::from(Text::from(info.peer_id.clone()).alignment(Alignment::Left)),
Cell::from(Text::from(info.roles.clone()).alignment(Alignment::Center)), Cell::from(Text::from(info.roles.clone()).alignment(Alignment::Center)),
Cell::from(Text::from(info.best_hash.to_string()).alignment(Alignment::Center)), Cell::from(Text::from(info.best_hash.to_string()).alignment(Alignment::Center)),
Cell::from( Cell::from(Text::from(info.best_number.to_string()).alignment(Alignment::Right)),
Text::from(info.best_number.to_string()).alignment(Alignment::Right),
),
]) ])
}), }),
[ [
@ -176,15 +172,13 @@ 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(Block::bordered()
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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title("My Peers"), .title("My Peers"));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -195,10 +189,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,14 +1,14 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame
}; };
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,7 +54,8 @@ 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(Action::EventLog(message, level, ActionTarget::ValidatorLog)); let _ = action_tx.send(
Action::EventLog(message, level, ActionTarget::ValidatorLog));
} }
} }
@ -73,10 +74,9 @@ 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) => { Err(err) => self.log_event(
self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error) format!("invalid amount, error: {err}"), ActionLevel::Error),
}
} }
} }
} }
@ -126,16 +126,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -148,7 +143,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)
@ -165,14 +160,13 @@ 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()).block( let input = Paragraph::new(self.amount.value())
Block::bordered() .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(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);
@ -182,7 +176,7 @@ impl Component for RebondPopup {
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,16 +1,20 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::layout::Constraint; use ratatui::layout::Constraint;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect},
text::Text, text::Text,
layout::{Alignment, Rect},
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame, Frame
}; };
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::types::EraRewardPoints; use crate::types::EraRewardPoints;
use crate::widgets::DotSpinner; use crate::widgets::DotSpinner;
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
pub struct RewardDetails { pub struct RewardDetails {
palette: StylePalette, palette: StylePalette,
@ -54,12 +58,9 @@ 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 { if self.nominators_blocked { "blocked".to_string() }
"blocked".to_string() else { format!("{:.2}%", commission as f64 / 10_000_000.0) }
} else { },
format!("{:.2}%", commission as f64 / 10_000_000.0)
}
}
None => DotSpinner::default().to_string(), None => DotSpinner::default().to_string(),
} }
} }
@ -76,28 +77,20 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
} }
@ -105,12 +98,8 @@ 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) => { Action::SetCurrentValidatorEraRewards(_, _, individual) => self.update_individual(&individual),
self.update_individual(&individual) Action::SetValidatorPrefs(commission, disabled, account_id) if self.stash == account_id => {
}
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;
@ -121,7 +110,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,
@ -141,8 +130,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",
} }
}; };
@ -167,18 +156,19 @@ 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(Block::bordered()
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(staking_status), .title(staking_status));
);
frame.render_widget(table, place); frame.render_widget(table, place);

View File

@ -1,16 +1,20 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
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 std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)] #[derive(Debug)]
pub struct RotatePopup { pub struct RotatePopup {
@ -19,7 +23,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 {
@ -51,9 +55,7 @@ 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.secret_seed, self.cached_keys.clone()));
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);
@ -106,16 +108,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -134,7 +131,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)
@ -164,18 +161,19 @@ impl Component for RotatePopup {
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(Block::bordered()
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("Rotate session keys (Enter to proceed / Esc to close)"), .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,16 +1,20 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::layout::Constraint; use ratatui::layout::Constraint;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect},
text::Text, text::Text,
layout::{Alignment, Rect},
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame, Frame
}; };
use subxt::ext::sp_core::crypto::{AccountId32, Ss58AddressFormat, Ss58Codec}; use subxt::ext::sp_core::crypto::{Ss58Codec, Ss58AddressFormat, AccountId32};
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::types::RewardDestination; use crate::types::RewardDestination;
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
pub struct StakingDetails { pub struct StakingDetails {
palette: StylePalette, palette: StylePalette,
@ -59,34 +63,27 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
} }
@ -95,9 +92,8 @@ 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;
@ -116,45 +112,34 @@ 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( Cell::from(Text::from(self.prepare_u128(self.staked_total)).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_u128(self.staked_own)).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_u128(self.staked_total.saturating_sub(self.staked_own))).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_u128(self.next_reward)).alignment(Alignment::Right)),
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(Block::bordered()
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), .title(title));
);
frame.render_widget(table, place); frame.render_widget(table, place);

View File

@ -1,16 +1,20 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::layout::Constraint; use ratatui::layout::Constraint;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect},
text::Text, text::Text,
layout::{Alignment, Rect},
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame, Frame
}; };
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::widgets::DotSpinner; use crate::widgets::DotSpinner;
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
pub struct StashDetails { pub struct StashDetails {
palette: StylePalette, palette: StylePalette,
@ -52,8 +56,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)
} }
} }
@ -67,7 +71,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 {
@ -78,22 +82,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
} }
@ -102,31 +98,22 @@ 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) Action::BalanceResponse(account_id, maybe_balance) if account_id == self.stash_account_id => {
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| { self.free_balance = maybe_balance.map(|balance| balance.free
balance
.free
.saturating_sub(balance.frozen) .saturating_sub(balance.frozen)
.saturating_sub(balance.reserved) .saturating_sub(balance.reserved));
}); },
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -144,38 +131,30 @@ 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( Cell::from(Text::from(self.prepare_u128(self.free_balance)).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_u128(self.staked_total)).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_u128(self.staked_active)).alignment(Alignment::Right)),
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(Block::bordered()
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("Stash details"), .title("Stash details"));
);
frame.render_widget(table, place); frame.render_widget(table, place);

View File

@ -1,32 +1,37 @@
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::fs::File;
use std::io::{Write, BufRead, BufReader};
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::{
layout::{Alignment, Rect},
text::Text, text::Text,
layout::{Alignment, Rect},
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame, Frame
}; };
use std::sync::mpsc::Sender;
use subxt::{ use subxt::{
ext::sp_core::{
crypto::{AccountId32, Ss58AddressFormat, Ss58Codec},
sr25519::Pair,
Pair as PairT,
},
tx::PairSigner, tx::PairSigner,
ext::sp_core::{
Pair as PairT,
sr25519::Pair,
crypto::{Ss58Codec, Ss58AddressFormat, AccountId32},
},
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::casper::CasperConfig; use crate::casper::CasperConfig;
use crate::types::{ActionLevel, ActionTarget}; use crate::types::{ActionLevel, ActionTarget};
use crate::{action::Action, config::Config, palette::StylePalette, types::SessionKeyInfo}; use crate::{
types::SessionKeyInfo,
action::Action,
config::Config,
palette::StylePalette,
};
pub struct StashInfo { pub struct StashInfo {
is_active: bool, is_active: bool,
@ -63,7 +68,8 @@ 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(Action::EventLog(message, level, ActionTarget::ValidatorLog)); let _ = action_tx.send(
Action::EventLog(message, level, ActionTarget::ValidatorLog));
} }
} }
@ -90,8 +96,7 @@ 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);
@ -99,24 +104,16 @@ impl StashInfo {
Ok(()) Ok(())
} else { } else {
self.log_event( self.log_event(
format!( format!("file at '{:?}' is empty, trying to create new key", &self.stash_filepath),
"file at '{:?}' is empty, trying to create new key", ActionLevel::Warn);
&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!( format!("file at '{:?}' not found, trying to create new key", &self.stash_filepath),
"file at '{:?}' not found, trying to create new key", ActionLevel::Warn);
&self.stash_filepath
),
ActionLevel::Warn,
);
self.generate_and_save_new_key() self.generate_and_save_new_key()
} }
@ -136,12 +133,8 @@ impl StashInfo {
self.initiate_stash_info(account_id, seed); self.initiate_stash_info(account_id, seed);
self.log_event( self.log_event(
format!( format!("new stash key {} created and stored at {:?}", &address, self.stash_filepath),
"new stash key {} created and stored at {:?}", ActionLevel::Info);
&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);
@ -186,7 +179,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 {
@ -202,22 +195,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
@ -235,29 +220,28 @@ 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.iter().map(|name| { self.key_names
.iter()
.map(|name| {
let address_text = match self.session_keys.get(*name) { let address_text = match self.session_keys.get(*name) {
Some(key_info) => { Some(key_info) => {
let mut address_text = let mut address_text = Text::from(key_info.key.clone()).alignment(Alignment::Center);
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 = queued_address_text.add_modifier(Modifier::CROSSED_OUT);
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![ Row::new(vec![
@ -276,14 +260,12 @@ 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(Block::bordered()
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.stash_address.clone()), .title(self.stash_address.clone()));
);
frame.render_widget(table, place); frame.render_widget(table, place);
Ok(()) Ok(())

View File

@ -1,14 +1,14 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame
}; };
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,7 +58,8 @@ 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(Action::EventLog(message, level, ActionTarget::ValidatorLog)); let _ = action_tx.send(
Action::EventLog(message, level, ActionTarget::ValidatorLog));
} }
} }
@ -74,20 +75,19 @@ 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(self.stash_secret_seed, amount)); let _ = network_tx.send(Action::UnbondFrom(
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) => { Err(err) => self.log_event(
self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error) format!("invalid amount, error: {err}"), ActionLevel::Error),
}
} }
} }
} }
@ -137,16 +137,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -159,7 +154,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)
@ -167,9 +162,8 @@ 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,
_ => {} _ => {}
@ -180,14 +174,13 @@ 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()).block( let input = Paragraph::new(self.amount.value())
Block::bordered() .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(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);
@ -197,7 +190,7 @@ impl Component for UnbondPopup {
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 color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame
}; };
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,7 +54,8 @@ 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(Action::EventLog(message, level, ActionTarget::ValidatorLog)); let _ = action_tx.send(
Action::EventLog(message, level, ActionTarget::ValidatorLog));
} }
} }
@ -73,10 +74,9 @@ 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) => { Err(err) => self.log_event(
self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error) format!("invalid amount, error: {err}"), ActionLevel::Error),
}
} }
} }
} }
@ -126,16 +126,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -148,7 +143,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)
@ -165,14 +160,13 @@ 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()).block( let input = Paragraph::new(self.amount.value())
Block::bordered() .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(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);
@ -182,7 +176,7 @@ impl Component for ValidatePopup {
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 color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
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 tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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 {
@ -70,8 +70,7 @@ impl WithdrawPopup {
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 {
@ -107,16 +106,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -126,7 +120,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)
@ -134,26 +128,15 @@ 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) => { Action::SetExistentialDeposit(existential_deposit) => self.existential_deposit = 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) Action::SetSlashingSpansLength(length, account_id) if self.stash_account == account_id =>
if self.stash_account == account_id => self.slashing_spans_length = length as u32,
{ Action::SetStakedAmountRatio(_, active_balance, account_id) if self.stash_account == account_id =>
self.slashing_spans_length = length as u32 self.active_balance = active_balance.unwrap_or_default(),
} Action::SetValidatorEraUnlocking(unlockings, account_id) if self.stash_account == account_id =>
Action::SetStakedAmountRatio(_, active_balance, account_id) self.unlocking_is_empty = unlockings.is_empty(),
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)
@ -167,14 +150,13 @@ 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).block( let input = Paragraph::new(text)
Block::bordered() .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

@ -3,18 +3,23 @@ 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::{
layout::{Alignment, Rect},
text::Text, text::Text,
layout::{Alignment, Rect},
widgets::{ widgets::{
Block, Cell, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, TableState, Block, Cell, Row, Table, TableState, Scrollbar,
ScrollbarOrientation, ScrollbarState,
}, },
Frame, Frame
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::types::{ActionLevel, ActionTarget, UnlockChunk}; use crate::types::{ActionTarget, ActionLevel, UnlockChunk};
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
pub struct Withdrawals { pub struct Withdrawals {
is_active: bool, is_active: bool,
@ -59,8 +64,7 @@ impl Withdrawals {
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));
));
} }
} }
} }
@ -81,7 +85,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));
@ -104,8 +108,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);
@ -124,7 +128,8 @@ impl Withdrawals {
} }
} }
self.unlockings = updated_unlockings; self.unlockings = updated_unlockings;
self.scroll_state = self.scroll_state.content_length(self.unlockings.len()); self.scroll_state = self.scroll_state
.content_length(self.unlockings.len());
} }
fn prepare_u128(&self, value: u128) -> String { fn prepare_u128(&self, value: u128) -> String {
@ -163,22 +168,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
} }
@ -187,11 +184,8 @@ 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) Action::SetValidatorEraUnlocking(unlockings, account_id) if self.stash_account == account_id =>
if self.stash_account == account_id => self.add_new_unlocking(unlockings),
{
self.add_new_unlocking(unlockings)
}
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -205,7 +199,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)
@ -215,31 +209,35 @@ 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.iter().map(|unlock| { self.unlockings
let mut est_era_text = .iter()
Text::from(self.estimate_time(unlock.era)).alignment(Alignment::Center); .map(|unlock| {
let mut value_text = let mut est_era_text = Text::from(self.estimate_time(unlock.era)).alignment(Alignment::Center);
Text::from(self.prepare_u128(unlock.value)).alignment(Alignment::Right); let mut value_text = 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![Cell::from(est_era_text), Cell::from(value_text)]) Row::new(vec![
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(Block::bordered()
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("Withdrawals"), .title("Withdrawals"));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -250,10 +248,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -8,7 +8,12 @@ use ratatui::{
use subxt::utils::H256; use subxt::utils::H256;
use super::Component; use super::Component;
use crate::{action::Action, config::Config, palette::StylePalette, widgets::OghamCenter}; use crate::{
config::Config,
action::Action,
palette::StylePalette,
widgets::OghamCenter,
};
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Version { pub struct Version {
@ -30,10 +35,8 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_normal_border_style(style.get("normal_border_style").copied());
} }
Ok(()) Ok(())
} }
@ -54,25 +57,14 @@ 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( Line::styled(self.chain_name.clone().unwrap_or(OghamCenter::default().to_string()), text_style),
self.chain_name Line::styled(self.node_version.clone().unwrap_or(OghamCenter::default().to_string()), text_style),
.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(Block::bordered()
Block::bordered()
.border_style(border_style) .border_style(border_style)
.border_type(border_type), .border_type(border_type)
) )
.alignment(Alignment::Center) .alignment(Alignment::Center)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });

View File

@ -1,15 +1,20 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect}, layout::{Alignment, Constraint, Flex, Layout, Rect},
text::Text,
widgets::{Block, Cell, Clear, Row, Table}, widgets::{Block, Cell, Clear, Row, Table},
text::Text,
Frame, Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{action::Action, config::Config, palette::StylePalette, widgets::DotSpinner}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
widgets::DotSpinner,
};
#[derive(Debug)] #[derive(Debug)]
pub struct AccountDetails { pub struct AccountDetails {
@ -61,8 +66,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)
} }
} }
} }
@ -94,16 +99,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -119,12 +119,11 @@ 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 let transferable = account_info.free
.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;
@ -143,7 +142,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)
@ -156,54 +155,39 @@ 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( Cell::from(Text::from(self.nonce
Text::from(
self.nonce
.map(|n| n.to_string()) .map(|n| n.to_string())
.unwrap_or(DotSpinner::default().to_string()), .unwrap_or(DotSpinner::default().to_string())
) ).alignment(Alignment::Right)),
.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( Cell::from(Text::from(self.prepare_u128(self.total_balance)).alignment(Alignment::Right))
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( Cell::from(Text::from(self.prepare_u128(self.transferable_balance)).alignment(Alignment::Right))
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( Cell::from(Text::from(self.prepare_u128(self.locked_balance)).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_u128(self.reserved_balance)).alignment(Alignment::Right)),
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(Block::bordered()
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(format!("Details for {}", &self.name)), .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,32 +1,36 @@
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::fs::File;
use std::io::{Write, BufRead, BufReader};
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::{
layout::{Alignment, Rect},
text::Text, text::Text,
layout::{Alignment, Rect},
widgets::{ widgets::{
Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, Block, Cell, Row, Table, TableState, Scrollbar, Padding,
TableState, ScrollbarOrientation, ScrollbarState,
}, },
Frame, Frame
}; };
use std::sync::mpsc::Sender;
use subxt::ext::sp_core::{ use subxt::ext::sp_core::{
crypto::{AccountId32, Ss58AddressFormat, Ss58Codec},
sr25519::Pair,
Pair as PairT, Pair as PairT,
sr25519::Pair,
crypto::{Ss58Codec, Ss58AddressFormat, AccountId32},
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::types::{ActionLevel, ActionTarget, SystemAccount}; use crate::types::{ActionLevel, ActionTarget, SystemAccount};
use crate::widgets::DotSpinner; use crate::widgets::DotSpinner;
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
struct AccountInfo { struct AccountInfo {
name: String, name: String,
@ -75,7 +79,9 @@ 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.get(&account_id).map(|info| info.nonce); let used_nonce = self.balances
.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));
} }
} }
@ -84,17 +90,22 @@ 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(account_id, used_seed.clone())); let _ = action_tx.send(Action::UsedAccount(
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(account_id, false)); let _ = network_tx.send(Action::GetNominatorsByAccount(
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(Action::EventLog(message, level, ActionTarget::WalletLog)); let _ = action_tx.send(
Action::EventLog(message, level, ActionTarget::WalletLog));
} }
} }
@ -108,15 +119,16 @@ 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(self.wallet_keys[index].name.clone())); let _ = action_tx.send(Action::RenameAccount(
self.wallet_keys[index].name.clone()
));
} }
} }
} }
@ -129,20 +141,19 @@ impl Accounts {
.arg("clipboard") .arg("clipboard")
.stdin(std::process::Stdio::piped()) .stdin(std::process::Stdio::piped())
.spawn() .spawn()
.and_then(|child| { .and_then(|child| Ok(child
Ok(child
.stdin .stdin
.and_then(|mut cs| cs.write_all(address.as_bytes()).ok())) .and_then(|mut cs| cs
}) { .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" "command `xclip` not found, consider installing `xclip` on your machine".to_string(),
.to_string(), ActionLevel::Error),
ActionLevel::Error,
),
} }
} }
} }
@ -155,12 +166,9 @@ impl Accounts {
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)); .to_ss58check_with_version(Ss58AddressFormat::custom(1996));
self.log_event( self.log_event(
format!( format!("new wallet '{}' with public address {} created",
"new wallet '{}' with public address {} created", &name, &address),
&name, &address ActionLevel::Info);
),
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 {
@ -177,12 +185,12 @@ 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( self.log_event(format!("wallet '{}' renamed to {}",
format!("wallet '{}' renamed to {}", &new_name, &old_name), &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();
} }
} }
@ -216,13 +224,9 @@ impl Accounts {
self.previous_row(); self.previous_row();
self.save_to_file(); self.save_to_file();
self.log_event( self.log_event(format!("wallet `{}` with public address {} removed",
format!( &wallet.name, &wallet.address),
"wallet `{}` with public address {} removed", ActionLevel::Warn);
&wallet.name, &wallet.address
),
ActionLevel::Warn,
);
} }
} }
} }
@ -264,11 +268,10 @@ impl Accounts {
seed: wallet_key.to_string(), seed: wallet_key.to_string(),
}); });
} }
self.log_event( self.log_event(format!("read {} wallets from disk",
format!("read {} wallets from disk", self.wallet_keys.len()), 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) => {
@ -282,8 +285,7 @@ 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)
@ -291,8 +293,7 @@ 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)
} }
@ -345,16 +346,11 @@ 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!( format!("{} from `{}` loaded to ghost-eye", name_for_key, path_to_key),
"{} from `{}` loaded to ghost-eye", ActionLevel::Warn);
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 {
@ -365,12 +361,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);
);
} }
}; };
} }
@ -378,14 +374,12 @@ 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 let wallet_key = self.wallet_keys
.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()));
));
} }
} }
} }
@ -407,7 +401,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));
@ -434,8 +428,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);
@ -450,7 +444,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)
} }
} }
} }
@ -477,22 +471,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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;
@ -507,11 +493,7 @@ 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 if self.wallet_keys.iter().any(|wallet| wallet.account_id == account_id) {
.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),
@ -523,7 +505,7 @@ impl Component for Accounts {
} }
} }
} }
} },
_ => {} _ => {}
}; };
Ok(None) Ok(None)
@ -543,7 +525,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)
@ -554,27 +536,33 @@ 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.iter().map(|info| { self.wallet_keys
let balance = self.balances.get(&info.account_id).map(|b| b.free); .iter()
.map(|info| {
let balance = self.balances
.get(&info.account_id)
.map(|b| b.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),
],
) )
.highlight_style(self.palette.create_highlight_style()) .highlight_style(self.palette.create_highlight_style())
.column_spacing(1) .column_spacing(1)
.block( .block(Block::bordered()
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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title("My Accounts"), .title("My Accounts"));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -585,10 +573,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,18 +1,18 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,7 +48,8 @@ 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(self.name.value().to_string())); let _ = action_tx.send(Action::NewAccount(
self.name.value().to_string()));
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
} }
} }
@ -77,7 +78,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());
} },
}; };
} }
} }
@ -90,16 +91,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -113,7 +109,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)
@ -125,14 +121,13 @@ 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()).block( let input = Paragraph::new(self.name.value())
Block::bordered() .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("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);
@ -142,7 +137,7 @@ impl Component for AddAccount {
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 color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,23 +59,20 @@ 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 => { NameOrAddress::Address => if let Some(action_tx) = &self.action_tx {
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::NewAddressBookRecord( 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);
} }
} }
} }
}
fn enter_char(&mut self, new_char: char) { fn enter_char(&mut self, new_char: char) {
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));
} }
@ -86,7 +83,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);
} }
@ -97,7 +94,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);
} }
@ -108,7 +105,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);
} }
@ -127,7 +124,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;
} }
} },
}; };
} }
} }
@ -140,16 +137,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -165,7 +157,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)
@ -178,23 +170,21 @@ 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()).block( let input_name = Paragraph::new(self.name.value())
Block::bordered() .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("New name in Address Book"), .title("New name in Address Book"));
);
let input_address = Paragraph::new(self.address.value()).block( let input_address = Paragraph::new(self.address.value())
Block::bordered() .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("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);
@ -213,13 +203,13 @@ impl Component for AddAddressBookRecord {
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,17 +9,23 @@ 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, Row, ScrollbarState, Table, TableState}, widgets::{Block, ScrollbarState, Row, Table, TableState},
Frame, Frame
};
use subxt::ext::sp_core::crypto::{
ByteArray, Ss58Codec, Ss58AddressFormat, AccountId32,
}; };
use std::sync::mpsc::Sender;
use subxt::ext::sp_core::crypto::{AccountId32, ByteArray, Ss58AddressFormat, Ss58Codec};
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::types::{ActionLevel, ActionTarget, SystemAccount}; use crate::types::{ActionLevel, ActionTarget, SystemAccount};
use crate::widgets::DotSpinner; use crate::widgets::DotSpinner;
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
struct BookRecord { struct BookRecord {
name: String, name: String,
@ -74,8 +80,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 _ = let _ = action_tx.send(Action::TransferTo(
action_tx.send(Action::TransferTo(self.address_book[index].address.clone())); self.address_book[index].address.clone()));
} }
} }
} }
@ -88,7 +94,8 @@ 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(Action::EventLog(message, level, ActionTarget::WalletLog)); let _ = action_tx.send(
Action::EventLog(message, level, ActionTarget::WalletLog));
} }
} }
@ -122,55 +129,26 @@ 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"),
"Pierre", ("Ghost_7", "3666e4e19f87bb8680495f31864ce1f1c69d4178002cc01911aef2cc7313f203"),
"328d3b7c3046ef7700937d99fb2e98ce2591682c2b5dcf3f562e4da157650237", ("Neptune1", "ac871e8bab00dd56ba3a1c0bd289357203dcaf10010b0b04ad7472870cd22a3c"),
), ("Neptune2", "425ccd7bda4f5c76788ba23bc0381d7a2e496179c93301208c57501c80a4232a"),
( ("Doctor K", "927a98dcf8f721103005f168476c24b91d7d10d580f457006a908e10e62c7729"),
"Ghost_7", ("Starman", "ac9e227e30a63ce6eeb55cfbb1fb832aa7e1d3fad2bcb3f663de4a91d744fd50"),
"3666e4e19f87bb8680495f31864ce1f1c69d4178002cc01911aef2cc7313f203", ("Kitsune1", "46c78fcacffd80abc9cca4917ef8369a37e21a1691ca11e7a3b53f80be745313"),
), ("Scientio", "fa5e5a295ec74c3dda81118d9240db1552b28f831838465ae0712e97e78a6728"),
( ("Kitsune2", "4078ddb1ba1388f768fe6aa40ba9124a72692ecbcc83dc088fa86c735e4dc128"),
"Neptune1", ("Proxmio", "5e1456904c40192cd3a18183df7dffea90d97739830a902cabb702ecdae4f649"),
"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.iter().for_each(|chad_info| { chad_boyz
.iter()
.for_each(|chad_info| {
writeln!(new_file, "{}:0x{}", chad_info.0, chad_info.1) writeln!(new_file, "{}:0x{}", chad_info.0, chad_info.1)
.expect("should write to address book; qed"); .expect("should write to address book; qed");
let chad_account_id: [u8; 32] = hex::decode(&chad_info.1[..]) let chad_account_id: [u8; 32] = hex::decode(&chad_info.1[..])
@ -191,8 +169,7 @@ impl AddressBook {
}); });
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());
@ -204,8 +181,7 @@ 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();
} }
@ -216,34 +192,21 @@ 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!( format!("provided public address for {} is not part of Casper/Ghost ecosystem", &address),
"provided public address for {} is not part of Casper/Ghost ecosystem", ActionLevel::Error);
&address
),
ActionLevel::Error,
);
} }
match self match self.address_book.iter().position(|record| record.address == address) {
.address_book
.iter()
.position(|record| record.address == address)
{
Some(index) => { Some(index) => {
let record = self let record = self.address_book
.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!( format!("record with name `{}` already stores address {}", &record.name, &record.address),
"record with name `{}` already stores address {}", ActionLevel::Warn);
&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];
@ -251,12 +214,8 @@ impl AddressBook {
let seed_str = hex::encode(seed_vec); let seed_str = hex::encode(seed_vec);
self.log_event( self.log_event(
format!( format!("account {} with address {} added to address book", &name, &address),
"account {} with address {} added to address book", ActionLevel::Info);
&name, &address
),
ActionLevel::Info,
);
self.address_book.push(BookRecord { self.address_book.push(BookRecord {
name, name,
@ -265,18 +224,16 @@ impl AddressBook {
seed: seed_str, seed: seed_str,
}); });
self.scroll_state = self.scroll_state = self.scroll_state.content_length(self.address_book.len());
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),
),
} }
} }
@ -284,7 +241,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()
)); ));
} }
} }
@ -330,13 +287,9 @@ 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( self.log_event(format!("record `{}` with public address {} removed",
format!( &record.name, &record.address),
"record `{}` with public address {} removed", ActionLevel::Warn);
&record.name, &record.address
),
ActionLevel::Warn,
);
} }
} }
@ -355,7 +308,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));
@ -378,8 +331,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);
@ -391,8 +344,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)
} }
} }
} }
@ -418,22 +371,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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;
@ -445,25 +390,24 @@ 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) => self.rename_record(new_name), Action::UpdateAddressBookRecord(new_name) =>
Action::NewAddressBookRecord(name, address) => self.add_new_record(name, address), self.rename_record(new_name),
Action::NewAddressBookRecord(name, address) =>
self.add_new_record(name, address),
Action::BalanceResponse(account_id, maybe_balance) => { Action::BalanceResponse(account_id, maybe_balance) => {
if self if self.address_book.iter().any(|record| record.account_id == account_id) {
.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 {
@ -477,7 +421,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)
@ -488,9 +432,10 @@ 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.iter().map(|info| { self.address_book
let balance = self .iter()
.balances .map(|info| {
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![
@ -499,20 +444,22 @@ impl Component for AddressBook {
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(Block::bordered()
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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title("Address Book"), .title("Address Book"));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -523,10 +470,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state, &mut self.scroll_state,
); );

View File

@ -1,13 +1,18 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Rect},
text::Text, text::Text,
layout::{Alignment, Constraint, Rect},
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame, Frame
}; };
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{action::Action, config::Config, palette::StylePalette, widgets::DotSpinner}; use crate::{
widgets::DotSpinner,
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)] #[derive(Debug)]
pub struct Balance { pub struct Balance {
@ -17,7 +22,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 {
@ -48,8 +53,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)
} }
} }
} }
@ -66,37 +71,31 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_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) => match maybe_balance { Action::BalanceSetActive(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 let transferable = balance.free
.free
.saturating_sub(balance.reserved) .saturating_sub(balance.reserved)
.saturating_sub(balance.frozen); .saturating_sub(balance.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;
@ -104,6 +103,7 @@ impl Component for Balance {
self.total_balance = None; self.total_balance = None;
self.nonce = None; self.nonce = None;
} }
}
}, },
_ => {} _ => {}
}; };
@ -112,60 +112,46 @@ 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.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(
[ [
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( Cell::from(Text::from(self.nonce
Text::from(
self.nonce
.map(|n| n.to_string()) .map(|n| n.to_string())
.unwrap_or(DotSpinner::default().to_string()), .unwrap_or(DotSpinner::default().to_string())
) ).alignment(Alignment::Right)),
.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( Cell::from(Text::from(self.prepare_u128(self.total_balance)).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_u128(self.transferable_balance)).alignment(Alignment::Right))
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( Cell::from(Text::from(self.prepare_u128(self.locked_balance)).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_u128(self.reserved_balance)).alignment(Alignment::Right)),
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(Block::bordered()
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("Balance"), .title("Balance"));
);
frame.render_widget(table, place); frame.render_widget(table, place);
Ok(()) Ok(())

View File

@ -1,14 +1,14 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame
}; };
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,7 +53,8 @@ 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(Action::EventLog(message, level, ActionTarget::WalletLog)); let _ = action_tx.send(
Action::EventLog(message, level, ActionTarget::WalletLog));
} }
} }
@ -87,25 +88,16 @@ 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( network_tx.send(Action::BondValidatorExtraFrom(self.account_secret_seed, amount, log_target))
self.account_secret_seed,
amount,
log_target,
))
} else { } else {
network_tx.send(Action::BondValidatorFrom( network_tx.send(Action::BondValidatorFrom(self.account_secret_seed, amount, log_target))
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) => { Err(err) => self.log_event(
self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error) format!("invalid amount, error: {err}"), ActionLevel::Error),
}
} }
} }
} }
@ -155,16 +147,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -178,7 +165,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)
@ -186,12 +173,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.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,
_ => {} _ => {}
}; };
@ -201,14 +185,13 @@ 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()).block( let input = Paragraph::new(self.amount.value())
Block::bordered() .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(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);
@ -218,7 +201,7 @@ impl Component for BondPopup {
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, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,53 +130,29 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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) => { Action::SetChoosenValidator(account_id, individual, total) => self.update_choosen_validator(account_id, individual, total),
self.update_choosen_validator(account_id, individual, total) Action::SetValidatorPrefs(commission, is_disabled, account_id) 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::SetValidatorPrefs(commission, is_disabled, account_id) Action::SetStakedAmountRatio(_, active_stake, account_id) if self.choosen == account_id => self.active_stake = active_stake.unwrap_or_default(),
if self.choosen == account_id => Action::SetValidatorLatestClaim(era_index, account_id) if self.choosen == account_id => self.latest_era_claim = era_index,
{
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;
} },
_ => {} _ => {}
}; };
@ -191,85 +167,54 @@ 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( Cell::from(Text::from(self.is_nomination_disabled.to_string()).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.others_len.to_string()).alignment(Alignment::Right)),
Text::from(self.others_len.to_string()).alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from( Cell::from(Text::from("Total staked".to_string()).alignment(Alignment::Left)),
Text::from("Total staked".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("Own stake".to_string()).alignment(Alignment::Left)), Cell::from(Text::from("Own stake".to_string()).alignment(Alignment::Left)),
Cell::from( Cell::from(Text::from(self.prepare_u128(self.own_balance)).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_stake_imbalance()).alignment(Alignment::Right)),
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( Cell::from(Text::from(format!("{:.4}%", self.commission)).alignment(Alignment::Right)),
Text::from(format!("{:.4}%", self.commission))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from( Cell::from(Text::from("Points ratio".to_string()).alignment(Alignment::Left)),
Text::from("Points ratio".to_string()).alignment(Alignment::Left), Cell::from(Text::from(format!("{:.4}%", self.points_ratio)).alignment(Alignment::Right)),
),
Cell::from(
Text::from(format!("{:.4}%", self.points_ratio))
.alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from( Cell::from(Text::from("In next era".to_string()).alignment(Alignment::Left)),
Text::from("In next era".to_string()).alignment(Alignment::Left), Cell::from(Text::from(self.prepare_state_string()).alignment(Alignment::Right)),
),
Cell::from(
Text::from(self.prepare_state_string()).alignment(Alignment::Right),
),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from( Cell::from(Text::from("Last payout".to_string()).alignment(Alignment::Left)),
Text::from("Last payout".to_string()).alignment(Alignment::Left), Cell::from(Text::from(format!("{} days ago", self.latest_era_claim)).alignment(Alignment::Right)),
),
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(Block::bordered()
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("Validator details"), .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,29 +1,33 @@
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::{Modifier, Stylize}; use ratatui::style::{Stylize, Modifier};
use ratatui::widgets::Clear; use ratatui::widgets::Clear;
use ratatui::{ use ratatui::{
layout::{Alignment, Rect},
text::{Line, Text}, text::{Line, Text},
layout::{Alignment, Rect},
widgets::{ widgets::{
Block, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, Block, Cell, Row, Table, TableState, Scrollbar, Padding,
TableState, ScrollbarOrientation, ScrollbarState,
}, },
Frame, Frame
}; };
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{PartialComponent, Component, CurrentTab};
use crate::types::{ActionLevel, ActionTarget, EraRewardPoints, Nominations}; use crate::types::{ActionLevel, Nominations, ActionTarget, EraRewardPoints};
use crate::{action::Action, config::Config, palette::StylePalette}; use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
pub struct CurrentValidators { pub struct CurrentValidators {
is_active: bool, is_active: bool,
@ -123,8 +127,7 @@ 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 let (selected_account_id, selected_points) = self.filtered_vector
.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();
@ -132,8 +135,7 @@ impl CurrentValidators {
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));
));
} }
} }
@ -143,24 +145,21 @@ 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.checked_validators.extend(self.my_nominations
self.my_nominations
.get(&self.account_id) .get(&self.account_id)
.map(|nom| nom.targets.clone()) .map(|nom| nom.targets.clone())
.unwrap_or_default(), .unwrap_or_default());
);
} }
fn choose_all_validators(&mut self) { fn choose_all_validators(&mut self) {
self.clear_choosen(); self.clear_choosen();
self.checked_validators self.checked_validators.extend(self.individual
.extend(self.individual.iter().map(|ind| ind.account_id)); .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 self.filtered_vector = self.individual
.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);
@ -194,8 +193,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 = let mut file = File::create(&self.known_validators_file)
File::create(&self.known_validators_file).expect("file should be accessible; qed"); .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);
@ -247,7 +246,7 @@ impl CurrentValidators {
} else { } else {
i + 1 i + 1
} }
} },
None => 0, None => 0,
}; };
self.move_selected(i); self.move_selected(i);
@ -268,8 +267,8 @@ impl CurrentValidators {
} else { } else {
i - 1 i - 1
} }
} },
None => 0, None => 0
}; };
self.move_selected(i); self.move_selected(i);
} }
@ -295,11 +294,7 @@ impl CurrentValidators {
} }
fn nominate_choosen(&mut self) { fn nominate_choosen(&mut self) {
if self if self.my_stash_id.map(|acc| acc == self.account_id).unwrap_or_default() {
.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(),
@ -308,15 +303,12 @@ 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 let nominate_targets: Vec<[u8; 32]> = self.checked_validators
.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, self.account_secret_seed, nominate_targets));
nominate_targets,
));
} }
} }
self.close_popup(); self.close_popup();
@ -324,8 +316,7 @@ 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 let nominations = self.my_nominations
.my_nominations
.get(&self.account_id) .get(&self.account_id)
.unwrap_or(&empty_nominations); .unwrap_or(&empty_nominations);
@ -337,9 +328,9 @@ impl CurrentValidators {
} else { } else {
"Active" "Active"
}; };
format!( format!("Submitted at era #{} | {} ",
"Submitted at era #{} | {} ", nominations.submitted_in,
nominations.submitted_in, status, status,
) )
} }
} }
@ -367,22 +358,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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);
@ -393,19 +376,12 @@ 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) => { Action::UpdateKnownValidator(validator_name) => self.save_validator_name(validator_name),
self.save_validator_name(validator_name) 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::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)
@ -428,7 +404,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)
@ -439,11 +415,7 @@ 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!( let top_title = format!("Validators {} | Total points: {}", self.individual.len(), self.total_points);
"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(
@ -451,18 +423,14 @@ impl Component for CurrentValidators {
.iter() .iter()
.chain(&self.not_active_nominations) .chain(&self.not_active_nominations)
.map(|info| { .map(|info| {
let is_validator_choosen = let is_validator_choosen = self.checked_validators.contains(&info.account_id);
self.checked_validators.contains(&info.account_id); let is_current_nomination = self.my_nominations
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 = let mut address_text = Text::from(info.address.clone()).alignment(Alignment::Center);
Text::from(info.address.clone()).alignment(Alignment::Center); let mut points_text = Text::from(info.points.to_string()).alignment(Alignment::Right);
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);
@ -479,14 +447,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 let default_name = self.my_stash_id
.my_stash_id .map(|account_id|
.map(|account_id| account_id.eq(&info.account_id).then(|| "My stash")) account_id.eq(&info.account_id).then(|| "My stash")
)
.flatten() .flatten()
.unwrap_or("Ghostie"); .unwrap_or("Ghostie");
let name = self let name = self.known_validators
.known_validators
.get(&info.account_id) .get(&info.account_id)
.cloned() .cloned()
.unwrap_or(default_name.to_string()); .unwrap_or(default_name.to_string());
@ -496,8 +464,7 @@ 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),
@ -509,15 +476,13 @@ impl Component for CurrentValidators {
.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(Block::bordered()
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)
@ -529,10 +494,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
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, Cell, Padding, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, Block, Padding, Cell, Row, Scrollbar, ScrollbarOrientation,
TableState, ScrollbarState, Table, TableState,
}, },
Frame, Frame
}; };
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,22 +112,14 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette self.palette.with_highlight_style(style.get("highlight_style").copied());
.with_hover_border_style(style.get("hover_border_style").copied()); self.palette.with_scrollbar_style(style.get("scrollbar_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(())
} }
@ -138,16 +130,15 @@ impl Component for EventLogs {
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)
@ -162,38 +153,33 @@ 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.iter().map(|log| { self.logs
.iter()
.map(|log| {
let style = match log.level { let style = match log.level {
ActionLevel::Info => info_style, ActionLevel::Info => info_style,
ActionLevel::Warn => warn_style, ActionLevel::Warn => warn_style,
ActionLevel::Error => error_style, ActionLevel::Error => error_style,
}; };
Row::new(vec![ Row::new(vec![
Cell::from( Cell::from(Text::from(log.time.format("%H:%M:%S").to_string()).style(style).alignment(Alignment::Left)),
Text::from(log.time.format("%H:%M:%S").to_string()) Cell::from(Text::from(log.message.clone()).style(style).alignment(Alignment::Left)),
.style(style)
.alignment(Alignment::Left),
),
Cell::from(
Text::from(log.message.clone())
.style(style)
.alignment(Alignment::Left),
),
]) ])
}), }),
[Constraint::Max(8), Constraint::Min(0)], [
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(Block::bordered()
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_alignment(Alignment::Right) .title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false)) .title_style(self.palette.create_title_style(false))
.title("Action Logs"), .title("Action Logs"));
);
let scrollbar = Scrollbar::default() let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight) .orientation(ScrollbarOrientation::VerticalRight)
@ -204,10 +190,7 @@ 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 { place.inner(Margin { vertical: 1, horizontal: 1 }),
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 account_details;
mod accounts;
mod add_account;
mod add_address_book_record;
mod address_book;
mod balance; mod balance;
mod bond_popup;
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 staking_ledger;
mod transfer; 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 staking_ledger;
mod bond_popup;
mod payee_popup;
mod current_validators;
mod current_validator_details;
mod rename_known_validator;
use account_details::AccountDetails;
use accounts::Accounts;
use add_account::AddAccount;
use add_address_book_record::AddAddressBookRecord;
use address_book::AddressBook;
use balance::Balance; use balance::Balance;
use bond_popup::BondPopup;
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 staking_ledger::StakingLedger;
use transfer::Transfer; 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 staking_ledger::StakingLedger;
use bond_popup::BondPopup;
use payee_popup::PayeePopup;
use current_validators::CurrentValidators;
use current_validator_details::CurrentValidatorDetails;
use rename_known_validator::RenameKnownValidator;
use super::Component; use super::Component;
use crate::{action::Action, app::Mode, config::Config}; use crate::{action::Action, app::Mode, config::Config};
@ -146,64 +146,62 @@ 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 { if !self.is_active { return Ok(None) }
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)
} }
@ -213,11 +211,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;
@ -225,11 +223,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,
_ => {} _ => {}
} }
@ -250,12 +248,18 @@ impl Component for Wallet {
} }
pub fn wallet_layout(area: Rect) -> [Rect; 2] { pub fn wallet_layout(area: Rect) -> [Rect; 2] {
Layout::vertical([Constraint::Percentage(75), Constraint::Percentage(25)]).areas(area) Layout::vertical([
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([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(place) Layout::horizontal([
Constraint::Percentage(50),
Constraint::Percentage(50),
]).areas(place)
} }
pub fn account_layout(area: Rect) -> [Rect; 4] { pub fn account_layout(area: Rect) -> [Rect; 4] {
@ -265,12 +269,14 @@ 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([Constraint::Percentage(60), Constraint::Percentage(40)]).areas(area) Layout::horizontal([
Constraint::Percentage(60),
Constraint::Percentage(40),
]).areas(area)
} }

View File

@ -1,20 +1,24 @@
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Rect},
text::Text, text::Text,
layout::{Alignment, Constraint, Rect},
widgets::{Block, Cell, Row, Table}, widgets::{Block, Cell, Row, Table},
Frame, Frame
}; };
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{action::Action, config::Config, palette::StylePalette, widgets::DotSpinner}; use crate::{
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 {
@ -42,8 +46,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)
} }
} }
} }
@ -60,18 +64,12 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_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(())
} }
@ -87,35 +85,31 @@ 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.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(
[ [
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( Cell::from(Text::from(self.prepare_u128(self.total_issuance)).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_u128(self.existential_balance)).alignment(Alignment::Right)),
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(Block::bordered()
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("Overview"), .title("Overview"));
);
frame.render_widget(table, place); frame.render_widget(table, place);
Ok(()) Ok(())

View File

@ -1,16 +1,18 @@
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
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 std::sync::mpsc::Sender; use subxt::ext::sp_core::crypto::{
use subxt::ext::sp_core::crypto::{AccountId32, ByteArray, Ss58AddressFormat, Ss58Codec}; ByteArray, Ss58Codec, Ss58AddressFormat, AccountId32,
};
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -34,7 +36,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 {
@ -59,18 +61,9 @@ 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)"),
"Re-stake", ("Stake", "(pay into the stash account, not increasing the amount at stake)"),
"(pay into the stash account, increasing the amount at stake accordingly)", ("Account", "(pay into a specified account different from stash)"),
),
(
"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(),
@ -92,7 +85,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()),
}; };
@ -113,12 +106,13 @@ 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) => {
@ -127,15 +121,18 @@ 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(message, level, ActionTarget::WalletLog)); let _ = action_tx.send(Action::EventLog(
message,
level,
ActionTarget::WalletLog));
} }
} }
@ -158,12 +155,8 @@ 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!( format!("provided public address for {} is not part of Casper/Ghost ecosystem", self.address.value()),
"provided public address for {} is not part of Casper/Ghost ecosystem", ActionLevel::Error);
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];
@ -171,18 +164,15 @@ 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!( format!("could not create valid account id from {}", self.address.value()),
"could not create valid account id from {}", ActionLevel::Error);
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) {
@ -191,8 +181,7 @@ impl PayeePopup {
0 => RewardDestination::Staked, 0 => RewardDestination::Staked,
1 => RewardDestination::Stash, 1 => RewardDestination::Stash,
2 => { 2 => {
let account_id = self let account_id = self.proposed_account_id
.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)
} }
@ -203,20 +192,17 @@ 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 {
@ -271,18 +257,12 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_style").copied());
.with_normal_title_style(style.get("normal_title_style").copied()); self.palette.with_highlight_style(style.get("highlight_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(())
} }
@ -297,7 +277,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 {
@ -306,7 +286,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(),
_ => {} _ => {},
}; };
} }
} }
@ -315,15 +295,10 @@ 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) => { Action::UsedAccount(account_id, secret_seed) => self.update_used_account(account_id, secret_seed),
self.update_used_account(account_id, secret_seed) Action::SetIsBonded(is_bonded, account_id) if self.account_id == account_id =>
} self.is_bonded = is_bonded,
Action::SetIsBonded(is_bonded, account_id) if self.account_id == account_id => { Action::SetStakingPayee(reward_destination, 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() {
@ -352,18 +327,16 @@ 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(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("Select reward destination"), .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);
@ -374,14 +347,13 @@ 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()).block( let input_amount = Paragraph::new(self.address.value())
Block::bordered() .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("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);
@ -393,7 +365,7 @@ impl Component for PayeePopup {
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 color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,7 +50,8 @@ 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(self.name.value().to_string())); let _ = action_tx.send(Action::UpdateAccountName(
self.name.value().to_string()));
let _ = action_tx.send(Action::ClosePopup); let _ = action_tx.send(Action::ClosePopup);
} }
} }
@ -93,16 +94,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -124,7 +120,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)
@ -133,14 +129,13 @@ 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()).block( let input = Paragraph::new(self.name.value())
Block::bordered() .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(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);
@ -150,7 +145,7 @@ impl Component for RenameAccount {
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 color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,8 +51,7 @@ 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);
} }
} }
@ -95,16 +94,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -126,7 +120,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)
@ -135,14 +129,13 @@ 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()).block( let input = Paragraph::new(self.name.value())
Block::bordered() .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(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);
@ -152,7 +145,7 @@ impl Component for RenameAddressBookRecord {
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 color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
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,7 +46,8 @@ 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(self.name.value().to_string())); let _ = action_tx.send(Action::UpdateKnownValidator(
self.name.value().to_string()));
} }
self.close_popup(); self.close_popup();
} }
@ -69,7 +70,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 {
@ -88,16 +89,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -111,7 +107,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)
@ -123,14 +119,13 @@ 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()).block( let input = Paragraph::new(self.name.value())
Block::bordered() .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("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);
@ -140,7 +135,7 @@ impl Component for RenameKnownValidator {
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::{
layout::{Alignment, Constraint, Rect},
text::Text, text::Text,
layout::{Alignment, Constraint, Rect},
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, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{ use crate::{
action::Action, config::Config, palette::StylePalette, types::RewardDestination, action::Action, config::Config, palette::StylePalette,
widgets::DotSpinner, types::RewardDestination, 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,18 +110,12 @@ 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 self.palette.with_normal_style(style.get("normal_style").copied());
.with_normal_style(style.get("normal_style").copied()); self.palette.with_hover_style(style.get("hover_style").copied());
self.palette self.palette.with_normal_border_style(style.get("normal_border_style").copied());
.with_hover_style(style.get("hover_style").copied()); self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette self.palette.with_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_hover_title_style(style.get("hover_title_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(())
} }
@ -129,20 +123,14 @@ 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 =>
Action::SetStakingPayee(reward_destination, account_id) self.reward_destination = reward_destination,
if self.account_id == account_id => Action::SetStakedAmountRatio(total, active, 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)
@ -150,7 +138,8 @@ 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.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(
[ [
@ -160,35 +149,28 @@ 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( Cell::from(Text::from(self.get_reward_destination()).alignment(Alignment::Right)),
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( Cell::from(Text::from(self.prepare_u128(self.total_staked)).alignment(Alignment::Right))
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( Cell::from(Text::from(self.prepare_u128(self.active_staked)).alignment(Alignment::Right)),
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(Block::bordered()
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("Nomination stake"), .title("Nomination stake"));
);
frame.render_widget(table, place); frame.render_widget(table, place);
Ok(()) Ok(())

View File

@ -5,12 +5,14 @@ use ratatui::{
widgets::{Block, Clear, Paragraph}, widgets::{Block, Clear, Paragraph},
Frame, Frame,
}; };
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use subxt::ext::sp_core::crypto::{AccountId32, ByteArray, Ss58AddressFormat, Ss58Codec}; use subxt::ext::sp_core::crypto::{
ByteArray, Ss58Codec, Ss58AddressFormat, AccountId32,
};
use super::{Component, CurrentTab, PartialComponent}; use super::{Component, PartialComponent, CurrentTab};
use crate::{ use crate::{
action::Action, action::Action,
config::Config, config::Config,
@ -66,21 +68,22 @@ 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(Action::EventLog(message, level, ActionTarget::WalletLog)); let _ = action_tx.send(
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 => self.receiver_or_amount = ReceiverOrAmount::Amount, ReceiverOrAmount::Receiver =>
ReceiverOrAmount::Amount => { self.receiver_or_amount = ReceiverOrAmount::Amount,
if let Some(network_tx) = &self.network_tx { ReceiverOrAmount::Amount => 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) => { Ok((account_id, format)) if format == Ss58AddressFormat::custom(1996) => {
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];
@ -96,28 +99,18 @@ impl Transfer {
Ok(value) => { Ok(value) => {
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::TransferBalance( let _ = network_tx.send(Action::TransferBalance(
self.sender.clone(), self.sender.clone(), account_id, amount));
account_id,
amount,
));
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) => self.log_event(
format!("invalid amount, error: {err}"), format!("invalid amount, error: {err}"), ActionLevel::Error),
ActionLevel::Error,
),
}
} }
},
_ => self.log_event( _ => self.log_event(
format!( format!("could not create valid account id from {}", self.receiver.value()),
"could not create valid account id from {}", ActionLevel::Error),
self.receiver.value()
),
ActionLevel::Error,
),
}
} }
} }
} }
@ -127,7 +120,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 {
@ -141,7 +134,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);
} }
@ -152,7 +145,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);
} }
@ -163,7 +156,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);
} }
@ -182,11 +175,12 @@ 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);
@ -200,16 +194,11 @@ 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 self.palette.with_normal_style(style.get("normal_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_normal_title_style(style.get("normal_title_style").copied());
.with_normal_border_style(style.get("normal_border_style").copied()); self.palette.with_popup_style(style.get("popup_style").copied());
self.palette self.palette.with_popup_title_style(style.get("popup_title_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());
} }
Ok(()) Ok(())
} }
@ -225,7 +214,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)
@ -250,23 +239,21 @@ 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()).block( let input_receiver = Paragraph::new(self.receiver.value())
Block::bordered() .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("Receiver"), .title("Receiver"));
);
let input_amount = Paragraph::new(self.amount.value()).block( let input_amount = Paragraph::new(self.amount.value())
Block::bordered() .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("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);
@ -285,13 +272,13 @@ impl Component for Transfer {
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,7 +93,9 @@ 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.entry(style_key.clone()).or_insert(*style); user_styles
.entry(style_key.clone())
.or_insert(*style);
} }
} }
@ -130,8 +132,7 @@ 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 where D: Deserializer<'de>,
D: Deserializer<'de>,
{ {
let parsed_map = HashMap::<Mode, HashMap<String, Action>>::deserialize(deserializer)?; let parsed_map = HashMap::<Mode, HashMap<String, Action>>::deserialize(deserializer)?;
@ -199,7 +200,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,
@ -252,12 +253,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 => "",
@ -326,8 +327,7 @@ 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 where D: Deserializer<'de>
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

@ -16,7 +16,8 @@ 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().with_default_directive(tracing::Level::INFO.into()); let env_filter = EnvFilter::builder()
.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,21 +1,24 @@
use clap::Parser; use clap::Parser;
use color_eyre::Result; use color_eyre::Result;
use subxt::{backend::rpc::RpcClient, OnlineClient}; use subxt::{
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 modes;
mod network;
mod palette;
mod tui; mod tui;
mod types; mod network;
mod widgets; mod widgets;
mod types;
mod palette;
mod casper;
use casper::{CasperAccountId, CasperConfig}; use casper::{CasperAccountId, CasperConfig};
@ -59,7 +62,11 @@ 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(cloned_action_tx, online_client, rpc_client); let mut network = network::Network::new(
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);
}); });
@ -77,8 +84,11 @@ 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 = let mut subscription = network::BestSubscription::new(
network::BestSubscription::new(cloned_action_tx, cloned_sync_tx, best_blocks_sub); cloned_action_tx,
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::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
#[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,22 +1,18 @@
use codec::{Decode, Encode};
use color_eyre::Result;
use subxt::{
backend::{legacy::rpc_methods::SystemHealth, rpc::RpcClient},
rpc_params,
};
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use color_eyre::Result;
use subxt::{backend::{legacy::rpc_methods::SystemHealth, rpc::RpcClient}, rpc_params};
use codec::{Encode, Decode};
use crate::{ use crate::{action::Action, network::miscellaneous::get_slow_clap_storage_key, types::{ActionLevel, ActionTarget, BlockRange, PeerInformation}};
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.request("system_name", rpc_params![]).await.ok(); let maybe_node_name = rpc_client
.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(())
} }
@ -29,18 +25,15 @@ 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(())
} }
@ -49,7 +42,10 @@ 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.request("chain_getBlockHash", params).await.ok(); let maybe_genesis_hash = rpc_client
.request("chain_getBlockHash", params)
.await
.ok();
action_tx.send(Action::SetGenesisHash(maybe_genesis_hash))?; action_tx.send(Action::SetGenesisHash(maybe_genesis_hash))?;
Ok(()) Ok(())
} }
@ -58,7 +54,10 @@ 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.request("system_chain", rpc_params![]).await.ok(); let maybe_chain_name = rpc_client
.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(())
} }
@ -138,7 +137,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);
@ -147,10 +146,7 @@ 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( .request("offchain_localStorageGet", rpc_params!["PERSISTENT", block_range_key])
"offchain_localStorageGet",
rpc_params!["PERSISTENT", block_range_key],
)
.await .await
.ok() .ok()
.map(|hex_string: String| { .map(|hex_string: String| {
@ -158,10 +154,7 @@ 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 { BlockRange { from_block, to_block }
from_block,
to_block,
}
}) })
.unwrap_or_default(); .unwrap_or_default();
@ -173,7 +166,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);
@ -189,10 +182,7 @@ pub async fn nullify_blocks(
} }
match rpc_client match rpc_client
.request::<()>( .request::<()>("offchain_localStorageSet", rpc_params!["PERSISTENT", block_range_key, zeroes_bytes])
"offchain_localStorageSet",
rpc_params!["PERSISTENT", block_range_key, zeroes_bytes],
)
.await .await
{ {
Ok(_) => action_tx.send(Action::EventLog( Ok(_) => action_tx.send(Action::EventLog(
@ -209,80 +199,3 @@ pub async fn nullify_blocks(
Ok(()) Ok(())
} }
pub async fn get_stored_rpc_endpoints(
action_tx: &UnboundedSender<Action>,
rpc_client: &RpcClient,
chain_id: u64,
) -> Result<()> {
let chain_id_encoded = chain_id.encode();
let endpoint_key_raw = get_slow_clap_storage_key(b"endpoint-", &chain_id_encoded);
let mut endpoint_key = String::from("0x");
for byte in endpoint_key_raw {
endpoint_key.push_str(&format!("{:02x}", byte));
}
let stored_rpc_endpoints: String = rpc_client
.request(
"offchain_localStorageGet",
rpc_params!["PERSISTENT", endpoint_key],
)
.await
.ok()
.unwrap_or_default();
let stored_rpc_endpoints = stored_rpc_endpoints.trim_start_matches("0x");
let scale_encoded = hex::decode(&stored_rpc_endpoints).unwrap();
let stored_rpc_endpoints = Vec::<String>::decode(&mut scale_encoded.as_slice())
.ok()
.unwrap_or_default();
action_tx.send(Action::SetStoredRpcEndpoints(stored_rpc_endpoints))?;
Ok(())
}
pub async fn set_stored_rpc_endpoints(
action_tx: &UnboundedSender<Action>,
rpc_client: &RpcClient,
chain_id: u64,
stored_endpoints: Vec<String>,
) -> Result<()> {
let chain_id_encoded = chain_id.encode();
let endpoint_key_raw = get_slow_clap_storage_key(b"endpoint-", &chain_id_encoded);
let mut endpoint_key = String::from("0x");
for byte in endpoint_key_raw {
endpoint_key.push_str(&format!("{:02x}", byte));
}
let stored_endpoints = stored_endpoints.encode();
let mut encoded_endpoints = String::from("0x");
for byte in stored_endpoints {
encoded_endpoints.push_str(&format!("{:02x}", byte));
}
match rpc_client
.request::<()>(
"offchain_localStorageSet",
rpc_params!["PERSISTENT", endpoint_key, encoded_endpoints],
)
.await
{
Ok(_) => {
action_tx.send(Action::EventLog(
format!("RPC endpoints updated for network #{:?}", chain_id),
ActionLevel::Info,
ActionTarget::ValidatorLog,
))?;
get_stored_rpc_endpoints(action_tx, rpc_client, chain_id).await?;
}
Err(err) => action_tx.send(Action::EventLog(
format!("RPC endpoints update failed: {:?}", err),
ActionLevel::Error,
ActionTarget::ValidatorLog,
))?,
};
Ok(())
}

View File

@ -10,130 +10,37 @@ const SLOW_CLAP_DB_PREFIX: &[u8] = b"slow_clap::";
// 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(690000000), (Perbill::from_parts(692740000), Perbill::from_parts(664536000)),
Perbill::from_parts(690000000), (Perbill::from_parts(695588000), Perbill::from_parts(639072000)),
), (Perbill::from_parts(698554000), Perbill::from_parts(613608000)),
( (Perbill::from_parts(701647000), Perbill::from_parts(588144000)),
Perbill::from_parts(692740000), (Perbill::from_parts(704879000), Perbill::from_parts(562680000)),
Perbill::from_parts(664536000), (Perbill::from_parts(708262000), Perbill::from_parts(537216000)),
), (Perbill::from_parts(711811000), Perbill::from_parts(511752000)),
( (Perbill::from_parts(715545000), Perbill::from_parts(486288000)),
Perbill::from_parts(695588000), (Perbill::from_parts(719482000), Perbill::from_parts(460824000)),
Perbill::from_parts(639072000), (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(698554000), (Perbill::from_parts(737811000), Perbill::from_parts(358968000)),
Perbill::from_parts(613608000), (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(701647000), (Perbill::from_parts(762447000), Perbill::from_parts(257112000)),
Perbill::from_parts(588144000), (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(704879000), (Perbill::from_parts(800151000), Perbill::from_parts(155256000)),
Perbill::from_parts(562680000), (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(708262000), (Perbill::from_parts(877801000), Perbill::from_parts(57460000)),
Perbill::from_parts(537216000), (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(711811000), (Perbill::from_parts(979651000), Perbill::from_parts(19220000)),
Perbill::from_parts(511752000), (Perbill::from_parts(1000000000), Perbill::from_parts(16291000)),
),
(
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);
@ -141,34 +48,23 @@ 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 let next_point_index = PIECEWISE_LINEAR_POUNTS.iter().position(|p| n < p.0 * d.clone());
.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 let fraction = PIECEWISE_LINEAR_POUNTS.first().map(|p| p.1).unwrap_or_else(Perbill::zero);
.first() return (MAXIMUM_INFLATION, fraction)
.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 let fraction = PIECEWISE_LINEAR_POUNTS.last().map(|p| p.1).unwrap_or_else(Perbill::zero);
.last() return (MAXIMUM_INFLATION, fraction)
.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(

View File

@ -1,28 +1,25 @@
use tokio::sync::mpsc::UnboundedSender;
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{
backend::rpc::RpcClient, backend::rpc::RpcClient, tx::{TxProgress, TxStatus}, utils::H256, OnlineClient
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 raw_calls;
mod subscriptions; mod subscriptions;
mod miscellaneous;
mod raw_calls;
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::{BestSubscription, FinalizedSubscription}; pub use subscriptions::{FinalizedSubscription, BestSubscription};
const GATEKEEPED_CHAIN_IDS: [u64; 1] = [ const GATEKEEPED_CHAIN_IDS: [u64; 1] = [
11155111, //Sepolia 11155111, //Sepolia
@ -72,12 +69,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),
} }
} }
@ -110,138 +107,40 @@ 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( predefined_calls::get_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &stash_to_watch).await?;
&self.action_tx, predefined_calls::get_queued_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &stash_to_watch).await?;
&self.online_client_api, predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
&self.rpc_client, predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
&stash_to_watch, 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?;
.await?; predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_queued_session_keys( predefined_calls::get_account_payee(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
&self.action_tx, predefined_calls::get_slashing_spans(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
&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( predefined_calls::get_validator_staking_result(&self.action_tx, &self.online_client_api, &stash_to_watch, *era_index).await?;
&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( predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
&self.action_tx, predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
&self.online_client_api, predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
&validator_details_to_watch, 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?;
.await?; predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_validator_prefs( predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
&self.action_tx, predefined_calls::get_nominators_by_account(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
&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( predefined_calls::get_balance(&self.action_tx, &self.online_client_api, &account_id).await?;
&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() {
@ -249,198 +148,69 @@ 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)) => match status { Some(Ok(status)) => {
TxStatus::Validated => self.action_tx.send(Action::EventLog( match status {
format!("transaction {} is part of future queue", ext_hash), TxStatus::Validated => self.action_tx.send(Action::EventLog(format!("transaction {} is part of future queue", ext_hash), ActionLevel::Info, log_target))?,
ActionLevel::Info, 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))?,
log_target, TxStatus::NoLongerInBestBlock => self.action_tx.send(Action::EventLog(format!("transaction {} is no longer in a best block", 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::Broadcasted { num_peers } => {
self.action_tx.send(Action::EventLog(
format!(
"transaction {} has been broardcasted to {} nodes",
ext_hash, num_peers
),
ActionLevel::Info,
log_target,
))?
}
TxStatus::NoLongerInBestBlock => {
self.action_tx.send(Action::EventLog(
format!(
"transaction {} is no longer in a best block",
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) => { TxStatus::InFinalizedBlock(b) => {
self.action_tx.send(Action::EventLog( self.action_tx.send(Action::EventLog(format!("transaction {} has been finalized in block header {}", b.extrinsic_hash(), b.block_hash()), ActionLevel::Info, log_target))?;
format!(
"transaction {} has been finalized in block header {}",
b.extrinsic_hash(),
b.block_hash()
),
ActionLevel::Info,
log_target,
))?;
self.transactions_to_watch.remove(i); self.transactions_to_watch.remove(i);
} }
TxStatus::Error { message } => { TxStatus::Error { message } => {
self.action_tx.send(Action::EventLog( self.action_tx.send(Action::EventLog(format!("transaction {} error, something get wrong: {message}", ext_hash), ActionLevel::Error, log_target))?;
format!(
"transaction {} error, something get wrong: {message}",
ext_hash
),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i); self.remove_transaction_and_decrement_nonce(i);
} }
TxStatus::Invalid { message } => { TxStatus::Invalid { message } => {
self.action_tx.send(Action::EventLog( self.action_tx.send(Action::EventLog(format!("transaction {} invalid: {message}", ext_hash), ActionLevel::Error, log_target))?;
format!("transaction {} invalid: {message}", ext_hash),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i); self.remove_transaction_and_decrement_nonce(i);
} }
TxStatus::Dropped { message } => { TxStatus::Dropped { message } => {
self.action_tx.send(Action::EventLog( self.action_tx.send(Action::EventLog(format!("transaction {} was dropped: {message}", ext_hash), ActionLevel::Error, log_target))?;
format!("transaction {} was dropped: {message}", ext_hash),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i); self.remove_transaction_and_decrement_nonce(i);
} }
}
}, },
_ => { _ => {
self.action_tx.send(Action::EventLog( self.action_tx.send(Action::EventLog(format!("transaction {} was dropped", ext_hash), ActionLevel::Error, log_target))?;
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 => { Action::GetSystemHealth => legacy_rpc_calls::get_system_health(&self.action_tx, &self.rpc_client).await,
legacy_rpc_calls::get_system_health(&self.action_tx, &self.rpc_client).await Action::GetNodeName => legacy_rpc_calls::get_node_name(&self.action_tx, &self.rpc_client).await,
} Action::GetGenesisHash => legacy_rpc_calls::get_genesis_hash(&self.action_tx, &self.rpc_client).await,
Action::GetNodeName => { Action::GetChainName => legacy_rpc_calls::get_chain_name(&self.action_tx, &self.rpc_client).await,
legacy_rpc_calls::get_node_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::GetGenesisHash => { Action::GetConnectedPeers => legacy_rpc_calls::get_connected_peers(&self.action_tx, &self.rpc_client).await,
legacy_rpc_calls::get_genesis_hash(&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::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) legacy_rpc_calls::get_block_range(&self.action_tx, &self.rpc_client, chain_id).await?;
.await?;
} }
Ok(()) Ok(())
} }
Action::GetBlockAuthor(hash, logs) => { Action::GetBlockAuthor(hash, logs) => predefined_calls::get_block_author(&self.action_tx, &self.online_client_api, &logs, &hash).await,
predefined_calls::get_block_author( Action::GetActiveEra => predefined_calls::get_active_era(&self.action_tx, &self.online_client_api).await,
&self.action_tx, Action::GetCurrentEra => predefined_calls::get_current_era(&self.action_tx, &self.online_client_api).await,
&self.online_client_api, Action::GetEpochProgress => predefined_calls::get_epoch_progress(&self.action_tx, &self.online_client_api).await,
&logs, Action::GetMinValidatorBond => predefined_calls::get_minimal_validator_bond(&self.action_tx, &self.online_client_api).await,
&hash, Action::GetGatekeepedNetwork(chain_id) => predefined_calls::get_gatekeeped_network(&self.action_tx, &self.online_client_api, chain_id).await,
)
.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 => {
predefined_calls::get_existential_deposit(&self.action_tx, &self.online_client_api) Action::GetExistentialDeposit => predefined_calls::get_existential_deposit(&self.action_tx, &self.online_client_api).await,
.await Action::GetTotalIssuance => predefined_calls::get_total_issuance(&self.action_tx, &self.online_client_api).await,
} Action::GetValidatorsNumber => predefined_calls::get_validators_number(&self.action_tx, &self.online_client_api).await,
Action::GetTotalIssuance => { Action::GetNominatorsNumber => predefined_calls::get_nominators_number(&self.action_tx, &self.online_client_api).await,
predefined_calls::get_total_issuance(&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::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);
@ -453,126 +223,59 @@ 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( predefined_calls::get_account_payee(&self.action_tx, &self.online_client_api, &account_id).await
&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( predefined_calls::get_validator_latest_claim(&self.action_tx, &self.online_client_api, &account_id).await
&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( predefined_calls::get_slashing_spans(&self.action_tx, &self.online_client_api, &account_id).await
&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( predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &account_id).await
&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( predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &account_id).await
&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( predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &account_id).await
&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( predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &account_id).await
&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( predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &account_id).await
&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( predefined_calls::get_nominators_by_account(&self.action_tx, &self.online_client_api, &account_id).await
&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( predefined_calls::get_validator_staking_results(&self.action_tx, &self.online_client_api, &account_id).await
&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( predefined_calls::get_queued_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &account_id).await
&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( predefined_calls::get_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &account_id).await
&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( predefined_calls::get_balance(&self.action_tx, &self.online_client_api, &account_id).await
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
} }
} }
Action::TransferBalance(sender, receiver, amount) => { Action::TransferBalance(sender, receiver, amount) => {
@ -592,9 +295,7 @@ 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,
@ -614,9 +315,7 @@ 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,
@ -635,9 +334,7 @@ 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,
@ -656,9 +353,7 @@ 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,
@ -677,9 +372,7 @@ 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,
@ -697,9 +390,7 @@ 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,
@ -716,9 +407,7 @@ 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,
@ -736,9 +425,7 @@ 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,
@ -756,9 +443,7 @@ 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,
@ -776,9 +461,7 @@ 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,
@ -797,9 +480,7 @@ 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,
@ -817,9 +498,7 @@ 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,
@ -834,19 +513,9 @@ impl Network {
&self.rpc_client, &self.rpc_client,
chain_id, chain_id,
new_block, new_block,
) ).await
.await
} }
Action::UpdateStoredRpcEndpoints(chain_id, stored_endpoints) => { _ => Ok(())
legacy_rpc_calls::set_stored_rpc_endpoints(
&self.action_tx,
&self.rpc_client,
chain_id,
stored_endpoints,
)
.await
}
_ => Ok(()),
} }
} }
} }

View File

@ -1,24 +1,22 @@
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::{AccountId32, Ss58AddressFormat, Ss58Codec}, ext::sp_runtime::Saturating,
ext::sp_core::crypto::{
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::{ casper_network::runtime_types::{ghost_networks::NetworkType, pallet_staking::RewardDestination, sp_consensus_slots},
ghost_networks::NetworkType, pallet_staking::RewardDestination, sp_consensus_slots, types::{EraInfo, EraRewardPoints, Gatekeeper, Nominations, Nominator, SlashingSpan, SessionKeyInfo, SystemAccount, UnlockChunk},
}, 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(
@ -27,29 +25,22 @@ pub async fn get_block_author(
logs: &Vec<DigestItem>, logs: &Vec<DigestItem>,
at_hash: &H256, at_hash: &H256,
) -> Result<()> { ) -> Result<()> {
use crate::casper_network::runtime_types::sp_consensus_babe::digests::PreDigest;
use codec::Decode; use codec::Decode;
use crate::casper_network::runtime_types::sp_consensus_babe::digests::PreDigest;
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 let maybe_author = match logs.iter().find(|item| matches!(item, DigestItem::PreRuntime(..))) {
.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)) => { Ok(PreDigest::SecondaryPlain(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),
}
Ok(PreDigest::SecondaryVRF(secondary)) => {
validators.get(secondary.authority_index as usize)
}
_ => None, _ => None,
} }
} },
_ => None, _ => None,
}; };
@ -59,7 +50,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(),
}; };
@ -121,7 +112,8 @@ 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).await?; let maybe_total_issuance = super::raw_calls::balances::total_issuance(api, None)
.await?;
action_tx.send(Action::SetTotalIssuance(maybe_total_issuance))?; action_tx.send(Action::SetTotalIssuance(maybe_total_issuance))?;
Ok(()) Ok(())
} }
@ -147,7 +139,8 @@ pub async fn get_balance(
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(())
} }
@ -193,11 +186,10 @@ 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| { .map(|imbalance| total_issuance
total_issuance
.saturating_add(imbalance.bridged_out) .saturating_add(imbalance.bridged_out)
.saturating_sub(imbalance.bridged_in) .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)
@ -239,7 +231,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()),
}; };
@ -261,7 +253,8 @@ 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) => match session_keys.iter().find(|tuple| tuple.0 == account) { Some(session_keys) => {
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));
@ -270,8 +263,9 @@ pub async fn get_queued_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()),
}
}, },
None => (String::new(), String::new(), String::new(), String::new()), None => (String::new(), String::new(), String::new(), String::new()),
}; };
@ -300,7 +294,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(())
@ -349,14 +343,14 @@ pub async fn get_current_validator_reward_in_era(
.await? .await?
.unwrap_or_default(); .unwrap_or_default();
let maybe_era_reward_points = let maybe_era_reward_points = super::raw_calls::staking::eras_reward_points(api, None, era_index)
super::raw_calls::staking::eras_reward_points(api, None, era_index).await?; .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))| {
@ -370,15 +364,13 @@ pub async fn get_current_validator_reward_in_era(
} }
}) })
.collect(), .collect(),
), )
},
None => (0, Vec::new()), None => (0, Vec::new()),
}; };
action_tx.send(Action::SetCurrentValidatorEraRewards( action_tx.send(Action::SetCurrentValidatorEraRewards(
era_index, era_index, total_points, individual))?;
total_points,
individual,
))?;
Ok(()) Ok(())
} }
@ -389,16 +381,12 @@ 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 = let maybe_era_reward_points = super::raw_calls::staking::eras_reward_points(api, None, era_index).await?;
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 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 let my_points = era_reward_points.individual
.individual
.iter() .iter()
.find(|(acc, _)| acc.0 == *account_id) .find(|(acc, _)| acc.0 == *account_id)
.map(|info| info.1) .map(|info| info.1)
@ -406,7 +394,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,
}; };
@ -421,11 +409,14 @@ 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 = let maybe_claimed_rewards = super::raw_calls::staking::claimed_rewards(api, None, era_index, account_id)
super::raw_calls::staking::claimed_rewards(api, None, era_index, account_id).await?; .await?;
if let Some(claimed_rewards) = maybe_claimed_rewards { if let Some(claimed_rewards) = maybe_claimed_rewards {
let already_claimed = claimed_rewards.first().map(|x| *x == 0).unwrap_or(false); let already_claimed = claimed_rewards
.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))?;
} }
@ -438,8 +429,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 = let maybe_slash_in_era = super::raw_calls::staking::validator_slash_in_era(api, None, era_index, account_id)
super::raw_calls::staking::validator_slash_in_era(api, None, era_index, account_id).await?; .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))?;
@ -453,26 +444,18 @@ 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).await?; let maybe_ledger = super::raw_calls::staking::ledger(api, None, account_id)
.await?;
match maybe_ledger { match maybe_ledger {
Some(ledger) => { Some(ledger) => {
let chunks = ledger let chunks = ledger.unlocking.0
.unlocking
.0
.iter() .iter()
.map(|chunk| UnlockChunk { .map(|chunk| UnlockChunk { value: chunk.value, era: chunk.era })
value: chunk.value,
era: chunk.era,
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
action_tx.send(Action::SetStakedAmountRatio( action_tx.send(Action::SetStakedAmountRatio(Some(ledger.total), Some(ledger.active), *account_id))?;
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))?;
@ -490,8 +473,7 @@ 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: n.targets
.targets
.0 .0
.into_iter() .into_iter()
.map(|account_id_32| account_id_32.0) .map(|account_id_32| account_id_32.0)
@ -514,8 +496,7 @@ 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 = let maybe_eras_stakers_overview = super::raw_calls::staking::eras_stakers_overview(api, None, active_era_index, account_id)
super::raw_calls::staking::eras_stakers_overview(api, None, active_era_index, account_id)
.await?; .await?;
let nominators = match maybe_eras_stakers_overview { let nominators = match maybe_eras_stakers_overview {
@ -523,18 +504,10 @@ pub async fn get_nominators_by_validator(
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( let nominators = super::raw_calls::staking::eras_stakers_paged(api, None, active_era_index, page_index, account_id)
api,
None,
active_era_index,
page_index,
account_id,
)
.await?; .await?;
others.append( others.append(&mut nominators
&mut nominators .map(|n| n.others
.map(|n| {
n.others
.iter() .iter()
.map(|info| Nominator { .map(|info| Nominator {
account_id: info.who.0, account_id: info.who.0,
@ -543,12 +516,12 @@ pub async fn get_nominators_by_validator(
value: info.value, value: info.value,
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
}) )
.unwrap_or_default(), .unwrap_or_default()
); );
} }
others others
} },
None => Vec::new(), None => Vec::new(),
}; };
@ -577,8 +550,7 @@ 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 = let maybe_era_stakers_overview = super::raw_calls::staking::eras_stakers_overview(api, None, active_era_index, account_id)
super::raw_calls::staking::eras_stakers_overview(api, None, active_era_index, account_id)
.await?; .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),
@ -593,8 +565,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 = let maybe_validator_prefs = super::raw_calls::staking::validators(api, None, account_id)
super::raw_calls::staking::validators(api, None, account_id).await?; .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),
@ -621,12 +593,22 @@ pub async fn get_slashing_spans(
) -> Result<()> { ) -> Result<()> {
let slashing_spans_length = super::raw_calls::staking::slashing_spans(api, None, account_id) let slashing_spans_length = super::raw_calls::staking::slashing_spans(api, None, account_id)
.await? .await?
.map(|spans| spans.prior.len().saturating_add(1)) .map(|spans| {
let mut last_start = spans.last_start;
let mut index = spans.span_index;
let last = SlashingSpan { index, start: last_start, length: None };
let prior = spans.prior.iter().cloned().map(move |length| {
let start = last_start.saturating_sub(length);
last_start = start;
index.saturating_reduce(1);
SlashingSpan { index, start, length: Some(length) }
});
std::iter::once(last).chain(prior).count()
})
.unwrap_or_default(); .unwrap_or_default();
action_tx.send(Action::SetSlashingSpansLength( action_tx.send(Action::SetSlashingSpansLength(slashing_spans_length, *account_id))?;
slashing_spans_length,
*account_id,
))?;
Ok(()) Ok(())
} }
@ -645,8 +627,7 @@ 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 = let is_claimed = super::raw_calls::staking::claimed_rewards(api, None, claimed_era, account_id)
super::raw_calls::staking::claimed_rewards(api, None, claimed_era, account_id)
.await? .await?
.map(|claimed_rewards| claimed_rewards.len() > 0) .map(|claimed_rewards| claimed_rewards.len() > 0)
.unwrap_or_default(); .unwrap_or_default();
@ -658,10 +639,7 @@ pub async fn get_validator_latest_claim(
claimed_era -= 1; claimed_era -= 1;
} }
action_tx.send(Action::SetValidatorLatestClaim( action_tx.send(Action::SetValidatorLatestClaim(current_era.saturating_sub(claimed_era), *account_id))?;
current_era.saturating_sub(claimed_era),
*account_id,
))?;
Ok(()) Ok(())
} }
@ -676,9 +654,7 @@ 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) => { RewardDestination::Account(account_id_32) => crate::types::RewardDestination::Account(account_id_32.0),
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,
}) })
@ -696,18 +672,15 @@ 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).to_string(), chain_name: String::from_utf8_lossy(&network.chain_name)
.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 gatekeeper: String::from_utf8_lossy(&network.gatekeeper)
.default_endpoints .to_string(),
.iter()
.map(|endpoint| String::from_utf8_lossy(&endpoint).to_string())
.collect(),
gatekeeper: String::from_utf8_lossy(&network.gatekeeper).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,9 +22,7 @@ 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() let transfer_tx = casper_network::tx().balances().transfer_allow_death(receiver_id, *amount);
.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,
@ -33,8 +31,7 @@ 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(
@ -54,8 +51,7 @@ 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(
@ -67,11 +63,8 @@ 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 = let reward_destination = casper_network::runtime_types::pallet_staking::RewardDestination::Staked;
casper_network::runtime_types::pallet_staking::RewardDestination::Staked; let bond_tx = casper_network::tx().staking().bond(*amount, reward_destination);
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,
@ -80,8 +73,7 @@ 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(
@ -93,9 +85,7 @@ 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() let payout_stakers_tx = casper_network::tx().staking().payout_stakers(stash_id, era_index);
.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,
@ -104,8 +94,7 @@ 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(
@ -118,26 +107,10 @@ 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]) hex::decode(&s[0..64]).unwrap().as_slice().try_into().unwrap(),
.unwrap() hex::decode(&s[64..128]).unwrap().as_slice().try_into().unwrap(),
.as_slice() hex::decode(&s[128..192]).unwrap().as_slice().try_into().unwrap(),
.try_into() hex::decode(&s[192..256]).unwrap().as_slice().try_into().unwrap(),
.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 {
@ -148,9 +121,7 @@ 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() let set_keys_tx = casper_network::tx().session().set_keys(session_keys, Vec::new());
.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,
@ -159,8 +130,7 @@ 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(
@ -183,8 +153,7 @@ 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(
@ -202,8 +171,7 @@ 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(
@ -222,8 +190,7 @@ 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(
@ -242,8 +209,7 @@ 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(
@ -262,8 +228,7 @@ 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(
@ -275,18 +240,10 @@ 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 => { RewardDestination::Staked => casper_network::runtime_types::pallet_staking::RewardDestination::Staked,
casper_network::runtime_types::pallet_staking::RewardDestination::Staked RewardDestination::Stash => casper_network::runtime_types::pallet_staking::RewardDestination::Stash,
} RewardDestination::Controller => casper_network::runtime_types::pallet_staking::RewardDestination::Controller,
RewardDestination::Stash => { RewardDestination::None => casper_network::runtime_types::pallet_staking::RewardDestination::None,
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)
@ -301,8 +258,7 @@ 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(
@ -325,8 +281,7 @@ 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(
@ -345,26 +300,21 @@ 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) => { Ok(tx_progress) => {
action_tx.send(Action::EventLog( action_tx.send(Action::EventLog(
format!( format!("{tx_name} transaction {} sent", tx_progress.extrinsic_hash()),
"{tx_name} transaction {} sent",
tx_progress.extrinsic_hash()
),
ActionLevel::Info, ActionLevel::Info,
target, target))?;
))?;
Ok(tx_progress) Ok(tx_progress)
} },
Err(err) => { Err(err) => {
if let Some(ref mut nonce) = maybe_nonce { if let Some(ref mut nonce) = maybe_nonce {
**nonce = nonce.saturating_sub(1); **nonce = nonce.saturating_sub(1);
@ -372,8 +322,7 @@ async fn inner_sign_and_submit_then_watch(
action_tx.send(Action::EventLog( action_tx.send(Action::EventLog(
format!("error during {tx_name} transaction: {err}"), format!("error during {tx_name} transaction: {err}"),
ActionLevel::Error, ActionLevel::Error,
target, target))?;
))?;
Err(err.into()) Err(err.into())
} }
} }

View File

@ -1,11 +1,11 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{client::OnlineClient, utils::H256}; use subxt::{
utils::H256,
use crate::{ client::OnlineClient,
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>,
at_hash: Option<&H256>, at_hash: Option<&H256>,
@ -33,7 +33,9 @@ pub async fn genesis_slot(
Ok(maybe_genesis_slot) Ok(maybe_genesis_slot)
} }
pub fn epoch_duration(online_client: &OnlineClient<CasperConfig>) -> Result<u64> { pub fn epoch_duration(
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,7 +1,10 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{client::OnlineClient, utils::H256}; use subxt::{
utils::H256,
client::OnlineClient,
};
use crate::{casper_network, CasperConfig}; use crate::{CasperConfig, casper_network};
pub async fn total_issuance( pub async fn total_issuance(
online_client: &OnlineClient<CasperConfig>, online_client: &OnlineClient<CasperConfig>,
@ -12,7 +15,9 @@ pub async fn total_issuance(
Ok(maybe_total_issuance) Ok(maybe_total_issuance)
} }
pub fn existential_deposit(online_client: &OnlineClient<CasperConfig>) -> Result<u128> { pub fn existential_deposit(
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 babe;
pub mod balances;
pub mod networks;
pub mod session; pub mod session;
pub mod staking; pub mod staking;
pub mod system; pub mod system;
pub mod babe;
pub mod balances;
pub mod networks;
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,10 +24,17 @@ 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.backend().latest_finalized_block_ref().await?, None => online_client
.backend()
.latest_finalized_block_ref()
.await?,
}; };
online_client.storage().at(at_hash).fetch(storage_key).await online_client
.storage()
.at(at_hash)
.fetch(storage_key)
.await
} }
pub fn do_constant_call<'address, Addr>( pub fn do_constant_call<'address, Addr>(
@ -35,12 +42,10 @@ 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 constant_client.validate(constant_query).expect("constant query should be correct; qed");
.validate(constant_query)
.expect("constant query should be correct; qed");
constant_client.at(constant_query) constant_client.at(constant_query)
} }

View File

@ -1,5 +1,8 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{client::OnlineClient, utils::H256}; use subxt::{
utils::H256,
client::OnlineClient,
};
use crate::{ use crate::{
casper_network::{ casper_network::{
@ -13,11 +16,8 @@ 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() let storage_key = casper_network::storage().ghost_networks().bridged_imbalance();
.ghost_networks() let maybe_bridged_imbalance = super::do_storage_call(online_client, &storage_key, at_hash).await?;
.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,11 +25,8 @@ 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() let storage_key = casper_network::storage().ghost_networks().accumulated_commission();
.ghost_networks() let maybe_accumulated_commission = super::do_storage_call(online_client, &storage_key, at_hash).await?;
.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)
} }
@ -38,9 +35,7 @@ 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() let storage_key = casper_network::storage().ghost_networks().networks(chain_id);
.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,13 +1,10 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{ use subxt::{
client::OnlineClient,
utils::{AccountId32, H256}, utils::{AccountId32, H256},
client::OnlineClient,
}; };
use crate::{ use crate::{casper_network::{self, runtime_types::casper_runtime::opaque}, CasperConfig};
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, Nominations, slashing::SlashingSpans, ActiveEraInfo, EraRewardPoints,
RewardDestination, StakingLedger, ValidatorPrefs, RewardDestination, StakingLedger, ValidatorPrefs, Nominations,
}, },
sp_arithmetic::per_things::Perbill, sp_arithmetic::per_things::Perbill,
sp_staking::{ExposurePage, PagedExposureMetadata}, sp_staking::{ExposurePage, PagedExposureMetadata},
@ -42,8 +42,7 @@ 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 = let maybe_counter_for_validators = super::do_storage_call(online_client, &storage_key, at_hash).await?;
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_counter_for_validators) Ok(maybe_counter_for_validators)
} }
@ -52,8 +51,7 @@ 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 = let maybe_counter_for_nominators = super::do_storage_call(online_client, &storage_key, at_hash).await?;
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_counter_for_nominators) Ok(maybe_counter_for_nominators)
} }
@ -64,9 +62,7 @@ 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) let maybe_nominators = super::do_storage_call(online_client, &storage_key, at_hash).await.unwrap();
.await
.unwrap();
Ok(maybe_nominators) Ok(maybe_nominators)
} }
@ -75,11 +71,8 @@ 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() let storage_key = casper_network::storage().staking().eras_total_stake(era_index);
.staking() let maybe_eras_total_stake = super::do_storage_call(online_client, &storage_key, at_hash).await?;
.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)
} }
@ -88,11 +81,8 @@ 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() let storage_key = casper_network::storage().staking().eras_validator_reward(era_index);
.staking() let maybe_eras_validator_reward = super::do_storage_call(online_client, &storage_key, at_hash).await?;
.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)
} }
@ -101,11 +91,8 @@ 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() let storage_key = casper_network::storage().staking().eras_reward_points(era_index);
.staking() let maybe_eras_reward_points = super::do_storage_call(online_client, &storage_key, at_hash).await?;
.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)
} }
@ -116,11 +103,8 @@ 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() let storage_key = casper_network::storage().staking().claimed_rewards(era_index, account_id);
.staking() let maybe_claimed_rewards = super::do_storage_call(online_client, &storage_key, at_hash).await?;
.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)
} }
@ -131,11 +115,8 @@ 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() let storage_key = casper_network::storage().staking().validator_slash_in_era(era_index, account_id);
.staking() let maybe_validator_slash_in_era = super::do_storage_call(online_client, &storage_key, at_hash).await?;
.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)
} }
@ -158,11 +139,8 @@ 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() let storage_key = casper_network::storage().staking().eras_stakers_paged(era_index, account_id, page_index);
.staking() let maybe_eras_stakers_paged = super::do_storage_call(online_client, &storage_key, at_hash).await?;
.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)
} }
@ -184,11 +162,8 @@ 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() let storage_key = casper_network::storage().staking().eras_stakers_overview(era_index, account_id);
.staking() let maybe_eras_stakers_overview = super::do_storage_call(online_client, &storage_key, at_hash).await?;
.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)
} }
@ -208,8 +183,7 @@ 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 = let maybe_disabled_validators = super::do_storage_call(online_client, &storage_key, at_hash).await?;
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_disabled_validators) Ok(maybe_disabled_validators)
} }
@ -218,8 +192,7 @@ 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 = let maybe_min_validator_bond = super::do_storage_call(online_client, &storage_key, at_hash).await?;
super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_min_validator_bond) Ok(maybe_min_validator_bond)
} }
@ -229,9 +202,7 @@ 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() let storage_key = casper_network::storage().staking().slashing_spans(account_id);
.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)
} }
@ -247,7 +218,9 @@ pub async fn payee(
Ok(maybe_payee) Ok(maybe_payee)
} }
pub fn history_depth(online_client: &OnlineClient<CasperConfig>) -> Result<u32> { pub fn history_depth(
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,5 +1,8 @@
use color_eyre::Result; use color_eyre::Result;
use subxt::{client::OnlineClient, utils::H256}; use subxt::{
utils::H256,
client::OnlineClient,
};
use crate::{ use crate::{
casper_network::{ casper_network::{

View File

@ -1,4 +1,8 @@
use crate::{action::Action, casper::CasperBlock, types::CasperExtrinsicDetails}; use crate::{
types::CasperExtrinsicDetails,
action::Action,
casper::CasperBlock,
};
use color_eyre::Result; use color_eyre::Result;
use super::GATEKEEPED_CHAIN_IDS; use super::GATEKEEPED_CHAIN_IDS;
@ -15,11 +19,7 @@ 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 { Self { action_tx, network_tx, finalized_blocks_sub }
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,18 +52,12 @@ impl FinalizedSubscription {
)); ));
} }
self.action_tx self.action_tx.send(Action::FinalizedBlockInformation(block_hash, block_number))?;
.send(Action::FinalizedBlockInformation(block_hash, block_number))?; self.action_tx.send(Action::ExtrinsicsForBlock(block_number, extrinsic_details))?;
self.action_tx self.action_tx.send(Action::NewFinalizedBlock(block_number))?;
.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( self.network_tx.send(Action::GetBlockAuthor(block_hash, block.header().digest.logs.clone()))?;
block_hash,
block.header().digest.logs.clone(),
))?;
} }
Ok(()) Ok(())
} }
@ -81,11 +75,7 @@ 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 { Self { action_tx, network_tx, best_blocks_sub }
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<()> {
@ -119,21 +109,14 @@ impl BestSubscription {
)); ));
} }
self.action_tx self.action_tx.send(Action::BestBlockInformation(block_hash, block_number))?;
.send(Action::BestBlockInformation(block_hash, block_number))?; self.action_tx.send(Action::ExtrinsicsForBlock(block_number, extrinsic_details))?;
self.action_tx self.action_tx.send(Action::BestBlockUpdated(block_number))?;
.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 self.action_tx.send(Action::ExtrinsicsLength(block_number, extrinsics_length))?;
.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( self.network_tx.send(Action::GetBlockAuthor(block_hash, block.header().digest.logs.clone()))?;
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)?;
@ -141,13 +124,11 @@ 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 self.network_tx.send(Action::GetCurrentValidatorEraRewards)?;
.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 self.network_tx.send(Action::GetGatekeepedNetwork(chain_id))?;
.send(Action::GetGatekeepedNetwork(chain_id))?;
} }
} }
Ok(()) Ok(())

View File

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

View File

@ -8,14 +8,15 @@ use color_eyre::Result;
use crossterm::{ use crossterm::{
cursor, cursor,
event::{ event::{
DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste,
Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind, MouseEvent, EnableMouseCapture, Event as CrosstermEvent, EventStream, KeyEvent,
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::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
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::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
#[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::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
#[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::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
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,24 @@
mod account;
mod era; mod era;
mod extrinsics; mod extrinsics;
mod log; mod log;
mod networks; mod account;
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 networks::BlockRange; pub use account::SystemAccount;
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 staking::RewardDestination; pub use nominator::Nominator;
pub use nominator::Nominations;
pub use staking::UnlockChunk; pub use staking::UnlockChunk;
pub use staking::RewardDestination;
pub use staking::SlashingSpan;
pub use networks::Gatekeeper;
pub use networks::BlockRange;

View File

@ -1,5 +1,5 @@
use codec::Decode; use codec::Decode;
use serde::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct Gatekeeper { pub struct Gatekeeper {
@ -9,7 +9,6 @@ pub struct Gatekeeper {
pub gatekeeper: String, pub gatekeeper: String,
pub incoming_fee: u32, pub incoming_fee: u32,
pub outgoing_fee: u32, pub outgoing_fee: u32,
pub default_endpoints: Vec<String>,
} }
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Decode)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Decode)]

View File

@ -1,5 +1,5 @@
use codec::Decode; use codec::Decode;
use serde::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
#[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 codec::Decode;
use serde::{Deserialize, Serialize};
use subxt::utils::H256; use subxt::utils::H256;
use codec::Decode;
use serde::{Serialize, Deserialize};
#[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::{Deserialize, Serialize}; use serde::{Serialize, Deserialize};
#[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 {
@ -8,6 +8,13 @@ pub struct UnlockChunk {
pub era: u32, pub era: u32,
} }
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct SlashingSpan {
pub index: u32,
pub start: u32,
pub length: Option<u32>
}
#[derive(Default, Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)] #[derive(Default, Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
pub enum RewardDestination { pub enum RewardDestination {
#[default] #[default]

View File

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

View File

@ -7,10 +7,7 @@ pub struct OghamCenter {
impl Default for OghamCenter { impl Default for OghamCenter {
fn default() -> Self { fn default() -> Self {
Self { Self {
elements: PROGRESS_CENTER elements: PROGRESS_CENTER.iter().map(|s| s.to_string()).collect::<Vec<_>>(),
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>(),
} }
} }
} }