Skip to content

Commit b6ab13f

Browse files
authored
feat: hide call status change messages (#7175)
this PR uses the initial "call messages" (that has a separate viewtype since #7174) to show all call status. this is what most other messengers are doing as well. additional "info messages" after a call are no longer needed. on the wire, as we cannot pickpack on visible info messages, we use hidden messages, similar to eg. webxdc status updates. in future PR, it is planned to allow getting call state as a json, so that UI can render nicely. it is then decided if we want to translate the strings in the core. <img width="320" alt="IMG_0150" src="https://github.com/user-attachments/assets/41ee3fa3-8be4-42c3-8dd9-d20f49881650" /> successor of #6650
1 parent 53a3e51 commit b6ab13f

File tree

3 files changed

+69
-35
lines changed

3 files changed

+69
-35
lines changed

src/calls.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ impl Context {
114114
chat.id.accept(self).await?;
115115
}
116116

117+
call.update_text(self, "Call accepted").await?;
117118
call.msg
118119
.mark_call_as_accepted(self, accept_call_info.to_string())
119120
.await?;
@@ -125,6 +126,7 @@ impl Context {
125126
..Default::default()
126127
};
127128
msg.param.set_cmd(SystemMessage::CallAccepted);
129+
msg.hidden = true;
128130
msg.param
129131
.set(Param::WebrtcAccepted, accept_call_info.to_string());
130132
msg.set_quote(self, Some(&call.msg)).await?;
@@ -133,20 +135,24 @@ impl Context {
133135
msg_id: call.msg.id,
134136
accept_call_info,
135137
});
138+
self.emit_msgs_changed(call.msg.chat_id, call_id);
136139
Ok(())
137140
}
138141

