diff --git a/client/public/version.js b/client/public/version.js index 118229c..94f4e3d 100644 --- a/client/public/version.js +++ b/client/public/version.js @@ -1,5 +1,5 @@ // Maintainer-controlled web client version. // Format: YYYY.MM.DD Rn (example: 2026.02.20 R2) -window.CHGRID_WEB_VERSION = "2026.02.25 R267"; +window.CHGRID_WEB_VERSION = "2026.02.25 R268"; // Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid. window.CHGRID_TIME_ZONE = "America/Detroit"; diff --git a/client/src/webrtc/peerManager.ts b/client/src/webrtc/peerManager.ts index 753bb8b..05efd40 100644 --- a/client/src/webrtc/peerManager.ts +++ b/client/src/webrtc/peerManager.ts @@ -4,7 +4,6 @@ import type { RemoteUser } from '../network/protocol'; export type PeerRuntime = SpatialPeerRuntime & { id: string; pc: RTCPeerConnection; - initiator: boolean; remoteStream?: MediaStream; }; @@ -39,7 +38,6 @@ export class PeerManager { const peer: PeerRuntime = { id: targetId, - initiator: isInitiator, nickname: userData.nickname ?? 'user...', x: userData.x ?? 20, y: userData.y ?? 20, @@ -119,9 +117,7 @@ export class PeerManager { if (!newTrack) { return; } - const peersToRenegotiate: PeerRuntime[] = []; for (const peer of this.peers.values()) { - let shouldRenegotiate = false; const sender = peer.pc.getSenders().find((candidate) => candidate.track?.kind === 'audio') ?? peer.pc @@ -130,35 +126,24 @@ export class PeerManager { ?.sender; if (!sender) { peer.pc.addTrack(newTrack, stream); - shouldRenegotiate = true; + await this.renegotiatePeer(peer); } else { - if (!sender.track) { - shouldRenegotiate = true; - } await sender.replaceTrack(newTrack); } - const audioTransceiver = peer.pc - .getTransceivers() - .find((transceiver) => transceiver.receiver.track?.kind === 'audio' || transceiver.sender.track?.kind === 'audio'); - if (audioTransceiver && (audioTransceiver.direction === 'recvonly' || audioTransceiver.direction === 'inactive')) { - audioTransceiver.direction = 'sendrecv'; - shouldRenegotiate = true; - } - if (shouldRenegotiate && peer.initiator) { - peersToRenegotiate.push(peer); - } } - for (const peer of peersToRenegotiate) { - if (peer.pc.connectionState === 'closed') continue; - if (peer.pc.signalingState !== 'stable') continue; - try { - let offer = await peer.pc.createOffer(); - offer = this.tuneOpus(offer); - await peer.pc.setLocalDescription(offer); - this.sendSignal(peer.id, { sdp: peer.pc.localDescription ?? undefined }); - } catch { - // Best-effort renegotiation; transport-level failures recover on subsequent signaling. - } + } + + /** Re-negotiate one peer connection after adding a new outbound track. */ + private async renegotiatePeer(peer: PeerRuntime): Promise { + if (peer.pc.connectionState === 'closed') return; + if (peer.pc.signalingState !== 'stable') return; + try { + let offer = await peer.pc.createOffer(); + offer = this.tuneOpus(offer); + await peer.pc.setLocalDescription(offer); + this.sendSignal(peer.id, { sdp: peer.pc.localDescription ?? undefined }); + } catch { + // Best-effort renegotiation; transport-level failures recover on subsequent signaling. } }