-
-
Notifications
You must be signed in to change notification settings - Fork 126
feat: Send webxdc status updates in InBroadcast-s (#7679) #7836
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -107,12 +107,14 @@ pub const DC_CHAT_ID_LAST_SPECIAL: ChatId = ChatId::new(9); | |
| IntoStaticStr, | ||
| Serialize, | ||
| Deserialize, | ||
| Default, | ||
| )] | ||
| #[repr(u32)] | ||
| pub enum Chattype { | ||
| /// A 1:1 chat, i.e. a normal chat with a single contact. | ||
| /// | ||
| /// Created by [`ChatId::create_for_contact`]. | ||
| #[default] | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is to avoid adding |
||
| Single = 100, | ||
|
|
||
| /// Group chat. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -453,6 +453,7 @@ pub struct Message { | |
| pub(crate) is_dc_message: MessengerMessage, | ||
| pub(crate) original_msg_id: MsgId, | ||
| pub(crate) mime_modified: bool, | ||
| pub(crate) chat_typ: Chattype, | ||
| pub(crate) chat_visibility: ChatVisibility, | ||
| pub(crate) chat_blocked: Blocked, | ||
| pub(crate) location_id: u32, | ||
|
|
@@ -527,6 +528,7 @@ impl Message { | |
| m.param AS param, | ||
| m.hidden AS hidden, | ||
| m.location_id AS location, | ||
| c.type AS chat_typ, | ||
| c.archived AS visibility, | ||
| c.blocked AS blocked | ||
| FROM msgs m | ||
|
|
@@ -586,6 +588,10 @@ impl Message { | |
| param: row.get::<_, String>("param")?.parse().unwrap_or_default(), | ||
| hidden: row.get("hidden")?, | ||
| location_id: row.get("location")?, | ||
| // This is safe: see `ChatId::delete_ex()`, `None` chat type can't happen. | ||
| chat_typ: row | ||
| .get::<_, Option<_>>("chat_typ")? | ||
| .unwrap_or(Chattype::Single), | ||
|
Comment on lines
+591
to
+594
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it makes sense to load all kind of chat data every time we load a message. Also,
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But here it's cheap, until we remove JOIN at least. Also, why not have some context in the |
||
| chat_visibility: row.get::<_, Option<_>>("visibility")?.unwrap_or_default(), | ||
| chat_blocked: row | ||
| .get::<_, Option<Blocked>>("blocked")? | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1589,10 +1589,7 @@ impl MimeFactory { | |
| } | ||
| } | ||
|
|
||
| if chat.typ == Chattype::Group | ||
| || chat.typ == Chattype::OutBroadcast | ||
| || chat.typ == Chattype::InBroadcast | ||
| { | ||
| if chat.typ == Chattype::Group || chat.typ == Chattype::OutBroadcast { | ||
| headers.push(( | ||
| "Chat-Group-Name", | ||
| mail_builder::headers::text::Text::new(chat.name.to_string()).into(), | ||
|
|
@@ -1603,7 +1600,11 @@ impl MimeFactory { | |
| mail_builder::headers::text::Text::new(ts.to_string()).into(), | ||
| )); | ||
| } | ||
|
|
||
| } | ||
| if chat.typ == Chattype::Group | ||
| || chat.typ == Chattype::OutBroadcast | ||
| || chat.typ == Chattype::InBroadcast | ||
| { | ||
| match command { | ||
| SystemMessage::MemberRemovedFromGroup => { | ||
| let email_to_remove = msg.param.get(Param::Arg).unwrap_or_default(); | ||
|
|
@@ -2036,14 +2037,15 @@ impl MimeFactory { | |
| HeaderDef::IrohGossipTopic.get_headername(), | ||
| mail_builder::headers::raw::Raw::new(topic).into(), | ||
| )); | ||
| if let (Some(json), _) = context | ||
| .render_webxdc_status_update_object( | ||
| msg.id, | ||
| StatusUpdateSerial::MIN, | ||
| StatusUpdateSerial::MAX, | ||
| None, | ||
| ) | ||
| .await? | ||
| if msg.chat_typ != Chattype::OutBroadcast | ||
| && let (Some(json), _) = context | ||
| .render_webxdc_status_update_object( | ||
| msg.id, | ||
| StatusUpdateSerial::MIN, | ||
| StatusUpdateSerial::MAX, | ||
| None, | ||
| ) | ||
| .await? | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not very used to the webxdc code, but: Doesn't this change mean that no status updates will be sent into OutBroadcasts at all anymore? And, isn't the change in
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Status updates will be sent, but only as separate messages (see |
||
| { | ||
| parts.push(context.build_status_update_part(&json)); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,9 @@ use crate::chat::{ | |
| use crate::chatlist::Chatlist; | ||
| use crate::config::Config; | ||
| use crate::ephemeral; | ||
| use crate::imex::{BackupProvider, get_backup}; | ||
| use crate::receive_imf::receive_imf; | ||
| use crate::securejoin::get_securejoin_qr; | ||
| use crate::test_utils::{E2EE_INFO_MSGS, TestContext, TestContextManager}; | ||
| use crate::tools::{self, SystemTime}; | ||
| use crate::{message, sql}; | ||
|
|
@@ -1588,6 +1590,93 @@ async fn test_webxdc_no_internet_access() -> Result<()> { | |
| Ok(()) | ||
| } | ||
|
|
||
| #[tokio::test(flavor = "multi_thread", worker_threads = 2)] | ||
| async fn test_in_broadcast_send_status_update() -> Result<()> { | ||
| let mut tcm = TestContextManager::new(); | ||
| let alice = &tcm.alice().await; | ||
| let bob = &tcm.bob().await; | ||
|
|
||
| let alice_chat_id = create_broadcast(alice, "bc".to_string()).await?; | ||
| let qr = get_securejoin_qr(alice, Some(alice_chat_id)).await?; | ||
| let bob_chat_id = tcm.exec_securejoin_qr(bob, alice, &qr).await; | ||
|
|
||
| let mut alice_instance = create_webxdc_instance( | ||
| alice, | ||
| "minimal.xdc", | ||
| include_bytes!("../../test-data/webxdc/minimal.xdc"), | ||
| )?; | ||
| alice_chat_id | ||
| .set_draft(alice, Some(&mut alice_instance)) | ||
| .await?; | ||
| alice_instance = alice_chat_id.get_draft(alice).await?.unwrap(); | ||
| alice | ||
| .send_webxdc_status_update(alice_instance.id, r#"{"payload":41}"#) | ||
| .await?; | ||
| send_msg(alice, alice_chat_id, &mut alice_instance).await?; | ||
| let sent_msg = alice.pop_sent_msg().await; | ||
| let bob_instance = bob.recv_msg(&sent_msg).await; | ||
| assert_eq!(bob_instance.chat_id, bob_chat_id); | ||
|
|
||
| let provider = BackupProvider::prepare(bob).await?; | ||
| let bob2 = &tcm.unconfigured().await; | ||
| get_backup(bob2, provider.qr()).await?; | ||
|
|
||
| bob.send_webxdc_status_update(bob_instance.id, r#"{"payload":42}"#) | ||
| .await?; | ||
| bob.flush_status_updates().await?; | ||
| // Don't wait to make sure that status updates are sent immediately, otherwise the check for | ||
| // Alice below isn't reliable. | ||
| let sent_msg = bob.pop_sent_msg_opt(Duration::ZERO).await.unwrap(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why this change?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is to make sure that status updates are sent immediately, otherwise the check below: // See above for bob -- status updates are flushed immediately, no need to wait.
assert!(alice.pop_sent_msg_opt(Duration::ZERO).await.is_none());i.e. that alice (the channel owner) doesn't resend subscribers' status updates isn't reliable. |
||
|
|
||
| alice.recv_msg_trash(&sent_msg).await; | ||
| assert_eq!( | ||
| alice | ||
| .get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0)) | ||
| .await?, | ||
| r#"[{"payload":41,"serial":1,"max_serial":2}, | ||
| {"payload":42,"serial":2,"max_serial":2}]"# | ||
| ); | ||
|
|
||
| bob2.recv_msg_trash(&sent_msg).await; | ||
| assert_eq!( | ||
| bob2.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0)) | ||
| .await?, | ||
| r#"[{"payload":41,"serial":1,"max_serial":2}, | ||
| {"payload":42,"serial":2,"max_serial":2}]"# | ||
| ); | ||
|
|
||
| // Non-subscriber's status updates are rejected. | ||
| let alice_bob_id = alice.add_or_lookup_contact_id(bob).await; | ||
| remove_contact_from_chat(alice, alice_chat_id, alice_bob_id).await?; | ||
| alice.pop_sent_msg().await; | ||
| let status = | ||
| helper_send_receive_status_update(bob, alice, &bob_instance, &alice_instance).await?; | ||
| assert_eq!( | ||
| status, | ||
| r#"[{"payload":41,"serial":1,"max_serial":2}, | ||
| {"payload":42,"serial":2,"max_serial":2}]"# | ||
| ); | ||
|
|
||
| // Subscribers' status updates are confidential and shalln't be re-sent. So initial status | ||
| // updates aren't re-sent too. | ||
| let fiona = &tcm.fiona().await; | ||
| let fiona_chat_id = tcm.exec_securejoin_qr(fiona, alice, &qr).await; | ||
| resend_msgs(alice, &[alice_instance.id]).await?; | ||
| let sent1 = alice.pop_sent_msg().await; | ||
| alice.flush_status_updates().await?; | ||
| assert!(alice.pop_sent_msg_opt(Duration::ZERO).await.is_none()); | ||
| let fiona_instance = fiona.recv_msg(&sent1).await; | ||
| assert_eq!(fiona_instance.chat_id, fiona_chat_id); | ||
| assert_eq!(fiona_instance.chat_typ, Chattype::InBroadcast); | ||
| assert_eq!( | ||
| fiona | ||
| .get_webxdc_status_updates(fiona_instance.id, StatusUpdateSerial(0)) | ||
| .await?, | ||
| r#"[]"# | ||
| ); | ||
| Ok(()) | ||
| } | ||
|
|
||
| #[tokio::test(flavor = "multi_thread", worker_threads = 2)] | ||
| async fn test_webxdc_chatlist_summary() -> Result<()> { | ||
| let t = TestContext::new_alice().await; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just add
| SystemMessage::WebxdcStatusUpdateto this code above:?
Then the
let skip_fn = |reason: &CantSendReason| *reason == CantSendReason::InBroadcast;change inwebxdc.rsalso wouldn't be necessary.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then
why_cant_send_ex()will be called twice and it's not a cheap function, so it's better to check everything early, before creating the status update record