Merge pull request #2089 from vitorpamplona/claude/review-webrtc-calls-sAQqe

Improve call handling: busy rejection, peer tracking, and ICE state
This commit is contained in:
Vitor Pamplona
2026-04-02 23:01:46 -04:00
committed by GitHub
3 changed files with 22 additions and 7 deletions
@@ -112,7 +112,10 @@ class WebRtcCallSession(
}
PeerConnection.IceConnectionState.DISCONNECTED -> {
onDisconnected()
// DISCONNECTED is often transient (network switch, brief
// packet loss). WebRTC will transition to FAILED if
// recovery is impossible, so we only act on FAILED above.
Log.d(TAG) { "ICE disconnected (transient, waiting for recovery or FAILED)" }
}
else -> {}
@@ -110,7 +110,15 @@ class CallManager(
if (!isFollowing(callerPubKey)) return
if (_state.value !is CallState.Idle) return
if (_state.value !is CallState.Idle) {
// Already in a call — send a "busy" reject so the caller gets
// immediate feedback instead of waiting for the 60s timeout.
scope.launch {
val result = factory.createReject(callerPubKey, callId, "busy", signer = signer)
publishEvent(result.wrap)
}
return
}
val groupMembers = event.groupMembers()
@@ -341,12 +349,12 @@ class CallManager(
}
is CallState.Connecting -> {
peerPubKeys = current.peerPubKeys
peerPubKeys = current.peerPubKeys + current.pendingPeerPubKeys
callId = current.callId
}
is CallState.Connected -> {
peerPubKeys = current.peerPubKeys
peerPubKeys = current.allPeerPubKeys
callId = current.callId
}
@@ -157,13 +157,17 @@ class WebRtcCallFactory {
reason: String = "",
signer: NostrSigner,
): GroupResult {
val template = CallHangupEvent.build(peerPubKeys.first(), callId, reason)
val signed = signer.sign(template)
// Each peer gets its own signed hangup with the correct `p` tag
// targeting that specific recipient.
var firstSigned: Event? = null
val wraps =
peerPubKeys.map { pubKey ->
val template = CallHangupEvent.build(pubKey, callId, reason)
val signed = signer.sign(template)
if (firstSigned == null) firstSigned = signed
GiftWrapEvent.create(event = signed, recipientPubKey = pubKey, expirationDelta = WRAP_EXPIRATION_SECONDS)
}
return GroupResult(signed, wraps)
return GroupResult(firstSigned!!, wraps)
}
/**