Compare commits
4 Commits
0a4ab1b40d
...
9cc63952f4
| Author | SHA1 | Date | |
|---|---|---|---|
| 9cc63952f4 | |||
|
|
2b04b785c0 | ||
|
|
f1a26b906c | ||
|
|
c27d027ebf |
@@ -134,11 +134,6 @@ impl ChatState {
|
||||
matches!(self, ChatState::PinnedMessages { .. })
|
||||
}
|
||||
|
||||
/// Проверка: находимся в обычном режиме
|
||||
pub fn is_normal(&self) -> bool {
|
||||
matches!(self, ChatState::Normal)
|
||||
}
|
||||
|
||||
/// Возвращает ID выбранного сообщения (если есть)
|
||||
pub fn selected_message_id(&self) -> Option<MessageId> {
|
||||
match self {
|
||||
|
||||
@@ -16,25 +16,6 @@ pub const MAX_CHATS: usize = 200;
|
||||
/// Максимальное количество user_ids для хранения в чате
|
||||
pub const MAX_CHAT_USER_IDS: usize = 500;
|
||||
|
||||
// ============================================================================
|
||||
// UI Constants
|
||||
// ============================================================================
|
||||
|
||||
/// Количество колонок в emoji picker сетке
|
||||
pub const EMOJI_PICKER_COLUMNS: usize = 8;
|
||||
|
||||
/// Количество рядов в emoji picker сетке
|
||||
pub const EMOJI_PICKER_ROWS: usize = 6;
|
||||
|
||||
/// Максимальная высота поля ввода (в строках)
|
||||
pub const MAX_INPUT_HEIGHT: usize = 10;
|
||||
|
||||
/// Минимальная ширина терминала для корректного отображения
|
||||
pub const MIN_TERMINAL_WIDTH: u16 = 80;
|
||||
|
||||
/// Минимальная высота терминала для корректного отображения
|
||||
pub const MIN_TERMINAL_HEIGHT: u16 = 20;
|
||||
|
||||
// ============================================================================
|
||||
// Performance
|
||||
// ============================================================================
|
||||
@@ -52,18 +33,5 @@ pub const LAZY_LOAD_USERS_PER_TICK: usize = 5;
|
||||
// TDLib
|
||||
// ============================================================================
|
||||
|
||||
/// Лимит количества чатов для загрузки через TDLib за раз
|
||||
pub const TDLIB_CHAT_LIMIT: i32 = 50;
|
||||
|
||||
/// Лимит количества сообщений для загрузки через TDLib за раз
|
||||
pub const TDLIB_MESSAGE_LIMIT: i32 = 50;
|
||||
|
||||
// ============================================================================
|
||||
// Formatting
|
||||
// ============================================================================
|
||||
|
||||
/// Максимальная длина имени пользователя для отображения
|
||||
pub const MAX_USERNAME_DISPLAY_LENGTH: usize = 20;
|
||||
|
||||
/// Отступ для wrap текста сообщений
|
||||
pub const MESSAGE_TEXT_INDENT: usize = 2;
|
||||
|
||||
101
src/error.rs
101
src/error.rs
@@ -1,101 +0,0 @@
|
||||
/// Error types for tele-tui application
|
||||
///
|
||||
/// Provides type-safe error handling across the application,
|
||||
/// replacing generic String errors with structured variants.
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TeletuiError {
|
||||
/// TDLib-related errors
|
||||
#[error("TDLib error: {0}")]
|
||||
TdLib(String),
|
||||
|
||||
/// Configuration errors
|
||||
#[error("Configuration error: {0}")]
|
||||
Config(String),
|
||||
|
||||
/// Network connectivity errors
|
||||
#[error("Network error: {0}")]
|
||||
Network(String),
|
||||
|
||||
/// Authentication errors
|
||||
#[error("Authentication error: {0}")]
|
||||
Auth(String),
|
||||
|
||||
/// Invalid timezone format
|
||||
#[error("Invalid timezone format: {0}")]
|
||||
InvalidTimezone(String),
|
||||
|
||||
/// Invalid color value
|
||||
#[error("Invalid color: {0}")]
|
||||
InvalidColor(String),
|
||||
|
||||
/// Message operation errors
|
||||
#[error("Message error: {0}")]
|
||||
Message(String),
|
||||
|
||||
/// Chat operation errors
|
||||
#[error("Chat error: {0}")]
|
||||
Chat(String),
|
||||
|
||||
/// User operation errors
|
||||
#[error("User error: {0}")]
|
||||
User(String),
|
||||
|
||||
/// File system errors
|
||||
#[error("IO error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// TOML parsing errors
|
||||
#[error("TOML error: {0}")]
|
||||
Toml(#[from] toml::de::Error),
|
||||
|
||||
/// JSON parsing errors
|
||||
#[error("JSON error: {0}")]
|
||||
Json(#[from] serde_json::Error),
|
||||
|
||||
/// Clipboard errors
|
||||
#[error("Clipboard error: {0}")]
|
||||
Clipboard(String),
|
||||
|
||||
/// Generic error for cases not covered by specific variants
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
/// Result type alias using TeletuiError
|
||||
pub type Result<T> = std::result::Result<T, TeletuiError>;
|
||||
|
||||
/// Helper trait for converting String errors to TeletuiError
|
||||
pub trait IntoTeletuiError {
|
||||
fn into_teletui_error(self, variant: ErrorVariant) -> TeletuiError;
|
||||
}
|
||||
|
||||
impl IntoTeletuiError for String {
|
||||
fn into_teletui_error(self, variant: ErrorVariant) -> TeletuiError {
|
||||
match variant {
|
||||
ErrorVariant::TdLib => TeletuiError::TdLib(self),
|
||||
ErrorVariant::Config => TeletuiError::Config(self),
|
||||
ErrorVariant::Network => TeletuiError::Network(self),
|
||||
ErrorVariant::Auth => TeletuiError::Auth(self),
|
||||
ErrorVariant::Message => TeletuiError::Message(self),
|
||||
ErrorVariant::Chat => TeletuiError::Chat(self),
|
||||
ErrorVariant::User => TeletuiError::User(self),
|
||||
ErrorVariant::Clipboard => TeletuiError::Clipboard(self),
|
||||
ErrorVariant::Other => TeletuiError::Other(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error variant selector for conversion
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ErrorVariant {
|
||||
TdLib,
|
||||
Config,
|
||||
Network,
|
||||
Auth,
|
||||
Message,
|
||||
Chat,
|
||||
User,
|
||||
Clipboard,
|
||||
Other,
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
pub mod app;
|
||||
pub mod config;
|
||||
pub mod constants;
|
||||
pub mod error;
|
||||
pub mod formatting;
|
||||
pub mod input;
|
||||
pub mod message_grouping;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
mod app;
|
||||
mod config;
|
||||
mod constants;
|
||||
mod error;
|
||||
mod formatting;
|
||||
mod input;
|
||||
mod tdlib;
|
||||
|
||||
@@ -6,7 +6,6 @@ use tdlib_rs::functions;
|
||||
/// Отслеживает текущий этап аутентификации пользователя,
|
||||
/// от инициализации TDLib до полной авторизации.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum AuthState {
|
||||
/// Ожидание параметров TDLib (начальное состояние).
|
||||
WaitTdlibParameters,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use crate::constants::TDLIB_CHAT_LIMIT;
|
||||
use crate::types::{ChatId, UserId};
|
||||
use std::time::Instant;
|
||||
use tdlib_rs::enums::{ChatAction, ChatList, ChatType};
|
||||
use tdlib_rs::functions;
|
||||
|
||||
use super::types::{ChatInfo, FolderInfo, MessageInfo, ProfileInfo};
|
||||
use super::types::{ChatInfo, FolderInfo, ProfileInfo};
|
||||
|
||||
/// Менеджер чатов TDLib.
|
||||
///
|
||||
@@ -183,10 +182,7 @@ impl ChatManager {
|
||||
Err(e) => return Err(format!("Ошибка получения чата: {:?}", e)),
|
||||
};
|
||||
|
||||
let chat = match chat_enum {
|
||||
tdlib_rs::enums::Chat::Chat(c) => c,
|
||||
_ => return Err("Неожиданный тип чата".to_string()),
|
||||
};
|
||||
let tdlib_rs::enums::Chat::Chat(chat) = chat_enum;
|
||||
|
||||
let chat_type_str = match &chat.r#type {
|
||||
ChatType::Private(_) => "Личный чат",
|
||||
|
||||
@@ -59,22 +59,31 @@ pub struct TdClient {
|
||||
pub network_state: NetworkState,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl TdClient {
|
||||
/// Creates a new TDLib client instance.
|
||||
///
|
||||
/// Reads API credentials from environment variables `API_ID` and `API_HASH`.
|
||||
/// Reads API credentials from:
|
||||
/// 1. ~/.config/tele-tui/credentials file
|
||||
/// 2. Environment variables `API_ID` and `API_HASH` (fallback)
|
||||
///
|
||||
/// Initializes all managers and sets initial network state to Connecting.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A new `TdClient` instance ready for authentication.
|
||||
pub fn new() -> Self {
|
||||
// Пробуем загрузить credentials из Config (файл или env)
|
||||
let (api_id, api_hash) = crate::config::Config::load_credentials()
|
||||
.unwrap_or_else(|_| {
|
||||
// Fallback на прямое чтение из env (старое поведение)
|
||||
let api_id = env::var("API_ID")
|
||||
.unwrap_or_else(|_| "0".to_string())
|
||||
.parse()
|
||||
.unwrap_or(0);
|
||||
let api_hash = env::var("API_HASH").unwrap_or_default();
|
||||
(api_id, api_hash)
|
||||
});
|
||||
|
||||
let client_id = tdlib_rs::create_client();
|
||||
|
||||
Self {
|
||||
@@ -92,15 +101,6 @@ impl TdClient {
|
||||
|
||||
// Делегирование к auth
|
||||
|
||||
/// Checks if the user is authenticated.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` if authentication is complete, `false` otherwise.
|
||||
pub fn is_authenticated(&self) -> bool {
|
||||
self.auth.is_authenticated()
|
||||
}
|
||||
|
||||
/// Sends phone number for authentication.
|
||||
///
|
||||
/// This is the first step of the authentication flow.
|
||||
@@ -214,10 +214,6 @@ impl TdClient {
|
||||
self.chat_manager.send_chat_action(chat_id, action).await
|
||||
}
|
||||
|
||||
pub fn get_typing_text(&self) -> Option<String> {
|
||||
self.chat_manager.get_typing_text()
|
||||
}
|
||||
|
||||
pub fn clear_stale_typing_status(&mut self) -> bool {
|
||||
self.chat_manager.clear_stale_typing_status()
|
||||
}
|
||||
@@ -319,10 +315,6 @@ impl TdClient {
|
||||
}
|
||||
|
||||
// Делегирование к user_cache
|
||||
pub async fn get_user_name(&self, user_id: UserId) -> String {
|
||||
self.user_cache.get_user_name(user_id).await
|
||||
}
|
||||
|
||||
pub fn get_user_status_by_chat_id(&self, chat_id: ChatId) -> Option<&UserOnlineStatus> {
|
||||
self.user_cache.get_status_by_chat_id(chat_id)
|
||||
}
|
||||
@@ -361,7 +353,6 @@ impl TdClient {
|
||||
pub async fn get_me(&self) -> Result<i64, String> {
|
||||
match functions::get_me(self.client_id).await {
|
||||
Ok(tdlib_rs::enums::User::User(user)) => Ok(user.id),
|
||||
Ok(_) => Err("Неожиданный тип пользователя".to_string()),
|
||||
Err(e) => Err(format!("Ошибка получения текущего пользователя: {:?}", e)),
|
||||
}
|
||||
}
|
||||
@@ -452,33 +443,6 @@ impl TdClient {
|
||||
&mut self.user_cache
|
||||
}
|
||||
|
||||
/// Инициализация TDLib
|
||||
pub async fn init(&mut self) -> Result<(), String> {
|
||||
let result = functions::set_tdlib_parameters(
|
||||
false, // use_test_dc
|
||||
"tdlib_data".to_string(), // database_directory
|
||||
"".to_string(), // files_directory
|
||||
"".to_string(), // database_encryption_key
|
||||
true, // use_file_database
|
||||
true, // use_chat_info_database
|
||||
true, // use_message_database
|
||||
false, // use_secret_chats
|
||||
self.api_id, // api_id
|
||||
self.api_hash.clone(), // api_hash
|
||||
"en".to_string(), // system_language_code
|
||||
"Desktop".to_string(), // device_model
|
||||
"".to_string(), // system_version
|
||||
env!("CARGO_PKG_VERSION").to_string(), // application_version
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(format!("Failed to set TDLib parameters: {:?}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Обрабатываем одно обновление от TDLib
|
||||
pub fn handle_update(&mut self, update: Update) {
|
||||
match update {
|
||||
@@ -846,10 +810,7 @@ impl TdClient {
|
||||
|
||||
fn add_or_update_chat(&mut self, td_chat_enum: &TdChat) {
|
||||
// Pattern match to get inner Chat struct
|
||||
let td_chat = match td_chat_enum {
|
||||
TdChat::Chat(chat) => chat,
|
||||
_ => return,
|
||||
};
|
||||
let TdChat::Chat(td_chat) = td_chat_enum;
|
||||
|
||||
// Пропускаем удалённые аккаунты
|
||||
if td_chat.title == "Deleted Account" || td_chat.title.is_empty() {
|
||||
@@ -1102,7 +1063,7 @@ impl TdClient {
|
||||
fn extract_forward_info(&self, message: &TdMessage) -> Option<ForwardInfo> {
|
||||
message.forward_info.as_ref().map(|info| {
|
||||
let sender_name = self.get_origin_sender_name(&info.origin);
|
||||
ForwardInfo { sender_name, date: info.date }
|
||||
ForwardInfo { sender_name }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::constants::{MAX_MESSAGES_IN_CHAT, TDLIB_MESSAGE_LIMIT};
|
||||
use crate::types::{ChatId, MessageId};
|
||||
use tdlib_rs::enums::{ChatAction, InputMessageContent, InputMessageReplyTo, MessageContent, MessageSender, SearchMessagesFilter, TextParseMode};
|
||||
use tdlib_rs::enums::{InputMessageContent, InputMessageReplyTo, MessageContent, MessageSender, SearchMessagesFilter, TextParseMode};
|
||||
use tdlib_rs::functions;
|
||||
use tdlib_rs::types::{Chat as TdChat, FormattedText, InputMessageReplyToMessage, InputMessageText, Message as TdMessage, TextEntity, TextParseModeMarkdown};
|
||||
use tdlib_rs::types::{FormattedText, InputMessageReplyToMessage, InputMessageText, Message as TdMessage, TextParseModeMarkdown};
|
||||
|
||||
use super::types::{ForwardInfo, MessageBuilder, MessageInfo, ReactionInfo, ReplyInfo};
|
||||
|
||||
@@ -178,7 +178,6 @@ impl MessageManager {
|
||||
sleep(Duration::from_millis(200)).await;
|
||||
}
|
||||
}
|
||||
Ok(_) => return Err("Неожиданный тип сообщений".to_string()),
|
||||
Err(e) => return Err(format!("Ошибка загрузки истории: {:?}", e)),
|
||||
}
|
||||
}
|
||||
@@ -237,7 +236,6 @@ impl MessageManager {
|
||||
}
|
||||
Ok(messages)
|
||||
}
|
||||
Ok(_) => Err("Неожиданный тип сообщений".to_string()),
|
||||
Err(e) => Err(format!("Ошибка загрузки старых сообщений: {:?}", e)),
|
||||
}
|
||||
}
|
||||
@@ -286,7 +284,6 @@ impl MessageManager {
|
||||
}
|
||||
Ok(pinned_messages)
|
||||
}
|
||||
Ok(_) => Err("Неожиданный тип результата поиска".to_string()),
|
||||
Err(e) => Err(format!("Ошибка загрузки закреплённых: {:?}", e)),
|
||||
}
|
||||
}
|
||||
@@ -302,11 +299,10 @@ impl MessageManager {
|
||||
/// TODO: В tdlib-rs 1.8.29 поле `pinned_message_id` было удалено из `Chat`.
|
||||
/// Нужно использовать `getChatPinnedMessage` или альтернативный способ.
|
||||
/// Временно отключено, возвращает `None`.
|
||||
pub async fn load_current_pinned_message(&mut self, chat_id: ChatId) {
|
||||
pub async fn load_current_pinned_message(&mut self, _chat_id: ChatId) {
|
||||
// TODO: В tdlib-rs 1.8.29 поле pinned_message_id было удалено из Chat.
|
||||
// Нужно использовать getChatPinnedMessage или альтернативный способ.
|
||||
// Временно отключено.
|
||||
let _ = chat_id;
|
||||
self.current_pinned_message = None;
|
||||
|
||||
// match functions::get_chat(chat_id, self.client_id).await {
|
||||
@@ -363,7 +359,6 @@ impl MessageManager {
|
||||
}
|
||||
Ok(search_results)
|
||||
}
|
||||
Ok(_) => Err("Неожиданный тип результата поиска".to_string()),
|
||||
Err(e) => Err(format!("Ошибка поиска: {:?}", e)),
|
||||
}
|
||||
}
|
||||
@@ -468,7 +463,6 @@ impl MessageManager {
|
||||
|
||||
Ok(msg_info)
|
||||
}
|
||||
Ok(_) => Err("Неожиданный тип сообщения".to_string()),
|
||||
Err(e) => Err(format!("Ошибка отправки сообщения: {:?}", e)),
|
||||
}
|
||||
}
|
||||
@@ -524,7 +518,6 @@ impl MessageManager {
|
||||
.convert_message(&msg)
|
||||
.await
|
||||
.ok_or_else(|| "Не удалось конвертировать отредактированное сообщение".to_string()),
|
||||
Ok(_) => Err("Неожиданный тип сообщения".to_string()),
|
||||
Err(e) => Err(format!("Ошибка редактирования: {:?}", e)),
|
||||
}
|
||||
}
|
||||
@@ -720,7 +713,6 @@ impl MessageManager {
|
||||
if let tdlib_rs::enums::MessageOrigin::User(origin_user) = &fi.origin {
|
||||
Some(ForwardInfo {
|
||||
sender_name: format!("User {}", origin_user.sender_user_id),
|
||||
date: fi.date,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@@ -834,7 +826,7 @@ impl MessageManager {
|
||||
if let Ok(original_msg_enum) =
|
||||
functions::get_message(chat_id.as_i64(), message_id.as_i64(), self.client_id).await
|
||||
{
|
||||
if let tdlib_rs::enums::Message::Message(original_msg) = original_msg_enum {
|
||||
let tdlib_rs::enums::Message::Message(original_msg) = original_msg_enum;
|
||||
if let Some(orig_info) = self.convert_message(&original_msg).await {
|
||||
// Update the reply info
|
||||
for msg in &mut self.current_chat_messages {
|
||||
@@ -855,5 +847,4 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ pub mod users;
|
||||
pub use auth::AuthState;
|
||||
pub use client::TdClient;
|
||||
pub use types::{
|
||||
ChatInfo, FolderInfo, ForwardInfo, MessageBuilder, MessageInfo, NetworkState, ProfileInfo,
|
||||
ReactionInfo, ReplyInfo, UserOnlineStatus,
|
||||
ChatInfo, MessageBuilder, MessageInfo, NetworkState, ProfileInfo, ReplyInfo, UserOnlineStatus,
|
||||
};
|
||||
|
||||
// Re-export ChatAction для удобства
|
||||
|
||||
@@ -70,7 +70,7 @@ impl ReactionManager {
|
||||
) -> Result<Vec<String>, String> {
|
||||
// Получаем сообщение
|
||||
let msg_result = functions::get_message(chat_id.as_i64(), message_id.as_i64(), self.client_id).await;
|
||||
let msg = match msg_result {
|
||||
let _msg = match msg_result {
|
||||
Ok(m) => m,
|
||||
Err(e) => return Err(format!("Ошибка получения сообщения: {:?}", e)),
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ use tdlib_rs::types::TextEntity;
|
||||
use crate::types::{ChatId, MessageId};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct ChatInfo {
|
||||
pub id: ChatId,
|
||||
pub title: String,
|
||||
@@ -41,9 +40,6 @@ pub struct ReplyInfo {
|
||||
pub struct ForwardInfo {
|
||||
/// Имя оригинального отправителя
|
||||
pub sender_name: String,
|
||||
/// Дата оригинального сообщения (для будущего использования)
|
||||
#[allow(dead_code)]
|
||||
pub date: i32,
|
||||
}
|
||||
|
||||
/// Информация о реакции на сообщение
|
||||
@@ -164,10 +160,6 @@ impl MessageInfo {
|
||||
self.metadata.date
|
||||
}
|
||||
|
||||
pub fn edit_date(&self) -> i32 {
|
||||
self.metadata.edit_date
|
||||
}
|
||||
|
||||
pub fn is_edited(&self) -> bool {
|
||||
self.metadata.edit_date > 0
|
||||
}
|
||||
@@ -312,12 +304,6 @@ impl MessageBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Пометить сообщение как отредактированное (edit_date = date + 60)
|
||||
pub fn edited(mut self) -> Self {
|
||||
self.edit_date = self.date + 60;
|
||||
self
|
||||
}
|
||||
|
||||
/// Пометить сообщение как прочитанное
|
||||
pub fn read(mut self) -> Self {
|
||||
self.is_read = true;
|
||||
@@ -366,12 +352,6 @@ impl MessageBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Добавить одну реакцию
|
||||
pub fn add_reaction(mut self, reaction: ReactionInfo) -> Self {
|
||||
self.reactions.push(reaction);
|
||||
self
|
||||
}
|
||||
|
||||
/// Построить MessageInfo из данных builder'а
|
||||
pub fn build(self) -> MessageInfo {
|
||||
MessageInfo::new(
|
||||
@@ -434,11 +414,11 @@ mod tests {
|
||||
let message = MessageBuilder::new(MessageId::new(789))
|
||||
.text("Original text")
|
||||
.date(1640000000)
|
||||
.edited()
|
||||
.edit_date(1640000060)
|
||||
.build();
|
||||
|
||||
assert!(message.is_edited());
|
||||
assert_eq!(message.edit_date(), 1640000060);
|
||||
assert_eq!(message.metadata.edit_date, 1640000060);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -468,7 +448,7 @@ mod tests {
|
||||
|
||||
let message = MessageBuilder::new(MessageId::new(300))
|
||||
.text("Cool message")
|
||||
.add_reaction(reaction.clone())
|
||||
.reactions(vec![reaction.clone()])
|
||||
.build();
|
||||
|
||||
assert_eq!(message.reactions().len(), 1);
|
||||
|
||||
@@ -89,12 +89,6 @@ where
|
||||
pub fn contains_key(&self, key: &K) -> bool {
|
||||
self.map.contains_key(key)
|
||||
}
|
||||
|
||||
/// Количество элементов
|
||||
#[allow(dead_code)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Кэш информации о пользователях Telegram.
|
||||
@@ -158,21 +152,6 @@ impl UserCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// Получить username пользователя
|
||||
pub fn get_username(&mut self, user_id: &UserId) -> Option<&String> {
|
||||
self.user_usernames.get(user_id)
|
||||
}
|
||||
|
||||
/// Получить имя пользователя
|
||||
pub fn get_name(&mut self, user_id: &UserId) -> Option<&String> {
|
||||
self.user_names.get(user_id)
|
||||
}
|
||||
|
||||
/// Получить user_id по chat_id
|
||||
pub fn get_user_id_by_chat(&self, chat_id: ChatId) -> Option<UserId> {
|
||||
self.chat_user_ids.get(&chat_id).copied()
|
||||
}
|
||||
|
||||
/// Получить статус пользователя по chat_id
|
||||
pub fn get_status_by_chat_id(&self, chat_id: ChatId) -> Option<&UserOnlineStatus> {
|
||||
let user_id = self.chat_user_ids.get(&chat_id)?;
|
||||
@@ -187,7 +166,7 @@ impl UserCache {
|
||||
///
|
||||
/// * `user_enum` - Обновление пользователя от TDLib
|
||||
pub fn handle_user_update(&mut self, user_enum: &User) {
|
||||
if let User::User(user) = user_enum {
|
||||
let User::User(user) = user_enum;
|
||||
let user_id = user.id;
|
||||
|
||||
// Сохраняем username
|
||||
@@ -202,7 +181,6 @@ impl UserCache {
|
||||
// Обновляем статус
|
||||
self.update_status(UserId::new(user_id), &user.status);
|
||||
}
|
||||
}
|
||||
|
||||
/// Обновляет онлайн-статус пользователя.
|
||||
///
|
||||
@@ -222,11 +200,6 @@ impl UserCache {
|
||||
self.user_statuses.insert(user_id, online_status);
|
||||
}
|
||||
|
||||
/// Сохранить связь chat_id -> user_id
|
||||
pub fn register_private_chat(&mut self, chat_id: ChatId, user_id: UserId) {
|
||||
self.chat_user_ids.insert(chat_id, user_id);
|
||||
}
|
||||
|
||||
/// Получает имя пользователя из кэша или загружает из TDLib.
|
||||
///
|
||||
/// Сначала проверяет кэш, затем при необходимости загружает из API.
|
||||
|
||||
@@ -15,12 +15,5 @@
|
||||
//
|
||||
// Пока этот файл служит placeholder'ом для будущего рефакторинга.
|
||||
|
||||
use crate::tdlib::MessageInfo;
|
||||
|
||||
/// Placeholder для функции рендеринга пузыря сообщения
|
||||
///
|
||||
/// TODO: Реализовать после выполнения P3.8 и P3.9
|
||||
pub fn render_message_bubble(_message: &MessageInfo) {
|
||||
// Будет реализовано позже
|
||||
unimplemented!("Message bubble rendering requires P3.8 and P3.9 first")
|
||||
}
|
||||
// Placeholder file - функция render_message_bubble удалена как неиспользуемая.
|
||||
// Рендеринг сообщений находится в src/ui/messages.rs
|
||||
|
||||
@@ -7,8 +7,6 @@ pub mod chat_list_item;
|
||||
pub mod emoji_picker;
|
||||
|
||||
// Экспорт основных функций
|
||||
pub use modal::render_modal;
|
||||
pub use input_field::render_input_field;
|
||||
pub use message_bubble::render_message_bubble;
|
||||
pub use chat_list_item::render_chat_list_item;
|
||||
pub use emoji_picker::render_emoji_picker;
|
||||
|
||||
@@ -52,12 +52,6 @@ fn parse_timezone_offset(tz: &str) -> i32 {
|
||||
3 // fallback к MSK
|
||||
}
|
||||
|
||||
/// Устаревшая функция для обратной совместимости (используется дефолтный +03:00)
|
||||
#[allow(dead_code)]
|
||||
pub fn format_timestamp(timestamp: i32) -> String {
|
||||
format_timestamp_with_tz(timestamp, "+03:00")
|
||||
}
|
||||
|
||||
/// Форматирование timestamp в дату для разделителя
|
||||
pub fn format_date(timestamp: i32) -> String {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
@@ -179,7 +179,7 @@ fn snapshot_chat_with_online_status() {
|
||||
let user_id = tele_tui::types::UserId::new(123);
|
||||
|
||||
// Регистрируем чат как приватный
|
||||
app.td_client.user_cache.register_private_chat(chat_id, user_id);
|
||||
app.td_client.user_cache.chat_user_ids.insert(chat_id, user_id);
|
||||
|
||||
// Устанавливаем онлайн-статус
|
||||
app.td_client.user_cache.user_statuses.insert(user_id, UserOnlineStatus::Online);
|
||||
|
||||
@@ -293,7 +293,7 @@ async fn test_user_journey_edit_during_conversation() {
|
||||
let edited_history = client.get_chat_history(ChatId::new(555), 50).await.unwrap();
|
||||
assert_eq!(edited_history.len(), 1);
|
||||
assert_eq!(edited_history[0].text(), "I'll be there at 5pm tomorrow");
|
||||
assert!(edited_history[0].edit_date() > 0, "Должна быть установлена дата редактирования");
|
||||
assert!(edited_history[0].metadata.edit_date > 0, "Должна быть установлена дата редактирования");
|
||||
|
||||
// 6. Проверяем историю редактирований
|
||||
assert_eq!(client.get_edited_messages().len(), 1);
|
||||
|
||||
@@ -39,15 +39,15 @@ async fn test_edit_message_sets_edit_date() {
|
||||
// Получаем дату до редактирования
|
||||
let messages_before = client.get_messages(123);
|
||||
let date_before = messages_before[0].date();
|
||||
assert_eq!(messages_before[0].edit_date(), 0); // Не редактировалось
|
||||
assert_eq!(messages_before[0].metadata.edit_date, 0); // Не редактировалось
|
||||
|
||||
// Редактируем сообщение
|
||||
client.edit_message(ChatId::new(123), msg.id(), "Edited".to_string()).await.unwrap();
|
||||
|
||||
// Проверяем что edit_date установлена
|
||||
let messages_after = client.get_messages(123);
|
||||
assert!(messages_after[0].edit_date() > 0);
|
||||
assert!(messages_after[0].edit_date() > date_before); // edit_date после date
|
||||
assert!(messages_after[0].metadata.edit_date > 0);
|
||||
assert!(messages_after[0].metadata.edit_date > date_before); // edit_date после date
|
||||
}
|
||||
|
||||
/// Test: Редактирование только своих сообщений (проверка через can_be_edited)
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tele_tui::tdlib::{ChatInfo, FolderInfo, MessageInfo, NetworkState, ProfileInfo, ReplyInfo, ReactionInfo};
|
||||
use tele_tui::tdlib::{ChatInfo, MessageInfo, NetworkState, ProfileInfo, ReplyInfo};
|
||||
use tele_tui::tdlib::types::{FolderInfo, ReactionInfo};
|
||||
use tele_tui::types::{ChatId, MessageId, UserId};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@@ -817,7 +818,7 @@ mod tests {
|
||||
let edited = client.get_edited_messages();
|
||||
assert_eq!(edited.len(), 1);
|
||||
assert_eq!(client.get_messages(123)[0].text(), "Hello World");
|
||||
assert!(client.get_messages(123)[0].edit_date() > 0);
|
||||
assert!(client.get_messages(123)[0].metadata.edit_date > 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Test data builders and fixtures
|
||||
|
||||
use tele_tui::tdlib::{ChatInfo, ForwardInfo, MessageInfo, ProfileInfo, ReactionInfo, ReplyInfo};
|
||||
use tele_tui::tdlib::{ChatInfo, MessageInfo, ProfileInfo, ReplyInfo};
|
||||
use tele_tui::tdlib::types::{ForwardInfo, ReactionInfo};
|
||||
use tele_tui::types::{ChatId, MessageId};
|
||||
|
||||
/// Builder для создания тестового чата
|
||||
@@ -176,7 +177,6 @@ impl TestMessageBuilder {
|
||||
pub fn forwarded_from(mut self, sender: &str) -> Self {
|
||||
self.forward_from = Some(ForwardInfo {
|
||||
sender_name: sender.to_string(),
|
||||
date: self.date - 3600,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ mod helpers;
|
||||
|
||||
use helpers::fake_tdclient::FakeTdClient;
|
||||
use helpers::test_data::TestMessageBuilder;
|
||||
use tele_tui::tdlib::{ForwardInfo, ReplyInfo};
|
||||
use tele_tui::tdlib::ReplyInfo;
|
||||
use tele_tui::tdlib::types::ForwardInfo;
|
||||
use tele_tui::types::{ChatId, MessageId};
|
||||
|
||||
/// Test: Reply создаёт сообщение с reply_to
|
||||
@@ -106,7 +107,6 @@ async fn test_forward_creates_message_with_forward_from() {
|
||||
|
||||
let forward = messages[0].forward_from().unwrap();
|
||||
assert_eq!(forward.sender_name, "Bob");
|
||||
assert!(forward.date > 0); // Дата установлена
|
||||
}
|
||||
|
||||
/// Test: Forward показывает "↪ Переслано от ..."
|
||||
|
||||
Reference in New Issue
Block a user