From 0a9ae8b4480e73c9685fe0aed27ceb559a8f78db Mon Sep 17 00:00:00 2001 From: Mikhail Kilin Date: Wed, 21 Jan 2026 02:49:28 +0300 Subject: [PATCH] fixes --- src/main.rs | 18 +++++++--- src/tdlib/client.rs | 87 +++++++++++++++++++++++++++++++++++++-------- src/ui/messages.rs | 80 +++++++++++++++++++++++++---------------- 3 files changed, 137 insertions(+), 48 deletions(-) diff --git a/src/main.rs b/src/main.rs index fd52145..dc77f03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -109,12 +109,22 @@ async fn run_app( app.td_client.process_pending_view_messages().await; } + // Обрабатываем очередь user_id для загрузки имён + if !app.td_client.pending_user_ids.is_empty() { + app.td_client.process_pending_user_ids().await; + } + // Синхронизируем сообщения из td_client в app (для новых сообщений в реальном времени) if app.selected_chat_id.is_some() && !app.td_client.current_chat_messages.is_empty() { - // Добавляем новые сообщения, которых ещё нет в app.current_messages - for msg in &app.td_client.current_chat_messages { - if !app.current_messages.iter().any(|m| m.id == msg.id) { - app.current_messages.push(msg.clone()); + // Синхронизируем все сообщения (включая обновлённые имена и is_read) + for td_msg in &app.td_client.current_chat_messages { + if let Some(app_msg) = app.current_messages.iter_mut().find(|m| m.id == td_msg.id) { + // Обновляем существующее сообщение + app_msg.sender_name = td_msg.sender_name.clone(); + app_msg.is_read = td_msg.is_read; + } else { + // Добавляем новое сообщение + app.current_messages.push(td_msg.clone()); } } } diff --git a/src/tdlib/client.rs b/src/tdlib/client.rs index 9f3f654..04db9eb 100644 --- a/src/tdlib/client.rs +++ b/src/tdlib/client.rs @@ -52,10 +52,14 @@ pub struct TdClient { pub current_chat_id: Option, /// Кэш usernames: user_id -> username user_usernames: HashMap, + /// Кэш имён: user_id -> display_name (first_name + last_name) + user_names: HashMap, /// Связь chat_id -> user_id для приватных чатов chat_user_ids: HashMap, /// Очередь сообщений для отметки как прочитанных: (chat_id, message_ids) pub pending_view_messages: Vec<(i64, Vec)>, + /// Очередь user_id для загрузки имён + pub pending_user_ids: Vec, } #[allow(dead_code)] @@ -78,8 +82,10 @@ impl TdClient { current_chat_messages: Vec::new(), current_chat_id: None, user_usernames: HashMap::new(), + user_names: HashMap::new(), chat_user_ids: HashMap::new(), pending_view_messages: Vec::new(), + pending_user_ids: Vec::new(), } } @@ -211,8 +217,18 @@ impl TdClient { } } Update::User(update) => { - // Сохраняем username пользователя + // Сохраняем имя и username пользователя let user = update.user; + + // Сохраняем display name (first_name + last_name) + let display_name = if user.last_name.is_empty() { + user.first_name.clone() + } else { + format!("{} {}", user.first_name, user.last_name) + }; + self.user_names.insert(user.id, display_name); + + // Сохраняем username если есть if let Some(usernames) = user.usernames { if let Some(username) = usernames.active_usernames.first() { self.user_usernames.insert(user.id, username.clone()); @@ -305,10 +321,28 @@ impl TdClient { self.chats.sort_by(|a, b| b.order.cmp(&a.order)); } - fn convert_message(&self, message: &TdMessage, chat_id: i64) -> MessageInfo { + fn convert_message(&mut self, message: &TdMessage, chat_id: i64) -> MessageInfo { let sender_name = match &message.sender_id { - tdlib_rs::enums::MessageSender::User(user) => format!("User_{}", user.user_id), - tdlib_rs::enums::MessageSender::Chat(chat) => format!("Chat_{}", chat.chat_id), + tdlib_rs::enums::MessageSender::User(user) => { + // Пробуем получить имя из кеша + if let Some(name) = self.user_names.get(&user.user_id) { + name.clone() + } else { + // Добавляем в очередь для загрузки + if !self.pending_user_ids.contains(&user.user_id) { + self.pending_user_ids.push(user.user_id); + } + format!("User_{}", user.user_id) + } + } + tdlib_rs::enums::MessageSender::Chat(chat) => { + // Для чатов используем название чата + self.chats + .iter() + .find(|c| c.id == chat.chat_id) + .map(|c| c.title.clone()) + .unwrap_or_else(|| format!("Chat_{}", chat.chat_id)) + } }; // Определяем, прочитано ли исходящее сообщение @@ -412,11 +446,10 @@ impl TdClient { match result { Ok(tdlib_rs::enums::Messages::Messages(messages)) => { - let batch: Vec = messages - .messages - .into_iter() - .filter_map(|m| m.map(|msg| self.convert_message(&msg, chat_id))) - .collect(); + let mut batch: Vec = Vec::new(); + for m in messages.messages.into_iter().flatten() { + batch.push(self.convert_message(&m, chat_id)); + } if batch.is_empty() { break; @@ -484,11 +517,10 @@ impl TdClient { match result { Ok(tdlib_rs::enums::Messages::Messages(messages)) => { - let mut result_messages: Vec = messages - .messages - .into_iter() - .filter_map(|m| m.map(|msg| self.convert_message(&msg, chat_id))) - .collect(); + let mut result_messages: Vec = Vec::new(); + for m in messages.messages.into_iter().flatten() { + result_messages.push(self.convert_message(&m, chat_id)); + } // Сообщения приходят от новых к старым, переворачиваем result_messages.reverse(); @@ -585,6 +617,33 @@ impl TdClient { .await; } } + + /// Обработка очереди user_id для загрузки имён + pub async fn process_pending_user_ids(&mut self) { + let pending = std::mem::take(&mut self.pending_user_ids); + for user_id in pending { + // Пропускаем если имя уже есть + if self.user_names.contains_key(&user_id) { + continue; + } + // Загружаем информацию о пользователе + if let Ok(User::User(user)) = functions::get_user(user_id, self.client_id).await { + let display_name = if user.last_name.is_empty() { + user.first_name.clone() + } else { + format!("{} {}", user.first_name, user.last_name) + }; + self.user_names.insert(user_id, display_name.clone()); + + // Обновляем имя в текущих сообщениях + for msg in &mut self.current_chat_messages { + if msg.sender_name == format!("User_{}", user_id) { + msg.sender_name = display_name.clone(); + } + } + } + } + } } /// Статическая функция для извлечения текста сообщения (без &self) diff --git a/src/ui/messages.rs b/src/ui/messages.rs index e01cb2a..b464859 100644 --- a/src/ui/messages.rs +++ b/src/ui/messages.rs @@ -33,6 +33,9 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) { ); f.render_widget(header, message_chunks[0]); + // Ширина области сообщений (без рамок) + let content_width = message_chunks[1].width.saturating_sub(2) as usize; + // Messages с группировкой по дате и отправителю let mut lines: Vec = Vec::new(); let mut last_day: Option = None; @@ -45,13 +48,13 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) { if last_day.is_some() { lines.push(Line::from("")); // Пустая строка перед разделителем } - // Добавляем разделитель даты + // Добавляем разделитель даты по центру let date_str = format_date(msg.date); + let date_line = format!("──────── {} ────────", date_str); + let padding = content_width.saturating_sub(date_line.chars().count()) / 2; lines.push(Line::from(vec![ - Span::styled( - format!("──────── {} ────────", date_str), - Style::default().fg(Color::DarkGray), - ), + Span::raw(" ".repeat(padding)), + Span::styled(date_line, Style::default().fg(Color::Gray)), ])); lines.push(Line::from("")); last_day = Some(msg_day); @@ -76,20 +79,28 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) { } let sender_style = if msg.is_outgoing { - Style::default() - .fg(Color::Green) - .add_modifier(Modifier::BOLD) + Style::default().fg(Color::Green).add_modifier(Modifier::BOLD) } else { - Style::default() - .fg(Color::Cyan) - .add_modifier(Modifier::BOLD) + Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD) }; - // Заголовок отправителя - lines.push(Line::from(vec![ - Span::styled(format!("{} ", sender_name), sender_style), - Span::styled("────────────────", Style::default().fg(Color::DarkGray)), - ])); + if msg.is_outgoing { + // Заголовок "Вы" справа + let header_text = format!("{} ────────────────", sender_name); + let header_len = header_text.chars().count(); + let padding = content_width.saturating_sub(header_len + 1); + lines.push(Line::from(vec![ + Span::raw(" ".repeat(padding)), + Span::styled(format!("{} ", sender_name), sender_style), + Span::styled("────────────────", Style::default().fg(Color::Gray)), + ])); + } else { + // Заголовок входящих слева + lines.push(Line::from(vec![ + Span::styled(format!("{} ", sender_name), sender_style), + Span::styled("────────────────", Style::default().fg(Color::Gray)), + ])); + } last_sender = Some(current_sender); } @@ -97,24 +108,33 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) { // Форматируем время (HH:MM) let time = format_timestamp(msg.date); - let read_mark = if msg.is_outgoing { - if msg.is_read { " ✓✓" } else { " ✓" } - } else { - "" - }; + if msg.is_outgoing { + // Исходящие: справа, формат "текст (HH:MM ✓✓)" + let read_mark = if msg.is_read { "✓✓" } else { "✓" }; + let time_mark = format!("({} {})", time, read_mark); + let msg_text = format!("{} {}", msg.content, time_mark); + let msg_len = msg_text.chars().count(); + let padding = content_width.saturating_sub(msg_len + 1); - // Сообщение с временем - lines.push(Line::from(vec![ - Span::styled(format!(" [{}]", time), Style::default().fg(Color::DarkGray)), - Span::raw(format!(" {}", msg.content)), - Span::styled(read_mark.to_string(), Style::default().fg(Color::DarkGray)), - ])); + lines.push(Line::from(vec![ + Span::raw(" ".repeat(padding)), + Span::styled(msg.content.clone(), Style::default().fg(Color::Green)), + Span::styled(format!(" {}", time_mark), Style::default().fg(Color::Gray)), + ])); + } else { + // Входящие: слева, формат "(HH:MM) текст" + let time_str = format!("({})", time); + lines.push(Line::from(vec![ + Span::styled(format!(" {}", time_str), Style::default().fg(Color::Gray)), + Span::raw(format!(" {}", msg.content)), + ])); + } } if lines.is_empty() { lines.push(Line::from(Span::styled( "Нет сообщений", - Style::default().fg(Color::DarkGray), + Style::default().fg(Color::Gray), ))); } @@ -142,7 +162,7 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) { format!("> {}", app.message_input) }; let input_style = if app.message_input.is_empty() { - Style::default().fg(Color::DarkGray) + Style::default().fg(Color::Gray) } else { Style::default().fg(Color::Yellow) }; @@ -153,7 +173,7 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) { } else { let empty = Paragraph::new("Выберите чат") .block(Block::default().borders(Borders::ALL)) - .style(Style::default().fg(Color::DarkGray)) + .style(Style::default().fg(Color::Gray)) .alignment(Alignment::Center); f.render_widget(empty, area); }