ghost-eye/src/components/validator/reward_details.rs
Uncle Stretch b9c863e2ea
align tables on validator tab with additional values
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-08-08 02:27:40 +03:00

178 lines
6.4 KiB
Rust

use color_eyre::Result;
use ratatui::layout::Constraint;
use ratatui::{
text::Text,
layout::{Alignment, Rect},
widgets::{Block, Cell, Row, Table},
Frame
};
use super::{PartialComponent, Component, CurrentTab};
use crate::types::EraRewardPoints;
use crate::widgets::DotSpinner;
use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
pub struct RewardDetails {
palette: StylePalette,
commission: Option<u32>,
nominators_blocked: bool,
apy: String,
treasury_apy: String,
inflation: String,
stash: [u8; 32],
in_staking_validators: bool,
in_queued_keys: bool,
in_next_keys: bool,
is_disabled: bool,
in_rewards: bool,
}
impl Default for RewardDetails {
fn default() -> Self {
Self::new()
}
}
impl RewardDetails {
pub fn new() -> Self {
Self {
palette: StylePalette::default(),
commission: None,
nominators_blocked: false,
apy: String::from("0.0%"),
treasury_apy: String::from("0.0%"),
inflation: String::from("0.0%"),
stash: [0u8; 32],
in_staking_validators: false,
in_queued_keys: false,
in_next_keys: false,
is_disabled: false,
in_rewards: false,
}
}
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) }
},
None => DotSpinner::default().to_string(),
}
}
fn update_individual(&mut self, individual: &Vec<EraRewardPoints>) {
let (is_disabled, in_rewards) = individual
.iter()
.find(|data| data.account_id == self.stash)
.map(|data| (data.disabled, true))
.unwrap_or((false, false));
self.is_disabled = is_disabled;
self.in_rewards = in_rewards;
}
}
impl PartialComponent for RewardDetails {
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());
}
Ok(())
}
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 => {
self.commission = commission;
self.in_staking_validators = commission.is_some();
self.nominators_blocked = disabled;
}
Action::SetSessionKey(name, session_key_info) => {
if name.starts_with("q_") {
self.in_queued_keys = !session_key_info.key.is_empty();
} 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,
_ => {}
};
Ok(None)
}
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [_, _, place] = super::validator_balance_layout(area);
let (border_style, border_type) = self.palette.create_border_style(false);
let status = if self.is_disabled {
"Disabled"
} else {
let is_fully_in_session = self.in_queued_keys && self.in_next_keys;
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" }
_ => "Nothing",
}
};
let staking_status = format!("Staking status: {status}");
let table = Table::new(
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)),
]),
Row::new(vec![
Cell::from(Text::from("Validator APY".to_string()).alignment(Alignment::Left)),
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)),
]),
Row::new(vec![
Cell::from(Text::from("Inflation".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(self.inflation.clone()).alignment(Alignment::Right)),
]),
],
[
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));
frame.render_widget(table, place);
Ok(())
}
}