style: auto-format entire codebase with cargo fmt (stable rustfmt.toml)
Some checks failed
ci/woodpecker/pr/check Pipeline failed
CI / Check (pull_request) Has been cancelled
CI / Format (pull_request) Has been cancelled
CI / Clippy (pull_request) Has been cancelled
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Build (ubuntu-latest) (pull_request) Has been cancelled
CI / Build (windows-latest) (pull_request) Has been cancelled
Some checks failed
ci/woodpecker/pr/check Pipeline failed
CI / Check (pull_request) Has been cancelled
CI / Format (pull_request) Has been cancelled
CI / Clippy (pull_request) Has been cancelled
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Build (ubuntu-latest) (pull_request) Has been cancelled
CI / Build (windows-latest) (pull_request) Has been cancelled
This commit is contained in:
@@ -3,10 +3,10 @@
|
||||
//! Renders message bubbles grouped by date/sender, pinned bar, and delegates
|
||||
//! to modals (search, pinned, reactions, delete) and compose_bar.
|
||||
|
||||
use crate::app::App;
|
||||
use crate::app::methods::{messages::MessageMethods, modal::ModalMethods, search::SearchMethods};
|
||||
use crate::tdlib::TdClientTrait;
|
||||
use crate::app::App;
|
||||
use crate::message_grouping::{group_messages, MessageGroup};
|
||||
use crate::tdlib::TdClientTrait;
|
||||
use crate::ui::components;
|
||||
use crate::ui::{compose_bar, modals};
|
||||
use ratatui::{
|
||||
@@ -18,7 +18,12 @@ use ratatui::{
|
||||
};
|
||||
|
||||
/// Рендерит заголовок чата с typing status
|
||||
fn render_chat_header<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &App<T>, chat: &crate::tdlib::ChatInfo) {
|
||||
fn render_chat_header<T: TdClientTrait>(
|
||||
f: &mut Frame,
|
||||
area: Rect,
|
||||
app: &App<T>,
|
||||
chat: &crate::tdlib::ChatInfo,
|
||||
) {
|
||||
let typing_action = app
|
||||
.td_client
|
||||
.typing_status()
|
||||
@@ -34,10 +39,7 @@ fn render_chat_header<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &App<T>,
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)];
|
||||
if let Some(username) = &chat.username {
|
||||
spans.push(Span::styled(
|
||||
format!(" {}", username),
|
||||
Style::default().fg(Color::Gray),
|
||||
));
|
||||
spans.push(Span::styled(format!(" {}", username), Style::default().fg(Color::Gray)));
|
||||
}
|
||||
spans.push(Span::styled(
|
||||
format!(" {}", action),
|
||||
@@ -90,8 +92,7 @@ fn render_pinned_bar<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &App<T>)
|
||||
Span::raw(" ".repeat(padding)),
|
||||
Span::styled(pinned_hint, Style::default().fg(Color::Gray)),
|
||||
]);
|
||||
let pinned_bar =
|
||||
Paragraph::new(pinned_line).style(Style::default().bg(Color::Rgb(40, 20, 40)));
|
||||
let pinned_bar = Paragraph::new(pinned_line).style(Style::default().bg(Color::Rgb(40, 20, 40)));
|
||||
f.render_widget(pinned_bar, area);
|
||||
}
|
||||
|
||||
@@ -104,9 +105,7 @@ pub(super) struct WrappedLine {
|
||||
/// (используется только для search/pinned режимов, основной рендеринг через message_bubble)
|
||||
pub(super) fn wrap_text_with_offsets(text: &str, max_width: usize) -> Vec<WrappedLine> {
|
||||
if max_width == 0 {
|
||||
return vec![WrappedLine {
|
||||
text: text.to_string(),
|
||||
}];
|
||||
return vec![WrappedLine { text: text.to_string() }];
|
||||
}
|
||||
|
||||
let mut result = Vec::new();
|
||||
@@ -131,9 +130,7 @@ pub(super) fn wrap_text_with_offsets(text: &str, max_width: usize) -> Vec<Wrappe
|
||||
current_line.push_str(&word);
|
||||
current_width += 1 + word_width;
|
||||
} else {
|
||||
result.push(WrappedLine {
|
||||
text: current_line,
|
||||
});
|
||||
result.push(WrappedLine { text: current_line });
|
||||
current_line = word;
|
||||
current_width = word_width;
|
||||
}
|
||||
@@ -155,23 +152,17 @@ pub(super) fn wrap_text_with_offsets(text: &str, max_width: usize) -> Vec<Wrappe
|
||||
current_line.push(' ');
|
||||
current_line.push_str(&word);
|
||||
} else {
|
||||
result.push(WrappedLine {
|
||||
text: current_line,
|
||||
});
|
||||
result.push(WrappedLine { text: current_line });
|
||||
current_line = word;
|
||||
}
|
||||
}
|
||||
|
||||
if !current_line.is_empty() {
|
||||
result.push(WrappedLine {
|
||||
text: current_line,
|
||||
});
|
||||
result.push(WrappedLine { text: current_line });
|
||||
}
|
||||
|
||||
if result.is_empty() {
|
||||
result.push(WrappedLine {
|
||||
text: String::new(),
|
||||
});
|
||||
result.push(WrappedLine { text: String::new() });
|
||||
}
|
||||
|
||||
result
|
||||
@@ -208,10 +199,7 @@ fn render_message_list<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &mut Ap
|
||||
is_first_date = false;
|
||||
is_first_sender = true; // Сбрасываем счётчик заголовков после даты
|
||||
}
|
||||
MessageGroup::SenderHeader {
|
||||
is_outgoing,
|
||||
sender_name,
|
||||
} => {
|
||||
MessageGroup::SenderHeader { is_outgoing, sender_name } => {
|
||||
// Рендерим заголовок отправителя
|
||||
lines.extend(components::render_sender_header(
|
||||
is_outgoing,
|
||||
@@ -240,9 +228,16 @@ fn render_message_list<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &mut Ap
|
||||
// Собираем deferred image renders для всех загруженных фото
|
||||
#[cfg(feature = "images")]
|
||||
if let Some(photo) = msg.photo_info() {
|
||||
if let crate::tdlib::PhotoDownloadState::Downloaded(path) = &photo.download_state {
|
||||
let inline_width = content_width.min(crate::constants::INLINE_IMAGE_MAX_WIDTH);
|
||||
let img_height = components::calculate_image_height(photo.width, photo.height, inline_width);
|
||||
if let crate::tdlib::PhotoDownloadState::Downloaded(path) =
|
||||
&photo.download_state
|
||||
{
|
||||
let inline_width =
|
||||
content_width.min(crate::constants::INLINE_IMAGE_MAX_WIDTH);
|
||||
let img_height = components::calculate_image_height(
|
||||
photo.width,
|
||||
photo.height,
|
||||
inline_width,
|
||||
);
|
||||
let img_width = inline_width as u16;
|
||||
let bubble_len = bubble_lines.len();
|
||||
let placeholder_start = lines.len() + bubble_len - img_height as usize;
|
||||
@@ -352,7 +347,8 @@ fn render_message_list<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &mut Ap
|
||||
use ratatui_image::StatefulImage;
|
||||
|
||||
// THROTTLING: Рендерим изображения максимум 15 FPS (каждые 66ms)
|
||||
let should_render_images = app.last_image_render_time
|
||||
let should_render_images = app
|
||||
.last_image_render_time
|
||||
.map(|t| t.elapsed() > std::time::Duration::from_millis(66))
|
||||
.unwrap_or(true);
|
||||
|
||||
@@ -384,7 +380,7 @@ fn render_message_list<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &mut Ap
|
||||
if let Some(renderer) = &mut app.inline_image_renderer {
|
||||
// Загружаем только если видимо (early return если уже в кеше)
|
||||
let _ = renderer.load_image(d.message_id, &d.photo_path);
|
||||
|
||||
|
||||
if let Some(protocol) = renderer.get_protocol(&d.message_id) {
|
||||
f.render_stateful_widget(StatefulImage::default(), img_rect, protocol);
|
||||
}
|
||||
@@ -487,14 +483,9 @@ pub fn render<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &mut App<T>) {
|
||||
}
|
||||
|
||||
// Модалка выбора реакции
|
||||
if let crate::app::ChatState::ReactionPicker {
|
||||
available_reactions,
|
||||
selected_index,
|
||||
..
|
||||
} = &app.chat_state
|
||||
if let crate::app::ChatState::ReactionPicker { available_reactions, selected_index, .. } =
|
||||
&app.chat_state
|
||||
{
|
||||
modals::render_reaction_picker(f, area, available_reactions, *selected_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user