call: stop the ring on a user's other devices when one answers (answered-elsewhere)

A user signed in on two devices (iPhone + Android) rang on both. When they
answered on Android, the iPhone kept ringing: markDmAnswered only emitted a
WS 'call-taken', which never reaches a backgrounded/killed device ringing on
CallKit (no live socket).

Now markDmAnswered also pushes a native cancel to the answerer's OTHER
devices. The device that actually answered is skipped via exceptToken (its own
call-push token) — critical on iOS, where a cancel would end the just-answered
CallKit call; Android's cancel branch never tears down a live call anyway.

- push.sendCallCancel(userId, uuid, { exceptToken, reason }) — skips the
  answering device's token; existing 2-arg callers unchanged.
- calls.markDmAnswered(room, userId, exceptToken) — fires the cancel.
- POST /api/calls/answered now takes deviceToken.
- home.html: the native answerCall handler POSTs /api/calls/answered with this
  device's own token (ios-voip or fcm), so native/LiveKit calls (which bypass
  the mesh) are marked answered and siblings stop ringing.

Server + web only — no native build needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
SravanandClaude Opus 4.8 committed 2026-10-30 16:38:51 +05:30
1 parent aa794102c3
commit 08eff043ec
4 files changed
+36 -11

No files matched your search

+4 -2
View File
@@ -1120,9 +1120,11 @@ route('POST', '/api/calls/decline', async (req, res) => {
route('POST', '/api/calls/answered', async (req, res) => {
const u = await currentUser(req);
if (!u) return json(res, 401, { error: 'unauthorized' });
const { room } = await readBody(req);
const { room, deviceToken } = await readBody(req);
if (!room) return json(res, 400, { error: 'room required' });
try { CALLS.markDmAnswered(String(room), u.id); } catch (_) {}
// deviceToken = THIS device's own call-push token (ios-voip / android fcm). Passed so the answered-elsewhere
// cancel sent to the user's other devices skips the one that actually answered.
try { CALLS.markDmAnswered(String(room), u.id, deviceToken ? String(deviceToken) : undefined); } catch (_) {}
json(res, 200, { ok: true });
});
route('POST', '/api/calls/end', async (req, res) => {