diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e60a3f8c..c397b8523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - send normal messages with higher priority than MDNs #3243 - make Scheduler stateless #3302 - support `source_code_url` from Webxdc manifests #3314 +- support Webxdc document names and add `document` to `dc_msg_get_webxdc_info()` #3317 - improve chat encryption info, make it easier to find contacts without keys #3318 ### API-Changes diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 900d60c66..db3ae52ee 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -3728,6 +3728,8 @@ 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. + * - document: if the Webxdc represents a document, this is the name of the document, + * otherwise, this is an empty string. * - 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. diff --git a/draft/webxdc-dev-reference.md b/draft/webxdc-dev-reference.md index 41a2e26a2..b16cad21b 100644 --- a/draft/webxdc-dev-reference.md +++ b/draft/webxdc-dev-reference.md @@ -37,6 +37,8 @@ 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.document`: optional, name of the document in edit, + must not be used eg. in games where the Webxdc does not create documents - `update.summary`: optional, short text, shown beside app icon; it is recommended to use some aggregated value, eg. "8 votes", "Highscore: 123" diff --git a/src/param.rs b/src/param.rs index f538dc478..51a78b73f 100644 --- a/src/param.rs +++ b/src/param.rs @@ -169,6 +169,12 @@ pub enum Param { /// For Chats: timestamp of protection settings update. ProtectionSettingsTimestamp = b'L', + /// For Webxdc Message Instances: Current document name + WebxdcDocument = b'R', + + /// For Webxdc Message Instances: timestamp of document name update. + WebxdcDocumentTimestamp = b'W', + /// For Webxdc Message Instances: Current summary WebxdcSummary = b'N', diff --git a/src/webxdc.rs b/src/webxdc.rs index 177f8853c..03af87dbb 100644 --- a/src/webxdc.rs +++ b/src/webxdc.rs @@ -59,6 +59,7 @@ struct WebxdcManifest { pub struct WebxdcInfo { pub name: String, pub icon: String, + pub document: String, pub summary: String, pub source_code_url: String, } @@ -116,6 +117,9 @@ pub(crate) struct StatusUpdateItem { #[serde(skip_serializing_if = "Option::is_none")] info: Option, + #[serde(skip_serializing_if = "Option::is_none")] + document: Option, + #[serde(skip_serializing_if = "Option::is_none")] summary: Option, } @@ -191,8 +195,8 @@ impl Context { Ok(()) } - /// Takes an update-json as `{payload: PAYLOAD}` (or legacy `PAYLOAD`) - /// writes it to the database and handles events, info-messages and summary. + /// Takes an update-json as `{payload: PAYLOAD}` + /// writes it to the database and handles events, info-messages, document name and summary. async fn create_status_update_record( &self, instance: &mut Message, @@ -212,6 +216,7 @@ impl Context { | MessageState::OutDraft => StatusUpdateItem { payload: item.payload, info: None, // no info-messages in draft mode + document: item.document, summary: item.summary, }, _ => item, @@ -234,17 +239,33 @@ impl Context { .await?; } + let mut param_changed = false; + + if let Some(ref document) = status_update_item.document { + if instance + .param + .update_timestamp(Param::WebxdcDocumentTimestamp, timestamp)? + { + instance.param.set(Param::WebxdcDocument, document); + param_changed = true; + } + } + 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_msgs_changed(instance.chat_id, instance.id); + param_changed = true; } } + if param_changed { + instance.update_param(self).await; + self.emit_msgs_changed(instance.chat_id, instance.id); + } + let rowid = self .sql .insert( @@ -572,6 +593,11 @@ impl Message { } else { WEBXDC_DEFAULT_ICON.to_string() }, + document: self + .param + .get(Param::WebxdcDocument) + .unwrap_or_default() + .to_string(), summary: self .param .get(Param::WebxdcSummary) @@ -1536,6 +1562,48 @@ sth_for_the = "future""# Ok(()) } + #[async_std::test] + async fn test_webxdc_document_name() -> Result<()> { + let alice = TestContext::new_alice().await; + let bob = TestContext::new_bob().await; + + // Alice creates an webxdc instance and updates document name + 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.document, "".to_string()); + assert_eq!(info.summary, "".to_string()); + + alice + .send_webxdc_status_update( + alice_instance.id, + r#"{"document":"my file", "payload":1337}"#, + "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.document, "my file".to_string()); + assert_eq!(info.summary, "".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; + let info = Message::load_from_db(&bob, bob_instance.id) + .await? + .get_webxdc_info(&bob) + .await?; + assert_eq!(info.document, "my file".to_string()); + assert_eq!(info.summary, "".to_string()); + + Ok(()) + } + #[async_std::test] async fn test_webxdc_info_msg() -> Result<()> { let alice = TestContext::new_alice().await;