diff --git a/src/app/core/services/webrtc.service.ts b/src/app/core/services/webrtc.service.ts index 8f95ada..5892dde 100644 --- a/src/app/core/services/webrtc.service.ts +++ b/src/app/core/services/webrtc.service.ts @@ -464,23 +464,37 @@ export class WebRTCService implements OnDestroy { } const existing = this.peerManager.activePeerConnections.get(user.oderId); - const healthy = this.isPeerHealthy(existing); - if (existing && !healthy) { - this.logger.info('Removing stale peer before recreate', { oderId: user.oderId }); + if (this.canReusePeerConnection(existing)) { + this.logger.info('Reusing active peer connection', { + connectionState: existing?.connection.connectionState ?? 'unknown', + dataChannelState: existing?.dataChannel?.readyState ?? 'missing', + oderId: user.oderId, + serverId: message.serverId, + signalUrl + }); + continue; + } + + if (existing) { + this.logger.info('Removing failed peer before recreate', { + connectionState: existing.connection.connectionState, + dataChannelState: existing.dataChannel?.readyState ?? 'missing', + oderId: user.oderId, + serverId: message.serverId, + signalUrl + }); this.peerManager.removePeer(user.oderId); } - if (healthy) - continue; - this.logger.info('Create peer connection to existing user', { oderId: user.oderId, - serverId: message.serverId + serverId: message.serverId, + signalUrl }); this.peerManager.createPeerConnection(user.oderId, true); - this.peerManager.createAndSendOffer(user.oderId); + void this.peerManager.createAndSendOffer(user.oderId); } } @@ -1282,15 +1296,14 @@ export class WebRTCService implements OnDestroy { this._isDeafened.set(this.mediaManager.getIsSelfDeafened()); } - /** Returns true if a peer connection exists and its data channel is open. */ - private isPeerHealthy(peer: import('./webrtc').PeerData | undefined): boolean { + /** Returns true if a peer connection is still alive enough to finish negotiating. */ + private canReusePeerConnection(peer: import('./webrtc').PeerData | undefined): boolean { if (!peer) return false; const connState = peer.connection?.connectionState; - const dcState = peer.dataChannel?.readyState; - return connState === 'connected' && dcState === 'open'; + return connState !== 'closed' && connState !== 'failed'; } private handlePeerControlMessage(event: ChatEvent): void { diff --git a/src/app/features/voice/floating-voice-controls/floating-voice-controls.component.ts b/src/app/features/voice/floating-voice-controls/floating-voice-controls.component.ts index 211575d..cc5fd5b 100644 --- a/src/app/features/voice/floating-voice-controls/floating-voice-controls.component.ts +++ b/src/app/features/voice/floating-voice-controls/floating-voice-controls.component.ts @@ -69,18 +69,17 @@ export class FloatingVoiceControlsComponent implements OnInit { isConnected = computed(() => this.webrtcService.isVoiceConnected()); isMuted = signal(false); isDeafened = signal(false); - isScreenSharing = signal(false); + isScreenSharing = this.webrtcService.isScreenSharing; includeSystemAudio = signal(false); screenShareQuality = signal('balanced'); askScreenShareQuality = signal(true); showScreenShareQualityDialog = signal(false); - /** Sync local mute/deafen/screen-share state from the WebRTC service on init. */ + /** Sync local mute/deafen state from the WebRTC service on init. */ ngOnInit(): void { // Sync mute/deafen state from webrtc service this.isMuted.set(this.webrtcService.isMuted()); this.isDeafened.set(this.webrtcService.isDeafened()); - this.isScreenSharing.set(this.webrtcService.isScreenSharing()); this.syncScreenShareSettings(); const settings = loadVoiceSettingsFromStorage(); @@ -145,7 +144,6 @@ export class FloatingVoiceControlsComponent implements OnInit { async toggleScreenShare(): Promise { if (this.isScreenSharing()) { this.webrtcService.stopScreenShare(); - this.isScreenSharing.set(false); } else { this.syncScreenShareSettings(); @@ -214,7 +212,6 @@ export class FloatingVoiceControlsComponent implements OnInit { this.voiceSessionService.endSession(); // Reset local state - this.isScreenSharing.set(false); this.isMuted.set(false); this.isDeafened.set(false); } @@ -288,8 +285,6 @@ export class FloatingVoiceControlsComponent implements OnInit { includeSystemAudio: this.includeSystemAudio(), quality }); - - this.isScreenSharing.set(true); } catch (_error) { // Screen share request was denied or failed } diff --git a/src/app/features/voice/voice-controls/voice-controls.component.ts b/src/app/features/voice/voice-controls/voice-controls.component.ts index 565c5e1..2d163a1 100644 --- a/src/app/features/voice/voice-controls/voice-controls.component.ts +++ b/src/app/features/voice/voice-controls/voice-controls.component.ts @@ -84,7 +84,7 @@ export class VoiceControlsComponent implements OnInit, OnDestroy { connectionErrorMessage = computed(() => this.webrtcService.connectionErrorMessage()); isMuted = signal(false); isDeafened = signal(false); - isScreenSharing = signal(false); + isScreenSharing = this.webrtcService.isScreenSharing; showSettings = signal(false); inputDevices = signal([]); @@ -286,7 +286,6 @@ export class VoiceControlsComponent implements OnInit, OnDestroy { // End voice session for floating controls this.voiceSessionService.endSession(); - this.isScreenSharing.set(false); this.isMuted.set(false); this.isDeafened.set(false); } @@ -368,7 +367,6 @@ export class VoiceControlsComponent implements OnInit, OnDestroy { async toggleScreenShare(): Promise { if (this.isScreenSharing()) { this.webrtcService.stopScreenShare(); - this.isScreenSharing.set(false); } else { this.syncScreenShareSettings(); @@ -539,8 +537,6 @@ export class VoiceControlsComponent implements OnInit, OnDestroy { includeSystemAudio: this.includeSystemAudio(), quality }); - - this.isScreenSharing.set(true); } catch (_error) {} }