//! Shared message list rendering for search and pinned modals use crate::tdlib::MessageInfo; use ratatui::{ layout::Alignment, style::{Color, Modifier, Style}, text::{Line, Span}, widgets::{Block, Borders, Paragraph}, }; /// Renders a single message item with marker, sender, date, and wrapped text pub fn render_message_item( msg: &MessageInfo, is_selected: bool, content_width: usize, max_preview_lines: usize, ) -> Vec> { let mut lines = Vec::new(); // Marker, sender name, and date let marker = if is_selected { "▶ " } else { " " }; let sender_color = if msg.is_outgoing() { Color::Green } else { Color::Cyan }; let sender_name = if msg.is_outgoing() { "Вы".to_string() } else { msg.sender_name().to_string() }; lines.push(Line::from(vec![ Span::styled( marker.to_string(), Style::default() .fg(Color::Yellow) .add_modifier(Modifier::BOLD), ), Span::styled( format!("{} ", sender_name), Style::default() .fg(sender_color) .add_modifier(Modifier::BOLD), ), Span::styled( format!("({})", crate::utils::format_datetime(msg.date())), Style::default().fg(Color::Gray), ), ])); // Wrapped message text let msg_color = if is_selected { Color::Yellow } else { Color::White }; let max_width = content_width.saturating_sub(4); let wrapped = crate::ui::messages::wrap_text_with_offsets(msg.text(), max_width); let wrapped_count = wrapped.len(); for wrapped_line in wrapped.into_iter().take(max_preview_lines) { lines.push(Line::from(vec![ Span::raw(" ".to_string()), Span::styled(wrapped_line.text, Style::default().fg(msg_color)), ])); } if wrapped_count > max_preview_lines { lines.push(Line::from(vec![ Span::raw(" ".to_string()), Span::styled("...".to_string(), Style::default().fg(Color::Gray)), ])); } lines } /// Calculates scroll offset to keep selected item visible pub fn calculate_scroll_offset( selected_index: usize, lines_per_item: usize, visible_height: u16, ) -> u16 { let visible = visible_height.saturating_sub(2) as usize; let selected_line = selected_index * lines_per_item; if selected_line > visible / 2 { (selected_line - visible / 2) as u16 } else { 0 } } /// Renders a help bar with keyboard shortcuts pub fn render_help_bar(shortcuts: &[(&str, &str, Color)], border_color: Color) -> Paragraph<'static> { let mut spans: Vec> = Vec::new(); for (i, (key, label, color)) in shortcuts.iter().enumerate() { if i > 0 { spans.push(Span::raw(" ".to_string())); } spans.push(Span::styled( format!(" {} ", key), Style::default() .fg(*color) .add_modifier(Modifier::BOLD), )); spans.push(Span::raw(label.to_string())); } Paragraph::new(Line::from(spans)) .block( Block::default() .borders(Borders::ALL) .border_style(Style::default().fg(border_color)), ) .alignment(Alignment::Center) }