use egui::ScrollArea; use enostr::{RelayLogEvent, SubsDebug}; pub struct RelayDebugView<'a> { debug: &'a mut SubsDebug, } impl<'a> RelayDebugView<'a> { pub fn new(debug: &'a mut SubsDebug) -> Self { Self { debug } } } impl RelayDebugView<'_> { pub fn ui(&mut self, ui: &mut egui::Ui) { ScrollArea::vertical() .id_salt(ui.id().with("relays_debug")) .max_height(ui.max_rect().height() / 2.0) .show(ui, |ui| { ui.label("Active Relays:"); for (relay_str, data) in self.debug.get_data() { egui::CollapsingHeader::new(format!( "{} {} {}", relay_str, format_total(&data.count), format_sec(&data.count) )) .default_open(true) .show(ui, |ui| { ui.horizontal_wrapped(|ui| { for (i, sub_data) in data.sub_data.values().enumerate() { ui.label(format!( "Filter {} ({})", i + 1, format_sec(&sub_data.count) )) .on_hover_cursor(egui::CursorIcon::Help) .on_hover_text(sub_data.filter.to_string()); } }) }); } }); ui.separator(); egui::ComboBox::from_label("Show events from relay") .selected_text( self.debug .relay_events_selection .as_ref() .map_or(String::new(), |s| s.clone()), ) .show_ui(ui, |ui| { let mut make_selection = None; for relay in self.debug.get_data().keys() { if ui .selectable_label( if let Some(s) = &self.debug.relay_events_selection { *s == *relay } else { false }, relay, ) .clicked() { make_selection = Some(relay.clone()); } } if make_selection.is_some() { self.debug.relay_events_selection = make_selection } }); let show_relay_evs = |ui: &mut egui::Ui, relay: Option, events: Vec| { for ev in events { ui.horizontal_wrapped(|ui| { if let Some(r) = &relay { ui.label("relay").on_hover_text(r.clone()); } match ev { RelayLogEvent::Send(client_message) => { ui.label("SEND: "); let msg = &match client_message { enostr::ClientMessage::Event { .. } => "Event", enostr::ClientMessage::Req { .. } => "Req", enostr::ClientMessage::Close { .. } => "Close", enostr::ClientMessage::Raw(_) => "Raw", }; if let Ok(json) = client_message.to_json() { ui.label(*msg).on_hover_text(json) } else { ui.label(*msg) } } RelayLogEvent::Recieve(e) => { ui.label("RECIEVE: "); match e { enostr::OwnedRelayEvent::Opened => ui.label("Opened"), enostr::OwnedRelayEvent::Closed => ui.label("Closed"), enostr::OwnedRelayEvent::Other(s) => { ui.label("Other").on_hover_text(s) } enostr::OwnedRelayEvent::Error(s) => { ui.label("Error").on_hover_text(s) } enostr::OwnedRelayEvent::Message(s) => { ui.label("Message").on_hover_text(s) } } } } }); } }; ScrollArea::vertical() .id_salt(ui.id().with("events")) .show(ui, |ui| { if let Some(relay) = &self.debug.relay_events_selection { if let Some(data) = self.debug.get_data().get(relay) { show_relay_evs(ui, None, data.events.clone()); } } else { for (relay, data) in self.debug.get_data() { show_relay_evs(ui, Some(relay.clone()), data.events.clone()); } } }); self.debug.try_increment_stats(); } pub fn window(ctx: &egui::Context, debug: &mut SubsDebug) { let mut open = true; egui::Window::new("Relay Debugger") .open(&mut open) .show(ctx, |ui| { RelayDebugView::new(debug).ui(ui); }); } } fn format_sec(c: &enostr::TransferStats) -> String { format!( "⬇{} ⬆️{}", byte_to_string(c.down_sec_prior), byte_to_string(c.up_sec_prior) ) } fn format_total(c: &enostr::TransferStats) -> String { format!( "total: ⬇{} ⬆️{}", byte_to_string(c.down_total), byte_to_string(c.up_total) ) } const MB: usize = 1_048_576; const KB: usize = 1024; fn byte_to_string(b: usize) -> String { if b >= MB { let mbs = b as f32 / MB as f32; format!("{:.2} MB", mbs) } else if b >= KB { let kbs = b as f32 / KB as f32; format!("{:.2} KB", kbs) } else { format!("{} B", b) } }