From 065b574d930789d8c63721226d57091499f1c51b Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 26 Jun 2021 11:40:54 +0300 Subject: [PATCH] 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 --- deltachat-ffi/deltachat.h | 202 +++++------------- deltachat-ffi/src/lib.rs | 109 ++++------ examples/repl/cmdline.rs | 54 ++--- examples/repl/main.rs | 7 +- python/src/deltachat/account.py | 6 - python/src/deltachat/chat.py | 24 ++- python/src/deltachat/hookspec.py | 2 +- python/src/deltachat/message.py | 9 +- python/tests/test_account.py | 15 +- src/chat.rs | 337 ++++++++++++------------------- src/chatlist.rs | 99 +++------ src/constants.rs | 6 +- src/contact.rs | 21 +- src/context.rs | 10 +- src/dc_receive_imf.rs | 179 +++++++--------- src/job.rs | 37 +--- src/message.rs | 142 +++---------- src/mimefactory.rs | 5 +- src/mimeparser.rs | 2 +- src/peerstate.rs | 7 +- src/qr.rs | 2 +- src/securejoin.rs | 4 +- src/stock_str.rs | 8 - 23 files changed, 428 insertions(+), 859 deletions(-) diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 3687ea9be..8bc5724a6 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -305,7 +305,7 @@ char* dc_get_blobdir (const dc_context_t* context); * DC_SHOW_EMAILS_ACCEPTED_CONTACTS (1)= * also show all mails of confirmed contacts, * DC_SHOW_EMAILS_ALL (2)= - * also show mails of unconfirmed contacts in the deaddrop. + * also show mails of unconfirmed contacts. * - `key_gen_type` = DC_KEY_GEN_DEFAULT (0)= * generate recommended key type (default), * DC_KEY_GEN_RSA2048 (1)= @@ -682,12 +682,6 @@ int dc_preconfigure_keypair (dc_context_t* context, const cha * * By default, the function adds some special entries to the list. * These special entries can be identified by the ID returned by dc_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 dc_chatlist_get_msg_id(). Typically, the UI asks the user "Do you want to chat with NAME?" - * and offers the options "Start chat", "Block" or "Not now". - * Call dc_decide_on_contact_request() when the user selected one of these options. * - 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 @@ -705,10 +699,10 @@ int dc_preconfigure_keypair (dc_context_t* context, const cha * 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 the deaddrop. + * and hides the "Device chat" and contact requests. * typically used on forwarding, may be combined with DC_GCL_NO_SPECIALS * to also hide the archive link. - * - 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 e.g. 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 @@ -728,42 +722,12 @@ dc_chatlist_t* dc_get_chatlist (dc_context_t* context, int flags, // handle chats -/** - * Create a normal chat or a group chat by a messages ID that comes typically - * from the deaddrop, DC_CHAT_ID_DEADDROP (1). - * - * If the given message ID already belongs to a normal chat or to a group chat, - * the chat ID of this chat is returned and no new chat is created. - * If a new chat is created, the given message ID is moved to this chat, however, - * there may be more messages moved to the chat from the deaddrop. To get the - * chat messages, use dc_get_chat_msgs(). - * - * If the user is asked before creation, he should be - * asked whether he wants to chat with the _contact_ belonging to the message; - * the group names may be really weird when taken from the subject of implicit - * groups and this may look confusing. - * - * Moreover, this function also scales up the origin of the contact belonging - * to the message and, depending on the contacts origin, messages from the - * same group may be shown or not - so, all in all, it is fine to show the - * contact name only. - * - * @deprecated Deprecated 2021-02-07, use dc_decide_on_contact_request() instead - * @memberof dc_context_t - * @param context The context object as returned from dc_context_new(). - * @param msg_id The message ID to create the chat for. - * @return The created or reused chat ID on success. 0 on errors. - */ -uint32_t dc_create_chat_by_msg_id (dc_context_t* context, uint32_t msg_id); - - /** * Create a normal chat with a single user. To create group chats, * see dc_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, e.g. from the deaddrop, to get the - * chat messages, use dc_get_chat_msgs(). + * to get the chat messages, use dc_get_chat_msgs(). * * @memberof dc_context_t * @param context The context object as returned from dc_context_new(). @@ -1135,7 +1099,7 @@ int dc_estimate_deletion_cnt (dc_context_t* context, int from_ser * or badge counters eg. on the app-icon. * The list is already sorted and starts with the most recent fresh message. * - * Messages belonging to muted chats or to the deaddrop are not returned; + * Messages belonging to muted chats or to the contact requests are not returned; * these messages should not be notified * and also badge counters should not include these messages. * @@ -1161,8 +1125,7 @@ dc_array_t* dc_get_fresh_msgs (dc_context_t* context); * * @memberof dc_context_t * @param context The context object as returned from dc_context_new(). - * @param chat_id The chat ID of which all messages should be marked as being noticed - * (this also works for the virtual chat ID DC_CHAT_ID_DEADDROP). + * @param chat_id The chat ID of which all messages should be marked as being noticed. */ void dc_marknoticed_chat (dc_context_t* context, uint32_t chat_id); @@ -1271,6 +1234,31 @@ void dc_set_chat_visibility (dc_context_t* context, uint32_t ch */ void dc_delete_chat (dc_context_t* context, uint32_t chat_id); +/** + * Block a chat. + * + * Blocking 1:1 chats blocks the corresponding contact. Blocking + * mailing lists creates a pseudo-contact in the list of blocked + * contacts, so blocked mailing lists can be discovered and unblocked + * the same way as the contacts. Blocking group chats deletes the + * chat without blocking any contacts, so it may pop up again later. + * + * @memberof dc_context_t + * @param context The context object as returned from dc_context_new(). + * @param chat_id The ID of the chat to block. + */ +void dc_block_chat (dc_context_t* context, uint32_t chat_id); + +/** + * Accept a contact request chat. + * + * Use it to accept "contact request" chats as indicated by dc_chat_is_contact_request(). + * + * @memberof dc_context_t + * @param context The context object as returned from dc_context_new(). + * @param chat_id The ID of the chat to accept. + */ +void dc_accept_chat (dc_context_t* context, uint32_t chat_id); /** * Get contact IDs belonging to a chat. @@ -1282,8 +1270,6 @@ void dc_delete_chat (dc_context_t* context, uint32_t ch * explicitly as it may happen that oneself gets removed from a still existing * group * - * - for the deaddrop, the list is empty - * * - for mailing lists, the behavior is not documented currently, we will decide on that later. * for now, the UI should not show the list for mailing lists. * (we do not know all members and there is not always a global mailing list address, @@ -1631,25 +1617,6 @@ void dc_delete_msgs (dc_context_t* context, const uint3 void dc_forward_msgs (dc_context_t* context, const uint32_t* msg_ids, int msg_cnt, uint32_t chat_id); -/** - * Mark all messages sent by the given contact as _noticed_. - * This function is typically used to ignore a user in the deaddrop temporarily ("Not now" button). - * - * The contact is expected to belong to the deaddrop; - * only one #DC_EVENT_MSGS_NOTICED with chat_id=DC_CHAT_ID_DEADDROP may be emitted. - * - * See also dc_marknoticed_chat() and dc_markseen_msgs() - * - * @deprecated Deprecated 2021-02-07, use dc_decide_on_contact_request() if the user just hit "Not now" on a button in the deaddrop, - * dc_marknoticed_chat() if the user has entered a chat - * and dc_markseen_msgs() if the user actually _saw_ a message. - * @memberof dc_context_t - * @param context The context object. - * @param contact_id The contact ID of which all messages should be marked as noticed. - */ -void dc_marknoticed_contact (dc_context_t* context, uint32_t contact_id); - - /** * Mark messages as presented to the user. * Typically, UIs call this function on scrolling through the chatlist, @@ -1661,12 +1628,12 @@ void dc_marknoticed_contact (dc_context_t* context, uint32_t co * (if dc_set_config()-options `mdns_enabled` is set) * and the internal state is changed to DC_STATE_IN_SEEN to reflect these actions. * - * - For the deaddrop, no IMAP or MNDs is done - * and the internal change is not changed therefore. + * - For contact requests, no IMAP or MDNs is done + * and the internal state is not changed therefore. * See also dc_marknoticed_chat(). * * Moreover, timer is started for incoming ephemeral messages. - * This also happens for messages in the deaddrop. + * This also happens for contact requests chats. * * One #DC_EVENT_MSGS_NOTICED event is emitted per modified chat. * @@ -1693,53 +1660,6 @@ void dc_markseen_msgs (dc_context_t* context, const uint3 dc_msg_t* dc_get_msg (dc_context_t* context, uint32_t msg_id); -#define DC_DECISION_START_CHAT 0 -#define DC_DECISION_BLOCK 1 -#define DC_DECISION_NOT_NOW 2 - - -/** - * Call this when the user decided about a deaddrop message ("Do you want to chat with NAME?"). - * - * Possible decisions are: - * - DC_DECISION_START_CHAT (0) - * - This will create a new chat and return the chat id. - * - DC_DECISION_BLOCK (1) - * - This will block the sender. - * - When a new message from the sender arrives, - * that will not result in a new contact request. - * - The blocked sender will be returned by dc_get_blocked_contacts() - * typically, the UI offers an option to unblock senders from there. - * - DC_DECISION_NOT_NOW (2) - * - This will mark all messages from this sender as noticed. - * - That the contact request is removed from the chat list. - * - When a new message from the sender arrives, - * a new contact request with the new message will pop up in the chatlist. - * - The contact request stays available in the explicit deaddrop. - * - If the contact request is already noticed, nothing happens. - * - * If the message belongs to a mailing list, - * the function makes sure that all messages - * from the 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?" - * (use dc_msg_get_real_chat_id() to get the chat-id for the contact request - * and then dc_chat_is_mailing_list(), dc_chat_get_name() and so on) - * - * @memberof dc_context_t - * @param context The context object. - * @param msg_id ID of Message to decide on. - * @param decision One of the DC_DECISION_* values. - * @return The chat id of the created chat, if any. - */ -uint32_t dc_decide_on_contact_request (dc_context_t* context, uint32_t msg_id, int decision); - - // handle contacts /** @@ -2884,15 +2804,9 @@ int dc_array_search_id (const dc_array_t* array, uint32_t * 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. + * Without listflags, dc_get_chatlist() adds + * 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. */ @@ -3033,7 +2947,6 @@ char* dc_chat_get_info_json (dc_context_t* context, size_t chat */ -#define DC_CHAT_ID_DEADDROP 1 // virtual chat showing all messages belonging to chats flagged with chats.blocked=2 #define DC_CHAT_ID_TRASH 3 // 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) #define DC_CHAT_ID_ARCHIVED_LINK 6 // only an indicator in a chatlist #define DC_CHAT_ID_ALLDONE_HINT 7 // only an indicator in a chatlist @@ -3060,7 +2973,6 @@ void dc_chat_unref (dc_chat_t* chat); * Get chat ID. The chat ID is the ID under which the chat is filed in the database. * * Special IDs: - * - DC_CHAT_ID_DEADDROP (1) - Virtual chat containing messages which senders are not confirmed by the user. * - DC_CHAT_ID_ARCHIVED_LINK (6) - A link at the end of the chatlist, if present the UI should show the button "Archived chats"- * * "Normal" chat IDs are larger than these special IDs (larger than DC_CHAT_ID_LAST_SPECIAL). @@ -3152,6 +3064,25 @@ uint32_t dc_chat_get_color (const dc_chat_t* chat); int dc_chat_get_visibility (const dc_chat_t* chat); +/** + * Check if a chat is a contact request chat. + * + * UI should display such chats with a [New] badge in the chatlist. + * + * When such chat is opened, user should be presented with a set of + * options instead of the message composition area, for example: + * - Accept chat (dc_accept_chat()) + * - Block chat (dc_block_chat()) + * - Delete chat (dc_delete_chat()) + * + * @memberof dc_chat_t + * @param chat The chat object. + * @return 1=chat is a contact request chat + * 0=chat is not a contact request chat + */ +int dc_chat_is_contact_request (const dc_chat_t* chat); + + /** * Check if a group chat is still unpromoted. * @@ -3205,7 +3136,7 @@ int dc_chat_is_device_talk (const dc_chat_t* chat); /** * Check if messages can be sent to a given chat. - * This is not true e.g. for the deaddrop or for the device-talk, cmp. dc_chat_is_device_talk(). + * This is not true e.g. for contact requests or for the device-talk, cmp. dc_chat_is_device_talk(). * * Calling dc_send_msg() for these chats will fail * and the UI may decide to hide input controls therefore. @@ -3349,9 +3280,6 @@ uint32_t dc_msg_get_from_id (const dc_msg_t* msg); /** * Get the ID of chat the message belongs to. * To get details about the chat, pass the returned ID to dc_get_chat(). - * If a message is still in the deaddrop, the ID DC_CHAT_ID_DEADDROP is returned - * although internally another ID is used. - * (to get that internal id, use dc_msg_get_real_chat_id()) * * @memberof dc_msg_t * @param msg The message object. @@ -3360,19 +3288,6 @@ uint32_t dc_msg_get_from_id (const dc_msg_t* msg); uint32_t dc_msg_get_chat_id (const dc_msg_t* msg); -/** - * Get the ID of chat the message belongs to. - * To get details about the chat, pass the returned ID to dc_get_chat(). - * In contrast to dc_msg_get_chat_id(), this function returns the chat-id also - * for messages in the deaddrop. - * - * @memberof dc_msg_t - * @param msg The message object. - * @return The ID of the chat the message belongs to, 0 on errors. - */ -uint32_t dc_msg_get_real_chat_id (const dc_msg_t* msg); - - /** * Get the type of the message. * @@ -5405,11 +5320,6 @@ void dc_event_unref(dc_event_t* event); /// Used in summaries. #define DC_STR_VOICEMESSAGE 7 -/// "Contact requests" -/// -/// Used as the name for the corresponding chat. -#define DC_STR_DEADDROP 8 - /// "Image" /// /// Used in summaries. diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 8d8b14a05..572060343 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -642,23 +642,6 @@ pub unsafe extern "C" fn dc_get_chatlist( }) } -#[no_mangle] -pub unsafe extern "C" fn dc_create_chat_by_msg_id(context: *mut dc_context_t, msg_id: u32) -> u32 { - if context.is_null() { - eprintln!("ignoring careless call to dc_create_chat_by_msg_id()"); - return 0; - } - let ctx = &*context; - - block_on(async move { - chat::create_by_msg_id(&ctx, MsgId::new(msg_id)) - .await - .log_err(ctx, "Failed to create chat from msg_id") - .map(|id| id.to_u32()) - .unwrap_or(0) - }) -} - #[no_mangle] pub unsafe extern "C" fn dc_create_chat_by_contact_id( context: *mut dc_context_t, @@ -1175,8 +1158,39 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32 ChatId::new(chat_id) .delete(&ctx) .await - .log_err(ctx, "Failed chat delete") - .unwrap_or(()) + .ok_or_log_msg(ctx, "Failed chat delete"); + }) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_block_chat(context: *mut dc_context_t, chat_id: u32) { + if context.is_null() { + eprintln!("ignoring careless call to dc_block_chat()"); + return; + } + let ctx = &*context; + + block_on(async move { + ChatId::new(chat_id) + .block(&ctx) + .await + .ok_or_log_msg(ctx, "Failed chat block"); + }) +} + +#[no_mangle] +pub unsafe extern "C" fn dc_accept_chat(context: *mut dc_context_t, chat_id: u32) { + if context.is_null() { + eprintln!("ignoring careless call to dc_accept_chat()"); + return; + } + let ctx = &*context; + + block_on(async move { + ChatId::new(chat_id) + .accept(&ctx) + .await + .ok_or_log_msg(ctx, "Failed chat accept"); }) } @@ -1569,17 +1583,6 @@ pub unsafe extern "C" fn dc_forward_msgs( }) } -#[no_mangle] -pub unsafe extern "C" fn dc_marknoticed_contact(context: *mut dc_context_t, contact_id: u32) { - if context.is_null() { - eprintln!("ignoring careless call to dc_marknoticed_contact()"); - return; - } - let ctx = &*context; - - block_on(Contact::mark_noticed(&ctx, contact_id)) -} - #[no_mangle] pub unsafe extern "C" fn dc_markseen_msgs( context: *mut dc_context_t, @@ -2530,6 +2533,16 @@ pub unsafe extern "C" fn dc_chat_get_visibility(chat: *mut dc_chat_t) -> libc::c } } +#[no_mangle] +pub unsafe extern "C" fn dc_chat_is_contact_request(chat: *mut dc_chat_t) -> libc::c_int { + if chat.is_null() { + eprintln!("ignoring careless call to dc_chat_is_contact_request()"); + return 0; + } + let ffi_chat = &*chat; + ffi_chat.chat.is_contact_request() as libc::c_int +} + #[no_mangle] pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { @@ -2731,16 +2744,6 @@ pub unsafe extern "C" fn dc_msg_get_chat_id(msg: *mut dc_msg_t) -> u32 { ffi_msg.message.get_chat_id().to_u32() } -#[no_mangle] -pub unsafe extern "C" fn dc_msg_get_real_chat_id(msg: *mut dc_msg_t) -> u32 { - if msg.is_null() { - eprintln!("ignoring careless call to dc_msg_get_real_chat_id()"); - return 0; - } - let ffi_msg = &*msg; - ffi_msg.message.get_real_chat_id().to_u32() -} - #[no_mangle] pub unsafe extern "C" fn dc_msg_get_viewtype(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { @@ -3096,32 +3099,6 @@ pub unsafe extern "C" fn dc_msg_get_videochat_url(msg: *mut dc_msg_t) -> *mut li .strdup() } -#[no_mangle] -pub unsafe extern "C" fn dc_decide_on_contact_request( - context: *mut dc_context_t, - msg_id: u32, - decision: libc::c_int, -) -> u32 { - if context.is_null() || msg_id <= constants::DC_MSG_ID_LAST_SPECIAL as u32 { - eprintln!("ignoring careless call to dc_decide_on_contact_request()"); - } - let ctx = &*context; - - match from_prim(decision) { - None => { - warn!(ctx, "{} is not a valid decision, ignoring", decision); - 0 - } - Some(d) => block_on(message::decide_on_contact_request( - ctx, - MsgId::new(msg_id), - d, - )) - .unwrap_or_default() - .to_u32(), - } -} - #[no_mangle] pub unsafe extern "C" fn dc_msg_get_videochat_type(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 3beb15a9c..3b0e797d2 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -17,7 +17,7 @@ use deltachat::imex::*; use deltachat::location; use deltachat::log::LogExt; use deltachat::lot::LotState; -use deltachat::message::{self, ContactRequestDecision, Message, MessageState, MsgId}; +use deltachat::message::{self, Message, MessageState, MsgId}; use deltachat::peerstate::*; use deltachat::qr::*; use deltachat::sql; @@ -389,10 +389,8 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu protect \n\ unprotect \n\ delchat \n\ - ===========================Contact requests==\n\ - decidestartchat \n\ - decideblock \n\ - decidenotnow \n\ + accept \n\ + decline \n\ ===========================Message commands==\n\ listmsgs \n\ msginfo \n\ @@ -547,7 +545,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu for i in (0..cnt).rev() { let chat = Chat::load_from_db(&context, chatlist.get_chat_id(i)).await?; println!( - "{}#{}: {} [{} fresh] {}{}{}", + "{}#{}: {} [{} fresh] {}{}{}{}", chat_prefix(&chat), chat.get_id(), chat.get_name(), @@ -559,6 +557,11 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu ChatVisibility::Pinned => "📌", }, if chat.is_protected() { "🛡️" } else { "" }, + if chat.is_contact_request() { + "🆕" + } else { + "" + }, ); let lot = chatlist.get_summary(&context, i, Some(&chat)).await?; let statestr = if chat.visibility == ChatVisibility::Archived { @@ -689,35 +692,6 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu println!("Single#{} created successfully.", chat_id,); } - "decidestartchat" | "createchatbymsg" => { - ensure!(!arg1.is_empty(), "Argument missing"); - let msg_id = MsgId::new(arg1.parse()?); - match message::decide_on_contact_request( - &context, - msg_id, - ContactRequestDecision::StartChat, - ) - .await - { - Some(chat_id) => { - let chat = Chat::load_from_db(&context, chat_id).await?; - println!("{}#{} created successfully.", chat_prefix(&chat), chat_id); - } - None => println!("Cannot crate chat."), - } - } - "decidenotnow" => { - ensure!(!arg1.is_empty(), "Argument missing"); - let msg_id = MsgId::new(arg1.parse()?); - message::decide_on_contact_request(&context, msg_id, ContactRequestDecision::NotNow) - .await; - } - "decideblock" => { - ensure!(!arg1.is_empty(), "Argument missing"); - let msg_id = MsgId::new(arg1.parse()?); - message::decide_on_contact_request(&context, msg_id, ContactRequestDecision::Block) - .await; - } "creategroup" => { ensure!(!arg1.is_empty(), "Argument missing."); let chat_id = @@ -1034,6 +1008,16 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu let chat_id = ChatId::new(arg1.parse()?); chat_id.delete(&context).await?; } + "accept" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let chat_id = ChatId::new(arg1.parse()?); + chat_id.accept(&context).await?; + } + "blockchat" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let chat_id = ChatId::new(arg1.parse()?); + chat_id.block(&context).await?; + } "msginfo" => { ensure!(!arg1.is_empty(), "Argument missing."); let id = MsgId::new(arg1.parse()?); diff --git a/examples/repl/main.rs b/examples/repl/main.rs index 59bda2e84..4838d2ef5 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -167,14 +167,11 @@ const DB_COMMANDS: [&str; 10] = [ "housekeeping", ]; -const CHAT_COMMANDS: [&str; 34] = [ +const CHAT_COMMANDS: [&str; 33] = [ "listchats", "listarchived", "chat", "createchat", - "decidestartchat", - "decideblock", - "decidenotnow", "creategroup", "createverified", "addmember", @@ -202,6 +199,8 @@ const CHAT_COMMANDS: [&str; 34] = [ "protect", "unprotect", "delchat", + "accept", + "blockchat", ]; const MESSAGE_COMMANDS: [&str; 6] = [ "listmsgs", diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 8e718fb7b..cbda1412b 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -330,9 +330,6 @@ class Account(object): """ Create a 1:1 chat with Account, Contact or e-mail address. """ return self.create_contact(obj).create_chat() - def _create_chat_by_message_id(self, msg_id): - return Chat(self, lib.dc_create_chat_by_msg_id(self._dc_context, msg_id)) - def create_group_chat(self, name, contacts=None, verified=False): """ create a new group chat object. @@ -367,9 +364,6 @@ class Account(object): chatlist.append(Chat(self, chat_id)) return chatlist - def get_deaddrop_chat(self): - return Chat(self, const.DC_CHAT_ID_DEADDROP) - def get_device_chat(self): return Contact(self, const.DC_CONTACT_ID_DEVICE).create_chat() diff --git a/python/src/deltachat/chat.py b/python/src/deltachat/chat.py index d645a48b3..4924fee15 100644 --- a/python/src/deltachat/chat.py +++ b/python/src/deltachat/chat.py @@ -50,6 +50,14 @@ class Chat(object): """ lib.dc_delete_chat(self.account._dc_context, self.id) + def block(self): + """Block this chat.""" + lib.dc_block_chat(self.account._dc_context, self.id) + + def accept(self): + """Accept this contact request chat.""" + lib.dc_accept_chat(self.account._dc_context, self.id) + # ------ chat status/metadata API ------------------------------ def is_group(self): @@ -59,13 +67,6 @@ class Chat(object): """ return lib.dc_chat_get_type(self._dc_chat) == const.DC_CHAT_TYPE_GROUP - def is_deaddrop(self): - """ return true if this chat is a deaddrop chat. - - :returns: True if chat is the deaddrop chat, False otherwise. - """ - return self.id == const.DC_CHAT_ID_DEADDROP - def is_muted(self): """ return true if this chat is muted. @@ -73,6 +74,13 @@ class Chat(object): """ return lib.dc_chat_is_muted(self._dc_chat) + def is_contact_request(self): + """ return True if this chat is a contact request chat. + + :returns: True if chat is a contact request chat, False otherwise. + """ + return lib.dc_chat_is_contact_request(self._dc_chat) + def is_promoted(self): """ return True if this chat is promoted, i.e. the member contacts are aware of their membership, @@ -84,7 +92,7 @@ class Chat(object): def can_send(self): """Check if messages can be sent to a give chat. - This is not true eg. for the deaddrop or for the device-talk + This is not true eg. for the contact requests or for the device-talk :returns: True if the chat is writable, False otherwise """ diff --git a/python/src/deltachat/hookspec.py b/python/src/deltachat/hookspec.py index fa58d5f15..27ce6b085 100644 --- a/python/src/deltachat/hookspec.py +++ b/python/src/deltachat/hookspec.py @@ -43,7 +43,7 @@ class PerAccount: @account_hookspec def ac_incoming_message(self, message): - """ Called on any incoming message (to deaddrop or chat). """ + """ Called on any incoming message (both existing chats and contact requests). """ @account_hookspec def ac_outgoing_message(self, message): diff --git a/python/src/deltachat/message.py b/python/src/deltachat/message.py index cb4646e29..ff82677c1 100644 --- a/python/src/deltachat/message.py +++ b/python/src/deltachat/message.py @@ -61,16 +61,13 @@ class Message(object): def create_chat(self): """ create or get an existing chat (group) object for this message. - If the message is a deaddrop contact request + If the message is a contact request the sender will become an accepted contact. :returns: a :class:`deltachat.chat.Chat` object. """ - from .chat import Chat - chat_id = lib.dc_create_chat_by_msg_id(self.account._dc_context, self.id) - ctx = self.account._dc_context - self._dc_msg = ffi.gc(lib.dc_get_msg(ctx, self.id), lib.dc_msg_unref) - return Chat(self.account, chat_id) + self.chat.accept() + return self.chat @props.with_doc def id(self): diff --git a/python/tests/test_account.py b/python/tests/test_account.py index 24dd7ce55..77101b37e 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -909,12 +909,11 @@ class TestOnlineAccount: msg_in = ac2.get_message_by_id(msg_out.id) assert msg_in.text == "message2" - lp.sec("ac2: check that the message arrive in deaddrop") + lp.sec("ac2: check that the message arrived in a chat") chat2 = msg_in.chat assert msg_in in chat2.get_messages() assert not msg_in.is_forwarded() - assert chat2.is_deaddrop() - assert chat2 == ac2.get_deaddrop_chat() + assert chat2.is_contact_request() lp.sec("ac2: create new chat and forward message to it") chat3 = ac2.create_group_chat("newgroup") @@ -979,16 +978,16 @@ class TestOnlineAccount: assert not msg2.is_forwarded() assert msg2.get_sender_contact().display_name == ac1.get_config("displayname") - lp.sec("check the message arrived in contact-requests/deaddrop") + lp.sec("check the message arrived in contact request chat") chat2 = msg2.chat assert msg2 in chat2.get_messages() - assert chat2.is_deaddrop() + assert chat2.is_contact_request() assert chat2.count_fresh_messages() == 1 assert msg2.time_received >= msg1.time_sent lp.sec("create new chat with contact and verify it's proper") chat2b = msg2.create_chat() - assert not chat2b.is_deaddrop() + assert not chat2b.is_contact_request() assert chat2b.count_fresh_messages() == 1 lp.sec("mark chat as noticed") @@ -1853,7 +1852,7 @@ class TestOnlineAccount: lp.sec("ac2: wait for receiving message and avatar from ac1") msg2 = ac2._evtracker.wait_next_messages_changed() - assert msg2.chat.is_deaddrop() + assert msg2.chat.is_contact_request() received_path = msg2.get_sender_contact().get_profile_image() assert open(received_path, "rb").read() == open(p, "rb").read() @@ -1928,6 +1927,8 @@ class TestOnlineAccount: ev = in_list.get(timeout=10) assert ev.action == "chat-modified" ev = in_list.get(timeout=10) + assert ev.action == "chat-modified" + ev = in_list.get(timeout=10) assert ev.action == "added" assert ev.message.get_sender_contact().addr == ac1_addr assert ev.contact.addr == "devnull@testrun.org" diff --git a/src/chat.rs b/src/chat.rs index e1ce28de3..7583eca15 100644 --- a/src/chat.rs +++ b/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 { 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 { 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 { - 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 { - 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 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(()) diff --git a/src/chatlist.rs b/src/chatlist.rs index 8603214fb..0fc6f1ffd 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -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 { Ok(count) } -async fn get_last_deaddrop_fresh_msg(context: &Context) -> Result> { - // 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 \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?; diff --git a/src/constants.rs b/src/constants.rs index c4e194766..6f32d899a 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -25,7 +25,7 @@ pub static DC_VERSION_STR: Lazy = 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] diff --git a/src/contact.rs b/src/contact.rs index a021b80b5..9539d7232 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -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?; } } } diff --git a/src/context.rs b/src/context.rs index ee12b6d04..51bec68df 100644 --- a/src/context.rs +++ b/src/context.rs @@ -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. diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 351fe9c8d..fbcf9524c 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -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"); } diff --git a/src/job.rs b/src/job.rs index 7e53996e5..2b7da407a 100644 --- a/src/job.rs +++ b/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 . - 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(())) } diff --git a/src/message.rs b/src/message.rs index a711ff114..3c7ca9c29 100644 --- a/src/message.rs +++ b/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 { - 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 { let msg = Message::load_from_db(context, msg_id).await?; let rawtxt: Option = 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 diff --git a/src/mimefactory.rs b/src/mimefactory.rs index f51917b84..58f989242 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -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())); diff --git a/src/mimeparser.rs b/src/mimeparser.rs index c697f91bf..595580ecb 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -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()); diff --git a/src/peerstate.rs b/src/peerstate.rs index 5ec14cdf7..ecf0a7bf1 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -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; diff --git a/src/qr.rs b/src/qr.rs index ff0af3c3c..66ac98570 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -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") { diff --git a/src/securejoin.rs b/src/securejoin.rs index 0c7b868f6..20e344a72 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -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 }; diff --git a/src/stock_str.rs b/src/stock_str.rs index 4db2c8a9f..a4e2776a7 100644 --- a/src/stock_str.rs +++ b/src/stock_str.rs @@ -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