1use anyhow::{Context as _, Result, ensure};
4use std::sync::LazyLock;
5
6use crate::chat::{Chat, ChatId, ChatVisibility, update_special_chat_names};
7use crate::constants::{
8 Blocked, Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_GCL_ADD_ALLDONE_HINT,
9 DC_GCL_ARCHIVED_ONLY, DC_GCL_FOR_FORWARDING, DC_GCL_NO_SPECIALS,
10};
11use crate::contact::{Contact, ContactId};
12use crate::context::Context;
13use crate::log::warn;
14use crate::message::{Message, MessageState, MsgId};
15use crate::param::{Param, Params};
16use crate::stock_str;
17use crate::summary::Summary;
18use crate::tools::IsNoneOrEmpty;
19
20pub static IS_UNREAD_FILTER: LazyLock<regex::Regex> =
22 LazyLock::new(|| regex::Regex::new(r"\bis:unread\b").unwrap());
23
24#[derive(Debug)]
46pub struct Chatlist {
47 ids: Vec<(ChatId, Option<MsgId>)>,
49}
50
51impl Chatlist {
52 pub async fn try_load(
94 context: &Context,
95 listflags: usize,
96 query: Option<&str>,
97 query_contact_id: Option<ContactId>,
98 ) -> Result<Self> {
99 let flag_archived_only = 0 != listflags & DC_GCL_ARCHIVED_ONLY;
100 let flag_for_forwarding = 0 != listflags & DC_GCL_FOR_FORWARDING;
101 let flag_no_specials = 0 != listflags & DC_GCL_NO_SPECIALS;
102 let flag_add_alldone_hint = 0 != listflags & DC_GCL_ADD_ALLDONE_HINT;
103
104 let process_row = |row: &rusqlite::Row| {
105 let chat_id: ChatId = row.get(0)?;
106 let msg_id: Option<MsgId> = row.get(1)?;
107 Ok((chat_id, msg_id))
108 };
109
110 let process_rows = |rows: rusqlite::MappedRows<_>| {
111 rows.collect::<std::result::Result<Vec<_>, _>>()
112 .map_err(Into::into)
113 };
114
115 let skip_id = if flag_for_forwarding {
116 ChatId::lookup_by_contact(context, ContactId::DEVICE)
117 .await?
118 .unwrap_or_default()
119 } else {
120 ChatId::new(0)
121 };
122
123 let ids = if let Some(query_contact_id) = query_contact_id {
134 context.sql.query_map(
136 "SELECT c.id, m.id
137 FROM chats c
138 LEFT JOIN msgs m
139 ON c.id=m.chat_id
140 AND m.id=(
141 SELECT id
142 FROM msgs
143 WHERE chat_id=c.id
144 AND (hidden=0 OR state=?1)
145 ORDER BY timestamp DESC, id DESC LIMIT 1)
146 WHERE c.id>9
147 AND c.blocked!=1
148 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?2 AND add_timestamp >= remove_timestamp)
149 GROUP BY c.id
150 ORDER BY c.archived=?3 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
151 (MessageState::OutDraft, query_contact_id, ChatVisibility::Pinned),
152 process_row,
153 process_rows,
154 ).await?
155 } else if flag_archived_only {
156 context
161 .sql
162 .query_map(
163 "SELECT c.id, m.id
164 FROM chats c
165 LEFT JOIN msgs m
166 ON c.id=m.chat_id
167 AND m.id=(
168 SELECT id
169 FROM msgs
170 WHERE chat_id=c.id
171 AND (hidden=0 OR state=?)
172 ORDER BY timestamp DESC, id DESC LIMIT 1)
173 WHERE c.id>9
174 AND c.blocked!=1
175 AND c.archived=1
176 GROUP BY c.id
177 ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
178 (MessageState::OutDraft,),
179 process_row,
180 process_rows,
181 )
182 .await?
183 } else if let Some(query) = query {
184 let mut query = query.trim().to_string();
185 ensure!(!query.is_empty(), "query mustn't be empty");
186 let only_unread = IS_UNREAD_FILTER.find(&query).is_some();
187 query = IS_UNREAD_FILTER.replace(&query, "").trim().to_string();
188
189 if let Err(err) = update_special_chat_names(context).await {
192 warn!(context, "Cannot update special chat names: {err:#}.")
193 }
194
195 let str_like_cmd = format!("%{query}%");
196 context
197 .sql
198 .query_map(
199 "SELECT c.id, m.id
200 FROM chats c
201 LEFT JOIN msgs m
202 ON c.id=m.chat_id
203 AND m.id=(
204 SELECT id
205 FROM msgs
206 WHERE chat_id=c.id
207 AND (hidden=0 OR state=?1)
208 ORDER BY timestamp DESC, id DESC LIMIT 1)
209 WHERE c.id>9 AND c.id!=?2
210 AND c.blocked!=1
211 AND c.name LIKE ?3
212 AND (NOT ?4 OR EXISTS (SELECT 1 FROM msgs m WHERE m.chat_id = c.id AND m.state == ?5 AND hidden=0))
213 GROUP BY c.id
214 ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
215 (MessageState::OutDraft, skip_id, str_like_cmd, only_unread, MessageState::InFresh),
216 process_row,
217 process_rows,
218 )
219 .await?
220 } else {
221 let mut ids = if flag_for_forwarding {
222 let sort_id_up = ChatId::lookup_by_contact(context, ContactId::SELF)
223 .await?
224 .unwrap_or_default();
225 let process_row = |row: &rusqlite::Row| {
226 let chat_id: ChatId = row.get(0)?;
227 let typ: Chattype = row.get(1)?;
228 let param: Params = row.get::<_, String>(2)?.parse().unwrap_or_default();
229 let msg_id: Option<MsgId> = row.get(3)?;
230 Ok((chat_id, typ, param, msg_id))
231 };
232 let process_rows = |rows: rusqlite::MappedRows<_>| {
233 rows.filter_map(|row: std::result::Result<(_, _, Params, _), _>| match row {
234 Ok((chat_id, typ, param, msg_id)) => {
235 if typ == Chattype::Mailinglist
236 && param.get(Param::ListPost).is_none_or_empty()
237 {
238 None
239 } else {
240 Some(Ok((chat_id, msg_id)))
241 }
242 }
243 Err(e) => Some(Err(e)),
244 })
245 .collect::<std::result::Result<Vec<_>, _>>()
246 .map_err(Into::into)
247 };
248 context.sql.query_map(
249 "SELECT c.id, c.type, c.param, m.id
250 FROM chats c
251 LEFT JOIN msgs m
252 ON c.id=m.chat_id
253 AND m.id=(
254 SELECT id
255 FROM msgs
256 WHERE chat_id=c.id
257 AND (hidden=0 OR state=?)
258 ORDER BY timestamp DESC, id DESC LIMIT 1)
259 WHERE c.id>9 AND c.id!=?
260 AND c.blocked=0
261 AND NOT c.archived=?
262 AND (c.type!=? OR c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=? AND add_timestamp >= remove_timestamp))
263 GROUP BY c.id
264 ORDER BY c.id=? DESC, c.archived=? DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
265 (
266 MessageState::OutDraft, skip_id, ChatVisibility::Archived,
267 Chattype::Group, ContactId::SELF,
268 sort_id_up, ChatVisibility::Pinned,
269 ),
270 process_row,
271 process_rows,
272 ).await?
273 } else {
274 context.sql.query_map(
276 "SELECT c.id, m.id
277 FROM chats c
278 LEFT JOIN msgs m
279 ON c.id=m.chat_id
280 AND m.id=(
281 SELECT id
282 FROM msgs
283 WHERE chat_id=c.id
284 AND (hidden=0 OR state=?)
285 ORDER BY timestamp DESC, id DESC LIMIT 1)
286 WHERE c.id>9 AND c.id!=?
287 AND (c.blocked=0 OR c.blocked=2)
288 AND NOT c.archived=?
289 GROUP BY c.id
290 ORDER BY c.id=0 DESC, c.archived=? DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
291 (MessageState::OutDraft, skip_id, ChatVisibility::Archived, ChatVisibility::Pinned),
292 process_row,
293 process_rows,
294 ).await?
295 };
296 if !flag_no_specials && get_archived_cnt(context).await? > 0 {
297 if ids.is_empty() && flag_add_alldone_hint {
298 ids.push((DC_CHAT_ID_ALLDONE_HINT, None));
299 }
300 ids.insert(0, (DC_CHAT_ID_ARCHIVED_LINK, None));
301 }
302 ids
303 };
304
305 Ok(Chatlist { ids })
306 }
307
308 pub(crate) async fn from_chat_ids(context: &Context, chat_ids: &[ChatId]) -> Result<Self> {
310 let mut ids = Vec::new();
311 for &chat_id in chat_ids {
312 let msg_id: Option<MsgId> = context
313 .sql
314 .query_get_value(
315 "SELECT id
316 FROM msgs
317 WHERE chat_id=?1
318 AND (hidden=0 OR state=?2)
319 ORDER BY timestamp DESC, id DESC LIMIT 1",
320 (chat_id, MessageState::OutDraft),
321 )
322 .await
323 .with_context(|| format!("failed to get msg ID for chat {chat_id}"))?;
324 ids.push((chat_id, msg_id));
325 }
326 Ok(Chatlist { ids })
327 }
328
329 pub fn len(&self) -> usize {
331 self.ids.len()
332 }
333
334 pub fn is_empty(&self) -> bool {
336 self.ids.is_empty()
337 }
338
339 pub fn get_chat_id(&self, index: usize) -> Result<ChatId> {
343 let (chat_id, _msg_id) = self
344 .ids
345 .get(index)
346 .context("chatlist index is out of range")?;
347 Ok(*chat_id)
348 }
349
350 pub fn get_msg_id(&self, index: usize) -> Result<Option<MsgId>> {
354 let (_chat_id, msg_id) = self
355 .ids
356 .get(index)
357 .context("chatlist index is out of range")?;
358 Ok(*msg_id)
359 }
360
361 pub async fn get_summary(
363 &self,
364 context: &Context,
365 index: usize,
366 chat: Option<&Chat>,
367 ) -> Result<Summary> {
368 let (chat_id, lastmsg_id) = self
373 .ids
374 .get(index)
375 .context("chatlist index is out of range")?;
376 Chatlist::get_summary2(context, *chat_id, *lastmsg_id, chat).await
377 }
378
379 pub async fn get_summary2(
381 context: &Context,
382 chat_id: ChatId,
383 lastmsg_id: Option<MsgId>,
384 chat: Option<&Chat>,
385 ) -> Result<Summary> {
386 let chat_loaded: Chat;
387 let chat = if let Some(chat) = chat {
388 chat
389 } else {
390 let chat = Chat::load_from_db(context, chat_id).await?;
391 chat_loaded = chat;
392 &chat_loaded
393 };
394
395 let lastmsg = if let Some(lastmsg_id) = lastmsg_id {
396 Message::load_from_db_optional(context, lastmsg_id)
399 .await
400 .context("Loading message failed")?
401 } else {
402 None
403 };
404
405 let lastcontact = if let Some(lastmsg) = &lastmsg {
406 if lastmsg.from_id == ContactId::SELF {
407 None
408 } else if chat.typ == Chattype::Group
409 || chat.typ == Chattype::OutBroadcast
410 || chat.typ == Chattype::InBroadcast
411 || chat.typ == Chattype::Mailinglist
412 || chat.is_self_talk()
413 {
414 let lastcontact = Contact::get_by_id(context, lastmsg.from_id)
415 .await
416 .context("loading contact failed")?;
417 Some(lastcontact)
418 } else {
419 None
420 }
421 } else {
422 None
423 };
424
425 if chat.id.is_archived_link() {
426 Ok(Default::default())
427 } else if let Some(lastmsg) = lastmsg.filter(|msg| msg.from_id != ContactId::UNDEFINED) {
428 Summary::new_with_reaction_details(context, &lastmsg, chat, lastcontact.as_ref()).await
429 } else {
430 Ok(Summary {
431 text: stock_str::no_messages(context).await,
432 ..Default::default()
433 })
434 }
435 }
436
437 pub fn get_index_for_id(&self, id: ChatId) -> Option<usize> {
439 self.ids.iter().position(|(chat_id, _)| chat_id == &id)
440 }
441
442 pub fn iter(&self) -> impl Iterator<Item = &(ChatId, Option<MsgId>)> {
444 self.ids.iter()
445 }
446}
447
448pub async fn get_archived_cnt(context: &Context) -> Result<usize> {
450 let count = context
451 .sql
452 .count(
453 "SELECT COUNT(*) FROM chats WHERE blocked!=? AND archived=?;",
454 (Blocked::Yes, ChatVisibility::Archived),
455 )
456 .await?;
457 Ok(count)
458}
459
460pub async fn get_last_message_for_chat(
463 context: &Context,
464 chat_id: ChatId,
465) -> Result<Option<MsgId>> {
466 context
467 .sql
468 .query_get_value(
469 "SELECT id
470 FROM msgs
471 WHERE chat_id=?2
472 AND (hidden=0 OR state=?1)
473 ORDER BY timestamp DESC, id DESC LIMIT 1",
474 (MessageState::OutDraft, chat_id),
475 )
476 .await
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482 use crate::chat::save_msgs;
483 use crate::chat::{
484 ProtectionStatus, add_contact_to_chat, create_group_chat, get_chat_contacts,
485 remove_contact_from_chat, send_text_msg,
486 };
487 use crate::receive_imf::receive_imf;
488 use crate::stock_str::StockMessage;
489 use crate::test_utils::TestContext;
490 use crate::test_utils::TestContextManager;
491 use crate::tools::SystemTime;
492 use std::time::Duration;
493
494 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
495 async fn test_try_load() {
496 let mut tcm = TestContextManager::new();
497 let bob = &tcm.bob().await;
498 let chat_id1 = create_group_chat(bob, ProtectionStatus::Unprotected, "a chat")
499 .await
500 .unwrap();
501 let chat_id2 = create_group_chat(bob, ProtectionStatus::Unprotected, "b chat")
502 .await
503 .unwrap();
504 let chat_id3 = create_group_chat(bob, ProtectionStatus::Unprotected, "c chat")
505 .await
506 .unwrap();
507
508 let chats = Chatlist::try_load(bob, 0, None, None).await.unwrap();
510 assert_eq!(chats.len(), 3);
511 assert_eq!(chats.get_chat_id(0).unwrap(), chat_id3);
512 assert_eq!(chats.get_chat_id(1).unwrap(), chat_id2);
513 assert_eq!(chats.get_chat_id(2).unwrap(), chat_id1);
514
515 SystemTime::shift(Duration::from_secs(5));
516
517 for chat_id in &[chat_id1, chat_id3, chat_id2] {
526 let mut msg = Message::new_text("hello".to_string());
527 chat_id.set_draft(bob, Some(&mut msg)).await.unwrap();
528 }
529
530 let chats = Chatlist::try_load(bob, 0, None, None).await.unwrap();
531 assert_eq!(chats.get_chat_id(0).unwrap(), chat_id2);
532
533 let chats = Chatlist::try_load(bob, 0, Some("b"), None).await.unwrap();
535 assert_eq!(chats.len(), 1);
536
537 let alice = &tcm.alice().await;
539 let alice_chat_id = create_group_chat(alice, ProtectionStatus::Unprotected, "alice chat")
540 .await
541 .unwrap();
542 add_contact_to_chat(
543 alice,
544 alice_chat_id,
545 alice.add_or_lookup_contact_id(bob).await,
546 )
547 .await
548 .unwrap();
549 send_text_msg(alice, alice_chat_id, "hi".into())
550 .await
551 .unwrap();
552 let sent_msg = alice.pop_sent_msg().await;
553
554 bob.recv_msg(&sent_msg).await;
555 let chats = Chatlist::try_load(bob, 0, Some("is:unread"), None)
556 .await
557 .unwrap();
558 assert_eq!(chats.len(), 1);
559
560 let chats = Chatlist::try_load(bob, DC_GCL_ARCHIVED_ONLY, None, None)
561 .await
562 .unwrap();
563 assert_eq!(chats.len(), 0);
564
565 chat_id1
566 .set_visibility(bob, ChatVisibility::Archived)
567 .await
568 .ok();
569 let chats = Chatlist::try_load(bob, DC_GCL_ARCHIVED_ONLY, None, None)
570 .await
571 .unwrap();
572 assert_eq!(chats.len(), 1);
573 }
574
575 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
576 async fn test_sort_self_talk_up_on_forward() {
577 let t = TestContext::new_alice().await;
578 t.update_device_chats().await.unwrap();
579 create_group_chat(&t, ProtectionStatus::Unprotected, "a chat")
580 .await
581 .unwrap();
582
583 let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
584 assert_eq!(chats.len(), 3);
585 assert!(
586 !Chat::load_from_db(&t, chats.get_chat_id(0).unwrap())
587 .await
588 .unwrap()
589 .is_self_talk()
590 );
591
592 let chats = Chatlist::try_load(&t, DC_GCL_FOR_FORWARDING, None, None)
593 .await
594 .unwrap();
595 assert_eq!(chats.len(), 2); assert!(
597 Chat::load_from_db(&t, chats.get_chat_id(0).unwrap())
598 .await
599 .unwrap()
600 .is_self_talk()
601 );
602
603 remove_contact_from_chat(&t, chats.get_chat_id(1).unwrap(), ContactId::SELF)
604 .await
605 .unwrap();
606 let chats = Chatlist::try_load(&t, DC_GCL_FOR_FORWARDING, None, None)
607 .await
608 .unwrap();
609 assert_eq!(chats.len(), 1);
610 }
611
612 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
613 async fn test_search_special_chat_names() {
614 let t = TestContext::new_alice().await;
615 t.update_device_chats().await.unwrap();
616
617 let chats = Chatlist::try_load(&t, 0, Some("t-1234-s"), None)
618 .await
619 .unwrap();
620 assert_eq!(chats.len(), 0);
621 let chats = Chatlist::try_load(&t, 0, Some("t-5678-b"), None)
622 .await
623 .unwrap();
624 assert_eq!(chats.len(), 0);
625
626 t.set_stock_translation(StockMessage::SavedMessages, "test-1234-save".to_string())
627 .await
628 .unwrap();
629 let chats = Chatlist::try_load(&t, 0, Some("t-1234-s"), None)
630 .await
631 .unwrap();
632 assert_eq!(chats.len(), 1);
633
634 t.set_stock_translation(StockMessage::DeviceMessages, "test-5678-babbel".to_string())
635 .await
636 .unwrap();
637 let chats = Chatlist::try_load(&t, 0, Some("t-5678-b"), None)
638 .await
639 .unwrap();
640 assert_eq!(chats.len(), 1);
641 }
642
643 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
644 async fn test_search_single_chat() -> anyhow::Result<()> {
645 let t = TestContext::new_alice().await;
646
647 receive_imf(
649 &t,
650 b"From: Bob Authname <bob@example.org>\n\
651 To: alice@example.org\n\
652 Subject: foo\n\
653 Message-ID: <msg1234@example.org>\n\
654 Chat-Version: 1.0\n\
655 Date: Sun, 22 Mar 2021 22:37:57 +0000\n\
656 \n\
657 hello foo\n",
658 false,
659 )
660 .await?;
661
662 let chats = Chatlist::try_load(&t, 0, Some("Bob Authname"), None).await?;
663 assert_eq!(chats.len(), 1);
665
666 let msg = t.get_last_msg().await;
667 let chat_id = msg.get_chat_id();
668 chat_id.accept(&t).await.unwrap();
669
670 let contacts = get_chat_contacts(&t, chat_id).await?;
671 let contact_id = *contacts.first().unwrap();
672 let chat = Chat::load_from_db(&t, chat_id).await?;
673 assert_eq!(chat.get_name(), "Bob Authname");
674
675 let chats = Chatlist::try_load(&t, 0, Some("bob authname"), None).await?;
677 assert_eq!(chats.len(), 1);
678 assert_eq!(chats.get_chat_id(0).unwrap(), chat_id);
679
680 let test_id = Contact::create(&t, "Bob Nickname", "bob@example.org").await?;
682 assert_eq!(contact_id, test_id);
683 let chat = Chat::load_from_db(&t, chat_id).await?;
684 assert_eq!(chat.get_name(), "Bob Nickname");
685 let chats = Chatlist::try_load(&t, 0, Some("bob authname"), None).await?;
686 assert_eq!(chats.len(), 0);
687 let chats = Chatlist::try_load(&t, 0, Some("bob nickname"), None).await?;
688 assert_eq!(chats.len(), 1);
689
690 let test_id = Contact::create(&t, "", "bob@example.org").await?;
692 assert_eq!(contact_id, test_id);
693 let chat = Chat::load_from_db(&t, chat_id).await?;
694 assert_eq!(chat.get_name(), "Bob Authname");
695 let chats = Chatlist::try_load(&t, 0, Some("bob authname"), None).await?;
696 assert_eq!(chats.len(), 1);
697 let chats = Chatlist::try_load(&t, 0, Some("bob nickname"), None).await?;
698 assert_eq!(chats.len(), 0);
699
700 Ok(())
701 }
702
703 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
704 async fn test_search_single_chat_without_authname() -> anyhow::Result<()> {
705 let t = TestContext::new_alice().await;
706
707 receive_imf(
709 &t,
710 b"From: bob@example.org\n\
711 To: alice@example.org\n\
712 Subject: foo\n\
713 Message-ID: <msg5678@example.org>\n\
714 Chat-Version: 1.0\n\
715 Date: Sun, 22 Mar 2021 22:38:57 +0000\n\
716 \n\
717 hello foo\n",
718 false,
719 )
720 .await?;
721
722 let msg = t.get_last_msg().await;
723 let chat_id = msg.get_chat_id();
724 chat_id.accept(&t).await.unwrap();
725 let contacts = get_chat_contacts(&t, chat_id).await?;
726 let contact_id = *contacts.first().unwrap();
727 let chat = Chat::load_from_db(&t, chat_id).await?;
728 assert_eq!(chat.get_name(), "bob@example.org");
729
730 let chats = Chatlist::try_load(&t, 0, Some("bob@example.org"), None).await?;
732 assert_eq!(chats.len(), 1);
733 assert_eq!(chats.get_chat_id(0)?, chat_id);
734
735 let test_id = Contact::create(&t, "Bob Nickname", "bob@example.org").await?;
737 assert_eq!(contact_id, test_id);
738 let chat = Chat::load_from_db(&t, chat_id).await?;
739 assert_eq!(chat.get_name(), "Bob Nickname");
740 let chats = Chatlist::try_load(&t, 0, Some("bob@example.org"), None).await?;
741 assert_eq!(chats.len(), 0); let chats = Chatlist::try_load(&t, 0, Some("Bob Nickname"), None).await?;
743 assert_eq!(chats.len(), 1);
744 assert_eq!(chats.get_chat_id(0)?, chat_id);
745
746 let test_id = Contact::create(&t, "", "bob@example.org").await?;
748 assert_eq!(contact_id, test_id);
749 let chat = Chat::load_from_db(&t, chat_id).await?;
750 assert_eq!(chat.get_name(), "bob@example.org");
751 let chats = Chatlist::try_load(&t, 0, Some("bob@example.org"), None).await?;
752 assert_eq!(chats.len(), 1);
753 let chats = Chatlist::try_load(&t, 0, Some("bob nickname"), None).await?;
754 assert_eq!(chats.len(), 0);
755
756 let chats = Chatlist::try_load(&t, 0, Some("b@exa"), None).await?;
758 assert_eq!(chats.len(), 1);
759 let chats = Chatlist::try_load(&t, 0, Some("b@exac"), None).await?;
760 assert_eq!(chats.len(), 0);
761
762 Ok(())
763 }
764
765 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
766 async fn test_get_summary_unwrap() {
767 let t = TestContext::new().await;
768 let chat_id1 = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat")
769 .await
770 .unwrap();
771
772 let mut msg = Message::new_text("foo:\nbar \r\n test".to_string());
773 chat_id1.set_draft(&t, Some(&mut msg)).await.unwrap();
774
775 let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
776 let summary = chats.get_summary(&t, 0, None).await.unwrap();
777 assert_eq!(summary.text, "foo: bar test"); }
779
780 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
783 async fn test_get_summary_deleted_draft() {
784 let t = TestContext::new().await;
785
786 let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat")
787 .await
788 .unwrap();
789 let mut msg = Message::new_text("Foobar".to_string());
790 chat_id.set_draft(&t, Some(&mut msg)).await.unwrap();
791
792 let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
793 chat_id.set_draft(&t, None).await.unwrap();
794
795 let summary_res = chats.get_summary(&t, 0, None).await;
796 assert!(summary_res.is_ok());
797 }
798
799 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
800 async fn test_get_summary_for_saved_messages() -> Result<()> {
801 let mut tcm = TestContextManager::new();
802 let alice = tcm.alice().await;
803 let bob = tcm.bob().await;
804 let chat_alice = alice.create_chat(&bob).await;
805
806 send_text_msg(&alice, chat_alice.id, "hi".into()).await?;
807 let sent1 = alice.pop_sent_msg().await;
808 save_msgs(&alice, &[sent1.sender_msg_id]).await?;
809 let chatlist = Chatlist::try_load(&alice, 0, None, None).await?;
810 let summary = chatlist.get_summary(&alice, 0, None).await?;
811 assert_eq!(summary.prefix.unwrap().to_string(), "Me");
812 assert_eq!(summary.text, "hi");
813
814 let msg = bob.recv_msg(&sent1).await;
815 save_msgs(&bob, &[msg.id]).await?;
816 let chatlist = Chatlist::try_load(&bob, 0, None, None).await?;
817 let summary = chatlist.get_summary(&bob, 0, None).await?;
818 assert_eq!(summary.prefix.unwrap().to_string(), "alice@example.org");
819 assert_eq!(summary.text, "hi");
820
821 Ok(())
822 }
823
824 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
825 async fn test_load_broken() {
826 let t = TestContext::new_bob().await;
827 let chat_id1 = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat")
828 .await
829 .unwrap();
830 create_group_chat(&t, ProtectionStatus::Unprotected, "b chat")
831 .await
832 .unwrap();
833 create_group_chat(&t, ProtectionStatus::Unprotected, "c chat")
834 .await
835 .unwrap();
836
837 let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
839 assert_eq!(chats.len(), 3);
840
841 t.sql
843 .execute("UPDATE chats SET type=10 WHERE id=?", (chat_id1,))
844 .await
845 .unwrap();
846
847 assert!(Chat::load_from_db(&t, chat_id1).await.is_err());
849
850 let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
852
853 assert!(chats.get_summary(&t, 0, None).await.is_ok());
855 assert!(chats.get_summary(&t, 1, None).await.is_ok());
856 assert!(chats.get_summary(&t, 2, None).await.is_err());
857 assert_eq!(chats.get_index_for_id(chat_id1).unwrap(), 2);
858 }
859}