mirror of
https://github.com/chatmail/core.git
synced 2026-04-27 18:36:30 +03:00
feat: update.href api (#6248)
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 https://github.com/webxdc/website/pull/90
This commit is contained in:
@@ -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().
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")]
|
||||
@@ -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,
|
||||
@@ -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?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
@@ -1457,6 +1480,7 @@ mod tests {
|
||||
StatusUpdateItem {
|
||||
payload: json!({"foo": "bar"}),
|
||||
info: None,
|
||||
href: None,
|
||||
document: None,
|
||||
summary: None,
|
||||
uid: Some("iecie2Ze".to_string()),
|
||||
@@ -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()),
|
||||
@@ -1516,6 +1541,7 @@ mod tests {
|
||||
StatusUpdateItem {
|
||||
payload: json!({"foo2": "bar2"}),
|
||||
info: None,
|
||||
href: None,
|
||||
document: None,
|
||||
summary: None,
|
||||
uid: None,
|
||||
@@ -1536,6 +1562,7 @@ mod tests {
|
||||
StatusUpdateItem {
|
||||
payload: Value::Bool(true),
|
||||
info: None,
|
||||
href: None,
|
||||
document: None,
|
||||
summary: None,
|
||||
uid: None,
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user