Move module functions to type methods

This moves the module-level lookup and creation functions to the
types, which make the naming more consistent.  Now the lookup_* get_*
and create_* functions all behave similarly.

Peraps even more important the API of the lookup now allows
distinguishing failure from not found.  This in turn is important to
be able to remove reliance on a ChatId with a 0 or "unset" value.  The
locations where this ChatId(0) is still used is in database queries
which should be solved in an independed commit.
This commit is contained in:
Floris Bruynooghe
2021-04-26 22:41:13 +02:00
parent be413b20f1
commit d7b4a5fc9e
17 changed files with 175 additions and 194 deletions

View File

@@ -639,7 +639,7 @@ pub unsafe extern "C" fn dc_create_chat_by_contact_id(
let ctx = &*context; let ctx = &*context;
block_on(async move { block_on(async move {
chat::create_by_contact_id(&ctx, contact_id) ChatId::create_for_contact(&ctx, contact_id)
.await .await
.log_err(ctx, "Failed to create chat from contact_id") .log_err(ctx, "Failed to create chat from contact_id")
.map(|id| id.to_u32()) .map(|id| id.to_u32())
@@ -659,11 +659,12 @@ pub unsafe extern "C" fn dc_get_chat_id_by_contact_id(
let ctx = &*context; let ctx = &*context;
block_on(async move { block_on(async move {
chat::get_by_contact_id(&ctx, contact_id) ChatId::lookup_by_contact(&ctx, contact_id)
.await .await
.log_err(ctx, "Failed to get chat for contact_id") .log_err(ctx, "Failed to get chat for contact_id")
.unwrap_or_default() // unwraps the Result
.map(|id| id.to_u32()) .map(|id| id.to_u32())
.unwrap_or(0) .unwrap_or(0) // unwraps the Option
}) })
} }

View File

@@ -675,7 +675,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
"createchat" => { "createchat" => {
ensure!(!arg1.is_empty(), "Argument <contact-id> missing."); ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id: u32 = arg1.parse()?; let contact_id: u32 = arg1.parse()?;
let chat_id = chat::create_by_contact_id(&context, contact_id).await?; let chat_id = ChatId::create_for_contact(&context, contact_id).await?;
println!("Single#{} created successfully.", chat_id,); println!("Single#{} created successfully.", chat_id,);
} }

View File

@@ -1,6 +1,6 @@
use tempfile::tempdir; use tempfile::tempdir;
use deltachat::chat; use deltachat::chat::{self, ChatId};
use deltachat::chatlist::*; use deltachat::chatlist::*;
use deltachat::config; use deltachat::config;
use deltachat::contact::*; use deltachat::contact::*;
@@ -70,7 +70,7 @@ async fn main() {
let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com") let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com")
.await .await
.unwrap(); .unwrap();
let chat_id = chat::create_by_contact_id(&ctx, contact_id).await.unwrap(); let chat_id = ChatId::create_for_contact(&ctx, contact_id).await.unwrap();
for i in 0..1 { for i in 0..1 {
log::info!("sending message {}", i); log::info!("sending message {}", i);

View File

@@ -155,15 +155,66 @@ impl ChatId {
self == DC_CHAT_ID_ALLDONE_HINT self == DC_CHAT_ID_ALLDONE_HINT
} }
/// Returns the [`ChatId`] for the 1:1 chat with `contact_id` if it exists.
///
/// If it does not exist, `None` is returned.
pub async fn lookup_by_contact(context: &Context, contact_id: u32) -> Result<Option<Self>> {
ChatIdBlocked::lookup_by_contact(context, contact_id)
.await
.map(|lookup| lookup.map(|chat| chat.id))
}
/// Returns the [`ChatId`] for the 1:1 chat with `contact_id`. /// Returns the [`ChatId`] for the 1:1 chat with `contact_id`.
/// ///
/// If the chat does not yet exist an unblocked chat ([`Blocked::Not`]) is created. /// If the chat does not yet exist an unblocked chat ([`Blocked::Not`]) is created.
///
/// This is an internal API, if **a user action** needs to get a chat
/// [`ChatId::create_for_contact`] should be used as this also scales up the
/// [`Contact`]'s origin.
pub async fn get_for_contact(context: &Context, contact_id: u32) -> Result<Self> { pub async fn get_for_contact(context: &Context, contact_id: u32) -> Result<Self> {
ChatIdBlocked::get_for_contact(context, contact_id, Blocked::Not) ChatIdBlocked::get_for_contact(context, contact_id, Blocked::Not)
.await .await
.map(|chat| chat.id) .map(|chat| chat.id)
} }
/// Returns the unblocked 1:1 chat with `contact_id`.
///
/// This should be used when **a user action** creates a chat 1:1, it ensures the chat
/// exists and is unblocked and scales the [`Contact`]'s origin.
///
/// If a chat was in the deaddrop unblocking is how it becomes a normal chat and it will
/// look to the user like the chat was newly created.
pub async fn create_for_contact(context: &Context, contact_id: u32) -> Result<Self> {
let chat_id = match ChatIdBlocked::lookup_by_contact(context, contact_id).await? {
Some(chat) => {
if chat.blocked != Blocked::Not {
chat.id.unblock(context).await;
}
chat.id
}
None => {
if Contact::real_exists_by_id(context, contact_id).await
|| contact_id == DC_CONTACT_ID_SELF
{
let chat_id = ChatId::get_for_contact(context, contact_id).await?;
Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat).await;
chat_id
} else {
warn!(
context,
"Cannot create chat, contact {} does not exist.", contact_id,
);
bail!("Can not create chat for non-existing contact");
}
}
};
context.emit_event(EventType::MsgsChanged {
chat_id: ChatId::new(0),
msg_id: MsgId::new(0),
});
Ok(chat_id)
}
pub async fn set_selfavatar_timestamp(self, context: &Context, timestamp: i64) -> Result<()> { pub async fn set_selfavatar_timestamp(self, context: &Context, timestamp: i64) -> Result<()> {
context context
.sql .sql
@@ -1312,50 +1363,9 @@ pub async fn create_by_msg_id(context: &Context, msg_id: MsgId) -> Result<ChatId
Ok(chat.id) Ok(chat.id)
} }
/// Create a normal chat with a single user.
///
/// To create group chats, see [`create_group_chat`].
///
/// If a chat already exists, this ID is returned, otherwise a new chat is created;
/// this new chat may already contain messages, eg. from the deaddrop, to get the
/// chat messages, use dc_get_chat_msgs().
pub async fn create_by_contact_id(context: &Context, contact_id: u32) -> Result<ChatId> {
let chat_id = match lookup_by_contact_id(context, contact_id).await {
Ok((chat_id, chat_blocked)) => {
if chat_blocked != Blocked::Not {
// unblock chat (typically move it from the deaddrop to view)
chat_id.unblock(context).await;
}
chat_id
}
Err(err) => {
if !Contact::real_exists_by_id(context, contact_id).await
&& contact_id != DC_CONTACT_ID_SELF
{
warn!(
context,
"Cannot create chat, contact {} does not exist.", contact_id,
);
return Err(err);
} else {
let chat_id = ChatId::get_for_contact(context, contact_id).await?;
Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat).await;
chat_id
}
}
};
context.emit_event(EventType::MsgsChanged {
chat_id: ChatId::new(0),
msg_id: MsgId::new(0),
});
Ok(chat_id)
}
pub(crate) async fn update_saved_messages_icon(context: &Context) -> Result<()> { pub(crate) async fn update_saved_messages_icon(context: &Context) -> Result<()> {
// if there is no saved-messages chat, there is nothing to update. this is no error. // if there is no saved-messages chat, there is nothing to update. this is no error.
if let Ok((chat_id, _)) = lookup_by_contact_id(context, DC_CONTACT_ID_SELF).await { if let Some(chat_id) = ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF).await? {
let icon = include_bytes!("../assets/icon-saved-messages.png"); let icon = include_bytes!("../assets/icon-saved-messages.png");
let blob = BlobObject::create(context, "icon-saved-messages.png".to_string(), icon).await?; let blob = BlobObject::create(context, "icon-saved-messages.png".to_string(), icon).await?;
let icon = blob.as_name().to_string(); let icon = blob.as_name().to_string();
@@ -1369,7 +1379,7 @@ pub(crate) async fn update_saved_messages_icon(context: &Context) -> Result<()>
pub(crate) async fn update_device_icon(context: &Context) -> Result<()> { pub(crate) async fn update_device_icon(context: &Context) -> Result<()> {
// if there is no device-chat, there is nothing to update. this is no error. // if there is no device-chat, there is nothing to update. this is no error.
if let Ok((chat_id, _)) = lookup_by_contact_id(context, DC_CONTACT_ID_DEVICE).await { if let Some(chat_id) = ChatId::lookup_by_contact(context, DC_CONTACT_ID_DEVICE).await? {
let icon = include_bytes!("../assets/icon-device.png"); let icon = include_bytes!("../assets/icon-device.png");
let blob = BlobObject::create(context, "icon-device.png".to_string(), icon).await?; let blob = BlobObject::create(context, "icon-device.png".to_string(), icon).await?;
let icon = blob.as_name().to_string(); let icon = blob.as_name().to_string();
@@ -1386,7 +1396,7 @@ pub(crate) async fn update_device_icon(context: &Context) -> Result<()> {
} }
async fn update_special_chat_name(context: &Context, contact_id: u32, name: String) -> Result<()> { async fn update_special_chat_name(context: &Context, contact_id: u32, name: String) -> Result<()> {
if let Ok((chat_id, _)) = lookup_by_contact_id(context, contact_id).await { if let Some(chat_id) = ChatId::lookup_by_contact(context, contact_id).await? {
// the `!= name` condition avoids unneeded writes // the `!= name` condition avoids unneeded writes
context context
.sql .sql
@@ -1432,7 +1442,7 @@ impl ChatIdBlocked {
/// Searches the database for the 1:1 chat with this contact. /// Searches the database for the 1:1 chat with this contact.
/// ///
/// If no chat is found `None` is returned. /// If no chat is found `None` is returned.
async fn lookup_by_contact(context: &Context, contact_id: u32) -> Result<Option<Self>, Error> { pub async fn lookup_by_contact(context: &Context, contact_id: u32) -> Result<Option<Self>> {
ensure!(context.sql.is_open().await, "Database not available"); ensure!(context.sql.is_open().await, "Database not available");
ensure!(contact_id > 0, "Invalid contact id requested"); ensure!(contact_id > 0, "Invalid contact id requested");
@@ -1465,7 +1475,7 @@ impl ChatIdBlocked {
context: &Context, context: &Context,
contact_id: u32, contact_id: u32,
create_blocked: Blocked, create_blocked: Blocked,
) -> Result<Self, Error> { ) -> Result<Self> {
ensure!(context.sql.is_open().await, "Database not available"); ensure!(context.sql.is_open().await, "Database not available");
ensure!(contact_id > 0, "Invalid contact id requested"); ensure!(contact_id > 0, "Invalid contact id requested");
@@ -1533,41 +1543,6 @@ impl ChatIdBlocked {
} }
} }
pub(crate) async fn lookup_by_contact_id(
context: &Context,
contact_id: u32,
) -> Result<(ChatId, Blocked)> {
ensure!(context.sql.is_open().await, "Database not available");
let row = context
.sql
.query_row(
"SELECT c.id, c.blocked
FROM chats c
INNER JOIN chats_contacts j
ON c.id=j.chat_id
WHERE c.type=100
AND c.id>9
AND j.contact_id=?;",
paramsv![contact_id as i32],
|row| {
Ok((
row.get::<_, ChatId>(0)?,
row.get::<_, Option<_>>(1)?.unwrap_or_default(),
))
},
)
.await?;
Ok(row)
}
pub async fn get_by_contact_id(context: &Context, contact_id: u32) -> Result<ChatId> {
let (chat_id, blocked) = lookup_by_contact_id(context, contact_id).await?;
ensure_eq!(blocked, Blocked::Not, "Requested contact is blocked");
Ok(chat_id)
}
pub async fn prepare_msg(context: &Context, chat_id: ChatId, msg: &mut Message) -> Result<MsgId> { pub async fn prepare_msg(context: &Context, chat_id: ChatId, msg: &mut Message) -> Result<MsgId> {
ensure!( ensure!(
!chat_id.is_special(), !chat_id.is_special(),
@@ -3182,7 +3157,7 @@ mod tests {
async fn test_add_remove_contact_for_single() { async fn test_add_remove_contact_for_single() {
let ctx = TestContext::new_alice().await; let ctx = TestContext::new_alice().await;
let bob = Contact::create(&ctx, "", "bob@f.br").await.unwrap(); let bob = Contact::create(&ctx, "", "bob@f.br").await.unwrap();
let chat_id = create_by_contact_id(&ctx, bob).await.unwrap(); let chat_id = ChatId::create_for_contact(&ctx, bob).await.unwrap();
let chat = Chat::load_from_db(&ctx, chat_id).await.unwrap(); let chat = Chat::load_from_db(&ctx, chat_id).await.unwrap();
assert_eq!(chat.typ, Chattype::Single); assert_eq!(chat.typ, Chattype::Single);
assert_eq!(get_chat_contacts(&ctx, chat.id).await.unwrap().len(), 1); assert_eq!(get_chat_contacts(&ctx, chat.id).await.unwrap().len(), 1);
@@ -3623,11 +3598,15 @@ mod tests {
.unwrap(); .unwrap();
assert_ne!(contact1, 0); assert_ne!(contact1, 0);
let chat_id = create_by_contact_id(&context.ctx, contact1).await.unwrap(); let chat_id = ChatId::create_for_contact(&context.ctx, contact1)
.await
.unwrap();
assert!(!chat_id.is_special(), "chat_id too small {}", chat_id); assert!(!chat_id.is_special(), "chat_id too small {}", chat_id);
let chat = Chat::load_from_db(&context.ctx, chat_id).await.unwrap(); let chat = Chat::load_from_db(&context.ctx, chat_id).await.unwrap();
let chat2_id = create_by_contact_id(&context.ctx, contact1).await.unwrap(); let chat2_id = ChatId::create_for_contact(&context.ctx, contact1)
.await
.unwrap();
assert_eq!(chat2_id, chat_id); assert_eq!(chat2_id, chat_id);
let chat2 = Chat::load_from_db(&context.ctx, chat2_id).await.unwrap(); let chat2 = Chat::load_from_db(&context.ctx, chat2_id).await.unwrap();
@@ -3823,13 +3802,16 @@ mod tests {
// create contact, then unblocked chat // create contact, then unblocked chat
let contact_id = Contact::create(&ctx, "", "bob@foo.de").await.unwrap(); let contact_id = Contact::create(&ctx, "", "bob@foo.de").await.unwrap();
assert_ne!(contact_id, 0); assert_ne!(contact_id, 0);
let res = lookup_by_contact_id(&ctx, contact_id).await; let found = ChatId::lookup_by_contact(&ctx, contact_id).await.unwrap();
assert!(res.is_err()); assert!(found.is_none());
let chat_id = create_by_contact_id(&ctx, contact_id).await.unwrap(); let chat_id = ChatId::create_for_contact(&ctx, contact_id).await.unwrap();
let (chat_id2, blocked) = lookup_by_contact_id(&ctx, contact_id).await.unwrap(); let chat2 = ChatIdBlocked::lookup_by_contact(&ctx, contact_id)
assert_eq!(chat_id, chat_id2); .await
assert_eq!(blocked, Blocked::Not); .unwrap()
.unwrap();
assert_eq!(chat_id, chat2.id);
assert_eq!(chat2.blocked, Blocked::Not);
// create contact, then blocked chat // create contact, then blocked chat
let contact_id = Contact::create(&ctx, "", "claire@foo.de").await.unwrap(); let contact_id = Contact::create(&ctx, "", "claire@foo.de").await.unwrap();
@@ -3837,33 +3819,38 @@ mod tests {
.await .await
.unwrap() .unwrap()
.id; .id;
let (chat_id2, blocked) = lookup_by_contact_id(&ctx, contact_id).await.unwrap(); let chat2 = ChatIdBlocked::lookup_by_contact(&ctx, contact_id)
assert_eq!(chat_id, chat_id2); .await
assert_eq!(blocked, Blocked::Manually); .unwrap()
.unwrap();
assert_eq!(chat_id, chat2.id);
assert_eq!(chat2.blocked, Blocked::Manually);
// test nonexistent contact, and also check if reasonable defaults are used // test nonexistent contact
let res = lookup_by_contact_id(&ctx, 1234).await; let found = ChatId::lookup_by_contact(&ctx, 1234).await.unwrap();
assert!(res.is_err()); assert!(found.is_none());
let (chat_id, blocked) = lookup_by_contact_id(&ctx, 1234).await.unwrap_or_default(); let found = ChatIdBlocked::lookup_by_contact(&ctx, 1234).await.unwrap();
assert_eq!(chat_id, ChatId::new(0)); assert!(found.is_none());
assert_eq!(blocked, Blocked::Not);
} }
#[async_std::test] #[async_std::test]
async fn test_lookup_self_by_contact_id() { async fn test_lookup_self_by_contact_id() {
let ctx = TestContext::new_alice().await; let ctx = TestContext::new_alice().await;
let res = lookup_by_contact_id(&ctx, DC_CONTACT_ID_SELF).await; let chat = ChatId::lookup_by_contact(&ctx, DC_CONTACT_ID_SELF)
assert!(res.is_err());
ctx.update_device_chats().await.unwrap();
let (chat_id, blocked) = lookup_by_contact_id(&ctx, DC_CONTACT_ID_SELF)
.await .await
.unwrap(); .unwrap();
assert!(!chat_id.is_special()); assert!(chat.is_none());
assert!(chat_id.is_self_talk(&ctx).await.unwrap());
assert_eq!(blocked, Blocked::Not); ctx.update_device_chats().await.unwrap();
let chat = ChatIdBlocked::lookup_by_contact(&ctx, DC_CONTACT_ID_SELF)
.await
.unwrap()
.unwrap();
assert!(!chat.id.is_special());
assert!(chat.id.is_self_talk(&ctx).await.unwrap());
assert_eq!(chat.blocked, Blocked::Not);
} }
#[async_std::test] #[async_std::test]

View File

@@ -2,7 +2,6 @@
use anyhow::{bail, ensure, Result}; use anyhow::{bail, ensure, Result};
use crate::chat;
use crate::chat::{update_special_chat_names, Chat, ChatId, ChatVisibility}; use crate::chat::{update_special_chat_names, Chat, ChatId, ChatVisibility};
use crate::constants::{ use crate::constants::{
Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_CHAT_ID_DEADDROP, Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_CHAT_ID_DEADDROP,
@@ -110,15 +109,6 @@ impl Chatlist {
let mut add_archived_link_item = false; let mut add_archived_link_item = false;
let skip_id = if flag_for_forwarding {
chat::lookup_by_contact_id(context, DC_CONTACT_ID_DEVICE)
.await
.unwrap_or_default()
.0
} else {
ChatId::new(0)
};
let process_row = |row: &rusqlite::Row| { let process_row = |row: &rusqlite::Row| {
let chat_id: ChatId = row.get(0)?; let chat_id: ChatId = row.get(0)?;
let msg_id: MsgId = row.get(1).unwrap_or_default(); let msg_id: MsgId = row.get(1).unwrap_or_default();
@@ -130,6 +120,14 @@ impl Chatlist {
.map_err(Into::into) .map_err(Into::into)
}; };
let skip_id = if flag_for_forwarding {
ChatId::lookup_by_contact(context, DC_CONTACT_ID_DEVICE)
.await?
.unwrap_or_default()
} else {
ChatId::new(0)
};
// select with left join and minimum: // select with left join and minimum:
// //
// - the inner select must use `hidden` and _not_ `m.hidden` // - the inner select must use `hidden` and _not_ `m.hidden`
@@ -232,10 +230,9 @@ impl Chatlist {
} else { } else {
// show normal chatlist // show normal chatlist
let sort_id_up = if flag_for_forwarding { let sort_id_up = if flag_for_forwarding {
chat::lookup_by_contact_id(context, DC_CONTACT_ID_SELF) ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF)
.await .await?
.unwrap_or_default() .unwrap_or_default()
.0
} else { } else {
ChatId::new(0) ChatId::new(0)
}; };

View File

@@ -609,8 +609,7 @@ mod tests {
use super::*; use super::*;
use crate::chat::{ use crate::chat::{
create_by_contact_id, get_chat_contacts, get_chat_msgs, send_msg, set_muted, Chat, get_chat_contacts, get_chat_msgs, send_msg, set_muted, Chat, ChatId, MuteDuration,
MuteDuration,
}; };
use crate::constants::{Viewtype, DC_CONTACT_ID_SELF}; use crate::constants::{Viewtype, DC_CONTACT_ID_SELF};
use crate::dc_receive_imf::dc_receive_imf; use crate::dc_receive_imf::dc_receive_imf;
@@ -893,7 +892,7 @@ mod tests {
#[async_std::test] #[async_std::test]
async fn test_search_msgs() -> Result<()> { async fn test_search_msgs() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let self_talk = create_by_contact_id(&alice, DC_CONTACT_ID_SELF).await?; let self_talk = ChatId::create_for_contact(&alice, DC_CONTACT_ID_SELF).await?;
let chat = alice let chat = alice
.create_chat_with_contact("Bob", "bob@example.org") .create_chat_with_contact("Bob", "bob@example.org")
.await; .await;

View File

@@ -474,10 +474,9 @@ async fn add_parts(
} }
} }
let (test_normal_chat_id, test_normal_chat_id_blocked) = let test_normal_chat = ChatIdBlocked::lookup_by_contact(context, from_id)
chat::lookup_by_contact_id(context, from_id) .await
.await .unwrap_or_default();
.unwrap_or_default();
// get the chat_id - a chat_id here is no indicator that the chat is displayed in the normal list, // get the chat_id - a chat_id here is no indicator that the chat is displayed in the normal list,
// it might also be blocked and displayed in the deaddrop as a result // it might also be blocked and displayed in the deaddrop as a result
@@ -498,17 +497,18 @@ async fn add_parts(
if chat_id.is_unset() { if chat_id.is_unset() {
// try to create a group // try to create a group
let create_blocked = let create_blocked = match test_normal_chat {
if !test_normal_chat_id.is_unset() && test_normal_chat_id_blocked == Blocked::Not { Some(ChatIdBlocked {
Blocked::Not id: _,
} else { blocked: Blocked::Not,
Blocked::Deaddrop }) => Blocked::Not,
}; _ => Blocked::Deaddrop,
};
let (new_chat_id, new_chat_id_blocked) = create_or_lookup_group( let (new_chat_id, new_chat_id_blocked) = create_or_lookup_group(
context, context,
&mut mime_parser, &mut mime_parser,
if test_normal_chat_id.is_unset() { if test_normal_chat.is_none() {
allow_creation allow_creation
} else { } else {
true true
@@ -600,9 +600,9 @@ async fn add_parts(
Blocked::Deaddrop Blocked::Deaddrop
}; };
if !test_normal_chat_id.is_unset() { if let Some(chat) = test_normal_chat {
*chat_id = test_normal_chat_id; *chat_id = chat.id;
chat_id_blocked = test_normal_chat_id_blocked; chat_id_blocked = chat.blocked;
} else if allow_creation { } else if allow_creation {
if let Ok(chat) = ChatIdBlocked::get_for_contact(context, from_id, create_blocked) if let Ok(chat) = ChatIdBlocked::get_for_contact(context, from_id, create_blocked)
.await .await
@@ -2358,7 +2358,7 @@ mod tests {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let bob_id = Contact::create(&t, "bob", "bob@example.com").await.unwrap(); let bob_id = Contact::create(&t, "bob", "bob@example.com").await.unwrap();
let one2one_id = chat::create_by_contact_id(&t, bob_id).await.unwrap(); let one2one_id = ChatId::create_for_contact(&t, bob_id).await.unwrap();
one2one_id one2one_id
.set_visibility(&t, ChatVisibility::Archived) .set_visibility(&t, ChatVisibility::Archived)
.await .await
@@ -2527,7 +2527,7 @@ mod tests {
let contact_id = Contact::create(&t, "foobar", "foobar@example.com") let contact_id = Contact::create(&t, "foobar", "foobar@example.com")
.await .await
.unwrap(); .unwrap();
let chat_id = chat::create_by_contact_id(&t, contact_id).await.unwrap(); let chat_id = ChatId::create_for_contact(&t, contact_id).await.unwrap();
dc_receive_imf( dc_receive_imf(
&t, &t,
b"From: =?UTF-8?B?0JjQvNGPLCDQpNCw0LzQuNC70LjRjw==?= <foobar@example.com>\n\ b"From: =?UTF-8?B?0JjQvNGPLCDQpNCw0LzQuNC70LjRjw==?= <foobar@example.com>\n\

View File

@@ -65,19 +65,17 @@ use anyhow::{ensure, Context as _, Result};
use async_std::task; use async_std::task;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::chat::{send_msg, ChatId};
use crate::constants::{ use crate::constants::{
Viewtype, DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_SELF, Viewtype, DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_SELF,
}; };
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::time; use crate::dc_tools::time;
use crate::events::EventType; use crate::events::EventType;
use crate::job;
use crate::message::{Message, MessageState, MsgId}; use crate::message::{Message, MessageState, MsgId};
use crate::mimeparser::SystemMessage; use crate::mimeparser::SystemMessage;
use crate::stock_str; use crate::stock_str;
use crate::{
chat::{lookup_by_contact_id, send_msg, ChatId},
job,
};
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] #[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub enum Timer { pub enum Timer {
@@ -329,14 +327,12 @@ WHERE
> 0; > 0;
if let Some(delete_device_after) = context.get_config_delete_device_after().await? { if let Some(delete_device_after) = context.get_config_delete_device_after().await? {
let self_chat_id = lookup_by_contact_id(context, DC_CONTACT_ID_SELF) let self_chat_id = ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF)
.await .await?
.unwrap_or_default() .unwrap_or_default();
.0; let device_chat_id = ChatId::lookup_by_contact(context, DC_CONTACT_ID_DEVICE)
let device_chat_id = lookup_by_contact_id(context, DC_CONTACT_ID_DEVICE) .await?
.await .unwrap_or_default();
.unwrap_or_default()
.0;
let threshold_timestamp = time() - delete_device_after; let threshold_timestamp = time() - delete_device_after;

View File

@@ -213,6 +213,8 @@ pub enum EventType {
/// - Messages sent, received or removed /// - Messages sent, received or removed
/// - Chats created, deleted or archived /// - Chats created, deleted or archived
/// - A draft has been set /// - A draft has been set
///
/// The `chat_id` and `msg_id` values will be 0 if more than one message is changed.
#[strum(props(id = "2000"))] #[strum(props(id = "2000"))]
MsgsChanged { chat_id: ChatId, msg_id: MsgId }, MsgsChanged { chat_id: ChatId, msg_id: MsgId },

View File

@@ -3,16 +3,18 @@
use std::any::Any; use std::any::Any;
use std::ffi::OsStr; use std::ffi::OsStr;
use ::pgp::types::KeyTrait;
use anyhow::{bail, ensure, format_err, Context as _, Result}; use anyhow::{bail, ensure, format_err, Context as _, Result};
use async_std::path::{Path, PathBuf};
use async_std::{ use async_std::{
fs::{self, File}, fs::{self, File},
path::{Path, PathBuf},
prelude::*, prelude::*,
}; };
use async_tar::Archive;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use crate::chat; use crate::blob::BlobObject;
use crate::chat::delete_and_reset_all_device_msgs; use crate::chat::{self, delete_and_reset_all_device_msgs, ChatId};
use crate::config::Config; use crate::config::Config;
use crate::constants::{Viewtype, DC_CONTACT_ID_SELF}; use crate::constants::{Viewtype, DC_CONTACT_ID_SELF};
use crate::context::Context; use crate::context::Context;
@@ -23,15 +25,13 @@ use crate::dc_tools::{
use crate::e2ee; use crate::e2ee;
use crate::events::EventType; use crate::events::EventType;
use crate::key::{self, DcKey, DcSecretKey, SignedPublicKey, SignedSecretKey}; use crate::key::{self, DcKey, DcSecretKey, SignedPublicKey, SignedSecretKey};
use crate::log::LogExt;
use crate::message::{Message, MsgId}; use crate::message::{Message, MsgId};
use crate::mimeparser::SystemMessage; use crate::mimeparser::SystemMessage;
use crate::param::Param; use crate::param::Param;
use crate::pgp; use crate::pgp;
use crate::sql::{self, Sql}; use crate::sql::{self, Sql};
use crate::stock_str; use crate::stock_str;
use crate::{blob::BlobObject, log::LogExt};
use ::pgp::types::KeyTrait;
use async_tar::Archive;
// Name of the database file in the backup. // Name of the database file in the backup.
const DBFILE_BACKUP_NAME: &str = "dc_database_backup.sqlite"; const DBFILE_BACKUP_NAME: &str = "dc_database_backup.sqlite";
@@ -231,7 +231,7 @@ async fn do_initiate_key_transfer(context: &Context) -> Result<String> {
) )
.await?; .await?;
let chat_id = chat::create_by_contact_id(context, DC_CONTACT_ID_SELF).await?; let chat_id = ChatId::create_for_contact(context, DC_CONTACT_ID_SELF).await?;
msg = Message::default(); msg = Message::default();
msg.viewtype = Viewtype::File; msg.viewtype = Viewtype::File;
msg.param.set(Param::File, setup_file_blob.as_name()); msg.param.set(Param::File, setup_file_blob.as_name());

View File

@@ -12,25 +12,24 @@ use deltachat_derive::{FromSql, ToSql};
use itertools::Itertools; use itertools::Itertools;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use crate::blob::BlobObject;
use crate::chat::{self, Chat, ChatId, ChatIdBlocked, ChatItem};
use crate::config::Config;
use crate::constants::{Blocked, Chattype, DC_CHAT_ID_DEADDROP};
use crate::contact::{normalize_name, Contact, Modifier, Origin};
use crate::context::Context;
use crate::dc_tools::{dc_delete_file, dc_read_file, time}; use crate::dc_tools::{dc_delete_file, dc_read_file, time};
use crate::ephemeral::load_imap_deletion_msgid; use crate::ephemeral::load_imap_deletion_msgid;
use crate::events::EventType; use crate::events::EventType;
use crate::imap::{Imap, ImapActionResult}; use crate::imap::{Imap, ImapActionResult};
use crate::location; use crate::location;
use crate::message::MsgId; use crate::log::LogExt;
use crate::message::{self, Message, MessageState}; use crate::message::{self, Message, MessageState, MsgId};
use crate::mimefactory::MimeFactory; use crate::mimefactory::MimeFactory;
use crate::param::{Param, Params}; use crate::param::{Param, Params};
use crate::scheduler::InterruptInfo;
use crate::smtp::Smtp; use crate::smtp::Smtp;
use crate::{blob::BlobObject, contact::normalize_name, contact::Modifier, contact::Origin}; use crate::sql;
use crate::{
chat::{self, Chat, ChatId, ChatItem},
constants::DC_CHAT_ID_DEADDROP,
};
use crate::{config::Config, constants::Blocked};
use crate::{constants::Chattype, contact::Contact};
use crate::{context::Context, log::LogExt};
use crate::{scheduler::InterruptInfo, sql};
// results in ~3 weeks for the last backoff timespan // results in ~3 weeks for the last backoff timespan
const JOB_RETRIES: u32 = 17; const JOB_RETRIES: u32 = 17;
@@ -729,10 +728,12 @@ impl Job {
}; };
match chat.typ { match chat.typ {
Chattype::Group | Chattype::Mailinglist => { Chattype::Group | Chattype::Mailinglist => {
if let Ok((_1to1_chat, Blocked::Not)) = if let Ok(Some(one_to_one_chat)) =
chat::lookup_by_contact_id(context, msg.from_id).await ChatIdBlocked::lookup_by_contact(context, msg.from_id).await
{ {
chat.id.unblock(context).await; if one_to_one_chat.blocked == Blocked::Not {
chat.id.unblock(context).await;
}
} }
} }
Chattype::Single | Chattype::Undefined => {} Chattype::Single | Chattype::Undefined => {}

View File

@@ -1941,10 +1941,9 @@ pub async fn estimate_deletion_cnt(
from_server: bool, from_server: bool,
seconds: i64, seconds: i64,
) -> Result<usize> { ) -> Result<usize> {
let self_chat_id = chat::lookup_by_contact_id(context, DC_CONTACT_ID_SELF) let self_chat_id = ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF)
.await .await?
.unwrap_or_default() .unwrap_or_default();
.0;
let threshold_timestamp = time() - seconds; let threshold_timestamp = time() - seconds;
let cnt = if from_server { let cnt = if from_server {
@@ -2226,7 +2225,7 @@ mod tests {
let contact_id = Contact::create(&t.ctx, "", "bob@example.net") let contact_id = Contact::create(&t.ctx, "", "bob@example.net")
.await .await
.unwrap(); .unwrap();
chat::create_by_contact_id(&t.ctx, contact_id) ChatId::create_for_contact(&t.ctx, contact_id)
.await .await
.unwrap(); .unwrap();
} }

View File

@@ -1348,11 +1348,11 @@ mod tests {
use async_std::prelude::*; use async_std::prelude::*;
use crate::chat::ChatId; use crate::chat::ChatId;
use crate::chatlist::Chatlist;
use crate::contact::Origin; use crate::contact::Origin;
use crate::dc_receive_imf::dc_receive_imf; use crate::dc_receive_imf::dc_receive_imf;
use crate::mimeparser::MimeMessage; use crate::mimeparser::MimeMessage;
use crate::test_utils::TestContext; use crate::test_utils::{get_chat_msg, TestContext};
use crate::{chatlist::Chatlist, test_utils::get_chat_msg};
use async_std::fs::File; use async_std::fs::File;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@@ -1687,7 +1687,7 @@ mod tests {
.unwrap() .unwrap()
.0; .0;
let chat_id = chat::create_by_contact_id(&t, contact_id).await.unwrap(); let chat_id = ChatId::create_for_contact(&t, contact_id).await.unwrap();
let mut new_msg = Message::new(Viewtype::Text); let mut new_msg = Message::new(Viewtype::Text);
new_msg.set_text(Some("Hi".to_string())); new_msg.set_text(Some("Hi".to_string()));

View File

@@ -304,7 +304,7 @@ async fn securejoin(context: &Context, qr: &str) -> Result<ChatId, JoinError> {
StartedProtocolVariant::SetupContact => { StartedProtocolVariant::SetupContact => {
// for a one-to-one-chat, the chat is already known, return the chat-id, // for a one-to-one-chat, the chat is already known, return the chat-id,
// the verification runs in background // the verification runs in background
let chat_id = chat::create_by_contact_id(context, invite.contact_id()) let chat_id = ChatId::create_for_contact(context, invite.contact_id())
.await .await
.map_err(JoinError::UnknownContact)?; .map_err(JoinError::UnknownContact)?;
Ok(chat_id) Ok(chat_id)

View File

@@ -185,7 +185,7 @@ impl BobState {
context: &Context, context: &Context,
invite: QrInvite, invite: QrInvite,
) -> Result<(Self, BobHandshakeStage), JoinError> { ) -> Result<(Self, BobHandshakeStage), JoinError> {
let chat_id = chat::create_by_contact_id(context, invite.contact_id()) let chat_id = ChatId::create_for_contact(context, invite.contact_id())
.await .await
.map_err(JoinError::UnknownContact)?; .map_err(JoinError::UnknownContact)?;
if fingerprint_equals_sender(context, invite.fingerprint(), chat_id).await? { if fingerprint_equals_sender(context, invite.fingerprint(), chat_id).await? {

View File

@@ -8,8 +8,7 @@ use strum::EnumProperty;
use strum_macros::EnumProperty; use strum_macros::EnumProperty;
use crate::blob::BlobObject; use crate::blob::BlobObject;
use crate::chat; use crate::chat::{self, ChatId, ProtectionStatus};
use crate::chat::ProtectionStatus;
use crate::config::Config; use crate::config::Config;
use crate::constants::{Viewtype, DC_CONTACT_ID_SELF}; use crate::constants::{Viewtype, DC_CONTACT_ID_SELF};
use crate::contact::{Contact, Origin}; use crate::contact::{Contact, Origin};
@@ -916,7 +915,7 @@ impl Context {
self.sql self.sql
.set_raw_config_bool("self-chat-added", true) .set_raw_config_bool("self-chat-added", true)
.await?; .await?;
chat::create_by_contact_id(self, DC_CONTACT_ID_SELF).await?; ChatId::create_for_contact(self, DC_CONTACT_ID_SELF).await?;
} }
// add welcome-messages. by the label, this is done only once, // add welcome-messages. by the label, this is done only once,

View File

@@ -355,7 +355,7 @@ impl TestContext {
.await .await
.unwrap(); .unwrap();
let chat_id = chat::create_by_contact_id(self, contact_id).await.unwrap(); let chat_id = ChatId::create_for_contact(self, contact_id).await.unwrap();
Chat::load_from_db(self, chat_id).await.unwrap() Chat::load_from_db(self, chat_id).await.unwrap()
} }
@@ -367,13 +367,13 @@ impl TestContext {
let contact = Contact::create(self, name, addr) let contact = Contact::create(self, name, addr)
.await .await
.expect("failed to create contact"); .expect("failed to create contact");
let chat_id = chat::create_by_contact_id(self, contact).await.unwrap(); let chat_id = ChatId::create_for_contact(self, contact).await.unwrap();
Chat::load_from_db(self, chat_id).await.unwrap() Chat::load_from_db(self, chat_id).await.unwrap()
} }
/// Retrieves the "self" chat. /// Retrieves the "self" chat.
pub async fn get_self_chat(&self) -> Chat { pub async fn get_self_chat(&self) -> Chat {
let chat_id = chat::create_by_contact_id(self, DC_CONTACT_ID_SELF) let chat_id = ChatId::create_for_contact(self, DC_CONTACT_ID_SELF)
.await .await
.unwrap(); .unwrap();
Chat::load_from_db(self, chat_id).await.unwrap() Chat::load_from_db(self, chat_id).await.unwrap()