diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 723bb6a43..8fdefbea5 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -1135,6 +1135,44 @@ uint32_t dc_add_device_msg (dc_context_t* context, dc_msg_t* m uint32_t dc_add_device_msg_once (dc_context_t* context, const char* label, dc_msg_t* msg); +/** + * Skip a device-message permanently. + * Subsequent calls to dc_add_device_msg_once() with the same label + * won't add the device-message then. + * This might be handy if you want to add + * eg. different messages for first-install and updates. + * + * @memberof dc_context_t + * @param context The context as created by dc_context_new(). + * @param label A unique name for the message to skip. + * The label is typically not displayed to the user and + * must be created from the characters `A-Z`, `a-z`, `0-9`, `_` or `-`. + * If a message with that label already exist, + * nothing happens. + * @return None. + * + * Example: + * ~~~ + * dc_msg_t* welcome_msg = dc_msg_new(DC_MSG_TEXT); + * dc_msg_set_text(welcome_msg, "great that you give this app a try!"); + * + * dc_msg_t* changelog_msg = dc_msg_new(DC_MSG_TEXT); + * dc_msg_set_text(changelog_msg, "we have added 3 new emojis :)"); + * + * if (dc_add_device_msg_once(context, "welcome", welcome_msg)) { + * // do not add the changelog on a new installations - + * // not now and not when this code is executed again + * dc_skip_device_msg(context, "update-123"); + * } else { + * // welcome message was not added now, this is an oder installation, + * // add a changelog + * dc_add_device_msg_once(context, "update-123", changelog_msg); + * } + * ~~~ + */ +void dc_skip_device_msg (dc_context_t* context, const char* label); + + /** * Get draft for a chat, if any. * See dc_set_draft() for more details about drafts. diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index b7895549d..b0f469f9c 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -849,6 +849,24 @@ pub unsafe extern "C" fn dc_add_device_msg_once( .unwrap_or(0) } +#[no_mangle] +pub unsafe extern "C" fn dc_skip_device_msg( + context: *mut dc_context_t, + label: *const libc::c_char, +) { + if context.is_null() || label.is_null() { + eprintln!("ignoring careless call to dc_skip_device_msg()"); + return; + } + let ffi_context = &mut *context; + ffi_context + .with_inner(|ctx| { + chat::skip_device_msg(ctx, &to_string_lossy(label)) + .unwrap_or_log_default(ctx, "Failed to skip device message") + }) + .unwrap_or(()) +} + #[no_mangle] pub unsafe extern "C" fn dc_get_draft(context: *mut dc_context_t, chat_id: u32) -> *mut dc_msg_t { if context.is_null() { diff --git a/src/chat.rs b/src/chat.rs index e20611199..289c31540 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1974,6 +1974,7 @@ fn add_device_msg_maybe_labelled( // if the device message is labeled and was ever added, do nothing if let Some(label) = label { + ensure!(!label.is_empty(), "cannot add empty label"); if let Ok(()) = context.sql.query_row( "SELECT label FROM devmsglabels WHERE label=?", params![label], @@ -2009,10 +2010,7 @@ fn add_device_msg_maybe_labelled( let msg_id = MsgId::new(row_id); if let Some(label) = label { - context.sql.execute( - "INSERT INTO devmsglabels (label, msg_id) VALUES (?, ?);", - params![label, msg_id], - )?; + skip_device_msg(context, label)?; } if !msg_id.is_unset() { @@ -2022,6 +2020,25 @@ fn add_device_msg_maybe_labelled( Ok(msg_id) } +pub fn skip_device_msg(context: &Context, label: &str) -> Result<(), Error> { + ensure!(!label.is_empty(), "cannot skip empty label"); + if let Ok(()) = context.sql.query_row( + "SELECT label FROM devmsglabels WHERE label=?", + params![label], + |_| Ok(()), + ) { + info!(context, "device-message {} already added", label); + return Ok(()); + } + + context.sql.execute( + "INSERT INTO devmsglabels (label) VALUES (?);", + params![label], + )?; + + Ok(()) +} + pub fn add_info_msg(context: &Context, chat_id: u32, text: impl AsRef) { let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device"); @@ -2214,6 +2231,26 @@ mod tests { assert!(msg2_id.as_ref().unwrap().is_unset()); } + #[test] + fn test_skip_device_msg() { + let t = test_context(Some(Box::new(logging_cb))); + let res = skip_device_msg(&t.ctx, ""); + assert!(res.is_err()); + let res = skip_device_msg(&t.ctx, "some-label"); + assert!(res.is_ok()); + + let mut msg = Message::new(Viewtype::Text); + msg.text = Some("message text".to_string()); + + let msg_id = add_device_msg_once(&t.ctx, "some-label", &mut msg); + assert!(msg_id.is_ok()); + assert!(msg_id.as_ref().unwrap().is_unset()); + + let msg_id = add_device_msg_once(&t.ctx, "unused-label", &mut msg); + assert!(msg_id.is_ok()); + assert!(!msg_id.as_ref().unwrap().is_unset()); + } + fn chatlist_len(ctx: &Context, listflags: usize) -> usize { Chatlist::try_load(ctx, listflags, None, None) .unwrap() diff --git a/src/sql.rs b/src/sql.rs index 78cc2f1e4..8f70eea50 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -815,7 +815,7 @@ fn open( // records in the devmsglabels are kept when the message is deleted. // so, msg_id may or may not exist. sql.execute( - "CREATE TABLE devmsglabels (id INTEGER PRIMARY KEY AUTOINCREMENT, label TEXT, msg_id INTEGER);", + "CREATE TABLE devmsglabels (id INTEGER PRIMARY KEY AUTOINCREMENT, label TEXT, msg_id INTEGER DEFAULT 0);", NO_PARAMS, )?; sql.execute(