deltachat/events/
chatlist_events.rs

1use crate::{EventType, chat::ChatId, contact::ContactId, context::Context};
2
3/// order or content of chatlist changes (chat ids, not the actual chatlist item)
4pub(crate) fn emit_chatlist_changed(context: &Context) {
5    context.emit_event(EventType::ChatlistChanged);
6}
7
8/// Chatlist item of a specific chat changed
9pub(crate) fn emit_chatlist_item_changed(context: &Context, chat_id: ChatId) {
10    context.emit_event(EventType::ChatlistItemChanged {
11        chat_id: Some(chat_id),
12    });
13}
14
15/// Used when you don't know which chatlist items changed, this reloads all cached chatlist items in the UI
16///
17/// Avoid calling this when you can find out the affected chat ids easialy (without extra expensive db queries).
18///
19/// This method is not public, so you have to define and document your new case here in this file.
20fn emit_unknown_chatlist_items_changed(context: &Context) {
21    context.emit_event(EventType::ChatlistItemChanged { chat_id: None });
22}
23
24/// update event for the 1:1 chat with the contact
25/// used when recently seen changes and when profile image changes
26pub(crate) async fn emit_chatlist_item_changed_for_contact_chat(
27    context: &Context,
28    contact_id: ContactId,
29) {
30    match ChatId::lookup_by_contact(context, contact_id).await {
31        Ok(Some(chat_id)) => self::emit_chatlist_item_changed(context, chat_id),
32        Ok(None) => {}
33        Err(error) => error!(
34            context,
35            "failed to find chat id for contact for chatlist event: {error:?}"
36        ),
37    }
38}
39
40/// update items for chats that have the contact
41/// used when contact changes their name or did AEAP for example
42///
43/// The most common case is that the contact changed their name
44/// and their name should be updated in the chatlistitems for the chats
45/// where they sent the last message as there their name is shown in the summary on those
46pub(crate) fn emit_chatlist_items_changed_for_contact(context: &Context, _contact_id: ContactId) {
47    // note:(treefit): it is too expensive to find the right chats
48    // so we'll just tell ui to reload every loaded item
49    emit_unknown_chatlist_items_changed(context)
50    // note:(treefit): in the future we could instead emit an extra event for this and also store contact id in the chatlistitems
51    // (contact id for dm chats and contact id of contact that wrote the message in the summary)
52    // the ui could then look for this info in the cache and only reload the needed chats.
53}
54
55/// Tests for chatlist events
56///
57/// Only checks if the events are emitted,
58/// does not check for excess/too-many events
59#[cfg(test)]
60mod test_chatlist_events {
61
62    use std::{
63        sync::atomic::{AtomicBool, Ordering},
64        time::Duration,
65    };
66
67    use crate::{
68        EventType,
69        chat::{
70            self, ChatId, ChatVisibility, MuteDuration, create_broadcast, create_group, set_muted,
71        },
72        config::Config,
73        constants::*,
74        contact::Contact,
75        message::{self, Message, MessageState},
76        reaction,
77        receive_imf::receive_imf,
78        securejoin::{get_securejoin_qr, join_securejoin},
79        test_utils::{TestContext, TestContextManager},
80    };
81
82    use crate::tools::SystemTime;
83    use anyhow::Result;
84
85    async fn wait_for_chatlist_and_specific_item(context: &TestContext, chat_id: ChatId) {
86        let first_event_is_item = AtomicBool::new(false);
87        context
88            .evtracker
89            .get_matching(|evt| match evt {
90                EventType::ChatlistItemChanged {
91                    chat_id: Some(ev_chat_id),
92                } if ev_chat_id == &chat_id => {
93                    first_event_is_item.store(true, Ordering::Relaxed);
94                    true
95                }
96                EventType::ChatlistChanged => true,
97                _ => false,
98            })
99            .await;
100        if first_event_is_item.load(Ordering::Relaxed) {
101            wait_for_chatlist(context).await;
102        } else {
103            wait_for_chatlist_specific_item(context, chat_id).await;
104        }
105    }
106
107    async fn wait_for_chatlist_specific_item(context: &TestContext, chat_id: ChatId) {
108        context
109            .evtracker
110            .get_matching(|evt| match evt {
111                EventType::ChatlistItemChanged {
112                    chat_id: Some(ev_chat_id),
113                } => ev_chat_id == &chat_id,
114                _ => false,
115            })
116            .await;
117    }
118
119    async fn wait_for_chatlist_all_items(context: &TestContext) {
120        context
121            .evtracker
122            .get_matching(|evt| matches!(evt, EventType::ChatlistItemChanged { chat_id: None }))
123            .await;
124    }
125
126    async fn wait_for_chatlist(context: &TestContext) {
127        context
128            .evtracker
129            .get_matching(|evt| matches!(evt, EventType::ChatlistChanged))
130            .await;
131    }
132
133    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
134    async fn test_change_chat_visibility() -> Result<()> {
135        let mut tcm = TestContextManager::new();
136        let alice = tcm.alice().await;
137        let chat_id = create_group(&alice, "my_group").await?;
138
139        chat_id
140            .set_visibility(&alice, ChatVisibility::Pinned)
141            .await?;
142        wait_for_chatlist_and_specific_item(&alice, chat_id).await;
143
144        chat_id
145            .set_visibility(&alice, ChatVisibility::Archived)
146            .await?;
147        wait_for_chatlist_and_specific_item(&alice, chat_id).await;
148
149        chat_id
150            .set_visibility(&alice, ChatVisibility::Normal)
151            .await?;
152        wait_for_chatlist_and_specific_item(&alice, chat_id).await;
153
154        Ok(())
155    }
156
157    /// mute a chat, archive it, then use another account to send a message to it, the counter on the archived chatlist item should change
158    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
159    async fn test_archived_counter_increases_for_muted_chats() -> Result<()> {
160        let mut tcm = TestContextManager::new();
161        let alice = tcm.alice().await;
162        let bob = tcm.bob().await;
163
164        let chat = alice.create_chat(&bob).await;
165        let sent_msg = alice.send_text(chat.id, "moin").await;
166        bob.recv_msg(&sent_msg).await;
167
168        let bob_chat = bob.create_chat(&alice).await;
169        bob_chat
170            .id
171            .set_visibility(&bob, ChatVisibility::Archived)
172            .await?;
173        set_muted(&bob, bob_chat.id, MuteDuration::Forever).await?;
174
175        bob.evtracker.clear_events();
176
177        let sent_msg = alice.send_text(chat.id, "moin2").await;
178        bob.recv_msg(&sent_msg).await;
179
180        bob.evtracker
181            .get_matching(|evt| match evt {
182                EventType::ChatlistItemChanged {
183                    chat_id: Some(chat_id),
184                } => chat_id.is_archived_link(),
185                _ => false,
186            })
187            .await;
188
189        Ok(())
190    }
191
192    /// Mark noticed on archive-link chatlistitem should update the unread counter on it
193    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
194    async fn test_archived_counter_update_on_mark_noticed() -> Result<()> {
195        let mut tcm = TestContextManager::new();
196        let alice = tcm.alice().await;
197        let bob = tcm.bob().await;
198        let chat = alice.create_chat(&bob).await;
199        let sent_msg = alice.send_text(chat.id, "moin").await;
200        bob.recv_msg(&sent_msg).await;
201        let bob_chat = bob.create_chat(&alice).await;
202        bob_chat
203            .id
204            .set_visibility(&bob, ChatVisibility::Archived)
205            .await?;
206        set_muted(&bob, bob_chat.id, MuteDuration::Forever).await?;
207        let sent_msg = alice.send_text(chat.id, "moin2").await;
208        bob.recv_msg(&sent_msg).await;
209
210        bob.evtracker.clear_events();
211        chat::marknoticed_chat(&bob, DC_CHAT_ID_ARCHIVED_LINK).await?;
212        wait_for_chatlist_specific_item(&bob, DC_CHAT_ID_ARCHIVED_LINK).await;
213
214        Ok(())
215    }
216
217    /// Contact name update - expect all chats to update
218    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
219    async fn test_contact_name_update() -> Result<()> {
220        let mut tcm = TestContextManager::new();
221        let alice = tcm.alice().await;
222        let bob = tcm.bob().await;
223        let alice_to_bob_chat = alice.create_chat(&bob).await;
224        let sent_msg = alice.send_text(alice_to_bob_chat.id, "hello").await;
225        bob.recv_msg(&sent_msg).await;
226
227        bob.evtracker.clear_events();
228        // set alice name then receive messagefrom her with bob
229        alice.set_config(Config::Displayname, Some("Alice")).await?;
230        let sent_msg = alice
231            .send_text(alice_to_bob_chat.id, "hello, I set a displayname")
232            .await;
233        bob.recv_msg(&sent_msg).await;
234        let alice_on_bob = bob.add_or_lookup_contact(&alice).await;
235        assert!(alice_on_bob.get_display_name() == "Alice");
236
237        wait_for_chatlist_all_items(&bob).await;
238
239        bob.evtracker.clear_events();
240        // set name
241        alice_on_bob.id.set_name(&bob, "Alice2").await?;
242        assert!(bob.add_or_lookup_contact(&alice).await.get_display_name() == "Alice2");
243
244        wait_for_chatlist_all_items(&bob).await;
245
246        Ok(())
247    }
248
249    /// Contact changed avatar
250    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
251    async fn test_contact_changed_avatar() -> Result<()> {
252        let mut tcm = TestContextManager::new();
253        let alice = tcm.alice().await;
254        let bob = tcm.bob().await;
255        let alice_to_bob_chat = alice.create_chat(&bob).await;
256        let sent_msg = alice.send_text(alice_to_bob_chat.id, "hello").await;
257        bob.recv_msg(&sent_msg).await;
258
259        bob.evtracker.clear_events();
260        // set alice avatar then receive messagefrom her with bob
261        let file = alice.dir.path().join("avatar.png");
262        let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
263        tokio::fs::write(&file, bytes).await?;
264        alice
265            .set_config(Config::Selfavatar, Some(file.to_str().unwrap()))
266            .await?;
267        let sent_msg = alice
268            .send_text(alice_to_bob_chat.id, "hello, I have a new avatar")
269            .await;
270        bob.recv_msg(&sent_msg).await;
271        let alice_on_bob = bob.add_or_lookup_contact(&alice).await;
272        assert!(alice_on_bob.get_profile_image(&bob).await?.is_some());
273
274        wait_for_chatlist_specific_item(&bob, bob.create_chat(&alice).await.id).await;
275        Ok(())
276    }
277
278    /// Delete chat
279    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
280    async fn test_delete_chat() -> Result<()> {
281        let mut tcm = TestContextManager::new();
282        let alice = tcm.alice().await;
283        let chat = create_group(&alice, "My Group").await?;
284
285        alice.evtracker.clear_events();
286        chat.delete(&alice).await?;
287        wait_for_chatlist(&alice).await;
288        Ok(())
289    }
290
291    /// Create group chat
292    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
293    async fn test_create_group() -> Result<()> {
294        let mut tcm = TestContextManager::new();
295        let alice = tcm.alice().await;
296        alice.evtracker.clear_events();
297        let chat = create_group(&alice, "My Group").await?;
298        wait_for_chatlist_and_specific_item(&alice, chat).await;
299        Ok(())
300    }
301
302    /// Create broadcast channel
303    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
304    async fn test_create_broadcast() -> Result<()> {
305        let mut tcm = TestContextManager::new();
306        let alice = tcm.alice().await;
307        alice.evtracker.clear_events();
308        create_broadcast(&alice, "Channel".to_string()).await?;
309        wait_for_chatlist(&alice).await;
310        Ok(())
311    }
312
313    /// Mute chat
314    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
315    async fn test_mute_chat() -> Result<()> {
316        let mut tcm = TestContextManager::new();
317        let alice = tcm.alice().await;
318        let chat = create_group(&alice, "My Group").await?;
319
320        alice.evtracker.clear_events();
321        chat::set_muted(&alice, chat, MuteDuration::Forever).await?;
322        wait_for_chatlist_specific_item(&alice, chat).await;
323
324        alice.evtracker.clear_events();
325        chat::set_muted(&alice, chat, MuteDuration::NotMuted).await?;
326        wait_for_chatlist_specific_item(&alice, chat).await;
327
328        Ok(())
329    }
330
331    /// Expiry of mute should also trigger an event
332    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
333    #[ignore = "does not work yet"]
334    async fn test_mute_chat_expired() -> Result<()> {
335        let mut tcm = TestContextManager::new();
336        let alice = tcm.alice().await;
337        let chat = create_group(&alice, "My Group").await?;
338
339        let mute_duration = MuteDuration::Until(
340            std::time::SystemTime::now()
341                .checked_add(Duration::from_secs(2))
342                .unwrap(),
343        );
344        chat::set_muted(&alice, chat, mute_duration).await?;
345        alice.evtracker.clear_events();
346        SystemTime::shift(Duration::from_secs(3));
347        wait_for_chatlist_specific_item(&alice, chat).await;
348
349        Ok(())
350    }
351
352    /// Change chat name
353    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
354    async fn test_change_chat_name() -> Result<()> {
355        let mut tcm = TestContextManager::new();
356        let alice = tcm.alice().await;
357        let chat = create_group(&alice, "My Group").await?;
358
359        alice.evtracker.clear_events();
360        chat::set_chat_name(&alice, chat, "New Name").await?;
361        wait_for_chatlist_specific_item(&alice, chat).await;
362
363        Ok(())
364    }
365
366    /// Change chat profile image
367    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
368    async fn test_change_chat_profile_image() -> Result<()> {
369        let mut tcm = TestContextManager::new();
370        let alice = tcm.alice().await;
371        let chat = create_group(&alice, "My Group").await?;
372
373        alice.evtracker.clear_events();
374        let file = alice.dir.path().join("avatar.png");
375        let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
376        tokio::fs::write(&file, bytes).await?;
377        chat::set_chat_profile_image(&alice, chat, file.to_str().unwrap()).await?;
378        wait_for_chatlist_specific_item(&alice, chat).await;
379
380        Ok(())
381    }
382
383    /// Receive group and receive name change
384    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
385    async fn test_receiving_group_and_group_changes() -> Result<()> {
386        let mut tcm = TestContextManager::new();
387        let alice = tcm.alice().await;
388        let bob = tcm.bob().await;
389        let chat = alice.create_group_with_members("My Group", &[&bob]).await;
390
391        let sent_msg = alice.send_text(chat, "Hello").await;
392        let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
393        wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
394        chat_id_for_bob.accept(&bob).await?;
395
396        bob.evtracker.clear_events();
397        chat::set_chat_name(&alice, chat, "New Name").await?;
398        let sent_msg = alice.send_text(chat, "Hello").await;
399        bob.recv_msg(&sent_msg).await;
400        wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
401
402        Ok(())
403    }
404
405    /// Accept contact request
406    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
407    async fn test_accept_contact_request() -> Result<()> {
408        let mut tcm = TestContextManager::new();
409        let alice = tcm.alice().await;
410        let bob = tcm.bob().await;
411        let chat = alice.create_group_with_members("My Group", &[&bob]).await;
412        let sent_msg = alice.send_text(chat, "Hello").await;
413        let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
414
415        bob.evtracker.clear_events();
416        chat_id_for_bob.accept(&bob).await?;
417        wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
418
419        Ok(())
420    }
421
422    /// Block contact request
423    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
424    async fn test_block_contact_request() -> Result<()> {
425        let mut tcm = TestContextManager::new();
426        let alice = tcm.alice().await;
427        let bob = tcm.bob().await;
428        let chat = alice.create_group_with_members("My Group", &[&bob]).await;
429        let sent_msg = alice.send_text(chat, "Hello").await;
430        let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
431
432        bob.evtracker.clear_events();
433        chat_id_for_bob.block(&bob).await?;
434        wait_for_chatlist(&bob).await;
435
436        Ok(())
437    }
438
439    /// Delete message
440    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
441    async fn test_delete_message() -> Result<()> {
442        let mut tcm = TestContextManager::new();
443        let alice = tcm.alice().await;
444        let chat = create_group(&alice, "My Group").await?;
445        let message = chat::send_text_msg(&alice, chat, "Hello World".to_owned()).await?;
446
447        alice.evtracker.clear_events();
448        message::delete_msgs(&alice, &[message]).await?;
449        wait_for_chatlist_specific_item(&alice, chat).await;
450
451        Ok(())
452    }
453
454    /// Click on chat should remove the unread count (on msgs noticed)
455    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
456    async fn test_msgs_noticed_on_chat() -> Result<()> {
457        let mut tcm = TestContextManager::new();
458        let alice = tcm.alice().await;
459        let bob = tcm.bob().await;
460
461        let chat = alice.create_group_with_members("My Group", &[&bob]).await;
462        let sent_msg = alice.send_text(chat, "Hello").await;
463        let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
464        chat_id_for_bob.accept(&bob).await?;
465
466        let sent_msg = alice.send_text(chat, "New Message").await;
467        let chat_id_for_bob = bob.recv_msg(&sent_msg).await.chat_id;
468        assert!(chat_id_for_bob.get_fresh_msg_cnt(&bob).await? >= 1);
469
470        bob.evtracker.clear_events();
471        chat::marknoticed_chat(&bob, chat_id_for_bob).await?;
472        wait_for_chatlist_specific_item(&bob, chat_id_for_bob).await;
473
474        Ok(())
475    }
476
477    // Block and Unblock contact
478    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
479    async fn test_unblock_contact() -> Result<()> {
480        let mut tcm = TestContextManager::new();
481        let alice = tcm.alice().await;
482        let contact_id = Contact::create(&alice, "example", "example@example.com").await?;
483        let _ = ChatId::create_for_contact(&alice, contact_id).await;
484
485        alice.evtracker.clear_events();
486        Contact::block(&alice, contact_id).await?;
487        wait_for_chatlist(&alice).await;
488
489        alice.evtracker.clear_events();
490        Contact::unblock(&alice, contact_id).await?;
491        wait_for_chatlist(&alice).await;
492
493        Ok(())
494    }
495
496    /// Tests that expired disappearing message
497    /// produces events about chatlist being modified.
498    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
499    async fn test_update_after_ephemeral_messages() -> Result<()> {
500        let mut tcm = TestContextManager::new();
501        let alice = tcm.alice().await;
502        let chat = create_group(&alice, "My Group").await?;
503        chat.set_ephemeral_timer(&alice, crate::ephemeral::Timer::Enabled { duration: 60 })
504            .await?;
505        alice
506            .evtracker
507            .get_matching(|evt| matches!(evt, EventType::ChatEphemeralTimerModified { .. }))
508            .await;
509
510        let _ = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
511        wait_for_chatlist_and_specific_item(&alice, chat).await;
512
513        SystemTime::shift(Duration::from_secs(70));
514        crate::ephemeral::delete_expired_messages(&alice, crate::tools::time()).await?;
515        wait_for_chatlist_and_specific_item(&alice, chat).await;
516
517        Ok(())
518    }
519
520    /// AdHoc (Groups without a group ID.) group receiving
521    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
522    async fn test_adhoc_group() -> Result<()> {
523        let alice = TestContext::new_alice().await;
524        let mime = br#"Subject: First thread
525Message-ID: first@example.org
526To: Alice <alice@example.org>, Bob <bob@example.net>
527From: Claire <claire@example.org>
528Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
529
530First thread."#;
531
532        alice.evtracker.clear_events();
533        receive_imf(&alice, mime, false).await?;
534        wait_for_chatlist(&alice).await;
535
536        Ok(())
537    }
538
539    /// Test both direction of securejoin
540    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
541    async fn test_secure_join_group() -> Result<()> {
542        let mut tcm = TestContextManager::new();
543        let alice = tcm.alice().await;
544        let bob = tcm.bob().await;
545
546        let alice_chatid = chat::create_group(&alice.ctx, "the chat").await?;
547
548        // Step 1: Generate QR-code, secure-join implied by chatid
549        let qr = get_securejoin_qr(&alice.ctx, Some(alice_chatid)).await?;
550
551        // Step 2: Bob scans QR-code, sends vg-request
552        bob.evtracker.clear_events();
553        let bob_chatid = join_securejoin(&bob.ctx, &qr).await?;
554        wait_for_chatlist(&bob).await;
555
556        let sent = bob.pop_sent_msg().await;
557
558        // Step 3: Alice receives vg-request, sends vg-auth-required
559        alice.evtracker.clear_events();
560        alice.recv_msg_trash(&sent).await;
561
562        let sent = alice.pop_sent_msg().await;
563
564        // Step 4: Bob receives vg-auth-required, sends vg-request-with-auth
565        bob.evtracker.clear_events();
566        bob.recv_msg_trash(&sent).await;
567        wait_for_chatlist_and_specific_item(&bob, bob_chatid).await;
568
569        let sent = bob.pop_sent_msg().await;
570
571        // Step 5+6: Alice receives vg-request-with-auth, sends vg-member-added
572        alice.evtracker.clear_events();
573        alice.recv_msg_trash(&sent).await;
574        wait_for_chatlist_and_specific_item(&alice, alice_chatid).await;
575
576        let sent = alice.pop_sent_msg().await;
577
578        // Step 7: Bob receives vg-member-added
579        bob.evtracker.clear_events();
580        bob.recv_msg(&sent).await;
581        wait_for_chatlist_and_specific_item(&bob, bob_chatid).await;
582
583        Ok(())
584    }
585
586    /// Call Resend on message
587    ///
588    /// (the event is technically only needed if it is the last message in the chat, but checking that would be too expensive so the event is always emitted)
589    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
590    async fn test_resend_message() -> Result<()> {
591        let mut tcm = TestContextManager::new();
592        let alice = tcm.alice().await;
593        let chat = create_group(&alice, "My Group").await?;
594
595        let msg_id = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
596        let _ = alice.pop_sent_msg().await;
597
598        let message = Message::load_from_db(&alice, msg_id).await?;
599        assert_eq!(message.get_state(), MessageState::OutDelivered);
600
601        alice.evtracker.clear_events();
602        chat::resend_msgs(&alice, &[msg_id]).await?;
603        wait_for_chatlist_specific_item(&alice, chat).await;
604
605        Ok(())
606    }
607
608    /// test that setting a reaction emits chatlistitem update event
609    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
610    async fn test_reaction() -> Result<()> {
611        let mut tcm = TestContextManager::new();
612        let alice = tcm.alice().await;
613        let chat = create_group(&alice, "My Group").await?;
614        let msg_id = chat::send_text_msg(&alice, chat, "Hello".to_owned()).await?;
615        let _ = alice.pop_sent_msg().await;
616
617        alice.evtracker.clear_events();
618        reaction::send_reaction(&alice, msg_id, "👍").await?;
619        let _ = alice.pop_sent_msg().await;
620        wait_for_chatlist_specific_item(&alice, chat).await;
621
622        Ok(())
623    }
624}