diff --git a/src/session/content/chat_history.rs b/src/session/content/chat_history.rs index f381da914..7f11f7bc5 100644 --- a/src/session/content/chat_history.rs +++ b/src/session/content/chat_history.rs @@ -21,6 +21,7 @@ mod imp { use once_cell::sync::Lazy; use once_cell::unsync::OnceCell; use std::cell::{Cell, RefCell}; + use std::collections::HashSet; #[derive(Debug, Default, CompositeTemplate)] #[template(resource = "/com/github/melix99/telegrand/ui/content-chat-history.ui")] @@ -31,6 +32,7 @@ mod imp { pub(super) message_menu: OnceCell, pub(super) is_auto_scrolling: Cell, pub(super) sticky: Cell, + pub(super) visible_messages: RefCell>, #[template_child] pub(super) window_title: TemplateChild, #[template_child] @@ -76,6 +78,38 @@ mod imp { widget.show_leave_chat_dialog().await; }, ); + klass.install_action_async( + "chat-history.add-visible-message", + Some("x"), + |widget, _, variant| async move { + let message_id = variant.and_then(|v| v.get()).unwrap(); + if widget + .imp() + .visible_messages + .borrow_mut() + .insert(message_id) + { + println!("ADD {}", message_id); + widget.update_visible_messages().await; + } + }, + ); + klass.install_action_async( + "chat-history.remove-visible-message", + Some("x"), + |widget, _, variant| async move { + let message_id = variant.and_then(|v| v.get()).unwrap(); + if widget + .imp() + .visible_messages + .borrow_mut() + .remove(&message_id) + { + println!("REMOVE {}", message_id); + widget.update_visible_messages().await; + } + }, + ); } fn instance_init(obj: &glib::subclass::InitializingObject) { @@ -217,6 +251,39 @@ impl ChatHistory { } } + async fn update_visible_messages(&self) { + if let Some(chat) = self.chat() { + let client_id = chat.session().client_id(); + let message_ids = self + .imp() + .visible_messages + .borrow() + .clone() + .into_iter() + .collect(); + let result = + tdlib::functions::view_messages(chat.id(), 0, message_ids, false, client_id).await; + + if let Err(e) = result { + log::warn!("Error setting visible messages: {e:?}"); + } + + let msgs: Vec = self + .imp() + .visible_messages + .borrow() + .iter() + .map(|id| { + let message = self.chat().unwrap().message(*id).unwrap(); + format!("{} ||| {}", crate::strings::message_content(&message), id) + }) + .collect(); + dbg!(msgs); + println!(); + println!(); + } + } + fn open_info_dialog(&self) { if let Some(chat) = self.chat() { ChatInfoWindow::new(&self.parent_window(), &chat).present(); diff --git a/src/session/content/message_row/mod.rs b/src/session/content/message_row/mod.rs index db9e0746b..98b4c1f3a 100644 --- a/src/session/content/message_row/mod.rs +++ b/src/session/content/message_row/mod.rs @@ -36,11 +36,13 @@ use crate::utils::spawn; const AVATAR_SIZE: i32 = 32; const SPACING: i32 = 6; +const VISIBLE_MESSAGE_DELAY_MILLIS: u64 = 100; mod imp { use super::*; use once_cell::sync::Lazy; use std::cell::RefCell; + use std::time::Duration; #[derive(Debug, Default, CompositeTemplate)] #[template(string = r#" @@ -124,7 +126,43 @@ mod imp { } } - impl WidgetImpl for MessageRow {} + impl WidgetImpl for MessageRow { + fn map(&self) { + self.parent_map(); + + let obj = self.obj(); + glib::timeout_add_local_once( + Duration::from_millis(VISIBLE_MESSAGE_DELAY_MILLIS), + clone!(@weak obj => move || if obj.is_mapped() { + if let Ok(message) = obj.message().downcast::() { + obj.activate_action( + "chat-history.add-visible-message", + Some(&message.id().to_variant()), + ) + .unwrap(); + } + }), + ); + } + + fn unmap(&self) { + self.parent_unmap(); + + let obj = self.obj(); + glib::timeout_add_local_once( + Duration::from_millis(VISIBLE_MESSAGE_DELAY_MILLIS), + clone!(@weak obj => move || if !obj.is_mapped() { + if let Ok(message) = obj.message().downcast::() { + obj.activate_action( + "chat-history.remove-visible-message", + Some(&message.id().to_variant()), + ) + .unwrap(); + } + }), + ); + } + } } glib::wrapper! { diff --git a/src/session/content/mod.rs b/src/session/content/mod.rs index 2a9b4b30a..fae34d084 100644 --- a/src/session/content/mod.rs +++ b/src/session/content/mod.rs @@ -18,11 +18,13 @@ use self::event_row::EventRow; use self::message_row::MessageRow; use self::send_photo_dialog::SendPhotoDialog; +use glib::clone; use gtk::glib; use gtk::prelude::*; use gtk::subclass::prelude::*; use crate::tdlib::Chat; +use crate::utils::spawn; mod imp { use super::*; @@ -140,7 +142,23 @@ impl Content { imp.stack.set_visible_child(&imp.unselected_chat.get()); } - imp.chat.replace(chat); + // Mark the previous chat as closed, if any + if let Some(old_chat) = imp.chat.replace(chat) { + spawn(clone!(@weak old_chat => async move { + if let Err(e) = old_chat.close().await { + log::warn!("Error closing a chat: {e:?}"); + } + })); + } + + // Mark the new chat as opened, if any + if let Some(chat) = imp.chat.borrow().as_ref() { + spawn(clone!(@weak chat => async move { + if let Err(e) = chat.open().await { + log::warn!("Error opening a chat: {e:?}"); + } + })); + } self.notify("chat"); } diff --git a/src/tdlib/chat.rs b/src/tdlib/chat.rs index 9874a8197..fc24ad432 100644 --- a/src/tdlib/chat.rs +++ b/src/tdlib/chat.rs @@ -264,20 +264,16 @@ impl Chat { self.set_unread_mention_count(update.unread_mention_count) } DeleteMessages(data) => { - // FIXME: This should be removed after we notify opened and closed chats to TDLib - // See discussion here: https://t.me/tdlibchat/65304 - if !data.from_cache { - let mut messages = imp.messages.borrow_mut(); - let deleted_messages: Vec = data - .message_ids - .into_iter() - .filter_map(|id| messages.remove(&id)) - .collect(); - - drop(messages); - for message in deleted_messages { - self.emit_by_name::<()>("deleted-message", &[&message]); - } + let mut messages = imp.messages.borrow_mut(); + let deleted_messages: Vec = data + .message_ids + .into_iter() + .filter_map(|id| messages.remove(&id)) + .collect(); + + drop(messages); + for message in deleted_messages { + self.emit_by_name::<()>("deleted-message", &[&message]); } } MessageContent(ref data) => { @@ -522,6 +518,21 @@ impl Chat { self.imp().messages.borrow().get(&message_id).cloned() } + // Mark this chat as opened. This allows receiving additional updates for this + // chat and it's also needed to correctly manage the internal message cache. + pub(crate) async fn open(&self) -> Result<(), types::Error> { + let chat_id = self.id(); + let client_id = self.session().client_id(); + functions::open_chat(chat_id, client_id).await + } + + // Mark this chat as closed. + pub(crate) async fn close(&self) -> Result<(), types::Error> { + let chat_id = self.id(); + let client_id = self.session().client_id(); + functions::close_chat(chat_id, client_id).await + } + pub(crate) async fn get_chat_history( &self, from_message_id: i64,