mirror of
https://github.com/chatmail/core.git
synced 2026-07-01 12:04:57 +03:00
Compare commits
5 Commits
webxdc-dee
...
sk/hide_dr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c68e25a970 | ||
|
|
e04c4446d6 | ||
|
|
1b9f3368fa | ||
|
|
3b9e6d6ffa | ||
|
|
8f3be764d2 |
@@ -1154,13 +1154,7 @@ uint32_t dc_send_videochat_invitation (dc_context_t* context, uint32_t chat_id);
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @param msg_id The ID of the message with the webxdc instance.
|
||||
* @param json program-readable data, this is create in JS land as:
|
||||
* - `payload`: any JS object or primitive.
|
||||
* - `info`: optional informational message. will be shown in chat an may be added as system notification.
|
||||
* - `notify`: optional array of user `addr` that should be notified eg. by a sound.
|
||||
* note that still all users get the update payload and the `info` message shown in a chat.
|
||||
* - `document`: optional document name. shown eg. in title bar.
|
||||
* - `summary`: optional summary. shown beside app icon.
|
||||
* @param json program-readable data, the actual payload
|
||||
* @param descr The user-visible description of JSON data,
|
||||
* in case of a chess game, e.g. the move.
|
||||
* @return 1=success, 0=error
|
||||
@@ -4517,26 +4511,6 @@ 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 deeplink attached to an info message.
|
||||
* The info message need to be of type DC_INFO_WEBXDC_INFO_MESSAGE.
|
||||
*
|
||||
* Typically, this is used to start the corresponding webxdc directly or indirectly
|
||||
* and passing the deeplink to `window.webxdc.deeplink` in JS land.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @param info_msg_id The info message object.
|
||||
* Not: the webxdc instance.
|
||||
* @return The deeplink that can be passed to `window.webxdc.deeplink` in JS land.
|
||||
* The content of the deeplink is a full status update and its semantic is defined by the app.
|
||||
* Returns NULL if there is no deeplink attached to the info message and on errors.
|
||||
*
|
||||
*/
|
||||
char* dc_msg_get_webxdc_deeplink (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().
|
||||
@@ -6111,10 +6085,6 @@ void dc_event_unref(dc_event_t* event);
|
||||
*
|
||||
* There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event.
|
||||
*
|
||||
* If the message is an webxdc info message,
|
||||
* dc_msg_get_parent() returns the webxdc instance the notification belongs to.
|
||||
* Use dc_msg_get_webxdc_deeplink() to get the deeplink to pass to `window.webxdc.deeplink` in JS land.
|
||||
*
|
||||
* @param data1 (int) chat_id
|
||||
* @param data2 (int) msg_id
|
||||
*/
|
||||
|
||||
@@ -3681,31 +3681,6 @@ 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_deeplink(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
||||
if msg.is_null() {
|
||||
eprintln!("ignoring careless call to dc_msg_get_webxdc_deeplink()");
|
||||
return "".strdup();
|
||||
}
|
||||
|
||||
let ffi_msg = &*msg;
|
||||
let ctx = &*ffi_msg.context;
|
||||
let res = block_on(async move {
|
||||
ffi_msg
|
||||
.message
|
||||
.get_webxdc_deeplink(ctx)
|
||||
.await
|
||||
.context("failed to get deeplink")
|
||||
.log_err(ctx)
|
||||
.unwrap_or(None)
|
||||
});
|
||||
|
||||
match res {
|
||||
Some(str) => str.strdup(),
|
||||
None => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg_t) -> libc::c_int {
|
||||
if msg.is_null() {
|
||||
|
||||
@@ -705,7 +705,7 @@ class TestOfflineChat:
|
||||
ac1 = acfactory.get_pseudo_configured_account()
|
||||
ac2 = acfactory.get_pseudo_configured_account()
|
||||
qr = ac1.get_setup_contact_qr()
|
||||
assert qr.startswith("OPENPGP4FPR:")
|
||||
assert qr.startswith("https://i.delta.chat")
|
||||
res = ac2.check_qr(qr)
|
||||
assert res.is_ask_verifycontact()
|
||||
assert not res.is_ask_verifygroup()
|
||||
|
||||
123
src/chat.rs
123
src/chat.rs
@@ -1,11 +1,11 @@
|
||||
//! # Chat module.
|
||||
|
||||
use std::cmp;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::{cmp, convert};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Context as _, Result};
|
||||
use deltachat_contact_tools::{sanitize_bidi_characters, sanitize_single_line, ContactAddress};
|
||||
@@ -809,6 +809,14 @@ impl ChatId {
|
||||
///
|
||||
/// Passing `None` as message just deletes the draft
|
||||
pub async fn set_draft(self, context: &Context, mut msg: Option<&mut Message>) -> Result<()> {
|
||||
let self_in_chat = self.is_self_in_chat(context).await?;
|
||||
if !self_in_chat {
|
||||
warn!(
|
||||
context,
|
||||
"Not setting draft because self is not member of the chat"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
if self.is_special() {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -875,6 +883,22 @@ impl ChatId {
|
||||
> 0)
|
||||
}
|
||||
|
||||
async fn get_chat_type(self, context: &Context) -> Result<Chattype> {
|
||||
context
|
||||
.sql
|
||||
.query_get_value("SELECT type FROM chats WHERE id=?;", (self,))
|
||||
.await
|
||||
.map(|res| res.ok_or(anyhow!("Can't get type for char")))
|
||||
.and_then(convert::identity)
|
||||
}
|
||||
|
||||
async fn is_self_in_chat(self, context: &Context) -> Result<bool> {
|
||||
match self.get_chat_type(context).await? {
|
||||
Chattype::Single | Chattype::Broadcast | Chattype::Mailinglist => Ok(true),
|
||||
Chattype::Group => is_contact_in_chat(context, self, ContactId::SELF).await,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set provided message as draft message for specified chat.
|
||||
/// Returns true if the draft was added or updated in place.
|
||||
async fn do_set_draft(self, context: &Context, msg: &mut Message) -> Result<bool> {
|
||||
@@ -1838,8 +1862,9 @@ impl Chat {
|
||||
/// This is somewhat experimental, even more so than the rest of
|
||||
/// deltachat, and the data returned is still subject to change.
|
||||
pub async fn get_info(&self, context: &Context) -> Result<ChatInfo> {
|
||||
let self_in_chat = self.is_self_in_chat(context).await?;
|
||||
let draft = match self.id.get_draft(context).await? {
|
||||
Some(message) => message.text,
|
||||
Some(message) if self_in_chat => message.text,
|
||||
_ => String::new(),
|
||||
};
|
||||
Ok(ChatInfo {
|
||||
@@ -4508,34 +4533,9 @@ pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Resul
|
||||
/// Adds an informational message to chat.
|
||||
///
|
||||
/// For example, it can be a message showing that a member was added to a group.
|
||||
/// Doesn't fail if the chat doesn't exist.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn add_info_msg_with_cmd(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
text: &str,
|
||||
cmd: SystemMessage,
|
||||
timestamp_sort: i64,
|
||||
timestamp_sent_rcvd: Option<i64>,
|
||||
parent: Option<&Message>,
|
||||
from_id: Option<ContactId>,
|
||||
) -> Result<MsgId> {
|
||||
add_info_msg_with_importance(
|
||||
context,
|
||||
chat_id,
|
||||
text,
|
||||
cmd,
|
||||
timestamp_sort,
|
||||
timestamp_sent_rcvd,
|
||||
parent,
|
||||
from_id,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Adds an informational message to chat, optionally showing a notification for important messages.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn add_info_msg_with_importance(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
text: &str,
|
||||
@@ -4545,7 +4545,6 @@ pub(crate) async fn add_info_msg_with_importance(
|
||||
timestamp_sent_rcvd: Option<i64>,
|
||||
parent: Option<&Message>,
|
||||
from_id: Option<ContactId>,
|
||||
important: bool,
|
||||
) -> Result<MsgId> {
|
||||
let rfc724_mid = create_outgoing_rfc724_mid();
|
||||
let ephemeral_timer = chat_id.get_ephemeral_timer(context).await?;
|
||||
@@ -4579,7 +4578,7 @@ pub(crate) async fn add_info_msg_with_importance(
|
||||
context.new_msgs_notify.notify_one();
|
||||
|
||||
let msg_id = MsgId::new(row_id.try_into()?);
|
||||
chat_id.emit_msg_event(context, msg_id, important);
|
||||
context.emit_msgs_changed(chat_id, msg_id);
|
||||
|
||||
Ok(msg_id)
|
||||
}
|
||||
@@ -4920,6 +4919,70 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_draft_only_in_accesible_chat_summary() -> Result<()> {
|
||||
let mut t = TestContextManager::new();
|
||||
let alice = t.alice().await;
|
||||
let bob = t.bob().await;
|
||||
let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "chat").await?;
|
||||
let bob_contact = Contact::create(&alice, "Bob", "bob@example.net").await?;
|
||||
add_contact_to_chat(&alice, chat_id, bob_contact).await?;
|
||||
let bob_chat_id = bob
|
||||
.recv_msg(&alice.send_text(chat_id, "hello bob").await)
|
||||
.await
|
||||
.chat_id;
|
||||
bob_chat_id.accept(&bob).await?;
|
||||
|
||||
// Set draft and assure it is in chat info.
|
||||
let draft = String::from("I'm gonna send this!!");
|
||||
bob_chat_id
|
||||
.set_draft(&bob, Some(&mut Message::new_text(draft.clone())))
|
||||
.await?;
|
||||
let chat = Chat::load_from_db(&bob, bob_chat_id).await?;
|
||||
let info = chat.get_info(&bob).await?;
|
||||
assert_eq!(info.draft, draft);
|
||||
|
||||
// Alice removes bob, so draft is not shown in chat info.
|
||||
remove_contact_from_chat(&alice, chat_id, bob_contact).await?;
|
||||
bob.recv_msg(&alice.pop_sent_msg().await).await;
|
||||
let chat = Chat::load_from_db(&bob, bob_chat_id).await?;
|
||||
assert!(!chat.can_send(&bob).await?);
|
||||
let info = chat.get_info(&bob).await?;
|
||||
assert_eq!(info.draft, String::from(""));
|
||||
|
||||
// Alice re-adds bob, so draft is shown again.
|
||||
add_contact_to_chat(&alice, chat_id, bob_contact).await?;
|
||||
let bob_chat_id = bob
|
||||
.recv_msg(&alice.send_text(chat_id, "hello again, bob").await)
|
||||
.await
|
||||
.chat_id;
|
||||
|
||||
let chat = Chat::load_from_db(&bob, bob_chat_id).await?;
|
||||
assert!(chat.can_send(&bob).await?);
|
||||
let info = chat.get_info(&bob).await?;
|
||||
assert_eq!(info.draft, draft);
|
||||
|
||||
// Bob leaves group so draft is not shown.
|
||||
remove_contact_from_chat(&bob, bob_chat_id, ContactId::SELF).await?;
|
||||
let chat = Chat::load_from_db(&bob, bob_chat_id).await?;
|
||||
assert!(!chat.can_send(&bob).await?);
|
||||
let info = chat.get_info(&bob).await?;
|
||||
assert_eq!(info.draft, String::from(""));
|
||||
|
||||
// Bob can not set a draft if not in chat.
|
||||
bob_chat_id
|
||||
.set_draft(
|
||||
&bob,
|
||||
Some(&mut Message::new_text(String::from(
|
||||
"I'm gonna send this fr fr!!",
|
||||
))),
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(bob_chat_id.get_draft(&bob).await?.unwrap().text, draft);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_change_quotes_on_reused_message_object() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
@@ -2082,7 +2082,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Change the config circumventing the cache
|
||||
// This simulates what the notfication plugin on iOS might do
|
||||
// This simulates what the notification plugin on iOS might do
|
||||
// because it runs in a different process
|
||||
alice
|
||||
.sql
|
||||
|
||||
@@ -63,7 +63,6 @@ pub async fn debug_logging_loop(context: &Context, events: Receiver<DebugEventLo
|
||||
summary: None,
|
||||
document: None,
|
||||
uid: None,
|
||||
notify: None,
|
||||
},
|
||||
time,
|
||||
)
|
||||
|
||||
@@ -104,7 +104,7 @@ pub async fn get_securejoin_qr(context: &Context, group: Option<ChatId>) -> Resu
|
||||
context.scheduler.interrupt_inbox().await;
|
||||
}
|
||||
format!(
|
||||
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
|
||||
"https://i.delta.chat/#{}&a={}&g={}&x={}&i={}&s={}",
|
||||
fingerprint.hex(),
|
||||
self_addr_urlencoded,
|
||||
&group_name_urlencoded,
|
||||
@@ -119,7 +119,7 @@ pub async fn get_securejoin_qr(context: &Context, group: Option<ChatId>) -> Resu
|
||||
context.scheduler.interrupt_inbox().await;
|
||||
}
|
||||
format!(
|
||||
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
|
||||
"https://i.delta.chat/#{}&a={}&n={}&i={}&s={}",
|
||||
fingerprint.hex(),
|
||||
self_addr_urlencoded,
|
||||
self_name_urlencoded,
|
||||
|
||||
242
src/webxdc.rs
242
src/webxdc.rs
@@ -177,10 +177,6 @@ pub struct StatusUpdateItem {
|
||||
/// If there is no ID, message is always considered to be unique.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub uid: Option<String>,
|
||||
|
||||
/// Array of Addr that should be notified about this update.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub notify: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// Update items as passed to the UIs.
|
||||
@@ -315,53 +311,29 @@ impl Context {
|
||||
|
||||
if can_info_msg {
|
||||
if let Some(ref info) = status_update_item.info {
|
||||
let mut info_msg_id = self.get_overwritable_info_msg_id(instance, from_id).await?;
|
||||
let notify_list = status_update_item.notify;
|
||||
|
||||
if notify_list.is_none() && info_msg_id.is_some() {
|
||||
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?;
|
||||
}
|
||||
if let Some(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?;
|
||||
} else {
|
||||
let notify = if let Some(notify_list) = notify_list {
|
||||
if let Ok(self_addr) = self.get_primary_self_addr().await {
|
||||
notify_list.contains(&self_addr)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
info_msg_id = Some(
|
||||
chat::add_info_msg_with_importance(
|
||||
self,
|
||||
instance.chat_id,
|
||||
info.as_str(),
|
||||
SystemMessage::WebxdcInfoMessage,
|
||||
timestamp,
|
||||
None,
|
||||
Some(instance),
|
||||
Some(from_id),
|
||||
notify,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(info_msg_id) = info_msg_id {
|
||||
let mut info_msg = Message::load_from_db(self, info_msg_id).await?;
|
||||
info_msg
|
||||
.param
|
||||
.set_int(Param::Arg, status_update_serial.to_u32() as i32);
|
||||
info_msg.update_param(self).await?;
|
||||
chat::add_info_msg_with_cmd(
|
||||
self,
|
||||
instance.chat_id,
|
||||
info.as_str(),
|
||||
SystemMessage::WebxdcInfoMessage,
|
||||
timestamp,
|
||||
None,
|
||||
Some(instance),
|
||||
Some(from_id),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -934,37 +906,6 @@ impl Message {
|
||||
internet_access,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get deeplink attached to an info message.
|
||||
///
|
||||
/// The info message need to be of type SystemMessage::WebxdcInfoMessage.
|
||||
/// Typically, this is used to start the corresponding webxdc directly or indirectly
|
||||
// and passing the deeplink to `window.webxdc.deeplink` in JS land.
|
||||
pub async fn get_webxdc_deeplink(&self, context: &Context) -> Result<Option<String>> {
|
||||
let Some(serial) = self.param.get_int(Param::Arg) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let serial = StatusUpdateSerial::new(serial as u32);
|
||||
let Some(instance) = self.parent(&context).await? else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let update_item_str: String = context
|
||||
.sql
|
||||
.query_get_value(
|
||||
"SELECT update_item FROM msgs_status_updates WHERE msg_id=? AND id=?",
|
||||
(instance.id, serial),
|
||||
)
|
||||
.await?
|
||||
.context("cannot read update item")?;
|
||||
let update_item = StatusUpdateItem {
|
||||
uid: None, // Erase UIDs, apps, bots and tests don't need to know them.
|
||||
..serde_json::from_str(&update_item_str)?
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&update_item)?;
|
||||
Ok(Some(json))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1481,7 +1422,6 @@ mod tests {
|
||||
document: None,
|
||||
summary: None,
|
||||
uid: Some("iecie2Ze".to_string()),
|
||||
notify: None,
|
||||
},
|
||||
1640178619,
|
||||
true,
|
||||
@@ -1506,7 +1446,6 @@ mod tests {
|
||||
document: None,
|
||||
summary: None,
|
||||
uid: Some("iecie2Ze".to_string()),
|
||||
notify: None,
|
||||
},
|
||||
1640178619,
|
||||
true,
|
||||
@@ -1540,7 +1479,6 @@ mod tests {
|
||||
document: None,
|
||||
summary: None,
|
||||
uid: None,
|
||||
notify: None,
|
||||
},
|
||||
1640178619,
|
||||
true,
|
||||
@@ -1560,7 +1498,6 @@ mod tests {
|
||||
document: None,
|
||||
summary: None,
|
||||
uid: None,
|
||||
notify: None,
|
||||
},
|
||||
1640178619,
|
||||
true,
|
||||
@@ -2934,139 +2871,4 @@ sth_for_the = "future""#
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn has_incoming_msg_event(t: &TestContext, msg: Message) -> bool {
|
||||
t.evtracker
|
||||
.get_matching_opt(t, |evt| {
|
||||
if let EventType::IncomingMsg { msg_id, .. } = evt {
|
||||
*msg_id == msg.id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.await
|
||||
.is_some()
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_notify_one() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
let fiona = tcm.fiona().await;
|
||||
|
||||
let grp_id = alice
|
||||
.create_group_with_members(ProtectionStatus::Unprotected, "grp", &[&bob, &fiona])
|
||||
.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":7,"info": "your move!","notify":["bob@example.net"]}"#,
|
||||
"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!(!has_incoming_msg_event(&alice, info_msg).await);
|
||||
|
||||
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!(has_incoming_msg_event(&bob, info_msg).await);
|
||||
|
||||
fiona.recv_msg(&sent1).await;
|
||||
fiona.recv_msg_trash(&sent2).await;
|
||||
let info_msg = fiona.get_last_msg().await;
|
||||
assert!(info_msg.is_info());
|
||||
assert!(!has_incoming_msg_event(&fiona, info_msg).await);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_notify_multiple() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
let fiona = tcm.fiona().await;
|
||||
|
||||
let grp_id = alice
|
||||
.create_group_with_members(ProtectionStatus::Unprotected, "grp", &[&bob, &fiona])
|
||||
.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":7,"info": "my move!","notify":["bob@example.net","fiona@example.net"]}"#,
|
||||
"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!(!has_incoming_msg_event(&alice, info_msg).await);
|
||||
|
||||
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!(has_incoming_msg_event(&bob, info_msg).await);
|
||||
|
||||
fiona.recv_msg(&sent1).await;
|
||||
fiona.recv_msg_trash(&sent2).await;
|
||||
let info_msg = fiona.get_last_msg().await;
|
||||
assert!(info_msg.is_info());
|
||||
assert!(has_incoming_msg_event(&fiona, info_msg).await);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_deeplink() -> 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!"}"#,
|
||||
"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_deeplink(&alice).await?.unwrap(),
|
||||
r#"{"payload":"my deeplink data","info":"my move!"}"#
|
||||
);
|
||||
|
||||
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_deeplink(&bob).await?.unwrap(),
|
||||
r#"{"payload":"my deeplink data","info":"my move!"}"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,6 @@ pub(crate) async fn intercept_get_updates(
|
||||
document: None,
|
||||
summary: None,
|
||||
uid: None,
|
||||
notify: None,
|
||||
},
|
||||
serial: StatusUpdateSerial(location.location_id),
|
||||
max_serial: StatusUpdateSerial(location.location_id),
|
||||
|
||||
Reference in New Issue
Block a user