feat: implement Phase 11 — inline photo viewing with ratatui-image
Add feature-gated (`images`) inline photo support: - New types: MediaInfo, PhotoInfo, PhotoDownloadState, ImagesConfig - Media module: ImageCache (LRU filesystem cache), ImageRenderer (terminal protocol detection) - Photo metadata extraction from TDLib MessagePhoto with download_file() API - ViewImage command (v/м) to toggle photo expand/collapse in message selection - Two-pass UI rendering: placeholder lines in message bubbles + StatefulImage overlay - Collapse all expanded photos on Esc (exit selection mode) Dependencies: ratatui-image 8.1, image 0.25 (optional, behind `images` feature flag) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Integration tests for config flow
|
||||
|
||||
use tele_tui::config::{Config, ColorsConfig, GeneralConfig, Keybindings, NotificationsConfig};
|
||||
use tele_tui::config::{Config, ColorsConfig, GeneralConfig, ImagesConfig, Keybindings, NotificationsConfig};
|
||||
|
||||
/// Test: Дефолтные значения конфигурации
|
||||
#[test]
|
||||
@@ -34,6 +34,7 @@ fn test_config_custom_values() {
|
||||
},
|
||||
keybindings: Keybindings::default(),
|
||||
notifications: NotificationsConfig::default(),
|
||||
images: ImagesConfig::default(),
|
||||
};
|
||||
|
||||
assert_eq!(config.general.timezone, "+05:00");
|
||||
@@ -118,6 +119,7 @@ fn test_config_toml_serialization() {
|
||||
},
|
||||
keybindings: Keybindings::default(),
|
||||
notifications: NotificationsConfig::default(),
|
||||
images: ImagesConfig::default(),
|
||||
};
|
||||
|
||||
// Сериализуем в TOML
|
||||
|
||||
@@ -51,6 +51,9 @@ pub struct FakeTdClient {
|
||||
// Update channel для симуляции событий
|
||||
pub update_tx: Arc<Mutex<Option<mpsc::UnboundedSender<TdUpdate>>>>,
|
||||
|
||||
// Скачанные файлы (file_id -> local_path)
|
||||
pub downloaded_files: Arc<Mutex<HashMap<i32, String>>>,
|
||||
|
||||
// Настройки поведения
|
||||
pub simulate_delays: bool,
|
||||
pub fail_next_operation: Arc<Mutex<bool>>,
|
||||
@@ -121,6 +124,7 @@ impl Clone for FakeTdClient {
|
||||
viewed_messages: Arc::clone(&self.viewed_messages),
|
||||
chat_actions: Arc::clone(&self.chat_actions),
|
||||
pending_view_messages: Arc::clone(&self.pending_view_messages),
|
||||
downloaded_files: Arc::clone(&self.downloaded_files),
|
||||
update_tx: Arc::clone(&self.update_tx),
|
||||
simulate_delays: self.simulate_delays,
|
||||
fail_next_operation: Arc::clone(&self.fail_next_operation),
|
||||
@@ -154,6 +158,7 @@ impl FakeTdClient {
|
||||
viewed_messages: Arc::new(Mutex::new(vec![])),
|
||||
chat_actions: Arc::new(Mutex::new(vec![])),
|
||||
pending_view_messages: Arc::new(Mutex::new(vec![])),
|
||||
downloaded_files: Arc::new(Mutex::new(HashMap::new())),
|
||||
update_tx: Arc::new(Mutex::new(None)),
|
||||
simulate_delays: false,
|
||||
fail_next_operation: Arc::new(Mutex::new(false)),
|
||||
@@ -237,6 +242,12 @@ impl FakeTdClient {
|
||||
self
|
||||
}
|
||||
|
||||
/// Добавить скачанный файл (для mock download_file)
|
||||
pub fn with_downloaded_file(self, file_id: i32, path: &str) -> Self {
|
||||
self.downloaded_files.lock().unwrap().insert(file_id, path.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Установить доступные реакции
|
||||
pub fn with_available_reactions(self, reactions: Vec<String>) -> Self {
|
||||
*self.available_reactions.lock().unwrap() = reactions;
|
||||
@@ -587,6 +598,20 @@ impl FakeTdClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Скачать файл (mock)
|
||||
pub async fn download_file(&self, file_id: i32) -> Result<String, String> {
|
||||
if self.should_fail() {
|
||||
return Err("Failed to download file".to_string());
|
||||
}
|
||||
|
||||
self.downloaded_files
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&file_id)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("File {} not found", file_id))
|
||||
}
|
||||
|
||||
/// Получить информацию о профиле
|
||||
pub async fn get_profile_info(&self, chat_id: ChatId) -> Result<ProfileInfo, String> {
|
||||
if self.should_fail() {
|
||||
|
||||
@@ -161,6 +161,11 @@ impl TdClientTrait for FakeTdClient {
|
||||
FakeTdClient::toggle_reaction(self, chat_id, message_id, reaction).await
|
||||
}
|
||||
|
||||
// ============ File methods ============
|
||||
async fn download_file(&self, file_id: i32) -> Result<String, String> {
|
||||
FakeTdClient::download_file(self, file_id).await
|
||||
}
|
||||
|
||||
// ============ Getters (immutable) ============
|
||||
fn client_id(&self) -> i32 {
|
||||
0 // Fake client ID
|
||||
|
||||
Reference in New Issue
Block a user