This commit is contained in:
Mikhail Kilin
2026-01-30 16:18:16 +03:00
parent 4deb0fbe00
commit a4cf6bac72
9 changed files with 936 additions and 215 deletions

69
src/constants.rs Normal file
View File

@@ -0,0 +1,69 @@
// Application constants
// ============================================================================
// Memory Limits
// ============================================================================
/// Максимальное количество сообщений в одном чате (для оптимизации памяти)
pub const MAX_MESSAGES_IN_CHAT: usize = 500;
/// Максимальный размер кэша пользователей (LRU)
pub const MAX_USER_CACHE_SIZE: usize = 500;
/// Максимальное количество чатов для загрузки
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
// ============================================================================
/// Таймаут poll для event loop (16ms = 60 FPS)
pub const POLL_TIMEOUT_MS: u64 = 16;
/// Таймаут ожидания graceful shutdown (в секундах)
pub const SHUTDOWN_TIMEOUT_SECS: u64 = 2;
/// Количество пользователей для ленивой загрузки за один тик
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;

View File

@@ -3,6 +3,7 @@
pub mod app;
pub mod config;
pub mod constants;
pub mod input;
pub mod tdlib;
pub mod ui;

View File

@@ -1,5 +1,6 @@
mod app;
mod config;
mod constants;
mod input;
mod tdlib;
mod ui;
@@ -18,6 +19,7 @@ use std::time::Duration;
use tdlib_rs::enums::Update;
use app::{App, AppScreen};
use constants::{POLL_TIMEOUT_MS, SHUTDOWN_TIMEOUT_SECS};
use input::{handle_auth_input, handle_main_input};
use tdlib::client::AuthState;
use utils::disable_tdlib_logs;
@@ -148,7 +150,7 @@ async fn run_app<B: ratatui::backend::Backend>(
// Используем poll с коротким таймаутом для быстрой реакции на ввод
// 16ms ≈ 60 FPS потенциально, но рендерим только при изменениях
if event::poll(Duration::from_millis(16))? {
if event::poll(Duration::from_millis(POLL_TIMEOUT_MS))? {
match event::read()? {
Event::Key(key) => {
// Global quit command
@@ -162,7 +164,7 @@ async fn run_app<B: ratatui::backend::Backend>(
let _ = tdlib_rs::functions::close(app.td_client.client_id()).await;
// Ждём завершения polling задачи (с таймаутом)
let _ = tokio::time::timeout(Duration::from_secs(2), polling_handle).await;
let _ = tokio::time::timeout(Duration::from_secs(SHUTDOWN_TIMEOUT_SECS), polling_handle).await;
return Ok(());
}

View File

@@ -1,3 +1,7 @@
use crate::constants::{
LAZY_LOAD_USERS_PER_TICK, MAX_CHAT_USER_IDS, MAX_CHATS, MAX_MESSAGES_IN_CHAT,
MAX_USER_CACHE_SIZE, TDLIB_CHAT_LIMIT, TDLIB_MESSAGE_LIMIT,
};
use std::collections::HashMap;
use std::env;
use std::time::Instant;
@@ -7,15 +11,6 @@ use tdlib_rs::enums::{
};
use tdlib_rs::types::TextEntity;
/// Максимальный размер кэшей пользователей
const MAX_USER_CACHE_SIZE: usize = 500;
/// Максимальное количество сообщений в текущем чате
const MAX_MESSAGES_IN_CHAT: usize = 500;
/// Максимальное количество чатов
const MAX_CHATS: usize = 200;
/// Максимальный размер кэша chat_user_ids
const MAX_CHAT_USER_IDS: usize = 500;
/// Простой LRU-кэш на основе HashMap + Vec для отслеживания порядка
pub struct LruCache<V> {
map: HashMap<i64, V>,
@@ -1312,11 +1307,11 @@ impl TdClient {
chat_id,
query.to_string(),
None, // sender_id
0, // from_message_id
0, // offset
50, // limit
None, // filter (no filter = search by text)
0, // message_thread_id
0, // from_message_id
0, // offset
TDLIB_MESSAGE_LIMIT, // limit
None, // filter (no filter = search by text)
0, // message_thread_id
0, // saved_messages_topic_id
self.client_id,
)
@@ -1895,15 +1890,15 @@ impl TdClient {
/// Загружает только последние 5 запросов за цикл для снижения нагрузки
pub async fn process_pending_user_ids(&mut self) {
// Берём только последние запросы (они актуальнее — от недавних сообщений)
const BATCH_SIZE: usize = 5;
const LAZY_LOAD_USERS_PER_TICK: usize = 5;
// Убираем дубликаты и уже загруженные
self.pending_user_ids
.retain(|id| !self.user_names.contains_key(id));
self.pending_user_ids.dedup();
// Берём последние BATCH_SIZE элементов
let start = self.pending_user_ids.len().saturating_sub(BATCH_SIZE);
// Берём последние LAZY_LOAD_USERS_PER_TICK элементов
let start = self.pending_user_ids.len().saturating_sub(LAZY_LOAD_USERS_PER_TICK);
let batch: Vec<i64> = self.pending_user_ids.drain(start..).collect();
for user_id in batch {