refactor: restructure MessageInfo with logical field grouping (P2.6)

Сгруппированы 16 плоских полей MessageInfo в 4 логические структуры
для улучшения организации кода и maintainability.

Новые структуры:
- MessageMetadata: id, sender_name, date, edit_date
- MessageContent: text, entities
- MessageState: is_outgoing, is_read, can_be_edited, can_be_deleted_*
- MessageInteractions: reply_to, forward_from, reactions

Изменения:
- Добавлены 4 новые структуры в tdlib/types.rs
- Обновлена MessageInfo для использования новых структур
- Добавлен конструктор MessageInfo::new() для удобного создания
- Добавлены getter методы (id(), text(), sender_name() и др.) для удобного доступа
- Обновлены все места создания MessageInfo (convert_message)
- Обновлены все места использования (~200+ обращений):
  * ui/messages.rs: рендеринг сообщений
  * app/mod.rs: логика приложения
  * input/main_input.rs: обработка ввода и копирование
  * tdlib/client.rs: обработка updates
  * Все тестовые файлы (14 файлов)

Преимущества:
- Логическая группировка данных
- Проще понимать структуру сообщения
- Легче добавлять новые поля в будущем
- Улучшенная читаемость кода

Статус: Priority 2 теперь 80% (4/5 задач)
-  Error enum
-  Config validation
-  Newtype для ID
-  MessageInfo реструктуризация
-  MessageBuilder pattern

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Kilin
2026-01-31 01:45:54 +03:00
parent 7081a886ad
commit 43960332d9
14 changed files with 274 additions and 144 deletions

View File

