From fb05e90151b34bb17c9d0866e536330b79f73552 Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Thu, 4 Sep 2025 17:40:56 +0200 Subject: [PATCH] hide call status change messages --- src/calls.rs | 16 +++++++- src/calls/calls_tests.rs | 83 ++++++++++++++++++++++++---------------- src/receive_imf.rs | 5 +++ 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/src/calls.rs b/src/calls.rs index a968ccfdfe..18155d9d45 100644 --- a/src/calls.rs +++ b/src/calls.rs @@ -114,6 +114,7 @@ impl Context { chat.id.accept(self).await?; } + call.update_text(self, "Call accepted").await?; call.msg .mark_call_as_accepted(self, accept_call_info.to_string()) .await?; @@ -125,6 +126,7 @@ impl Context { ..Default::default() }; msg.param.set_cmd(SystemMessage::CallAccepted); + msg.hidden = true; msg.param .set(Param::WebrtcAccepted, accept_call_info.to_string()); msg.set_quote(self, Some(&call.msg)).await?; @@ -133,6 +135,7 @@ impl Context { msg_id: call.msg.id, accept_call_info, }); + self.emit_msgs_changed(call.msg.chat_id, call_id); Ok(()) } @@ -140,6 +143,8 @@ impl Context { pub async fn end_call(&self, call_id: MsgId) -> Result<()> { let call: CallInfo = self.load_call_by_id(call_id).await?; + call.update_text(self, "Call ended").await?; + if call.is_accepted || !call.is_incoming { let mut msg = Message { viewtype: Viewtype::Text, @@ -147,6 +152,7 @@ impl Context { ..Default::default() }; msg.param.set_cmd(SystemMessage::CallEnded); + msg.hidden = true; msg.set_quote(self, Some(&call.msg)).await?; msg.id = send_msg(self, call.msg.chat_id, &mut msg).await?; } else if call.is_incoming { @@ -161,6 +167,7 @@ impl Context { self.emit_event(EventType::CallEnded { msg_id: call.msg.id, }); + self.emit_msgs_changed(call.msg.chat_id, call_id); Ok(()) } @@ -189,9 +196,9 @@ impl Context { if call.is_incoming { if call.is_stale_call() { call.update_text(self, "Missed call").await?; - self.emit_incoming_msg(call.msg.chat_id, call_id); + self.emit_incoming_msg(call.msg.chat_id, call_id); // notify missed call } else { - self.emit_msgs_changed(call.msg.chat_id, call_id); + self.emit_msgs_changed(call.msg.chat_id, call_id); // ringing calls are not additionally notified self.emit_event(EventType::IncomingCall { msg_id: call.msg.id, place_call_info: call.place_call_info.to_string(), @@ -210,6 +217,7 @@ impl Context { match mime_message.is_system_message { SystemMessage::CallAccepted => { let call = self.load_call_by_id(call_id).await?; + call.update_text(self, "Call accepted").await?; self.emit_msgs_changed(call.msg.chat_id, call_id); if call.is_incoming { self.emit_event(EventType::IncomingCallAccepted { @@ -232,6 +240,7 @@ impl Context { } SystemMessage::CallEnded => { let call = self.load_call_by_id(call_id).await?; + call.update_text(self, "Call ended").await?; self.emit_msgs_changed(call.msg.chat_id, call_id); self.emit_event(EventType::CallEnded { msg_id: call.msg.id, @@ -245,7 +254,10 @@ impl Context { pub(crate) async fn sync_call_rejection(&self, rfc724_mid: &str) -> Result<()> { if let Some((msg_id, _)) = rfc724_mid_exists(self, rfc724_mid).await? { + let call = self.load_call_by_id(msg_id).await?; + call.update_text(self, "Call ended").await?; self.emit_event(EventType::CallEnded { msg_id }); + self.emit_msgs_changed(call.msg.chat_id, msg_id); } Ok(()) } diff --git a/src/calls/calls_tests.rs b/src/calls/calls_tests.rs index 0e5eb14180..4c15666b9f 100644 --- a/src/calls/calls_tests.rs +++ b/src/calls/calls_tests.rs @@ -6,6 +6,7 @@ struct CallSetup { pub alice: TestContext, pub alice2: TestContext, pub alice_call: Message, + pub alice2_call: Message, pub bob: TestContext, pub bob2: TestContext, pub bob_call: Message, @@ -61,6 +62,7 @@ async fn setup_call() -> Result { alice, alice2, alice_call, + alice2_call, bob, bob2, bob_call, @@ -73,13 +75,14 @@ async fn accept_call() -> Result { alice, alice2, alice_call, + alice2_call, bob, bob2, bob_call, bob2_call, } = setup_call().await?; - // Bob accepts the incoming call, this does not add an additional message to the chat + // Bob accepts the incoming call bob.accept_incoming_call(bob_call.id, "accepted_info".to_string()) .await?; bob.evtracker @@ -91,9 +94,11 @@ async fn accept_call() -> Result { assert_eq!(info.place_call_info, "place_info"); assert_eq!(info.accept_call_info, "accepted_info"); - let bob_accept_msg = bob2.recv_msg(&sent2).await; - assert!(bob_accept_msg.is_info()); - assert_eq!(bob_accept_msg.get_info_type(), SystemMessage::CallAccepted); + bob2.recv_msg_trash(&sent2).await; + assert_eq!( + Message::load_from_db(&bob, bob_call.id).await?.text, + "Call accepted" + ); bob2.evtracker .get_matching(|evt| matches!(evt, EventType::IncomingCallAccepted { .. })) .await; @@ -101,11 +106,10 @@ async fn accept_call() -> Result { assert!(!info.is_accepted); // "accepted" is only true on the device that does the call // Alice receives the acceptance message - let alice_accept_msg = alice.recv_msg(&sent2).await; - assert!(alice_accept_msg.is_info()); + alice.recv_msg_trash(&sent2).await; assert_eq!( - alice_accept_msg.get_info_type(), - SystemMessage::CallAccepted + Message::load_from_db(&alice, alice_call.id).await?.text, + "Call accepted" ); alice .evtracker @@ -116,11 +120,10 @@ async fn accept_call() -> Result { assert_eq!(info.place_call_info, "place_info"); assert_eq!(info.accept_call_info, "accepted_info"); - let alice2_accept_msg = alice2.recv_msg(&sent2).await; - assert!(alice2_accept_msg.is_info()); + alice2.recv_msg_trash(&sent2).await; assert_eq!( - alice2_accept_msg.get_info_type(), - SystemMessage::CallAccepted + Message::load_from_db(&alice2, alice2_call.id).await?.text, + "Call accepted" ); alice2 .evtracker @@ -131,6 +134,7 @@ async fn accept_call() -> Result { alice, alice2, alice_call, + alice2_call, bob, bob2, bob_call, @@ -138,9 +142,9 @@ async fn accept_call() -> Result { }) } -fn assert_is_call_ended_info_msg(msg: Message) { - assert!(msg.is_info()); - assert_eq!(msg.get_info_type(), SystemMessage::CallEnded); +async fn assert_is_call_ended(t: &TestContext, call_id: MsgId) -> Result<()> { + assert_eq!(Message::load_from_db(t, call_id).await?.text, "Call ended"); + Ok(()) } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -148,36 +152,40 @@ async fn test_accept_call_callee_ends() -> Result<()> { // Alice calls Bob, Bob accepts let CallSetup { alice, + alice_call, alice2, + alice2_call, bob, bob2, bob_call, + bob2_call, .. } = accept_call().await?; // Bob has accepted the call and also ends it bob.end_call(bob_call.id).await?; + assert_is_call_ended(&bob, bob_call.id).await?; bob.evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; let sent3 = bob.pop_sent_msg().await; - let bob2_end_call_msg = bob2.recv_msg(&sent3).await; - assert_is_call_ended_info_msg(bob2_end_call_msg); + bob2.recv_msg_trash(&sent3).await; + assert_is_call_ended(&bob2, bob2_call.id).await?; bob2.evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; // Alice receives the ending message - let alice_end_call_msg = alice.recv_msg(&sent3).await; - assert_is_call_ended_info_msg(alice_end_call_msg); + alice.recv_msg_trash(&sent3).await; + assert_is_call_ended(&alice, alice_call.id).await?; alice .evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; - let alice2_end_call_msg = alice2.recv_msg(&sent3).await; - assert_is_call_ended_info_msg(alice2_end_call_msg); + alice2.recv_msg_trash(&sent3).await; + assert_is_call_ended(&alice2, alice2_call.id).await?; alice2 .evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) @@ -192,9 +200,11 @@ async fn test_accept_call_caller_ends() -> Result<()> { let CallSetup { alice, alice2, + alice2_call, bob, bob2, bob_call, + bob2_call, .. } = accept_call().await?; @@ -206,22 +216,22 @@ async fn test_accept_call_caller_ends() -> Result<()> { .await; let sent3 = alice.pop_sent_msg().await; - let alice2_end_call_msg = alice2.recv_msg(&sent3).await; - assert_is_call_ended_info_msg(alice2_end_call_msg); + alice2.recv_msg_trash(&sent3).await; + assert_is_call_ended(&alice2, alice2_call.id).await?; alice2 .evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; // Bob receives the ending message - let bob_end_call_msg = bob.recv_msg(&sent3).await; - assert_is_call_ended_info_msg(bob_end_call_msg); + bob.recv_msg_trash(&sent3).await; + assert_is_call_ended(&bob, bob_call.id).await?; bob.evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; - let bob2_end_call_msg = bob2.recv_msg(&sent3).await; - assert_is_call_ended_info_msg(bob2_end_call_msg); + bob2.recv_msg_trash(&sent3).await; + assert_is_call_ended(&bob2, bob2_call.id).await?; bob2.evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; @@ -236,6 +246,7 @@ async fn test_callee_rejects_call() -> Result<()> { bob, bob2, bob_call, + bob2_call, .. } = setup_call().await?; @@ -243,11 +254,13 @@ async fn test_callee_rejects_call() -> Result<()> { // To protect Bob's privacy, no message is sent to Alice (who will time out). // To let Bob close the call window on all devices, a sync message is used instead. bob.end_call(bob_call.id).await?; + assert_is_call_ended(&bob, bob_call.id).await?; bob.evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; sync(&bob, &bob2).await; + assert_is_call_ended(&bob2, bob2_call.id).await?; bob2.evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; @@ -262,35 +275,39 @@ async fn test_caller_cancels_call() -> Result<()> { alice, alice2, alice_call, + alice2_call, bob, bob2, + bob_call, + bob2_call, .. } = setup_call().await?; // Alice changes their mind before Bob picks up alice.end_call(alice_call.id).await?; + assert_is_call_ended(&alice, alice_call.id).await?; alice .evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; let sent3 = alice.pop_sent_msg().await; - let alice2_call_ended_msg = alice2.recv_msg(&sent3).await; - assert_is_call_ended_info_msg(alice2_call_ended_msg); + alice2.recv_msg_trash(&sent3).await; + assert_is_call_ended(&alice2, alice2_call.id).await?; alice2 .evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; // Bob receives the ending message - let bob_call_ended_msg = bob.recv_msg(&sent3).await; - assert_is_call_ended_info_msg(bob_call_ended_msg); + bob.recv_msg_trash(&sent3).await; + assert_is_call_ended(&bob, bob_call.id).await?; bob.evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; - let bob2_call_ended_msg = bob2.recv_msg(&sent3).await; - assert_is_call_ended_info_msg(bob2_call_ended_msg); + bob2.recv_msg_trash(&sent3).await; + assert_is_call_ended(&bob2, bob2_call.id).await?; bob2.evtracker .get_matching(|evt| matches!(evt, EventType::CallEnded { .. })) .await; diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 6331aa9377..d247d1123f 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -1153,6 +1153,11 @@ async fn decide_chat_assignment( { info!(context, "Chat edit/delete/iroh/sync message (TRASH)."); true + } else if mime_parser.is_system_message == SystemMessage::CallAccepted + || mime_parser.is_system_message == SystemMessage::CallEnded + { + info!(context, "Call state changed (TRASH)."); + true } else if mime_parser.decrypting_failed && !mime_parser.incoming { // Outgoing undecryptable message. let last_time = context