| | |
| |
|
| | use anyhow::{Context as _, Result, ensure}; |
| | use std::sync::LazyLock; |
| |
|
| | use crate::chat::{Chat, ChatId, ChatVisibility, update_special_chat_names}; |
| | use crate::constants::{ |
| | Blocked, Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_GCL_ADD_ALLDONE_HINT, |
| | DC_GCL_ARCHIVED_ONLY, DC_GCL_FOR_FORWARDING, DC_GCL_NO_SPECIALS, |
| | }; |
| | use crate::contact::{Contact, ContactId}; |
| | use crate::context::Context; |
| | use crate::log::warn; |
| | use crate::message::{Message, MessageState, MsgId}; |
| | use crate::param::{Param, Params}; |
| | use crate::stock_str; |
| | use crate::summary::Summary; |
| | use crate::tools::IsNoneOrEmpty; |
| |
|
| | |
| | pub static IS_UNREAD_FILTER: LazyLock<regex::Regex> = |
| | LazyLock::new(|| regex::Regex::new(r"\bis:unread\b").unwrap()); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #[derive(Debug)] |
| | pub struct Chatlist { |
| | |
| | ids: Vec<(ChatId, Option<MsgId>)>, |
| | } |
| |
|
| | impl Chatlist { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | pub async fn try_load( |
| | context: &Context, |
| | listflags: usize, |
| | query: Option<&str>, |
| | query_contact_id: Option<ContactId>, |
| | ) -> Result<Self> { |
| | let flag_archived_only = 0 != listflags & DC_GCL_ARCHIVED_ONLY; |
| | let flag_for_forwarding = 0 != listflags & DC_GCL_FOR_FORWARDING; |
| | let flag_no_specials = 0 != listflags & DC_GCL_NO_SPECIALS; |
| | let flag_add_alldone_hint = 0 != listflags & DC_GCL_ADD_ALLDONE_HINT; |
| |
|
| | let process_row = |row: &rusqlite::Row| { |
| | let chat_id: ChatId = row.get(0)?; |
| | let msg_id: Option<MsgId> = row.get(1)?; |
| | Ok((chat_id, msg_id)) |
| | }; |
| |
|
| | let skip_id = if flag_for_forwarding { |
| | ChatId::lookup_by_contact(context, ContactId::DEVICE) |
| | .await? |
| | .unwrap_or_default() |
| | } else { |
| | ChatId::new(0) |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | let ids = if let Some(query_contact_id) = query_contact_id { |
| | |
| | context.sql.query_map_vec( |
| | "SELECT c.id, m.id |
| | FROM chats c |
| | LEFT JOIN msgs m |
| | ON c.id=m.chat_id |
| | AND m.id=( |
| | SELECT id |
| | FROM msgs |
| | WHERE chat_id=c.id |
| | AND (hidden=0 OR state=?1) |
| | ORDER BY timestamp DESC, id DESC LIMIT 1) |
| | WHERE c.id>9 |
| | AND c.blocked!=1 |
| | AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?2 AND add_timestamp >= remove_timestamp) |
| | GROUP BY c.id |
| | ORDER BY c.archived=?3 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", |
| | (MessageState::OutDraft, query_contact_id, ChatVisibility::Pinned), |
| | process_row, |
| | ).await? |
| | } else if flag_archived_only { |
| | |
| | |
| | |
| | |
| | context |
| | .sql |
| | .query_map_vec( |
| | "SELECT c.id, m.id |
| | FROM chats c |
| | LEFT JOIN msgs m |
| | ON c.id=m.chat_id |
| | AND m.id=( |
| | SELECT id |
| | FROM msgs |
| | WHERE chat_id=c.id |
| | AND (hidden=0 OR state=?) |
| | ORDER BY timestamp DESC, id DESC LIMIT 1) |
| | WHERE c.id>9 |
| | AND c.blocked!=1 |
| | AND c.archived=1 |
| | GROUP BY c.id |
| | ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", |
| | (MessageState::OutDraft,), |
| | process_row, |
| | ) |
| | .await? |
| | } else if let Some(query) = query { |
| | let mut query = query.trim().to_string(); |
| | ensure!(!query.is_empty(), "query mustn't be empty"); |
| | let only_unread = IS_UNREAD_FILTER.find(&query).is_some(); |
| | query = IS_UNREAD_FILTER.replace(&query, "").trim().to_string(); |
| |
|
| | |
| | |
| | if let Err(err) = update_special_chat_names(context).await { |
| | warn!(context, "Cannot update special chat names: {err:#}.") |
| | } |
| |
|
| | let str_like_cmd = format!("%{}%", query.to_lowercase()); |
| | context |
| | .sql |
| | .query_map_vec( |
| | "SELECT c.id, m.id |
| | FROM chats c |
| | LEFT JOIN msgs m |
| | ON c.id=m.chat_id |
| | AND m.id=( |
| | SELECT id |
| | FROM msgs |
| | WHERE chat_id=c.id |
| | AND (hidden=0 OR state=?1) |
| | ORDER BY timestamp DESC, id DESC LIMIT 1) |
| | WHERE c.id>9 AND c.id!=?2 |
| | AND c.blocked!=1 |
| | AND IFNULL(c.name_normalized,c.name) LIKE ?3 |
| | AND (NOT ?4 OR EXISTS (SELECT 1 FROM msgs m WHERE m.chat_id = c.id AND m.state == ?5 AND hidden=0)) |
| | GROUP BY c.id |
| | ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", |
| | (MessageState::OutDraft, skip_id, str_like_cmd, only_unread, MessageState::InFresh), |
| | process_row, |
| | ) |
| | .await? |
| | } else { |
| | let mut ids = if flag_for_forwarding { |
| | let sort_id_up = ChatId::lookup_by_contact(context, ContactId::SELF) |
| | .await? |
| | .unwrap_or_default(); |
| | let process_row = |row: &rusqlite::Row| { |
| | let chat_id: ChatId = row.get(0)?; |
| | let typ: Chattype = row.get(1)?; |
| | let param: Params = row.get::<_, String>(2)?.parse().unwrap_or_default(); |
| | let msg_id: Option<MsgId> = row.get(3)?; |
| | Ok((chat_id, typ, param, msg_id)) |
| | }; |
| | let process_rows = |rows: rusqlite::AndThenRows<_>| { |
| | rows.filter_map(|row: std::result::Result<(_, _, Params, _), _>| match row { |
| | Ok((chat_id, typ, param, msg_id)) => { |
| | if typ == Chattype::Mailinglist |
| | && param.get(Param::ListPost).is_none_or_empty() |
| | { |
| | None |
| | } else { |
| | Some(Ok((chat_id, msg_id))) |
| | } |
| | } |
| | Err(e) => Some(Err(e)), |
| | }) |
| | .collect::<std::result::Result<Vec<_>, _>>() |
| | }; |
| | context.sql.query_map( |
| | "SELECT c.id, c.type, c.param, m.id |
| | FROM chats c |
| | LEFT JOIN msgs m |
| | ON c.id=m.chat_id |
| | AND m.id=( |
| | SELECT id |
| | FROM msgs |
| | WHERE chat_id=c.id |
| | AND (hidden=0 OR state=?) |
| | ORDER BY timestamp DESC, id DESC LIMIT 1) |
| | WHERE c.id>9 AND c.id!=? |
| | AND c.blocked=0 |
| | AND NOT c.archived=? |
| | AND (c.type!=? OR c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=? AND add_timestamp >= remove_timestamp)) |
| | GROUP BY c.id |
| | ORDER BY c.id=? DESC, c.archived=? DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", |
| | ( |
| | MessageState::OutDraft, skip_id, ChatVisibility::Archived, |
| | Chattype::Group, ContactId::SELF, |
| | sort_id_up, ChatVisibility::Pinned, |
| | ), |
| | process_row, |
| | process_rows, |
| | ).await? |
| | } else { |
| | |
| | context.sql.query_map_vec( |
| | "SELECT c.id, m.id |
| | FROM chats c |
| | LEFT JOIN msgs m |
| | ON c.id=m.chat_id |
| | AND m.id=( |
| | SELECT id |
| | FROM msgs |
| | WHERE chat_id=c.id |
| | AND (hidden=0 OR state=?) |
| | ORDER BY timestamp DESC, id DESC LIMIT 1) |
| | WHERE c.id>9 AND c.id!=? |
| | AND (c.blocked=0 OR c.blocked=2) |
| | AND NOT c.archived=? |
| | GROUP BY c.id |
| | ORDER BY c.id=0 DESC, c.archived=? DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", |
| | (MessageState::OutDraft, skip_id, ChatVisibility::Archived, ChatVisibility::Pinned), |
| | process_row, |
| | ).await? |
| | }; |
| | if !flag_no_specials && get_archived_cnt(context).await? > 0 { |
| | if ids.is_empty() && flag_add_alldone_hint { |
| | ids.push((DC_CHAT_ID_ALLDONE_HINT, None)); |
| | } |
| | ids.insert(0, (DC_CHAT_ID_ARCHIVED_LINK, None)); |
| | } |
| | ids |
| | }; |
| |
|
| | Ok(Chatlist { ids }) |
| | } |
| |
|
| | |
| | pub(crate) async fn from_chat_ids(context: &Context, chat_ids: &[ChatId]) -> Result<Self> { |
| | let mut ids = Vec::new(); |
| | for &chat_id in chat_ids { |
| | let msg_id: Option<MsgId> = context |
| | .sql |
| | .query_get_value( |
| | "SELECT id |
| | FROM msgs |
| | WHERE chat_id=?1 |
| | AND (hidden=0 OR state=?2) |
| | ORDER BY timestamp DESC, id DESC LIMIT 1", |
| | (chat_id, MessageState::OutDraft), |
| | ) |
| | .await |
| | .with_context(|| format!("failed to get msg ID for chat {chat_id}"))?; |
| | ids.push((chat_id, msg_id)); |
| | } |
| | Ok(Chatlist { ids }) |
| | } |
| |
|
| | |
| | pub fn len(&self) -> usize { |
| | self.ids.len() |
| | } |
| |
|
| | |
| | pub fn is_empty(&self) -> bool { |
| | self.ids.is_empty() |
| | } |
| |
|
| | |
| | |
| | |
| | pub fn get_chat_id(&self, index: usize) -> Result<ChatId> { |
| | let (chat_id, _msg_id) = self |
| | .ids |
| | .get(index) |
| | .context("chatlist index is out of range")?; |
| | Ok(*chat_id) |
| | } |
| |
|
| | |
| | |
| | |
| | pub fn get_msg_id(&self, index: usize) -> Result<Option<MsgId>> { |
| | let (_chat_id, msg_id) = self |
| | .ids |
| | .get(index) |
| | .context("chatlist index is out of range")?; |
| | Ok(*msg_id) |
| | } |
| |
|
| | |
| | pub async fn get_summary( |
| | &self, |
| | context: &Context, |
| | index: usize, |
| | chat: Option<&Chat>, |
| | ) -> Result<Summary> { |
| | |
| | |
| | |
| | |
| | let (chat_id, lastmsg_id) = self |
| | .ids |
| | .get(index) |
| | .context("chatlist index is out of range")?; |
| | Chatlist::get_summary2(context, *chat_id, *lastmsg_id, chat).await |
| | } |
| |
|
| | |
| | pub async fn get_summary2( |
| | context: &Context, |
| | chat_id: ChatId, |
| | lastmsg_id: Option<MsgId>, |
| | chat: Option<&Chat>, |
| | ) -> Result<Summary> { |
| | let chat_loaded: Chat; |
| | let chat = if let Some(chat) = chat { |
| | chat |
| | } else { |
| | let chat = Chat::load_from_db(context, chat_id).await?; |
| | chat_loaded = chat; |
| | &chat_loaded |
| | }; |
| |
|
| | let lastmsg = if let Some(lastmsg_id) = lastmsg_id { |
| | |
| | |
| | Message::load_from_db_optional(context, lastmsg_id) |
| | .await |
| | .context("Loading message failed")? |
| | } else { |
| | None |
| | }; |
| |
|
| | let lastcontact = if let Some(lastmsg) = &lastmsg { |
| | if lastmsg.from_id == ContactId::SELF { |
| | None |
| | } else if chat.typ == Chattype::Group |
| | || chat.typ == Chattype::Mailinglist |
| | || chat.is_self_talk() |
| | { |
| | let lastcontact = Contact::get_by_id(context, lastmsg.from_id) |
| | .await |
| | .context("loading contact failed")?; |
| | Some(lastcontact) |
| | } else { |
| | None |
| | } |
| | } else { |
| | None |
| | }; |
| |
|
| | if chat.id.is_archived_link() { |
| | Ok(Default::default()) |
| | } else if let Some(lastmsg) = lastmsg.filter(|msg| msg.from_id != ContactId::UNDEFINED) { |
| | Summary::new_with_reaction_details(context, &lastmsg, chat, lastcontact.as_ref()).await |
| | } else { |
| | Ok(Summary { |
| | text: stock_str::no_messages(context).await, |
| | ..Default::default() |
| | }) |
| | } |
| | } |
| |
|
| | |
| | pub fn get_index_for_id(&self, id: ChatId) -> Option<usize> { |
| | self.ids.iter().position(|(chat_id, _)| chat_id == &id) |
| | } |
| |
|
| | |
| | pub fn iter(&self) -> impl Iterator<Item = &(ChatId, Option<MsgId>)> { |
| | self.ids.iter() |
| | } |
| | } |
| |
|
| | |
| | pub async fn get_archived_cnt(context: &Context) -> Result<usize> { |
| | let count = context |
| | .sql |
| | .count( |
| | "SELECT COUNT(*) FROM chats WHERE blocked!=? AND archived=?;", |
| | (Blocked::Yes, ChatVisibility::Archived), |
| | ) |
| | .await?; |
| | Ok(count) |
| | } |
| |
|
| | |
| | |
| | pub async fn get_last_message_for_chat( |
| | context: &Context, |
| | chat_id: ChatId, |
| | ) -> Result<Option<MsgId>> { |
| | context |
| | .sql |
| | .query_get_value( |
| | "SELECT id |
| | FROM msgs |
| | WHERE chat_id=?2 |
| | AND (hidden=0 OR state=?1) |
| | ORDER BY timestamp DESC, id DESC LIMIT 1", |
| | (MessageState::OutDraft, chat_id), |
| | ) |
| | .await |
| | } |
| |
|
| | #[cfg(test)] |
| | mod tests { |
| | use super::*; |
| | use crate::chat::save_msgs; |
| | use crate::chat::{ |
| | add_contact_to_chat, create_broadcast, create_group, get_chat_contacts, |
| | remove_contact_from_chat, send_text_msg, set_chat_name, |
| | }; |
| | use crate::receive_imf::receive_imf; |
| | use crate::securejoin::get_securejoin_qr; |
| | use crate::stock_str::StockMessage; |
| | use crate::test_utils::TestContext; |
| | use crate::test_utils::TestContextManager; |
| | use crate::tools::SystemTime; |
| | use std::time::Duration; |
| |
|
| | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| | async fn test_try_load() -> Result<()> { |
| | let mut tcm = TestContextManager::new(); |
| | let bob = &tcm.bob().await; |
| | let chat_id1 = create_group(bob, "a chat").await.unwrap(); |
| | let chat_id2 = create_group(bob, "b chat").await.unwrap(); |
| | let chat_id3 = create_group(bob, "c chat").await.unwrap(); |
| |
|
| | |
| | let chats = Chatlist::try_load(bob, 0, None, None).await.unwrap(); |
| | assert_eq!(chats.len(), 3); |
| | assert_eq!(chats.get_chat_id(0).unwrap(), chat_id3); |
| | assert_eq!(chats.get_chat_id(1).unwrap(), chat_id2); |
| | assert_eq!(chats.get_chat_id(2).unwrap(), chat_id1); |
| |
|
| | SystemTime::shift(Duration::from_secs(5)); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | for chat_id in &[chat_id1, chat_id3, chat_id2] { |
| | let mut msg = Message::new_text("hello".to_string()); |
| | chat_id.set_draft(bob, Some(&mut msg)).await.unwrap(); |
| | } |
| |
|
| | let chats = Chatlist::try_load(bob, 0, None, None).await.unwrap(); |
| | assert_eq!(chats.get_chat_id(0).unwrap(), chat_id2); |
| |
|
| | |
| | let chats = Chatlist::try_load(bob, 0, Some("b"), None).await.unwrap(); |
| | assert_eq!(chats.len(), 1); |
| |
|
| | |
| | let alice = &tcm.alice().await; |
| | let alice_chat_id = create_group(alice, "alice chat").await.unwrap(); |
| | add_contact_to_chat( |
| | alice, |
| | alice_chat_id, |
| | alice.add_or_lookup_contact_id(bob).await, |
| | ) |
| | .await |
| | .unwrap(); |
| | send_text_msg(alice, alice_chat_id, "hi".into()) |
| | .await |
| | .unwrap(); |
| | let sent_msg = alice.pop_sent_msg().await; |
| |
|
| | bob.recv_msg(&sent_msg).await; |
| | let chats = Chatlist::try_load(bob, 0, Some("is:unread"), None) |
| | .await |
| | .unwrap(); |
| | assert_eq!(chats.len(), 1); |
| |
|
| | let chats = Chatlist::try_load(bob, DC_GCL_ARCHIVED_ONLY, None, None) |
| | .await |
| | .unwrap(); |
| | assert_eq!(chats.len(), 0); |
| |
|
| | chat_id1 |
| | .set_visibility(bob, ChatVisibility::Archived) |
| | .await |
| | .ok(); |
| | let chats = Chatlist::try_load(bob, DC_GCL_ARCHIVED_ONLY, None, None) |
| | .await |
| | .unwrap(); |
| | assert_eq!(chats.len(), 1); |
| |
|
| | let chat_id = create_group(bob, "Δ-chat").await.unwrap(); |
| | let chats = Chatlist::try_load(bob, 0, Some("δ"), None).await?; |
| | assert_eq!(chats.len(), 1); |
| | assert_eq!(chats.ids[0].0, chat_id); |
| | set_chat_name(bob, chat_id, "abcδe").await?; |
| | let chats = Chatlist::try_load(bob, 0, Some("Δ"), None).await?; |
| | assert_eq!(chats.len(), 1); |
| | Ok(()) |
| | } |
| |
|
| | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| | async fn test_sort_self_talk_up_on_forward() { |
| | let t = TestContext::new_alice().await; |
| | t.update_device_chats().await.unwrap(); |
| | create_group(&t, "a chat").await.unwrap(); |
| |
|
| | let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap(); |
| | assert_eq!(chats.len(), 3); |
| | assert!( |
| | !Chat::load_from_db(&t, chats.get_chat_id(0).unwrap()) |
| | .await |
| | .unwrap() |
| | .is_self_talk() |
| | ); |
| |
|
| | let chats = Chatlist::try_load(&t, DC_GCL_FOR_FORWARDING, None, None) |
| | .await |
| | .unwrap(); |
| | assert_eq!(chats.len(), 2); |
| | assert!( |
| | Chat::load_from_db(&t, chats.get_chat_id(0).unwrap()) |
| | .await |
| | .unwrap() |
| | .is_self_talk() |
| | ); |
| |
|
| | remove_contact_from_chat(&t, chats.get_chat_id(1).unwrap(), ContactId::SELF) |
| | .await |
| | .unwrap(); |
| | let chats = Chatlist::try_load(&t, DC_GCL_FOR_FORWARDING, None, None) |
| | .await |
| | .unwrap(); |
| | assert_eq!(chats.len(), 1); |
| | } |
| |
|
| | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| | async fn test_search_special_chat_names() { |
| | let t = TestContext::new_alice().await; |
| | t.update_device_chats().await.unwrap(); |
| |
|
| | let chats = Chatlist::try_load(&t, 0, Some("t-1234-s"), None) |
| | .await |
| | .unwrap(); |
| | assert_eq!(chats.len(), 0); |
| | let chats = Chatlist::try_load(&t, 0, Some("t-5678-b"), None) |
| | .await |
| | .unwrap(); |
| | assert_eq!(chats.len(), 0); |
| |
|
| | t.set_stock_translation(StockMessage::SavedMessages, "test-1234-save".to_string()) |
| | .await |
| | .unwrap(); |
| | let chats = Chatlist::try_load(&t, 0, Some("t-1234-s"), None) |
| | .await |
| | .unwrap(); |
| | assert_eq!(chats.len(), 1); |
| |
|
| | t.set_stock_translation(StockMessage::DeviceMessages, "test-5678-babbel".to_string()) |
| | .await |
| | .unwrap(); |
| | let chats = Chatlist::try_load(&t, 0, Some("t-5678-b"), None) |
| | .await |
| | .unwrap(); |
| | assert_eq!(chats.len(), 1); |
| | } |
| |
|
| | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| | async fn test_search_single_chat() -> anyhow::Result<()> { |
| | let t = TestContext::new_alice().await; |
| |
|
| | |
| | receive_imf( |
| | &t, |
| | b"From: Bob Authname <bob@example.org>\n\ |
| | To: alice@example.org\n\ |
| | Subject: foo\n\ |
| | Message-ID: <msg1234@example.org>\n\ |
| | Chat-Version: 1.0\n\ |
| | Date: Sun, 22 Mar 2021 22:37:57 +0000\n\ |
| | \n\ |
| | hello foo\n", |
| | false, |
| | ) |
| | .await?; |
| |
|
| | let chats = Chatlist::try_load(&t, 0, Some("Bob Authname"), None).await?; |
| | |
| | assert_eq!(chats.len(), 1); |
| |
|
| | let msg = t.get_last_msg().await; |
| | let chat_id = msg.get_chat_id(); |
| | chat_id.accept(&t).await.unwrap(); |
| |
|
| | let contacts = get_chat_contacts(&t, chat_id).await?; |
| | let contact_id = *contacts.first().unwrap(); |
| | let chat = Chat::load_from_db(&t, chat_id).await?; |
| | assert_eq!(chat.get_name(), "Bob Authname"); |
| |
|
| | |
| | let chats = Chatlist::try_load(&t, 0, Some("bob authname"), None).await?; |
| | assert_eq!(chats.len(), 1); |
| | assert_eq!(chats.get_chat_id(0).unwrap(), chat_id); |
| |
|
| | |
| | let test_id = Contact::create(&t, "Bob Nickname", "bob@example.org").await?; |
| | assert_eq!(contact_id, test_id); |
| | let chat = Chat::load_from_db(&t, chat_id).await?; |
| | assert_eq!(chat.get_name(), "Bob Nickname"); |
| | let chats = Chatlist::try_load(&t, 0, Some("bob authname"), None).await?; |
| | assert_eq!(chats.len(), 0); |
| | let chats = Chatlist::try_load(&t, 0, Some("bob nickname"), None).await?; |
| | assert_eq!(chats.len(), 1); |
| |
|
| | |
| | let test_id = Contact::create(&t, "", "bob@example.org").await?; |
| | assert_eq!(contact_id, test_id); |
| | let chat = Chat::load_from_db(&t, chat_id).await?; |
| | assert_eq!(chat.get_name(), "Bob Authname"); |
| | let chats = Chatlist::try_load(&t, 0, Some("bob authname"), None).await?; |
| | assert_eq!(chats.len(), 1); |
| | let chats = Chatlist::try_load(&t, 0, Some("bob nickname"), None).await?; |
| | assert_eq!(chats.len(), 0); |
| |
|
| | Ok(()) |
| | } |
| |
|
| | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| | async fn test_search_single_chat_without_authname() -> anyhow::Result<()> { |
| | let t = TestContext::new_alice().await; |
| |
|
| | |
| | receive_imf( |
| | &t, |
| | b"From: bob@example.org\n\ |
| | To: alice@example.org\n\ |
| | Subject: foo\n\ |
| | Message-ID: <msg5678@example.org>\n\ |
| | Chat-Version: 1.0\n\ |
| | Date: Sun, 22 Mar 2021 22:38:57 +0000\n\ |
| | \n\ |
| | hello foo\n", |
| | false, |
| | ) |
| | .await?; |
| |
|
| | let msg = t.get_last_msg().await; |
| | let chat_id = msg.get_chat_id(); |
| | chat_id.accept(&t).await.unwrap(); |
| | let contacts = get_chat_contacts(&t, chat_id).await?; |
| | let contact_id = *contacts.first().unwrap(); |
| | let chat = Chat::load_from_db(&t, chat_id).await?; |
| | assert_eq!(chat.get_name(), "bob@example.org"); |
| |
|
| | |
| | let chats = Chatlist::try_load(&t, 0, Some("bob@example.org"), None).await?; |
| | assert_eq!(chats.len(), 1); |
| | assert_eq!(chats.get_chat_id(0)?, chat_id); |
| |
|
| | |
| | let test_id = Contact::create(&t, "Bob Nickname", "bob@example.org").await?; |
| | assert_eq!(contact_id, test_id); |
| | let chat = Chat::load_from_db(&t, chat_id).await?; |
| | assert_eq!(chat.get_name(), "Bob Nickname"); |
| | let chats = Chatlist::try_load(&t, 0, Some("bob@example.org"), None).await?; |
| | assert_eq!(chats.len(), 0); |
| | let chats = Chatlist::try_load(&t, 0, Some("Bob Nickname"), None).await?; |
| | assert_eq!(chats.len(), 1); |
| | assert_eq!(chats.get_chat_id(0)?, chat_id); |
| |
|
| | |
| | let test_id = Contact::create(&t, "", "bob@example.org").await?; |
| | assert_eq!(contact_id, test_id); |
| | let chat = Chat::load_from_db(&t, chat_id).await?; |
| | assert_eq!(chat.get_name(), "bob@example.org"); |
| | let chats = Chatlist::try_load(&t, 0, Some("bob@example.org"), None).await?; |
| | assert_eq!(chats.len(), 1); |
| | let chats = Chatlist::try_load(&t, 0, Some("bob nickname"), None).await?; |
| | assert_eq!(chats.len(), 0); |
| |
|
| | |
| | let chats = Chatlist::try_load(&t, 0, Some("b@exa"), None).await?; |
| | assert_eq!(chats.len(), 1); |
| | let chats = Chatlist::try_load(&t, 0, Some("b@exac"), None).await?; |
| | assert_eq!(chats.len(), 0); |
| |
|
| | Ok(()) |
| | } |
| |
|
| | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| | async fn test_get_summary_unwrap() { |
| | let t = TestContext::new().await; |
| | let chat_id1 = create_group(&t, "a chat").await.unwrap(); |
| |
|
| | let mut msg = Message::new_text("foo:\nbar \r\n test".to_string()); |
| | chat_id1.set_draft(&t, Some(&mut msg)).await.unwrap(); |
| |
|
| | let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap(); |
| | let summary = chats.get_summary(&t, 0, None).await.unwrap(); |
| | assert_eq!(summary.text, "foo: bar test"); |
| | } |
| |
|
| | |
| | |
| | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| | async fn test_get_summary_deleted_draft() { |
| | let t = TestContext::new().await; |
| |
|
| | let chat_id = create_group(&t, "a chat").await.unwrap(); |
| | let mut msg = Message::new_text("Foobar".to_string()); |
| | chat_id.set_draft(&t, Some(&mut msg)).await.unwrap(); |
| |
|
| | let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap(); |
| | chat_id.set_draft(&t, None).await.unwrap(); |
| |
|
| | let summary_res = chats.get_summary(&t, 0, None).await; |
| | assert!(summary_res.is_ok()); |
| | } |
| |
|
| | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| | async fn test_get_summary_for_saved_messages() -> Result<()> { |
| | let mut tcm = TestContextManager::new(); |
| | let alice = tcm.alice().await; |
| | let bob = tcm.bob().await; |
| | let chat_alice = alice.create_chat(&bob).await; |
| |
|
| | send_text_msg(&alice, chat_alice.id, "hi".into()).await?; |
| | let sent1 = alice.pop_sent_msg().await; |
| | save_msgs(&alice, &[sent1.sender_msg_id]).await?; |
| | let chatlist = Chatlist::try_load(&alice, 0, None, None).await?; |
| | let summary = chatlist.get_summary(&alice, 0, None).await?; |
| | assert_eq!(summary.prefix.unwrap().to_string(), "Me"); |
| | assert_eq!(summary.text, "hi"); |
| |
|
| | let msg = bob.recv_msg(&sent1).await; |
| | save_msgs(&bob, &[msg.id]).await?; |
| | let chatlist = Chatlist::try_load(&bob, 0, None, None).await?; |
| | let summary = chatlist.get_summary(&bob, 0, None).await?; |
| | assert_eq!(summary.prefix.unwrap().to_string(), "alice@example.org"); |
| | assert_eq!(summary.text, "hi"); |
| |
|
| | Ok(()) |
| | } |
| |
|
| | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| | async fn test_no_summary_prefix_for_channel() -> Result<()> { |
| | let mut tcm = TestContextManager::new(); |
| | let alice = tcm.alice().await; |
| | let bob = tcm.bob().await; |
| |
|
| | let alice_chat_id = create_broadcast(&alice, "alice's channel".to_string()).await?; |
| | let qr = get_securejoin_qr(&alice, Some(alice_chat_id)).await?; |
| | tcm.exec_securejoin_qr(&bob, &alice, &qr).await; |
| |
|
| | send_text_msg(&alice, alice_chat_id, "hi".into()).await?; |
| | let sent1 = alice.pop_sent_msg().await; |
| | let chatlist = Chatlist::try_load(&alice, 0, None, None).await?; |
| | let summary = chatlist.get_summary(&alice, 0, None).await?; |
| | assert!(summary.prefix.is_none()); |
| | assert_eq!(summary.text, "hi"); |
| |
|
| | bob.recv_msg(&sent1).await; |
| | let chatlist = Chatlist::try_load(&bob, 0, None, None).await?; |
| | let summary = chatlist.get_summary(&bob, 0, None).await?; |
| | assert!(summary.prefix.is_none()); |
| | assert_eq!(summary.text, "hi"); |
| |
|
| | Ok(()) |
| | } |
| |
|
| | #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| | async fn test_load_broken() { |
| | let t = TestContext::new_bob().await; |
| | let chat_id1 = create_group(&t, "a chat").await.unwrap(); |
| | create_group(&t, "b chat").await.unwrap(); |
| | create_group(&t, "c chat").await.unwrap(); |
| |
|
| | |
| | let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap(); |
| | assert_eq!(chats.len(), 3); |
| |
|
| | |
| | t.sql |
| | .execute("UPDATE chats SET type=10 WHERE id=?", (chat_id1,)) |
| | .await |
| | .unwrap(); |
| |
|
| | |
| | assert!(Chat::load_from_db(&t, chat_id1).await.is_err()); |
| |
|
| | |
| | let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap(); |
| |
|
| | |
| | assert!(chats.get_summary(&t, 0, None).await.is_ok()); |
| | assert!(chats.get_summary(&t, 1, None).await.is_ok()); |
| | assert!(chats.get_summary(&t, 2, None).await.is_err()); |
| | assert_eq!(chats.get_index_for_id(chat_id1).unwrap(), 2); |
| | } |
| | } |
| |
|