Group photos with shared media_album_id into single album bubbles with grid layout (up to 3x cols). Album navigation treats grouped photos as one unit (j/k skip entire album). Persist selected account to accounts.toml so it survives app restart. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
143 lines
4.9 KiB
Rust
143 lines
4.9 KiB
Rust
//! TDLib message conversion: JSON → MessageInfo, reply info fetching.
|
||
|
||
use crate::types::{ChatId, MessageId};
|
||
use tdlib_rs::functions;
|
||
use tdlib_rs::types::Message as TdMessage;
|
||
|
||
use crate::tdlib::types::{MessageBuilder, MessageInfo};
|
||
|
||
use super::MessageManager;
|
||
|
||
impl MessageManager {
|
||
/// Конвертировать TdMessage в MessageInfo
|
||
pub(crate) async fn convert_message(&self, msg: &TdMessage) -> Option<MessageInfo> {
|
||
use crate::tdlib::message_conversion::{
|
||
extract_content_text, extract_entities, extract_forward_info,
|
||
extract_media_info, extract_reactions, extract_reply_info, extract_sender_name,
|
||
};
|
||
|
||
// Извлекаем все части сообщения используя вспомогательные функции
|
||
let content_text = extract_content_text(msg);
|
||
let entities = extract_entities(msg);
|
||
let sender_name = extract_sender_name(msg, self.client_id).await;
|
||
let forward_from = extract_forward_info(msg);
|
||
let reply_to = extract_reply_info(msg);
|
||
let reactions = extract_reactions(msg);
|
||
let media = extract_media_info(msg);
|
||
|
||
let mut builder = MessageBuilder::new(MessageId::new(msg.id))
|
||
.sender_name(sender_name)
|
||
.text(content_text)
|
||
.entities(entities)
|
||
.date(msg.date)
|
||
.edit_date(msg.edit_date)
|
||
.media_album_id(msg.media_album_id);
|
||
|
||
if msg.is_outgoing {
|
||
builder = builder.outgoing();
|
||
} else {
|
||
builder = builder.incoming();
|
||
}
|
||
|
||
if !msg.contains_unread_mention {
|
||
builder = builder.read();
|
||
} else {
|
||
builder = builder.unread();
|
||
}
|
||
|
||
if msg.can_be_edited {
|
||
builder = builder.editable();
|
||
}
|
||
|
||
if msg.can_be_deleted_only_for_self {
|
||
builder = builder.deletable_for_self();
|
||
}
|
||
|
||
if msg.can_be_deleted_for_all_users {
|
||
builder = builder.deletable_for_all();
|
||
}
|
||
|
||
if let Some(reply) = reply_to {
|
||
builder = builder.reply_to(reply);
|
||
}
|
||
|
||
if let Some(forward) = forward_from {
|
||
builder = builder.forward_from(forward);
|
||
}
|
||
|
||
builder = builder.reactions(reactions);
|
||
|
||
if let Some(media) = media {
|
||
builder = builder.media(media);
|
||
}
|
||
|
||
Some(builder.build())
|
||
}
|
||
|
||
/// Загружает недостающую информацию об исходных сообщениях для ответов.
|
||
///
|
||
/// Ищет все reply-сообщения с `sender_name == "Unknown"` и загружает
|
||
/// полную информацию (имя отправителя, текст) из TDLib.
|
||
///
|
||
/// # Note
|
||
///
|
||
/// Вызывайте после загрузки истории чата для заполнения информации о цитируемых сообщениях.
|
||
pub async fn fetch_missing_reply_info(&mut self) {
|
||
// Early return if no chat selected
|
||
let Some(chat_id) = self.current_chat_id else {
|
||
return;
|
||
};
|
||
|
||
// Collect message IDs with missing reply info using filter_map
|
||
let to_fetch: Vec<MessageId> = self
|
||
.current_chat_messages
|
||
.iter()
|
||
.filter_map(|msg| {
|
||
msg.interactions
|
||
.reply_to
|
||
.as_ref()
|
||
.filter(|reply| reply.sender_name == "Unknown")
|
||
.map(|reply| reply.message_id)
|
||
})
|
||
.collect();
|
||
|
||
// Fetch and update each missing message
|
||
for message_id in to_fetch {
|
||
self.fetch_and_update_reply(chat_id, message_id).await;
|
||
}
|
||
}
|
||
|
||
/// Загружает одно сообщение и обновляет reply информацию.
|
||
async fn fetch_and_update_reply(&mut self, chat_id: ChatId, message_id: MessageId) {
|
||
// Try to fetch the original message
|
||
let Ok(original_msg_enum) =
|
||
functions::get_message(chat_id.as_i64(), message_id.as_i64(), self.client_id).await
|
||
else {
|
||
return;
|
||
};
|
||
|
||
let tdlib_rs::enums::Message::Message(original_msg) = original_msg_enum;
|
||
let Some(orig_info) = self.convert_message(&original_msg).await else {
|
||
return;
|
||
};
|
||
|
||
// Extract text preview (first 50 chars)
|
||
let text_preview: String = orig_info
|
||
.content
|
||
.text
|
||
.chars()
|
||
.take(50)
|
||
.collect();
|
||
|
||
// Update reply info in all messages that reference this message
|
||
self.current_chat_messages
|
||
.iter_mut()
|
||
.filter_map(|msg| msg.interactions.reply_to.as_mut())
|
||
.filter(|reply| reply.message_id == message_id)
|
||
.for_each(|reply| {
|
||
reply.sender_name = orig_info.metadata.sender_name.clone();
|
||
reply.text = text_preview.clone();
|
||
});
|
||
}
|
||
}
|