Files
p2p-chat/src/tui/chat_panel.rs
2026-02-11 20:37:44 +03:00

89 lines
2.5 KiB
Rust

//! Chat panel widget — scrollable chat history.
use ratatui::layout::Rect;
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, List, ListItem};
use ratatui::Frame;
use crate::chat::{ChatEntry, ChatState};
use crate::tui::App;
pub fn render(frame: &mut Frame, area: Rect, chat: &ChatState, app: &App) {
let block = Block::default()
.title(" 💬 Chat ")
.borders(Borders::ALL)
.border_style(Style::default().fg(app.theme.border));
let inner = block.inner(area);
frame.render_widget(block, area);
if chat.history.is_empty() {
let empty = ratatui::widgets::Paragraph::new("No messages yet. Start typing!")
.style(Style::default().fg(app.theme.time));
frame.render_widget(empty, inner);
return;
}
let visible_height = inner.height as usize;
let total = chat.history.len();
// Calculate scroll position
let end = if app.scroll_offset >= total {
total
} else {
total - app.scroll_offset
};
let start = end.saturating_sub(visible_height);
let items: Vec<ListItem> = chat.history[start..end]
.iter()
.map(|entry| format_entry(entry, app))
.collect();
let list = List::new(items);
frame.render_widget(list, inner);
}
fn format_entry(entry: &ChatEntry, app: &App) -> ListItem<'static> {
let time = chrono::DateTime::from_timestamp_millis(entry.timestamp as i64)
.map(|dt| dt.format("%H:%M").to_string())
.unwrap_or_default();
if entry.is_system {
let line = Line::from(vec![
Span::styled(
format!("[{}] ", time),
Style::default().fg(app.theme.time),
),
Span::styled(
format!("*** {} ***", entry.text),
Style::default()
.fg(app.theme.system_msg)
.add_modifier(Modifier::ITALIC),
),
]);
return ListItem::new(line);
}
let name_color = if entry.is_self {
app.theme.self_name
} else {
app.theme.peer_name
};
let line = Line::from(vec![
Span::styled(format!("[{}] ", time), Style::default().fg(app.theme.time)),
Span::styled(
format!("{}: ", entry.sender_name),
Style::default()
.fg(name_color)
.add_modifier(Modifier::BOLD),
),
Span::styled(entry.text.clone(), Style::default().fg(app.theme.text)),
]);
ListItem::new(line)
}