Skip to content

Commit 54587a4

Browse files
committed
feat: update.href api
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.
1 parent 29de7c3 commit 54587a4

File tree

5 files changed

+109
-11
lines changed

5 files changed

+109
-11
lines changed

deltachat-ffi/deltachat.h

+18
Original file line numberDiff line numberDiff line change
@@ -4518,6 +4518,24 @@ int dc_msg_get_info_type (const dc_msg_t* msg);
45184518
#define DC_INFO_INVALID_UNENCRYPTED_MAIL 13
45194519
#define DC_INFO_WEBXDC_INFO_MESSAGE 32
45204520

4521+
4522+
/**
4523+
* Get link attached to an webxdc info message.
4524+
* The info message needs to be of type DC_INFO_WEBXDC_INFO_MESSAGE.
4525+
*
4526+
* Typically, this is used to set `document.location.href` in JS land.
4527+
*
4528+
* Webxdc apps can define the link by setting `update.href` when sending and update,
4529+
* see dc_send_webxdc_status_update().
4530+
*
4531+
* @memberof dc_msg_t
4532+
* @param msg The info message object.
4533+
* Not: the webxdc instance.
4534+
* @return The link to be set to `document.location.href` in JS land.
4535+
* Returns NULL if there is no link attached to the info message and on errors.
4536+
*/
4537+
char* dc_msg_get_webxdc_href (const dc_msg_t* msg);
4538+
45214539
/**
45224540
* Check if a message is still in creation. A message is in creation between
45234541
* the calls to dc_prepare_msg() and dc_send_msg().

deltachat-ffi/src/lib.rs

+14
Original file line numberDiff line numberDiff line change
@@ -3687,6 +3687,20 @@ pub unsafe extern "C" fn dc_msg_get_info_type(msg: *mut dc_msg_t) -> libc::c_int
36873687
ffi_msg.message.get_info_type() as libc::c_int
36883688
}
36893689

3690+
#[no_mangle]
3691+
pub unsafe extern "C" fn dc_msg_get_webxdc_href(msg: *mut dc_msg_t) -> *mut libc::c_char {
3692+
if msg.is_null() {
3693+
eprintln!("ignoring careless call to dc_msg_get_webxdc_href()");
3694+
return "".strdup();
3695+
}
3696+
3697+
let ffi_msg = &*msg;
3698+
match ffi_msg.message.get_webxdc_href() {
3699+
Some(str) => str.strdup(),
3700+
None => ptr::null_mut(),
3701+
}
3702+
}
3703+
36903704
#[no_mangle]
36913705
pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg_t) -> libc::c_int {
36923706
if msg.is_null() {

src/debug_logging.rs

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub async fn debug_logging_loop(context: &Context, events: Receiver<DebugEventLo
6060
"time": time,
6161
}),
6262
info: None,
63+
href: None,
6364
summary: None,
6465
document: None,
6566
uid: None,

src/webxdc.rs

+75-11
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ pub struct StatusUpdateItem {
165165
#[serde(skip_serializing_if = "Option::is_none")]
166166
pub info: Option<String>,
167167

168+
/// Optional link the info message will point to.
169+
/// Used to set `window.location.href` in JS land.
170+
#[serde(skip_serializing_if = "Option::is_none")]
171+
pub href: Option<String>,
172+
168173
/// The new name of the editing document.
169174
/// This is not needed if the webxdc doesn't edit documents.
170175
#[serde(skip_serializing_if = "Option::is_none")]
@@ -353,19 +358,22 @@ impl Context {
353358

354359
if can_info_msg {
355360
if let Some(ref info) = status_update_item.info {
356-
if let Some(info_msg_id) = self
361+
let info_msg_id = self
357362
.get_overwritable_info_msg_id(&instance, from_id)
358-
.await?
359-
{
360-
chat::update_msg_text_and_timestamp(
361-
self,
362-
instance.chat_id,
363-
info_msg_id,
364-
info.as_str(),
365-
timestamp,
366-
)
367363
.await?;
368-
notify_msg_id = info_msg_id;
364+
365+
if info_msg_id.is_some() && !status_update_item.href.is_some() {
366+
if let Some(info_msg_id) = info_msg_id {
367+
chat::update_msg_text_and_timestamp(
368+
self,
369+
instance.chat_id,
370+
info_msg_id,
371+
info.as_str(),
372+
timestamp,
373+
)
374+
.await?;
375+
notify_msg_id = info_msg_id;
376+
}
369377
} else {
370378
notify_msg_id = chat::add_info_msg_with_cmd(
371379
self,
@@ -380,6 +388,12 @@ impl Context {
380388
.await?;
381389
}
382390
notify_text = info.to_string();
391+
392+
if let Some(href) = status_update_item.href {
393+
let mut notify_msg = Message::load_from_db(self, notify_msg_id).await?;
394+
notify_msg.param.set(Param::Arg, href);
395+
notify_msg.update_param(self).await?;
396+
}
383397
}
384398
}
385399

@@ -944,6 +958,18 @@ impl Message {
944958
let hash = Sha256::digest(data.as_bytes());
945959
Ok(format!("{:x}", hash))
946960
}
961+
962+
/// Get link attached to an info message.
963+
///
964+
/// The info message needs to be of type SystemMessage::WebxdcInfoMessage.
965+
/// Typically, this is used to start the corresponding
966+
// with `window.location.href` set in JS land.
967+
pub fn get_webxdc_href(&self) -> Option<String> {
968+
let Some(href) = self.param.get(Param::Arg) else {
969+
return None;
970+
};
971+
Some(href.to_string())
972+
}
947973
}
948974

949975
#[cfg(test)]
@@ -1457,6 +1483,7 @@ mod tests {
14571483
StatusUpdateItem {
14581484
payload: json!({"foo": "bar"}),
14591485
info: None,
1486+
href: None,
14601487
document: None,
14611488
summary: None,
14621489
uid: Some("iecie2Ze".to_string()),
@@ -1482,6 +1509,7 @@ mod tests {
14821509
StatusUpdateItem {
14831510
payload: json!({"nothing": "this should be ignored"}),
14841511
info: None,
1512+
href: None,
14851513
document: None,
14861514
summary: None,
14871515
uid: Some("iecie2Ze".to_string()),
@@ -1516,6 +1544,7 @@ mod tests {
15161544
StatusUpdateItem {
15171545
payload: json!({"foo2": "bar2"}),
15181546
info: None,
1547+
href: None,
15191548
document: None,
15201549
summary: None,
15211550
uid: None,
@@ -1536,6 +1565,7 @@ mod tests {
15361565
StatusUpdateItem {
15371566
payload: Value::Bool(true),
15381567
info: None,
1568+
href: None,
15391569
document: None,
15401570
summary: None,
15411571
uid: None,
@@ -3069,4 +3099,38 @@ sth_for_the = "future""#
30693099

30703100
Ok(())
30713101
}
3102+
3103+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3104+
async fn test_webxdc_href() -> Result<()> {
3105+
let mut tcm = TestContextManager::new();
3106+
let alice = tcm.alice().await;
3107+
let bob = tcm.bob().await;
3108+
3109+
let grp_id = alice
3110+
.create_group_with_members(ProtectionStatus::Unprotected, "grp", &[&bob])
3111+
.await;
3112+
let instance = send_webxdc_instance(&alice, grp_id).await?;
3113+
let sent1 = alice.pop_sent_msg().await;
3114+
3115+
alice
3116+
.send_webxdc_status_update(
3117+
instance.id,
3118+
r##"{"payload": "my deeplink data", "info": "my move!", "href": "#foobar"}"##,
3119+
"d",
3120+
)
3121+
.await?;
3122+
alice.flush_status_updates().await?;
3123+
let sent2 = alice.pop_sent_msg().await;
3124+
let info_msg = alice.get_last_msg().await;
3125+
assert!(info_msg.is_info());
3126+
assert_eq!(info_msg.get_webxdc_href(), Some("#foobar".to_string()));
3127+
3128+
bob.recv_msg(&sent1).await;
3129+
bob.recv_msg_trash(&sent2).await;
3130+
let info_msg = bob.get_last_msg().await;
3131+
assert!(info_msg.is_info());
3132+
assert_eq!(info_msg.get_webxdc_href(), Some("#foobar".to_string()));
3133+
3134+
Ok(())
3135+
}
30723136
}

src/webxdc/maps_integration.rs

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ pub(crate) async fn intercept_get_updates(
146146
item: StatusUpdateItem {
147147
payload: serde_json::to_value(location_item)?,
148148
info: None,
149+
href: None,
149150
document: None,
150151
summary: None,
151152
uid: None,

0 commit comments

Comments
 (0)