diff --git a/src/peer/answering-state.ts b/src/peer/answering-state.ts index ac1868b..600bfc0 100644 --- a/src/peer/answering-state.ts +++ b/src/peer/answering-state.ts @@ -6,8 +6,6 @@ import type RondevuPeer from './index.js'; * Answering an offer and sending to server */ export class AnsweringState extends PeerState { - private iceCandidateHandler?: (event: RTCPeerConnectionIceEvent) => void; - constructor(peer: RondevuPeer) { super(peer); } @@ -33,19 +31,7 @@ export class AnsweringState extends PeerState { await this.peer.offersApi.answer(offerId, answer.sdp!); // Enable trickle ICE - send candidates as they arrive - this.iceCandidateHandler = async (event: RTCPeerConnectionIceEvent) => { - if (event.candidate && offerId) { - const candidateData = event.candidate.toJSON(); - if (candidateData.candidate && candidateData.candidate !== '') { - try { - await this.peer.offersApi.addIceCandidates(offerId, [candidateData]); - } catch (err) { - console.error('Error sending ICE candidate:', err); - } - } - } - }; - this.peer.pc.addEventListener('icecandidate', this.iceCandidateHandler); + this.setupIceCandidateHandler(offerId); // Transition to exchanging ICE const { ExchangingIceState } = await import('./exchanging-ice-state.js'); @@ -56,10 +42,4 @@ export class AnsweringState extends PeerState { throw error; } } - - cleanup(): void { - if (this.iceCandidateHandler) { - this.peer.pc.removeEventListener('icecandidate', this.iceCandidateHandler); - } - } } diff --git a/src/peer/creating-offer-state.ts b/src/peer/creating-offer-state.ts index 4346ba8..b6b4e81 100644 --- a/src/peer/creating-offer-state.ts +++ b/src/peer/creating-offer-state.ts @@ -6,8 +6,6 @@ import type RondevuPeer from './index.js'; * Creating offer and sending to server */ export class CreatingOfferState extends PeerState { - private iceCandidateHandler?: (event: RTCPeerConnectionIceEvent) => void; - constructor(peer: RondevuPeer, private options: PeerOptions) { super(peer); } @@ -41,19 +39,7 @@ export class CreatingOfferState extends PeerState { this.peer.offerId = offerId; // Enable trickle ICE - send candidates as they arrive - this.iceCandidateHandler = async (event: RTCPeerConnectionIceEvent) => { - if (event.candidate && offerId) { - const candidateData = event.candidate.toJSON(); - if (candidateData.candidate && candidateData.candidate !== '') { - try { - await this.peer.offersApi.addIceCandidates(offerId, [candidateData]); - } catch (err) { - console.error('Error sending ICE candidate:', err); - } - } - } - }; - this.peer.pc.addEventListener('icecandidate', this.iceCandidateHandler); + this.setupIceCandidateHandler(offerId); // Transition to waiting for answer const { WaitingForAnswerState } = await import('./waiting-for-answer-state.js'); @@ -66,10 +52,4 @@ export class CreatingOfferState extends PeerState { throw error; } } - - cleanup(): void { - if (this.iceCandidateHandler) { - this.peer.pc.removeEventListener('icecandidate', this.iceCandidateHandler); - } - } } diff --git a/src/peer/state.ts b/src/peer/state.ts index f900d5d..6a27644 100644 --- a/src/peer/state.ts +++ b/src/peer/state.ts @@ -6,6 +6,8 @@ import type RondevuPeer from './index.js'; * Implements the State pattern for managing WebRTC connection lifecycle */ export abstract class PeerState { + protected iceCandidateHandler?: (event: RTCPeerConnectionIceEvent) => void; + constructor(protected peer: RondevuPeer) {} abstract get name(): string; @@ -29,8 +31,31 @@ export abstract class PeerState { } } + /** + * Setup trickle ICE candidate handler + * Sends local ICE candidates to server as they are discovered + */ + protected setupIceCandidateHandler(offerId: string): void { + this.iceCandidateHandler = async (event: RTCPeerConnectionIceEvent) => { + if (event.candidate && offerId) { + const candidateData = event.candidate.toJSON(); + if (candidateData.candidate && candidateData.candidate !== '') { + try { + await this.peer.offersApi.addIceCandidates(offerId, [candidateData]); + } catch (err) { + console.error('Error sending ICE candidate:', err); + } + } + } + }; + this.peer.pc.addEventListener('icecandidate', this.iceCandidateHandler); + } + cleanup(): void { - // Override in states that need cleanup + // Clean up ICE candidate handler if it exists + if (this.iceCandidateHandler) { + this.peer.pc.removeEventListener('icecandidate', this.iceCandidateHandler); + } } async close(): Promise {