Refactor: Send/receive ICE candidates as complete objects

- Update to send full RTCIceCandidateInit objects instead of partial data
- Simplify API by using JSON serialization
- Include usernameFragment field
- More maintainable and future-proof

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-14 19:38:41 +01:00
parent dd64a565aa
commit 9d9aba2cf5
2 changed files with 21 additions and 23 deletions

View File

@@ -1,4 +1,4 @@
import { RondevuOffers } from './offers.js'; import { RondevuOffers, RTCIceCandidateInit } from './offers.js';
/** /**
* Events emitted by RondevuConnection * Events emitted by RondevuConnection
@@ -60,11 +60,7 @@ export class RondevuConnection {
private lastIceTimestamp: number = Date.now(); private lastIceTimestamp: number = Date.now();
private eventListeners: Map<keyof RondevuConnectionEvents, Set<Function>> = new Map(); private eventListeners: Map<keyof RondevuConnectionEvents, Set<Function>> = new Map();
private dataChannel?: RTCDataChannel; private dataChannel?: RTCDataChannel;
private pendingIceCandidates: Array<{ private pendingIceCandidates: RTCIceCandidateInit[] = [];
candidate: string;
sdpMid: string | null;
sdpMLineIndex: number | null;
}> = [];
/** /**
* Current connection state * Current connection state
@@ -107,10 +103,12 @@ export class RondevuConnection {
private setupPeerConnection(): void { private setupPeerConnection(): void {
this.pc.onicecandidate = async (event) => { this.pc.onicecandidate = async (event) => {
if (event.candidate) { if (event.candidate) {
const candidateData = { // Convert RTCIceCandidate to RTCIceCandidateInit (plain object)
const candidateData: RTCIceCandidateInit = {
candidate: event.candidate.candidate, candidate: event.candidate.candidate,
sdpMid: event.candidate.sdpMid, sdpMid: event.candidate.sdpMid,
sdpMLineIndex: event.candidate.sdpMLineIndex, sdpMLineIndex: event.candidate.sdpMLineIndex,
usernameFragment: event.candidate.usernameFragment,
}; };
if (this.offerId) { if (this.offerId) {
@@ -282,14 +280,10 @@ export class RondevuConnection {
this.lastIceTimestamp this.lastIceTimestamp
); );
for (const candidate of candidates) { for (const cand of candidates) {
// Create ICE candidate with all fields // Use the candidate object directly - it's already RTCIceCandidateInit
await this.pc.addIceCandidate(new RTCIceCandidate({ await this.pc.addIceCandidate(new RTCIceCandidate(cand.candidate));
candidate: candidate.candidate, this.lastIceTimestamp = cand.createdAt;
sdpMid: candidate.sdpMid ?? undefined,
sdpMLineIndex: candidate.sdpMLineIndex ?? undefined,
}));
this.lastIceTimestamp = candidate.createdAt;
} }
} catch (err) { } catch (err) {
console.error('Error polling for ICE candidates:', err); console.error('Error polling for ICE candidates:', err);

View File

@@ -24,10 +24,18 @@ export interface Offer {
answeredAt?: number; answeredAt?: number;
} }
/**
* RTCIceCandidateInit interface for environments without native WebRTC types
*/
export interface RTCIceCandidateInit {
candidate?: string;
sdpMid?: string | null;
sdpMLineIndex?: number | null;
usernameFragment?: string | null;
}
export interface IceCandidate { export interface IceCandidate {
candidate: string; candidate: RTCIceCandidateInit; // Full candidate object
sdpMid: string | null;
sdpMLineIndex: number | null;
peerId: string; peerId: string;
role: 'offerer' | 'answerer'; role: 'offerer' | 'answerer';
createdAt: number; createdAt: number;
@@ -282,11 +290,7 @@ export class RondevuOffers {
*/ */
async addIceCandidates( async addIceCandidates(
offerId: string, offerId: string,
candidates: Array<{ candidates: RTCIceCandidateInit[]
candidate: string;
sdpMid?: string | null;
sdpMLineIndex?: number | null;
}>
): Promise<void> { ): Promise<void> {
const response = await this.fetchFn(`${this.baseUrl}/offers/${encodeURIComponent(offerId)}/ice-candidates`, { const response = await this.fetchFn(`${this.baseUrl}/offers/${encodeURIComponent(offerId)}/ice-candidates`, {
method: 'POST', method: 'POST',