//! Модуль для группировки сообщений по дате и отправителю //! //! Предоставляет функции для логической группировки сообщений //! перед отображением, отделяя логику группировки от рендеринга. use crate::tdlib::MessageInfo; use crate::utils::get_day; /// Элемент группированного списка сообщений #[derive(Debug, Clone)] pub enum MessageGroup { /// Разделитель даты (день в формате timestamp) DateSeparator(i32), /// Заголовок отправителя (is_outgoing, sender_name) SenderHeader { is_outgoing: bool, sender_name: String }, /// Сообщение Message(MessageInfo), } /// Группирует сообщения по дате и отправителю /// /// # Аргументы /// /// * `messages` - Список сообщений для группировки /// /// # Возвращает /// /// Вектор `MessageGroup` с разделителями дат, заголовками отправителей и сообщениями /// /// # Примеры /// /// ```no_run /// use tele_tui::message_grouping::{group_messages, MessageGroup}; /// /// # use tele_tui::tdlib::types::MessageBuilder; /// # use tele_tui::types::MessageId; /// # let msg = MessageBuilder::new(MessageId::new(1)).sender_name("Alice").text("Hello").build(); /// let messages = vec![msg]; /// let grouped = group_messages(&messages); /// /// for group in grouped { /// match group { /// MessageGroup::DateSeparator(_day) => { /// // Рендерим разделитель даты /// } /// MessageGroup::SenderHeader { is_outgoing, sender_name } => { /// // Рендерим заголовок отправителя /// println!("{}: {}", if is_outgoing { "Outgoing" } else { "Incoming" }, sender_name); /// } /// MessageGroup::Message(msg) => { /// // Рендерим сообщение /// println!("{}", msg.text()); /// } /// } /// } /// ``` pub fn group_messages(messages: &[MessageInfo]) -> Vec { let mut result = Vec::new(); let mut last_day: Option = None; let mut last_sender: Option<(bool, String)> = None; // (is_outgoing, sender_name) for msg in messages { // Проверяем, нужно ли добавить разделитель даты let msg_day = get_day(msg.date()); if last_day != Some(msg_day) { // Добавляем разделитель даты result.push(MessageGroup::DateSeparator(msg.date())); last_day = Some(msg_day); last_sender = None; // Сбрасываем отправителя при смене дня } let sender_name = if msg.is_outgoing() { "Вы".to_string() } else { msg.sender_name().to_string() }; let current_sender = (msg.is_outgoing(), sender_name.clone()); // Проверяем, нужно ли показать заголовок отправителя let show_sender_header = last_sender.as_ref() != Some(¤t_sender); if show_sender_header { result.push(MessageGroup::SenderHeader { is_outgoing: msg.is_outgoing(), sender_name, }); last_sender = Some(current_sender); } // Добавляем само сообщение result.push(MessageGroup::Message(msg.clone())); } result } #[cfg(test)] mod tests { use super::*; use crate::tdlib::types::MessageBuilder; use crate::types::MessageId; #[test] fn test_group_messages_by_date() { // Создаём сообщения с разными датами let msg1 = MessageBuilder::new(MessageId::new(1)) .sender_name("Alice") .text("Message 1") .date(1609459200) // 2021-01-01 00:00:00 UTC .incoming() .build(); let msg2 = MessageBuilder::new(MessageId::new(2)) .sender_name("Alice") .text("Message 2") .date(1609545600) // 2021-01-02 00:00:00 UTC .incoming() .build(); let messages = vec![msg1, msg2]; let grouped = group_messages(&messages); // Должно быть: DateSep, SenderHeader, Message, DateSep, SenderHeader, Message assert_eq!(grouped.len(), 6); assert!(matches!(grouped[0], MessageGroup::DateSeparator(_))); assert!(matches!(grouped[1], MessageGroup::SenderHeader { .. })); assert!(matches!(grouped[2], MessageGroup::Message(_))); assert!(matches!(grouped[3], MessageGroup::DateSeparator(_))); assert!(matches!(grouped[4], MessageGroup::SenderHeader { .. })); assert!(matches!(grouped[5], MessageGroup::Message(_))); } #[test] fn test_group_messages_by_sender() { // Создаём сообщения от разных отправителей let msg1 = MessageBuilder::new(MessageId::new(1)) .sender_name("Alice") .text("Message 1") .date(1609459200) .incoming() .build(); let msg2 = MessageBuilder::new(MessageId::new(2)) .sender_name("Alice") .text("Message 2") .date(1609459300) // +100 секунд, тот же день .incoming() .build(); let msg3 = MessageBuilder::new(MessageId::new(3)) .sender_name("Bob") .text("Message 3") .date(1609459400) .incoming() .build(); let messages = vec![msg1, msg2, msg3]; let grouped = group_messages(&messages); // Должно быть: DateSep, SenderHeader(Alice), Message, Message, SenderHeader(Bob), Message assert_eq!(grouped.len(), 6); assert!(matches!(grouped[0], MessageGroup::DateSeparator(_))); if let MessageGroup::SenderHeader { sender_name, .. } = &grouped[1] { assert_eq!(sender_name, "Alice"); } else { panic!("Expected SenderHeader"); } assert!(matches!(grouped[2], MessageGroup::Message(_))); assert!(matches!(grouped[3], MessageGroup::Message(_))); if let MessageGroup::SenderHeader { sender_name, .. } = &grouped[4] { assert_eq!(sender_name, "Bob"); } else { panic!("Expected SenderHeader"); } assert!(matches!(grouped[5], MessageGroup::Message(_))); } #[test] fn test_group_outgoing_vs_incoming() { // Проверяем группировку исходящих и входящих сообщений let msg1 = MessageBuilder::new(MessageId::new(1)) .sender_name("Alice") .text("Hello") .date(1609459200) .incoming() .build(); let msg2 = MessageBuilder::new(MessageId::new(2)) .sender_name("Me") .text("Hi") .date(1609459300) .outgoing() .build(); let messages = vec![msg1, msg2]; let grouped = group_messages(&messages); // Должно быть: DateSep, SenderHeader(Alice), Message, SenderHeader(Me), Message assert_eq!(grouped.len(), 5); if let MessageGroup::SenderHeader { is_outgoing, sender_name } = &grouped[1] { assert_eq!(*is_outgoing, false); assert_eq!(sender_name, "Alice"); } else { panic!("Expected SenderHeader"); } if let MessageGroup::SenderHeader { is_outgoing, sender_name } = &grouped[3] { assert_eq!(*is_outgoing, true); assert_eq!(sender_name, "Вы"); } else { panic!("Expected SenderHeader"); } } #[test] fn test_empty_messages() { let messages: Vec = vec![]; let grouped = group_messages(&messages); assert_eq!(grouped.len(), 0); } #[test] fn test_single_message() { let msg = MessageBuilder::new(MessageId::new(1)) .sender_name("Alice") .text("Single message") .date(1609459200) .incoming() .build(); let messages = vec![msg]; let grouped = group_messages(&messages); // Должно быть: DateSep, SenderHeader, Message assert_eq!(grouped.len(), 3); assert!(matches!(grouped[0], MessageGroup::DateSeparator(_))); assert!(matches!(grouped[1], MessageGroup::SenderHeader { .. })); assert!(matches!(grouped[2], MessageGroup::Message(_))); } }