diff --git a/.changeset/tricky-comics-wink.md b/.changeset/tricky-comics-wink.md new file mode 100644 index 0000000000000..2e78e5adb6856 --- /dev/null +++ b/.changeset/tricky-comics-wink.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/media-signaling': patch +--- + +Fixes an issue where voice calls could fail if the user navigated between rooms during the initial connection diff --git a/packages/media-signaling/src/lib/Session.ts b/packages/media-signaling/src/lib/Session.ts index 20c4eecda73bc..848f29b12989b 100644 --- a/packages/media-signaling/src/lib/Session.ts +++ b/packages/media-signaling/src/lib/Session.ts @@ -16,6 +16,7 @@ import type { import type { IClientMediaCall, CallActorType, CallContact, CallFeature, AnyMediaCallData } from '../definition/call'; import type { IMediaSignalLogger } from '../definition/logger'; import { SessionRegistration } from './components/SessionRegistration'; +import { isSameDeviceId } from './utils/isSameDeviceId'; export type MediaSignalingEvents = { sessionStateChange: void; @@ -249,7 +250,7 @@ export class MediaSignalingSession extends Emitter { // 1. doesn't have any input track yet // 2. it's the same device id // 3. has no restriction on which device to use - if (!this.inputTrack || deviceId === this.currentDeviceId || !deviceId) { + if (!this.inputTrack || !deviceId || isSameDeviceId(deviceId, this.currentDeviceId)) { return; } diff --git a/packages/media-signaling/src/lib/utils/isSameDeviceId.ts b/packages/media-signaling/src/lib/utils/isSameDeviceId.ts new file mode 100644 index 0000000000000..ff2ed72751ed4 --- /dev/null +++ b/packages/media-signaling/src/lib/utils/isSameDeviceId.ts @@ -0,0 +1,71 @@ +function ensureStringArray(value: string | string[] | undefined): string[] { + if (!value) { + return []; + } + + if (typeof value === 'string') { + return [value]; + } + + if (Array.isArray(value)) { + return value; + } + + return []; +} + +function normalizeDeviceId(deviceId: ConstrainDOMString | null): { exact: string[]; ideal: string[] } { + if (!deviceId) { + return { + exact: [], + ideal: [], + }; + } + + if (typeof deviceId === 'object' && !Array.isArray(deviceId)) { + return { + exact: ensureStringArray(deviceId.exact), + ideal: ensureStringArray(deviceId.ideal), + }; + } + + return { + exact: [], + ideal: ensureStringArray(deviceId), + }; +} + +function isSameStringArray(array1: string[], array2: string[]): boolean { + const uniqueArray1 = [...new Set(array1)]; + const uniqueArray2 = [...new Set(array2)]; + + if (uniqueArray1.length !== uniqueArray2.length) { + return false; + } + + for (const value of uniqueArray1) { + if (!uniqueArray2.includes(value)) { + return false; + } + } + + return true; +} + +export function isSameDeviceId(deviceId1: ConstrainDOMString | null, deviceId2: ConstrainDOMString | null): boolean { + if (deviceId1 === deviceId2 || (!deviceId1 && !deviceId2)) { + return true; + } + + const normalizedDeviceId1 = normalizeDeviceId(deviceId1); + const normalizedDeviceId2 = normalizeDeviceId(deviceId2); + + if (!isSameStringArray(normalizedDeviceId1.exact, normalizedDeviceId2.exact)) { + return false; + } + if (!isSameStringArray(normalizedDeviceId1.ideal, normalizedDeviceId2.ideal)) { + return false; + } + + return true; +}