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