rustfmt whole project

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

View File

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

View File

@ -1,13 +1,13 @@
use serde::{Deserialize, Serialize};
use strum::Display;
use subxt::utils::H256;
use subxt::config::substrate::DigestItem;
use subxt::utils::H256;
use crate::types::{
ActionLevel, ActionTarget, CasperExtrinsicDetails, EraInfo, EraRewardPoints,
Nominator, Nominations, PeerInformation, SessionKeyInfo, UnlockChunk, SystemAccount,
RewardDestination, Gatekeeper, BlockRange,
ActionLevel, ActionTarget, BlockRange, CasperExtrinsicDetails, EraInfo, EraRewardPoints,
Gatekeeper, Nominations, Nominator, PeerInformation, RewardDestination, SessionKeyInfo,
SystemAccount, UnlockChunk,
};
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
@ -141,7 +141,7 @@ pub enum Action {
SetActiveEra(EraInfo),
SetCurrentEra(u32),
SetEpochProgress(u64, u64),
SetPendingExtrinsicsLength(usize),
SetPendingExtrinsicsLength(usize),
SetConnectedPeers(Vec<PeerInformation>),
SetSessionKey(String, SessionKeyInfo),
SetListenAddresses(Vec<String>),

View File

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

View File

@ -1,8 +1,10 @@
//! Casper specific configuration
use subxt::{
Config, blocks::Block, client::OnlineClient,
config::{DefaultExtrinsicParamsBuilder, DefaultExtrinsicParams, SubstrateConfig},
blocks::Block,
client::OnlineClient,
config::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder, SubstrateConfig},
Config,
};
/// Default set of commonly used type by Casper nodes.
@ -21,7 +23,7 @@ impl Config for CasperConfig {
}
/// A 32-byte cryptographic identifier. This is a simplified version of
/// `sp_core::crypto::AccountId32`.
/// `sp_core::crypto::AccountId32`.
pub type CasperAccountId = subxt::utils::AccountId32;
pub type CasperBlock = Block<CasperConfig, OnlineClient<CasperConfig>>;

View File

@ -1,5 +1,5 @@
use color_eyre::Result;
use crossterm::event::{KeyEvent, KeyCode};
use crossterm::event::{KeyCode, KeyEvent};
use ratatui::{
layout::{Alignment, Rect},
text::Line,
@ -8,10 +8,7 @@ use ratatui::{
};
use super::Component;
use crate::{
components::generic::Activatable,
action::Action, app::Mode
};
use crate::{action::Action, app::Mode, components::generic::Activatable};
#[derive(Debug, Clone, Default)]
pub struct Empty {
@ -19,10 +16,18 @@ pub struct Empty {
}
impl Activatable for Empty {
fn is_active(&self) -> bool { self.is_active }
fn is_inactive(&self) -> bool { !self.is_active }
fn set_inactive(&mut self) { self.is_active = false; }
fn set_active(&mut self) { self.is_active = true; }
fn is_active(&self) -> bool {
self.is_active
}
fn is_inactive(&self) -> bool {
!self.is_active
}
fn set_inactive(&mut self) {
self.is_active = false;
}
fn set_active(&mut self) {
self.is_active = true;
}
}
impl Empty {
@ -110,7 +115,7 @@ impl Component for Empty {
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
match key.code {
KeyCode::Esc if self.is_active() => self.move_out(),
KeyCode::Esc if self.is_active() => self.move_out(),
_ => Ok(None),
}
}
@ -125,10 +130,7 @@ impl Component for Empty {
};
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)
.block(Block::bordered().padding(Padding::new(0, 0, padding_top / 2, 0)))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -79,8 +79,7 @@ impl Component for FpsCounter {
let message = format!(
" {:.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());

View File

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

View File

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

View File

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

View File

@ -10,14 +10,14 @@ use ratatui::{
use super::Component;
use crate::{
action::Action,
widgets::{DotSpinner, OghamCenter, VerticalBlocks}
widgets::{DotSpinner, OghamCenter, VerticalBlocks},
};
#[derive(Debug, Clone, PartialEq)]
pub struct Health {
name: Option<String>,
peers: Option<usize>,
is_syncing: bool,
is_syncing: bool,
should_have_peers: bool,
tx_pool_length: usize,
validators_count: u32,
@ -44,9 +44,9 @@ impl Health {
}
pub fn is_syncing_as_string(&self) -> String {
if self.is_syncing {
if self.is_syncing {
format!("syncing {}", VerticalBlocks::default().to_string())
} else {
} else {
String::from("synced")
}
}
@ -73,7 +73,7 @@ impl Component for Health {
self.peers = peers;
self.is_syncing = is_syncing;
self.should_have_peers = should_have_peers;
},
}
Action::SetNodeName(name) => self.name = name,
Action::SetPendingExtrinsicsLength(length) => self.tx_pool_length = length,
Action::NominatorsNumber(number) => self.nominators_count = number,

View File

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

View File

@ -1,34 +1,22 @@
use ratatui::layout::{Constraint, Layout, Rect};
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] {
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] {
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] {
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 {

View File

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

View File

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

View File

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

View File

@ -1,19 +1,19 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
types::{ActionTarget, ActionLevel},
action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
widgets::{Input, InputRequest},
};
@ -24,7 +24,7 @@ pub struct ChangeBlocksPopup {
network_tx: Option<Sender<Action>>,
chain_id: u64,
block_number: Input,
palette: StylePalette
palette: StylePalette,
}
impl ChangeBlocksPopup {
@ -36,7 +36,7 @@ impl ChangeBlocksPopup {
}
fn submit_new_block(&mut self) {
if let Some(network_tx) = &self.network_tx {
if let Some(network_tx) = &self.network_tx {
if let Some(action_tx) = &self.action_tx {
let _ = match self.block_number.value().parse::<u64>() {
Ok(new_block) => {
@ -44,9 +44,9 @@ impl ChangeBlocksPopup {
action_tx.send(Action::ClosePopup)
}
Err(_) => action_tx.send(Action::EventLog(
"Incorrect block number format".to_string(),
ActionLevel::Error,
ActionTarget::ValidatorLog,
"Incorrect block number format".to_string(),
ActionLevel::Error,
ActionTarget::ValidatorLog,
)),
};
}
@ -98,11 +98,16 @@ impl Component for ChangeBlocksPopup {
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_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());
}
Ok(())
}
@ -111,12 +116,12 @@ impl Component for ChangeBlocksPopup {
if self.is_active && key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Enter => self.submit_new_block(),
KeyCode::Esc => self.close_popup(),
KeyCode::Esc => self.close_popup(),
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(),
_ => {},
_ => {}
};
}
Ok(None)
@ -133,13 +138,14 @@ impl Component for ChangeBlocksPopup {
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 input = Paragraph::new(self.block_number.value())
.block(Block::bordered()
let input = Paragraph::new(self.block_number.value()).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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 h = Layout::horizontal([Constraint::Max(57)]).flex(Flex::Center);
let [area] = v.areas(area);
@ -148,8 +154,8 @@ impl Component for ChangeBlocksPopup {
frame.render_widget(Clear, area);
frame.render_widget(input, area);
frame.set_cursor_position(Position::new(
area.x + self.block_number.cursor() as u16 + 1,
area.y + 1
area.x + self.block_number.cursor() as u16 + 1,
area.y + 1,
));
}
Ok(())

View File

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

View File

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

View File

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

View File

@ -1,14 +1,24 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
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
layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
prelude::Margin,
style::{Color, Style},
widgets::{
Block, BorderType, Cell, Clear, Paragraph, Row, Scrollbar, ScrollbarOrientation,
ScrollbarState, Table, TableState,
},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action, config::Config, palette::StylePalette, widgets::{Input, InputRequest}
action::Action,
config::Config,
palette::StylePalette,
widgets::{Input, InputRequest},
};
#[derive(Debug, Default, Eq, PartialEq)]
@ -52,13 +62,14 @@ impl GatekeeperEndpoints {
}
fn submit_message(&mut self) {
if let Some(network_tx) = &self.network_tx {
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.chain_id,
stored_endpoints,
));
self.rpc_input = Input::new(String::new());
}
}
@ -68,27 +79,26 @@ impl GatekeeperEndpoints {
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));
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(),
_ => {}
}
}
_ => match new_char {
'j' => self.move_cursor_down(),
'k' => self.move_cursor_up(),
'l' => self.move_cursor_right(),
'h' => self.move_cursor_left(),
_ => {}
},
}
}
@ -100,7 +110,7 @@ impl GatekeeperEndpoints {
fn move_cursor_up(&mut self) {
match self.selected {
Selected::Input => {},
Selected::Input => {}
Selected::DefaultRpcs => {
let i = match self.default_table_state.selected() {
Some(i) => {
@ -109,12 +119,12 @@ impl GatekeeperEndpoints {
} else {
i - 1
}
},
None => 0
}
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) => {
@ -123,18 +133,18 @@ impl GatekeeperEndpoints {
} else {
i - 1
}
},
None => 0
}
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::Input => {}
Selected::DefaultRpcs => {
let i = match self.default_table_state.selected() {
Some(i) => {
@ -143,12 +153,12 @@ impl GatekeeperEndpoints {
} 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) => {
@ -157,12 +167,12 @@ impl GatekeeperEndpoints {
} else {
i + 1
}
},
}
None => 0,
};
self.stored_table_state.select(Some(i));
self.stored_scroll_state = self.stored_scroll_state.position(i);
},
}
};
}
@ -200,18 +210,20 @@ impl GatekeeperEndpoints {
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>>(),
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()
.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));
.title(title_name),
);
let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight)
@ -230,7 +242,7 @@ impl PartialComponent for GatekeeperEndpoints {
_ => {
self.is_active = false;
self.rpc_input = Input::new(String::new());
},
}
};
}
}
@ -248,16 +260,26 @@ impl Component for GatekeeperEndpoints {
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());
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(())
}
@ -272,8 +294,8 @@ impl Component for GatekeeperEndpoints {
KeyCode::Right => self.move_cursor_right(),
KeyCode::Up => self.move_cursor_up(),
KeyCode::Down => self.move_cursor_down(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -282,10 +304,12 @@ impl Component for GatekeeperEndpoints {
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,
Action::SetGatekeepedNetwork(network) => {
self.default_endpoints = network.default_endpoints
}
Action::SetStoredRpcEndpoints(stored_endpoints) => {
self.stored_endpoints = stored_endpoints
}
_ => {}
};
Ok(None)
@ -294,7 +318,8 @@ impl Component for GatekeeperEndpoints {
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 (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 {
@ -327,13 +352,14 @@ impl Component for GatekeeperEndpoints {
scrollbar_style,
);
let input = Paragraph::new(self.rpc_input.value())
.block(Block::bordered()
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"));
.title("Input new RPC"),
);
let v = Layout::vertical([Constraint::Max(14)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(80)]).flex(Flex::Center);
@ -341,34 +367,44 @@ impl Component for GatekeeperEndpoints {
let [area] = h.areas(area);
frame.render_widget(Clear, area);
let [tables_area, input_area] = Layout::vertical([
Constraint::Length(11),
Constraint::Length(3),
]).areas(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);
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_rpcs,
default_table_area,
&mut self.default_table_state,
);
frame.render_stateful_widget(
default_scrollbar,
default_table_area.inner(Margin { vertical: 1, horizontal: 1 }),
default_table_area.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.default_scroll_state,
);
frame.render_stateful_widget(stored_rpcs, stored_table_area, &mut self.stored_table_state);
frame.render_stateful_widget(
stored_rpcs,
stored_table_area,
&mut self.stored_table_state,
);
frame.render_stateful_widget(
stored_scrollbar,
stored_table_area.inner(Margin { vertical: 1, horizontal: 1 }),
stored_table_area.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.stored_scroll_state,
);
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
input_area.x + self.rpc_input.cursor() as u16 + 1,
input_area.y + 1,
));
}
Ok(())

View File

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

View File

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

View File

@ -3,21 +3,14 @@ use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::Margin;
use ratatui::widgets::ListItem;
use ratatui::{
layout::{Alignment, Rect},
widgets::{
Block, List, ListState, Scrollbar,
ScrollbarOrientation, ScrollbarState,
},
Frame
layout::{Alignment, Rect},
widgets::{Block, List, ListState, Scrollbar, ScrollbarOrientation, ScrollbarState},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use super::{PartialComponent, Component, CurrentTab};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
use super::{Component, CurrentTab, PartialComponent};
use crate::{action::Action, config::Config, palette::StylePalette};
pub struct ListenAddresses {
is_active: bool,
@ -63,7 +56,7 @@ impl ListenAddresses {
} else {
i + 1
}
},
}
None => 0,
};
self.list_state.select(Some(i));
@ -86,8 +79,8 @@ impl ListenAddresses {
} else {
i - 1
}
},
None => 0
}
None => 0,
};
self.list_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i);
@ -115,14 +108,22 @@ impl Component for ListenAddresses {
fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied());
self.palette.with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette.with_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.palette
.with_normal_style(style.get("normal_style").copied());
self.palette
.with_hover_style(style.get("hover_style").copied());
self.palette
.with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_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(())
}
@ -139,11 +140,11 @@ impl Component for ListenAddresses {
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
if self.is_active {
match key.code {
KeyCode::Up | KeyCode::Char('k') => self.previous_row(),
KeyCode::Down | KeyCode::Char('j') => self.next_row(),
KeyCode::Up | KeyCode::Char('k') => self.previous_row(),
KeyCode::Down | KeyCode::Char('j') => self.next_row(),
KeyCode::Char('g') => self.first_row(),
KeyCode::Char('G') => self.last_row(),
_ => {},
_ => {}
};
}
Ok(None)
@ -155,15 +156,17 @@ impl Component for ListenAddresses {
let list = List::new(
self.listen_addresses
.iter()
.map(|addr| ListItem::new(addr.clone()))
)
.highlight_style(self.palette.create_highlight_style())
.block(Block::bordered()
.map(|addr| ListItem::new(addr.clone())),
)
.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(self.local_identity.clone()));
.title(self.local_identity.clone()),
);
let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight)
@ -174,7 +177,10 @@ impl Component for ListenAddresses {
frame.render_stateful_widget(list, place, &mut self.list_state);
frame.render_stateful_widget(
scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }),
place.inner(Margin {
vertical: 1,
horizontal: 1,
}),
&mut self.scroll_state,
);

