//! Account switcher modal //! //! Renders a centered popup with account list (SelectAccount) or //! new account name input (AddAccount). use crate::app::{AccountSwitcherState, App}; use crate::tdlib::TdClientTrait; use ratatui::{ layout::Rect, style::{Color, Modifier, Style}, text::{Line, Span}, widgets::{Block, Borders, Clear, Paragraph}, Frame, }; /// Renders the account switcher modal overlay. pub fn render(f: &mut Frame, area: Rect, app: &App) { let Some(state) = &app.account_switcher else { return; }; match state { AccountSwitcherState::SelectAccount { accounts, selected_index, current_account } => { render_select_account(f, area, accounts, *selected_index, current_account); } AccountSwitcherState::AddAccount { name_input, cursor_position, error } => { render_add_account(f, area, name_input, *cursor_position, error.as_deref()); } } } fn render_select_account( f: &mut Frame, area: Rect, accounts: &[crate::accounts::AccountProfile], selected_index: usize, current_account: &str, ) { let mut lines: Vec = Vec::new(); lines.push(Line::from("")); for (idx, account) in accounts.iter().enumerate() { let is_selected = idx == selected_index; let is_current = account.name == current_account; let marker = if is_current { "● " } else { " " }; let suffix = if is_current { " (текущий)" } else { "" }; let display = format!("{}{} ({}){}", marker, account.name, account.display_name, suffix); let style = if is_selected { Style::default() .fg(Color::Yellow) .add_modifier(Modifier::BOLD) } else if is_current { Style::default().fg(Color::Green) } else { Style::default().fg(Color::White) }; lines.push(Line::from(Span::styled(format!(" {}", display), style))); } // Separator lines.push(Line::from(Span::styled( " ──────────────────────", Style::default().fg(Color::DarkGray), ))); // Add account item let add_selected = selected_index == accounts.len(); let add_style = if add_selected { Style::default() .fg(Color::Yellow) .add_modifier(Modifier::BOLD) } else { Style::default().fg(Color::Cyan) }; lines.push(Line::from(Span::styled(" + Добавить аккаунт", add_style))); lines.push(Line::from("")); // Help bar lines.push(Line::from(vec![ Span::styled(" j/k ", Style::default().fg(Color::Yellow)), Span::styled("Nav", Style::default().fg(Color::DarkGray)), Span::raw(" "), Span::styled(" Enter ", Style::default().fg(Color::Green)), Span::styled("Select", Style::default().fg(Color::DarkGray)), Span::raw(" "), Span::styled(" a ", Style::default().fg(Color::Cyan)), Span::styled("Add", Style::default().fg(Color::DarkGray)), Span::raw(" "), Span::styled(" Esc ", Style::default().fg(Color::Red)), Span::styled("Close", Style::default().fg(Color::DarkGray)), ])); // Calculate dynamic height: header(3) + accounts + separator(1) + add(1) + empty(1) + help(1) + footer(1) let content_height = (accounts.len() as u16) + 7; let height = content_height.min(area.height.saturating_sub(4)); let width = 40u16.min(area.width.saturating_sub(4)); let x = area.x + (area.width.saturating_sub(width)) / 2; let y = area.y + (area.height.saturating_sub(height)) / 2; let modal_area = Rect::new(x, y, width, height); f.render_widget(Clear, modal_area); let modal = Paragraph::new(lines).block( Block::default() .borders(Borders::ALL) .border_style(Style::default().fg(Color::Cyan)) .title(" АККАУНТЫ ") .title_style( Style::default() .fg(Color::Cyan) .add_modifier(Modifier::BOLD), ), ); f.render_widget(modal, modal_area); } fn render_add_account( f: &mut Frame, area: Rect, name_input: &str, _cursor_position: usize, error: Option<&str>, ) { let mut lines: Vec = Vec::new(); lines.push(Line::from("")); // Input field let input_display = if name_input.is_empty() { Span::styled("_", Style::default().fg(Color::DarkGray)) } else { Span::styled(format!("{}_", name_input), Style::default().fg(Color::White)) }; lines.push(Line::from(vec![ Span::styled(" Имя: ", Style::default().fg(Color::Cyan)), input_display, ])); // Hint lines.push(Line::from(Span::styled( " (a-z, 0-9, -, _)", Style::default().fg(Color::DarkGray), ))); lines.push(Line::from("")); // Error if let Some(err) = error { lines.push(Line::from(Span::styled(format!(" {}", err), Style::default().fg(Color::Red)))); lines.push(Line::from("")); } // Help bar lines.push(Line::from(vec![ Span::styled(" Enter ", Style::default().fg(Color::Green)), Span::styled("Create", Style::default().fg(Color::DarkGray)), Span::raw(" "), Span::styled(" Esc ", Style::default().fg(Color::Red)), Span::styled("Back", Style::default().fg(Color::DarkGray)), ])); let height = if error.is_some() { 10 } else { 8 }; let height = (height as u16).min(area.height.saturating_sub(4)); let width = 40u16.min(area.width.saturating_sub(4)); let x = area.x + (area.width.saturating_sub(width)) / 2; let y = area.y + (area.height.saturating_sub(height)) / 2; let modal_area = Rect::new(x, y, width, height); f.render_widget(Clear, modal_area); let modal = Paragraph::new(lines).block( Block::default() .borders(Borders::ALL) .border_style(Style::default().fg(Color::Cyan)) .title(" НОВЫЙ АККАУНТ ") .title_style( Style::default() .fg(Color::Cyan) .add_modifier(Modifier::BOLD), ), ); f.render_widget(modal, modal_area); }