additional pop-up for balance detalization in address book
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
parent
9b1136e4ca
commit
853bdcd76e
@ -45,6 +45,7 @@ pub enum Action {
|
||||
UpdateAddressBookRecord(String),
|
||||
UpdateKnownValidator(String),
|
||||
TransferTo(String),
|
||||
AccountDetailsOf(String, Option<SystemAccount>),
|
||||
|
||||
TransferBalance(String, [u8; 32], u128),
|
||||
EventLog(String, ActionLevel, ActionTarget),
|
||||
|
@ -231,6 +231,19 @@ impl AddressBook {
|
||||
}
|
||||
}
|
||||
|
||||
fn show_account_details(&mut self) {
|
||||
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()),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_up(&mut self) {
|
||||
if let Some(src_index) = self.table_state.selected() {
|
||||
let dst_index = src_index.saturating_sub(1);
|
||||
@ -390,6 +403,7 @@ impl Component for AddressBook {
|
||||
KeyCode::Char('J') => self.swap_down(),
|
||||
KeyCode::Char('D') => self.delete_row(),
|
||||
KeyCode::Char('R') => self.update_address_book_record(),
|
||||
KeyCode::Char('I') => self.show_account_details(),
|
||||
KeyCode::Enter => self.send_transfer_to(),
|
||||
_ => {},
|
||||
};
|
||||
@ -407,11 +421,7 @@ impl Component for AddressBook {
|
||||
.map(|info| {
|
||||
let balance = self.balances
|
||||
.get(&info.account_id)
|
||||
.map(|inner_balance| {
|
||||
inner_balance.free
|
||||
.saturating_add(inner_balance.reserved)
|
||||
.saturating_add(inner_balance.frozen)
|
||||
});
|
||||
.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)),
|
||||
|
@ -20,7 +20,7 @@ pub struct Balance {
|
||||
total_balance: Option<u128>,
|
||||
transferable_balance: Option<u128>,
|
||||
locked_balance: Option<u128>,
|
||||
bonded_balance: Option<u128>,
|
||||
reserved_balance: Option<u128>,
|
||||
nonce: Option<u32>,
|
||||
palette: StylePalette
|
||||
}
|
||||
@ -41,7 +41,7 @@ impl Balance {
|
||||
total_balance: None,
|
||||
transferable_balance: None,
|
||||
locked_balance: None,
|
||||
bonded_balance: None,
|
||||
reserved_balance: None,
|
||||
nonce: None,
|
||||
palette: StylePalette::default(),
|
||||
}
|
||||
@ -87,19 +87,19 @@ impl Component for Balance {
|
||||
match maybe_balance {
|
||||
Some(balance) => {
|
||||
self.total_balance = Some(balance.free);
|
||||
self.locked_balance = Some(balance.reserved);
|
||||
self.bonded_balance = Some(balance.frozen);
|
||||
self.locked_balance = Some(balance.frozen);
|
||||
self.reserved_balance = Some(balance.reserved);
|
||||
self.nonce = Some(balance.nonce);
|
||||
|
||||
let transferable = balance.free
|
||||
.saturating_add(balance.reserved)
|
||||
.saturating_add(balance.frozen);
|
||||
.saturating_sub(balance.reserved)
|
||||
.saturating_sub(balance.frozen);
|
||||
self.transferable_balance = Some(transferable);
|
||||
},
|
||||
None => {
|
||||
self.transferable_balance = None;
|
||||
self.locked_balance = None;
|
||||
self.bonded_balance = None;
|
||||
self.reserved_balance = None;
|
||||
self.total_balance = None;
|
||||
self.nonce = None;
|
||||
}
|
||||
@ -137,8 +137,8 @@ impl Component for Balance {
|
||||
Cell::from(Text::from(self.prepare_u128(self.locked_balance)).alignment(Alignment::Right)),
|
||||
]),
|
||||
Row::new(vec![
|
||||
Cell::from(Text::from("bonded: ".to_string()).alignment(Alignment::Left)),
|
||||
Cell::from(Text::from(self.prepare_u128(self.bonded_balance)).alignment(Alignment::Right)),
|
||||
Cell::from(Text::from("reserved: ".to_string()).alignment(Alignment::Left)),
|
||||
Cell::from(Text::from(self.prepare_u128(self.reserved_balance)).alignment(Alignment::Right)),
|
||||
]),
|
||||
],
|
||||
[
|
||||
|
202
src/components/wallet/details.rs
Normal file
202
src/components/wallet/details.rs
Normal file
@ -0,0 +1,202 @@
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
layout::{Alignment, Constraint, Flex, Layout, Rect},
|
||||
widgets::{Block, Cell, Clear, Row, Table},
|
||||
text::Text,
|
||||
Frame,
|
||||
};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::{Component, PartialComponent, CurrentTab};
|
||||
use crate::{
|
||||
action::Action,
|
||||
config::Config,
|
||||
palette::StylePalette,
|
||||
widgets::DotSpinner,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AccountDetails {
|
||||
is_active: bool,
|
||||
action_tx: Option<UnboundedSender<Action>>,
|
||||
palette: StylePalette,
|
||||
name: String,
|
||||
transferable_balance: Option<u128>,
|
||||
locked_balance: Option<u128>,
|
||||
reserved_balance: Option<u128>,
|
||||
total_balance: Option<u128>,
|
||||
nonce: Option<u32>,
|
||||
}
|
||||
|
||||
impl Default for AccountDetails {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl AccountDetails {
|
||||
const TICKER: &str = " CSPR";
|
||||
const DECIMALS: usize = 6;
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
is_active: false,
|
||||
action_tx: None,
|
||||
palette: StylePalette::default(),
|
||||
name: String::new(),
|
||||
transferable_balance: None,
|
||||
locked_balance: None,
|
||||
reserved_balance: None,
|
||||
total_balance: None,
|
||||
nonce: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn close_popup(&mut self) {
|
||||
self.is_active = false;
|
||||
if let Some(action_tx) = &self.action_tx {
|
||||
let _ = action_tx.send(Action::ClosePopup);
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_u128(&self, maybe_value: Option<u128>) -> String {
|
||||
match maybe_value {
|
||||
Some(value) => {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for AccountDetails {
|
||||
fn set_active(&mut self, current_tab: CurrentTab) {
|
||||
match current_tab {
|
||||
CurrentTab::AccountDetails => self.is_active = true,
|
||||
_ => {
|
||||
if self.is_active {
|
||||
self.is_active = false;
|
||||
self.name = String::new();
|
||||
self.transferable_balance = None;
|
||||
self.locked_balance = None;
|
||||
self.reserved_balance = None;
|
||||
self.total_balance = None;
|
||||
self.nonce = None;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for AccountDetails {
|
||||
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
|
||||
self.action_tx = Some(tx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
|
||||
self.palette.with_normal_style(style.get("normal_style").copied());
|
||||
self.palette.with_normal_border_style(style.get("normal_border_style").copied());
|
||||
self.palette.with_normal_title_style(style.get("normal_title_style").copied());
|
||||
self.palette.with_popup_style(style.get("popup_style").copied());
|
||||
self.palette.with_popup_title_style(style.get("popup_title_style").copied());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::AccountDetailsOf(name, maybe_account_info) => {
|
||||
self.name = name;
|
||||
match maybe_account_info {
|
||||
Some(account_info) => {
|
||||
self.total_balance = Some(account_info.free);
|
||||
self.locked_balance = Some(account_info.frozen);
|
||||
self.reserved_balance = Some(account_info.reserved);
|
||||
self.nonce = Some(account_info.nonce);
|
||||
|
||||
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;
|
||||
self.reserved_balance = None;
|
||||
self.total_balance = None;
|
||||
self.nonce = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
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(),
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
if self.is_active {
|
||||
let (border_style, border_type) = self.palette.create_popup_style();
|
||||
let 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)),
|
||||
]),
|
||||
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))
|
||||
]),
|
||||
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))
|
||||
]),
|
||||
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)),
|
||||
]),
|
||||
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)),
|
||||
]),
|
||||
],
|
||||
[
|
||||
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)));
|
||||
|
||||
let v = Layout::vertical([Constraint::Max(7)]).flex(Flex::Center);
|
||||
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
|
||||
let [area] = v.areas(area);
|
||||
let [area] = h.areas(area);
|
||||
|
||||
frame.render_widget(Clear, area);
|
||||
frame.render_widget(table, area);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ mod accounts;
|
||||
mod overview;
|
||||
mod add_address_book_record;
|
||||
mod rename_address_book_record;
|
||||
mod details;
|
||||
|
||||
use balance::Balance;
|
||||
use transfer::Transfer;
|
||||
@ -29,11 +30,12 @@ use accounts::Accounts;
|
||||
use overview::Overview;
|
||||
use add_address_book_record::AddAddressBookRecord;
|
||||
use rename_address_book_record::RenameAddressBookRecord;
|
||||
use details::AccountDetails;
|
||||
|
||||
use super::Component;
|
||||
use crate::{action::Action, app::Mode, config::Config};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum CurrentTab {
|
||||
Nothing,
|
||||
Accounts,
|
||||
@ -44,6 +46,7 @@ pub enum CurrentTab {
|
||||
RenameAccount,
|
||||
RenameAddressBookRecord,
|
||||
Transfer,
|
||||
AccountDetails,
|
||||
}
|
||||
|
||||
pub trait PartialComponent: Component {
|
||||
@ -53,6 +56,7 @@ pub trait PartialComponent: Component {
|
||||
pub struct Wallet {
|
||||
is_active: bool,
|
||||
current_tab: CurrentTab,
|
||||
previous_tab: CurrentTab,
|
||||
components: Vec<Box<dyn PartialComponent>>,
|
||||
}
|
||||
|
||||
@ -61,6 +65,7 @@ impl Default for Wallet {
|
||||
Self {
|
||||
is_active: false,
|
||||
current_tab: CurrentTab::Accounts,
|
||||
previous_tab: CurrentTab::Accounts,
|
||||
components: vec![
|
||||
Box::new(Overview::default()),
|
||||
Box::new(Accounts::default()),
|
||||
@ -72,6 +77,7 @@ impl Default for Wallet {
|
||||
Box::new(AddAddressBookRecord::default()),
|
||||
Box::new(RenameAddressBookRecord::default()),
|
||||
Box::new(Transfer::default()),
|
||||
Box::new(AccountDetails::default()),
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -127,9 +133,10 @@ impl Component for Wallet {
|
||||
CurrentTab::RenameAccount |
|
||||
CurrentTab::RenameAddressBookRecord |
|
||||
CurrentTab::Transfer |
|
||||
CurrentTab::AccountDetails |
|
||||
CurrentTab::AddAddressBookRecord => match key.code {
|
||||
KeyCode::Esc => {
|
||||
self.current_tab = CurrentTab::Accounts;
|
||||
self.current_tab = self.previous_tab;
|
||||
for component in self.components.iter_mut() {
|
||||
component.set_active(self.current_tab.clone());
|
||||
}
|
||||
@ -150,18 +157,21 @@ impl Component for Wallet {
|
||||
return Ok(Some(Action::SetActiveScreen(Mode::Menu)));
|
||||
},
|
||||
KeyCode::Char('W') => {
|
||||
self.previous_tab = self.current_tab;
|
||||
self.current_tab = CurrentTab::AddAccount;
|
||||
for component in self.components.iter_mut() {
|
||||
component.set_active(self.current_tab.clone());
|
||||
}
|
||||
},
|
||||
KeyCode::Char('A') => {
|
||||
self.previous_tab = self.current_tab;
|
||||
self.current_tab = CurrentTab::AddAddressBookRecord;
|
||||
for component in self.components.iter_mut() {
|
||||
component.set_active(self.current_tab.clone());
|
||||
}
|
||||
},
|
||||
KeyCode::Char('T') => {
|
||||
self.previous_tab = self.current_tab;
|
||||
self.current_tab = CurrentTab::Transfer;
|
||||
for component in self.components.iter_mut() {
|
||||
component.set_active(self.current_tab.clone());
|
||||
@ -194,15 +204,32 @@ impl Component for Wallet {
|
||||
Action::SetActiveScreen(Mode::Wallet) => {
|
||||
self.is_active = true;
|
||||
self.current_tab = CurrentTab::Accounts;
|
||||
self.previous_tab = CurrentTab::Accounts;
|
||||
},
|
||||
Action::UpdateAccountName(_) | Action::NewAccount(_) => {
|
||||
self.previous_tab = self.current_tab;
|
||||
self.current_tab = CurrentTab::Accounts;
|
||||
},
|
||||
Action::UpdateAddressBookRecord(_) | Action::NewAddressBookRecord(_, _) | Action::ClosePopup => {
|
||||
self.previous_tab = self.current_tab;
|
||||
self.current_tab = CurrentTab::AddressBook;
|
||||
},
|
||||
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;
|
||||
}
|
||||
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::UpdateAccountName(_) | Action::NewAccount(_) =>
|
||||
self.current_tab = CurrentTab::Accounts,
|
||||
Action::UpdateAddressBookRecord(_) | Action::NewAddressBookRecord(_, _) | Action::ClosePopup =>
|
||||
self.current_tab = CurrentTab::AddressBook,
|
||||
Action::RenameAccount(_) => self.current_tab = CurrentTab::RenameAccount,
|
||||
Action::RenameAddressBookRecord(_) =>
|
||||
self.current_tab = CurrentTab::RenameAddressBookRecord,
|
||||
Action::TransferTo(_) => self.current_tab = CurrentTab::Transfer,
|
||||
_ => {}
|
||||
}
|
||||
for component in self.components.iter_mut() {
|
||||
|
Loading…
Reference in New Issue
Block a user