View File

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

View File

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

View File

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

View File

@ -1,19 +1,15 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
use super::{Component, CurrentTab, PartialComponent};
use crate::{action::Action, config::Config, palette::StylePalette};
#[derive(Debug)]
pub struct PayoutAllPopup {
@ -23,7 +19,7 @@ pub struct PayoutAllPopup {
secret_seed: [u8; 32],
stash_account_id: [u8; 32],
era_indexes: Vec<u32>,
palette: StylePalette
palette: StylePalette,
}
impl Default for PayoutAllPopup {
@ -57,9 +53,10 @@ impl PayoutAllPopup {
if let Some(network_tx) = &self.network_tx {
for era_index in &self.era_indexes {
let _ = network_tx.send(Action::PayoutStakers(
self.secret_seed,
self.stash_account_id,
*era_index));
self.secret_seed,
self.stash_account_id,
*era_index,
));
}
}
self.close_popup();
@ -88,11 +85,16 @@ impl Component for PayoutAllPopup {
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_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());
}
Ok(())
}
@ -101,8 +103,8 @@ impl Component for PayoutAllPopup {
if self.is_active && key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Enter => self.start_payout(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -121,17 +123,24 @@ impl Component for PayoutAllPopup {
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 popup = Paragraph::new(
format!(" Do payout for {} eras, from {} to {}",
self.era_indexes.len(),
self.era_indexes.first().expect("Length of unclaimed indexes always more then one; qed"),
self.era_indexes.last().expect("Length of unclaimed indexes always more then one; qed")))
.block(Block::bordered()
let popup = Paragraph::new(format!(
" Do payout for {} eras, from {} to {}",
self.era_indexes.len(),
self.era_indexes
.first()
.expect("Length of unclaimed indexes always more then one; qed"),
self.era_indexes
.last()
.expect("Length of unclaimed indexes always more then one; qed")
))
.block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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 h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area);

View File

@ -1,19 +1,15 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
use super::{Component, CurrentTab, PartialComponent};
use crate::{action::Action, config::Config, palette::StylePalette};
#[derive(Debug)]
pub struct PayoutPopup {
@ -23,7 +19,7 @@ pub struct PayoutPopup {
secret_seed: [u8; 32],
stash_account_id: [u8; 32],
era_index: u32,
palette: StylePalette
palette: StylePalette,
}
impl Default for PayoutPopup {
@ -55,9 +51,10 @@ impl PayoutPopup {
fn start_payout(&mut self) {
if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::PayoutStakers(
self.secret_seed,
self.stash_account_id,
self.era_index));
self.secret_seed,
self.stash_account_id,
self.era_index,
));
}
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup);
@ -87,11 +84,16 @@ impl Component for PayoutPopup {
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_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());
}
Ok(())
}
@ -100,8 +102,8 @@ impl Component for PayoutPopup {
if self.is_active && key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Enter => self.start_payout(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -120,13 +122,14 @@ impl Component for PayoutPopup {
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 popup = Paragraph::new(format!(" Do payout for era #{}", self.era_index))
.block(Block::bordered()
let popup = Paragraph::new(format!(" Do payout for era #{}", self.era_index)).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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 h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area);

View File

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

View File

@ -1,19 +1,19 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
widgets::{Input, InputRequest},
};
@ -24,7 +24,7 @@ pub struct RebondPopup {
network_tx: Option<Sender<Action>>,
secret_seed: [u8; 32],
amount: Input,
palette: StylePalette
palette: StylePalette,
}
impl Default for RebondPopup {
@ -54,13 +54,12 @@ impl RebondPopup {
fn log_event(&mut self, message: String, level: ActionLevel) {
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));
}
}
fn submit_message(&mut self) {
if let Some(network_tx) = &self.network_tx {
if let Some(network_tx) = &self.network_tx {
let str_amount = self.amount.value();
let str_amount = if str_amount.starts_with('.') {
&format!("0{}", str_amount)[..]
@ -74,9 +73,10 @@ impl RebondPopup {
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup);
}
},
Err(err) => self.log_event(
format!("invalid amount, error: {err}"), ActionLevel::Error),
}
Err(err) => {
self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error)
}
}
}
}
@ -126,11 +126,16 @@ impl Component for RebondPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> {
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_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());
}
Ok(())
}
@ -143,7 +148,7 @@ impl Component for RebondPopup {
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(),
_ => {},
_ => {}
};
}
Ok(None)
@ -160,13 +165,14 @@ impl Component for RebondPopup {
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 input = Paragraph::new(self.amount.value())
.block(Block::bordered()
let input = Paragraph::new(self.amount.value()).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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 h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area);
@ -175,8 +181,8 @@ impl Component for RebondPopup {
frame.render_widget(Clear, area);
frame.render_widget(input, area);
frame.set_cursor_position(Position::new(
area.x + self.amount.cursor() as u16 + 1,
area.y + 1
area.x + self.amount.cursor() as u16 + 1,
area.y + 1,
));
}
Ok(())

View File

@ -1,20 +1,16 @@
use color_eyre::Result;
use ratatui::layout::Constraint;
use ratatui::{
layout::{Alignment, Rect},
text::Text,
layout::{Alignment, Rect},
widgets::{Block, Cell, Row, Table},
Frame
widgets::{Block, Cell, Row, Table},
Frame,
};
use super::{PartialComponent, Component, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::types::EraRewardPoints;
use crate::widgets::DotSpinner;
use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
use crate::{action::Action, config::Config, palette::StylePalette};
pub struct RewardDetails {
palette: StylePalette,
@ -24,7 +20,7 @@ pub struct RewardDetails {
treasury_apy: String,
inflation: String,
stash: [u8; 32],
in_staking_validators: bool,
in_staking_validators: bool,
in_queued_keys: bool,
in_next_keys: bool,
is_disabled: bool,
@ -47,7 +43,7 @@ impl RewardDetails {
treasury_apy: String::from("0.0%"),
inflation: String::from("0.0%"),
stash: [0u8; 32],
in_staking_validators: false,
in_staking_validators: false,
in_queued_keys: false,
in_next_keys: false,
is_disabled: false,
@ -58,9 +54,12 @@ impl RewardDetails {
fn comission_to_string(&self) -> String {
match self.commission {
Some(commission) => {
if self.nominators_blocked { "blocked".to_string() }
else { format!("{:.2}%", commission as f64 / 10_000_000.0) }
},
if self.nominators_blocked {
"blocked".to_string()
} else {
format!("{:.2}%", commission as f64 / 10_000_000.0)
}
}
None => DotSpinner::default().to_string(),
}
}
@ -77,20 +76,28 @@ impl 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 {
fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied());
self.palette.with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette.with_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.palette
.with_normal_style(style.get("normal_style").copied());
self.palette
.with_hover_style(style.get("hover_style").copied());
self.palette
.with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_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(())
}
@ -98,8 +105,12 @@ impl Component for RewardDetails {
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::SetStashAccount(stash) => self.stash = stash,
Action::SetCurrentValidatorEraRewards(_, _, individual) => self.update_individual(&individual),
Action::SetValidatorPrefs(commission, disabled, account_id) if self.stash == account_id => {
Action::SetCurrentValidatorEraRewards(_, _, individual) => {
self.update_individual(&individual)
}
Action::SetValidatorPrefs(commission, disabled, account_id)
if self.stash == account_id =>
{
self.commission = commission;
self.in_staking_validators = commission.is_some();
self.nominators_blocked = disabled;
@ -110,7 +121,7 @@ impl Component for RewardDetails {
} else {
self.in_next_keys = !session_key_info.key.is_empty();
}
},
}
Action::Apy(apy) => self.apy = apy,
Action::TreasuryApy(apy) => self.treasury_apy = apy,
Action::Inflation(inflation) => self.inflation = inflation,
@ -130,8 +141,8 @@ impl Component for RewardDetails {
match (self.in_staking_validators, is_fully_in_session) {
(true, true) => "Active",
(true, false) => "Rotating",
(false, true) if self.in_rewards => { "Stopping" }
(false, true) if !self.in_rewards => { "Chill" }
(false, true) if self.in_rewards => "Stopping",
(false, true) if !self.in_rewards => "Chill",
_ => "Nothing",
}
};
@ -141,34 +152,33 @@ impl Component for RewardDetails {
vec![
Row::new(vec![
Cell::from(Text::from("Nominators".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.comission_to_string()).alignment(Alignment::Right)),
Cell::from(Text::from(self.comission_to_string()).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("Validator APY".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.apy.clone()).alignment(Alignment::Right)),
Cell::from(Text::from(self.apy.clone()).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("Treasury APY".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.treasury_apy.clone()).alignment(Alignment::Right)),
Cell::from(Text::from(self.treasury_apy.clone()).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("Inflation".to_string()).alignment(Alignment::Left)),
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())
.column_spacing(1)
.block(Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title(staking_status));
.block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title(staking_status),
);
frame.render_widget(table, place);

View File

@ -1,20 +1,16 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect},
text::Text,
widgets::{Block, Cell, Clear, Row, Table},
layout::{Alignment, Constraint, Flex, Layout, Rect},
text::Text,
widgets::{Block, Cell, Clear, Row, Table},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
use super::{Component, CurrentTab, PartialComponent};
use crate::{action::Action, config::Config, palette::StylePalette};
#[derive(Debug)]
pub struct RotatePopup {
@ -23,7 +19,7 @@ pub struct RotatePopup {
network_tx: Option<Sender<Action>>,
cached_keys: String,
secret_seed: [u8; 32],
palette: StylePalette
palette: StylePalette,
}
impl Default for RotatePopup {
@ -55,7 +51,9 @@ impl RotatePopup {
if !self.cached_keys.is_empty() && self.cached_keys.len() == 258 {
if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::SetSessionKeys(
self.secret_seed, self.cached_keys.clone()));
self.secret_seed,
self.cached_keys.clone(),
));
}
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup);
@ -108,11 +106,16 @@ impl Component for RotatePopup {
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_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());
}
Ok(())
}
@ -130,8 +133,8 @@ impl Component for RotatePopup {
if self.is_active && key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Enter => self.rotate_keys(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -146,34 +149,33 @@ impl Component for RotatePopup {
vec![
Row::new(vec![
Cell::from(Text::from("gran".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(gran_key).alignment(Alignment::Right)),
Cell::from(Text::from(gran_key).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("babe".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(babe_key).alignment(Alignment::Right)),
Cell::from(Text::from(babe_key).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("audi".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(audi_key).alignment(Alignment::Right)),
Cell::from(Text::from(audi_key).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("slow".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(slow_key).alignment(Alignment::Right)),
Cell::from(Text::from(slow_key).alignment(Alignment::Right)),
]),
],
[
Constraint::Length(4),
Constraint::Min(0),
],
[Constraint::Length(4), Constraint::Min(0)],
)
.highlight_style(self.palette.create_highlight_style())
.column_spacing(1)
.block(Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title("Rotate session keys (Enter to proceed / Esc to close)"));
.block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title("Rotate session keys (Enter to proceed / Esc to close)"),
);
let v = Layout::vertical([Constraint::Max(6)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(73)]).flex(Flex::Center);

View File

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

View File

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

View File

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

View File

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

View File

@ -1,19 +1,19 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
widgets::{Input, InputRequest},
};
@ -24,7 +24,7 @@ pub struct ValidatePopup {
network_tx: Option<Sender<Action>>,
secret_seed: [u8; 32],
amount: Input,
palette: StylePalette
palette: StylePalette,
}
impl Default for ValidatePopup {
@ -54,13 +54,12 @@ impl ValidatePopup {
fn log_event(&mut self, message: String, level: ActionLevel) {
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));
}
}
fn submit_message(&mut self) {
if let Some(network_tx) = &self.network_tx {
if let Some(network_tx) = &self.network_tx {
let str_amount = self.amount.value();
let str_amount = if str_amount.starts_with('.') {
&format!("0{}", str_amount)[..]
@ -74,9 +73,10 @@ impl ValidatePopup {
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup);
}
},
Err(err) => self.log_event(
format!("invalid amount, error: {err}"), ActionLevel::Error),
}
Err(err) => {
self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error)
}
}
}
}
@ -126,11 +126,16 @@ impl Component for ValidatePopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> {
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_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());
}
Ok(())
}
@ -142,8 +147,8 @@ impl Component for ValidatePopup {
KeyCode::Backspace => self.delete_char(),
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -160,13 +165,14 @@ impl Component for ValidatePopup {
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 input = Paragraph::new(self.amount.value())
.block(Block::bordered()
let input = Paragraph::new(self.amount.value()).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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 h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area);
@ -175,8 +181,8 @@ impl Component for ValidatePopup {
frame.render_widget(Clear, area);
frame.render_widget(input, area);
frame.set_cursor_position(Position::new(
area.x + self.amount.cursor() as u16 + 1,
area.y + 1
area.x + self.amount.cursor() as u16 + 1,
area.y + 1,
));
}
Ok(())

View File

@ -1,19 +1,19 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
};
#[derive(Debug)]
@ -27,7 +27,7 @@ pub struct WithdrawPopup {
unlocking_is_empty: bool,
existential_deposit: u128,
active_balance: u128,
palette: StylePalette
palette: StylePalette,
}
impl Default for WithdrawPopup {
@ -64,13 +64,14 @@ impl WithdrawPopup {
}
fn proceed(&mut self) {
if let Some(network_tx) = &self.network_tx {
if let Some(network_tx) = &self.network_tx {
let spans_needed = if self.stash_should_be_killed() {
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::EventLog(
"Current stash account will be killed during this transaction".to_string(),
ActionLevel::Warn,
ActionTarget::ValidatorLog));
"Current stash account will be killed during this transaction".to_string(),
ActionLevel::Warn,
ActionTarget::ValidatorLog,
));
}
self.slashing_spans_length
} else {
@ -106,11 +107,16 @@ impl Component for WithdrawPopup {
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_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());
}
Ok(())
}
@ -119,8 +125,8 @@ impl Component for WithdrawPopup {
if self.is_active && key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Enter => self.proceed(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -128,15 +134,26 @@ impl Component for WithdrawPopup {
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::SetExistentialDeposit(existential_deposit) => self.existential_deposit = existential_deposit,
Action::SetExistentialDeposit(existential_deposit) => {
self.existential_deposit = existential_deposit
}
Action::SetStashAccount(account_id) => self.stash_account = account_id,
Action::SetStashSecret(secret_seed) => self.secret_seed = secret_seed,
Action::SetSlashingSpansLength(length, 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.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(),
Action::SetSlashingSpansLength(length, 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.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)
@ -150,13 +167,14 @@ impl Component for WithdrawPopup {
} else {
" Do you want to withdraw all unbonded funds?"
};
let input = Paragraph::new(text)
.block(Block::bordered()
let input = Paragraph::new(text).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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 h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area);

View File

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

View File

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

View File

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

View File

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

View File

@ -1,18 +1,18 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
widgets::{Input, InputRequest},
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)]
@ -20,7 +20,7 @@ pub struct AddAccount {
is_active: bool,
action_tx: Option<UnboundedSender<Action>>,
name: Input,
palette: StylePalette
palette: StylePalette,
}
impl Default for AddAccount {
@ -48,8 +48,7 @@ impl AddAccount {
fn submit_message(&mut self) {
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);
}
}
@ -78,7 +77,7 @@ impl PartialComponent for AddAccount {
_ => {
self.is_active = false;
self.name = Input::new(String::new());
},
}
};
}
}
@ -91,11 +90,16 @@ impl Component for AddAccount {
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_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());
}
Ok(())
}
@ -109,7 +113,7 @@ impl Component for AddAccount {
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(),
_ => {},
_ => {}
};
}
Ok(None)
@ -118,16 +122,17 @@ impl Component for AddAccount {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active {
let size = area.as_size();
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 input = Paragraph::new(self.name.value())
.block(Block::bordered()
let input = Paragraph::new(self.name.value()).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right)
.title("New wallet name"));
.title("New wallet name"),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area);
@ -136,8 +141,8 @@ impl Component for AddAccount {
frame.render_widget(Clear, area);
frame.render_widget(input, area);
frame.set_cursor_position(Position::new(
area.x + self.name.cursor() as u16 + 1,
area.y + 1
area.x + self.name.cursor() as u16 + 1,
area.y + 1,
));
}
Ok(())