@@ -422,8 +422,8 @@ impl TdClient {
// Если это текущий открытый чат — обновляем is_read у сообщений
if Some(ChatId::new(update.chat_id)) == self.current_chat_id() {
for msg in self.current_chat_messages_mut().iter_mut() {
if msg.is_outgoing && msg.id <= last_read_msg_id {
msg.is_read = true;
if msg.is_outgoing() && msg.id() <= last_read_msg_id {
msg.state.is_read = true;
}
}
}
@@ -477,7 +477,7 @@ impl TdClient {
let existing_idx = self
.current_chat_messages()
.iter()
.position(|m| m.id == msg_info.id);
.position(|m| m.id() == msg_info.id());
match existing_idx {
Some(idx) => {
@@ -646,10 +646,10 @@ impl TdClient {
if let Some(msg) = self
.current_chat_messages_mut()
.iter_mut()
.find(|m| m.id == MessageId::new(update.message_id))
.find(|m| m.id() == MessageId::new(update.message_id))
{
// Извлекаем реакции из interaction_info
msg.reactions = update
msg.interactions.reactions = update
.interaction_info
.as_ref()
.and_then(|info| info.reactions.as_ref())
@@ -870,22 +870,22 @@ impl TdClient {
// Извлекаем реакции
let reactions = self.extract_reactions(message);
MessageInfo {
id: message_id,
MessageInfo::new(
message_id,
sender_name,
is_outgoing: message.is_outgoing,
message.is_outgoing,
content,
entities,
date: message.date,
edit_date: message.edit_date,
message.date,
message.edit_date,
is_read,
can_be_edited: message.can_be_edited,
can_be_deleted_only_for_self: message.can_be_deleted_only_for_self,
can_be_deleted_for_all_users: message.can_be_deleted_for_all_users,
message.can_be_edited,
message.can_be_deleted_only_for_self,
message.can_be_deleted_for_all_users,
reply_to,
forward_from,
reactions,
}
)
}
/// Извлекает информацию о reply из сообщения
@@ -902,8 +902,8 @@ impl TdClient {
let reply_msg_id = MessageId::new(reply.message_id);
self.current_chat_messages()
.iter()
.find(|m| m.id == reply_msg_id)
.map(|m| m.sender_name.clone())
.find(|m| m.id() == reply_msg_id)
.map(|m| m.sender_name().to_string())
.unwrap_or_else(|| "...".to_string())
};
@@ -917,8 +917,8 @@ impl TdClient {
// Пробуем найти в текущих сообщениях
self.current_chat_messages()
.iter()
.find(|m| m.id == reply_msg_id)
.map(|m| m.content.clone())
.find(|m| m.id() == reply_msg_id)
.map(|m| m.text().to_string())
.unwrap_or_default()
};
@@ -996,7 +996,7 @@ impl TdClient {
let msg_data: std::collections::HashMap<i64, (String, String)> = self
.current_chat_messages()
.iter()
.map(|m| (m.id, (m.sender_name.clone(), m.content.clone())))
.map(|m| (m.id(), (m.sender_name().to_string(), m.text().to_string())))
.collect();
// Обновляем reply_to для сообщений с неполными данными

View File

@@ -57,17 +57,28 @@ pub struct ReactionInfo {
pub is_chosen: bool,
}
/// Метаданные сообщения (ID, отправитель, время)
#[derive(Debug, Clone)]
pub struct MessageInfo {
pub struct MessageMetadata {
pub id: MessageId,
pub sender_name: String,
pub is_outgoing: bool,
pub content: String,
/// Сущности форматирования (bold, italic, code и т.д.)
pub entities: Vec<TextEntity>,
pub date: i32,
/// Дата редактирования (0 если не редактировалось)
pub edit_date: i32,
}
/// Контент сообщения (текст и форматирование)
#[derive(Debug, Clone)]
pub struct MessageContent {
pub text: String,
/// Сущности форматирования (bold, italic, code и т.д.)
pub entities: Vec<TextEntity>,
}
/// Состояние и права доступа к сообщению
#[derive(Debug, Clone)]
pub struct MessageState {
pub is_outgoing: bool,
pub is_read: bool,
/// Можно ли редактировать сообщение
pub can_be_edited: bool,
@@ -75,6 +86,11 @@ pub struct MessageInfo {
pub can_be_deleted_only_for_self: bool,
/// Можно ли удалить для всех
pub can_be_deleted_for_all_users: bool,
}
/// Взаимодействия с сообщением (reply, forward, reactions)
#[derive(Debug, Clone)]
pub struct MessageInteractions {
/// Информация о reply (если это ответ на сообщение)
pub reply_to: Option<ReplyInfo>,
/// Информация о forward (если сообщение переслано)
@@ -83,6 +99,120 @@ pub struct MessageInfo {
pub reactions: Vec<ReactionInfo>,
}
#[derive(Debug, Clone)]
pub struct MessageInfo {
pub metadata: MessageMetadata,
pub content: MessageContent,
pub state: MessageState,
pub interactions: MessageInteractions,
}
impl MessageInfo {
/// Создать новое сообщение
pub fn new(
id: MessageId,
sender_name: String,
is_outgoing: bool,
content: String,
entities: Vec<TextEntity>,
date: i32,
edit_date: i32,
is_read: bool,
can_be_edited: bool,
can_be_deleted_only_for_self: bool,
can_be_deleted_for_all_users: bool,
reply_to: Option<ReplyInfo>,
forward_from: Option<ForwardInfo>,
reactions: Vec<ReactionInfo>,
) -> Self {
Self {
metadata: MessageMetadata {
id,
sender_name,
date,
edit_date,
},
content: MessageContent {
text: content,
entities,
},
state: MessageState {
is_outgoing,
is_read,
can_be_edited,
can_be_deleted_only_for_self,
can_be_deleted_for_all_users,
},
interactions: MessageInteractions {
reply_to,
forward_from,
reactions,
},
}
}
// Удобные getter'ы для частых операций
pub fn id(&self) -> MessageId {
self.metadata.id
}
pub fn sender_name(&self) -> &str {
&self.metadata.sender_name
}
pub fn date(&self) -> i32 {
self.metadata.date
}
pub fn edit_date(&self) -> i32 {
self.metadata.edit_date
}
pub fn is_edited(&self) -> bool {
self.metadata.edit_date > 0
}
pub fn text(&self) -> &str {
&self.content.text
}
pub fn entities(&self) -> &[TextEntity] {
&self.content.entities
}
pub fn is_outgoing(&self) -> bool {
self.state.is_outgoing
}
pub fn is_read(&self) -> bool {
self.state.is_read
}
pub fn can_be_edited(&self) -> bool {
self.state.can_be_edited
}
pub fn can_be_deleted_only_for_self(&self) -> bool {
self.state.can_be_deleted_only_for_self
}
pub fn can_be_deleted_for_all_users(&self) -> bool {
self.state.can_be_deleted_for_all_users
}
pub fn reply_to(&self) -> Option<&ReplyInfo> {
self.interactions.reply_to.as_ref()
}
pub fn forward_from(&self) -> Option<&ForwardInfo> {
self.interactions.forward_from.as_ref()
}
pub fn reactions(&self) -> &[ReactionInfo] {
&self.interactions.reactions
}
}
#[derive(Debug, Clone)]
pub struct FolderInfo {
pub id: i32,