use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style}, text::{Line, Span}, widgets::{Block, Borders, Paragraph}, Frame, }; use crate::app::App; use crate::utils::format_timestamp; pub fn render(f: &mut Frame, area: Rect, app: &App) { if let Some(chat) = app.get_selected_chat() { let message_chunks = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Length(3), // Chat header Constraint::Min(0), // Messages Constraint::Length(3), // Input box ]) .split(area); // Chat header let header = Paragraph::new(format!("👤 {}", chat.title)) .block(Block::default().borders(Borders::ALL)) .style( Style::default() .fg(Color::Cyan) .add_modifier(Modifier::BOLD), ); f.render_widget(header, message_chunks[0]); // Messages let mut lines: Vec = Vec::new(); for msg in &app.current_messages { let sender_style = if msg.is_outgoing { Style::default() .fg(Color::Green) .add_modifier(Modifier::BOLD) } else { Style::default() .fg(Color::Cyan) .add_modifier(Modifier::BOLD) }; let sender_name = if msg.is_outgoing { "You".to_string() } else { msg.sender_name.clone() }; let read_mark = if msg.is_outgoing { if msg.is_read { " ✓✓" } else { " ✓" } } else { "" }; // Форматируем время let time = format_timestamp(msg.date); lines.push(Line::from(vec![ Span::styled(format!("{} ", sender_name), sender_style), Span::raw("── "), Span::styled(format!("{}{}", time, read_mark), Style::default().fg(Color::DarkGray)), ])); lines.push(Line::from(msg.content.clone())); lines.push(Line::from("")); } if lines.is_empty() { lines.push(Line::from(Span::styled( "Нет сообщений", Style::default().fg(Color::DarkGray), ))); } // Вычисляем скролл с учётом пользовательского offset let visible_height = message_chunks[1].height.saturating_sub(2) as usize; let total_lines = lines.len(); let base_scroll = if total_lines > visible_height { total_lines - visible_height } else { 0 }; let scroll_offset = base_scroll.saturating_sub(app.message_scroll_offset) as u16; let messages_widget = Paragraph::new(lines) .block(Block::default().borders(Borders::ALL)) .scroll((scroll_offset, 0)); f.render_widget(messages_widget, message_chunks[1]); // Input box let input_text = if app.message_input.is_empty() { "> Введите сообщение...".to_string() } else { format!("> {}", app.message_input) }; let input_style = if app.message_input.is_empty() { Style::default().fg(Color::DarkGray) } else { Style::default().fg(Color::Yellow) }; let input = Paragraph::new(input_text) .block(Block::default().borders(Borders::ALL)) .style(input_style); f.render_widget(input, message_chunks[2]); } else { let empty = Paragraph::new("Выберите чат") .block(Block::default().borders(Borders::ALL)) .style(Style::default().fg(Color::DarkGray)) .alignment(Alignment::Center); f.render_widget(empty, area); } }