deltachat/webxdc/
integration.rsuse crate::chat::{send_msg, ChatId};
use crate::config::Config;
use crate::contact::ContactId;
use crate::context::Context;
use crate::message::{Message, MsgId, Viewtype};
use crate::param::Param;
use crate::webxdc::{maps_integration, StatusUpdateItem, StatusUpdateSerial};
use anyhow::Result;
impl Context {
pub async fn set_webxdc_integration(&self, file: &str) -> Result<()> {
let chat_id = ChatId::create_for_contact(self, ContactId::SELF).await?;
let mut msg = Message::new(Viewtype::Webxdc);
msg.set_file(file, None);
msg.hidden = true;
msg.param.set_int(Param::WebxdcIntegration, 1);
msg.param.set_int(Param::GuaranteeE2ee, 1); send_msg(self, chat_id, &mut msg).await?;
Ok(())
}
pub async fn init_webxdc_integration(
&self,
integrate_for: Option<ChatId>,
) -> Result<Option<MsgId>> {
let Some(instance_id) = self
.get_config_parsed::<u32>(Config::WebxdcIntegration)
.await?
else {
return Ok(None);
};
let Some(mut instance) =
Message::load_from_db_optional(self, MsgId::new(instance_id)).await?
else {
return Ok(None);
};
if instance.viewtype != Viewtype::Webxdc {
return Ok(None);
}
let integrate_for = integrate_for.unwrap_or_default().to_u32() as i32;
if instance.param.get_int(Param::WebxdcIntegrateFor) != Some(integrate_for) {
instance
.param
.set_int(Param::WebxdcIntegrateFor, integrate_for);
instance.update_param(self).await?;
}
Ok(Some(instance.id))
}
pub(crate) async fn update_webxdc_integration_database(
&self,
msg: &mut Message,
context: &Context,
) -> Result<()> {
if msg.viewtype == Viewtype::Webxdc {
let is_integration = if msg.param.get_int(Param::WebxdcIntegration).is_some() {
true
} else if msg.chat_id.is_self_talk(context).await? {
let info = msg.get_webxdc_info(context).await?;
if info.request_integration == "map" {
msg.param.set_int(Param::WebxdcIntegration, 1);
msg.update_param(context).await?;
true
} else {
false
}
} else {
false
};
if is_integration {
self.set_config_internal(
Config::WebxdcIntegration,
Some(&msg.id.to_u32().to_string()),
)
.await?;
}
}
Ok(())
}
pub(crate) async fn intercept_send_webxdc_status_update(
&self,
instance: Message,
status_update: StatusUpdateItem,
) -> Result<()> {
let chat_id = instance.webxdc_integrated_for();
maps_integration::intercept_send_update(self, chat_id, status_update).await
}
pub(crate) async fn intercept_get_webxdc_status_updates(
&self,
instance: Message,
last_known_serial: StatusUpdateSerial,
) -> Result<String> {
let chat_id = instance.webxdc_integrated_for();
maps_integration::intercept_get_updates(self, chat_id, last_known_serial).await
}
}
impl Message {
fn webxdc_integrated_for(&self) -> Option<ChatId> {
let raw_id = self.param.get_int(Param::WebxdcIntegrateFor).unwrap_or(0) as u32;
if raw_id > 0 {
Some(ChatId::new(raw_id))
} else {
None
}
}
pub(crate) async fn is_set_as_webxdc_integration(&self, context: &Context) -> Result<bool> {
if let Some(integration_id) = context
.get_config_parsed::<u32>(Config::WebxdcIntegration)
.await?
{
Ok(integration_id == self.id.to_u32())
} else {
Ok(false)
}
}
}
#[cfg(test)]
mod tests {
use crate::config::Config;
use crate::context::Context;
use crate::message;
use crate::message::{Message, Viewtype};
use crate::test_utils::TestContext;
use anyhow::Result;
use std::time::Duration;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_default_integrations_are_single_device() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config_bool(Config::BccSelf, false).await?;
let bytes = include_bytes!("../../test-data/webxdc/minimal.xdc");
let file = t.get_blobdir().join("maps.xdc");
tokio::fs::write(&file, bytes).await.unwrap();
t.set_webxdc_integration(file.to_str().unwrap()).await?;
let sent = t.pop_sent_msg_opt(Duration::from_secs(1)).await;
assert!(sent.is_none());
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_overwrite_default_integration() -> Result<()> {
let t = TestContext::new_alice().await;
let self_chat = &t.get_self_chat().await;
assert!(t.init_webxdc_integration(None).await?.is_none());
async fn assert_integration(t: &Context, name: &str) -> Result<()> {
let integration_id = t.init_webxdc_integration(None).await?.unwrap();
let integration = Message::load_from_db(t, integration_id).await?;
let integration_info = integration.get_webxdc_info(t).await?;
assert_eq!(integration_info.name, name);
Ok(())
}
let bytes = include_bytes!("../../test-data/webxdc/with-manifest-and-png-icon.xdc");
let file = t.get_blobdir().join("maps.xdc");
tokio::fs::write(&file, bytes).await.unwrap();
t.set_webxdc_integration(file.to_str().unwrap()).await?;
assert_integration(&t, "with some icon").await?;
let mut msg = Message::new(Viewtype::Webxdc);
msg.set_file_from_bytes(
&t,
"mapstest.xdc",
include_bytes!("../../test-data/webxdc/mapstest-integration-unset.xdc"),
None,
)
.await?;
t.send_msg(self_chat.id, &mut msg).await;
assert_integration(&t, "with some icon").await?; let mut msg = Message::new(Viewtype::Webxdc);
msg.set_file_from_bytes(
&t,
"mapstest.xdc",
include_bytes!("../../test-data/webxdc/mapstest-integration-set.xdc"),
None,
)
.await?;
let sent = t.send_msg(self_chat.id, &mut msg).await;
let info = msg.get_webxdc_info(&t).await?;
assert!(info.summary.contains("Used as map"));
assert_integration(&t, "Maps Test 2").await?;
let t2 = TestContext::new_alice().await;
let msg2 = t2.recv_msg(&sent).await;
let info = msg2.get_webxdc_info(&t2).await?;
assert!(info.summary.contains("To use as map,"));
assert!(t2.init_webxdc_integration(None).await?.is_none());
message::delete_msgs(&t, &[msg.id]).await?;
assert!(t.init_webxdc_integration(None).await?.is_none());
Ok(())
}
}