rustfmt whole project
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
parent
565758bb84
commit
bae21bb505
@ -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"
|
||||
|
||||
@ -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>),
|
||||
|
||||
23
src/app.rs
23
src/app.rs
@ -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) {
|
||||
|
||||
@ -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>>;
|
||||
|
||||
|
||||
@ -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)))
|
||||
|
||||
@ -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,
|
||||
);
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
);
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 });
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use color_eyre::Result;
|
||||
use crate::action::Action;
|
||||
use color_eyre::Result;
|
||||
|
||||
use super::Activatable;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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<()> {
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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,
|
||||
);
|
||||
|
||||
|
||||
@ -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,
|
||||
);
|
||||
|
||||
|
||||
@ -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,
|
||||
);
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
);
|
||||
|
||||
|
||||
@ -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,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
);
|
||||
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
);
|
||||
|
||||
|
||||
@ -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 });
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
);
|
||||
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
);
|
||||
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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" {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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())?;
|
||||
|
||||
30
src/main.rs
30
src/main.rs
@ -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);
|
||||
});
|
||||
|
||||
|
||||
@ -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"],
|
||||
|
||||
@ -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,
|
||||
))?,
|
||||
};
|
||||
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
})
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
13
src/tui.rs
13
src/tui.rs
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use subxt::utils::H256;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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")]
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
Loading…
Reference in New Issue
Block a user