This commit is contained in:
Mikhail Kilin
2026-01-18 14:49:31 +03:00
parent d701464fde
commit b6d9291864
29 changed files with 3920 additions and 833 deletions

116
src/ui/messages.rs Normal file
View File

@@ -0,0 +1,116 @@
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<Line> = 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);
}
}