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:
308
src/app/methods/modal.rs
Normal file
308
src/app/methods/modal.rs
Normal file
@@ -0,0 +1,308 @@
|
||||
//! Modal methods for App
|
||||
//!
|
||||
//! Handles modal dialogs: Profile, Pinned Messages, Reactions, Delete Confirmation
|
||||
|
||||
use crate::app::{App, ChatState};
|
||||
use crate::tdlib::{MessageInfo, ProfileInfo, TdClientTrait};
|
||||
use crate::types::MessageId;
|
||||
|
||||
/// Modal dialog methods
|
||||
pub trait ModalMethods<T: TdClientTrait> {
|
||||
// === Delete Confirmation ===
|
||||
|
||||
/// Check if delete confirmation modal is shown
|
||||
fn is_confirm_delete_shown(&self) -> bool;
|
||||
|
||||
// === Pinned Messages ===
|
||||
|
||||
/// Check if in pinned messages mode
|
||||
fn is_pinned_mode(&self) -> bool;
|
||||
|
||||
/// Enter pinned messages mode
|
||||
fn enter_pinned_mode(&mut self, messages: Vec<MessageInfo>);
|
||||
|
||||
/// Exit pinned messages mode
|
||||
fn exit_pinned_mode(&mut self);
|
||||
|
||||
/// Select previous pinned message (up = older)
|
||||
fn select_previous_pinned(&mut self);
|
||||
|
||||
/// Select next pinned message (down = newer)
|
||||
fn select_next_pinned(&mut self);
|
||||
|
||||
/// Get currently selected pinned message
|
||||
fn get_selected_pinned(&self) -> Option<&MessageInfo>;
|
||||
|
||||
/// Get ID of selected pinned message for navigation
|
||||
fn get_selected_pinned_id(&self) -> Option<i64>;
|
||||
|
||||
// === Profile ===
|
||||
|
||||
/// Check if in profile mode
|
||||
fn is_profile_mode(&self) -> bool;
|
||||
|
||||
/// Enter profile mode
|
||||
fn enter_profile_mode(&mut self, info: ProfileInfo);
|
||||
|
||||
/// Exit profile mode
|
||||
fn exit_profile_mode(&mut self);
|
||||
|
||||
/// Select previous profile action
|
||||
fn select_previous_profile_action(&mut self);
|
||||
|
||||
/// Select next profile action
|
||||
fn select_next_profile_action(&mut self, max_actions: usize);
|
||||
|
||||
/// Show first leave group confirmation
|
||||
fn show_leave_group_confirmation(&mut self);
|
||||
|
||||
/// Show second leave group confirmation
|
||||
fn show_leave_group_final_confirmation(&mut self);
|
||||
|
||||
/// Cancel leave group confirmation
|
||||
fn cancel_leave_group(&mut self);
|
||||
|
||||
/// Get current leave group confirmation step (0, 1, or 2)
|
||||
fn get_leave_group_confirmation_step(&self) -> u8;
|
||||
|
||||
/// Get profile info
|
||||
fn get_profile_info(&self) -> Option<&ProfileInfo>;
|
||||
|
||||
/// Get selected profile action index
|
||||
fn get_selected_profile_action(&self) -> Option<usize>;
|
||||
|
||||
// === Reactions ===
|
||||
|
||||
/// Check if in reaction picker mode
|
||||
fn is_reaction_picker_mode(&self) -> bool;
|
||||
|
||||
/// Enter reaction picker mode
|
||||
fn enter_reaction_picker_mode(&mut self, message_id: i64, available_reactions: Vec<String>);
|
||||
|
||||
/// Exit reaction picker mode
|
||||
fn exit_reaction_picker_mode(&mut self);
|
||||
|
||||
/// Select previous reaction
|
||||
fn select_previous_reaction(&mut self);
|
||||
|
||||
/// Select next reaction
|
||||
fn select_next_reaction(&mut self);
|
||||
|
||||
/// Get currently selected reaction emoji
|
||||
fn get_selected_reaction(&self) -> Option<&String>;
|
||||
|
||||
/// Get message ID for which reaction is being selected
|
||||
fn get_selected_message_for_reaction(&self) -> Option<i64>;
|
||||
}
|
||||
|
||||
impl<T: TdClientTrait> ModalMethods<T> for App<T> {
|
||||
fn is_confirm_delete_shown(&self) -> bool {
|
||||
self.chat_state.is_delete_confirmation()
|
||||
}
|
||||
|
||||
fn is_pinned_mode(&self) -> bool {
|
||||
self.chat_state.is_pinned_mode()
|
||||
}
|
||||
|
||||
fn enter_pinned_mode(&mut self, messages: Vec<MessageInfo>) {
|
||||
if !messages.is_empty() {
|
||||
self.chat_state = ChatState::PinnedMessages {
|
||||
messages,
|
||||
selected_index: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_pinned_mode(&mut self) {
|
||||
self.chat_state = ChatState::Normal;
|
||||
}
|
||||
|
||||
fn select_previous_pinned(&mut self) {
|
||||
if let ChatState::PinnedMessages {
|
||||
selected_index,
|
||||
messages,
|
||||
} = &mut self.chat_state
|
||||
{
|
||||
if *selected_index + 1 < messages.len() {
|
||||
*selected_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_next_pinned(&mut self) {
|
||||
if let ChatState::PinnedMessages { selected_index, .. } = &mut self.chat_state {
|
||||
if *selected_index > 0 {
|
||||
*selected_index -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_selected_pinned(&self) -> Option<&MessageInfo> {
|
||||
if let ChatState::PinnedMessages {
|
||||
messages,
|
||||
selected_index,
|
||||
} = &self.chat_state
|
||||
{
|
||||
messages.get(*selected_index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_selected_pinned_id(&self) -> Option<i64> {
|
||||
self.get_selected_pinned().map(|m| m.id().as_i64())
|
||||
}
|
||||
|
||||
fn is_profile_mode(&self) -> bool {
|
||||
self.chat_state.is_profile()
|
||||
}
|
||||
|
||||
fn enter_profile_mode(&mut self, info: ProfileInfo) {
|
||||
self.chat_state = ChatState::Profile {
|
||||
info,
|
||||
selected_action: 0,
|
||||
leave_group_confirmation_step: 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn exit_profile_mode(&mut self) {
|
||||
self.chat_state = ChatState::Normal;
|
||||
}
|
||||
|
||||
fn select_previous_profile_action(&mut self) {
|
||||
if let ChatState::Profile {
|
||||
selected_action, ..
|
||||
} = &mut self.chat_state
|
||||
{
|
||||
if *selected_action > 0 {
|
||||
*selected_action -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_next_profile_action(&mut self, max_actions: usize) {
|
||||
if let ChatState::Profile {
|
||||
selected_action, ..
|
||||
} = &mut self.chat_state
|
||||
{
|
||||
if *selected_action < max_actions.saturating_sub(1) {
|
||||
*selected_action += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_leave_group_confirmation(&mut self) {
|
||||
if let ChatState::Profile {
|
||||
leave_group_confirmation_step,
|
||||
..
|
||||
} = &mut self.chat_state
|
||||
{
|
||||
*leave_group_confirmation_step = 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn show_leave_group_final_confirmation(&mut self) {
|
||||
if let ChatState::Profile {
|
||||
leave_group_confirmation_step,
|
||||
..
|
||||
} = &mut self.chat_state
|
||||
{
|
||||
*leave_group_confirmation_step = 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel_leave_group(&mut self) {
|
||||
if let ChatState::Profile {
|
||||
leave_group_confirmation_step,
|
||||
..
|
||||
} = &mut self.chat_state
|
||||
{
|
||||
*leave_group_confirmation_step = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_leave_group_confirmation_step(&self) -> u8 {
|
||||
if let ChatState::Profile {
|
||||
leave_group_confirmation_step,
|
||||
..
|
||||
} = &self.chat_state
|
||||
{
|
||||
*leave_group_confirmation_step
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn get_profile_info(&self) -> Option<&ProfileInfo> {
|
||||
if let ChatState::Profile { info, .. } = &self.chat_state {
|
||||
Some(info)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_selected_profile_action(&self) -> Option<usize> {
|
||||
if let ChatState::Profile {
|
||||
selected_action, ..
|
||||
} = &self.chat_state
|
||||
{
|
||||
Some(*selected_action)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn is_reaction_picker_mode(&self) -> bool {
|
||||
self.chat_state.is_reaction_picker()
|
||||
}
|
||||
|
||||
fn enter_reaction_picker_mode(&mut self, message_id: i64, available_reactions: Vec<String>) {
|
||||
self.chat_state = ChatState::ReactionPicker {
|
||||
message_id: MessageId::new(message_id),
|
||||
available_reactions,
|
||||
selected_index: 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn exit_reaction_picker_mode(&mut self) {
|
||||
self.chat_state = ChatState::Normal;
|
||||
}
|
||||
|
||||
fn select_previous_reaction(&mut self) {
|
||||
if let ChatState::ReactionPicker { selected_index, .. } = &mut self.chat_state {
|
||||
if *selected_index > 0 {
|
||||
*selected_index -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_next_reaction(&mut self) {
|
||||
if let ChatState::ReactionPicker {
|
||||
selected_index,
|
||||
available_reactions,
|
||||
..
|
||||
} = &mut self.chat_state
|
||||
{
|
||||
if *selected_index + 1 < available_reactions.len() {
|
||||
*selected_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_selected_reaction(&self) -> Option<&String> {
|
||||
if let ChatState::ReactionPicker {
|
||||
available_reactions,
|
||||
selected_index,
|
||||
..
|
||||
} = &self.chat_state
|
||||
{
|
||||
available_reactions.get(*selected_index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_selected_message_for_reaction(&self) -> Option<i64> {
|
||||
self.chat_state.selected_message_id().map(|id| id.as_i64())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user