diff --git a/src/lib/webrtc/signaling.ts b/src/lib/webrtc/signaling.ts index ab48542..f580bc4 100644 --- a/src/lib/webrtc/signaling.ts +++ b/src/lib/webrtc/signaling.ts @@ -8,23 +8,55 @@ export type SignalMessage = | { type: 'answer'; viewerId: string; sdp: RTCSessionDescriptionInit } | { type: 'ice'; viewerId: string; from: 'broadcaster' | 'viewer'; candidate: RTCIceCandidateInit }; +const readyChannels = new WeakMap>(); + export function joinSignalingChannel( code: string, onMessage: (msg: SignalMessage) => void, ): RealtimeChannel { + let resolveReady!: () => void; + let rejectReady!: (error: Error) => void; + const ready = new Promise((resolve, reject) => { + resolveReady = resolve; + rejectReady = reject; + }); + const channel = supabase.channel(`share:${code}`, { config: { broadcast: { self: false, ack: false } }, }); + readyChannels.set(channel, ready); + channel .on('broadcast', { event: 'signal' }, ({ payload }) => onMessage(payload as SignalMessage)) - .subscribe(); + .subscribe((status, err) => { + if (status === 'SUBSCRIBED') resolveReady(); + if (status === 'CHANNEL_ERROR' || status === 'TIMED_OUT' || status === 'CLOSED') { + rejectReady(err instanceof Error ? err : new Error(`Canal de sinalização: ${status}`)); + } + }); return channel; } -export function sendSignal(channel: RealtimeChannel, msg: SignalMessage) { - return channel.send({ type: 'broadcast', event: 'signal', payload: msg }); +export async function waitForSignalingReady(channel: RealtimeChannel, timeoutMs = 5000) { + const ready = readyChannels.get(channel); + if (!ready) return; + await Promise.race([ + ready, + new Promise((_, reject) => { + window.setTimeout(() => reject(new Error('Tempo esgotado ao conectar sinalização')), timeoutMs); + }), + ]); +} + +export async function sendSignal(channel: RealtimeChannel, msg: SignalMessage) { + await waitForSignalingReady(channel); + const response = await channel.send({ type: 'broadcast', event: 'signal', payload: msg }); + if (response !== 'ok') { + const restResponse = await channel.httpSend('signal', msg); + if (!restResponse.success) throw new Error(restResponse.error); + } } export function generateRoomCode(): string {