Фаза 1, Подход 2 - постепенная инкапсуляция полей App. Changes: - src/app/mod.rs: сделаны приватными phone_input, code_input, password_input - src/input/auth.rs: замены на phone_input_mut(), code_input_mut(), password_input_mut() - src/ui/auth.rs: замены на phone_input(), code_input(), password_input() - tests/helpers/app_builder.rs: замены на set_phone_input(), set_code_input(), set_password_input() Используются существующие геттеры/сеттеры (были добавлены ранее). Progress: Group 1/5 complete (auth fields) Next: Group 2 (UI state: screen, is_loading, needs_redraw, is_searching) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
138 lines
5.3 KiB
Rust
138 lines
5.3 KiB
Rust
use crate::app::App;
|
|
use crate::tdlib::TdClientTrait;
|
|
use crate::tdlib::AuthState;
|
|
use ratatui::{
|
|
layout::{Alignment, Constraint, Direction, Layout},
|
|
style::{Color, Modifier, Style},
|
|
text::Line,
|
|
widgets::{Block, Borders, Paragraph},
|
|
Frame,
|
|
};
|
|
|
|
pub fn render<T: TdClientTrait>(f: &mut Frame, app: &App<T>) {
|
|
let area = f.area();
|
|
|
|
let vertical_chunks = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.constraints([
|
|
Constraint::Percentage(30),
|
|
Constraint::Length(15),
|
|
Constraint::Percentage(30),
|
|
])
|
|
.split(area);
|
|
|
|
let horizontal_chunks = Layout::default()
|
|
.direction(Direction::Horizontal)
|
|
.constraints([
|
|
Constraint::Percentage(25),
|
|
Constraint::Percentage(50),
|
|
Constraint::Percentage(25),
|
|
])
|
|
.split(vertical_chunks[1]);
|
|
|
|
let auth_area = horizontal_chunks[1];
|
|
|
|
let auth_chunks = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.constraints([
|
|
Constraint::Length(3), // Title
|
|
Constraint::Length(4), // Instructions
|
|
Constraint::Length(3), // Input
|
|
Constraint::Length(2), // Error/Status message
|
|
Constraint::Min(0), // Spacer
|
|
])
|
|
.split(auth_area);
|
|
|
|
// Title
|
|
let title = Paragraph::new("TTUI - Telegram Authentication")
|
|
.style(
|
|
Style::default()
|
|
.fg(Color::Cyan)
|
|
.add_modifier(Modifier::BOLD),
|
|
)
|
|
.alignment(Alignment::Center)
|
|
.block(Block::default().borders(Borders::ALL));
|
|
f.render_widget(title, auth_chunks[0]);
|
|
|
|
// Instructions and Input based on auth state
|
|
match &app.td_client.auth_state() {
|
|
AuthState::WaitPhoneNumber => {
|
|
let instructions = vec![
|
|
Line::from("Введите номер телефона в международном формате"),
|
|
Line::from("Пример: +79991111111"),
|
|
];
|
|
let instructions_widget = Paragraph::new(instructions)
|
|
.style(Style::default().fg(Color::Gray))
|
|
.alignment(Alignment::Center)
|
|
.block(Block::default().borders(Borders::NONE));
|
|
f.render_widget(instructions_widget, auth_chunks[1]);
|
|
|
|
let input_text = format!("📱 {}", app.phone_input());
|
|
let input = Paragraph::new(input_text)
|
|
.style(Style::default().fg(Color::Yellow))
|
|
.alignment(Alignment::Center)
|
|
.block(
|
|
Block::default()
|
|
.borders(Borders::ALL)
|
|
.title(" Phone Number "),
|
|
);
|
|
f.render_widget(input, auth_chunks[2]);
|
|
}
|
|
AuthState::WaitCode => {
|
|
let instructions = vec![
|
|
Line::from("Введите код подтверждения из Telegram"),
|
|
Line::from("Код был отправлен на ваш номер"),
|
|
];
|
|
let instructions_widget = Paragraph::new(instructions)
|
|
.style(Style::default().fg(Color::Gray))
|
|
.alignment(Alignment::Center)
|
|
.block(Block::default().borders(Borders::NONE));
|
|
f.render_widget(instructions_widget, auth_chunks[1]);
|
|
|
|
let input_text = format!("🔐 {}", app.code_input());
|
|
let input = Paragraph::new(input_text)
|
|
.style(Style::default().fg(Color::Yellow))
|
|
.alignment(Alignment::Center)
|
|
.block(
|
|
Block::default()
|
|
.borders(Borders::ALL)
|
|
.title(" Verification Code "),
|
|
);
|
|
f.render_widget(input, auth_chunks[2]);
|
|
}
|
|
AuthState::WaitPassword => {
|
|
let instructions = vec![
|
|
Line::from("Введите пароль двухфакторной аутентификации"),
|
|
Line::from(""),
|
|
];
|
|
let instructions_widget = Paragraph::new(instructions)
|
|
.style(Style::default().fg(Color::Gray))
|
|
.alignment(Alignment::Center)
|
|
.block(Block::default().borders(Borders::NONE));
|
|
f.render_widget(instructions_widget, auth_chunks[1]);
|
|
|
|
let masked_password = "*".repeat(app.password_input().len());
|
|
let input_text = format!("🔒 {}", masked_password);
|
|
let input = Paragraph::new(input_text)
|
|
.style(Style::default().fg(Color::Yellow))
|
|
.alignment(Alignment::Center)
|
|
.block(Block::default().borders(Borders::ALL).title(" Password "));
|
|
f.render_widget(input, auth_chunks[2]);
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
// Error or status message
|
|
if let Some(error) = &app.error_message {
|
|
let error_widget = Paragraph::new(error.as_str())
|
|
.style(Style::default().fg(Color::Red))
|
|
.alignment(Alignment::Center);
|
|
f.render_widget(error_widget, auth_chunks[3]);
|
|
} else if let Some(status) = &app.status_message {
|
|
let status_widget = Paragraph::new(status.as_str())
|
|
.style(Style::default().fg(Color::Yellow))
|
|
.alignment(Alignment::Center);
|
|
f.render_widget(status_widget, auth_chunks[3]);
|
|
}
|
|
}
|