fix(calls): cancel the CallKit ring when the caller ends before answer

Bug: a killed/backgrounded callee is woken only for the CallKit ring and has no
WebSocket yet, so the existing dm-call active:false (WS-only) never reaches it and
it keeps ringing after the caller hangs up.

Fix: send a 'cancel' VoIP push on call teardown.
- push.js: sendCallCancel() sends a {type:'cancel',callUUID} VoIP push to the user's
  ios-voip tokens; invites now carry type:'invite'.
- calls.js: endDmCallByRoom + endGroupCallByRoom fire sendCallCancel to the rung users.
- NativeCallPlugin: on a cancel push, end the reported call (reportCall endedAt); if the
  invite was never seen, report-then-end to satisfy iOS's 'report a call per VoIP push'.

Server part deploys now; the plugin part needs the next Codemagic build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 11:09:07 +05:30
parent cd7ab74eed
commit 3e21aa30d7
3 changed files with 37 additions and 1 deletions
+4
View File
@@ -89,6 +89,8 @@ async function endGroupCallByRoom(room) {
let teamId = call.teamId; try { const g = await R.conversations.byId(group); if (g) { teamId = g.team_id; postSystem(group, g.team_id, '📞 Group call ended · ' + fmtDur(now() - call.startedAt)).catch(() => {}); } } catch (_) {}
if (call.historyId && teamId) { try { await R.scheduledMeetings.end(call.historyId, teamId); } catch (_) {} } // mark the history row past
broadcast(group, { type: 'group-call', group, active: false, room, uuid: call.uuid });
// Stop any CallKit ring on members' killed/backgrounded devices.
try { for (const mid of await R.conversations.members(group)) PUSH.sendCallCancel(mid, call.uuid); } catch (_) {}
}
}
@@ -142,6 +144,8 @@ async function endDmCallByRoom(room, silent) {
call.users.forEach((uid) => { try { CHAT.pushToUser(uid, { type: 'chat-message', message: dto }); } catch (_) {} });
} catch (_) {}
call.users.forEach((uid, i) => { try { CHAT.pushToUser(uid, { type: 'dm-call', active: false, uuid: call.uuid, with: call.users[1 - i], room }); } catch (_) {} });
// Stop any CallKit ring on a killed/backgrounded device (no WS to receive the dm-call above).
try { for (const uid of call.users) PUSH.sendCallCancel(uid, call.uuid); } catch (_) {}
}
// Mark a 1:1 call answered when the callee (anyone other than the caller) joins its room, so the end