diff --git a/.changeset/stupid-rice-reflect.md b/.changeset/stupid-rice-reflect.md new file mode 100644 index 000000000..037958e04 --- /dev/null +++ b/.changeset/stupid-rice-reflect.md @@ -0,0 +1,5 @@ +--- +'livekit-client': patch +--- + +Add RoomEvent.Connected, fix connectFuture rejection exception diff --git a/src/room/Room.ts b/src/room/Room.ts index 57a898b92..5a548ded1 100644 --- a/src/room/Room.ts +++ b/src/room/Room.ts @@ -171,7 +171,7 @@ class Room extends (EventEmitter as new () => TypedEmitter) }) .on(EngineEvent.Resumed, () => { this.setAndEmitConnectionState(ConnectionState.Connected); - this.reconnectFuture?.resolve(); + this.reconnectFuture?.resolve?.(); this.reconnectFuture = undefined; this.emit(RoomEvent.Reconnected); this.updateSubscriptions(); @@ -218,7 +218,7 @@ class Room extends (EventEmitter as new () => TypedEmitter) this.connectFuture = this.reconnectFuture; return this.connectFuture.promise; } - const connectPromise = new Promise(async (resolve, reject) => { + const connectFn = async (resolve: () => void, reject: (reason: any) => void) => { this.setAndEmitConnectionState(ConnectionState.Connecting); if (!this.abortController || this.abortController.signal.aborted) { this.abortController = new AbortController(); @@ -347,8 +347,11 @@ class Room extends (EventEmitter as new () => TypedEmitter) this.setAndEmitConnectionState(ConnectionState.Connected); resolve(); }); - }).finally(() => (this.connectFuture = undefined)); - this.connectFuture = new Future(connectPromise); + }; + this.connectFuture = new Future(connectFn, () => { + this.connectFuture = undefined; + this.emit(RoomEvent.Connected); + }); return this.connectFuture.promise; }; @@ -363,7 +366,7 @@ class Room extends (EventEmitter as new () => TypedEmitter) log.warn('abort connection attempt'); this.abortController?.abort(); // in case the abort controller didn't manage to cancel the connection attempt, reject the connect promise explicitly - this.connectFuture?.reject(new ConnectionError('Client initiated disconnect')); + this.connectFuture?.reject?.(new ConnectionError('Client initiated disconnect')); this.connectFuture = undefined; } // send leave @@ -625,7 +628,7 @@ class Room extends (EventEmitter as new () => TypedEmitter) }); this.setAndEmitConnectionState(ConnectionState.Connected); this.emit(RoomEvent.Reconnected); - this.reconnectFuture?.resolve(); + this.reconnectFuture?.resolve?.(); this.reconnectFuture = undefined; // rehydrate participants @@ -672,7 +675,7 @@ class Room extends (EventEmitter as new () => TypedEmitter) } // reject potentially ongoing reconnection attempt if (this.connectFuture === this.reconnectFuture) { - this.connectFuture?.reject(undefined); + this.connectFuture?.reject?.(undefined); this.connectFuture = undefined; } this.reconnectFuture = undefined; @@ -1152,6 +1155,7 @@ class Room extends (EventEmitter as new () => TypedEmitter) export default Room; export type RoomEventCallbacks = { + connected: () => void; reconnecting: () => void; reconnected: () => void; disconnected: (reason?: DisconnectReason) => void; diff --git a/src/room/events.ts b/src/room/events.ts index abd495650..3b5f87613 100644 --- a/src/room/events.ts +++ b/src/room/events.ts @@ -9,6 +9,11 @@ */ export enum RoomEvent { + /** + * When the connection to the server has been established + */ + Connected = 'connected', + /** * When the connection to the server has been interrupted and it's attempting * to reconnect. diff --git a/src/room/utils.ts b/src/room/utils.ts index 9dbe5b597..fb922ab42 100644 --- a/src/room/utils.ts +++ b/src/room/utils.ts @@ -182,16 +182,23 @@ export function getEmptyAudioStreamTrack() { export class Future { promise: Promise; - resolve!: (arg: T) => void; - - reject!: (e: any) => void; - - constructor(promise?: Promise) { - this.promise = - promise ?? - new Promise((resolve, reject) => { - this.resolve = resolve; - this.reject = reject; - }); + resolve?: (arg: T) => void; + + reject?: (e: any) => void; + + onFinally?: () => void; + + constructor( + futureBase?: (resolve: (arg: T) => void, reject: (e: any) => void) => void, + onFinally?: () => void, + ) { + this.onFinally = onFinally; + this.promise = new Promise(async (resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + if (futureBase) { + await futureBase(resolve, reject); + } + }).finally(() => this.onFinally?.()); } }