From 2c82f698508420310b4b7555b98a89c9e2f6cb4e Mon Sep 17 00:00:00 2001 From: micielski <73398428+micielski@users.noreply.github.com> Date: Tue, 27 Aug 2024 16:20:41 +0200 Subject: [PATCH] feat: graphs in statistics (#108) --- rm-main/src/tui/tabs/torrents/mod.rs | 3 + rm-main/src/tui/tabs/torrents/popups/mod.rs | 2 +- rm-main/src/tui/tabs/torrents/popups/stats.rs | 74 +++++++++++++++++-- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/rm-main/src/tui/tabs/torrents/mod.rs b/rm-main/src/tui/tabs/torrents/mod.rs index 02c2a43..ff4f0d5 100644 --- a/rm-main/src/tui/tabs/torrents/mod.rs +++ b/rm-main/src/tui/tabs/torrents/mod.rs @@ -167,6 +167,9 @@ impl Component for TorrentsTab { fn handle_update_action(&mut self, action: UpdateAction) { match action { UpdateAction::SessionStats(stats) => { + if let Some(CurrentPopup::Stats(popup)) = &mut self.popup_manager.current_popup { + popup.update_stats(&stats) + } self.bottom_stats.set_stats(stats); } UpdateAction::FreeSpace(free_space) => { diff --git a/rm-main/src/tui/tabs/torrents/popups/mod.rs b/rm-main/src/tui/tabs/torrents/popups/mod.rs index 961b464..9b24819 100644 --- a/rm-main/src/tui/tabs/torrents/popups/mod.rs +++ b/rm-main/src/tui/tabs/torrents/popups/mod.rs @@ -15,7 +15,7 @@ pub mod stats; pub struct PopupManager { ctx: app::Ctx, - current_popup: Option, + pub current_popup: Option, } pub enum CurrentPopup { diff --git a/rm-main/src/tui/tabs/torrents/popups/stats.rs b/rm-main/src/tui/tabs/torrents/popups/stats.rs index 8498c1d..add8ea5 100644 --- a/rm-main/src/tui/tabs/torrents/popups/stats.rs +++ b/rm-main/src/tui/tabs/torrents/popups/stats.rs @@ -2,8 +2,9 @@ use std::sync::Arc; use ratatui::{ prelude::*, - widgets::{Clear, Paragraph}, + widgets::{BarChart, Block, Clear, Paragraph}, }; +use rm_config::CONFIG; use transmission_rpc::types::SessionStats; use rm_shared::{action::Action, utils::bytes_to_human_format}; @@ -14,11 +15,37 @@ use crate::tui::components::{ pub struct StatisticsPopup { stats: Arc, + upload_data: Vec<(&'static str, u64)>, + download_data: Vec<(&'static str, u64)>, + max_up: i64, + max_down: i64, } impl StatisticsPopup { - pub const fn new(stats: Arc) -> Self { - Self { stats } + pub fn new(stats: Arc) -> Self { + Self { + upload_data: vec![("", stats.upload_speed as u64)], + download_data: vec![("", stats.download_speed as u64)], + max_up: stats.upload_speed, + max_down: stats.download_speed, + stats, + } + } + + pub fn update_stats(&mut self, stats: &SessionStats) { + let up = stats.upload_speed; + let down = stats.download_speed; + + if up > self.max_up { + self.max_up = up; + } + + if down > self.max_down { + self.max_down = down; + } + + self.upload_data.insert(0, ("", up as u64)); + self.download_data.insert(0, ("", down as u64)); } } @@ -33,20 +60,57 @@ impl Component for StatisticsPopup { } fn render(&mut self, f: &mut Frame, rect: Rect) { - let (popup_rect, block_rect, text_rect) = popup_rects(rect, 50, 50); + let (popup_rect, block_rect, text_rect) = popup_rects(rect, 75, 50); + + let [text_rect, _, upload_rect, download_rect] = Layout::vertical([ + Constraint::Length(3), + Constraint::Length(1), + Constraint::Percentage(50), + Constraint::Percentage(50), + ]) + .areas(text_rect); let block = popup_block_with_close_highlight(" Statistics "); + let upload_barchart = make_barchart("Upload", self.max_up as u64, &self.upload_data); + let download_barchart = + make_barchart("Download", self.max_down as u64, &self.download_data); + let uploaded_bytes = self.stats.cumulative_stats.uploaded_bytes; let downloaded_bytes = self.stats.cumulative_stats.downloaded_bytes; let uploaded = bytes_to_human_format(uploaded_bytes); let downloaded = bytes_to_human_format(downloaded_bytes); let ratio = uploaded_bytes as f64 / downloaded_bytes as f64; - let text = format!("Uploaded: {uploaded}\nDownloaded: {downloaded}\nRatio: {ratio:.2}"); + let text = format!( + "Total uploaded: {uploaded}\nTotal downloaded: {downloaded}\nRatio: {ratio:.2}" + ); let paragraph = Paragraph::new(text); f.render_widget(Clear, popup_rect); f.render_widget(block, block_rect); f.render_widget(paragraph, text_rect); + f.render_widget(upload_barchart, upload_rect); + f.render_widget(download_barchart, download_rect); } } + +fn make_barchart<'a>( + name: &'static str, + max: u64, + data: &'a [(&'static str, u64)], +) -> BarChart<'a> { + let avg = bytes_to_human_format( + (data.iter().fold(0, |acc, x| acc + x.1) / u64::try_from(data.len()).unwrap()) as i64, + ); + + BarChart::default() + .block(Block::new().title(format!( + "{name} (avg {avg}/sec - max {})", + bytes_to_human_format(max as i64) + ))) + .bar_width(1) + .bar_gap(0) + .bar_style(Style::new().fg(CONFIG.general.accent_color)) + .data(data) + .max(max) +}