diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 47951a2e1..3ed4c68dc 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -3681,6 +3681,31 @@ pub unsafe extern "C" fn dc_msg_get_info_type(msg: *mut dc_msg_t) -> libc::c_int ffi_msg.message.get_info_type() as libc::c_int } +#[no_mangle] +pub unsafe extern "C" fn dc_msg_get_webxdc_deeplink(msg: *mut dc_msg_t) -> *mut libc::c_char { + if msg.is_null() { + eprintln!("ignoring careless call to dc_msg_get_webxdc_deeplink()"); + return "".strdup(); + } + + let ffi_msg = &*msg; + let ctx = &*ffi_msg.context; + let res = block_on(async move { + ffi_msg + .message + .get_webxdc_deeplink(ctx) + .await + .context("failed to get deeplink") + .log_err(ctx) + .unwrap_or(None) + }); + + match res { + Some(str) => str.strdup(), + None => ptr::null_mut(), + } +} + #[no_mangle] pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg_t) -> libc::c_int { if msg.is_null() { diff --git a/src/webxdc.rs b/src/webxdc.rs index ceaf1eb72..670a75ca1 100644 --- a/src/webxdc.rs +++ b/src/webxdc.rs @@ -315,16 +315,15 @@ impl Context { if can_info_msg { if let Some(ref info) = status_update_item.info { - let overwritable_info = - self.get_overwritable_info_msg_id(instance, from_id).await?; + let mut info_msg_id = self.get_overwritable_info_msg_id(instance, from_id).await?; let notify_list = status_update_item.notify; - if notify_list.is_none() && overwritable_info.is_some() { - if let Some(overwritable_info) = overwritable_info { + if notify_list.is_none() && info_msg_id.is_some() { + if let Some(info_msg_id) = info_msg_id { chat::update_msg_text_and_timestamp( self, instance.chat_id, - overwritable_info, + info_msg_id, info.as_str(), timestamp, ) @@ -341,18 +340,28 @@ impl Context { false }; - chat::add_info_msg_with_importance( - self, - instance.chat_id, - info.as_str(), - SystemMessage::WebxdcInfoMessage, - timestamp, - None, - Some(instance), - Some(from_id), - notify, - ) - .await?; + info_msg_id = Some( + chat::add_info_msg_with_importance( + self, + instance.chat_id, + info.as_str(), + SystemMessage::WebxdcInfoMessage, + timestamp, + None, + Some(instance), + Some(from_id), + notify, + ) + .await?, + ); + } + + if let Some(info_msg_id) = info_msg_id { + let mut info_msg = Message::load_from_db(self, info_msg_id).await?; + info_msg + .param + .set_int(Param::Arg, status_update_serial.to_u32() as i32); + info_msg.update_param(self).await?; } } } @@ -925,6 +934,37 @@ impl Message { internet_access, }) } + + /// Get deeplink attached to an info message. + /// + /// The info message need to be of type SystemMessage::WebxdcInfoMessage. + /// Typically, this is used to start the corresponding webxdc directly or indirectly + // and passing the deeplink to `window.webxdc.deeplink` in JS land. + pub async fn get_webxdc_deeplink(&self, context: &Context) -> Result> { + let Some(serial) = self.param.get_int(Param::Arg) else { + return Ok(None); + }; + let serial = StatusUpdateSerial::new(serial as u32); + let Some(instance) = self.parent(&context).await? else { + return Ok(None); + }; + + let update_item_str: String = context + .sql + .query_get_value( + "SELECT update_item FROM msgs_status_updates WHERE msg_id=? AND id=?", + (instance.id, serial), + ) + .await? + .context("cannot read update item")?; + let update_item = StatusUpdateItem { + uid: None, // Erase UIDs, apps, bots and tests don't need to know them. + ..serde_json::from_str(&update_item_str)? + }; + + let json = serde_json::to_string(&update_item)?; + Ok(Some(json)) + } } #[cfg(test)] @@ -2989,4 +3029,44 @@ sth_for_the = "future""# Ok(()) } + + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_webxdc_deeplink() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = tcm.alice().await; + let bob = tcm.bob().await; + + let grp_id = alice + .create_group_with_members(ProtectionStatus::Unprotected, "grp", &[&bob]) + .await; + let instance = send_webxdc_instance(&alice, grp_id).await?; + let sent1 = alice.pop_sent_msg().await; + + alice + .send_webxdc_status_update( + instance.id, + r#"{"payload": "my deeplink data", "info": "my move!"}"#, + "d", + ) + .await?; + alice.flush_status_updates().await?; + let sent2 = alice.pop_sent_msg().await; + let info_msg = alice.get_last_msg().await; + assert!(info_msg.is_info()); + assert_eq!( + info_msg.get_webxdc_deeplink(&alice).await?.unwrap(), + r#"{"payload":"my deeplink data","info":"my move!"}"# + ); + + bob.recv_msg(&sent1).await; + bob.recv_msg_trash(&sent2).await; + let info_msg = bob.get_last_msg().await; + assert!(info_msg.is_info()); + assert_eq!( + info_msg.get_webxdc_deeplink(&bob).await?.unwrap(), + r#"{"payload":"my deeplink data","info":"my move!"}"# + ); + + Ok(()) + } }