diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/WebRtcCallSession.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/WebRtcCallSession.kt index 0d8b50e3c..e70549dfa 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/WebRtcCallSession.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/call/WebRtcCallSession.kt @@ -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 -> {} diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallManager.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallManager.kt index 724cab6a7..6e1a7969f 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallManager.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/call/CallManager.kt @@ -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 } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipACWebRtcCalls/WebRtcCallFactory.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipACWebRtcCalls/WebRtcCallFactory.kt index c517eb723..845d397ae 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipACWebRtcCalls/WebRtcCallFactory.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipACWebRtcCalls/WebRtcCallFactory.kt @@ -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) } /**