View File

@ -1,18 +1,18 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
widgets::{Input, InputRequest},
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)]
@ -28,7 +28,7 @@ pub struct AddAddressBookRecord {
action_tx: Option<UnboundedSender<Action>>,
name: Input,
address: Input,
palette: StylePalette
palette: StylePalette,
}
impl Default for AddAddressBookRecord {
@ -59,11 +59,14 @@ impl AddAddressBookRecord {
fn submit_message(&mut self) {
match self.name_or_address {
NameOrAddress::Name => self.name_or_address = NameOrAddress::Address,
NameOrAddress::Address => if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::NewAddressBookRecord(
NameOrAddress::Address => {
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::NewAddressBookRecord(
self.name.value().to_string(),
self.address.value().to_string()));
let _ = action_tx.send(Action::ClosePopup);
self.address.value().to_string(),
));
let _ = action_tx.send(Action::ClosePopup);
}
}
}
}
@ -72,7 +75,7 @@ impl AddAddressBookRecord {
match self.name_or_address {
NameOrAddress::Name => {
let _ = self.name.handle(InputRequest::InsertChar(new_char));
},
}
NameOrAddress::Address => {
let _ = self.address.handle(InputRequest::InsertChar(new_char));
}
@ -83,7 +86,7 @@ impl AddAddressBookRecord {
match self.name_or_address {
NameOrAddress::Name => {
let _ = self.name.handle(InputRequest::DeletePrevChar);
},
}
NameOrAddress::Address => {
let _ = self.address.handle(InputRequest::DeletePrevChar);
}
@ -94,7 +97,7 @@ impl AddAddressBookRecord {
match self.name_or_address {
NameOrAddress::Name => {
let _ = self.name.handle(InputRequest::GoToNextChar);
},
}
NameOrAddress::Address => {
let _ = self.address.handle(InputRequest::GoToNextChar);
}
@ -105,7 +108,7 @@ impl AddAddressBookRecord {
match self.name_or_address {
NameOrAddress::Name => {
let _ = self.name.handle(InputRequest::GoToPrevChar);
},
}
NameOrAddress::Address => {
let _ = self.address.handle(InputRequest::GoToPrevChar);
}
@ -124,7 +127,7 @@ impl PartialComponent for AddAddressBookRecord {
self.address = Input::new(String::new());
self.name_or_address = NameOrAddress::Name;
}
},
}
};
}
}
@ -137,11 +140,16 @@ impl Component for AddAddressBookRecord {
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_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());
}
Ok(())
}
@ -156,8 +164,8 @@ impl Component for AddAddressBookRecord {
KeyCode::Backspace => self.delete_char(),
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -166,25 +174,27 @@ impl Component for AddAddressBookRecord {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active {
let size = area.as_size();
let name_area = Rect::new(size.width / 2, size.height / 2, 51, 3);
let address_area = Rect::new(size.width / 2, size.height / 2 + 3, 51, 3);
let name_area = Rect::new(size.width / 2, size.height / 2, 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 input_name = Paragraph::new(self.name.value())
.block(Block::bordered()
let input_name = Paragraph::new(self.name.value()).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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(Block::bordered()
let input_address = Paragraph::new(self.address.value()).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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 h = Layout::horizontal([Constraint::Length(51)]).flex(Flex::Center);
@ -202,14 +212,14 @@ impl Component for AddAddressBookRecord {
match self.name_or_address {
NameOrAddress::Name => {
frame.set_cursor_position(Position::new(
name_area.x + self.name.cursor() as u16 + 1,
name_area.y + 1
name_area.x + self.name.cursor() as u16 + 1,
name_area.y + 1,
));
},
}
NameOrAddress::Address => {
frame.set_cursor_position(Position::new(
address_area.x + self.address.cursor() as u16 + 1,
address_area.y + 1
address_area.x + self.address.cursor() as u16 + 1,
address_area.y + 1,
));
}
}

View File

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

View File

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

View File

@ -1,19 +1,19 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
widgets::{Input, InputRequest},
};
@ -27,7 +27,7 @@ pub struct BondPopup {
minimal_bond: u128,
is_bonded: bool,
amount: Input,
palette: StylePalette
palette: StylePalette,
}
impl Default for BondPopup {
@ -53,8 +53,7 @@ impl BondPopup {
fn log_event(&mut self, message: String, level: ActionLevel) {
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));
}
}
@ -76,7 +75,7 @@ impl BondPopup {
}
fn submit_message(&mut self) {
if let Some(network_tx) = &self.network_tx {
if let Some(network_tx) = &self.network_tx {
let str_amount = self.amount.value();
let str_amount = if str_amount.starts_with('.') {
&format!("0{}", str_amount)[..]
@ -88,16 +87,25 @@ impl BondPopup {
let amount = (value * 1_000_000_000_000_000_000.0) as u128;
let log_target = ActionTarget::WalletLog;
let _ = if self.is_bonded {
network_tx.send(Action::BondValidatorExtraFrom(self.account_secret_seed, amount, log_target))
network_tx.send(Action::BondValidatorExtraFrom(
self.account_secret_seed,
amount,
log_target,
))
} else {
network_tx.send(Action::BondValidatorFrom(self.account_secret_seed, amount, log_target))
network_tx.send(Action::BondValidatorFrom(
self.account_secret_seed,
amount,
log_target,
))
};
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup);
}
},
Err(err) => self.log_event(
format!("invalid amount, error: {err}"), ActionLevel::Error),
}
Err(err) => {
self.log_event(format!("invalid amount, error: {err}"), ActionLevel::Error)
}
}
}
}
@ -147,11 +155,16 @@ impl Component for BondPopup {
fn register_config_handler(&mut self, config: Config) -> Result<()> {
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_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());
}
Ok(())
}
@ -164,8 +177,8 @@ impl Component for BondPopup {
KeyCode::Backspace => self.delete_char(),
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -173,9 +186,12 @@ impl Component for BondPopup {
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::SetIsBonded(is_bonded, account_id) if self.account_id == account_id =>
self.is_bonded = is_bonded,
Action::UsedAccount(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::UsedAccount(account_id, secret_seed) => {
self.update_used_account(account_id, secret_seed)
}
Action::SetMinValidatorBond(minimal_bond) => self.minimal_bond = minimal_bond,
_ => {}
};
@ -185,13 +201,14 @@ impl Component for BondPopup {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.amount.value())
.block(Block::bordered()
let input = Paragraph::new(self.amount.value()).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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 h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area);
@ -200,8 +217,8 @@ impl Component for BondPopup {
frame.render_widget(Clear, area);
frame.render_widget(input, area);
frame.set_cursor_position(Position::new(
area.x + self.amount.cursor() as u16 + 1,
area.y + 1
area.x + self.amount.cursor() as u16 + 1,
area.y + 1,
));
}
Ok(())

View File

@ -2,14 +2,14 @@ use std::sync::mpsc::Sender;
use color_eyre::Result;
use ratatui::{
layout::{Alignment, Constraint, Rect},
text::Text,
widgets::Clear,
layout::{Alignment, Constraint, Rect},
text::Text,
widgets::{Block, Cell, Row, Table},
Frame
widgets::{Block, Cell, Row, Table},
Frame,
};
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{action::Action, config::Config, palette::StylePalette};
#[derive(Debug)]
@ -86,11 +86,11 @@ impl CurrentValidatorDetails {
Some(commission) => {
self.commission = commission as f64 / 10_000_000.0;
self.is_active_validator = true;
},
}
None => {
self.commission = 0.0;
self.is_active_validator = false;
},
}
}
}
@ -130,29 +130,53 @@ impl Component for CurrentValidatorDetails {
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_hover_style(style.get("hover_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette.with_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.palette
.with_normal_style(style.get("normal_style").copied());
self.palette
.with_hover_style(style.get("hover_style").copied());
self.palette
.with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_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(())
}
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::SetChoosenValidator(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::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::SetChoosenValidator(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::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 => {
self.total_balance = total;
self.own_balance = own;
},
}
_ => {}
};
@ -167,54 +191,85 @@ impl Component for CurrentValidatorDetails {
vec![
Row::new(vec![
Cell::from(Text::from("Forbidden".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.is_nomination_disabled.to_string()).alignment(Alignment::Right)),
Cell::from(
Text::from(self.is_nomination_disabled.to_string())
.alignment(Alignment::Right),
),
]),
Row::new(vec![
Cell::from(Text::from("Nominators".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.others_len.to_string()).alignment(Alignment::Right)),
Cell::from(
Text::from(self.others_len.to_string()).alignment(Alignment::Right),
),
]),
Row::new(vec![
Cell::from(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("Total staked".to_string()).alignment(Alignment::Left),
),
Cell::from(
Text::from(self.prepare_u128(self.total_balance))
.alignment(Alignment::Right),
),
]),
Row::new(vec![
Cell::from(Text::from("Own stake".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.own_balance)).alignment(Alignment::Right)),
Cell::from(
Text::from(self.prepare_u128(self.own_balance))
.alignment(Alignment::Right),
),
]),
Row::new(vec![
Cell::from(Text::from("Imbalance".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_stake_imbalance()).alignment(Alignment::Right)),
Cell::from(
Text::from(self.prepare_stake_imbalance()).alignment(Alignment::Right),
),
]),
Row::new(vec![
Cell::from(Text::from("Commission".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(format!("{:.4}%", self.commission)).alignment(Alignment::Right)),
Cell::from(
Text::from(format!("{:.4}%", self.commission))
.alignment(Alignment::Right),
),
]),
Row::new(vec![
Cell::from(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("Points ratio".to_string()).alignment(Alignment::Left),
),
Cell::from(
Text::from(format!("{:.4}%", self.points_ratio))
.alignment(Alignment::Right),
),
]),
Row::new(vec![
Cell::from(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("In next era".to_string()).alignment(Alignment::Left),
),
Cell::from(
Text::from(self.prepare_state_string()).alignment(Alignment::Right),
),
]),
Row::new(vec![
Cell::from(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("Last payout".to_string()).alignment(Alignment::Left),
),
Cell::from(
Text::from(format!("{} days ago", self.latest_era_claim))
.alignment(Alignment::Right),
),
]),
],
[
Constraint::Max(12),
Constraint::Min(0),
],
[Constraint::Max(12), Constraint::Min(0)],
)
.column_spacing(1)
.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("Validator details"));
.block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title("Validator details"),
);
frame.render_widget(Clear, place);
frame.render_widget(table, place);

