fix: исправлены баги с сообщениями, редактированием и reply
- Изменён порядок хранения сообщений (теперь от старых к новым) - Исправлена логика выбора сообщений для редактирования - Исправлена отправка reply (структура условий) - Добавлено сохранение reply_info при отправке - Удалены отладочные логи Fixes: сообщения теперь отображаются корректно в UI Fixes: редактирование работает без ошибки 'Message not found' Fixes: reply показывает превью исходного сообщения
This commit is contained in:
@@ -133,30 +133,34 @@ impl App {
|
||||
|
||||
/// Начать выбор сообщения для редактирования (при стрелке вверх в пустом инпуте)
|
||||
pub fn start_message_selection(&mut self) {
|
||||
if self.td_client.current_chat_messages().is_empty() {
|
||||
let total = self.td_client.current_chat_messages().len();
|
||||
if total == 0 {
|
||||
return;
|
||||
}
|
||||
// Начинаем с последнего сообщения (индекс 0 = самое новое снизу)
|
||||
self.chat_state = ChatState::MessageSelection { selected_index: 0 };
|
||||
// Начинаем с последнего сообщения (индекс len-1 = самое новое внизу)
|
||||
self.chat_state = ChatState::MessageSelection { selected_index: total - 1 };
|
||||
}
|
||||
|
||||
/// Выбрать предыдущее сообщение (вверх по списку = увеличить индекс)
|
||||
/// Выбрать предыдущее сообщение (вверх по списку = к старым = уменьшить индекс)
|
||||
pub fn select_previous_message(&mut self) {
|
||||
if let ChatState::MessageSelection { selected_index } = &mut self.chat_state {
|
||||
if *selected_index > 0 {
|
||||
*selected_index -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Выбрать следующее сообщение (вниз по списку = к новым = увеличить индекс)
|
||||
pub fn select_next_message(&mut self) {
|
||||
let total = self.td_client.current_chat_messages().len();
|
||||
if total == 0 {
|
||||
return;
|
||||
}
|
||||
if let ChatState::MessageSelection { selected_index } = &mut self.chat_state {
|
||||
*selected_index = (*selected_index + 1).min(total - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Выбрать следующее сообщение (вниз по списку = уменьшить индекс)
|
||||
pub fn select_next_message(&mut self) {
|
||||
if let ChatState::MessageSelection { selected_index } = &mut self.chat_state {
|
||||
if *selected_index > 0 {
|
||||
*selected_index -= 1;
|
||||
if *selected_index < total - 1 {
|
||||
*selected_index += 1;
|
||||
} else {
|
||||
// Дошли до самого нового сообщения - выходим из режима выбора
|
||||
self.chat_state = ChatState::Normal;
|
||||
}
|
||||
}
|
||||
@@ -165,12 +169,7 @@ impl App {
|
||||
/// Получить выбранное сообщение
|
||||
pub fn get_selected_message(&self) -> Option<&crate::tdlib::MessageInfo> {
|
||||
self.chat_state.selected_message_index().and_then(|idx| {
|
||||
let total = self.td_client.current_chat_messages().len();
|
||||
if total == 0 || idx >= total {
|
||||
return None;
|
||||
}
|
||||
// idx=0 это последнее сообщение (total-1), idx=1 это предпоследнее (total-2), и т.д.
|
||||
self.td_client.current_chat_messages().get(total - 1 - idx)
|
||||
self.td_client.current_chat_messages().get(idx)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -494,7 +494,12 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
Ok(Ok(messages)) => {
|
||||
// Сохраняем загруженные сообщения
|
||||
*app.td_client.current_chat_messages_mut() = messages;
|
||||
// ВАЖНО: Устанавливаем current_chat_id ТОЛЬКО ПОСЛЕ сохранения истории
|
||||
// Это предотвращает race condition с Update::NewMessage
|
||||
app.td_client.set_current_chat_id(Some(ChatId::new(chat_id)));
|
||||
// Загружаем недостающие reply info
|
||||
let _ = timeout(
|
||||
Duration::from_secs(5),
|
||||
@@ -563,39 +568,39 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
if let Some(chat_id) = app.get_selected_chat_id() {
|
||||
let text = app.message_input.clone();
|
||||
|
||||
if let Some(msg_id) = app.chat_state.selected_message_id() {
|
||||
if app.is_editing() {
|
||||
// Режим редактирования
|
||||
app.message_input.clear();
|
||||
app.cursor_position = 0;
|
||||
app.chat_state = crate::app::ChatState::Normal;
|
||||
|
||||
match timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.edit_message(ChatId::new(chat_id), msg_id, text),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(edited_msg)) => {
|
||||
// Обновляем сообщение в списке
|
||||
if let Some(msg) = app
|
||||
.td_client
|
||||
.current_chat_messages_mut()
|
||||
.iter_mut()
|
||||
.find(|m| m.id() == msg_id)
|
||||
{
|
||||
msg.content.text = edited_msg.content.text;
|
||||
msg.content.entities = edited_msg.content.entities;
|
||||
msg.metadata.edit_date = edited_msg.metadata.edit_date;
|
||||
if app.is_editing() {
|
||||
// Режим редактирования
|
||||
if let Some(msg_id) = app.chat_state.selected_message_id() {
|
||||
match timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.edit_message(ChatId::new(chat_id), msg_id, text),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(edited_msg)) => {
|
||||
// Обновляем сообщение в списке
|
||||
if let Some(msg) = app
|
||||
.td_client
|
||||
.current_chat_messages_mut()
|
||||
.iter_mut()
|
||||
.find(|m| m.id() == msg_id)
|
||||
{
|
||||
msg.content.text = edited_msg.content.text;
|
||||
msg.content.entities = edited_msg.content.entities;
|
||||
msg.metadata.edit_date = edited_msg.metadata.edit_date;
|
||||
}
|
||||
// Очищаем инпут и сбрасываем состояние ПОСЛЕ успешного редактирования
|
||||
app.message_input.clear();
|
||||
app.cursor_position = 0;
|
||||
app.chat_state = crate::app::ChatState::Normal;
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
app.error_message = Some(e);
|
||||
}
|
||||
Err(_) => {
|
||||
app.error_message = Some("Таймаут редактирования".to_string());
|
||||
}
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
app.error_message = Some(e);
|
||||
}
|
||||
Err(_) => {
|
||||
app.error_message = Some("Таймаут редактирования".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Обычная отправка (или reply)
|
||||
@@ -663,7 +668,12 @@ pub async fn handle(app: &mut App, key: KeyEvent) {
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
Ok(Ok(messages)) => {
|
||||
// Сохраняем загруженные сообщения
|
||||
*app.td_client.current_chat_messages_mut() = messages;
|
||||
// ВАЖНО: Устанавливаем current_chat_id ТОЛЬКО ПОСЛЕ сохранения истории
|
||||
// Это предотвращает race condition с Update::NewMessage
|
||||
app.td_client.set_current_chat_id(Some(ChatId::new(chat_id)));
|
||||
// Загружаем недостающие reply info
|
||||
let _ = timeout(
|
||||
Duration::from_secs(5),
|
||||
|
||||
@@ -498,7 +498,7 @@ impl TdClient {
|
||||
}
|
||||
None => {
|
||||
// Нового сообщения нет - добавляем
|
||||
self.push_message(msg_info);
|
||||
self.push_message(msg_info.clone());
|
||||
// Если это входящее сообщение — добавляем в очередь для отметки как прочитанное
|
||||
if is_incoming {
|
||||
self.pending_view_messages_mut().push((chat_id, vec![msg_id]));
|
||||
|
||||
@@ -29,11 +29,11 @@ impl MessageManager {
|
||||
|
||||
/// Добавить сообщение в список текущего чата
|
||||
pub fn push_message(&mut self, msg: MessageInfo) {
|
||||
self.current_chat_messages.insert(0, msg);
|
||||
self.current_chat_messages.push(msg); // Добавляем в конец
|
||||
|
||||
// Ограничиваем размер списка
|
||||
// Ограничиваем размер списка (удаляем старые с начала)
|
||||
if self.current_chat_messages.len() > MAX_MESSAGES_IN_CHAT {
|
||||
self.current_chat_messages.truncate(MAX_MESSAGES_IN_CHAT);
|
||||
self.current_chat_messages.drain(0..(self.current_chat_messages.len() - MAX_MESSAGES_IN_CHAT));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,34 +43,63 @@ impl MessageManager {
|
||||
chat_id: ChatId,
|
||||
limit: i32,
|
||||
) -> Result<Vec<MessageInfo>, String> {
|
||||
// Устанавливаем текущий чат для получения новых сообщений
|
||||
self.current_chat_id = Some(chat_id);
|
||||
use tokio::time::{sleep, Duration};
|
||||
|
||||
let result = functions::get_chat_history(
|
||||
chat_id.as_i64(),
|
||||
0, // from_message_id
|
||||
0, // offset
|
||||
limit,
|
||||
false,
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
// ВАЖНО: Сначала открываем чат в TDLib
|
||||
// Это сообщает TDLib что пользователь открыл чат и нужно загрузить историю
|
||||
let _ = functions::open_chat(chat_id.as_i64(), self.client_id).await;
|
||||
|
||||
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);
|
||||
// Даём TDLib время на синхронизацию (загрузку истории с сервера)
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// НЕ устанавливаем current_chat_id здесь!
|
||||
// Он будет установлен снаружи ПОСЛЕ сохранения истории
|
||||
// Это предотвращает race condition с Update::NewMessage
|
||||
|
||||
// Пробуем загрузить несколько раз, TDLib может подгружать с сервера
|
||||
let mut all_messages = Vec::new();
|
||||
let max_attempts = 3;
|
||||
|
||||
for attempt in 1..=max_attempts {
|
||||
let result = functions::get_chat_history(
|
||||
chat_id.as_i64(),
|
||||
0, // from_message_id (0 = from latest)
|
||||
0, // offset
|
||||
limit,
|
||||
false, // only_local - false means can fetch from server
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(tdlib_rs::enums::Messages::Messages(messages_obj)) => {
|
||||
if !messages_obj.messages.is_empty() {
|
||||
all_messages.clear(); // Очищаем предыдущие результаты
|
||||
for msg_opt in messages_obj.messages.iter().rev() {
|
||||
if let Some(msg) = msg_opt {
|
||||
if let Some(info) = self.convert_message(msg).await {
|
||||
all_messages.push(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Если получили достаточно сообщений, прекращаем попытки
|
||||
if all_messages.len() >= 2 || attempt == max_attempts {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Если сообщений мало, ждём перед следующей попыткой
|
||||
if attempt < max_attempts {
|
||||
sleep(Duration::from_millis(200)).await;
|
||||
}
|
||||
}
|
||||
Ok(messages)
|
||||
Ok(_) => return Err("Неожиданный тип сообщений".to_string()),
|
||||
Err(e) => return Err(format!("Ошибка загрузки истории: {:?}", e)),
|
||||
}
|
||||
Ok(_) => Err("Неожиданный тип сообщений".to_string()),
|
||||
Err(e) => Err(format!("Ошибка загрузки истории: {:?}", e)),
|
||||
}
|
||||
|
||||
Ok(all_messages)
|
||||
}
|
||||
|
||||
/// Загрузить более старые сообщения
|
||||
@@ -194,7 +223,7 @@ impl MessageManager {
|
||||
chat_id: ChatId,
|
||||
text: String,
|
||||
reply_to_message_id: Option<MessageId>,
|
||||
_reply_info: Option<ReplyInfo>,
|
||||
reply_info: Option<ReplyInfo>,
|
||||
) -> Result<MessageInfo, String> {
|
||||
// Парсим markdown в тексте
|
||||
let formatted_text = match functions::parse_text_entities(
|
||||
@@ -241,10 +270,19 @@ impl MessageManager {
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(tdlib_rs::enums::Message::Message(msg)) => self
|
||||
.convert_message(&msg)
|
||||
.await
|
||||
.ok_or_else(|| "Не удалось конвертировать сообщение".to_string()),
|
||||
Ok(tdlib_rs::enums::Message::Message(msg)) => {
|
||||
let mut msg_info = self
|
||||
.convert_message(&msg)
|
||||
.await
|
||||
.ok_or_else(|| "Не удалось конвертировать сообщение".to_string())?;
|
||||
|
||||
// Добавляем reply_info если был передан
|
||||
if let Some(reply) = reply_info {
|
||||
msg_info.interactions.reply_to = Some(reply);
|
||||
}
|
||||
|
||||
Ok(msg_info)
|
||||
}
|
||||
Ok(_) => Err("Неожиданный тип сообщения".to_string()),
|
||||
Err(e) => Err(format!("Ошибка отправки сообщения: {:?}", e)),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user