use darkfi::error::{Error, Result}; use fxhash::{FxHashMap, FxHashSet}; use tui::widgets::ListState; use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, style::Style, text::{Span, Spans}, widgets::{Block, Borders, List, ListItem, Paragraph}, Frame, }; use crate::model::{NodeInfo, SelectableObject}; //use log::debug; #[derive(Debug)] pub struct View { pub active_ids: IdListView, pub nodes: NodeInfoView, pub selectables: FxHashMap, pub msg_log: FxHashMap>, } impl View { pub fn new( active_ids: IdListView, nodes: NodeInfoView, selectables: FxHashMap, msg_log: FxHashMap>, ) -> View { View { active_ids, nodes, selectables, msg_log } } pub fn update( &mut self, nodes: FxHashMap, selectables: FxHashMap, msg_log: FxHashMap>, ) { self.update_nodes(nodes); self.update_selectable(selectables); self.update_active_ids(); self.update_msg_log(msg_log); } fn update_nodes(&mut self, nodes: FxHashMap) { for (id, node) in nodes { self.nodes.infos.insert(id, node); } } fn update_selectable(&mut self, selectables: FxHashMap) { for (id, obj) in selectables { self.selectables.insert(id, obj); } } fn update_active_ids(&mut self) { for info in self.nodes.infos.values() { self.active_ids.ids.insert(info.node_id.to_string()); for child in &info.children { if !child.is_empty == true { self.active_ids.ids.insert(child.session_id.to_string()); for child in &child.children { self.active_ids.ids.insert(child.connect_id.to_string()); } } } } } fn update_msg_log(&mut self, msg_log: FxHashMap>) { for (id, msg) in msg_log { self.msg_log.insert(id, msg); } } pub fn render(&mut self, f: &mut Frame<'_, B>) { let mut nodes = Vec::new(); let mut ids = Vec::new(); let style = Style::default(); let list_margin = 2; let list_direction = Direction::Horizontal; let list_cnstrnts = vec![Constraint::Percentage(50), Constraint::Percentage(50)]; for info in self.nodes.infos.values() { let name_span = Span::raw(&info.node_name); let lines = vec![Spans::from(name_span)]; let names = ListItem::new(lines); nodes.push(names); ids.push(&info.node_id); for session in &info.children { if !session.is_empty == true { let name = Span::styled(format!(" {}", session.session_name), style); let lines = vec![Spans::from(name)]; let names = ListItem::new(lines); nodes.push(names); ids.push(&session.session_id); for connection in &session.children { let mut info = Vec::new(); let name = Span::styled(format!(" {}", connection.addr), style); info.push(name); match connection.last_status.as_str() { "recv" => { let msg = Span::styled( format!(" [R: {}]", connection.last_msg), style, ); info.push(msg); } "sent" => { let msg = Span::styled( format!(" [S: {}]", connection.last_msg), style, ); info.push(msg); } _ => { // TODO } } let lines = vec![Spans::from(info)]; let names = ListItem::new(lines); nodes.push(names); ids.push(&connection.connect_id); } } } } let slice = Layout::default() .direction(list_direction) .margin(list_margin) .constraints(list_cnstrnts) .split(f.size()); let nodes = List::new(nodes).block(Block::default().borders(Borders::ALL)).highlight_symbol(">> "); f.render_stateful_widget(nodes, slice[0], &mut self.active_ids.state); ids.dedup(); // get the id at the current index match self.active_ids.state.selected() { Some(i) => { match ids.get(i) { Some(i) => { self.render_info(f, slice.clone(), i.to_string()); // found id } None => { // TODO: Error } } } None => { // TODO: nothing is selected } } } fn render_info( &mut self, f: &mut Frame<'_, B>, slice: Vec, selected: String, ) { let style = Style::default(); //let mut infos = Vec::new(); let mut spans = Vec::new(); //let mut msgs = Vec::new(); let info = self.selectables.get(&selected); match info { Some(SelectableObject::Node(node)) => { let name_span = Spans::from("Node Info"); spans.push(name_span); } Some(SelectableObject::Session(session)) => { let name_span = Spans::from("Session Info"); spans.push(name_span); } Some(SelectableObject::Connect(connect)) => { let log = self.msg_log.get(&connect.connect_id); match log { Some(values) => { for (k, v) in values { match k.as_str() { "send" => { let msg_log = Spans::from(Span::styled(format!("S: {}", v), style)); spans.push(msg_log); } "recv" => { let msg_log = Spans::from(Span::styled(format!("R: {}", v), style)); spans.push(msg_log); } _ => { // TODO } } } } None => { // TODO } } } None => { // TODO } } let graph = Paragraph::new(spans) .block(Block::default().borders(Borders::ALL)) .style(Style::default()); f.render_widget(graph, slice[1]); } } #[derive(Debug, Clone)] pub struct IdListView { pub state: ListState, pub ids: FxHashSet, } impl IdListView { pub fn new(ids: FxHashSet) -> IdListView { IdListView { state: ListState::default(), ids } } pub fn next(&mut self) { let i = match self.state.selected() { Some(i) => { if i >= self.ids.len() - 1 { 0 } else { i + 1 } } None => 0, }; self.state.select(Some(i)); } pub fn previous(&mut self) { let i = match self.state.selected() { Some(i) => { if i == 0 { self.ids.len() - 1 } else { i - 1 } } None => 0, }; self.state.select(Some(i)); } pub fn unselect(&mut self) { self.state.select(None); } } #[derive(Debug, Clone)] pub struct NodeInfoView { pub index: usize, pub infos: FxHashMap, } impl NodeInfoView { pub fn new(infos: FxHashMap) -> NodeInfoView { let index = 0; NodeInfoView { index, infos } } pub fn next(&mut self) { self.index = (self.index + 1) % self.infos.len(); } pub fn previous(&mut self) { if self.index > 0 { self.index -= 1; } else { self.index = self.infos.len() - 1; } } }