fix: don't notify of missed call from blocked user

Closes https://github.com/chatmail/core/issues/8576.

The diff might look big, but it's only two things:
- move `can_call_me` one scope up
- replace `emit_incoming_msg` with `emit_msg_event`
  with `important = can_call_me`

I decided not to completely unify the `important` logic
with the other occurrence of `emit_msg_event()`
as I suggested in the issue yet.
That IMO should still be considered, but let's start simple.

Note that there is #7840 which may be closed by #7955,
which will basically supersede this MR.
I think, however, that it's OK to merge this one,
and then that one can just revert this one, including tests,
and rebase on top of the revert.
This commit is contained in:
WofWca
2026-08-28 13:11:09 +04:00
committed by GitHub
parent 693c404266
commit 0517beef61
2 changed files with 124 additions and 23 deletions

View File

@@ -360,15 +360,6 @@ impl Context {
};
if call.is_incoming() {
if call.is_stale() {
let missed_call_str = stock_str::missed_call(self);
call.update_text(self, &missed_call_str).await?;
self.emit_incoming_msg(call.msg.chat_id, call_id); // notify missed call
} else {
let incoming_call_str =
stock_str::incoming_call(self, call.has_video_initially());
call.update_text(self, &incoming_call_str).await?;
self.emit_msgs_changed(call.msg.chat_id, call_id); // ringing calls are not additionally notified
let can_call_me = match who_can_call_me(self).await? {
WhoCanCallMe::Contacts => ChatIdBlocked::lookup_by_contact(self, from_id)
.await?
@@ -390,6 +381,19 @@ impl Context {
.is_none_or(|chat_id_blocked| chat_id_blocked.blocked != Blocked::Yes),
WhoCanCallMe::Nobody => false,
};
if call.is_stale() {
let missed_call_str = stock_str::missed_call(self);
call.update_text(self, &missed_call_str).await?;
let important = can_call_me;
// notify missed call
call.msg
.chat_id
.emit_msg_event(self, call.msg.id, important);
} else {
let incoming_call_str =
stock_str::incoming_call(self, call.has_video_initially());
call.update_text(self, &incoming_call_str).await?;
self.emit_msgs_changed(call.msg.chat_id, call_id); // ringing calls are not additionally notified
if can_call_me {
self.emit_event(EventType::IncomingCall {
msg_id: call.msg.id,

View File

@@ -1,10 +1,14 @@
use super::*;
use crate::chat::forward_msgs;
use crate::config::Config;
use crate::contact::Contact;
use crate::message::MessageState;
use crate::receive_imf::receive_imf;
use crate::test_utils;
use crate::test_utils::{TestContext, TestContextManager};
use crate::test_utils::{
ExpectedEvents, TestContext, TestContextManager, TimeShiftFalsePositiveNote,
};
use crate::tools::SystemTime;
struct CallSetup {
pub alice: TestContext,
@@ -448,6 +452,99 @@ async fn test_callee_sees_contact_request_call() -> Result<()> {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_call_from_blocked_chat_normal() -> Result<()> {
let stale = false;
test_get_call_from_blocked_chat(stale).await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_call_from_blocked_chat_stale() -> Result<()> {
let stale = true;
test_get_call_from_blocked_chat(stale).await
}
async fn test_get_call_from_blocked_chat(stale: bool) -> Result<()> {
let _n = TimeShiftFalsePositiveNote;
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let alice_chat = alice.create_chat(bob).await;
let bob_chat = bob.create_chat(alice).await;
assert!(!bob_chat.is_contact_request());
Contact::block(bob, bob.add_or_lookup_contact_id(alice).await).await?;
let alice_msg_id = alice
.place_outgoing_call(alice_chat.id, PLACE_INFO.to_string(), true)
.await?;
let sent_start_call = alice.pop_sent_msg().await;
alice.end_call(alice_msg_id).await?;
let sent_end_call = alice.pop_sent_msg().await;
if stale {
SystemTime::shift(Duration::from_secs(5 * 60));
}
let bob_call = bob.recv_msg(&sent_start_call).await;
assert_eq!(
call_state(bob, bob_call.id).await?,
if stale {
CallState::Missed
} else {
CallState::Alerting
}
);
// Messages from blocked contacts are immediately marked as "seen".
assert_eq!(bob_call.id.get_state(bob).await?, MessageState::InSeen);
bob.evtracker
.get_matching_ext(
bob,
ExpectedEvents {
expected: |evt| matches!(evt, EventType::MsgsChanged { .. }),
unexpected: |evt| {
matches!(
evt,
EventType::IncomingMsg { .. }
| EventType::IncomingCall { .. }
| EventType::IncomingCallAccepted { .. }
| EventType::OutgoingCallAccepted { .. }
)
},
},
)
.await
.unwrap();
bob.recv_msg_trash(&sent_end_call).await;
assert_eq!(call_state(bob, bob_call.id).await?, CallState::Missed);
assert_eq!(bob_call.id.get_state(bob).await?, MessageState::InSeen);
bob.evtracker
.get_matching_ext(
bob,
ExpectedEvents {
expected: |evt| matches!(evt, EventType::CallEnded { .. }),
// No "missed call" notification or any other stuff.
unexpected: |evt| {
matches!(
evt,
EventType::IncomingMsg { .. }
| EventType::IncomingCall { .. }
| EventType::IncomingCallAccepted { .. }
| EventType::OutgoingCallAccepted { .. }
)
},
},
)
.await
.unwrap();
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_caller_cancels_call() -> Result<()> {
// Alice calls Bob