This commit is contained in:
Mikhail Kilin
2026-01-21 02:27:08 +03:00
parent 9912ac11bd
commit 32ab1df1fa
8 changed files with 291 additions and 79 deletions

View File

@@ -27,6 +27,8 @@ pub struct ChatInfo {
pub unread_count: i32,
pub is_pinned: bool,
pub order: i64,
/// ID последнего прочитанного исходящего сообщения (для галочек)
pub last_read_outbox_message_id: i64,
}
#[derive(Debug, Clone)]
@@ -46,10 +48,14 @@ pub struct TdClient {
client_id: i32,
pub chats: Vec<ChatInfo>,
pub current_chat_messages: Vec<MessageInfo>,
/// ID текущего открытого чата (для получения новых сообщений)
pub current_chat_id: Option<i64>,
/// Кэш usernames: user_id -> username
user_usernames: HashMap<i64, String>,
/// Связь chat_id -> user_id для приватных чатов
chat_user_ids: HashMap<i64, i64>,
/// Очередь сообщений для отметки как прочитанных: (chat_id, message_ids)
pub pending_view_messages: Vec<(i64, Vec<i64>)>,
}
#[allow(dead_code)]
@@ -70,8 +76,10 @@ impl TdClient {
client_id,
chats: Vec::new(),
current_chat_messages: Vec::new(),
current_chat_id: None,
user_usernames: HashMap::new(),
chat_user_ids: HashMap::new(),
pending_view_messages: Vec::new(),
}
}
@@ -150,6 +158,20 @@ impl TdClient {
chat.unread_count = update.unread_count;
}
}
Update::ChatReadOutbox(update) => {
// Обновляем last_read_outbox_message_id когда собеседник прочитал сообщения
if let Some(chat) = self.chats.iter_mut().find(|c| c.id == update.chat_id) {
chat.last_read_outbox_message_id = update.last_read_outbox_message_id;
}
// Если это текущий открытый чат — обновляем is_read у сообщений
if Some(update.chat_id) == self.current_chat_id {
for msg in &mut self.current_chat_messages {
if msg.is_outgoing && msg.id <= update.last_read_outbox_message_id {
msg.is_read = true;
}
}
}
}
Update::ChatPosition(update) => {
// Обновляем позицию чата или удаляем его из списка
match &update.position.list {
@@ -171,8 +193,22 @@ impl TdClient {
}
}
}
Update::NewMessage(_new_msg) => {
// Новые сообщения обрабатываются при обновлении UI
Update::NewMessage(new_msg) => {
// Добавляем новое сообщение если это текущий открытый чат
let chat_id = new_msg.message.chat_id;
if Some(chat_id) == self.current_chat_id {
let msg_info = self.convert_message(&new_msg.message, chat_id);
let msg_id = msg_info.id;
let is_incoming = !msg_info.is_outgoing;
// Проверяем, что сообщение ещё не добавлено (по id)
if !self.current_chat_messages.iter().any(|m| m.id == msg_info.id) {
self.current_chat_messages.push(msg_info);
// Если это входящее сообщение — добавляем в очередь для отметки как прочитанное
if is_incoming {
self.pending_view_messages.push((chat_id, vec![msg_id]));
}
}
}
}
Update::User(update) => {
// Сохраняем username пользователя
@@ -243,6 +279,7 @@ impl TdClient {
unread_count: td_chat.unread_count,
is_pinned,
order,
last_read_outbox_message_id: td_chat.last_read_outbox_message_id,
};
if let Some(existing) = self.chats.iter_mut().find(|c| c.id == td_chat.id) {
@@ -250,6 +287,7 @@ impl TdClient {
existing.last_message = chat_info.last_message;
existing.last_message_date = chat_info.last_message_date;
existing.unread_count = chat_info.unread_count;
existing.last_read_outbox_message_id = chat_info.last_read_outbox_message_id;
// Обновляем username если он появился
if chat_info.username.is_some() {
existing.username = chat_info.username;
@@ -267,19 +305,31 @@ impl TdClient {
self.chats.sort_by(|a, b| b.order.cmp(&a.order));
}
fn convert_message(&self, message: &TdMessage) -> MessageInfo {
fn convert_message(&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),
};
// Определяем, прочитано ли исходящее сообщение
let is_read = if message.is_outgoing {
// Сообщение прочитано, если его ID <= last_read_outbox_message_id чата
self.chats
.iter()
.find(|c| c.id == chat_id)
.map(|c| message.id <= c.last_read_outbox_message_id)
.unwrap_or(false)
} else {
true // Входящие сообщения не показывают галочки
};
MessageInfo {
id: message.id,
sender_name,
is_outgoing: message.is_outgoing,
content: extract_message_text_static(message),
date: message.date,
is_read: !message.is_outgoing || message.id <= 0,
is_read,
}
}
@@ -339,34 +389,80 @@ impl TdClient {
chat_id: i64,
limit: i32,
) -> Result<Vec<MessageInfo>, String> {
// Устанавливаем текущий чат для получения новых сообщений
self.current_chat_id = Some(chat_id);
let _ = functions::open_chat(chat_id, self.client_id).await;
// Загружаем историю с сервера (only_local=false)
let result = functions::get_chat_history(
chat_id,
0, // from_message_id (0 = с последнего сообщения)
0, // offset
limit,
false, // only_local - загружаем с сервера!
self.client_id,
)
.await;
// Пробуем загрузить несколько раз, так как сообщения могут подгружаться с сервера
let mut all_messages: Vec<MessageInfo> = Vec::new();
let mut from_message_id: i64 = 0;
let mut attempts = 0;
const MAX_ATTEMPTS: i32 = 3;
match result {
Ok(tdlib_rs::enums::Messages::Messages(messages)) => {
let mut result_messages: Vec<MessageInfo> = messages
.messages
.into_iter()
.filter_map(|m| m.map(|msg| self.convert_message(&msg)))
.collect();
while attempts < MAX_ATTEMPTS {
let result = functions::get_chat_history(
chat_id,
from_message_id,
0, // offset
limit,
false, // only_local - загружаем с сервера!
self.client_id,
)
.await;
// Сообщения приходят от новых к старым, переворачиваем
result_messages.reverse();
self.current_chat_messages = result_messages.clone();
Ok(result_messages)
match result {
Ok(tdlib_rs::enums::Messages::Messages(messages)) => {
let batch: Vec<MessageInfo> = messages
.messages
.into_iter()
.filter_map(|m| m.map(|msg| self.convert_message(&msg, chat_id)))
.collect();
if batch.is_empty() {
break;
}
// Запоминаем ID самого старого сообщения для следующей загрузки
if let Some(oldest) = batch.last() {
from_message_id = oldest.id;
}
// Добавляем сообщения (они приходят от новых к старым)
all_messages.extend(batch);
attempts += 1;
// Если получили достаточно сообщений, выходим
if all_messages.len() >= limit as usize {
break;
}
}
Err(e) => {
if all_messages.is_empty() {
return Err(format!("Ошибка загрузки сообщений: {:?}", e));
}
break;
}
}
Err(e) => Err(format!("Ошибка загрузки сообщений: {:?}", e)),
}
// Сообщения приходят от новых к старым, переворачиваем
all_messages.reverse();
self.current_chat_messages = all_messages.clone();
// Отмечаем сообщения как прочитанные
if !all_messages.is_empty() {
let message_ids: Vec<i64> = all_messages.iter().map(|m| m.id).collect();
let _ = functions::view_messages(
chat_id,
message_ids,
None, // source
true, // force_read
self.client_id,
)
.await;
}
Ok(all_messages)
}
/// Загрузка старых сообщений (для скролла вверх)
@@ -391,7 +487,7 @@ impl TdClient {
let mut result_messages: Vec<MessageInfo> = messages
.messages
.into_iter()
.filter_map(|m| m.map(|msg| self.convert_message(&msg)))
.filter_map(|m| m.map(|msg| self.convert_message(&msg, chat_id)))
.collect();
// Сообщения приходят от новых к старым, переворачиваем
@@ -474,6 +570,21 @@ impl TdClient {
Err(e) => Err(format!("Ошибка отправки сообщения: {:?}", e)),
}
}
/// Обработка очереди сообщений для отметки как прочитанных
pub async fn process_pending_view_messages(&mut self) {
let pending = std::mem::take(&mut self.pending_view_messages);
for (chat_id, message_ids) in pending {
let _ = functions::view_messages(
chat_id,
message_ids,
None, // source
true, // force_read
self.client_id,
)
.await;
}
}
}
/// Статическая функция для извлечения текста сообщения (без &self)