View File

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

View File

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

View File

@ -8,41 +8,41 @@ use ratatui::{
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
mod balance;
mod transfer;
mod address_book;
mod add_account;
mod rename_account;
mod event_logs;
mod accounts;
mod overview;
mod add_address_book_record;
mod rename_address_book_record;
mod account_details;
mod staking_ledger;
mod accounts;
mod add_account;
mod add_address_book_record;
mod address_book;
mod balance;
mod bond_popup;
mod payee_popup;
mod current_validators;
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;
use balance::Balance;
use transfer::Transfer;
use address_book::AddressBook;
use add_account::AddAccount;
use rename_account::RenameAccount;
use event_logs::EventLogs;
use accounts::Accounts;
use overview::Overview;
use add_address_book_record::AddAddressBookRecord;
use rename_address_book_record::RenameAddressBookRecord;
use account_details::AccountDetails;
use staking_ledger::StakingLedger;
use accounts::Accounts;
use add_account::AddAccount;
use add_address_book_record::AddAddressBookRecord;
use address_book::AddressBook;
use balance::Balance;
use bond_popup::BondPopup;
use payee_popup::PayeePopup;
use current_validators::CurrentValidators;
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 super::Component;
use crate::{action::Action, app::Mode, config::Config};
@ -146,61 +146,63 @@ impl Component for Wallet {
}
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
if !self.is_active { return Ok(None) }
if !self.is_active {
return Ok(None);
}
match self.current_tab {
// block the default key handle for popups
CurrentTab::AddAccount |
CurrentTab::RenameAccount |
CurrentTab::RenameAddressBookRecord |
CurrentTab::Transfer |
CurrentTab::AccountDetails |
CurrentTab::BondPopup |
CurrentTab::PayeePopup |
CurrentTab::CurrentValidatorsPopup |
CurrentTab::AddAddressBookRecord => {
for component in self.components.iter_mut() {
component.handle_key_event(key)?;
}
},
CurrentTab::AddAccount
| CurrentTab::RenameAccount
| CurrentTab::RenameAddressBookRecord
| CurrentTab::Transfer
| CurrentTab::AccountDetails
| CurrentTab::BondPopup
| CurrentTab::PayeePopup
| CurrentTab::CurrentValidatorsPopup
| CurrentTab::AddAddressBookRecord => {
for component in self.components.iter_mut() {
component.handle_key_event(key)?;
}
}
_ => match key.code {
KeyCode::Esc => {
self.is_active = false;
self.current_tab = CurrentTab::Nothing;
return Ok(Some(Action::SetActiveScreen(Mode::Menu)));
},
}
KeyCode::Char('W') => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::AddAccount;
},
}
KeyCode::Char('A') => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::AddAddressBookRecord;
},
}
KeyCode::Char('T') => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::Transfer;
},
}
KeyCode::Char('B') => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::BondPopup;
},
}
KeyCode::Char('O') => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::PayeePopup;
},
}
KeyCode::Char('N') => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::CurrentValidatorsPopup;
},
}
KeyCode::Char('l') | KeyCode::Right => self.move_right(),
KeyCode::Char('h') | KeyCode::Left => self.move_left(),
_ => {
for component in self.components.iter_mut() {
component.handle_key_event(key)?;
}
},
}
}
},
}
Ok(None)
}
@ -211,11 +213,11 @@ impl Component for Wallet {
self.is_active = true;
self.current_tab = CurrentTab::Accounts;
self.previous_tab = CurrentTab::Accounts;
},
}
Action::RenameAccount(_) => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::RenameAccount;
},
}
Action::RenameAddressBookRecord(_) => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::RenameAddressBookRecord;
@ -223,11 +225,11 @@ impl Component for Wallet {
Action::TransferTo(_) => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::Transfer;
},
}
Action::AccountDetailsOf(_, _) => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::AccountDetails;
},
}
Action::ClosePopup => self.current_tab = self.previous_tab,
_ => {}
}
@ -248,18 +250,12 @@ impl Component for Wallet {
}
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] {
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] {
@ -269,14 +265,12 @@ pub fn account_layout(area: Rect) -> [Rect; 4] {
Constraint::Min(0),
Constraint::Max(7),
Constraint::Max(6),
]).areas(place)
])
.areas(place)
}
pub fn nominator_layout(area: Rect) -> [Rect; 2] {
let v = Layout::vertical([Constraint::Max(11)]).flex(Flex::Center);
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,24 +1,20 @@
use color_eyre::Result;
use ratatui::{
layout::{Alignment, Constraint, Rect},
text::Text,
layout::{Alignment, Constraint, Rect},
widgets::{Block, Cell, Row, Table},
Frame
widgets::{Block, Cell, Row, Table},
Frame,
};
use super::{Component, PartialComponent, CurrentTab};
use crate::{
action::Action,
config::Config,
palette::StylePalette, widgets::DotSpinner,
};
use super::{Component, CurrentTab, PartialComponent};
use crate::{action::Action, config::Config, palette::StylePalette, widgets::DotSpinner};
#[derive(Debug)]
pub struct Overview {
is_active: bool,
existential_balance: Option<u128>,
total_issuance: Option<u128>,
palette: StylePalette
palette: StylePalette,
}
impl Default for Overview {
@ -46,8 +42,8 @@ impl Overview {
let value = value as f64 / 10f64.powi(18);
let after = Self::DECIMALS;
format!("{:.after$}{}", value, Self::TICKER)
},
None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER)
}
None => format!("{}{}", DotSpinner::default().to_string(), Self::TICKER),
}
}
}
@ -64,12 +60,18 @@ impl PartialComponent for Overview {
impl Component for Overview {
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_hover_style(style.get("hover_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied());
self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette
.with_normal_style(style.get("normal_style").copied());
self.palette
.with_hover_style(style.get("hover_style").copied());
self.palette
.with_normal_border_style(style.get("normal_border_style").copied());
self.palette
.with_hover_border_style(style.get("hover_border_style").copied());
self.palette
.with_normal_title_style(style.get("normal_title_style").copied());
self.palette
.with_hover_title_style(style.get("hover_title_style").copied());
}
Ok(())
}
@ -85,31 +87,35 @@ impl Component for Overview {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
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(
[
Row::new(vec![
Cell::from(Text::from("total supply: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.total_issuance)).alignment(Alignment::Right)),
Cell::from(
Text::from(self.prepare_u128(self.total_issuance))
.alignment(Alignment::Right),
),
]),
Row::new(vec![
Cell::from(Text::from("min deposit: ".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.prepare_u128(self.existential_balance)).alignment(Alignment::Right)),
Cell::from(
Text::from(self.prepare_u128(self.existential_balance))
.alignment(Alignment::Right),
),
]),
],
[
Constraint::Max(15),
Constraint::Min(0),
]
[Constraint::Max(15), Constraint::Min(0)],
)
.block(Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title("Overview"));
.block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title("Overview"),
);
frame.render_widget(table, place);
Ok(())

View File

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

View File

@ -1,18 +1,18 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
widgets::{Input, InputRequest},
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)]
@ -21,7 +21,7 @@ pub struct RenameAccount {
action_tx: Option<UnboundedSender<Action>>,
old_name: String,
name: Input,
palette: StylePalette
palette: StylePalette,
}
impl Default for RenameAccount {
@ -50,8 +50,7 @@ impl RenameAccount {
fn submit_message(&mut self) {
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);
}
}
@ -94,11 +93,16 @@ impl Component for RenameAccount {
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_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());
}
Ok(())
}
@ -119,8 +123,8 @@ impl Component for RenameAccount {
KeyCode::Backspace => self.delete_char(),
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -129,13 +133,14 @@ impl Component for RenameAccount {
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 input = Paragraph::new(self.name.value())
.block(Block::bordered()
let input = Paragraph::new(self.name.value()).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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 h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area);
@ -144,8 +149,8 @@ impl Component for RenameAccount {
frame.render_widget(Clear, area);
frame.render_widget(input, area);
frame.set_cursor_position(Position::new(
area.x + self.name.cursor() as u16 + 1,
area.y + 1
area.x + self.name.cursor() as u16 + 1,
area.y + 1,
));
}
Ok(())

View File

@ -1,18 +1,18 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
widgets::{Input, InputRequest},
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)]
@ -21,7 +21,7 @@ pub struct RenameAddressBookRecord {
action_tx: Option<UnboundedSender<Action>>,
old_name: String,
name: Input,
palette: StylePalette
palette: StylePalette,
}
impl Default for RenameAddressBookRecord {
@ -51,7 +51,8 @@ impl RenameAddressBookRecord {
fn submit_message(&mut self) {
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::UpdateAddressBookRecord(
self.name.value().to_string()));
self.name.value().to_string(),
));
let _ = action_tx.send(Action::ClosePopup);
}
}
@ -94,11 +95,16 @@ impl Component for RenameAddressBookRecord {
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_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());
}
Ok(())
}
@ -119,8 +125,8 @@ impl Component for RenameAddressBookRecord {
KeyCode::Backspace => self.delete_char(),
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -129,13 +135,14 @@ impl Component for RenameAddressBookRecord {
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 input = Paragraph::new(self.name.value())
.block(Block::bordered()
let input = Paragraph::new(self.name.value()).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.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 h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area);
@ -144,8 +151,8 @@ impl Component for RenameAddressBookRecord {
frame.render_widget(Clear, area);
frame.render_widget(input, area);
frame.set_cursor_position(Position::new(
area.x + self.name.cursor() as u16 + 1,
area.y + 1
area.x + self.name.cursor() as u16 + 1,
area.y + 1,
));
}
Ok(())

View File

@ -1,18 +1,18 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
layout::{Alignment, Constraint, Flex, Layout, Position, Rect},
widgets::{Block, Clear, Paragraph},
Frame,
};
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use super::{Component, CurrentTab, PartialComponent};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
widgets::{Input, InputRequest},
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug)]
@ -20,7 +20,7 @@ pub struct RenameKnownValidator {
is_active: bool,
action_tx: Option<UnboundedSender<Action>>,
name: Input,
palette: StylePalette
palette: StylePalette,
}
impl Default for RenameKnownValidator {
@ -43,11 +43,10 @@ impl RenameKnownValidator {
self.is_active = false;
self.name = Input::new(String::new());
}
fn submit_message(&mut self) {
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();
}
@ -70,7 +69,7 @@ impl 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 {
@ -89,11 +88,16 @@ impl Component for RenameKnownValidator {
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_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());
}
Ok(())
}
@ -106,8 +110,8 @@ impl Component for RenameKnownValidator {
KeyCode::Backspace => self.delete_char(),
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
KeyCode::Esc => self.close_popup(),
_ => {},
KeyCode::Esc => self.close_popup(),
_ => {}
};
}
Ok(None)
@ -116,16 +120,17 @@ impl Component for RenameKnownValidator {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active {
let size = area.as_size();
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 input = Paragraph::new(self.name.value())
.block(Block::bordered()
let input = Paragraph::new(self.name.value()).block(
Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right)
.title("Know validator name"));
.title("Know validator name"),
);
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let [area] = v.areas(area);
@ -134,8 +139,8 @@ impl Component for RenameKnownValidator {
frame.render_widget(Clear, area);
frame.render_widget(input, area);
frame.set_cursor_position(Position::new(
area.x + self.name.cursor() as u16 + 1,
area.y + 1
area.x + self.name.cursor() as u16 + 1,
area.y + 1,
));
}
Ok(())

View File

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

View File

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

View File

@ -36,7 +36,7 @@ lazy_static::lazy_static! {
pub static ref PROJECT_NAME: String = env!("CARGO_CRATE_NAME")
.to_uppercase()
.to_string();
pub static ref DATA_FOLDER: Option<PathBuf> =
pub static ref DATA_FOLDER: Option<PathBuf> =
env::var(format!("{}_DATA", PROJECT_NAME.clone()))
.ok()
.map(PathBuf::from);
@ -93,9 +93,7 @@ impl Config {
for (mode, default_styles) in default_config.styles.iter() {
let user_styles = cfg.styles.entry(*mode).or_default();
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);
}
}
@ -132,7 +130,8 @@ pub struct KeyBindings(pub HashMap<Mode, HashMap<Vec<KeyEvent>, Action>>);
impl<'de> Deserialize<'de> for KeyBindings {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>,
where
D: Deserializer<'de>,
{
let parsed_map = HashMap::<Mode, HashMap<String, Action>>::deserialize(deserializer)?;
@ -200,7 +199,7 @@ fn parse_key_code_with_modifiers(
"backtab" => {
modifiers.insert(KeyModifiers::SHIFT);
KeyCode::BackTab
},
}
"backspace" => KeyCode::Backspace,
"delete" => KeyCode::Delete,
"insert" => KeyCode::Insert,
@ -253,12 +252,12 @@ pub fn key_event_to_string(key_event: &KeyEvent) -> String {
KeyCode::F(c) => {
char = format!("f({c})");
&char
},
}
KeyCode::Char(' ') => "space",
KeyCode::Char(c) => {
char = c.to_string();
&char
},
}
KeyCode::Esc => "esc",
KeyCode::Null => "",
KeyCode::CapsLock => "",
@ -327,7 +326,8 @@ pub struct Styles(pub HashMap<Mode, HashMap<String, Style>>);
impl<'de> Deserialize<'de> for Styles {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
where
D: Deserializer<'de>,
{
let parsed_map = HashMap::<Mode, HashMap<String, String>>::deserialize(deserializer)?;
@ -347,7 +347,7 @@ impl<'de> Deserialize<'de> for Styles {
}
pub fn parse_style(line: &str) -> Style {
let (foreground, background) =
let (foreground, background) =
line.split_at(line.to_lowercase().find("on ").unwrap_or(line.len()));
let foreground = process_color_string(foreground);
let background = process_color_string(&background.replace("on ", ""));
@ -417,7 +417,7 @@ fn parse_color(s: &str) -> Option<Color> {
let red = (s.as_bytes()[3] as char).to_digit(10).unwrap_or_default() as u8;
let green = (s.as_bytes()[4] as char).to_digit(10).unwrap_or_default() as u8;
let blue = (s.as_bytes()[5] as char).to_digit(10).unwrap_or_default() as u8;
let c = 16 + red * 36 + green * 6 + blue;
Some(Color::Indexed(c))
} else if s == "bold black" {

View File

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

View File

@ -16,8 +16,7 @@ pub fn init() -> Result<()> {
let log_path = directory.join(LOG_FILE.clone());
let log_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
.try_from_env()
.or_else(|_| env_filter.with_env_var(LOG_ENV.clone()).from_env())?;

View File

@ -1,24 +1,21 @@
use clap::Parser;
use color_eyre::Result;
use subxt::{
OnlineClient,
backend::rpc::RpcClient,
};
use subxt::{backend::rpc::RpcClient, OnlineClient};
mod modes;
mod action;
mod app;
mod casper;
mod cli;
mod components;
mod config;
mod errors;
mod logging;
mod tui;
mod modes;
mod network;
mod widgets;
mod types;
mod palette;
mod casper;
mod tui;
mod types;
mod widgets;
use casper::{CasperAccountId, CasperConfig};
@ -27,7 +24,7 @@ pub mod casper_network {}
#[tokio::main]
async fn start_tokio_action_loop(
io_rx: std::sync::mpsc::Receiver<action::Action>,
io_rx: std::sync::mpsc::Receiver<action::Action>,
network: &mut network::Network,
) {
while let Ok(io_event) = io_rx.recv() {
@ -62,11 +59,7 @@ async fn main() -> Result<()> {
let cloned_action_tx = action_tx.clone();
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);
});
@ -84,11 +77,8 @@ async fn main() -> Result<()> {
let cloned_action_tx = action_tx.clone();
let cloned_sync_tx = sync_io_tx.clone();
std::thread::spawn(move || {
let mut subscription = network::BestSubscription::new(
cloned_action_tx,
cloned_sync_tx,
best_blocks_sub,
);
let mut subscription =
network::BestSubscription::new(cloned_action_tx, cloned_sync_tx, best_blocks_sub);
start_tokio_best_subscription(&mut subscription);
});

View File

@ -1,5 +1,5 @@
use ratatui::layout::Constraint;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Mode {
@ -19,7 +19,7 @@ impl Default for Mode {
impl Mode {
const MENU_DATA_TITLE: &str = "Help for navigation menu ";
const MENU_DATA_HEADERS: &[&str] = &["Hot Key", "Target", "Description"];
const MENU_DATA_HEADERS: &[&str] = &["Hot Key", "Target", "Description"];
const MENU_DATA_CONSTRAINTS: &[Constraint] = &[Constraint::Length(10), Constraint::Min(0)];
const MENU_DATA_ROWS: &[&[&str]] = &[
&["(↑) | k", "Select side menu item above"],

View File

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

View File

@ -9,38 +9,131 @@ const SLOW_CLAP_DB_PREFIX: &[u8] = b"slow_clap::";
// FALLOFF: u32 = 0_050_000;
// MAX_PIECE_COUNT: u32 = 40;
const PIECEWISE_LINEAR_POUNTS: [(Perbill, Perbill); 32] = [
(Perbill::from_parts(0), Perbill::from_parts(6900000)),
(Perbill::from_parts(690000000), Perbill::from_parts(690000000)),
(Perbill::from_parts(692740000), Perbill::from_parts(664536000)),
(Perbill::from_parts(695588000), Perbill::from_parts(639072000)),
(Perbill::from_parts(698554000), Perbill::from_parts(613608000)),
(Perbill::from_parts(701647000), Perbill::from_parts(588144000)),
(Perbill::from_parts(704879000), Perbill::from_parts(562680000)),
(Perbill::from_parts(708262000), Perbill::from_parts(537216000)),
(Perbill::from_parts(711811000), Perbill::from_parts(511752000)),
(Perbill::from_parts(715545000), Perbill::from_parts(486288000)),
(Perbill::from_parts(719482000), Perbill::from_parts(460824000)),
(Perbill::from_parts(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)),
(Perbill::from_parts(0), Perbill::from_parts(6900000)),
(
Perbill::from_parts(690000000),
Perbill::from_parts(690000000),
),
(
Perbill::from_parts(692740000),
Perbill::from_parts(664536000),
),
(
Perbill::from_parts(695588000),
Perbill::from_parts(639072000),
),
(
Perbill::from_parts(698554000),
Perbill::from_parts(613608000),
),
(
Perbill::from_parts(701647000),
Perbill::from_parts(588144000),
),
(
Perbill::from_parts(704879000),
Perbill::from_parts(562680000),
),
(
Perbill::from_parts(708262000),
Perbill::from_parts(537216000),
),
(
Perbill::from_parts(711811000),
Perbill::from_parts(511752000),
),
(
Perbill::from_parts(715545000),
Perbill::from_parts(486288000),
),
(
Perbill::from_parts(719482000),
Perbill::from_parts(460824000),
),
(
Perbill::from_parts(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);
@ -48,23 +141,34 @@ pub fn calculate_for_fraction(n: u128, d: u128) -> (Perbill, Perbill) {
let n = n.min(d.clone());
if PIECEWISE_LINEAR_POUNTS.is_empty() {
return (MAXIMUM_INFLATION, Perbill::zero())
return (MAXIMUM_INFLATION, Perbill::zero());
}
let next_point_index = PIECEWISE_LINEAR_POUNTS.iter().position(|p| n < p.0 * d.clone());
let next_point_index = PIECEWISE_LINEAR_POUNTS
.iter()
.position(|p| n < p.0 * d.clone());
let (prev, next) = if let Some(next_point_index) = next_point_index {
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 {
// There is no previous points, take first point ordinate
let fraction = PIECEWISE_LINEAR_POUNTS.first().map(|p| p.1).unwrap_or_else(Perbill::zero);
return (MAXIMUM_INFLATION, fraction)
let fraction = PIECEWISE_LINEAR_POUNTS
.first()
.map(|p| p.1)
.unwrap_or_else(Perbill::zero);
return (MAXIMUM_INFLATION, fraction);
}
} else {
// There is no next points, take last point ordinate
let fraction = PIECEWISE_LINEAR_POUNTS.last().map(|p| p.1).unwrap_or_else(Perbill::zero);
return (MAXIMUM_INFLATION, fraction)
let fraction = PIECEWISE_LINEAR_POUNTS
.last()
.map(|p| p.1)
.unwrap_or_else(Perbill::zero);
return (MAXIMUM_INFLATION, fraction);
};
let delta_y = multiply_by_rational_saturating(
@ -86,19 +190,19 @@ pub fn calculate_for_fraction(n: u128, d: u128) -> (Perbill, Perbill) {
}
fn abs_sub<N: Ord + core::ops::Sub<Output = N> + Clone>(a: N, b: N) -> N where {
a.clone().max(b.clone()) - a.min(b)
a.clone().max(b.clone()) - a.min(b)
}
fn multiply_by_rational_saturating(value: u128, p: u32, q: u32) -> u128 {
let q = q.max(1);
let result_divisor_part = (value / q as u128).saturating_mul(p as u128);
let result_remainder_part = {
let rem = value % q as u128;
let rem_u32 = rem as u32;
let rem_part = rem_u32 as u64 * p as u64 / q as u64;
rem_part as u128
};
result_divisor_part.saturating_add(result_remainder_part)
let q = q.max(1);
let result_divisor_part = (value / q as u128).saturating_mul(p as u128);
let result_remainder_part = {
let rem = value % q as u128;
let rem_u32 = rem as u32;
let rem_part = rem_u32 as u64 * p as u64 / q as u64;
rem_part as u128
};
result_divisor_part.saturating_add(result_remainder_part)
}
pub fn get_slow_clap_storage_key(first: &[u8], second: &[u8]) -> Vec<u8> {

View File

@ -1,25 +1,28 @@
use tokio::sync::mpsc::UnboundedSender;
use color_eyre::Result;
use subxt::{
backend::rpc::RpcClient, tx::{TxProgress, TxStatus}, utils::H256, OnlineClient
backend::rpc::RpcClient,
tx::{TxProgress, TxStatus},
utils::H256,
OnlineClient,
};
use tokio::sync::mpsc::UnboundedSender;
mod legacy_rpc_calls;
mod miscellaneous;
mod predefined_calls;
mod predefined_txs;
mod subscriptions;
mod miscellaneous;
mod raw_calls;
mod subscriptions;
pub use miscellaneous::calculate_for_fraction;
use crate::{
types::{ActionLevel, ActionTarget},
action::Action,
action::Action,
casper::CasperConfig,
types::{ActionLevel, ActionTarget},
};
pub use subscriptions::{FinalizedSubscription, BestSubscription};
pub use subscriptions::{BestSubscription, FinalizedSubscription};
const GATEKEEPED_CHAIN_IDS: [u64; 1] = [
11155111, //Sepolia
@ -47,11 +50,11 @@ pub struct Network {
impl Network {
pub fn new(
action_tx: UnboundedSender<Action>,
action_tx: UnboundedSender<Action>,
online_client_api: OnlineClient<CasperConfig>,
rpc_client: RpcClient,
) -> Self {
Self {
Self {
action_tx,
online_client_api,
rpc_client,
@ -69,12 +72,12 @@ impl Network {
fn store_stash_or_validator_if_possible(&mut self, account_id: [u8; 32], is_stash: bool) {
if is_stash {
match self.stash_to_watch {
Some(stash) if stash == account_id => {},
Some(stash) if stash == account_id => {}
_ => self.stash_to_watch = Some(account_id),
}
} else {
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),
}
}
@ -107,40 +110,138 @@ impl Network {
Action::NewBestHash(hash) => {
self.best_hash = Some(hash);
Ok(())
},
}
Action::NewFinalizedHash(hash) => {
self.finalized_hash = Some(hash);
if let Some(stash_to_watch) = self.stash_to_watch {
predefined_calls::get_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &stash_to_watch).await?;
predefined_calls::get_queued_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &stash_to_watch).await?;
predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_account_payee(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_slashing_spans(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_session_keys(
&self.action_tx,
&self.online_client_api,
&self.rpc_client,
&stash_to_watch,
)
.await?;
predefined_calls::get_queued_session_keys(
&self.action_tx,
&self.online_client_api,
&self.rpc_client,
&stash_to_watch,
)
.await?;
predefined_calls::get_nominators_by_validator(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_validator_prefs(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_staking_value_ratio(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_is_stash_bonded(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_validators_ledger(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_account_payee(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
predefined_calls::get_slashing_spans(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
)
.await?;
for era_index in self.eras_to_watch.iter() {
predefined_calls::get_validator_staking_result(&self.action_tx, &self.online_client_api, &stash_to_watch, *era_index).await?;
predefined_calls::get_validator_staking_result(
&self.action_tx,
&self.online_client_api,
&stash_to_watch,
*era_index,
)
.await?;
}
}
if let Some(validator_details_to_watch) = self.validator_details_to_watch {
predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_validator_latest_claim(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_account_payee(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_nominators_by_account(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_nominators_by_validator(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_validator_prefs(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_staking_value_ratio(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_validator_latest_claim(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_account_payee(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_validators_ledger(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_is_stash_bonded(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
predefined_calls::get_nominators_by_account(
&self.action_tx,
&self.online_client_api,
&validator_details_to_watch,
)
.await?;
}
for account_id in self.accounts_to_watch.iter() {
predefined_calls::get_balance(&self.action_tx, &self.online_client_api, &account_id).await?;
predefined_calls::get_balance(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await?;
}
Ok(())
},
}
Action::CheckPendingTransactions => {
let length = self.transactions_to_watch.len();
for i in (0..length).rev() {
@ -148,70 +249,198 @@ impl Network {
let ext_hash = pending_tx.tx_progress.extrinsic_hash();
let log_target = pending_tx.target.clone();
match (*pending_tx).tx_progress.next().await {
Some(Ok(status)) => {
match status {
TxStatus::Validated => self.action_tx.send(Action::EventLog(format!("transaction {} is part of future queue", ext_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) => {
self.action_tx.send(Action::EventLog(format!("transaction {} has been finalized in block header {}", b.extrinsic_hash(), b.block_hash()), ActionLevel::Info, log_target))?;
self.transactions_to_watch.remove(i);
}
TxStatus::Error { message } => {
self.action_tx.send(Action::EventLog(format!("transaction {} error, something get wrong: {message}", ext_hash), ActionLevel::Error, log_target))?;
self.remove_transaction_and_decrement_nonce(i);
}
TxStatus::Invalid { message } => {
self.action_tx.send(Action::EventLog(format!("transaction {} invalid: {message}", ext_hash), ActionLevel::Error, log_target))?;
self.remove_transaction_and_decrement_nonce(i);
}
TxStatus::Dropped { message } => {
self.action_tx.send(Action::EventLog(format!("transaction {} was dropped: {message}", ext_hash), ActionLevel::Error, log_target))?;
self.remove_transaction_and_decrement_nonce(i);
}
Some(Ok(status)) => match status {
TxStatus::Validated => self.action_tx.send(Action::EventLog(
format!("transaction {} is part of future queue", ext_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) => {
self.action_tx.send(Action::EventLog(
format!(
"transaction {} has been finalized in block header {}",
b.extrinsic_hash(),
b.block_hash()
),
ActionLevel::Info,
log_target,
))?;
self.transactions_to_watch.remove(i);
}
TxStatus::Error { message } => {
self.action_tx.send(Action::EventLog(
format!(
"transaction {} error, something get wrong: {message}",
ext_hash
),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i);
}
TxStatus::Invalid { message } => {
self.action_tx.send(Action::EventLog(
format!("transaction {} invalid: {message}", ext_hash),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i);
}
TxStatus::Dropped { message } => {
self.action_tx.send(Action::EventLog(
format!("transaction {} was dropped: {message}", ext_hash),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i);
}
},
_ => {
self.action_tx.send(Action::EventLog(format!("transaction {} was dropped", ext_hash), ActionLevel::Error, log_target))?;
self.action_tx.send(Action::EventLog(
format!("transaction {} was dropped", ext_hash),
ActionLevel::Error,
log_target,
))?;
self.remove_transaction_and_decrement_nonce(i);
}
}
}
Ok(())
},
Action::GetSystemHealth => 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::GetChainName => 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::GetSystemHealth => {
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::GetChainName => {
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 => {
for chain_id in GATEKEEPED_CHAIN_IDS {
legacy_rpc_calls::get_block_range(&self.action_tx, &self.rpc_client, chain_id).await?;
legacy_rpc_calls::get_block_range(&self.action_tx, &self.rpc_client, chain_id)
.await?;
}
Ok(())
}
Action::GetBlockAuthor(hash, logs) => predefined_calls::get_block_author(&self.action_tx, &self.online_client_api, &logs, &hash).await,
Action::GetActiveEra => predefined_calls::get_active_era(&self.action_tx, &self.online_client_api).await,
Action::GetCurrentEra => predefined_calls::get_current_era(&self.action_tx, &self.online_client_api).await,
Action::GetEpochProgress => predefined_calls::get_epoch_progress(&self.action_tx, &self.online_client_api).await,
Action::GetMinValidatorBond => predefined_calls::get_minimal_validator_bond(&self.action_tx, &self.online_client_api).await,
Action::GetGatekeepedNetwork(chain_id) => predefined_calls::get_gatekeeped_network(&self.action_tx, &self.online_client_api, chain_id).await,
Action::GetBlockAuthor(hash, logs) => {
predefined_calls::get_block_author(
&self.action_tx,
&self.online_client_api,
&logs,
&hash,
)
.await
}
Action::GetActiveEra => {
predefined_calls::get_active_era(&self.action_tx, &self.online_client_api).await
}
Action::GetCurrentEra => {
predefined_calls::get_current_era(&self.action_tx, &self.online_client_api).await
}
Action::GetEpochProgress => {
predefined_calls::get_epoch_progress(&self.action_tx, &self.online_client_api).await
}
Action::GetMinValidatorBond => {
predefined_calls::get_minimal_validator_bond(
&self.action_tx,
&self.online_client_api,
)
.await
}
Action::GetGatekeepedNetwork(chain_id) => {
predefined_calls::get_gatekeeped_network(
&self.action_tx,
&self.online_client_api,
chain_id,
)
.await
}
Action::GetExistentialDeposit => predefined_calls::get_existential_deposit(&self.action_tx, &self.online_client_api).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::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::GetExistentialDeposit => {
predefined_calls::get_existential_deposit(&self.action_tx, &self.online_client_api)
.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::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) => {
self.store_sender_nonce(&seed, maybe_nonce);
@ -224,59 +453,126 @@ impl Network {
Action::GetStakingPayee(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_account_payee(&self.action_tx, &self.online_client_api, &account_id).await
predefined_calls::get_account_payee(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetValidatorLatestClaim(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validator_latest_claim(&self.action_tx, &self.online_client_api, &account_id).await
predefined_calls::get_validator_latest_claim(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetSlashingSpans(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_slashing_spans(&self.action_tx, &self.online_client_api, &account_id).await
predefined_calls::get_slashing_spans(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetValidatorLedger(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &account_id).await
predefined_calls::get_validators_ledger(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetIsStashBonded(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &account_id).await
},
predefined_calls::get_is_stash_bonded(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetErasStakersOverview(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &account_id).await
},
predefined_calls::get_staking_value_ratio(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetValidatorPrefs(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &account_id).await
},
predefined_calls::get_validator_prefs(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetNominatorsByValidator(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &account_id).await
},
predefined_calls::get_nominators_by_validator(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetNominatorsByAccount(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_nominators_by_account(&self.action_tx, &self.online_client_api, &account_id).await
},
predefined_calls::get_nominators_by_account(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetValidatorAllRewards(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validator_staking_results(&self.action_tx, &self.online_client_api, &account_id).await
},
predefined_calls::get_validator_staking_results(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
Action::GetQueuedSessionKeys(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_queued_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &account_id).await
},
predefined_calls::get_queued_session_keys(
&self.action_tx,
&self.online_client_api,
&self.rpc_client,
&account_id,
)
.await
}
Action::GetSessionKeys(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &account_id).await
},
predefined_calls::get_session_keys(
&self.action_tx,
&self.online_client_api,
&self.rpc_client,
&account_id,
)
.await
}
Action::BalanceRequest(account_id, remove) => {
if remove {
let _ = self.accounts_to_watch.remove(&account_id);
Ok(())
} else {
let _ = self.accounts_to_watch.insert(account_id);
predefined_calls::get_balance(&self.action_tx, &self.online_client_api, &account_id).await
predefined_calls::get_balance(
&self.action_tx,
&self.online_client_api,
&account_id,
)
.await
}
}
Action::TransferBalance(sender, receiver, amount) => {
@ -290,13 +586,15 @@ impl Network {
.expect("stored seed is valid length; qed");
if let Ok(tx_progress) = predefined_txs::transfer_balance(
&self.action_tx,
&self.online_client_api,
&sender,
&receiver,
&self.action_tx,
&self.online_client_api,
&sender,
&receiver,
&amount,
maybe_nonce,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_cloned,
@ -310,13 +608,15 @@ impl Network {
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::bond_extra(
&self.action_tx,
&self.online_client_api,
&sender,
&self.action_tx,
&self.online_client_api,
&sender,
&amount,
maybe_nonce,
log_target,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
@ -329,13 +629,15 @@ impl Network {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::bond(
&self.action_tx,
&self.online_client_api,
&sender,
&self.action_tx,
&self.online_client_api,
&sender,
&amount,
maybe_nonce,
log_target,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
@ -354,7 +656,9 @@ impl Network {
&stash,
era_index,
maybe_nonce,
).await {
)
.await
{
self.eras_to_watch.insert(era_index);
self.transactions_to_watch.push(TxToWatch {
tx_progress,
@ -373,7 +677,9 @@ impl Network {
&sender,
&hashed_keys,
maybe_nonce,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
@ -391,7 +697,9 @@ impl Network {
&sender,
percent,
maybe_nonce,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
@ -408,7 +716,9 @@ impl Network {
&self.online_client_api,
&sender,
maybe_nonce,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
@ -426,7 +736,9 @@ impl Network {
&sender,
&amount,
maybe_nonce,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
@ -444,7 +756,9 @@ impl Network {
&sender,
&amount,
maybe_nonce,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
@ -462,7 +776,9 @@ impl Network {
&sender,
&spans,
maybe_nonce,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
@ -481,7 +797,9 @@ impl Network {
reward_destination,
maybe_nonce,
log_target,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
@ -499,7 +817,9 @@ impl Network {
&sender,
&nomination_targets,
maybe_nonce,
).await {
)
.await
{
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
@ -514,7 +834,8 @@ impl Network {
&self.rpc_client,
chain_id,
new_block,
).await
)
.await
}
Action::UpdateStoredRpcEndpoints(chain_id, stored_endpoints) => {
legacy_rpc_calls::set_stored_rpc_endpoints(
@ -522,9 +843,10 @@ impl Network {
&self.rpc_client,
chain_id,
stored_endpoints,
).await
)
.await
}
_ => Ok(())
_ => Ok(()),
}
}
}

View File

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

View File

@ -1,15 +1,15 @@
use color_eyre::Result;
use subxt::{
ext::sp_core::{sr25519::Pair, Pair as PairT},
tx::{PairSigner, TxProgress},
ext::sp_core::{sr25519::Pair, Pair as PairT},
tx::{PairSigner, TxProgress},
OnlineClient,
};
use tokio::sync::mpsc::UnboundedSender;
use crate::{
action::Action,
casper::{CasperConfig, CasperExtrinsicParamsBuilder},
casper_network::{self, runtime_types},
action::Action,
casper::{CasperConfig, CasperExtrinsicParamsBuilder},
casper_network::{self, runtime_types},
types::{ActionLevel, ActionTarget, RewardDestination},
};
@ -22,16 +22,19 @@ pub async fn transfer_balance(
maybe_nonce: Option<&mut u32>,
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let receiver_id = subxt::utils::MultiAddress::Id(subxt::utils::AccountId32::from(*receiver));
let transfer_tx = casper_network::tx().balances().transfer_allow_death(receiver_id, *amount);
let transfer_tx = casper_network::tx()
.balances()
.transfer_allow_death(receiver_id, *amount);
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(transfer_tx),
"transfer",
action_tx,
api,
sender,
maybe_nonce,
Box::new(transfer_tx),
"transfer",
ActionTarget::WalletLog,
).await
)
.await
}
pub async fn bond_extra(
@ -44,14 +47,15 @@ pub async fn bond_extra(
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let bond_extra_tx = casper_network::tx().staking().bond_extra(*amount);
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(bond_extra_tx),
"bond extra",
action_tx,
api,
sender,
maybe_nonce,
Box::new(bond_extra_tx),
"bond extra",
log_target,
).await
)
.await
}
pub async fn bond(
@ -63,17 +67,21 @@ pub async fn bond(
log_target: ActionTarget,
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
// auto-stake everything by now
let reward_destination = casper_network::runtime_types::pallet_staking::RewardDestination::Staked;
let bond_tx = casper_network::tx().staking().bond(*amount, reward_destination);
let reward_destination =
casper_network::runtime_types::pallet_staking::RewardDestination::Staked;
let bond_tx = casper_network::tx()
.staking()
.bond(*amount, reward_destination);
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(bond_tx),
"bond",
action_tx,
api,
sender,
maybe_nonce,
Box::new(bond_tx),
"bond",
log_target,
).await
)
.await
}
pub async fn payout_stakers(
@ -85,16 +93,19 @@ pub async fn payout_stakers(
maybe_nonce: Option<&mut u32>,
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let stash_id = subxt::utils::AccountId32::from(*stash);
let payout_stakers_tx = casper_network::tx().staking().payout_stakers(stash_id, era_index);
let payout_stakers_tx = casper_network::tx()
.staking()
.payout_stakers(stash_id, era_index);
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(payout_stakers_tx),
"payout stakers",
action_tx,
api,
sender,
maybe_nonce,
Box::new(payout_stakers_tx),
"payout stakers",
ActionTarget::ValidatorLog,
).await
)
.await
}
pub async fn set_keys(
@ -107,10 +118,26 @@ pub async fn set_keys(
let (gran_key, babe_key, audi_key, slow_key) = {
let s = hashed_keys_str.trim_start_matches("0x");
(
hex::decode(&s[0..64]).unwrap().as_slice().try_into().unwrap(),
hex::decode(&s[64..128]).unwrap().as_slice().try_into().unwrap(),
hex::decode(&s[128..192]).unwrap().as_slice().try_into().unwrap(),
hex::decode(&s[192..256]).unwrap().as_slice().try_into().unwrap(),
hex::decode(&s[0..64])
.unwrap()
.as_slice()
.try_into()
.unwrap(),
hex::decode(&s[64..128])
.unwrap()
.as_slice()
.try_into()
.unwrap(),
hex::decode(&s[128..192])
.unwrap()
.as_slice()
.try_into()
.unwrap(),
hex::decode(&s[192..256])
.unwrap()
.as_slice()
.try_into()
.unwrap(),
)
};
let session_keys = runtime_types::casper_runtime::opaque::SessionKeys {
@ -121,16 +148,19 @@ pub async fn set_keys(
};
// it seems like there is no check for the second paramter, that's why
// we it can be anything. For example empty vector.
let set_keys_tx = casper_network::tx().session().set_keys(session_keys, Vec::new());
let set_keys_tx = casper_network::tx()
.session()
.set_keys(session_keys, Vec::new());
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(set_keys_tx),
"set keys",
action_tx,
api,
sender,
maybe_nonce,
Box::new(set_keys_tx),
"set keys",
ActionTarget::ValidatorLog,
).await
)
.await
}
pub async fn validate(
@ -146,14 +176,15 @@ pub async fn validate(
};
let validate_tx = casper_network::tx().staking().validate(validator_prefs);
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(validate_tx),
"validate",
action_tx,
api,
sender,
maybe_nonce,
Box::new(validate_tx),
"validate",
ActionTarget::ValidatorLog,
).await
)
.await
}
pub async fn chill(
@ -164,14 +195,15 @@ pub async fn chill(
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let chill_tx = casper_network::tx().staking().chill();
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(chill_tx),
"chill",
action_tx,
api,
sender,
maybe_nonce,
Box::new(chill_tx),
"chill",
ActionTarget::ValidatorLog,
).await
)
.await
}
pub async fn unbond(
@ -183,14 +215,15 @@ pub async fn unbond(
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let unbond_tx = casper_network::tx().staking().unbond(*amount);
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(unbond_tx),
"unbond",
action_tx,
api,
sender,
maybe_nonce,
Box::new(unbond_tx),
"unbond",
ActionTarget::ValidatorLog,
).await
)
.await
}
pub async fn rebond(
@ -202,14 +235,15 @@ pub async fn rebond(
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let rebond_tx = casper_network::tx().staking().rebond(*amount);
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(rebond_tx),
"rebond",
action_tx,
api,
sender,
maybe_nonce,
Box::new(rebond_tx),
"rebond",
ActionTarget::ValidatorLog,
).await
)
.await
}
pub async fn withdraw_unbonded(
@ -221,14 +255,15 @@ pub async fn withdraw_unbonded(
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let withdraw_unbonded_tx = casper_network::tx().staking().withdraw_unbonded(*spans);
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(withdraw_unbonded_tx),
"withdraw unbonded",
action_tx,
api,
sender,
maybe_nonce,
Box::new(withdraw_unbonded_tx),
"withdraw unbonded",
ActionTarget::ValidatorLog,
).await
)
.await
}
pub async fn set_payee(
@ -240,10 +275,18 @@ pub async fn set_payee(
log_target: ActionTarget,
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let reward_destination = match reward_destination {
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::None => casper_network::runtime_types::pallet_staking::RewardDestination::None,
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::None => {
casper_network::runtime_types::pallet_staking::RewardDestination::None
}
RewardDestination::Account(account) => {
let account_id = subxt::utils::AccountId32::from(account);
casper_network::runtime_types::pallet_staking::RewardDestination::Account(account_id)
@ -251,14 +294,15 @@ pub async fn set_payee(
};
let set_payee_tx = casper_network::tx().staking().set_payee(reward_destination);
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(set_payee_tx),
"set payee",
action_tx,
api,
sender,
maybe_nonce,
Box::new(set_payee_tx),
"set payee",
log_target,
).await
)
.await
}
pub async fn nominate(
@ -274,14 +318,15 @@ pub async fn nominate(
.collect::<Vec<_>>();
let nominate_tx = casper_network::tx().staking().nominate(targets);
inner_sign_and_submit_then_watch(
action_tx,
api,
sender,
maybe_nonce,
Box::new(nominate_tx),
"nominate",
action_tx,
api,
sender,
maybe_nonce,
Box::new(nominate_tx),
"nominate",
ActionTarget::WalletLog,
).await
)
.await
}
async fn inner_sign_and_submit_then_watch(
@ -300,30 +345,36 @@ async fn inner_sign_and_submit_then_watch(
CasperExtrinsicParamsBuilder::new()
.nonce(nonce.saturating_sub(1) as u64)
.build()
},
}
None => CasperExtrinsicParamsBuilder::new().build(),
};
match api
.tx()
.sign_and_submit_then_watch(&tx_call, &signer, tx_params)
.await {
Ok(tx_progress) => {
action_tx.send(Action::EventLog(
format!("{tx_name} transaction {} sent", tx_progress.extrinsic_hash()),
ActionLevel::Info,
target))?;
Ok(tx_progress)
},
Err(err) => {
if let Some(ref mut nonce) = maybe_nonce {
**nonce = nonce.saturating_sub(1);
}
action_tx.send(Action::EventLog(
format!("error during {tx_name} transaction: {err}"),
ActionLevel::Error,
target))?;
Err(err.into())
}
.await
{
Ok(tx_progress) => {
action_tx.send(Action::EventLog(
format!(
"{tx_name} transaction {} sent",
tx_progress.extrinsic_hash()
),
ActionLevel::Info,
target,
))?;
Ok(tx_progress)
}
Err(err) => {
if let Some(ref mut nonce) = maybe_nonce {
**nonce = nonce.saturating_sub(1);
}
action_tx.send(Action::EventLog(
format!("error during {tx_name} transaction: {err}"),
ActionLevel::Error,
target,
))?;
Err(err.into())
}
}
}

View File

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

View File

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

View File

@ -1,40 +1,33 @@
use color_eyre::Result;
use subxt::{
backend::BlockRef,
utils::{Yes, H256, AccountId32},
client::OnlineClient,
utils::{AccountId32, Yes, H256},
};
use crate::CasperConfig;
pub mod session;
pub mod staking;
pub mod system;
pub mod babe;
pub mod balances;
pub mod networks;
pub mod session;
pub mod staking;
pub mod system;
pub async fn do_storage_call<'address, Addr>(
online_client: &OnlineClient<CasperConfig>,
storage_key: &'address Addr,
maybe_at_hash: Option<&H256>,
) -> Result<Option<Addr::Target>, subxt::Error>
) -> Result<Option<Addr::Target>, subxt::Error>
where
Addr: subxt::storage::Address<IsFetchable = Yes> + 'address,
{
let at_hash = match maybe_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>(
@ -42,10 +35,12 @@ pub fn do_constant_call<'address, Addr>(
constant_query: &'address Addr,
) -> Result<Addr::Target, subxt::Error>
where
Addr: subxt::constants::Address + 'address
Addr: subxt::constants::Address + 'address,
{
let constant_client = online_client.constants();
constant_client.validate(constant_query).expect("constant query should be correct; qed");
constant_client
.validate(constant_query)
.expect("constant query should be correct; qed");
constant_client.at(constant_query)
}

View File

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

View File

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

View File

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

View File

@ -1,14 +1,11 @@
use color_eyre::Result;
use subxt::{
utils::H256,
client::OnlineClient,
};
use subxt::{client::OnlineClient, utils::H256};
use crate::{
casper_network::{
self,
self,
runtime_types::{frame_system::AccountInfo, pallet_balances::types::AccountData},
},
},
CasperConfig,
};

View File

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

View File

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

View File

@ -8,15 +8,14 @@ use color_eyre::Result;
use crossterm::{
cursor,
event::{
DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste,
EnableMouseCapture, Event as CrosstermEvent, EventStream, KeyEvent,
KeyEventKind, MouseEvent,
DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture,
Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind, MouseEvent,
},
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
};
use futures::{FutureExt, StreamExt};
use ratatui::backend::CrosstermBackend as Backend;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use tokio::{
sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
task::JoinHandle,
@ -28,7 +27,7 @@ use tracing::error;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Event {
Init,
Quit,
Quit,
Error,
Closed,
Tick,
@ -126,7 +125,7 @@ impl Tui {
None => break,
},
};
if event_tx.send(event).is_err() {
break;
}
@ -142,7 +141,7 @@ impl Tui {
counter += 1;
if counter > 50 {
self.task.abort();
}
}
if counter > 100 {
error!("failed to abort task in 100 milliseconds for unknown reason");
break;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,11 @@
use codec::Decode;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct Gatekeeper {
pub chain_id: u64,
pub chain_name: String,
pub chain_type: String,
pub chain_type: String,
pub gatekeeper: String,
pub incoming_fee: u32,
pub outgoing_fee: u32,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,14 @@
mod big_text;
mod dot_spinner;
mod input;
mod ogham;
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::PixelSize;
pub use dot_spinner::DotSpinner;
pub use input::{Input, InputRequest};
pub use ogham::OghamCenter;
pub use vertical_block::VerticalBlocks;
const CYCLE: i64 = 1560;

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