139142
/// Cancel, reject or hangup an incoming or outgoing call.
140143
pub async fn end_call(&self, call_id: MsgId) -> Result<()> {
141144
let call: CallInfo = self.load_call_by_id(call_id).await?;
142145

146+
call.update_text(self, "Call ended").await?;
147+
143148
if call.is_accepted || !call.is_incoming {
144149
let mut msg = Message {
145150
viewtype: Viewtype::Text,
146151
text: "Call ended".into(),
147152
..Default::default()
148153
};
149154
msg.param.set_cmd(SystemMessage::CallEnded);
155+
msg.hidden = true;
150156
msg.set_quote(self, Some(&call.msg)).await?;
151157
msg.id = send_msg(self, call.msg.chat_id, &mut msg).await?;
152158
} else if call.is_incoming {
@@ -161,6 +167,7 @@ impl Context {
161167
self.emit_event(EventType::CallEnded {
162168
msg_id: call.msg.id,
163169
});
170+
self.emit_msgs_changed(call.msg.chat_id, call_id);
164171
Ok(())
165172
}
166173

@@ -189,9 +196,9 @@ impl Context {
189196
if call.is_incoming {
190197
if call.is_stale_call() {
191198
call.update_text(self, "Missed call").await?;
192-
self.emit_incoming_msg(call.msg.chat_id, call_id);
199+
self.emit_incoming_msg(call.msg.chat_id, call_id); // notify missed call
193200
} else {
194-
self.emit_msgs_changed(call.msg.chat_id, call_id);
201+
self.emit_msgs_changed(call.msg.chat_id, call_id); // ringing calls are not additionally notified
195202
self.emit_event(EventType::IncomingCall {
196203
msg_id: call.msg.id,
197204
place_call_info: call.place_call_info.to_string(),
@@ -210,6 +217,7 @@ impl Context {
210217
match mime_message.is_system_message {
211218
SystemMessage::CallAccepted => {
212219
let call = self.load_call_by_id(call_id).await?;
220+
call.update_text(self, "Call accepted").await?;
213221
self.emit_msgs_changed(call.msg.chat_id, call_id);
214222
if call.is_incoming {
215223
self.emit_event(EventType::IncomingCallAccepted {
@@ -232,6 +240,7 @@ impl Context {
232240
}
233241
SystemMessage::CallEnded => {
234242
let call = self.load_call_by_id(call_id).await?;
243+
call.update_text(self, "Call ended").await?;
235244
self.emit_msgs_changed(call.msg.chat_id, call_id);
236245
self.emit_event(EventType::CallEnded {
237246
msg_id: call.msg.id,
@@ -245,7 +254,10 @@ impl Context {
245254

246255
pub(crate) async fn sync_call_rejection(&self, rfc724_mid: &str) -> Result<()> {
247256
if let Some((msg_id, _)) = rfc724_mid_exists(self, rfc724_mid).await? {
257+
let call = self.load_call_by_id(msg_id).await?;
258+
call.update_text(self, "Call ended").await?;
248259
self.emit_event(EventType::CallEnded { msg_id });
260+
self.emit_msgs_changed(call.msg.chat_id, msg_id);
249261
}
250262
Ok(())
251263
}

src/calls/calls_tests.rs

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ struct CallSetup {
66
pub alice: TestContext,
77
pub alice2: TestContext,
88
pub alice_call: Message,
9+
pub alice2_call: Message,
910
pub bob: TestContext,
1011
pub bob2: TestContext,
1112
pub bob_call: Message,
@@ -61,6 +62,7 @@ async fn setup_call() -> Result<CallSetup> {
6162
alice,
6263
alice2,
6364
alice_call,
65+
alice2_call,
6466
bob,
6567
bob2,
6668
bob_call,
@@ -73,13 +75,14 @@ async fn accept_call() -> Result<CallSetup> {
7375
alice,
7476
alice2,
7577
alice_call,
78+
alice2_call,
7679
bob,
7780
bob2,
7881
bob_call,
7982
bob2_call,
8083
} = setup_call().await?;
8184

82-
// Bob accepts the incoming call, this does not add an additional message to the chat
85+
// Bob accepts the incoming call
8386
bob.accept_incoming_call(bob_call.id, "accepted_info".to_string())
8487
.await?;
8588
bob.evtracker
@@ -91,21 +94,22 @@ async fn accept_call() -> Result<CallSetup> {
9194
assert_eq!(info.place_call_info, "place_info");
9295
assert_eq!(info.accept_call_info, "accepted_info");
9396

94-
let bob_accept_msg = bob2.recv_msg(&sent2).await;
95-
assert!(bob_accept_msg.is_info());
96-
assert_eq!(bob_accept_msg.get_info_type(), SystemMessage::CallAccepted);
97+
bob2.recv_msg_trash(&sent2).await;
98+
assert_eq!(
99+
Message::load_from_db(&bob, bob_call.id).await?.text,
100+
"Call accepted"
101+
);
97102
bob2.evtracker
98103
.get_matching(|evt| matches!(evt, EventType::IncomingCallAccepted { .. }))
99104
.await;
100105
let info = bob2.load_call_by_id(bob2_call.id).await?;
101106
assert!(!info.is_accepted); // "accepted" is only true on the device that does the call
102107

103108
// Alice receives the acceptance message
104-
let alice_accept_msg = alice.recv_msg(&sent2).await;
105-
assert!(alice_accept_msg.is_info());
109+
alice.recv_msg_trash(&sent2).await;
106110
assert_eq!(
107-
alice_accept_msg.get_info_type(),
108-
SystemMessage::CallAccepted
111+
Message::load_from_db(&alice, alice_call.id).await?.text,
112+
"Call accepted"
109113
);
110114
alice
111115
.evtracker
@@ -116,11 +120,10 @@ async fn accept_call() -> Result<CallSetup> {
116120
assert_eq!(info.place_call_info, "place_info");
117121
assert_eq!(info.accept_call_info, "accepted_info");
118122

119-
let alice2_accept_msg = alice2.recv_msg(&sent2).await;
120-
assert!(alice2_accept_msg.is_info());
123+
alice2.recv_msg_trash(&sent2).await;
121124
assert_eq!(
122-
alice2_accept_msg.get_info_type(),
123-
SystemMessage::CallAccepted
125+
Message::load_from_db(&alice2, alice2_call.id).await?.text,
126+
"Call accepted"
124127
);
125128
alice2
126129
.evtracker
@@ -131,53 +134,58 @@ async fn accept_call() -> Result<CallSetup> {
131134
alice,
132135
alice2,
133136
alice_call,
137+
alice2_call,
134138
bob,
135139
bob2,
136140
bob_call,
137141
bob2_call,
138142
})
139143
}
140144

141-
fn assert_is_call_ended_info_msg(msg: Message) {
142-
assert!(msg.is_info());
143-
assert_eq!(msg.get_info_type(), SystemMessage::CallEnded);
145+
async fn assert_is_call_ended(t: &TestContext, call_id: MsgId) -> Result<()> {
146+
assert_eq!(Message::load_from_db(t, call_id).await?.text, "Call ended");
147+
Ok(())
144148
}
145149

146150
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
147151
async fn test_accept_call_callee_ends() -> Result<()> {
148152
// Alice calls Bob, Bob accepts
149153
let CallSetup {
150154
alice,
155+
alice_call,
151156
alice2,
157+
alice2_call,
152158
bob,
153159
bob2,
154160
bob_call,
161+
bob2_call,
155162
..
156163
} = accept_call().await?;
157164

158165
// Bob has accepted the call and also ends it
159166
bob.end_call(bob_call.id).await?;
167+
assert_is_call_ended(&bob, bob_call.id).await?;
160168
bob.evtracker
161169
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
162170
.await;
163171
let sent3 = bob.pop_sent_msg().await;
164172

165-
let bob2_end_call_msg = bob2.recv_msg(&sent3).await;
166-
assert_is_call_ended_info_msg(bob2_end_call_msg);
173+
bob2.recv_msg_trash(&sent3).await;
174+
assert_is_call_ended(&bob2, bob2_call.id).await?;
167175
bob2.evtracker
168176
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
169177
.await;
170178

171179
// Alice receives the ending message
172-
let alice_end_call_msg = alice.recv_msg(&sent3).await;
173-
assert_is_call_ended_info_msg(alice_end_call_msg);
180+
alice.recv_msg_trash(&sent3).await;
181+
assert_is_call_ended(&alice, alice_call.id).await?;
174182
alice
175183
.evtracker
176184
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
177185
.await;
178186

179-
let alice2_end_call_msg = alice2.recv_msg(&sent3).await;
180-
assert_is_call_ended_info_msg(alice2_end_call_msg);
187+
alice2.recv_msg_trash(&sent3).await;
188+
assert_is_call_ended(&alice2, alice2_call.id).await?;
181189
alice2
182190
.evtracker
183191
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
@@ -192,9 +200,11 @@ async fn test_accept_call_caller_ends() -> Result<()> {
192200
let CallSetup {
193201
alice,
194202
alice2,
203+
alice2_call,
195204
bob,
196205
bob2,
197206
bob_call,
207+
bob2_call,
198208
..
199209
} = accept_call().await?;
200210

@@ -206,22 +216,22 @@ async fn test_accept_call_caller_ends() -> Result<()> {
206216
.await;
207217
let sent3 = alice.pop_sent_msg().await;
208218

209-
let alice2_end_call_msg = alice2.recv_msg(&sent3).await;
210-
assert_is_call_ended_info_msg(alice2_end_call_msg);
219+
alice2.recv_msg_trash(&sent3).await;
220+
assert_is_call_ended(&alice2, alice2_call.id).await?;
211221
alice2
212222
.evtracker
213223
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
214224
.await;
215225

216226
// Bob receives the ending message
217-
let bob_end_call_msg = bob.recv_msg(&sent3).await;
218-
assert_is_call_ended_info_msg(bob_end_call_msg);
227+
bob.recv_msg_trash(&sent3).await;
228+
assert_is_call_ended(&bob, bob_call.id).await?;
219229
bob.evtracker
220230
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
221231
.await;
222232

223-
let bob2_end_call_msg = bob2.recv_msg(&sent3).await;
224-
assert_is_call_ended_info_msg(bob2_end_call_msg);
233+
bob2.recv_msg_trash(&sent3).await;
234+
assert_is_call_ended(&bob2, bob2_call.id).await?;
225235
bob2.evtracker
226236
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
227237
.await;
@@ -236,18 +246,21 @@ async fn test_callee_rejects_call() -> Result<()> {
236246
bob,
237247
bob2,
238248
bob_call,
249+
bob2_call,
239250
..
240251
} = setup_call().await?;
241252

242253
// Bob does not want to talk with Alice.
243254
// To protect Bob's privacy, no message is sent to Alice (who will time out).
244255
// To let Bob close the call window on all devices, a sync message is used instead.
245256
bob.end_call(bob_call.id).await?;
257+
assert_is_call_ended(&bob, bob_call.id).await?;
246258
bob.evtracker
247259
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
248260
.await;
249261

250262
sync(&bob, &bob2).await;
263+
assert_is_call_ended(&bob2, bob2_call.id).await?;
251264
bob2.evtracker
252265
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
253266
.await;
@@ -262,35 +275,39 @@ async fn test_caller_cancels_call() -> Result<()> {
262275
alice,
263276
alice2,
264277
alice_call,
278+
alice2_call,
265279
bob,
266280
bob2,
281+
bob_call,
282+
bob2_call,
267283
..
268284
} = setup_call().await?;
269285

270286
// Alice changes their mind before Bob picks up
271287
alice.end_call(alice_call.id).await?;
288+
assert_is_call_ended(&alice, alice_call.id).await?;
272289
alice
273290
.evtracker
274291
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
275292
.await;
276293
let sent3 = alice.pop_sent_msg().await;
277294

278-
let alice2_call_ended_msg = alice2.recv_msg(&sent3).await;
279-
assert_is_call_ended_info_msg(alice2_call_ended_msg);
295+
alice2.recv_msg_trash(&sent3).await;
296+
assert_is_call_ended(&alice2, alice2_call.id).await?;
280297
alice2
281298
.evtracker
282299
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
283300
.await;
284301

285302
// Bob receives the ending message
286-
let bob_call_ended_msg = bob.recv_msg(&sent3).await;
287-
assert_is_call_ended_info_msg(bob_call_ended_msg);
303+
bob.recv_msg_trash(&sent3).await;
304+
assert_is_call_ended(&bob, bob_call.id).await?;
288305
bob.evtracker
289306
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
290307
.await;
291308

292-
let bob2_call_ended_msg = bob2.recv_msg(&sent3).await;
293-
assert_is_call_ended_info_msg(bob2_call_ended_msg);
309+
bob2.recv_msg_trash(&sent3).await;
310+
assert_is_call_ended(&bob2, bob2_call.id).await?;
294311
bob2.evtracker
295312
.get_matching(|evt| matches!(evt, EventType::CallEnded { .. }))
296313
.await;

src/receive_imf.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,11 @@ async fn decide_chat_assignment(
11531153
{
11541154
info!(context, "Chat edit/delete/iroh/sync message (TRASH).");
11551155
true
1156+
} else if mime_parser.is_system_message == SystemMessage::CallAccepted
1157+
|| mime_parser.is_system_message == SystemMessage::CallEnded
1158+
{
1159+
info!(context, "Call state changed (TRASH).");
1160+
true
11561161
} else if mime_parser.decrypting_failed && !mime_parser.incoming {
11571162
// Outgoing undecryptable message.
11581163
let last_time = context

0 commit comments

Comments
 (0)