commit
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
use crate::app::App;
|
||||
use crate::tdlib::client::AuthState;
|
||||
use crossterm::event::KeyCode;
|
||||
use std::time::Duration;
|
||||
use tokio::time::timeout;
|
||||
use crate::app::App;
|
||||
use crate::tdlib::client::AuthState;
|
||||
|
||||
pub async fn handle(app: &mut App, key_code: KeyCode) {
|
||||
match &app.td_client.auth_state {
|
||||
@@ -18,7 +18,12 @@ pub async fn handle(app: &mut App, key_code: KeyCode) {
|
||||
KeyCode::Enter => {
|
||||
if !app.phone_input.is_empty() {
|
||||
app.status_message = Some("Отправка номера...".to_string());
|
||||
match timeout(Duration::from_secs(10), app.td_client.send_phone_number(app.phone_input.clone())).await {
|
||||
match timeout(
|
||||
Duration::from_secs(10),
|
||||
app.td_client.send_phone_number(app.phone_input.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
app.error_message = None;
|
||||
app.status_message = None;
|
||||
@@ -48,7 +53,12 @@ pub async fn handle(app: &mut App, key_code: KeyCode) {
|
||||
KeyCode::Enter => {
|
||||
if !app.code_input.is_empty() {
|
||||
app.status_message = Some("Проверка кода...".to_string());
|
||||
match timeout(Duration::from_secs(10), app.td_client.send_code(app.code_input.clone())).await {
|
||||
match timeout(
|
||||
Duration::from_secs(10),
|
||||
app.td_client.send_code(app.code_input.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
app.error_message = None;
|
||||
app.status_message = None;
|
||||
@@ -78,7 +88,12 @@ pub async fn handle(app: &mut App, key_code: KeyCode) {
|
||||
KeyCode::Enter => {
|
||||
if !app.password_input.is_empty() {
|
||||
app.status_message = Some("Проверка пароля...".to_string());
|
||||
match timeout(Duration::from_secs(10), app.td_client.send_password(app.password_input.clone())).await {
|
||||
match timeout(
|
||||
Duration::from_secs(10),
|
||||
app.td_client.send_password(app.password_input.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
app.error_message = None;
|
||||
app.status_message = None;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::app::App;
|
||||
use crate::tdlib::ChatAction;
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::time::timeout;
|
||||
use crate::app::App;
|
||||
use crate::tdlib::ChatAction;
|
||||
|
||||
pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
let has_ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
|
||||
@@ -27,7 +27,12 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if app.selected_chat_id.is_some() && !app.is_pinned_mode() {
|
||||
if let Some(chat_id) = app.get_selected_chat_id() {
|
||||
app.status_message = Some("Загрузка закреплённых...".to_string());
|
||||
match timeout(Duration::from_secs(5), app.td_client.get_pinned_messages(chat_id)).await {
|
||||
match timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.get_pinned_messages(chat_id),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(messages)) => {
|
||||
if messages.is_empty() {
|
||||
app.status_message = Some("Нет закреплённых сообщений".to_string());
|
||||
@@ -51,7 +56,10 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
}
|
||||
KeyCode::Char('f') if has_ctrl => {
|
||||
// Ctrl+F - поиск по сообщениям в открытом чате
|
||||
if app.selected_chat_id.is_some() && !app.is_pinned_mode() && !app.is_message_search_mode() {
|
||||
if app.selected_chat_id.is_some()
|
||||
&& !app.is_pinned_mode()
|
||||
&& !app.is_message_search_mode()
|
||||
{
|
||||
app.enter_message_search_mode();
|
||||
}
|
||||
return;
|
||||
@@ -125,13 +133,17 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if profile.username.is_some() {
|
||||
if action_index == current_idx {
|
||||
if let Some(username) = &profile.username {
|
||||
let url = format!("https://t.me/{}", username.trim_start_matches('@'));
|
||||
let url = format!(
|
||||
"https://t.me/{}",
|
||||
username.trim_start_matches('@')
|
||||
);
|
||||
match open::that(&url) {
|
||||
Ok(_) => {
|
||||
app.status_message = Some(format!("Открыто: {}", url));
|
||||
}
|
||||
Err(e) => {
|
||||
app.error_message = Some(format!("Ошибка открытия браузера: {}", e));
|
||||
app.error_message =
|
||||
Some(format!("Ошибка открытия браузера: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,7 +154,8 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
|
||||
// Действие: Скопировать ID
|
||||
if action_index == current_idx {
|
||||
app.status_message = Some(format!("ID скопирован: {}", profile.chat_id));
|
||||
app.status_message =
|
||||
Some(format!("ID скопирован: {}", profile.chat_id));
|
||||
return;
|
||||
}
|
||||
current_idx += 1;
|
||||
@@ -174,10 +187,12 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
KeyCode::Enter => {
|
||||
// Перейти к выбранному сообщению
|
||||
if let Some(msg_id) = app.get_selected_search_result_id() {
|
||||
let msg_index = app.td_client.current_chat_messages
|
||||
let msg_index = app
|
||||
.td_client
|
||||
.current_chat_messages
|
||||
.iter()
|
||||
.position(|m| m.id == msg_id);
|
||||
|
||||
|
||||
if let Some(idx) = msg_index {
|
||||
let total = app.td_client.current_chat_messages.len();
|
||||
app.message_scroll_offset = total.saturating_sub(idx + 5);
|
||||
@@ -192,8 +207,11 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if !app.message_search_query.is_empty() {
|
||||
if let Ok(Ok(results)) = timeout(
|
||||
Duration::from_secs(3),
|
||||
app.td_client.search_messages(chat_id, &app.message_search_query)
|
||||
).await {
|
||||
app.td_client
|
||||
.search_messages(chat_id, &app.message_search_query),
|
||||
)
|
||||
.await
|
||||
{
|
||||
app.set_search_results(results);
|
||||
}
|
||||
} else {
|
||||
@@ -207,8 +225,11 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if let Some(chat_id) = app.get_selected_chat_id() {
|
||||
if let Ok(Ok(results)) = timeout(
|
||||
Duration::from_secs(3),
|
||||
app.td_client.search_messages(chat_id, &app.message_search_query)
|
||||
).await {
|
||||
app.td_client
|
||||
.search_messages(chat_id, &app.message_search_query),
|
||||
)
|
||||
.await
|
||||
{
|
||||
app.set_search_results(results);
|
||||
}
|
||||
}
|
||||
@@ -234,10 +255,12 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
// Перейти к сообщению в истории
|
||||
if let Some(msg_id) = app.get_selected_pinned_id() {
|
||||
// Ищем индекс сообщения в текущей истории
|
||||
let msg_index = app.td_client.current_chat_messages
|
||||
let msg_index = app
|
||||
.td_client
|
||||
.current_chat_messages
|
||||
.iter()
|
||||
.position(|m| m.id == msg_id);
|
||||
|
||||
|
||||
if let Some(idx) = msg_index {
|
||||
// Вычисляем scroll offset чтобы показать сообщение
|
||||
let total = app.td_client.current_chat_messages.len();
|
||||
@@ -284,13 +307,17 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if let Some(chat_id) = app.selected_chat_id {
|
||||
app.status_message = Some("Отправка реакции...".to_string());
|
||||
app.needs_redraw = true;
|
||||
|
||||
|
||||
match timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.toggle_reaction(chat_id, message_id, emoji.clone())
|
||||
).await {
|
||||
app.td_client
|
||||
.toggle_reaction(chat_id, message_id, emoji.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
app.status_message = Some(format!("Реакция {} добавлена", emoji));
|
||||
app.status_message =
|
||||
Some(format!("Реакция {} добавлена", emoji));
|
||||
app.exit_reaction_picker_mode();
|
||||
app.needs_redraw = true;
|
||||
}
|
||||
@@ -300,7 +327,8 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
app.needs_redraw = true;
|
||||
}
|
||||
Err(_) => {
|
||||
app.error_message = Some("Таймаут отправки реакции".to_string());
|
||||
app.error_message =
|
||||
Some("Таймаут отправки реакции".to_string());
|
||||
app.status_message = None;
|
||||
app.needs_redraw = true;
|
||||
}
|
||||
@@ -326,7 +354,9 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if let Some(msg_id) = app.confirm_delete_message_id {
|
||||
if let Some(chat_id) = app.get_selected_chat_id() {
|
||||
// Находим сообщение для проверки can_be_deleted_for_all_users
|
||||
let can_delete_for_all = app.td_client.current_chat_messages
|
||||
let can_delete_for_all = app
|
||||
.td_client
|
||||
.current_chat_messages
|
||||
.iter()
|
||||
.find(|m| m.id == msg_id)
|
||||
.map(|m| m.can_be_deleted_for_all_users)
|
||||
@@ -334,11 +364,19 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
|
||||
match timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.delete_messages(chat_id, vec![msg_id], can_delete_for_all)
|
||||
).await {
|
||||
app.td_client.delete_messages(
|
||||
chat_id,
|
||||
vec![msg_id],
|
||||
can_delete_for_all,
|
||||
),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
// Удаляем из локального списка
|
||||
app.td_client.current_chat_messages.retain(|m| m.id != msg_id);
|
||||
app.td_client
|
||||
.current_chat_messages
|
||||
.retain(|m| m.id != msg_id);
|
||||
app.selected_message_index = None;
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
@@ -377,10 +415,17 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if let Some(from_chat_id) = app.get_selected_chat_id() {
|
||||
match timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.forward_messages(to_chat_id, from_chat_id, vec![msg_id])
|
||||
).await {
|
||||
app.td_client.forward_messages(
|
||||
to_chat_id,
|
||||
from_chat_id,
|
||||
vec![msg_id],
|
||||
),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
app.status_message = Some("Сообщение переслано".to_string());
|
||||
app.status_message =
|
||||
Some("Сообщение переслано".to_string());
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
app.error_message = Some(e);
|
||||
@@ -418,12 +463,25 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if let Some(chat_id) = app.get_selected_chat_id() {
|
||||
app.status_message = Some("Загрузка сообщений...".to_string());
|
||||
app.message_scroll_offset = 0;
|
||||
match timeout(Duration::from_secs(10), app.td_client.get_chat_history(chat_id, 100)).await {
|
||||
match timeout(
|
||||
Duration::from_secs(10),
|
||||
app.td_client.get_chat_history(chat_id, 100),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
// Загружаем недостающие reply info
|
||||
let _ = timeout(Duration::from_secs(5), app.td_client.fetch_missing_reply_info()).await;
|
||||
let _ = timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.fetch_missing_reply_info(),
|
||||
)
|
||||
.await;
|
||||
// Загружаем последнее закреплённое сообщение
|
||||
let _ = timeout(Duration::from_secs(2), app.td_client.load_current_pinned_message(chat_id)).await;
|
||||
let _ = timeout(
|
||||
Duration::from_secs(2),
|
||||
app.td_client.load_current_pinned_message(chat_id),
|
||||
)
|
||||
.await;
|
||||
// Загружаем черновик
|
||||
app.load_draft();
|
||||
app.status_message = None;
|
||||
@@ -460,8 +518,6 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Enter - открыть чат, отправить сообщение или редактировать
|
||||
if key.code == KeyCode::Enter {
|
||||
if app.selected_chat_id.is_some() {
|
||||
@@ -488,10 +544,20 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
app.cursor_position = 0;
|
||||
app.editing_message_id = None;
|
||||
|
||||
match timeout(Duration::from_secs(5), app.td_client.edit_message(chat_id, msg_id, text)).await {
|
||||
match timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.edit_message(chat_id, msg_id, text),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(edited_msg)) => {
|
||||
// Обновляем сообщение в списке
|
||||
if let Some(msg) = app.td_client.current_chat_messages.iter_mut().find(|m| m.id == msg_id) {
|
||||
if let Some(msg) = app
|
||||
.td_client
|
||||
.current_chat_messages
|
||||
.iter_mut()
|
||||
.find(|m| m.id == msg_id)
|
||||
{
|
||||
msg.content = edited_msg.content;
|
||||
msg.entities = edited_msg.entities;
|
||||
msg.edit_date = edited_msg.edit_date;
|
||||
@@ -521,9 +587,17 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
app.last_typing_sent = None;
|
||||
|
||||
// Отменяем typing status
|
||||
app.td_client.send_chat_action(chat_id, ChatAction::Cancel).await;
|
||||
app.td_client
|
||||
.send_chat_action(chat_id, ChatAction::Cancel)
|
||||
.await;
|
||||
|
||||
match timeout(Duration::from_secs(5), app.td_client.send_message(chat_id, text, reply_to_id, reply_info)).await {
|
||||
match timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client
|
||||
.send_message(chat_id, text, reply_to_id, reply_info),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(sent_msg)) => {
|
||||
// Добавляем отправленное сообщение в список (с лимитом)
|
||||
app.td_client.push_message(sent_msg);
|
||||
@@ -549,12 +623,25 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if let Some(chat_id) = app.get_selected_chat_id() {
|
||||
app.status_message = Some("Загрузка сообщений...".to_string());
|
||||
app.message_scroll_offset = 0;
|
||||
match timeout(Duration::from_secs(10), app.td_client.get_chat_history(chat_id, 100)).await {
|
||||
match timeout(
|
||||
Duration::from_secs(10),
|
||||
app.td_client.get_chat_history(chat_id, 100),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
// Загружаем недостающие reply info
|
||||
let _ = timeout(Duration::from_secs(5), app.td_client.fetch_missing_reply_info()).await;
|
||||
let _ = timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.fetch_missing_reply_info(),
|
||||
)
|
||||
.await;
|
||||
// Загружаем последнее закреплённое сообщение
|
||||
let _ = timeout(Duration::from_secs(2), app.td_client.load_current_pinned_message(chat_id)).await;
|
||||
let _ = timeout(
|
||||
Duration::from_secs(2),
|
||||
app.td_client.load_current_pinned_message(chat_id),
|
||||
)
|
||||
.await;
|
||||
// Загружаем черновик
|
||||
app.load_draft();
|
||||
app.status_message = None;
|
||||
@@ -593,7 +680,10 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
let _ = app.td_client.set_draft_message(chat_id, draft_text).await;
|
||||
} else if app.message_input.is_empty() {
|
||||
// Очищаем черновик если инпут пустой
|
||||
let _ = app.td_client.set_draft_message(chat_id, String::new()).await;
|
||||
let _ = app
|
||||
.td_client
|
||||
.set_draft_message(chat_id, String::new())
|
||||
.await;
|
||||
}
|
||||
}
|
||||
app.close_chat();
|
||||
@@ -616,7 +706,8 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
KeyCode::Char('d') | KeyCode::Char('в') | KeyCode::Delete => {
|
||||
// Показать модалку подтверждения удаления
|
||||
if let Some(msg) = app.get_selected_message() {
|
||||
let can_delete = msg.can_be_deleted_only_for_self || msg.can_be_deleted_for_all_users;
|
||||
let can_delete =
|
||||
msg.can_be_deleted_only_for_self || msg.can_be_deleted_for_all_users;
|
||||
if can_delete {
|
||||
app.confirm_delete_message_id = Some(msg.id);
|
||||
}
|
||||
@@ -649,18 +740,22 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if let Some(msg) = app.get_selected_message() {
|
||||
let chat_id = app.selected_chat_id.unwrap();
|
||||
let message_id = msg.id;
|
||||
|
||||
|
||||
app.status_message = Some("Загрузка реакций...".to_string());
|
||||
app.needs_redraw = true;
|
||||
|
||||
|
||||
// Запрашиваем доступные реакции
|
||||
match timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.get_message_available_reactions(chat_id, message_id)
|
||||
).await {
|
||||
app.td_client
|
||||
.get_message_available_reactions(chat_id, message_id),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(reactions)) => {
|
||||
if reactions.is_empty() {
|
||||
app.error_message = Some("Реакции недоступны для этого сообщения".to_string());
|
||||
app.error_message =
|
||||
Some("Реакции недоступны для этого сообщения".to_string());
|
||||
app.status_message = None;
|
||||
app.needs_redraw = true;
|
||||
} else {
|
||||
@@ -691,7 +786,8 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if key.code == KeyCode::Char('u') && has_ctrl {
|
||||
if let Some(chat_id) = app.selected_chat_id {
|
||||
app.status_message = Some("Загрузка профиля...".to_string());
|
||||
match timeout(Duration::from_secs(5), app.td_client.get_profile_info(chat_id)).await {
|
||||
match timeout(Duration::from_secs(5), app.td_client.get_profile_info(chat_id)).await
|
||||
{
|
||||
Ok(Ok(profile)) => {
|
||||
app.profile_info = Some(profile);
|
||||
app.enter_profile_mode();
|
||||
@@ -756,12 +852,15 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
app.cursor_position += 1;
|
||||
|
||||
// Отправляем typing status с throttling (не чаще 1 раза в 5 сек)
|
||||
let should_send_typing = app.last_typing_sent
|
||||
let should_send_typing = app
|
||||
.last_typing_sent
|
||||
.map(|t| t.elapsed().as_secs() >= 5)
|
||||
.unwrap_or(true);
|
||||
if should_send_typing {
|
||||
if let Some(chat_id) = app.get_selected_chat_id() {
|
||||
app.td_client.send_chat_action(chat_id, ChatAction::Typing).await;
|
||||
app.td_client
|
||||
.send_chat_action(chat_id, ChatAction::Typing)
|
||||
.await;
|
||||
app.last_typing_sent = Some(Instant::now());
|
||||
}
|
||||
}
|
||||
@@ -804,18 +903,29 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
|
||||
// Проверяем, нужно ли подгрузить старые сообщения
|
||||
if !app.td_client.current_chat_messages.is_empty() {
|
||||
let oldest_msg_id = app.td_client.current_chat_messages.first().map(|m| m.id).unwrap_or(0);
|
||||
let oldest_msg_id = app
|
||||
.td_client
|
||||
.current_chat_messages
|
||||
.first()
|
||||
.map(|m| m.id)
|
||||
.unwrap_or(0);
|
||||
if let Some(chat_id) = app.get_selected_chat_id() {
|
||||
// Подгружаем больше сообщений если скролл близко к верху
|
||||
if app.message_scroll_offset > app.td_client.current_chat_messages.len().saturating_sub(10) {
|
||||
if app.message_scroll_offset
|
||||
> app.td_client.current_chat_messages.len().saturating_sub(10)
|
||||
{
|
||||
if let Ok(Ok(older)) = timeout(
|
||||
Duration::from_secs(3),
|
||||
app.td_client.load_older_messages(chat_id, oldest_msg_id, 20)
|
||||
).await {
|
||||
app.td_client
|
||||
.load_older_messages(chat_id, oldest_msg_id, 20),
|
||||
)
|
||||
.await
|
||||
{
|
||||
if !older.is_empty() {
|
||||
// Добавляем старые сообщения в начало
|
||||
let mut new_messages = older;
|
||||
new_messages.extend(app.td_client.current_chat_messages.drain(..));
|
||||
new_messages
|
||||
.extend(app.td_client.current_chat_messages.drain(..));
|
||||
app.td_client.current_chat_messages = new_messages;
|
||||
}
|
||||
}
|
||||
@@ -848,7 +958,11 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
app.selected_folder_id = Some(folder_id);
|
||||
// Загружаем чаты папки
|
||||
app.status_message = Some("Загрузка чатов папки...".to_string());
|
||||
let _ = timeout(Duration::from_secs(5), app.td_client.load_folder_chats(folder_id, 50)).await;
|
||||
let _ = timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.load_folder_chats(folder_id, 50),
|
||||
)
|
||||
.await;
|
||||
app.status_message = None;
|
||||
}
|
||||
}
|
||||
@@ -862,73 +976,76 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
/// Подсчёт количества доступных действий в профиле
|
||||
fn get_available_actions_count(profile: &crate::tdlib::ProfileInfo) -> usize {
|
||||
let mut count = 0;
|
||||
|
||||
|
||||
if profile.username.is_some() {
|
||||
count += 1; // Открыть в браузере
|
||||
}
|
||||
|
||||
|
||||
count += 1; // Скопировать ID
|
||||
|
||||
|
||||
if profile.is_group {
|
||||
count += 1; // Покинуть группу
|
||||
}
|
||||
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
/// Копирует текст в системный буфер обмена
|
||||
fn copy_to_clipboard(text: &str) -> Result<(), String> {
|
||||
use arboard::Clipboard;
|
||||
|
||||
let mut clipboard = Clipboard::new().map_err(|e| format!("Не удалось инициализировать буфер обмена: {}", e))?;
|
||||
clipboard.set_text(text).map_err(|e| format!("Не удалось скопировать: {}", e))?;
|
||||
|
||||
|
||||
let mut clipboard =
|
||||
Clipboard::new().map_err(|e| format!("Не удалось инициализировать буфер обмена: {}", e))?;
|
||||
clipboard
|
||||
.set_text(text)
|
||||
.map_err(|e| format!("Не удалось скопировать: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Форматирует сообщение для копирования с контекстом
|
||||
fn format_message_for_clipboard(msg: &crate::tdlib::client::MessageInfo) -> String {
|
||||
let mut result = String::new();
|
||||
|
||||
|
||||
// Добавляем forward контекст если есть
|
||||
if let Some(forward) = &msg.forward_from {
|
||||
result.push_str(&format!("↪ Переслано от {}\n", forward.sender_name));
|
||||
}
|
||||
|
||||
|
||||
// Добавляем reply контекст если есть
|
||||
if let Some(reply) = &msg.reply_to {
|
||||
result.push_str(&format!("┌ {}: {}\n", reply.sender_name, reply.text));
|
||||
}
|
||||
|
||||
|
||||
// Добавляем основной текст с markdown форматированием
|
||||
result.push_str(&convert_entities_to_markdown(&msg.content, &msg.entities));
|
||||
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Конвертирует текст с entities в markdown
|
||||
fn convert_entities_to_markdown(text: &str, entities: &[tdlib_rs::types::TextEntity]) -> String {
|
||||
use tdlib_rs::enums::TextEntityType;
|
||||
|
||||
|
||||
if entities.is_empty() {
|
||||
return text.to_string();
|
||||
}
|
||||
|
||||
|
||||
// Создаём вектор символов для работы с unicode
|
||||
let chars: Vec<char> = text.chars().collect();
|
||||
let mut result = String::new();
|
||||
let mut i = 0;
|
||||
|
||||
|
||||
while i < chars.len() {
|
||||
// Ищем entity, который начинается в текущей позиции
|
||||
let mut entity_found = false;
|
||||
|
||||
|
||||
for entity in entities {
|
||||
if entity.offset as usize == i {
|
||||
entity_found = true;
|
||||
let end = (entity.offset + entity.length) as usize;
|
||||
let entity_text: String = chars[i..end.min(chars.len())].iter().collect();
|
||||
|
||||
|
||||
// Применяем форматирование в зависимости от типа
|
||||
let formatted = match &entity.r#type {
|
||||
TextEntityType::Bold => format!("**{}**", entity_text),
|
||||
@@ -948,18 +1065,18 @@ fn convert_entities_to_markdown(text: &str, entities: &[tdlib_rs::types::TextEnt
|
||||
TextEntityType::Spoiler => format!("||{}||", entity_text),
|
||||
_ => entity_text,
|
||||
};
|
||||
|
||||
|
||||
result.push_str(&formatted);
|
||||
i = end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !entity_found {
|
||||
result.push(chars[i]);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user