diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index f5393938f..47564bc98 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -3701,6 +3701,9 @@ char* dc_msg_get_webxdc_blob (const dc_msg_t* msg, const char* * To get the file, use dc_msg_get_webxdc_blob(). * App icons should should be square, * the implementations will add round corners etc. as needed. + * - summary: short string describing the state of the app, + * sth. as "2 votes", "Highscore: 123", + * can be changed by the apps and defaults to an empty string. * * @memberof dc_msg_t * @param msg The webxdc instance. diff --git a/draft/webxdc-dev-reference.md b/draft/webxdc-dev-reference.md index 926552a8b..45aeb69bd 100644 --- a/draft/webxdc-dev-reference.md +++ b/draft/webxdc-dev-reference.md @@ -33,6 +33,9 @@ To get a shared state, the peers use `sendUpdate()` to send updates to each othe eg. "Alice voted" or "Bob scored 123 in MyGame"; usually only one line of text is shown, use this option sparingly to not spam the chat. + - `update.summary`: optional, short text, shown beside app icon; + it is recommended to use some aggregated value, eg. "8 votes", "Highscore: 123" + - `descr`: short, human-readable description what this update is about. this is shown eg. as a fallback text in an email program. diff --git a/src/param.rs b/src/param.rs index acbb03a78..9afca38f0 100644 --- a/src/param.rs +++ b/src/param.rs @@ -171,6 +171,12 @@ pub enum Param { /// For Chats: timestamp of protection settings update. ProtectionSettingsTimestamp = b'L', + + /// For Webxdc Message Instances: Current summary + WebxdcSummary = b'N', + + /// For Webxdc Message Instances: timestamp of summary update. + WebxdcSummaryTimestamp = b'Q', } /// An object for handling key=value parameter lists. diff --git a/src/webxdc.rs b/src/webxdc.rs index 9cfb43b20..7ea93d604 100644 --- a/src/webxdc.rs +++ b/src/webxdc.rs @@ -49,6 +49,7 @@ struct WebxdcManifest { pub struct WebxdcInfo { pub name: String, pub icon: String, + pub summary: String, } /// Status Update ID. @@ -91,6 +92,9 @@ pub(crate) struct StatusUpdateItem { #[serde(skip_serializing_if = "Option::is_none")] info: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + summary: Option, } impl Context { @@ -138,14 +142,29 @@ impl Context { StatusUpdateItem { payload, info: None, + summary: None, } }; - if status_update_item.info.is_some() { + if status_update_item.info.is_some() || status_update_item.summary.is_some() { let mut instance = Message::load_from_db(self, instance_msg_id).await?; if let Some(ref info) = status_update_item.info { chat::add_info_msg(self, instance.chat_id, info.as_str(), timestamp).await?; } + + if let Some(ref summary) = status_update_item.summary { + if instance + .param + .update_timestamp(Param::WebxdcSummaryTimestamp, timestamp)? + { + instance.param.set(Param::WebxdcSummary, summary); + instance.update_param(self).await; + self.emit_event(EventType::MsgsChanged { + chat_id: instance.chat_id, + msg_id: instance.id, + }); + } + } } let rowid = self @@ -404,6 +423,11 @@ impl Message { } else { WEBXDC_DEFAULT_ICON.to_string() }, + summary: self + .param + .get(Param::WebxdcSummary) + .unwrap_or_default() + .to_string(), }) } } @@ -1120,6 +1144,72 @@ sth_for_the = "future""# Ok(()) } + #[async_std::test] + async fn test_webxdc_info_summary() -> Result<()> { + let alice = TestContext::new_alice().await; + let bob = TestContext::new_bob().await; + + // Alice creates an webxdc instance and updates summary + let alice_chat = alice.create_chat(&bob).await; + let alice_instance = send_webxdc_instance(&alice, alice_chat.id).await?; + let sent_instance = &alice.pop_sent_msg().await; + let info = alice_instance.get_webxdc_info(&alice).await?; + assert_eq!(info.summary, "".to_string()); + + alice + .send_webxdc_status_update( + alice_instance.id, + r#"{"summary":"sum: 1", "payload":1}"#, + "descr", + ) + .await?; + let sent_update1 = &alice.pop_sent_msg().await; + let info = Message::load_from_db(&alice, alice_instance.id) + .await? + .get_webxdc_info(&alice) + .await?; + assert_eq!(info.summary, "sum: 1".to_string()); + + alice + .send_webxdc_status_update( + alice_instance.id, + r#"{"summary":"sum: 2", "payload":2}"#, + "descr", + ) + .await?; + let sent_update2 = &alice.pop_sent_msg().await; + let info = Message::load_from_db(&alice, alice_instance.id) + .await? + .get_webxdc_info(&alice) + .await?; + assert_eq!(info.summary, "sum: 2".to_string()); + + // Bob receives the updates + bob.recv_msg(sent_instance).await; + let bob_instance = bob.get_last_msg().await; + bob.recv_msg(sent_update1).await; + bob.recv_msg(sent_update2).await; + let info = Message::load_from_db(&bob, bob_instance.id) + .await? + .get_webxdc_info(&bob) + .await?; + assert_eq!(info.summary, "sum: 2".to_string()); + + // Alice has a second device and also receives the updates there + let alice2 = TestContext::new_alice().await; + alice2.recv_msg(sent_instance).await; + let alice2_instance = alice2.get_last_msg().await; + alice2.recv_msg(sent_update1).await; + alice2.recv_msg(sent_update2).await; + let info = Message::load_from_db(&alice2, alice2_instance.id) + .await? + .get_webxdc_info(&alice2) + .await?; + assert_eq!(info.summary, "sum: 2".to_string()); + + Ok(()) + } + #[async_std::test] async fn test_webxdc_info_msg() -> Result<()> { let alice = TestContext::new_alice().await;