refactor: split app/mod.rs into trait-based architecture (1015→371 lines)
Split monolithic App impl into 5 specialized trait modules: - methods/navigation.rs (NavigationMethods) - 7 methods for chat navigation - methods/messages.rs (MessageMethods) - 8 methods for message operations - methods/compose.rs (ComposeMethods) - 10 methods for reply/forward/draft - methods/search.rs (SearchMethods) - 15 methods for search functionality - methods/modal.rs (ModalMethods) - 27 methods for modal dialogs Changes: - app/mod.rs: 1015→371 lines (removed 644 lines, -63%) - Created app/methods/ with 5 trait impl blocks - Left in app/mod.rs: constructors, get_command, get_selected_chat_id/chat, getters/setters - 116 functions → 5 trait impl blocks (67 in traits + 48 in core) - Single Responsibility Principle achieved - Updated CONTEXT.md with refactoring metrics - Updated ROADMAP.md: Phase 13 Etap 2 marked as DONE Phase 13 Etap 2: COMPLETED (100%) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
125
src/app/methods/messages.rs
Normal file
125
src/app/methods/messages.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
//! Message methods for App
|
||||
//!
|
||||
//! Handles message selection, editing, and operations
|
||||
|
||||
use crate::app::{App, ChatState};
|
||||
use crate::tdlib::{MessageInfo, TdClientTrait};
|
||||
|
||||
/// Message operation methods
|
||||
pub trait MessageMethods<T: TdClientTrait> {
|
||||
/// Start message selection mode (triggered by Up arrow in empty input)
|
||||
fn start_message_selection(&mut self);
|
||||
|
||||
/// Select previous message (up in history = older)
|
||||
fn select_previous_message(&mut self);
|
||||
|
||||
/// Select next message (down in history = newer)
|
||||
fn select_next_message(&mut self);
|
||||
|
||||
/// Get currently selected message
|
||||
fn get_selected_message(&self) -> Option<MessageInfo>;
|
||||
|
||||
/// Start editing the selected message
|
||||
/// Returns true if editing started, false if message cannot be edited
|
||||
fn start_editing_selected(&mut self) -> bool;
|
||||
|
||||
/// Cancel message editing and clear input
|
||||
fn cancel_editing(&mut self);
|
||||
|
||||
/// Check if currently in editing mode
|
||||
fn is_editing(&self) -> bool;
|
||||
|
||||
/// Check if currently in message selection mode
|
||||
fn is_selecting_message(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<T: TdClientTrait> MessageMethods<T> for App<T> {
|
||||
fn start_message_selection(&mut self) {
|
||||
let total = self.td_client.current_chat_messages().len();
|
||||
if total == 0 {
|
||||
return;
|
||||
}
|
||||
// Начинаем с последнего сообщения (индекс len-1 = самое новое внизу)
|
||||
self.chat_state = ChatState::MessageSelection { selected_index: total - 1 };
|
||||
}
|
||||
|
||||
fn select_previous_message(&mut self) {
|
||||
if let ChatState::MessageSelection { selected_index } = &mut self.chat_state {
|
||||
if *selected_index > 0 {
|
||||
*selected_index -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_next_message(&mut self) {
|
||||
let total = self.td_client.current_chat_messages().len();
|
||||
if total == 0 {
|
||||
return;
|
||||
}
|
||||
if let ChatState::MessageSelection { selected_index } = &mut self.chat_state {
|
||||
if *selected_index < total - 1 {
|
||||
*selected_index += 1;
|
||||
} else {
|
||||
// Дошли до самого нового сообщения - выходим из режима выбора
|
||||
self.chat_state = ChatState::Normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_selected_message(&self) -> Option<MessageInfo> {
|
||||
self.chat_state.selected_message_index().and_then(|idx| {
|
||||
self.td_client.current_chat_messages().get(idx).cloned()
|
||||
})
|
||||
}
|
||||
|
||||
fn start_editing_selected(&mut self) -> bool {
|
||||
// Получаем selected_index из текущего состояния
|
||||
let selected_idx = match &self.chat_state {
|
||||
ChatState::MessageSelection { selected_index } => Some(*selected_index),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if selected_idx.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Сначала извлекаем данные из сообщения
|
||||
let msg_data = self.get_selected_message().and_then(|msg| {
|
||||
// Проверяем:
|
||||
// 1. Можно редактировать
|
||||
// 2. Это исходящее сообщение
|
||||
// 3. ID не временный (временные ID в TDLib отрицательные)
|
||||
if msg.can_be_edited() && msg.is_outgoing() && msg.id().as_i64() > 0 {
|
||||
Some((msg.id(), msg.text().to_string(), selected_idx.unwrap()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
// Затем присваиваем
|
||||
if let Some((id, content, idx)) = msg_data {
|
||||
self.cursor_position = content.chars().count();
|
||||
self.message_input = content;
|
||||
self.chat_state = ChatState::Editing {
|
||||
message_id: id,
|
||||
selected_index: idx,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn cancel_editing(&mut self) {
|
||||
self.chat_state = ChatState::Normal;
|
||||
self.message_input.clear();
|
||||
self.cursor_position = 0;
|
||||
}
|
||||
|
||||
fn is_editing(&self) -> bool {
|
||||
self.chat_state.is_editing()
|
||||
}
|
||||
|
||||
fn is_selecting_message(&self) -> bool {
|
||||
self.chat_state.is_message_selection()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user