refactor: complete Phase 13 deep architecture refactoring (etaps 3-7)
Split monolithic files into modular architecture: - ui/messages.rs (893→365 lines): extract modals/, compose_bar.rs - tdlib/messages.rs (836→3 files): split into messages/mod, convert, operations - config/mod.rs (642→3 files): extract validation.rs, loader.rs - Code duplication cleanup: shared components, ~220 lines removed - Documentation: PROJECT_STRUCTURE.md rewrite, 16 files got //! docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
117
src/ui/modals/search.rs
Normal file
117
src/ui/modals/search.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
//! Message search modal
|
||||
|
||||
use crate::app::App;
|
||||
use crate::tdlib::TdClientTrait;
|
||||
use crate::ui::components::{render_message_item, calculate_scroll_offset, render_help_bar};
|
||||
use ratatui::{
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
style::{Color, Modifier, Style},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
|
||||
/// Renders message search mode
|
||||
pub fn render<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &App<T>) {
|
||||
// Извлекаем данные из ChatState
|
||||
let (query, results, selected_index) =
|
||||
if let crate::app::ChatState::SearchInChat {
|
||||
query,
|
||||
results,
|
||||
selected_index,
|
||||
} = &app.chat_state
|
||||
{
|
||||
(query.as_str(), results.as_slice(), *selected_index)
|
||||
} else {
|
||||
return; // Некорректное состояние, не рендерим
|
||||
};
|
||||
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
Constraint::Length(3), // Search input
|
||||
Constraint::Min(0), // Search results
|
||||
Constraint::Length(3), // Help bar
|
||||
])
|
||||
.split(area);
|
||||
|
||||
// Search input
|
||||
let total = results.len();
|
||||
let current = if total > 0 {
|
||||
selected_index + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let input_line = if query.is_empty() {
|
||||
Line::from(vec![
|
||||
Span::styled("🔍 ", Style::default().fg(Color::Yellow)),
|
||||
Span::styled("█", Style::default().fg(Color::Yellow)),
|
||||
Span::styled(" Введите текст для поиска...", Style::default().fg(Color::Gray)),
|
||||
])
|
||||
} else {
|
||||
Line::from(vec![
|
||||
Span::styled("🔍 ", Style::default().fg(Color::Yellow)),
|
||||
Span::styled(query, Style::default().fg(Color::White)),
|
||||
Span::styled("█", Style::default().fg(Color::Yellow)),
|
||||
Span::styled(format!(" ({}/{})", current, total), Style::default().fg(Color::Gray)),
|
||||
])
|
||||
};
|
||||
|
||||
let search_input = Paragraph::new(input_line).block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::Yellow))
|
||||
.title(" Поиск по сообщениям ")
|
||||
.title_style(
|
||||
Style::default()
|
||||
.fg(Color::Yellow)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
);
|
||||
f.render_widget(search_input, chunks[0]);
|
||||
|
||||
// Search results
|
||||
let content_width = chunks[1].width.saturating_sub(2) as usize;
|
||||
let mut lines: Vec<Line> = Vec::new();
|
||||
|
||||
if results.is_empty() {
|
||||
if !query.is_empty() {
|
||||
lines.push(Line::from(Span::styled(
|
||||
"Ничего не найдено",
|
||||
Style::default().fg(Color::Gray),
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
for (idx, msg) in results.iter().enumerate() {
|
||||
if idx > 0 {
|
||||
lines.push(Line::from(""));
|
||||
}
|
||||
lines.extend(render_message_item(msg, idx == selected_index, content_width, 2));
|
||||
}
|
||||
}
|
||||
|
||||
// Скролл к выбранному результату
|
||||
let scroll_offset = calculate_scroll_offset(selected_index, 4, chunks[1].height);
|
||||
|
||||
let results_widget = Paragraph::new(lines)
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::Yellow)),
|
||||
)
|
||||
.scroll((scroll_offset, 0));
|
||||
f.render_widget(results_widget, chunks[1]);
|
||||
|
||||
// Help bar
|
||||
let help = render_help_bar(
|
||||
&[
|
||||
("↑↓", "навигация", Color::Yellow),
|
||||
("n/N", "след./пред.", Color::Yellow),
|
||||
("Enter", "перейти", Color::Green),
|
||||
("Esc", "выход", Color::Red),
|
||||
],
|
||||
Color::Yellow,
|
||||
);
|
||||
f.render_widget(help, chunks[2]);
|
||||
}
|
||||
Reference in New Issue
Block a user