p2p_chat/ui/terminal/
controller.rs1use std::sync::Arc;
4
5use anyhow::Result;
6use chrono::{Duration, Utc};
7use tokio::sync::mpsc;
8use tracing::{debug, error};
9
10use crate::cli::commands::Node;
11use crate::logging::LogBuffer;
12use crate::types::Message;
13use crate::ui::{ChatMode, LogMode, UIAction, UIEvent, UIState};
14
15pub struct TerminalUI {
17 pub(super) state: UIState,
19 pub(super) chat_mode: ChatMode,
21 pub(super) log_mode: LogMode,
23 pub(super) event_rx: mpsc::UnboundedReceiver<UIEvent>,
25 pub(super) action_tx: mpsc::UnboundedSender<UIAction>,
27 pub(super) node: Option<Arc<Node>>,
29 pub(super) log_buffer: Option<Arc<LogBuffer>>,
31}
32
33impl TerminalUI {
34 pub fn new(
41 event_rx: mpsc::UnboundedReceiver<UIEvent>,
42 action_tx: mpsc::UnboundedSender<UIAction>,
43 ) -> Self {
44 Self {
45 state: UIState::new(),
46 chat_mode: ChatMode::new(),
47 log_mode: LogMode::new(),
48 event_rx,
49 action_tx,
50 node: None,
51 log_buffer: None,
52 }
53 }
54
55 pub fn set_node(&mut self, node: Arc<Node>) {
57 self.node = Some(node);
58 }
59
60 pub fn set_log_buffer(&mut self, log_buffer: Arc<LogBuffer>) {
62 self.log_buffer = Some(log_buffer);
63 }
64
65 pub fn update_friends(&mut self, friends: Vec<String>) {
67 self.chat_mode.update_friends(friends);
68 }
69
70 pub fn update_discovered_peers(&mut self, peers: Vec<String>) {
72 self.chat_mode.update_discovered_peers(peers);
73 }
74
75 pub fn preload_messages(&mut self, messages: Vec<Message>) {
83 let count = messages.len();
84 let earliest_timestamp = messages
85 .iter()
86 .filter_map(|msg| chrono::DateTime::<Utc>::from_timestamp_millis(msg.timestamp))
87 .min();
88
89 self.state.replace_messages(messages);
90
91 if count > 0 {
92 if let Some(earliest) = earliest_timestamp {
93 let header_ts = earliest
94 .checked_sub_signed(Duration::milliseconds(1))
95 .unwrap_or(earliest);
96 self.state.chat_messages.push((
97 header_ts,
98 format!(
99 "__HISTORY_OUTPUT__History: last {} message{}",
100 count,
101 if count == 1 { "" } else { "s" }
102 ),
103 ));
104 }
105 }
106 }
107
108 pub async fn run(&mut self) -> Result<()> {
118 self.initialize_terminal()?;
119
120 debug!("Starting terminal UI loop");
121
122 loop {
123 if let Some(event) = self.event_rx.recv().await {
124 if let Err(e) = self.handle_event(event).await {
125 error!("Error handling UI event: {}", e);
126 }
127 }
128
129 self.render()?;
130 }
131 }
132}