From b6a12e39141c8d20f09870d4abaac7a319391e31 Mon Sep 17 00:00:00 2001 From: iequidoo Date: Fri, 5 Apr 2024 22:48:32 -0300 Subject: [PATCH] fix: Fix emitting ContactsChanged events on "recently seen" status change (#5377) - Always emit `ContactsChanged` from `contact::update_last_seen()` if a contact was seen recently just for simplicity and symmetry with `RecentlySeenLoop::run()` which also may emit several events for single contact. - Fix sleep time calculation in `RecentlySeenLoop::run()` -- `now` must be updated on every iteration, before the initial value was used every time which led to progressively long sleeps. --- src/contact.rs | 58 +++++++++++++++++++++++++++++++++++++++++-- src/events/payload.rs | 2 +- src/scheduler.rs | 2 +- src/test_utils.rs | 5 +++- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/contact.rs b/src/contact.rs index 131a5cd0c7..5d57e3804e 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -1632,6 +1632,7 @@ pub(crate) async fn update_last_seen( > 0 && timestamp > time() - SEEN_RECENTLY_SECONDS { + context.emit_event(EventType::ContactsChanged(Some(contact_id))); context .scheduler .interrupt_recently_seen(contact_id, timestamp) @@ -1762,6 +1763,7 @@ impl RecentlySeenLoop { .unwrap_or_default(); loop { + let now = SystemTime::now(); let (until, contact_id) = if let Some((Reverse(timestamp), contact_id)) = unseen_queue.peek() { ( @@ -1804,7 +1806,10 @@ impl RecentlySeenLoop { timestamp, })) => { // Received an interrupt. - unseen_queue.push((Reverse(timestamp + SEEN_RECENTLY_SECONDS), contact_id)); + if contact_id != ContactId::UNDEFINED { + unseen_queue + .push((Reverse(timestamp + SEEN_RECENTLY_SECONDS), contact_id)); + } } } } else { @@ -1822,7 +1827,7 @@ impl RecentlySeenLoop { } } - pub(crate) fn interrupt(&self, contact_id: ContactId, timestamp: i64) { + pub(crate) fn try_interrupt(&self, contact_id: ContactId, timestamp: i64) { self.interrupt_send .try_send(RecentlySeenInterrupt { contact_id, @@ -1831,6 +1836,17 @@ impl RecentlySeenLoop { .ok(); } + #[cfg(test)] + pub(crate) async fn interrupt(&self, contact_id: ContactId, timestamp: i64) { + self.interrupt_send + .send(RecentlySeenInterrupt { + contact_id, + timestamp, + }) + .await + .unwrap(); + } + pub(crate) fn abort(self) { self.handle.abort(); } @@ -2812,6 +2828,44 @@ Hi."#; Ok(()) } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_was_seen_recently_event() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = tcm.alice().await; + let bob = tcm.bob().await; + let recently_seen_loop = RecentlySeenLoop::new(bob.ctx.clone()); + let chat = bob.create_chat(&alice).await; + let contacts = chat::get_chat_contacts(&bob, chat.id).await?; + + for _ in 0..2 { + let chat = alice.create_chat(&bob).await; + let sent_msg = alice.send_text(chat.id, "moin").await; + let contact = Contact::get_by_id(&bob, *contacts.first().unwrap()).await?; + assert!(!contact.was_seen_recently()); + while bob.evtracker.try_recv().is_ok() {} + bob.recv_msg(&sent_msg).await; + let contact = Contact::get_by_id(&bob, *contacts.first().unwrap()).await?; + assert!(contact.was_seen_recently()); + bob.evtracker + .get_matching(|evt| matches!(evt, EventType::ContactsChanged { .. })) + .await; + recently_seen_loop + .interrupt(contact.id, contact.last_seen) + .await; + + // Wait for `was_seen_recently()` to turn off. + while bob.evtracker.try_recv().is_ok() {} + SystemTime::shift(Duration::from_secs(SEEN_RECENTLY_SECONDS as u64 * 2)); + recently_seen_loop.interrupt(ContactId::UNDEFINED, 0).await; + let contact = Contact::get_by_id(&bob, *contacts.first().unwrap()).await?; + assert!(!contact.was_seen_recently()); + bob.evtracker + .get_matching(|evt| matches!(evt, EventType::ContactsChanged { .. })) + .await; + } + Ok(()) + } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_verified_by_none() -> Result<()> { let mut tcm = TestContextManager::new(); diff --git a/src/events/payload.rs b/src/events/payload.rs index 8771aee28c..d980831e7d 100644 --- a/src/events/payload.rs +++ b/src/events/payload.rs @@ -182,7 +182,7 @@ pub enum EventType { timer: EphemeralTimer, }, - /// Contact(s) created, renamed, blocked or deleted. + /// Contact(s) created, renamed, blocked, deleted or changed their "recently seen" status. /// /// @param data1 (int) If set, this is the contact_id of an added contact that should be selected. ContactsChanged(Option), diff --git a/src/scheduler.rs b/src/scheduler.rs index 651d50b867..9a3592fe7e 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -924,7 +924,7 @@ impl Scheduler { } fn interrupt_recently_seen(&self, contact_id: ContactId, timestamp: i64) { - self.recently_seen_loop.interrupt(contact_id, timestamp); + self.recently_seen_loop.try_interrupt(contact_id, timestamp); } /// Halt the scheduler. diff --git a/src/test_utils.rs b/src/test_utils.rs index f8ff5ba704..2d2c677d75 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -1100,7 +1100,10 @@ fn print_event(event: &Event) { "Received MSGS_CHANGED(chat_id={chat_id}, msg_id={msg_id})", )) ), - EventType::ContactsChanged(_) => format!("{}", green.paint("Received CONTACTS_CHANGED()")), + EventType::ContactsChanged(contact) => format!( + "{}", + green.paint(format!("Received CONTACTS_CHANGED(contact={contact:?})")) + ), EventType::LocationChanged(contact) => format!( "{}", green.paint(format!("Received LOCATION_CHANGED(contact={contact:?})"))