188 lines
7.2 KiB
Rust
188 lines
7.2 KiB
Rust
//! Compose bar / input box rendering
|
|
|
|
use crate::app::App;
|
|
use crate::app::InputMode;
|
|
use crate::app::methods::{compose::ComposeMethods, messages::MessageMethods};
|
|
use crate::tdlib::TdClientTrait;
|
|
use crate::ui::components;
|
|
use ratatui::{
|
|
layout::Rect,
|
|
style::{Color, Modifier, Style},
|
|
text::{Line, Span},
|
|
widgets::{Block, Borders, Paragraph},
|
|
Frame,
|
|
};
|
|
|
|
/// Renders input field with cursor at the specified position
|
|
fn render_input_with_cursor(
|
|
prefix: &str,
|
|
text: &str,
|
|
cursor_pos: usize,
|
|
color: Color,
|
|
) -> Line<'static> {
|
|
components::render_input_field(prefix, text, cursor_pos, color)
|
|
}
|
|
|
|
/// Renders input box with support for different modes (forward/select/edit/reply/normal)
|
|
pub fn render<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &App<T>) {
|
|
let (input_line, input_title): (Line, &str) = if app.is_forwarding() {
|
|
// Режим пересылки - показываем превью сообщения
|
|
let forward_preview = app
|
|
.get_forwarding_message()
|
|
.map(|m| {
|
|
let text_preview: String = m.text().chars().take(40).collect();
|
|
let ellipsis = if m.text().chars().count() > 40 {
|
|
"..."
|
|
} else {
|
|
""
|
|
};
|
|
format!("↪ {}{}", text_preview, ellipsis)
|
|
})
|
|
.unwrap_or_else(|| "↪ ...".to_string());
|
|
|
|
let line = Line::from(Span::styled(forward_preview, Style::default().fg(Color::Cyan)));
|
|
(line, " Выберите чат ← ")
|
|
} else if app.is_selecting_message() {
|
|
// Режим выбора сообщения - подсказка зависит от возможностей
|
|
let selected_msg = app.get_selected_message();
|
|
let can_edit = selected_msg
|
|
.as_ref()
|
|
.map(|m| m.can_be_edited() && m.is_outgoing())
|
|
.unwrap_or(false);
|
|
let can_delete = selected_msg
|
|
.as_ref()
|
|
.map(|m| m.can_be_deleted_only_for_self() || m.can_be_deleted_for_all_users())
|
|
.unwrap_or(false);
|
|
|
|
let hint = match (can_edit, can_delete) {
|
|
(true, true) => "↑↓ · Enter ред. · r ответ · f перслть · y копир. · d удал. · Esc",
|
|
(true, false) => "↑↓ · Enter ред. · r ответ · f переслть · y копир. · Esc",
|
|
(false, true) => "↑↓ · r ответ · f переслать · y копир. · d удалить · Esc",
|
|
(false, false) => "↑↓ · r ответить · f переслать · y копировать · Esc",
|
|
};
|
|
(
|
|
Line::from(Span::styled(hint, Style::default().fg(Color::Cyan))),
|
|
" Выбор сообщения ",
|
|
)
|
|
} else if app.is_editing() {
|
|
// Режим редактирования
|
|
if app.message_input.is_empty() {
|
|
let line = Line::from(vec![
|
|
Span::raw("✏ "),
|
|
Span::styled("█", Style::default().fg(Color::Magenta)),
|
|
Span::styled(" Введите новый текст...", Style::default().fg(Color::Gray)),
|
|
]);
|
|
(line, " Редактирование (Esc отмена) ")
|
|
} else {
|
|
let line = render_input_with_cursor(
|
|
"✏ ",
|
|
&app.message_input,
|
|
app.cursor_position,
|
|
Color::Magenta,
|
|
);
|
|
(line, " Редактирование (Esc отмена) ")
|
|
}
|
|
} else if app.is_replying() {
|
|
// Режим ответа на сообщение
|
|
let reply_preview = app
|
|
.get_replying_to_message()
|
|
.map(|m| {
|
|
let sender = if m.is_outgoing() {
|
|
"Вы"
|
|
} else {
|
|
m.sender_name()
|
|
};
|
|
let text_preview: String = m.text().chars().take(30).collect();
|
|
let ellipsis = if m.text().chars().count() > 30 {
|
|
"..."
|
|
} else {
|
|
""
|
|
};
|
|
format!("{}: {}{}", sender, text_preview, ellipsis)
|
|
})
|
|
.unwrap_or_else(|| "...".to_string());
|
|
|
|
if app.message_input.is_empty() {
|
|
let line = Line::from(vec![
|
|
Span::styled("↪ ", Style::default().fg(Color::Cyan)),
|
|
Span::styled(reply_preview, Style::default().fg(Color::Gray)),
|
|
Span::raw(" "),
|
|
Span::styled("█", Style::default().fg(Color::Yellow)),
|
|
]);
|
|
(line, " Ответ (Esc отмена) ")
|
|
} else {
|
|
let short_preview: String = reply_preview.chars().take(15).collect();
|
|
let prefix = format!("↪ {} > ", short_preview);
|
|
let line = render_input_with_cursor(
|
|
&prefix,
|
|
&app.message_input,
|
|
app.cursor_position,
|
|
Color::Yellow,
|
|
);
|
|
(line, " Ответ (Esc отмена) ")
|
|
}
|
|
} else if app.input_mode == InputMode::Normal {
|
|
// Normal mode — dim, no cursor
|
|
if app.message_input.is_empty() {
|
|
let line = Line::from(vec![
|
|
Span::styled("> Press i to type...", Style::default().fg(Color::DarkGray)),
|
|
]);
|
|
(line, "")
|
|
} else {
|
|
let draft_preview: String = app.message_input.chars().take(60).collect();
|
|
let ellipsis = if app.message_input.chars().count() > 60 { "..." } else { "" };
|
|
let line = Line::from(Span::styled(
|
|
format!("> {}{}", draft_preview, ellipsis),
|
|
Style::default().fg(Color::DarkGray),
|
|
));
|
|
(line, "")
|
|
}
|
|
} else {
|
|
// Insert mode — active, with cursor
|
|
if app.message_input.is_empty() {
|
|
let line = Line::from(vec![
|
|
Span::raw("> "),
|
|
Span::styled("█", Style::default().fg(Color::Yellow)),
|
|
Span::styled(" Введите сообщение...", Style::default().fg(Color::Gray)),
|
|
]);
|
|
(line, "")
|
|
} else {
|
|
let line = render_input_with_cursor(
|
|
"> ",
|
|
&app.message_input,
|
|
app.cursor_position,
|
|
Color::Yellow,
|
|
);
|
|
(line, "")
|
|
}
|
|
};
|
|
|
|
let input_block = if input_title.is_empty() {
|
|
let border_style = if app.input_mode == InputMode::Insert {
|
|
Style::default().fg(Color::Green)
|
|
} else {
|
|
Style::default().fg(Color::DarkGray)
|
|
};
|
|
Block::default().borders(Borders::ALL).border_style(border_style)
|
|
} else {
|
|
let title_color = if app.is_replying() || app.is_forwarding() {
|
|
Color::Cyan
|
|
} else {
|
|
Color::Magenta
|
|
};
|
|
Block::default()
|
|
.borders(Borders::ALL)
|
|
.title(input_title)
|
|
.title_style(
|
|
Style::default()
|
|
.fg(title_color)
|
|
.add_modifier(Modifier::BOLD),
|
|
)
|
|
};
|
|
|
|
let input = Paragraph::new(input_line)
|
|
.block(input_block)
|
|
.wrap(ratatui::widgets::Wrap { trim: false });
|
|
f.render_widget(input, area);
|
|
}
|