mirror of
https://github.com/chatmail/core.git
synced 2026-05-03 05:16:28 +03:00
Remove deaddrop chat
Contact request chats are not merged into a single virtual "deaddrop" chat anymore. Instead, they are shown in the chatlist the same way as other chats, but sending of messages to them is not allowed and MDNs are not sent automatically until the chat is "accepted" by the user. New API: - dc_chat_is_contact_request(): returns true if chat is a contact request. In this case option to accept and block the chat via dc_accept_chat() and dc_block_chat() should be shown in the UI. - dc_accept_chat(): accept contact request and unblock the chat - dc_block_chat(): decline contact request and block the chat Removed API: - dc_create_chat_by_msg_id(): deprecated 2021-02-07 in favor of dc_decide_on_contact_request() - dc_marknoticed_contact(): deprecated 2021-02-07 in favor of dc_decide_on_contact_request() - dc_decide_on_contact_request(): this call requires a message ID from deaddrop chat as input. As deaddrop chat is removed, this call can't be used anymore. - dc_msg_get_real_chat_id(): use dc_msg_get_chat_id() instead, the only difference between these calls was in handling of deaddrop chat - removed DC_CHAT_ID_DEADDROP and DC_STR_DEADDROP constants
This commit is contained in:
337
src/chat.rs
337
src/chat.rs
@@ -17,9 +17,9 @@ use crate::color::str_to_color;
|
||||
use crate::config::Config;
|
||||
use crate::constants::{
|
||||
Blocked, Chattype, Viewtype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK,
|
||||
DC_CHAT_ID_DEADDROP, DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH, DC_CONTACT_ID_DEVICE,
|
||||
DC_CONTACT_ID_INFO, DC_CONTACT_ID_LAST_SPECIAL, DC_CONTACT_ID_SELF, DC_GCM_ADDDAYMARKER,
|
||||
DC_GCM_INFO_ONLY, DC_RESEND_USER_AVATAR_DAYS,
|
||||
DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_INFO,
|
||||
DC_CONTACT_ID_LAST_SPECIAL, DC_CONTACT_ID_SELF, DC_GCM_ADDDAYMARKER, DC_GCM_INFO_ONLY,
|
||||
DC_RESEND_USER_AVATAR_DAYS,
|
||||
};
|
||||
use crate::contact::{addr_cmp, Contact, Origin, VerifiedStatus};
|
||||
use crate::context::Context;
|
||||
@@ -113,15 +113,6 @@ impl ChatId {
|
||||
(0..=DC_CHAT_ID_LAST_SPECIAL.0).contains(&self.0)
|
||||
}
|
||||
|
||||
/// Chat ID which represents the deaddrop chat.
|
||||
///
|
||||
/// This is a virtual chat showing all messages belonging to chats
|
||||
/// flagged with [Blocked::Deaddrop]. Usually the UI will show
|
||||
/// these messages as contact requests.
|
||||
pub fn is_deaddrop(self) -> bool {
|
||||
self == DC_CHAT_ID_DEADDROP
|
||||
}
|
||||
|
||||
/// Chat ID for messages which need to be deleted.
|
||||
///
|
||||
/// Messages which should be deleted get this chat ID and are
|
||||
@@ -180,14 +171,11 @@ impl ChatId {
|
||||
///
|
||||
/// 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.unblock(context).await?;
|
||||
}
|
||||
chat.id
|
||||
}
|
||||
@@ -227,23 +215,90 @@ impl ChatId {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_blocked(self, context: &Context, new_blocked: Blocked) -> bool {
|
||||
/// Updates chat blocked status.
|
||||
///
|
||||
/// Returns true if the value was modified.
|
||||
async fn set_blocked(self, context: &Context, new_blocked: Blocked) -> Result<bool> {
|
||||
if self.is_special() {
|
||||
warn!(context, "ignoring setting of Block-status for {}", self);
|
||||
return false;
|
||||
bail!("ignoring setting of Block-status for {}", self);
|
||||
}
|
||||
context
|
||||
let count = context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE chats SET blocked=? WHERE id=?;",
|
||||
"UPDATE chats SET blocked=?1 WHERE id=?2 AND blocked != ?1",
|
||||
paramsv![new_blocked, self],
|
||||
)
|
||||
.await
|
||||
.is_ok()
|
||||
.await?;
|
||||
Ok(count > 0)
|
||||
}
|
||||
|
||||
pub async fn unblock(self, context: &Context) {
|
||||
self.set_blocked(context, Blocked::Not).await;
|
||||
/// Blocks the chat as a result of explicit user action.
|
||||
pub async fn block(self, context: &Context) -> Result<()> {
|
||||
let chat = Chat::load_from_db(context, self).await?;
|
||||
|
||||
match chat.typ {
|
||||
Chattype::Undefined => bail!("Can't block chat of undefined chattype"),
|
||||
Chattype::Single => {
|
||||
for contact_id in get_chat_contacts(context, self).await? {
|
||||
if contact_id != DC_CONTACT_ID_SELF {
|
||||
info!(
|
||||
context,
|
||||
"Blocking the contact {} to block 1:1 chat", contact_id
|
||||
);
|
||||
Contact::block(context, contact_id).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Chattype::Group => {
|
||||
info!(context, "Can't block groups yet, deleting the chat");
|
||||
self.delete(context).await?;
|
||||
}
|
||||
Chattype::Mailinglist => {
|
||||
if self.set_blocked(context, Blocked::Manually).await? {
|
||||
context.emit_event(EventType::ChatModified(self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unblocks the chat.
|
||||
pub async fn unblock(self, context: &Context) -> Result<()> {
|
||||
self.set_blocked(context, Blocked::Not).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Accept the contact request.
|
||||
///
|
||||
/// Unblocks the chat and scales up origin of contacts.
|
||||
pub async fn accept(self, context: &Context) -> Result<()> {
|
||||
let chat = Chat::load_from_db(context, self).await?;
|
||||
|
||||
match chat.typ {
|
||||
Chattype::Undefined => bail!("Can't accept chat of undefined chattype"),
|
||||
Chattype::Single | Chattype::Group => {
|
||||
// User has "created a chat" with all these contacts.
|
||||
//
|
||||
// Previously accepting a chat literally created a chat because unaccepted chats
|
||||
// went to "contact requests" list rather than normal chatlist.
|
||||
for contact_id in get_chat_contacts(context, self).await? {
|
||||
if contact_id != DC_CONTACT_ID_SELF {
|
||||
Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
Chattype::Mailinglist => {
|
||||
// If the message is from a mailing list, the contacts are not counted as "known"
|
||||
}
|
||||
}
|
||||
|
||||
if self.set_blocked(context, Blocked::Not).await? {
|
||||
context.emit_event(EventType::ChatModified(self));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets protection without sending a message.
|
||||
@@ -580,27 +635,13 @@ impl ChatId {
|
||||
|
||||
/// Returns number of messages in a chat.
|
||||
pub async fn get_msg_cnt(self, context: &Context) -> Result<usize> {
|
||||
let count = if self.is_deaddrop() {
|
||||
context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*)
|
||||
FROM msgs
|
||||
WHERE hidden=0
|
||||
AND from_id!=?
|
||||
AND chat_id IN (SELECT id FROM chats WHERE blocked=?)",
|
||||
paramsv![DC_CONTACT_ID_INFO, Blocked::Deaddrop],
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*) FROM msgs WHERE hidden=0 AND chat_id=?",
|
||||
paramsv![self],
|
||||
)
|
||||
.await?
|
||||
};
|
||||
let count = context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*) FROM msgs WHERE hidden=0 AND chat_id=?",
|
||||
paramsv![self],
|
||||
)
|
||||
.await?;
|
||||
Ok(count as usize)
|
||||
}
|
||||
|
||||
@@ -615,32 +656,17 @@ impl ChatId {
|
||||
// the times are average, no matter if there are fresh messages or not -
|
||||
// and have to be multiplied by the number of items shown at once on the chatlist,
|
||||
// so savings up to 2 seconds are possible on older devices - newer ones will feel "snappier" :)
|
||||
let count = if self.is_deaddrop() {
|
||||
context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*)
|
||||
FROM msgs
|
||||
WHERE state=?
|
||||
AND hidden=0
|
||||
AND from_id!=?
|
||||
AND chat_id IN (SELECT id FROM chats WHERE blocked=?)",
|
||||
paramsv![MessageState::InFresh, DC_CONTACT_ID_INFO, Blocked::Deaddrop],
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*)
|
||||
let count = context
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*)
|
||||
FROM msgs
|
||||
WHERE state=?
|
||||
AND hidden=0
|
||||
AND chat_id=?;",
|
||||
paramsv![MessageState::InFresh, self],
|
||||
)
|
||||
.await?
|
||||
};
|
||||
paramsv![MessageState::InFresh, self],
|
||||
)
|
||||
.await?;
|
||||
Ok(count as usize)
|
||||
}
|
||||
|
||||
@@ -778,9 +804,7 @@ impl ChatId {
|
||||
|
||||
impl std::fmt::Display for ChatId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.is_deaddrop() {
|
||||
write!(f, "Chat#Deadrop")
|
||||
} else if self.is_trash() {
|
||||
if self.is_trash() {
|
||||
write!(f, "Chat#Trash")
|
||||
} else if self.is_archived_link() {
|
||||
write!(f, "Chat#ArchivedLink")
|
||||
@@ -867,9 +891,7 @@ impl Chat {
|
||||
.await
|
||||
.context(format!("Failed loading chat {} from database", chat_id))?;
|
||||
|
||||
if chat.id.is_deaddrop() {
|
||||
chat.name = stock_str::dead_drop(context).await;
|
||||
} else if chat.id.is_archived_link() {
|
||||
if chat.id.is_archived_link() {
|
||||
let tempname = stock_str::archived_chats(context).await;
|
||||
let cnt = dc_get_archived_cnt(context).await?;
|
||||
chat.name = format!("{} ({})", tempname, cnt);
|
||||
@@ -918,6 +940,7 @@ impl Chat {
|
||||
!self.id.is_special()
|
||||
&& !self.is_device_talk()
|
||||
&& !self.is_mailing_list()
|
||||
&& !self.is_contact_request()
|
||||
&& (self.typ == Chattype::Single
|
||||
|| is_contact_in_chat(context, self.id, DC_CONTACT_ID_SELF).await)
|
||||
}
|
||||
@@ -1019,6 +1042,14 @@ impl Chat {
|
||||
self.visibility
|
||||
}
|
||||
|
||||
/// Returns true if chat is a contact request.
|
||||
///
|
||||
/// Messages cannot be sent to such chat and read receipts are not
|
||||
/// sent until the chat is manually unblocked.
|
||||
pub fn is_contact_request(&self) -> bool {
|
||||
self.blocked == Blocked::Request
|
||||
}
|
||||
|
||||
pub fn is_unpromoted(&self) -> bool {
|
||||
self.param.get_int(Param::Unpromoted).unwrap_or_default() == 1
|
||||
}
|
||||
@@ -1353,58 +1384,12 @@ pub struct ChatInfo {
|
||||
/// Ephemeral message timer.
|
||||
pub ephemeral_timer: EphemeralTimer,
|
||||
// ToDo:
|
||||
// - [ ] deaddrop,
|
||||
// - [ ] summary,
|
||||
// - [ ] lastUpdated,
|
||||
// - [ ] freshMessageCounter,
|
||||
// - [ ] email
|
||||
}
|
||||
|
||||
/// Create a chat from a message ID.
|
||||
///
|
||||
/// Typically you'd do this for a message ID found in the
|
||||
/// [DC_CHAT_ID_DEADDROP] which turns the chat the message belongs to
|
||||
/// into a normal chat. The chat can be a 1:1 chat or a group chat
|
||||
/// and all messages belonging to the chat will be moved from the
|
||||
/// deaddrop to the normal chat.
|
||||
///
|
||||
/// In reality the messages already belong to this chat as receive_imf
|
||||
/// always creates chat IDs appropriately, so this function really
|
||||
/// only unblocks the chat and "scales up" the origin of the contact
|
||||
/// the message is from.
|
||||
///
|
||||
/// If prompting the user before calling this function, they should be
|
||||
/// asked whether they want to chat with the **contact** the message
|
||||
/// is from and **not** the group name since this can be really weird
|
||||
/// and confusing when taken from subject of implicit groups.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The "created" chat ID is returned.
|
||||
pub async fn create_by_msg_id(context: &Context, msg_id: MsgId) -> Result<ChatId> {
|
||||
let msg = Message::load_from_db(context, msg_id).await?;
|
||||
let chat = Chat::load_from_db(context, msg.chat_id).await?;
|
||||
ensure!(
|
||||
!chat.id.is_special(),
|
||||
"Message can not belong to a special chat"
|
||||
);
|
||||
if chat.blocked != Blocked::Not {
|
||||
chat.id.unblock(context).await;
|
||||
|
||||
// Sending with 0s as data since multiple messages may have changed.
|
||||
context.emit_event(EventType::MsgsChanged {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
|
||||
// If the message is from a mailing list, the contacts are not counted as "known"
|
||||
if !chat.is_mailing_list() {
|
||||
Contact::scaleup_origin_by_id(context, msg.from_id, Origin::CreateChat).await;
|
||||
}
|
||||
Ok(chat.id)
|
||||
}
|
||||
|
||||
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 let Some(chat_id) = ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF).await? {
|
||||
@@ -1942,27 +1927,7 @@ pub async fn get_chat_msgs(
|
||||
Ok(ret)
|
||||
};
|
||||
|
||||
let items = if chat_id.is_deaddrop() {
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp
|
||||
FROM msgs m
|
||||
LEFT JOIN chats
|
||||
ON m.chat_id=chats.id
|
||||
LEFT JOIN contacts
|
||||
ON m.from_id=contacts.id
|
||||
WHERE m.from_id!=?
|
||||
AND m.hidden=0
|
||||
AND chats.blocked=2
|
||||
AND contacts.blocked=0
|
||||
ORDER BY m.timestamp,m.id;",
|
||||
paramsv![DC_CONTACT_ID_INFO],
|
||||
process_row,
|
||||
process_rows,
|
||||
)
|
||||
.await?
|
||||
} else if (flags & DC_GCM_INFO_ONLY) != 0 {
|
||||
let items = if (flags & DC_GCM_INFO_ONLY) != 0 {
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
@@ -2021,31 +1986,6 @@ pub(crate) async fn marknoticed_chat_if_older_than(
|
||||
}
|
||||
|
||||
pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()> {
|
||||
// for the virtual deaddrop chat-id,
|
||||
// mark all messages that will appear in the deaddrop as noticed
|
||||
if chat_id.is_deaddrop() {
|
||||
if context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE msgs
|
||||
SET state=?1
|
||||
WHERE state=?2
|
||||
AND hidden=0
|
||||
AND chat_id IN (SELECT id FROM chats WHERE blocked=?3);",
|
||||
paramsv![
|
||||
MessageState::InNoticed,
|
||||
MessageState::InFresh,
|
||||
Blocked::Deaddrop
|
||||
],
|
||||
)
|
||||
.await?
|
||||
> 0
|
||||
{
|
||||
context.emit_event(EventType::MsgsNoticed(chat_id));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// "WHERE" below uses the index `(state, hidden, chat_id)`, see get_fresh_msg_cnt() for reasoning
|
||||
// the additional SELECT statement may speed up things as no write-blocking is needed.
|
||||
let exists = context
|
||||
@@ -2177,13 +2117,6 @@ pub async fn get_chat_contacts(context: &Context, chat_id: ChatId) -> Result<Vec
|
||||
// Normal chats do not include SELF. Group chats do (as it may happen that one is deleted from a
|
||||
// groupchat but the chats stays visible, moreover, this makes displaying lists easier)
|
||||
|
||||
if chat_id.is_deaddrop() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
// we could also create a list for all contacts in the deaddrop by searching contacts belonging to chats with
|
||||
// chats.blocked=2, however, currently this is not needed
|
||||
|
||||
let list = context
|
||||
.sql
|
||||
.query_map(
|
||||
@@ -3228,19 +3161,6 @@ mod tests {
|
||||
assert!(chat.get_profile_image(&t).await.unwrap().is_some());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_deaddrop_chat() {
|
||||
let t = TestContext::new().await;
|
||||
let chat = Chat::load_from_db(&t, DC_CHAT_ID_DEADDROP).await.unwrap();
|
||||
assert_eq!(DC_CHAT_ID_DEADDROP.0, 1);
|
||||
assert!(chat.id.is_deaddrop());
|
||||
assert!(!chat.is_self_talk());
|
||||
assert!(chat.visibility == ChatVisibility::Normal);
|
||||
assert!(!chat.is_device_talk());
|
||||
assert!(!chat.can_send(&t).await);
|
||||
assert_eq!(chat.name, stock_str::dead_drop(&t).await);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_add_device_msg_unlabelled() {
|
||||
let t = TestContext::new().await;
|
||||
@@ -3921,7 +3841,7 @@ mod tests {
|
||||
);
|
||||
send_text_msg(&alice, alice_chat_id, "hi!".to_string())
|
||||
.await
|
||||
.ok();
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
get_chat_msgs(&alice, alice_chat_id, 0, None)
|
||||
.await
|
||||
@@ -3945,11 +3865,14 @@ mod tests {
|
||||
let bob_chat = Chat::load_from_db(&bob, msg.chat_id).await.unwrap();
|
||||
assert_eq!(bob_chat.grpid, alice_chat.grpid);
|
||||
|
||||
// Bob accepts contact request.
|
||||
bob_chat.id.unblock(&bob).await.unwrap();
|
||||
|
||||
// Bob answers - simulate a normal MUA by not setting `Chat-*`-headers;
|
||||
// moreover, Bob's SMTP-server also replaces the `Message-ID:`-header
|
||||
send_text_msg(&bob, bob_chat.id, "ho!".to_string())
|
||||
.await
|
||||
.ok();
|
||||
.unwrap();
|
||||
let msg = bob.pop_sent_msg().await.payload();
|
||||
let msg = msg.replace("Message-ID: <Gr.", "Message-ID: <XXX");
|
||||
let msg = msg.replace("Chat-", "XXXX-");
|
||||
@@ -3994,7 +3917,6 @@ mod tests {
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 1);
|
||||
assert_eq!(chats.get_chat_id(0), chat.id);
|
||||
assert_ne!(chats.get_chat_id(0), DC_CHAT_ID_DEADDROP);
|
||||
assert_eq!(chat.id.get_fresh_msg_cnt(&t).await?, 1);
|
||||
assert_eq!(t.get_fresh_msgs().await?.len(), 1);
|
||||
|
||||
@@ -4020,7 +3942,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_marknoticed_deaddrop_chat() -> Result<()> {
|
||||
async fn test_contact_request_fresh_messages() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await?;
|
||||
@@ -4043,10 +3965,14 @@ mod tests {
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 1);
|
||||
assert_eq!(chats.get_chat_id(0), DC_CHAT_ID_DEADDROP);
|
||||
assert_eq!(DC_CHAT_ID_DEADDROP.get_msg_cnt(&t).await?, 1);
|
||||
assert_eq!(DC_CHAT_ID_DEADDROP.get_fresh_msg_cnt(&t).await?, 1);
|
||||
let msgs = get_chat_msgs(&t, DC_CHAT_ID_DEADDROP, 0, None).await?;
|
||||
let chat_id = chats.get_chat_id(0);
|
||||
assert!(Chat::load_from_db(&t, chat_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.is_contact_request());
|
||||
assert_eq!(chat_id.get_msg_cnt(&t).await?, 1);
|
||||
assert_eq!(chat_id.get_fresh_msg_cnt(&t).await?, 1);
|
||||
let msgs = get_chat_msgs(&t, chat_id, 0, None).await?;
|
||||
assert_eq!(msgs.len(), 1);
|
||||
let msg_id = match msgs.first().unwrap() {
|
||||
ChatItem::Message { msg_id } => *msg_id,
|
||||
@@ -4054,21 +3980,21 @@ mod tests {
|
||||
};
|
||||
let msg = message::Message::load_from_db(&t, msg_id).await?;
|
||||
assert_eq!(msg.state, MessageState::InFresh);
|
||||
assert_eq!(t.get_fresh_msgs().await?.len(), 0); // deaddrop is excluded from global badge
|
||||
|
||||
marknoticed_chat(&t, DC_CHAT_ID_DEADDROP).await?;
|
||||
// Contact requests are excluded from global badge.
|
||||
assert_eq!(t.get_fresh_msgs().await?.len(), 0);
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 0);
|
||||
assert_eq!(chats.len(), 1);
|
||||
let msg = message::Message::load_from_db(&t, msg_id).await?;
|
||||
assert_eq!(msg.state, MessageState::InNoticed);
|
||||
assert_eq!(msg.state, MessageState::InFresh);
|
||||
assert_eq!(t.get_fresh_msgs().await?.len(), 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_classic_deaddrop_chat() -> Result<()> {
|
||||
async fn test_classic_email_chat() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
// Alice enables receiving classic emails.
|
||||
@@ -4092,10 +4018,11 @@ mod tests {
|
||||
)
|
||||
.await?;
|
||||
|
||||
// There is one message in the contact requests chat.
|
||||
assert_eq!(DC_CHAT_ID_DEADDROP.get_fresh_msg_cnt(&alice).await?, 1);
|
||||
let msg = alice.get_last_msg().await;
|
||||
let chat_id = msg.chat_id;
|
||||
assert_eq!(chat_id.get_fresh_msg_cnt(&alice).await?, 1);
|
||||
|
||||
let msgs = get_chat_msgs(&alice, DC_CHAT_ID_DEADDROP, 0, None).await?;
|
||||
let msgs = get_chat_msgs(&alice, chat_id, 0, None).await?;
|
||||
assert_eq!(msgs.len(), 1);
|
||||
|
||||
// Alice disables receiving classic emails.
|
||||
@@ -4104,10 +4031,10 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Already received classic email should still be in the contact requests.
|
||||
assert_eq!(DC_CHAT_ID_DEADDROP.get_fresh_msg_cnt(&alice).await?, 1);
|
||||
// Already received classic email should still be in the chat.
|
||||
assert_eq!(chat_id.get_fresh_msg_cnt(&alice).await?, 1);
|
||||
|
||||
let msgs = get_chat_msgs(&alice, DC_CHAT_ID_DEADDROP, 0, None).await?;
|
||||
let msgs = get_chat_msgs(&alice, chat_id, 0, None).await?;
|
||||
assert_eq!(msgs.len(), 1);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -4,9 +4,9 @@ use anyhow::{bail, ensure, Result};
|
||||
|
||||
use crate::chat::{update_special_chat_names, Chat, ChatId, ChatVisibility};
|
||||
use crate::constants::{
|
||||
Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_CHAT_ID_DEADDROP,
|
||||
DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_SELF, DC_CONTACT_ID_UNDEFINED, DC_GCL_ADD_ALLDONE_HINT,
|
||||
DC_GCL_ARCHIVED_ONLY, DC_GCL_FOR_FORWARDING, DC_GCL_NO_SPECIALS,
|
||||
Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_CONTACT_ID_DEVICE,
|
||||
DC_CONTACT_ID_SELF, DC_CONTACT_ID_UNDEFINED, DC_GCL_ADD_ALLDONE_HINT, DC_GCL_ARCHIVED_ONLY,
|
||||
DC_GCL_FOR_FORWARDING, DC_GCL_NO_SPECIALS,
|
||||
};
|
||||
use crate::contact::Contact;
|
||||
use crate::context::Context;
|
||||
@@ -34,11 +34,8 @@ use crate::stock_str;
|
||||
/// and for each messages that is scrolled into view, dc_get_msg() is called then.
|
||||
///
|
||||
/// Why no listflags?
|
||||
/// Without listflags, dc_get_chatlist() adds the deaddrop and the archive "link" automatically as needed.
|
||||
/// The UI can just render these items differently then. Although the deaddrop link is currently always the
|
||||
/// first entry and only present on new messages, there is the rough idea that it can be optionally always
|
||||
/// present and sorted into the list by date. Rendering the deaddrop in the described way
|
||||
/// would not add extra work in the UI then.
|
||||
/// Without listflags, dc_get_chatlist() adds the archive "link" automatically as needed.
|
||||
/// The UI can just render these items differently then.
|
||||
#[derive(Debug)]
|
||||
pub struct Chatlist {
|
||||
/// Stores pairs of `chat_id, message_id`
|
||||
@@ -58,12 +55,6 @@ impl Chatlist {
|
||||
///
|
||||
/// By default, the function adds some special entries to the list.
|
||||
/// These special entries can be identified by the ID returned by chatlist.get_chat_id():
|
||||
/// - DC_CHAT_ID_DEADDROP (1) - this special chat is present if there are
|
||||
/// messages from addresses that have no relationship to the configured account.
|
||||
/// The last of these messages is represented by DC_CHAT_ID_DEADDROP and you can retrieve details
|
||||
/// about it with chatlist.get_msg_id(). Typically, the UI asks the user "Do you want to chat with NAME?"
|
||||
/// and offers the options "Start chat", "Block" and "Not now";
|
||||
/// The decision should be passed to dc_decide_on_contact_request().
|
||||
/// - DC_CHAT_ID_ARCHIVED_LINK (6) - this special chat is present if the user has
|
||||
/// archived *any* chat using dc_set_chat_visibility(). The UI should show a link as
|
||||
/// "Show archived chats", if the user clicks this item, the UI should show a
|
||||
@@ -79,9 +70,9 @@ impl Chatlist {
|
||||
/// the pseudo-chat DC_CHAT_ID_ARCHIVED_LINK is added if there are *any* archived
|
||||
/// chats
|
||||
/// - the flag DC_GCL_FOR_FORWARDING sorts "Saved messages" to the top of the chatlist
|
||||
/// and hides the device-chat,
|
||||
/// and hides the device-chat and contact requests
|
||||
/// typically used on forwarding, may be combined with DC_GCL_NO_SPECIALS
|
||||
/// - if the flag DC_GCL_NO_SPECIALS is set, deaddrop and archive link are not added
|
||||
/// - if the flag DC_GCL_NO_SPECIALS is set, archive link is not added
|
||||
/// to the list (may be used eg. for selecting chats on forwarding, the flag is
|
||||
/// not needed when DC_GCL_ARCHIVED_ONLY is already set)
|
||||
/// - if the flag DC_GCL_ADD_ALLDONE_HINT is set, DC_CHAT_ID_ALLDONE_HINT
|
||||
@@ -136,13 +127,8 @@ impl Chatlist {
|
||||
// timestamp
|
||||
// - the list starts with the newest chats
|
||||
//
|
||||
// nb: the query currently shows messages from blocked
|
||||
// contacts in groups. however, for normal-groups, this is
|
||||
// okay as the message is also returned by dc_get_chat_msgs()
|
||||
// (otherwise it would be hard to follow conversations, wa and
|
||||
// tg do the same) for the deaddrop, however, they should
|
||||
// really be hidden, however, _currently_ the deaddrop is not
|
||||
// shown at all permanent in the chatlist.
|
||||
// The query shows messages from blocked contacts in
|
||||
// groups. Otherwise it would be hard to follow conversations.
|
||||
let mut ids = if let Some(query_contact_id) = query_contact_id {
|
||||
// show chats shared with a given contact
|
||||
context.sql.query_map(
|
||||
@@ -157,7 +143,7 @@ impl Chatlist {
|
||||
AND (hidden=0 OR state=?1)
|
||||
ORDER BY timestamp DESC, id DESC LIMIT 1)
|
||||
WHERE c.id>9
|
||||
AND c.blocked=0
|
||||
AND c.blocked!=1
|
||||
AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?2)
|
||||
GROUP BY c.id
|
||||
ORDER BY c.archived=?3 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
|
||||
@@ -184,7 +170,7 @@ impl Chatlist {
|
||||
AND (hidden=0 OR state=?)
|
||||
ORDER BY timestamp DESC, id DESC LIMIT 1)
|
||||
WHERE c.id>9
|
||||
AND c.blocked=0
|
||||
AND c.blocked!=1
|
||||
AND c.archived=1
|
||||
GROUP BY c.id
|
||||
ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
|
||||
@@ -218,7 +204,7 @@ impl Chatlist {
|
||||
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=0
|
||||
AND c.blocked!=1
|
||||
AND c.name LIKE ?3
|
||||
GROUP BY c.id
|
||||
ORDER BY IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
|
||||
@@ -236,7 +222,7 @@ impl Chatlist {
|
||||
} else {
|
||||
ChatId::new(0)
|
||||
};
|
||||
let mut ids = context.sql.query_map(
|
||||
let ids = context.sql.query_map(
|
||||
"SELECT c.id, m.id
|
||||
FROM chats c
|
||||
LEFT JOIN msgs m
|
||||
@@ -248,22 +234,15 @@ impl Chatlist {
|
||||
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=0
|
||||
AND NOT c.archived=?3
|
||||
AND (c.blocked=0 OR (c.blocked=2 AND NOT ?3))
|
||||
AND NOT c.archived=?4
|
||||
GROUP BY c.id
|
||||
ORDER BY c.id=?4 DESC, c.archived=?5 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
|
||||
paramsv![MessageState::OutDraft, skip_id, ChatVisibility::Archived, sort_id_up, ChatVisibility::Pinned],
|
||||
ORDER BY c.id=?5 DESC, c.archived=?6 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
|
||||
paramsv![MessageState::OutDraft, skip_id, flag_for_forwarding, ChatVisibility::Archived, sort_id_up, ChatVisibility::Pinned],
|
||||
process_row,
|
||||
process_rows,
|
||||
).await?;
|
||||
if !flag_no_specials {
|
||||
if let Some(last_deaddrop_fresh_msg_id) =
|
||||
get_last_deaddrop_fresh_msg(context).await?
|
||||
{
|
||||
if !flag_for_forwarding {
|
||||
ids.insert(0, (DC_CHAT_ID_DEADDROP, Some(last_deaddrop_fresh_msg_id)));
|
||||
}
|
||||
}
|
||||
add_archived_link_item = true;
|
||||
}
|
||||
ids
|
||||
@@ -407,28 +386,6 @@ pub async fn dc_get_archived_cnt(context: &Context) -> Result<usize> {
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
async fn get_last_deaddrop_fresh_msg(context: &Context) -> Result<Option<MsgId>> {
|
||||
// We have an index over the state-column, this should be
|
||||
// sufficient as there are typically only few fresh messages.
|
||||
let id = context
|
||||
.sql
|
||||
.query_get_value(
|
||||
concat!(
|
||||
"SELECT m.id",
|
||||
" FROM msgs m",
|
||||
" LEFT JOIN chats c",
|
||||
" ON c.id=m.chat_id",
|
||||
" WHERE m.state=10",
|
||||
" AND m.hidden=0",
|
||||
" AND c.blocked=2",
|
||||
" ORDER BY m.timestamp DESC, m.id DESC;"
|
||||
),
|
||||
paramsv![],
|
||||
)
|
||||
.await?;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -436,8 +393,6 @@ mod tests {
|
||||
use crate::chat::{create_group_chat, get_chat_contacts, ProtectionStatus};
|
||||
use crate::constants::Viewtype;
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::message;
|
||||
use crate::message::ContactRequestDecision;
|
||||
use crate::stock_str::StockMessage;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
@@ -547,7 +502,7 @@ mod tests {
|
||||
async fn test_search_single_chat() -> anyhow::Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
// receive a one-to-one-message, accept contact request
|
||||
// receive a one-to-one-message
|
||||
dc_receive_imf(
|
||||
&t,
|
||||
b"From: Bob Authname <bob@example.org>\n\
|
||||
@@ -565,15 +520,13 @@ mod tests {
|
||||
.await?;
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, Some("Bob Authname"), None).await?;
|
||||
assert_eq!(chats.len(), 0);
|
||||
// Contact request should be searchable
|
||||
assert_eq!(chats.len(), 1);
|
||||
|
||||
let msg = t.get_last_msg().await;
|
||||
assert_eq!(msg.get_chat_id(), DC_CHAT_ID_DEADDROP);
|
||||
let chat_id = msg.get_chat_id();
|
||||
chat_id.accept(&t).await.unwrap();
|
||||
|
||||
let chat_id =
|
||||
message::decide_on_contact_request(&t, msg.get_id(), ContactRequestDecision::StartChat)
|
||||
.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?;
|
||||
@@ -611,7 +564,7 @@ mod tests {
|
||||
async fn test_search_single_chat_without_authname() -> anyhow::Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
// receive a one-to-one-message without authname set, accept contact request
|
||||
// receive a one-to-one-message without authname set
|
||||
dc_receive_imf(
|
||||
&t,
|
||||
b"From: bob@example.org\n\
|
||||
@@ -629,10 +582,8 @@ mod tests {
|
||||
.await?;
|
||||
|
||||
let msg = t.get_last_msg().await;
|
||||
let chat_id =
|
||||
message::decide_on_contact_request(&t, msg.get_id(), ContactRequestDecision::StartChat)
|
||||
.await
|
||||
.unwrap();
|
||||
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?;
|
||||
|
||||
@@ -25,7 +25,7 @@ pub static DC_VERSION_STR: Lazy<String> = Lazy::new(|| env!("CARGO_PKG_VERSION")
|
||||
pub enum Blocked {
|
||||
Not = 0,
|
||||
Manually = 1,
|
||||
Deaddrop = 2,
|
||||
Request = 2,
|
||||
}
|
||||
|
||||
impl Default for Blocked {
|
||||
@@ -123,8 +123,6 @@ pub const DC_RESEND_USER_AVATAR_DAYS: i64 = 14;
|
||||
// do not use too small value that will annoy users checking for nonexistant updates.
|
||||
pub const DC_OUTDATED_WARNING_DAYS: i64 = 365;
|
||||
|
||||
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
|
||||
pub const DC_CHAT_ID_DEADDROP: ChatId = ChatId::new(1);
|
||||
/// messages that should be deleted get this chat_id; the messages are deleted from the working thread later then. This is also needed as rfc724_mid should be preset as long as the message is not deleted on the server (otherwise it is downloaded again)
|
||||
pub const DC_CHAT_ID_TRASH: ChatId = ChatId::new(3);
|
||||
/// only an indicator in a chatlist
|
||||
@@ -404,7 +402,7 @@ mod tests {
|
||||
assert_eq!(Blocked::Not, Blocked::default());
|
||||
assert_eq!(Blocked::Not, Blocked::from_i32(0).unwrap());
|
||||
assert_eq!(Blocked::Manually, Blocked::from_i32(1).unwrap());
|
||||
assert_eq!(Blocked::Deaddrop, Blocked::from_i32(2).unwrap());
|
||||
assert_eq!(Blocked::Request, Blocked::from_i32(2).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -14,8 +14,8 @@ use crate::chat::ChatId;
|
||||
use crate::color::str_to_color;
|
||||
use crate::config::Config;
|
||||
use crate::constants::{
|
||||
Blocked, Chattype, DC_CHAT_ID_DEADDROP, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_DEVICE_ADDR,
|
||||
DC_CONTACT_ID_LAST_SPECIAL, DC_CONTACT_ID_SELF, DC_GCL_ADD_SELF, DC_GCL_VERIFIED_ONLY,
|
||||
Blocked, Chattype, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_DEVICE_ADDR, DC_CONTACT_ID_LAST_SPECIAL,
|
||||
DC_CONTACT_ID_SELF, DC_GCL_ADD_SELF, DC_GCL_VERIFIED_ONLY,
|
||||
};
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::{dc_get_abs_path, improve_single_line_input, EmailAddress};
|
||||
@@ -277,20 +277,15 @@ impl Contact {
|
||||
}
|
||||
|
||||
/// Mark messages from a contact as noticed.
|
||||
/// The contact is expected to belong to the deaddrop,
|
||||
/// therefore, DC_EVENT_MSGS_NOTICED(DC_CHAT_ID_DEADDROP) is emitted.
|
||||
pub async fn mark_noticed(context: &Context, id: u32) {
|
||||
if context
|
||||
pub async fn mark_noticed(context: &Context, id: u32) -> Result<()> {
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE msgs SET state=? WHERE from_id=? AND state=?;",
|
||||
paramsv![MessageState::InNoticed, id as i32, MessageState::InFresh],
|
||||
)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
context.emit_event(EventType::MsgsNoticed(DC_CHAT_ID_DEADDROP));
|
||||
}
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if an e-mail address belongs to a known and unblocked contact.
|
||||
@@ -1200,7 +1195,7 @@ WHERE type=? AND id IN (
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
Contact::mark_noticed(context, contact_id).await;
|
||||
Contact::mark_noticed(context, contact_id).await?;
|
||||
context.emit_event(EventType::ContactsChanged(Some(contact_id)));
|
||||
}
|
||||
|
||||
@@ -1208,7 +1203,7 @@ WHERE type=? AND id IN (
|
||||
// if the contact is a mailinglist address explicitly created to allow unblocking
|
||||
if !new_blocking && contact.origin == Origin::MailinglistAddress {
|
||||
if let Ok((chat_id, _, _)) = chat::get_chat_id_by_grpid(context, contact.addr).await {
|
||||
chat_id.set_blocked(context, Blocked::Not).await;
|
||||
chat_id.unblock(context).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,8 +279,8 @@ impl Context {
|
||||
let l2 = LoginParam::from_database(self, "configured_").await?;
|
||||
let displayname = self.get_config(Config::Displayname).await?;
|
||||
let chats = get_chat_cnt(self).await? as usize;
|
||||
let real_msgs = message::get_real_msg_cnt(self).await as usize;
|
||||
let deaddrop_msgs = message::get_deaddrop_msg_cnt(self).await as usize;
|
||||
let unblocked_msgs = message::get_unblocked_msg_cnt(self).await as usize;
|
||||
let request_msgs = message::get_request_msg_cnt(self).await as usize;
|
||||
let contacts = Contact::get_real_cnt(self).await? as usize;
|
||||
let is_configured = self.get_config_int(Config::Configured).await?;
|
||||
let dbversion = self
|
||||
@@ -336,8 +336,8 @@ impl Context {
|
||||
// insert values
|
||||
res.insert("bot", self.get_config_int(Config::Bot).await?.to_string());
|
||||
res.insert("number_of_chats", chats.to_string());
|
||||
res.insert("number_of_chat_messages", real_msgs.to_string());
|
||||
res.insert("messages_in_contact_requests", deaddrop_msgs.to_string());
|
||||
res.insert("number_of_chat_messages", unblocked_msgs.to_string());
|
||||
res.insert("messages_in_contact_requests", request_msgs.to_string());
|
||||
res.insert("number_of_contacts", contacts.to_string());
|
||||
res.insert("database_dir", self.get_dbfile().display().to_string());
|
||||
res.insert("database_version", dbversion.to_string());
|
||||
@@ -422,7 +422,7 @@ impl Context {
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Get a list of fresh, unmuted messages in any chat but deaddrop.
|
||||
/// Get a list of fresh, unmuted messages in unblocked chats.
|
||||
///
|
||||
/// The list starts with the most recent message
|
||||
/// and is typically used to show notifications.
|
||||
|
||||
@@ -351,9 +351,6 @@ pub async fn from_field_to_contact_id(
|
||||
context,
|
||||
"mail has an empty From header: {:?}", from_address_list
|
||||
);
|
||||
// if there is no from given, from_id stays 0 which is just fine. These messages
|
||||
// are very rare, however, we have to add them to the database (they go to the
|
||||
// "deaddrop" chat) to avoid a re-download from the server. See also [**]
|
||||
|
||||
Ok((0, false, Origin::Unknown))
|
||||
}
|
||||
@@ -463,8 +460,6 @@ async fn add_parts(
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
// 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
|
||||
if chat_id.is_unset() && mime_parser.failure_report.is_some() {
|
||||
*chat_id = DC_CHAT_ID_TRASH;
|
||||
info!(context, "Message belongs to an NDN (TRASH)",);
|
||||
@@ -487,7 +482,7 @@ async fn add_parts(
|
||||
id: _,
|
||||
blocked: Blocked::Not,
|
||||
}) => Blocked::Not,
|
||||
_ => Blocked::Deaddrop,
|
||||
_ => Blocked::Request,
|
||||
};
|
||||
|
||||
let (new_chat_id, new_chat_id_blocked) = create_or_lookup_group(
|
||||
@@ -509,7 +504,7 @@ async fn add_parts(
|
||||
&& chat_id_blocked != Blocked::Not
|
||||
&& create_blocked == Blocked::Not
|
||||
{
|
||||
new_chat_id.unblock(context).await;
|
||||
new_chat_id.unblock(context).await?;
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
@@ -582,7 +577,7 @@ async fn add_parts(
|
||||
let create_blocked = if from_id == to_id {
|
||||
Blocked::Not
|
||||
} else {
|
||||
Blocked::Deaddrop
|
||||
Blocked::Request
|
||||
};
|
||||
|
||||
if let Some(chat) = test_normal_chat {
|
||||
@@ -599,7 +594,7 @@ async fn add_parts(
|
||||
}
|
||||
if !chat_id.is_unset() && Blocked::Not != chat_id_blocked {
|
||||
if Blocked::Not == create_blocked {
|
||||
chat_id.unblock(context).await;
|
||||
chat_id.unblock(context).await?;
|
||||
chat_id_blocked = Blocked::Not;
|
||||
} else if parent.is_some() {
|
||||
// we do not want any chat to be created implicitly. Because of the origin-scale-up,
|
||||
@@ -631,13 +626,13 @@ async fn add_parts(
|
||||
&& show_emails != ShowEmails::All
|
||||
{
|
||||
state = MessageState::InNoticed;
|
||||
} else if fetching_existing_messages && Blocked::Deaddrop == chat_id_blocked {
|
||||
} else if fetching_existing_messages && Blocked::Request == chat_id_blocked {
|
||||
// The fetched existing message should be shown in the chatlist-contact-request because
|
||||
// a new user won't find the contact request in the menu
|
||||
state = MessageState::InFresh;
|
||||
}
|
||||
|
||||
let is_spam = (chat_id_blocked == Blocked::Deaddrop)
|
||||
let is_spam = (chat_id_blocked == Blocked::Request)
|
||||
&& !incoming_origin.is_known()
|
||||
&& (is_dc_message == MessengerMessage::No)
|
||||
&& context.is_spam_folder(server_folder).await?;
|
||||
@@ -723,7 +718,7 @@ async fn add_parts(
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
// automatically unblock chat when the user sends a message
|
||||
if !chat_id.is_unset() && chat_id_blocked != Blocked::Not {
|
||||
new_chat_id.unblock(context).await;
|
||||
new_chat_id.unblock(context).await?;
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
@@ -731,7 +726,7 @@ async fn add_parts(
|
||||
let create_blocked = if !Contact::is_blocked_load(context, to_id).await {
|
||||
Blocked::Not
|
||||
} else {
|
||||
Blocked::Deaddrop
|
||||
Blocked::Request
|
||||
};
|
||||
if let Ok(chat) =
|
||||
ChatIdBlocked::get_for_contact(context, to_id, create_blocked).await
|
||||
@@ -744,7 +739,7 @@ async fn add_parts(
|
||||
&& Blocked::Not != chat_id_blocked
|
||||
&& Blocked::Not == create_blocked
|
||||
{
|
||||
chat_id.unblock(context).await;
|
||||
chat_id.unblock(context).await?;
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
@@ -766,7 +761,7 @@ async fn add_parts(
|
||||
}
|
||||
|
||||
if !chat_id.is_unset() && Blocked::Not != chat_id_blocked {
|
||||
chat_id.unblock(context).await;
|
||||
chat_id.unblock(context).await?;
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
@@ -1689,14 +1684,14 @@ async fn create_or_lookup_mailinglist(
|
||||
Chattype::Mailinglist,
|
||||
&listid,
|
||||
&name,
|
||||
Blocked::Deaddrop,
|
||||
Blocked::Request,
|
||||
ProtectionStatus::Unprotected,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(chat_id) => {
|
||||
chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await;
|
||||
(chat_id, Blocked::Deaddrop)
|
||||
(chat_id, Blocked::Request)
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
@@ -1706,7 +1701,7 @@ async fn create_or_lookup_mailinglist(
|
||||
&listid,
|
||||
e.to_string()
|
||||
);
|
||||
(ChatId::new(0), Blocked::Deaddrop)
|
||||
(ChatId::new(0), Blocked::Request)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -2159,9 +2154,8 @@ mod tests {
|
||||
|
||||
use crate::chat::{get_chat_msgs, ChatItem, ChatVisibility};
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::constants::{DC_CHAT_ID_DEADDROP, DC_CONTACT_ID_INFO, DC_GCL_NO_SPECIALS};
|
||||
use crate::message::ContactRequestDecision::*;
|
||||
use crate::message::{ContactRequestDecision, Message};
|
||||
use crate::constants::{DC_CONTACT_ID_INFO, DC_GCL_NO_SPECIALS};
|
||||
use crate::message::Message;
|
||||
use crate::test_utils::{get_chat_msg, TestContext};
|
||||
|
||||
#[test]
|
||||
@@ -2322,12 +2316,12 @@ mod tests {
|
||||
.unwrap();
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1);
|
||||
assert!(chats.get_chat_id(0).is_deaddrop());
|
||||
let chat_id = chat::create_by_msg_id(&t, chats.get_msg_id(0).unwrap().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = chats.get_chat_id(0);
|
||||
assert!(!chat_id.is_special());
|
||||
let chat = chat::Chat::load_from_db(&t, chat_id).await.unwrap();
|
||||
assert!(chat.is_contact_request());
|
||||
chat_id.accept(&t).await.unwrap();
|
||||
let chat = chat::Chat::load_from_db(&t, chat_id).await.unwrap();
|
||||
assert_eq!(chat.typ, Chattype::Single);
|
||||
assert_eq!(chat.name, "Bob");
|
||||
assert_eq!(chat::get_chat_contacts(&t, chat_id).await.unwrap().len(), 1);
|
||||
@@ -2358,9 +2352,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 2);
|
||||
let chat_id = chat::create_by_msg_id(&t, chats.get_msg_id(0).unwrap().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = chats.get_chat_id(0);
|
||||
let chat = chat::Chat::load_from_db(&t, chat_id).await.unwrap();
|
||||
assert_eq!(chat.typ, Chattype::Group);
|
||||
assert_eq!(chat.name, "group with Alice, Bob and Claire");
|
||||
@@ -2375,13 +2367,13 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// adhoc-group with unknown contacts with show_emails=all will show up in the deaddrop
|
||||
// adhoc-group with unknown contacts with show_emails=all will show up in a single chat
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1);
|
||||
assert!(chats.get_chat_id(0).is_deaddrop());
|
||||
let chat_id = chat::create_by_msg_id(&t, chats.get_msg_id(0).unwrap().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = chats.get_chat_id(0);
|
||||
let chat = chat::Chat::load_from_db(&t, chat_id).await.unwrap();
|
||||
assert!(chat.is_contact_request());
|
||||
chat_id.accept(&t).await.unwrap();
|
||||
let chat = chat::Chat::load_from_db(&t, chat_id).await.unwrap();
|
||||
assert_eq!(chat.typ, Chattype::Group);
|
||||
assert_eq!(chat.name, "group with Alice, Bob and Claire");
|
||||
@@ -2526,8 +2518,8 @@ mod tests {
|
||||
#[async_std::test]
|
||||
async fn test_no_from() {
|
||||
// if there is no from given, from_id stays 0 which is just fine. These messages
|
||||
// are very rare, however, we have to add them to the database (they go to the
|
||||
// "deaddrop" chat) to avoid a re-download from the server. See also [**]
|
||||
// are very rare, however, we have to add them to the database
|
||||
// to avoid a re-download from the server.
|
||||
|
||||
let t = TestContext::new_alice().await;
|
||||
let context = &t;
|
||||
@@ -2912,9 +2904,8 @@ mod tests {
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1);
|
||||
|
||||
let chat_id = chat::create_by_msg_id(&t.ctx, chats.get_msg_id(0).unwrap().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = chats.get_chat_id(0);
|
||||
chat_id.accept(&t).await.unwrap();
|
||||
let chat = chat::Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
|
||||
assert!(chat.is_mailing_list());
|
||||
@@ -2985,9 +2976,8 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
let chat_id = chat::create_by_msg_id(&t.ctx, chats.get_msg_id(0).unwrap().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = chats.get_chat_id(0);
|
||||
chat_id.accept(&t).await.unwrap();
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
assert_eq!(chat.name, "delta-dev");
|
||||
|
||||
@@ -2997,8 +2987,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_mailing_list_decide_block() {
|
||||
let deaddrop = DC_CHAT_ID_DEADDROP;
|
||||
async fn test_block_mailing_list() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
@@ -3010,36 +2999,31 @@ mod tests {
|
||||
.unwrap();
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1);
|
||||
assert_eq!(chats.get_chat_id(0), deaddrop); // Test that the message is shown in the deaddrop
|
||||
let chat_id = chats.get_chat_id(0);
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
assert!(chat.is_contact_request());
|
||||
|
||||
let msg = get_chat_msg(&t, deaddrop, 0, 1).await;
|
||||
|
||||
// Answer "Block" on the contact request
|
||||
message::decide_on_contact_request(&t.ctx, msg.get_id(), Block).await;
|
||||
// Block the contact request.
|
||||
chat_id.block(&t).await.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 0); // Test that the message disappeared
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, deaddrop, 0, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(msgs.len(), 0);
|
||||
|
||||
dc_receive_imf(&t.ctx, DC_MAILINGLIST2, "INBOX", 1, false)
|
||||
dc_receive_imf(&t.ctx, DC_MAILINGLIST2, "INBOX", 2, false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Test that the mailing list stays disappeared
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 0); // Test that the message is not shown
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, deaddrop, 0, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(msgs.len(), 0);
|
||||
|
||||
// Both messages are in the same blocked chat.
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id, 0, None).await.unwrap();
|
||||
assert_eq!(msgs.len(), 2);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_mailing_list_decide_block_then_unblock() {
|
||||
let deaddrop = DC_CHAT_ID_DEADDROP;
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
|
||||
@@ -3049,16 +3033,16 @@ mod tests {
|
||||
let blocked = Contact::get_all_blocked(&t).await.unwrap();
|
||||
assert_eq!(blocked.len(), 0);
|
||||
|
||||
// Answer "Block" on the contact request,
|
||||
// this should add one blocked contact and deaddrop should be empty again
|
||||
let msg = get_chat_msg(&t, deaddrop, 0, 1).await;
|
||||
message::decide_on_contact_request(&t, msg.get_id(), Block).await;
|
||||
// Block the contact request, this should add one blocked contact.
|
||||
let msg = t.get_last_msg().await;
|
||||
msg.chat_id.block(&t).await.unwrap();
|
||||
|
||||
let blocked = Contact::get_all_blocked(&t).await.unwrap();
|
||||
assert_eq!(blocked.len(), 1);
|
||||
let msgs = chat::get_chat_msgs(&t, deaddrop, 0, None).await.unwrap();
|
||||
assert_eq!(msgs.len(), 0);
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 0); // Test that the message is not shown
|
||||
|
||||
// Unblock contact and check if the next message arrives in real chat
|
||||
// Unblock contact and check if the next message arrives in a chat
|
||||
Contact::unblock(&t, *blocked.first().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -3069,16 +3053,12 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
let msg = t.get_last_msg().await;
|
||||
assert_ne!(msg.chat_id, deaddrop);
|
||||
let msgs = chat::get_chat_msgs(&t, msg.chat_id, 0, None).await.unwrap();
|
||||
assert_eq!(msgs.len(), 2);
|
||||
let msgs = chat::get_chat_msgs(&t, deaddrop, 0, None).await.unwrap();
|
||||
assert_eq!(msgs.len(), 0);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_mailing_list_decide_not_now() {
|
||||
let deaddrop = DC_CHAT_ID_DEADDROP;
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
@@ -3089,33 +3069,31 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let msg = get_chat_msg(&t, deaddrop, 0, 1).await;
|
||||
let msg = t.get_last_msg().await;
|
||||
let chat_id = msg.get_chat_id();
|
||||
|
||||
// Answer "Not now" on the contact request
|
||||
message::decide_on_contact_request(&t.ctx, msg.get_id(), NotNow).await;
|
||||
// Open the chat and go back
|
||||
chat::marknoticed_chat(&t.ctx, chat_id).await.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 0); // Test that the message disappeared
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, deaddrop, 0, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(msgs.len(), 1); // ...but is still shown in the deaddrop
|
||||
assert_eq!(chats.len(), 1); // Test that chat is still in the chatlist
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id, 0, None).await.unwrap();
|
||||
assert_eq!(msgs.len(), 1); // ...and contains 1 message
|
||||
|
||||
dc_receive_imf(&t.ctx, DC_MAILINGLIST2, "INBOX", 1, false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1); // Test that the new mailing list message is shown again
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, deaddrop, 0, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(chats.len(), 1); // Test that the new mailing list message got into the same chat
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, chat_id, 0, None).await.unwrap();
|
||||
assert_eq!(msgs.len(), 2);
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
assert!(chat.is_contact_request());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_mailing_list_decide_accept() {
|
||||
let deaddrop = DC_CHAT_ID_DEADDROP;
|
||||
let t = TestContext::new_alice().await;
|
||||
t.ctx
|
||||
.set_config(Config::ShowEmails, Some("2"))
|
||||
@@ -3126,15 +3104,13 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let msg = get_chat_msg(&t, deaddrop, 0, 1).await;
|
||||
|
||||
// Answer "Start chat" on the contact request
|
||||
message::decide_on_contact_request(&t.ctx, msg.get_id(), StartChat).await;
|
||||
let msg = t.get_last_msg().await;
|
||||
let chat_id = msg.get_chat_id();
|
||||
chat_id.accept(&t).await.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
assert_eq!(chats.len(), 1); // Test that the message is shown
|
||||
let chat_id = chats.get_chat_id(0);
|
||||
assert_ne!(chat_id, deaddrop);
|
||||
assert!(!chat_id.is_special());
|
||||
|
||||
dc_receive_imf(&t.ctx, DC_MAILINGLIST2, "INBOX", 1, false)
|
||||
.await
|
||||
@@ -3168,10 +3144,7 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
let msg = t.get_last_msg().await;
|
||||
let chat_id =
|
||||
message::decide_on_contact_request(&t, msg.id, ContactRequestDecision::StartChat)
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = msg.get_chat_id();
|
||||
let chat = Chat::load_from_db(&t, chat_id).await.unwrap();
|
||||
assert_eq!(chat.typ, Chattype::Mailinglist);
|
||||
assert_eq!(chat.grpid, "mylist@bar.org");
|
||||
@@ -3236,7 +3209,7 @@ mod tests {
|
||||
let msg = t.get_last_msg().await;
|
||||
let chat = Chat::load_from_db(&t, msg.chat_id).await.unwrap();
|
||||
assert_eq!(chat.typ, Chattype::Mailinglist);
|
||||
assert_eq!(chat.blocked, Blocked::Deaddrop);
|
||||
assert_eq!(chat.blocked, Blocked::Request);
|
||||
assert_eq!(
|
||||
chat.grpid,
|
||||
"399fc0402f1b154b67965632e.100761.list-id.mcsv.net"
|
||||
@@ -3266,7 +3239,7 @@ mod tests {
|
||||
assert!(msg.has_html());
|
||||
let chat = Chat::load_from_db(&t, msg.chat_id).await.unwrap();
|
||||
assert_eq!(chat.typ, Chattype::Mailinglist);
|
||||
assert_eq!(chat.blocked, Blocked::Deaddrop);
|
||||
assert_eq!(chat.blocked, Blocked::Request);
|
||||
assert_eq!(chat.grpid, "1234ABCD-123LMNO.mailing.dhl.de");
|
||||
assert_eq!(chat.name, "DHL Paket");
|
||||
}
|
||||
@@ -3293,7 +3266,7 @@ mod tests {
|
||||
assert!(msg.has_html());
|
||||
let chat = Chat::load_from_db(&t, msg.chat_id).await.unwrap();
|
||||
assert_eq!(chat.typ, Chattype::Mailinglist);
|
||||
assert_eq!(chat.blocked, Blocked::Deaddrop);
|
||||
assert_eq!(chat.blocked, Blocked::Request);
|
||||
assert_eq!(chat.grpid, "dpdde.mxmail.service.dpd.de");
|
||||
assert_eq!(chat.name, "DPD");
|
||||
}
|
||||
@@ -3302,8 +3275,6 @@ mod tests {
|
||||
async fn test_mailing_list_with_mimepart_footer() {
|
||||
let t = TestContext::new_alice().await;
|
||||
t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
|
||||
let deaddrop = DC_CHAT_ID_DEADDROP;
|
||||
assert_eq!(get_chat_msgs(&t, deaddrop, 0, None).await.unwrap().len(), 0);
|
||||
|
||||
// the mailing list message contains two top-level texts.
|
||||
// the second text is a footer that is added by some mailing list software
|
||||
@@ -3326,13 +3297,12 @@ mod tests {
|
||||
);
|
||||
assert!(msg.has_html());
|
||||
let chat = Chat::load_from_db(&t, msg.chat_id).await.unwrap();
|
||||
assert_eq!(get_chat_msgs(&t, deaddrop, 0, None).await.unwrap().len(), 1);
|
||||
assert_eq!(
|
||||
get_chat_msgs(&t, msg.chat_id, 0, None).await.unwrap().len(),
|
||||
1
|
||||
);
|
||||
assert_eq!(chat.typ, Chattype::Mailinglist);
|
||||
assert_eq!(chat.blocked, Blocked::Deaddrop);
|
||||
assert_eq!(chat.blocked, Blocked::Request);
|
||||
assert_eq!(chat.grpid, "intern.lists.abc.de");
|
||||
assert_eq!(chat.name, "Intern");
|
||||
}
|
||||
@@ -3539,7 +3509,7 @@ YEAAAAAA!.
|
||||
assert_eq!(msg.chat_id, reply_msg.chat_id);
|
||||
|
||||
// Make sure we looked at real chat ID and do not just
|
||||
// test that both messages got into deaddrop.
|
||||
// test that both messages got into the same virtual chat.
|
||||
assert!(!msg.chat_id.is_special());
|
||||
}
|
||||
|
||||
@@ -3791,8 +3761,9 @@ YEAAAAAA!.
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
chat::create_by_msg_id(&claire, msg_id).await.unwrap();
|
||||
|
||||
let msg = Message::load_from_db(&claire, msg_id).await.unwrap();
|
||||
msg.chat_id.accept(&claire).await.unwrap();
|
||||
assert_eq!(msg.get_subject(), "i have a question");
|
||||
assert!(msg.get_text().unwrap().contains("hi support!"));
|
||||
let chat = Chat::load_from_db(&claire, msg.chat_id).await.unwrap();
|
||||
@@ -3919,9 +3890,8 @@ YEAAAAAA!.
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = chat::create_by_msg_id(&t, t.get_last_msg().await.id)
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = t.get_last_msg().await.chat_id;
|
||||
chat_id.accept(&t).await.unwrap();
|
||||
let msg = get_chat_msg(&t, chat_id, 0, 1).await; // Make sure that the message is actually in the chat
|
||||
assert!(!msg.chat_id.is_special());
|
||||
assert_eq!(msg.text.unwrap(), "Hi – hello");
|
||||
@@ -4093,7 +4063,6 @@ Message content",
|
||||
|
||||
// Outgoing email should create a chat.
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_ne!(msg.chat_id, DC_CHAT_ID_DEADDROP);
|
||||
assert_eq!(msg.get_text().unwrap(), "Subj – Message content");
|
||||
}
|
||||
|
||||
|
||||
37
src/job.rs
37
src/job.rs
@@ -13,9 +13,8 @@ use itertools::Itertools;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat::{self, Chat, ChatId, ChatIdBlocked, ChatItem};
|
||||
use crate::chat::{self, ChatId};
|
||||
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};
|
||||
@@ -711,40 +710,6 @@ impl Job {
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that if there now is a chat with a contact (created by an outgoing
|
||||
// message), then group contact requests from this contact should also be unblocked.
|
||||
// See <https://github.com/deltachat/deltachat-core-rust/issues/2097>.
|
||||
for item in job_try!(chat::get_chat_msgs(context, DC_CHAT_ID_DEADDROP, 0, None).await) {
|
||||
if let ChatItem::Message { msg_id } = item {
|
||||
let msg = match Message::load_from_db(context, msg_id).await {
|
||||
Err(e) => {
|
||||
warn!(context, "can't get msg: {:#}", e);
|
||||
return Status::RetryLater;
|
||||
}
|
||||
Ok(m) => m,
|
||||
};
|
||||
let chat = match Chat::load_from_db(context, msg.chat_id).await {
|
||||
Err(e) => {
|
||||
warn!(context, "can't get chat: {:#}", e);
|
||||
return Status::RetryLater;
|
||||
}
|
||||
Ok(c) => c,
|
||||
};
|
||||
match chat.typ {
|
||||
Chattype::Group | Chattype::Mailinglist => {
|
||||
if let Ok(Some(one_to_one_chat)) =
|
||||
ChatIdBlocked::lookup_by_contact(context, msg.from_id).await
|
||||
{
|
||||
if one_to_one_chat.blocked == Blocked::Not {
|
||||
chat.id.unblock(context).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
Chattype::Single | Chattype::Undefined => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!(context, "Done fetching existing messages.");
|
||||
Status::Finished(Ok(()))
|
||||
}
|
||||
|
||||
142
src/message.rs
142
src/message.rs
@@ -13,9 +13,9 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::chat::{self, Chat, ChatId};
|
||||
use crate::config::Config;
|
||||
use crate::constants::{
|
||||
Blocked, Chattype, VideochatType, Viewtype, DC_CHAT_ID_DEADDROP, DC_CHAT_ID_TRASH,
|
||||
DC_CONTACT_ID_INFO, DC_CONTACT_ID_LAST_SPECIAL, DC_CONTACT_ID_SELF, DC_MAX_GET_INFO_LEN,
|
||||
DC_MAX_GET_TEXT_LEN, DC_MSG_ID_LAST_SPECIAL,
|
||||
Blocked, Chattype, VideochatType, Viewtype, DC_CHAT_ID_TRASH, DC_CONTACT_ID_INFO,
|
||||
DC_CONTACT_ID_LAST_SPECIAL, DC_CONTACT_ID_SELF, DC_MAX_GET_INFO_LEN, DC_MAX_GET_TEXT_LEN,
|
||||
DC_MSG_ID_LAST_SPECIAL,
|
||||
};
|
||||
use crate::contact::{Contact, Origin};
|
||||
use crate::context::Context;
|
||||
@@ -109,7 +109,7 @@ impl MsgId {
|
||||
Ok(Some(ConfiguredInboxFolder))
|
||||
}
|
||||
} else {
|
||||
// Blocked/deaddrop message in the spam folder, leave it there
|
||||
// Blocked or contact request message in the spam folder, leave it there
|
||||
Ok(None)
|
||||
};
|
||||
}
|
||||
@@ -520,19 +520,8 @@ impl Message {
|
||||
self.from_id
|
||||
}
|
||||
|
||||
/// get the chat-id,
|
||||
/// if the message is a contact request, the DC_CHAT_ID_DEADDROP is returned.
|
||||
/// Returns the chat ID.
|
||||
pub fn get_chat_id(&self) -> ChatId {
|
||||
if self.chat_blocked != Blocked::Not {
|
||||
DC_CHAT_ID_DEADDROP
|
||||
} else {
|
||||
self.chat_id
|
||||
}
|
||||
}
|
||||
|
||||
/// get the chat-id, also when the message is still a contact request.
|
||||
/// DC_CHAT_ID_DEADDROP is never returned.
|
||||
pub fn get_real_chat_id(&self) -> ChatId {
|
||||
self.chat_id
|
||||
}
|
||||
|
||||
@@ -959,13 +948,6 @@ impl Message {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Display, Debug, FromPrimitive)]
|
||||
pub enum ContactRequestDecision {
|
||||
StartChat = 0,
|
||||
Block = 1,
|
||||
NotNow = 2,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@@ -1147,80 +1129,6 @@ impl Lot {
|
||||
}
|
||||
}
|
||||
|
||||
/// Call this when the user decided about a deaddrop message ("Do you want to chat with NAME?").
|
||||
///
|
||||
/// If the decision is `StartChat`, this will create a new chat and return the chat id.
|
||||
/// If the decision is `Block`, this will usually block the sender.
|
||||
/// If the decision is `NotNow`, this will usually mark all messages from this sender as read.
|
||||
///
|
||||
/// If the message belongs to a mailing list, makes sure that all messages from this mailing list are
|
||||
/// blocked or marked as noticed.
|
||||
///
|
||||
/// The user should be asked whether they want to chat with the _contact_ belonging to the message;
|
||||
/// the group names may be really weird when taken from the subject of implicit (= ad-hoc)
|
||||
/// groups and this may look confusing. Moreover, this function also scales up the origin of the contact.
|
||||
///
|
||||
/// If the chat belongs to a mailing list, you can also ask
|
||||
/// "Would you like to read MAILING LIST NAME in Delta Chat?"
|
||||
/// (use `Message.get_real_chat_id()` to get the chat-id for the contact request
|
||||
/// and then `Chat.is_mailing_list()`, `Chat.get_name()` and so on)
|
||||
pub async fn decide_on_contact_request(
|
||||
context: &Context,
|
||||
msg_id: MsgId,
|
||||
decision: ContactRequestDecision,
|
||||
) -> Option<ChatId> {
|
||||
let msg = match Message::load_from_db(context, msg_id).await {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
warn!(context, "Can't load message: {}", e);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let chat = match Chat::load_from_db(context, msg.chat_id).await {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
warn!(context, "Can't load chat: {}", e);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let mut created_chat_id = None;
|
||||
use ContactRequestDecision::*;
|
||||
match (decision, chat.is_mailing_list()) {
|
||||
(StartChat, _) => match chat::create_by_msg_id(context, msg.id).await {
|
||||
Ok(id) => created_chat_id = Some(id),
|
||||
Err(e) => warn!(context, "decide_on_contact_request error: {}", e),
|
||||
},
|
||||
|
||||
(Block, false) => {
|
||||
if let Err(e) = Contact::block(context, msg.from_id).await {
|
||||
warn!(context, "Can't block contact: {}", e);
|
||||
}
|
||||
}
|
||||
(Block, true) => {
|
||||
if !msg.chat_id.set_blocked(context, Blocked::Manually).await {
|
||||
warn!(context, "Block mailing list failed.")
|
||||
}
|
||||
}
|
||||
|
||||
(NotNow, false) => Contact::mark_noticed(context, msg.from_id).await,
|
||||
(NotNow, true) => {
|
||||
if let Err(e) = chat::marknoticed_chat(context, msg.chat_id).await {
|
||||
warn!(context, "Marknoticed failed: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiple chats may have changed, so send 0s
|
||||
// (performance is not so important because this function is not called very often)
|
||||
context.emit_event(EventType::MsgsChanged {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
created_chat_id
|
||||
}
|
||||
|
||||
pub async fn get_msg_info(context: &Context, msg_id: MsgId) -> Result<String> {
|
||||
let msg = Message::load_from_db(context, msg_id).await?;
|
||||
let rawtxt: Option<String> = context
|
||||
@@ -1907,8 +1815,8 @@ async fn ndn_maybe_add_info_msg(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The number of messages assigned to real chat (!=deaddrop, !=trash)
|
||||
pub async fn get_real_msg_cnt(context: &Context) -> usize {
|
||||
/// The number of messages assigned to unblocked chats
|
||||
pub async fn get_unblocked_msg_cnt(context: &Context) -> usize {
|
||||
match context
|
||||
.sql
|
||||
.count(
|
||||
@@ -1921,13 +1829,14 @@ pub async fn get_real_msg_cnt(context: &Context) -> usize {
|
||||
{
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
error!(context, "dc_get_real_msg_cnt() failed. {}", err);
|
||||
error!(context, "dc_get_unblocked_msg_cnt() failed. {}", err);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_deaddrop_msg_cnt(context: &Context) -> usize {
|
||||
/// Returns the number of messages in contact request chats.
|
||||
pub async fn get_request_msg_cnt(context: &Context) -> usize {
|
||||
match context
|
||||
.sql
|
||||
.count(
|
||||
@@ -1940,7 +1849,7 @@ pub async fn get_deaddrop_msg_cnt(context: &Context) -> usize {
|
||||
{
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
error!(context, "dc_get_deaddrop_msg_cnt() failed. {}", err);
|
||||
error!(context, "dc_get_request_msg_cnt() failed. {}", err);
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -2099,7 +2008,7 @@ mod tests {
|
||||
];
|
||||
|
||||
// These are the same as above, but all messages in Spam stay in Spam
|
||||
const COMBINATIONS_DEADDROP: &[(&str, bool, bool, &str)] = &[
|
||||
const COMBINATIONS_REQUEST: &[(&str, bool, bool, &str)] = &[
|
||||
("INBOX", false, false, "INBOX"),
|
||||
("INBOX", false, true, "INBOX"),
|
||||
("INBOX", true, false, "INBOX"),
|
||||
@@ -2132,8 +2041,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_needs_move_incoming_deaddrop() {
|
||||
for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_DEADDROP {
|
||||
async fn test_needs_move_incoming_request() {
|
||||
for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_REQUEST {
|
||||
check_needs_move_combination(
|
||||
folder,
|
||||
*mvbox_move,
|
||||
@@ -2682,10 +2591,7 @@ mod tests {
|
||||
|
||||
// check chat-id of this message
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert!(msg.get_chat_id().is_deaddrop());
|
||||
assert!(msg.get_chat_id().is_special());
|
||||
assert!(!msg.get_real_chat_id().is_deaddrop());
|
||||
assert!(!msg.get_real_chat_id().is_special());
|
||||
assert!(!msg.get_chat_id().is_special());
|
||||
assert_eq!(msg.get_text().unwrap(), "hello".to_string());
|
||||
}
|
||||
|
||||
@@ -2746,33 +2652,31 @@ mod tests {
|
||||
msg.set_text(Some("this is the text!".to_string()));
|
||||
|
||||
// alice sends to bob,
|
||||
// bob does not know alice yet and messages go to bob's deaddrop
|
||||
assert_eq!(Chatlist::try_load(&bob, 0, None, None).await?.len(), 0);
|
||||
bob.recv_msg(&alice.send_msg(alice_chat.id, &mut msg).await)
|
||||
.await;
|
||||
let msg1 = bob.get_last_msg().await;
|
||||
let bob_chat_id = msg1.chat_id;
|
||||
bob.recv_msg(&alice.send_msg(alice_chat.id, &mut msg).await)
|
||||
.await;
|
||||
let msg2 = bob.get_last_msg().await;
|
||||
assert_eq!(msg1.chat_id, msg2.chat_id);
|
||||
assert_ne!(msg1.chat_id, DC_CHAT_ID_DEADDROP);
|
||||
let chats = Chatlist::try_load(&bob, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 1);
|
||||
assert_eq!(chats.get_chat_id(0), DC_CHAT_ID_DEADDROP);
|
||||
let msgs = chat::get_chat_msgs(&bob, DC_CHAT_ID_DEADDROP, 0, None).await?;
|
||||
let msgs = chat::get_chat_msgs(&bob, bob_chat_id, 0, None).await?;
|
||||
assert_eq!(msgs.len(), 2);
|
||||
assert_eq!(bob.get_fresh_msgs().await?.len(), 0);
|
||||
|
||||
// that has no effect in deaddrop
|
||||
// that has no effect in contact request
|
||||
markseen_msgs(&bob, vec![msg1.id, msg2.id]).await?;
|
||||
|
||||
assert_eq!(Chatlist::try_load(&bob, 0, None, None).await?.len(), 1);
|
||||
let msgs = chat::get_chat_msgs(&bob, DC_CHAT_ID_DEADDROP, 0, None).await?;
|
||||
let bob_chat = Chat::load_from_db(&bob, bob_chat_id).await?;
|
||||
assert_eq!(bob_chat.blocked, Blocked::Request);
|
||||
|
||||
let msgs = chat::get_chat_msgs(&bob, bob_chat_id, 0, None).await?;
|
||||
assert_eq!(msgs.len(), 2);
|
||||
let bob_chat_id =
|
||||
decide_on_contact_request(&bob, msg2.get_id(), ContactRequestDecision::StartChat)
|
||||
.await
|
||||
.unwrap();
|
||||
bob_chat_id.accept(&bob).await.unwrap();
|
||||
|
||||
// bob sends to alice,
|
||||
// alice knows bob and messages appear in normal chat
|
||||
|
||||
@@ -1806,9 +1806,8 @@ mod tests {
|
||||
|
||||
let chats = Chatlist::try_load(context, 0, None, None).await.unwrap();
|
||||
|
||||
let chat_id = chat::create_by_msg_id(context, chats.get_msg_id(0).unwrap().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = chats.get_chat_id(0);
|
||||
chat_id.accept(context).await.unwrap();
|
||||
|
||||
let mut new_msg = Message::new(Viewtype::Text);
|
||||
new_msg.set_text(Some("Hi".to_string()));
|
||||
|
||||
@@ -2793,7 +2793,7 @@ On 2020-10-25, Bob wrote:
|
||||
assert_eq!(msg.viewtype, Viewtype::Image);
|
||||
assert_eq!(msg.error(), None);
|
||||
assert_eq!(msg.is_dc_message, MessengerMessage::No);
|
||||
assert_eq!(msg.chat_blocked, Blocked::Deaddrop);
|
||||
assert_eq!(msg.chat_blocked, Blocked::Request);
|
||||
assert_eq!(msg.state, MessageState::InFresh);
|
||||
assert_eq!(msg.get_filebytes(&t).await, 2115);
|
||||
assert!(msg.get_file(&t).is_some());
|
||||
|
||||
@@ -267,10 +267,9 @@ impl Peerstate {
|
||||
.query_get_value("SELECT id FROM contacts WHERE addr=?;", paramsv![self.addr])
|
||||
.await?
|
||||
{
|
||||
let chat_id =
|
||||
ChatIdBlocked::get_for_contact(context, contact_id, Blocked::Deaddrop)
|
||||
.await?
|
||||
.id;
|
||||
let chat_id = ChatIdBlocked::get_for_contact(context, contact_id, Blocked::Request)
|
||||
.await?
|
||||
.id;
|
||||
|
||||
let msg = stock_str::contact_setup_changed(context, self.addr.clone()).await;
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot {
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Ok(chat) = ChatIdBlocked::get_for_contact(context, lot.id, Blocked::Deaddrop)
|
||||
if let Ok(chat) = ChatIdBlocked::get_for_contact(context, lot.id, Blocked::Request)
|
||||
.await
|
||||
.log_err(context, "Failed to create (new) chat for contact")
|
||||
{
|
||||
|
||||
@@ -501,7 +501,7 @@ pub(crate) async fn handle_securejoin_handshake(
|
||||
)
|
||||
})?;
|
||||
if chat.blocked != Blocked::Not {
|
||||
chat.id.unblock(context).await;
|
||||
chat.id.unblock(context).await?;
|
||||
}
|
||||
chat.id
|
||||
};
|
||||
@@ -796,7 +796,7 @@ pub(crate) async fn observe_securejoin_on_other_device(
|
||||
)
|
||||
})?;
|
||||
if chat.blocked != Blocked::Not {
|
||||
chat.id.unblock(context).await;
|
||||
chat.id.unblock(context).await?;
|
||||
}
|
||||
chat.id
|
||||
};
|
||||
|
||||
@@ -39,9 +39,6 @@ pub enum StockMessage {
|
||||
#[strum(props(fallback = "Voice message"))]
|
||||
VoiceMessage = 7,
|
||||
|
||||
#[strum(props(fallback = "Contact requests"))]
|
||||
DeadDrop = 8,
|
||||
|
||||
#[strum(props(fallback = "Image"))]
|
||||
Image = 9,
|
||||
|
||||
@@ -360,11 +357,6 @@ pub(crate) async fn voice_message(context: &Context) -> String {
|
||||
translated(context, StockMessage::VoiceMessage).await
|
||||
}
|
||||
|
||||
/// Stock string: `Contact requests`.
|
||||
pub(crate) async fn dead_drop(context: &Context) -> String {
|
||||
translated(context, StockMessage::DeadDrop).await
|
||||
}
|
||||
|
||||
/// Stock string: `Image`.
|
||||
pub(crate) async fn image(context: &Context) -> String {
|
||||
translated(context, StockMessage::Image).await
|
||||
|
||||
Reference in New Issue
Block a user