add editable "summary" to dc_msg_get_webxdc_info()

the summary can be modified by the apps using
`sendUpdate({summary: "foo", payload: ...})`

the summary is updated when there is no newer update
and chat will be informed by the change as usual by
`DC_EVENT_MSGS_CHANGED`.
This commit is contained in:
B. Petersen
2022-01-15 17:36:34 +01:00
committed by bjoern
parent b6b8d11881
commit 6316ee7c9b
4 changed files with 103 additions and 1 deletions

View File

@@ -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(). * To get the file, use dc_msg_get_webxdc_blob().
* App icons should should be square, * App icons should should be square,
* the implementations will add round corners etc. as needed. * 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 * @memberof dc_msg_t
* @param msg The webxdc instance. * @param msg The webxdc instance.

View File

@@ -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"; eg. "Alice voted" or "Bob scored 123 in MyGame";
usually only one line of text is shown, usually only one line of text is shown,
use this option sparingly to not spam the chat. 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. - `descr`: short, human-readable description what this update is about.
this is shown eg. as a fallback text in an email program. this is shown eg. as a fallback text in an email program.

View File

@@ -171,6 +171,12 @@ pub enum Param {
/// For Chats: timestamp of protection settings update. /// For Chats: timestamp of protection settings update.
ProtectionSettingsTimestamp = b'L', 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. /// An object for handling key=value parameter lists.

View File

@@ -49,6 +49,7 @@ struct WebxdcManifest {
pub struct WebxdcInfo { pub struct WebxdcInfo {
pub name: String, pub name: String,
pub icon: String, pub icon: String,
pub summary: String,
} }
/// Status Update ID. /// Status Update ID.
@@ -91,6 +92,9 @@ pub(crate) struct StatusUpdateItem {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
info: Option<String>, info: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
summary: Option<String>,
} }
impl Context { impl Context {
@@ -138,14 +142,29 @@ impl Context {
StatusUpdateItem { StatusUpdateItem {
payload, payload,
info: None, 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?; let mut instance = Message::load_from_db(self, instance_msg_id).await?;
if let Some(ref info) = status_update_item.info { if let Some(ref info) = status_update_item.info {
chat::add_info_msg(self, instance.chat_id, info.as_str(), timestamp).await?; 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 let rowid = self
@@ -404,6 +423,11 @@ impl Message {
} else { } else {
WEBXDC_DEFAULT_ICON.to_string() 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(()) 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_std::test]
async fn test_webxdc_info_msg() -> Result<()> { async fn test_webxdc_info_msg() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;