Skip to content

Commit

Permalink
use a dedicated save_msgs() api; this would allow to force-sync even …
Browse files Browse the repository at this point in the history
…deleted messages
  • Loading branch information
r10s committed Jan 16, 2025
1 parent bf72686 commit 33e1a9c
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 32 deletions.
38 changes: 33 additions & 5 deletions deltachat-ffi/deltachat.h
Original file line number Diff line number Diff line change
Expand Up @@ -1960,10 +1960,10 @@ void dc_delete_msgs (dc_context_t* context, const uint3
/**
* Forward messages to another chat.
*
* Unless forwarded to "Saved Messages",
* UI should mark forwarded messages as such.
* Whether to mark a message as forwarded can be determined by dc_msg_is_forwarded().
* Original sender, webxdc updates and other metadata are not forwarded on purpose.
* All types of messages can be forwarded,
* however, they will be flagged as such (dc_msg_is_forwarded() is set).
*
* Original sender, info-state and webxdc updates are not forwarded on purpose.
*
* @memberof dc_context_t
* @param context The context object.
Expand All @@ -1974,6 +1974,33 @@ 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);


/**
* Save a copy of messages in "Saved Messages".
*
* In contrast to forwarding messages,
* information as author, date and origin are preserved.
* The action completes locally, so "Saved Messages" do not show sending errors in case one is offline.
* Still, a sync message is emitted, so that other devices will save the same message,
* as long as not deleted before.
*
* To check if a message was saved, use dc_msg_get_saved_msg_id(),
* UI may show an indicator and offer an "Unsave" instead of a "Save" button then.
*
* The other way round, from inside the "Saved Messages" chat,
* UI may show in indicator checking dc_msg_get_original_msg_id() and dc_msg_get_original_chat_id()
* and offer a button to go the original chat.
*
* "Unsave" is done by deleting the saved message.
* Webxdc updates are not copied on purpose.
*
* @memberof dc_context_t
* @param context The context object.
* @param msg_ids An array of uint32_t containing all message IDs that should be saved.
* @param msg_cnt The number of messages IDs in the msg_ids array.
*/
void dc_save_msgs (dc_context_t* context, const uint32_t* msg_ids, int msg_cnt);


/**
* Resend messages and make information available for newly added chat members.
* Resending sends out the original message, however, recipients and webxdc-status may differ.
Expand Down Expand Up @@ -4384,9 +4411,10 @@ int dc_msg_is_sent (const dc_msg_t* msg);


/**
* Check if the message should be marked as forwarded message.
* Check if the message is a forwarded message.
*
* Forwarded messages may not be created by the contact given as "from".
*
* Typically, the UI shows a little text for a symbol above forwarded messages.
*
* For privacy reasons, we do not provide the name or the e-mail address of the
Expand Down
20 changes: 20 additions & 0 deletions deltachat-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1979,6 +1979,26 @@ pub unsafe extern "C" fn dc_forward_msgs(
})
}

#[no_mangle]
pub unsafe extern "C" fn dc_save_msgs(
context: *mut dc_context_t,
msg_ids: *const u32,
msg_cnt: libc::c_int,
) {
if context.is_null() || msg_ids.is_null() || msg_cnt <= 0 {
eprintln!("ignoring careless call to dc_save_msgs()");
return;
}
let msg_ids = convert_and_prune_message_ids(msg_ids, msg_cnt);
let ctx = &*context;

block_on(async move {
chat::save_msgs(ctx, &msg_ids[..])
.await
.unwrap_or_log_default(ctx, "Failed to save message")
})
}

#[no_mangle]
pub unsafe extern "C" fn dc_resend_msgs(
context: *mut dc_context_t,
Expand Down
26 changes: 11 additions & 15 deletions src/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4153,11 +4153,6 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
if let Some(reason) = chat.why_cant_send(context).await? {
bail!("cannot send to {}: {}", chat_id, reason);
}

if chat.is_self_talk() {
return save_copies_in_self_talk_and_sync(context, msg_ids).await;
}

curr_timestamp = create_smeared_timestamps(context, msg_ids.len());
let mut msgs = Vec::with_capacity(msg_ids.len());
for id in msg_ids {
Expand Down Expand Up @@ -4214,7 +4209,9 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
Ok(())
}

async fn save_copies_in_self_talk_and_sync(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
/// Save a copy of the message in "Saved Messages"
/// and send a sync messages so that other devices save the message as well, unless deleted there.
pub async fn save_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
for src_msg_id in msg_ids {
let dest_rfc724_mid = create_outgoing_rfc724_mid();
let src_rfc724_mid = save_copy_in_self_talk(context, src_msg_id, &dest_rfc724_mid).await?;
Expand Down Expand Up @@ -6915,7 +6912,7 @@ mod tests {
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_forward_to_saved() -> Result<()> {
async fn test_save_msgs() -> Result<()> {
let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await;
let alice_chat = alice.create_chat(&bob).await;
Expand All @@ -6927,7 +6924,7 @@ mod tests {
assert!(sent_msg.get_original_chat_id(&alice).await?.is_none());

let self_chat = alice.get_self_chat().await;
forward_msgs(&alice, &[sent.sender_msg_id], self_chat.id).await?;
save_msgs(&alice, &[sent.sender_msg_id]).await?;

let saved_msg = alice.get_last_msg_in(self_chat.id).await;
assert_ne!(saved_msg.get_id(), sent.sender_msg_id);
Expand Down Expand Up @@ -6957,7 +6954,7 @@ mod tests {

let rcvd_msg = bob.recv_msg(&sent).await;
let self_chat = bob.get_self_chat().await;
forward_msgs(&bob, &[rcvd_msg.id], self_chat.id).await?;
save_msgs(&bob, &[rcvd_msg.id]).await?;
let saved_msg = bob.get_last_msg_in(self_chat.id).await;
assert_ne!(saved_msg.get_id(), rcvd_msg.id);
assert_eq!(
Expand Down Expand Up @@ -7004,7 +7001,7 @@ mod tests {
let msg = bob.get_last_msg_in(bob_chat.id).await;

let self_chat = bob.get_self_chat().await;
forward_msgs(&bob, &[msg.id], self_chat.id).await?;
save_msgs(&bob, &[msg.id]).await?;
let msg = bob.get_last_msg_in(self_chat.id).await;
let contact = Contact::get_by_id(&bob, msg.get_from_id()).await?;
assert_eq!(contact.get_addr(), "[email protected]");
Expand All @@ -7025,19 +7022,18 @@ mod tests {
bob.recv_msg(&sent).await;
let orig = bob.get_last_msg().await;
let self_chat = bob.get_self_chat().await;
forward_msgs(&bob, &[orig.id], self_chat.id).await?;
save_msgs(&bob, &[orig.id]).await?;
let saved1 = bob.get_last_msg().await;
assert_eq!(
saved1.get_original_msg_id(&bob).await?.unwrap(),
sent.sender_msg_id
);
assert_ne!(saved1.from_id, ContactId::SELF);

forward_msgs(&bob, &[saved1.id], self_chat.id).await?;
let saved2 = bob.get_last_msg().await;
assert_eq!(
saved2.get_original_msg_id(&bob).await?.unwrap(),
sent.sender_msg_id
);
assert!(saved2.get_original_msg_id(&bob).await?.is_none(),);
assert_eq!(saved2.from_id, ContactId::SELF);

Ok(())
}
Expand Down
15 changes: 7 additions & 8 deletions src/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ pub fn new_html_mimepart(html: String) -> PartBuilder {
mod tests {
use super::*;
use crate::chat;
use crate::chat::{forward_msgs, ProtectionStatus};
use crate::chat::{forward_msgs, save_msgs};
use crate::config::Config;
use crate::contact::ContactId;
use crate::message::{MessengerMessage, Viewtype};
Expand Down Expand Up @@ -500,7 +500,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_html_forward_to_saved_messages() -> Result<()> {
async fn test_html_save_msg() -> Result<()> {
// Alice receives a non-delta html-message
let alice = TestContext::new_alice().await;
let chat = alice
Expand All @@ -510,9 +510,9 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
receive_imf(&alice, raw, false).await?;
let msg = alice.get_last_msg_in(chat.get_id()).await;

// Alice forwards the message to "Saved Messages"
// Alice saves the message
let self_chat = alice.get_self_chat().await;
forward_msgs(&alice, &[msg.id], self_chat.id).await?;
save_msgs(&alice, &[msg.id]).await?;
let saved_msg = alice.get_last_msg_in(self_chat.get_id()).await;
assert_ne!(saved_msg.id, msg.id);
assert_eq!(
Expand Down Expand Up @@ -550,10 +550,8 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x

// forward the message to saved-messages,
// this will encrypt the message as new_alice() has set up keys
let chat_id = alice
.create_group_with_members(ProtectionStatus::Protected, "foo", &[])
.await;
forward_msgs(&alice, &[msg.get_id()], chat_id)
let chat = alice.get_self_chat().await;
forward_msgs(&alice, &[msg.get_id()], chat.get_id())
.await
.unwrap();
let msg = alice.pop_sent_msg().await;
Expand All @@ -565,6 +563,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
.await
.unwrap();
let msg = alice.recv_msg(&msg).await;
assert_eq!(msg.chat_id, alice.get_self_chat().await.id);
assert_eq!(msg.get_from_id(), ContactId::SELF);
assert_eq!(msg.is_dc_message, MessengerMessage::Yes);
assert!(msg.get_showpadlock());
Expand Down
8 changes: 4 additions & 4 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2212,8 +2212,8 @@ mod tests {

use super::*;
use crate::chat::{
self, add_contact_to_chat, forward_msgs, marknoticed_chat, send_text_msg, ChatItem,
ProtectionStatus,
self, add_contact_to_chat, forward_msgs, marknoticed_chat, save_msgs, send_text_msg,
ChatItem, ProtectionStatus,
};
use crate::chatlist::Chatlist;
use crate::config::Config;
Expand Down Expand Up @@ -2537,7 +2537,7 @@ mod tests {

// forwarding to "Saved Messages", the message gets the original ID attached
let self_chat = alice.get_self_chat().await;
forward_msgs(&alice, &[sent.sender_msg_id], self_chat.get_id()).await?;
save_msgs(&alice, &[sent.sender_msg_id]).await?;
let saved_msg = alice.get_last_msg_in(self_chat.get_id()).await;
assert_ne!(saved_msg.get_id(), orig_msg.get_id());
assert_eq!(
Expand All @@ -2547,7 +2547,7 @@ mod tests {
assert!(saved_msg.parent(&alice).await?.is_none());
assert!(saved_msg.quoted_message(&alice).await?.is_none());

// forwarding from "Saved Messages" back to another chat, the original ID attached
// forwarding from "Saved Messages" back to another chat, detaches original ID
forward_msgs(&alice, &[saved_msg.get_id()], one2one_chat.get_id()).await?;
let forwarded_msg = alice.get_last_msg_in(one2one_chat.get_id()).await;
assert_ne!(forwarded_msg.get_id(), saved_msg.get_id());
Expand Down

0 comments on commit 33e1a9c

Please sign in to comment.