Skip to content

Commit

Permalink
feat: update.href api (#6248)
Browse files Browse the repository at this point in the history
add `update.href` property option to update objects send via
`Context::send_webxdc_status_update()`.

when set together with `update.info`,
UI can implement the info message as a link that is passed to the webxdc
via `window.location.href`.
for that purpose, UI will read the link back from
`Message::get_webxdc_href()`.

Practically,
this allows e.g. an calendar.xdc
to emits clickable update messages
opening the calendar at the correct date.

closes #6219

documentation at webxdc/website#90
  • Loading branch information
r10s authored Nov 23, 2024
1 parent 1f0a12a commit 22b9308
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 11 deletions.
18 changes: 18 additions & 0 deletions deltachat-ffi/deltachat.h
Original file line number Diff line number Diff line change
Expand Up @@ -4518,6 +4518,24 @@ int dc_msg_get_info_type (const dc_msg_t* msg);
#define DC_INFO_INVALID_UNENCRYPTED_MAIL 13
#define DC_INFO_WEBXDC_INFO_MESSAGE 32


/**
* Get link attached to an webxdc info message.
* The info message needs to be of type DC_INFO_WEBXDC_INFO_MESSAGE.
*
* Typically, this is used to set `document.location.href` in JS land.
*
* Webxdc apps can define the link by setting `update.href` when sending and update,
* see dc_send_webxdc_status_update().
*
* @memberof dc_msg_t
* @param msg The info message object.
* Not: the webxdc instance.
* @return The link to be set to `document.location.href` in JS land.
* Returns NULL if there is no link attached to the info message and on errors.
*/
char* dc_msg_get_webxdc_href (const dc_msg_t* msg);

/**
* Check if a message is still in creation. A message is in creation between
* the calls to dc_prepare_msg() and dc_send_msg().
Expand Down
11 changes: 11 additions & 0 deletions deltachat-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3687,6 +3687,17 @@ 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_href(msg: *mut dc_msg_t) -> *mut libc::c_char {
if msg.is_null() {
eprintln!("ignoring careless call to dc_msg_get_webxdc_href()");
return "".strdup();
}

let ffi_msg = &*msg;
ffi_msg.message.get_webxdc_href().strdup()
}

#[no_mangle]
pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg_t) -> libc::c_int {
if msg.is_null() {
Expand Down
1 change: 1 addition & 0 deletions src/debug_logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub async fn debug_logging_loop(context: &Context, events: Receiver<DebugEventLo
"time": time,
}),
info: None,
href: None,
summary: None,
document: None,
uid: None,
Expand Down
83 changes: 72 additions & 11 deletions src/webxdc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ pub struct StatusUpdateItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub info: Option<String>,

/// Optional link the info message will point to.
/// Used to set `window.location.href` in JS land.
#[serde(skip_serializing_if = "Option::is_none")]
pub href: Option<String>,

/// The new name of the editing document.
/// This is not needed if the webxdc doesn't edit documents.
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -353,19 +358,22 @@ impl Context {

if can_info_msg {
if let Some(ref info) = status_update_item.info {
if let Some(info_msg_id) = self
let info_msg_id = self
.get_overwritable_info_msg_id(&instance, from_id)
.await?
{
chat::update_msg_text_and_timestamp(
self,
instance.chat_id,
info_msg_id,
info.as_str(),
timestamp,
)
.await?;
notify_msg_id = info_msg_id;

if info_msg_id.is_some() && status_update_item.href.is_none() {
if let Some(info_msg_id) = info_msg_id {
chat::update_msg_text_and_timestamp(
self,
instance.chat_id,
info_msg_id,
info.as_str(),
timestamp,
)
.await?;
notify_msg_id = info_msg_id;
}
} else {
notify_msg_id = chat::add_info_msg_with_cmd(
self,
Expand All @@ -380,6 +388,12 @@ impl Context {
.await?;
}
notify_text = info.to_string();

if let Some(href) = status_update_item.href {
let mut notify_msg = Message::load_from_db(self, notify_msg_id).await?;
notify_msg.param.set(Param::Arg, href);
notify_msg.update_param(self).await?;
}
}
}

Expand Down Expand Up @@ -944,6 +958,15 @@ impl Message {
let hash = Sha256::digest(data.as_bytes());
Ok(format!("{:x}", hash))
}

/// Get link attached to an info message.
///
/// The info message needs to be of type SystemMessage::WebxdcInfoMessage.
/// Typically, this is used to start the corresponding webxdc app
/// with `window.location.href` set in JS land.
pub fn get_webxdc_href(&self) -> Option<String> {
self.param.get(Param::Arg).map(|href| href.to_string())
}
}

#[cfg(test)]
Expand Down Expand Up @@ -1457,6 +1480,7 @@ mod tests {
StatusUpdateItem {
payload: json!({"foo": "bar"}),
info: None,
href: None,
document: None,
summary: None,
uid: Some("iecie2Ze".to_string()),
Expand All @@ -1482,6 +1506,7 @@ mod tests {
StatusUpdateItem {
payload: json!({"nothing": "this should be ignored"}),
info: None,
href: None,
document: None,
summary: None,
uid: Some("iecie2Ze".to_string()),
Expand Down Expand Up @@ -1516,6 +1541,7 @@ mod tests {
StatusUpdateItem {
payload: json!({"foo2": "bar2"}),
info: None,
href: None,
document: None,
summary: None,
uid: None,
Expand All @@ -1536,6 +1562,7 @@ mod tests {
StatusUpdateItem {
payload: Value::Bool(true),
info: None,
href: None,
document: None,
summary: None,
uid: None,
Expand Down Expand Up @@ -3110,4 +3137,38 @@ sth_for_the = "future""#

Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_href() -> 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!", "href": "#foobar"}"##,
"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_href(), Some("#foobar".to_string()));

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_href(), Some("#foobar".to_string()));

Ok(())
}
}
1 change: 1 addition & 0 deletions src/webxdc/maps_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ pub(crate) async fn intercept_get_updates(
item: StatusUpdateItem {
payload: serde_json::to_value(location_item)?,
info: None,
href: None,
document: None,
summary: None,
uid: None,
Expand Down

0 comments on commit 22b9308

Please sign in to comment.