use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style}, text::{Line, Span}, widgets::{Block, Borders, Paragraph}, Frame, }; use crate::app::App; use crate::tdlib::client::ProfileInfo; /// Рендерит режим просмотра профиля pub fn render(f: &mut Frame, area: Rect, app: &App, profile: &ProfileInfo) { // Проверяем, показывать ли модалку подтверждения let confirmation_step = app.get_leave_group_confirmation_step(); if confirmation_step > 0 { render_leave_confirmation_modal(f, area, confirmation_step); return; } let chunks = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Length(3), // Header Constraint::Min(0), // Profile info Constraint::Length(3), // Actions help ]) .split(area); // Header let header_text = format!("👤 ПРОФИЛЬ: {}", profile.title); let header = Paragraph::new(header_text) .block( Block::default() .borders(Borders::ALL) .border_style(Style::default().fg(Color::Cyan)) ) .style(Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)); f.render_widget(header, chunks[0]); // Profile info let mut lines: Vec = Vec::new(); // Тип чата lines.push(Line::from(vec![ Span::styled("Тип: ", Style::default().fg(Color::Gray)), Span::styled(&profile.chat_type, Style::default().fg(Color::White)), ])); lines.push(Line::from("")); // ID lines.push(Line::from(vec![ Span::styled("ID: ", Style::default().fg(Color::Gray)), Span::styled(format!("{}", profile.chat_id), Style::default().fg(Color::White)), ])); lines.push(Line::from("")); // Username if let Some(username) = &profile.username { lines.push(Line::from(vec![ Span::styled("Username: ", Style::default().fg(Color::Gray)), Span::styled(username, Style::default().fg(Color::Cyan)), ])); lines.push(Line::from("")); } // Phone number (только для личных чатов) if let Some(phone) = &profile.phone_number { lines.push(Line::from(vec![ Span::styled("Телефон: ", Style::default().fg(Color::Gray)), Span::styled(phone, Style::default().fg(Color::White)), ])); lines.push(Line::from("")); } // Online status (только для личных чатов) if let Some(status) = &profile.online_status { lines.push(Line::from(vec![ Span::styled("Статус: ", Style::default().fg(Color::Gray)), Span::styled(status, Style::default().fg(Color::Green)), ])); lines.push(Line::from("")); } // Bio (только для личных чатов) if let Some(bio) = &profile.bio { lines.push(Line::from(vec![ Span::styled("О себе: ", Style::default().fg(Color::Gray)), ])); // Разбиваем bio на строки если длинное let bio_lines: Vec<&str> = bio.lines().collect(); for bio_line in bio_lines { lines.push(Line::from(Span::styled(bio_line, Style::default().fg(Color::White)))); } lines.push(Line::from("")); } // Member count (для групп/каналов) if let Some(count) = profile.member_count { lines.push(Line::from(vec![ Span::styled("Участников: ", Style::default().fg(Color::Gray)), Span::styled(format!("{}", count), Style::default().fg(Color::White)), ])); lines.push(Line::from("")); } // Description (для групп/каналов) if let Some(desc) = &profile.description { lines.push(Line::from(vec![ Span::styled("Описание: ", Style::default().fg(Color::Gray)), ])); let desc_lines: Vec<&str> = desc.lines().collect(); for desc_line in desc_lines { lines.push(Line::from(Span::styled(desc_line, Style::default().fg(Color::White)))); } lines.push(Line::from("")); } // Invite link (для групп/каналов) if let Some(link) = &profile.invite_link { lines.push(Line::from(vec![ Span::styled("Ссылка: ", Style::default().fg(Color::Gray)), Span::styled(link, Style::default().fg(Color::Blue).add_modifier(Modifier::UNDERLINED)), ])); lines.push(Line::from("")); } // Разделитель lines.push(Line::from("────────────────────────────────")); lines.push(Line::from("")); // Действия lines.push(Line::from(Span::styled( "Действия:", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD), ))); lines.push(Line::from("")); let actions = get_available_actions(profile); for (idx, action) in actions.iter().enumerate() { let is_selected = idx == app.selected_profile_action; let marker = if is_selected { "▶ " } else { " " }; let style = if is_selected { Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD) } else { Style::default().fg(Color::White) }; lines.push(Line::from(vec![ Span::styled(marker, Style::default().fg(Color::Yellow)), Span::styled(*action, style), ])); } let info_widget = Paragraph::new(lines) .block( Block::default() .borders(Borders::ALL) .border_style(Style::default().fg(Color::Cyan)) ) .scroll((0, 0)); f.render_widget(info_widget, chunks[1]); // Help bar let help_line = Line::from(vec![ Span::styled(" ↑↓ ", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)), Span::raw("навигация"), Span::raw(" "), Span::styled(" Enter ", Style::default().fg(Color::Green).add_modifier(Modifier::BOLD)), Span::raw("выбрать"), Span::raw(" "), Span::styled(" Esc ", Style::default().fg(Color::Red).add_modifier(Modifier::BOLD)), Span::raw("выход"), ]); let help = Paragraph::new(help_line) .block( Block::default() .borders(Borders::ALL) .border_style(Style::default().fg(Color::Cyan)) ) .alignment(Alignment::Center); f.render_widget(help, chunks[2]); } /// Получить список доступных действий fn get_available_actions(profile: &ProfileInfo) -> Vec<&'static str> { let mut actions = vec![]; if profile.username.is_some() { actions.push("Открыть в браузере"); } actions.push("Скопировать ID"); if profile.is_group { actions.push("Покинуть группу"); } actions } /// Рендерит модалку подтверждения выхода из группы fn render_leave_confirmation_modal(f: &mut Frame, area: Rect, step: u8) { // Затемняем фон let modal_area = centered_rect(60, 30, area); let text = if step == 1 { "Вы хотите выйти из группы?" } else { "Вы ТОЧНО хотите выйти из группы?!?!?" }; let lines = vec![ Line::from(""), Line::from(Span::styled( text, Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD), )), Line::from(""), Line::from(""), Line::from(vec![ Span::styled("y/н/Enter", Style::default().fg(Color::Green).add_modifier(Modifier::BOLD)), Span::raw(" — да "), Span::styled("n/т/Esc", Style::default().fg(Color::Red).add_modifier(Modifier::BOLD)), Span::raw(" — нет"), ]), ]; let modal = Paragraph::new(lines) .block( Block::default() .borders(Borders::ALL) .border_style(Style::default().fg(Color::Red)) .title(" ⚠ ВНИМАНИЕ ") .title_style(Style::default().fg(Color::Red).add_modifier(Modifier::BOLD)) ) .alignment(Alignment::Center); f.render_widget(modal, modal_area); } /// Вспомогательная функция для центрирования прямоугольника fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { let popup_layout = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Percentage((100 - percent_y) / 2), Constraint::Percentage(percent_y), Constraint::Percentage((100 - percent_y) / 2), ]) .split(r); Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Percentage((100 - percent_x) / 2), Constraint::Percentage(percent_x), Constraint::Percentage((100 - percent_x) / 2), ]) .split(popup_layout[1])[1] }