fix: resolve all 23 clippy warnings
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:
@@ -14,9 +14,10 @@ pub enum InputMode {
|
||||
}
|
||||
|
||||
/// Состояния чата - взаимоисключающие режимы работы с чатом
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub enum ChatState {
|
||||
/// Обычный режим - просмотр сообщений, набор текста
|
||||
#[default]
|
||||
Normal,
|
||||
|
||||
/// Выбор сообщения для действия (edit/delete/reply/forward/reaction)
|
||||
@@ -90,12 +91,6 @@ pub enum ChatState {
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for ChatState {
|
||||
fn default() -> Self {
|
||||
ChatState::Normal
|
||||
}
|
||||
}
|
||||
|
||||
impl ChatState {
|
||||
/// Проверка: находимся в режиме выбора сообщения
|
||||
pub fn is_message_selection(&self) -> bool {
|
||||
|
||||
@@ -110,8 +110,19 @@ pub struct Keybindings {
|
||||
}
|
||||
|
||||
impl Keybindings {
|
||||
/// Создаёт дефолтную конфигурацию
|
||||
pub fn default() -> Self {
|
||||
/// Ищет команду по клавише
|
||||
pub fn get_command(&self, event: &KeyEvent) -> Option<Command> {
|
||||
for (command, bindings) in &self.bindings {
|
||||
if bindings.iter().any(|binding| binding.matches(event)) {
|
||||
return Some(*command);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Keybindings {
|
||||
fn default() -> Self {
|
||||
let mut bindings = HashMap::new();
|
||||
|
||||
// Navigation
|
||||
@@ -301,22 +312,6 @@ impl Keybindings {
|
||||
|
||||
Self { bindings }
|
||||
}
|
||||
|
||||
/// Ищет команду по клавише
|
||||
pub fn get_command(&self, event: &KeyEvent) -> Option<Command> {
|
||||
for (command, bindings) in &self.bindings {
|
||||
if bindings.iter().any(|binding| binding.matches(event)) {
|
||||
return Some(*command);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Keybindings {
|
||||
fn default() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Сериализация KeyModifiers
|
||||
@@ -428,8 +423,8 @@ mod key_code_serde {
|
||||
return Ok(KeyCode::Char(c));
|
||||
}
|
||||
|
||||
if s.starts_with("F") {
|
||||
let n = s[1..].parse().map_err(serde::de::Error::custom)?;
|
||||
if let Some(suffix) = s.strip_prefix("F") {
|
||||
let n = suffix.parse().map_err(serde::de::Error::custom)?;
|
||||
return Ok(KeyCode::F(n));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ pub use keybindings::{Command, Keybindings};
|
||||
/// println!("Timezone: {}", config.general.timezone);
|
||||
/// println!("Incoming color: {}", config.colors.incoming_message);
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
/// Общие настройки (timezone и т.д.).
|
||||
#[serde(default)]
|
||||
@@ -260,19 +260,6 @@ impl Default for NotificationsConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
general: GeneralConfig::default(),
|
||||
colors: ColorsConfig::default(),
|
||||
keybindings: Keybindings::default(),
|
||||
notifications: NotificationsConfig::default(),
|
||||
images: ImagesConfig::default(),
|
||||
audio: AudioConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -126,22 +126,22 @@ pub fn format_text_with_entities(
|
||||
let start = entity.offset as usize;
|
||||
let end = (entity.offset + entity.length) as usize;
|
||||
|
||||
for i in start..end.min(chars.len()) {
|
||||
for item in char_styles.iter_mut().take(end.min(chars.len())).skip(start) {
|
||||
match &entity.r#type {
|
||||
TextEntityType::Bold => char_styles[i].bold = true,
|
||||
TextEntityType::Italic => char_styles[i].italic = true,
|
||||
TextEntityType::Underline => char_styles[i].underline = true,
|
||||
TextEntityType::Strikethrough => char_styles[i].strikethrough = true,
|
||||
TextEntityType::Bold => item.bold = true,
|
||||
TextEntityType::Italic => item.italic = true,
|
||||
TextEntityType::Underline => item.underline = true,
|
||||
TextEntityType::Strikethrough => item.strikethrough = true,
|
||||
TextEntityType::Code | TextEntityType::Pre | TextEntityType::PreCode(_) => {
|
||||
char_styles[i].code = true
|
||||
item.code = true
|
||||
}
|
||||
TextEntityType::Spoiler => char_styles[i].spoiler = true,
|
||||
TextEntityType::Spoiler => item.spoiler = true,
|
||||
TextEntityType::Url
|
||||
| TextEntityType::TextUrl(_)
|
||||
| TextEntityType::EmailAddress
|
||||
| TextEntityType::PhoneNumber => char_styles[i].url = true,
|
||||
| TextEntityType::PhoneNumber => item.url = true,
|
||||
TextEntityType::Mention | TextEntityType::MentionName(_) => {
|
||||
char_styles[i].mention = true
|
||||
item.mention = true
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ pub async fn edit_message<T: TdClientTrait>(
|
||||
.interactions
|
||||
.reply_to
|
||||
.as_ref()
|
||||
.map_or(true, |r| r.sender_name == "Unknown")
|
||||
.is_none_or(|r| r.sender_name == "Unknown")
|
||||
{
|
||||
edited_msg.interactions.reply_to = Some(old_reply);
|
||||
}
|
||||
@@ -780,7 +780,7 @@ async fn handle_play_voice<T: TdClientTrait>(app: &mut App<T>) {
|
||||
return handle_play_voice_from_path(
|
||||
app,
|
||||
&found_path,
|
||||
&voice,
|
||||
voice,
|
||||
&msg,
|
||||
)
|
||||
.await;
|
||||
@@ -799,7 +799,7 @@ async fn handle_play_voice<T: TdClientTrait>(app: &mut App<T>) {
|
||||
let _ = cache.store(&file_id.to_string(), Path::new(&audio_path));
|
||||
}
|
||||
|
||||
handle_play_voice_from_path(app, &audio_path, &voice, &msg).await;
|
||||
handle_play_voice_from_path(app, &audio_path, voice, &msg).await;
|
||||
}
|
||||
VoiceDownloadState::Downloading => {
|
||||
app.status_message = Some("Загрузка голосового...".to_string());
|
||||
@@ -809,7 +809,7 @@ async fn handle_play_voice<T: TdClientTrait>(app: &mut App<T>) {
|
||||
let cache_key = file_id.to_string();
|
||||
if let Some(cached_path) = app.voice_cache.as_mut().and_then(|c| c.get(&cache_key)) {
|
||||
let path_str = cached_path.to_string_lossy().to_string();
|
||||
handle_play_voice_from_path(app, &path_str, &voice, &msg).await;
|
||||
handle_play_voice_from_path(app, &path_str, voice, &msg).await;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -822,7 +822,7 @@ async fn handle_play_voice<T: TdClientTrait>(app: &mut App<T>) {
|
||||
let _ = cache.store(&cache_key, std::path::Path::new(&path));
|
||||
}
|
||||
|
||||
handle_play_voice_from_path(app, &path, &voice, &msg).await;
|
||||
handle_play_voice_from_path(app, &path, voice, &msg).await;
|
||||
}
|
||||
Err(e) => {
|
||||
app.error_message = Some(format!("Ошибка загрузки: {}", e));
|
||||
|
||||
@@ -17,7 +17,7 @@ pub enum MessageGroup {
|
||||
sender_name: String,
|
||||
},
|
||||
/// Сообщение
|
||||
Message(MessageInfo),
|
||||
Message(Box<MessageInfo>),
|
||||
/// Альбом (группа фото с одинаковым media_album_id)
|
||||
Album(Vec<MessageInfo>),
|
||||
}
|
||||
@@ -78,7 +78,7 @@ pub fn group_messages(messages: &[MessageInfo]) -> Vec<MessageGroup> {
|
||||
result.push(MessageGroup::Album(std::mem::take(acc)));
|
||||
} else {
|
||||
// Одно сообщение — не альбом
|
||||
result.push(MessageGroup::Message(acc.remove(0)));
|
||||
result.push(MessageGroup::Message(Box::new(acc.remove(0))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ pub fn group_messages(messages: &[MessageInfo]) -> Vec<MessageGroup> {
|
||||
|
||||
// Обычное сообщение (не альбом) — flush аккумулятор
|
||||
flush_album(&mut album_acc, &mut result);
|
||||
result.push(MessageGroup::Message(msg.clone()));
|
||||
result.push(MessageGroup::Message(Box::new(msg.clone())));
|
||||
}
|
||||
|
||||
// Flush оставшийся аккумулятор
|
||||
|
||||
@@ -490,19 +490,6 @@ impl TdClient {
|
||||
|
||||
// ==================== Helper методы для упрощения обработки updates ====================
|
||||
|
||||
/// Находит мутабельную ссылку на чат по ID.
|
||||
///
|
||||
/// Упрощает повторяющийся паттерн `self.chats_mut().iter_mut().find(...)`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата для поиска
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Some(&mut ChatInfo)` - если чат найден
|
||||
/// * `None` - если чат не найден
|
||||
|
||||
/// Обрабатываем одно обновление от TDLib
|
||||
pub fn handle_update(&mut self, update: Update) {
|
||||
match update {
|
||||
|
||||
@@ -116,7 +116,7 @@ pub fn extract_reply_info(client: &TdClient, message: &TdMessage) -> Option<Repl
|
||||
let sender_name = reply
|
||||
.origin
|
||||
.as_ref()
|
||||
.map(|origin| get_origin_sender_name(origin))
|
||||
.map(get_origin_sender_name)
|
||||
.unwrap_or_else(|| {
|
||||
// Пробуем найти оригинальное сообщение в текущем списке
|
||||
let reply_msg_id = MessageId::new(reply.message_id);
|
||||
|
||||
@@ -206,11 +206,9 @@ impl MessageManager {
|
||||
match result {
|
||||
Ok(tdlib_rs::enums::Messages::Messages(messages_obj)) => {
|
||||
let mut messages = Vec::new();
|
||||
for msg_opt in messages_obj.messages.iter().rev() {
|
||||
if let Some(msg) = msg_opt {
|
||||
if let Some(info) = self.convert_message(msg).await {
|
||||
messages.push(info);
|
||||
}
|
||||
for msg in messages_obj.messages.iter().rev().flatten() {
|
||||
if let Some(info) = self.convert_message(msg).await {
|
||||
messages.push(info);
|
||||
}
|
||||
}
|
||||
Ok(messages)
|
||||
|
||||
@@ -155,6 +155,7 @@ pub struct MessageInfo {
|
||||
|
||||
impl MessageInfo {
|
||||
/// Создать новое сообщение
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
id: MessageId,
|
||||
sender_name: String,
|
||||
|
||||
@@ -105,7 +105,7 @@ pub fn handle_chat_action_update(client: &mut TdClient, update: UpdateChatAction
|
||||
ChatAction::ChoosingSticker => Some("выбирает стикер...".to_string()),
|
||||
ChatAction::RecordingVideoNote => Some("записывает видеосообщение...".to_string()),
|
||||
ChatAction::UploadingVideoNote(_) => Some("отправляет видеосообщение...".to_string()),
|
||||
ChatAction::Cancel | _ => None, // Отмена или неизвестное действие
|
||||
_ => None, // Отмена или неизвестное действие
|
||||
};
|
||||
|
||||
match action_text {
|
||||
|
||||
@@ -21,7 +21,7 @@ pub fn render_emoji_picker(
|
||||
) {
|
||||
// Размеры модалки (зависят от количества реакций)
|
||||
let emojis_per_row = 8;
|
||||
let rows = (available_reactions.len() + emojis_per_row - 1) / emojis_per_row;
|
||||
let rows = available_reactions.len().div_ceil(emojis_per_row);
|
||||
let modal_width = 50u16;
|
||||
let modal_height = (rows + 4) as u16; // +4 для заголовка, отступов и подсказки
|
||||
|
||||
|
||||
@@ -401,12 +401,10 @@ pub fn render_message_bubble(
|
||||
} else {
|
||||
format!("[{}]", reaction.emoji)
|
||||
}
|
||||
} else if reaction.count > 1 {
|
||||
format!("{} {}", reaction.emoji, reaction.count)
|
||||
} else {
|
||||
if reaction.count > 1 {
|
||||
format!("{} {}", reaction.emoji, reaction.count)
|
||||
} else {
|
||||
reaction.emoji.clone()
|
||||
}
|
||||
reaction.emoji.clone()
|
||||
};
|
||||
|
||||
let style = if reaction.is_chosen {
|
||||
@@ -548,7 +546,7 @@ pub fn render_album_bubble(
|
||||
let mut deferred: Vec<DeferredImageRender> = Vec::new();
|
||||
|
||||
let is_selected = messages.iter().any(|m| selected_msg_id == Some(m.id()));
|
||||
let is_outgoing = messages.first().map_or(false, |m| m.is_outgoing());
|
||||
let is_outgoing = messages.first().is_some_and(|m| m.is_outgoing());
|
||||
|
||||
// Selection marker
|
||||
let selection_marker = if is_selected { "▶ " } else { "" };
|
||||
@@ -567,7 +565,7 @@ pub fn render_album_bubble(
|
||||
|
||||
// Grid layout
|
||||
let cols = photo_count.min(ALBUM_GRID_MAX_COLS);
|
||||
let rows = (photo_count + cols - 1) / cols;
|
||||
let rows = photo_count.div_ceil(cols);
|
||||
|
||||
// Добавляем маркер выбора на первую строку
|
||||
if is_selected {
|
||||
|
||||
@@ -309,11 +309,7 @@ fn render_message_list<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &mut Ap
|
||||
let total_lines = lines.len();
|
||||
|
||||
// Базовый скролл (показываем последние сообщения)
|
||||
let base_scroll = if total_lines > visible_height {
|
||||
total_lines - visible_height
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let base_scroll = total_lines.saturating_sub(visible_height);
|
||||
|
||||
// Если выбрано сообщение, автоскроллим к нему
|
||||
let scroll_offset = if app.is_selecting_message() {
|
||||
@@ -431,7 +427,7 @@ pub fn render<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &mut App<T>) {
|
||||
1
|
||||
};
|
||||
// Минимум 3 строки (1 контент + 2 рамки), максимум 10
|
||||
let input_height = (input_lines + 2).min(10).max(3);
|
||||
let input_height = (input_lines + 2).clamp(3, 10);
|
||||
|
||||
// Проверяем, есть ли закреплённое сообщение
|
||||
let has_pinned = app.td_client.current_pinned_message().is_some();
|
||||
|
||||
Reference in New Issue
Block a user