diff --git a/internal/e2e-client/fixtures.ts b/internal/e2e-client/fixtures.ts index d8036b53e..6da2f8caf 100644 --- a/internal/e2e-client/fixtures.ts +++ b/internal/e2e-client/fixtures.ts @@ -8,7 +8,7 @@ import { createcXMLScriptResource, createRelayAppResource, createSWMLAppResource, - createVideoRoomResource, + createCallSessionResource, deleteResource, disconnectClient, enablePageLogs, @@ -26,7 +26,7 @@ type CustomFixture = { resource: { createcXMLExternalURLResource: typeof createcXMLExternalURLResource createcXMLScriptResource: typeof createcXMLScriptResource - createVideoRoomResource: typeof createVideoRoomResource + createCallSessionResource: typeof createCallSessionResource createSWMLAppResource: typeof createSWMLAppResource createRelayAppResource: typeof createRelayAppResource resources: Resource[] @@ -89,8 +89,8 @@ const test = baseTest.extend({ const resources: Resource[] = [] const resource = { - createVideoRoomResource: async (params?: string) => { - const data = await createVideoRoomResource(params) + createCallSessionResource: async (params?: string) => { + const data = await createCallSessionResource(params) resources.push(data) return data }, diff --git a/internal/e2e-client/tests/buildVideoWithFabricSDK.spec.ts b/internal/e2e-client/tests/buildVideoWithFabricSDK.spec.ts index 7742250df..df3138dfd 100644 --- a/internal/e2e-client/tests/buildVideoWithFabricSDK.spec.ts +++ b/internal/e2e-client/tests/buildVideoWithFabricSDK.spec.ts @@ -34,7 +34,7 @@ test.describe('buildVideoElement with Call SDK', () => { const page = await createCustomPage({ name: '[page]' }) await page.goto(SERVER_URL) const roomName = randomizeRoomName('bld-vd-el') - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -57,7 +57,7 @@ test.describe('buildVideoElement with Call SDK', () => { const page = await createCustomPage({ name: '[page]' }) await page.goto(SERVER_URL) const roomName = randomizeRoomName('bld-vd-el') - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -117,7 +117,7 @@ test.describe('buildVideoElement with Call SDK', () => { const page = await createCustomPage({ name: '[page]' }) await page.goto(SERVER_URL) const roomName = randomizeRoomName('bld-vd-el') - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -256,7 +256,7 @@ test.describe('buildVideoElement with Call SDK', () => { const page = await createCustomPage({ name: '[page]' }) await page.goto(SERVER_URL) const roomName = randomizeRoomName('bld-vd-el') - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -308,7 +308,7 @@ test.describe('buildVideoElement with Call SDK', () => { const page = await createCustomPage({ name: '[page]' }) await page.goto(SERVER_URL) const roomName = randomizeRoomName('bld-vd-el') - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -349,7 +349,7 @@ test.describe('buildVideoElement with Call SDK', () => { await Promise.all([pageOne.goto(SERVER_URL), pageTwo.goto(SERVER_URL)]) const roomName = randomizeRoomName('bld-vd-el') - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await Promise.all([createCFClient(pageOne), createCFClient(pageTwo)]) diff --git a/internal/e2e-client/tests/callfabric/audioFlags.spec.ts b/internal/e2e-client/tests/callfabric/audioFlags.spec.ts index c8e325f62..b5076ade8 100644 --- a/internal/e2e-client/tests/callfabric/audioFlags.spec.ts +++ b/internal/e2e-client/tests/callfabric/audioFlags.spec.ts @@ -25,7 +25,7 @@ test.describe('CallCall Audio Flags', () => { await page.goto(SERVER_URL) roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -324,7 +324,7 @@ test.describe('CallCall Audio Flags', () => { await pageTwo.goto(SERVER_URL) roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) }) await test.step('[pageOne] create client and join room', async () => { diff --git a/internal/e2e-client/tests/callfabric/cleanup.spec.ts b/internal/e2e-client/tests/callfabric/cleanup.spec.ts index 0a4a42821..50f14beb7 100644 --- a/internal/e2e-client/tests/callfabric/cleanup.spec.ts +++ b/internal/e2e-client/tests/callfabric/cleanup.spec.ts @@ -206,7 +206,7 @@ test.describe('Clean up', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page, { attachSagaMonitor: true }) diff --git a/internal/e2e-client/tests/callfabric/conversation.spec.ts b/internal/e2e-client/tests/callfabric/conversation.spec.ts index 571a14207..bdf56e28e 100644 --- a/internal/e2e-client/tests/callfabric/conversation.spec.ts +++ b/internal/e2e-client/tests/callfabric/conversation.spec.ts @@ -30,7 +30,7 @@ test.describe('Conversation Room', () => { await createCFClient(page2) roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) }) await test.step('get room address', async () => { diff --git a/internal/e2e-client/tests/callfabric/deviceEvent.spec.ts b/internal/e2e-client/tests/callfabric/deviceEvent.spec.ts index e66067a43..c09948d3e 100644 --- a/internal/e2e-client/tests/callfabric/deviceEvent.spec.ts +++ b/internal/e2e-client/tests/callfabric/deviceEvent.spec.ts @@ -26,7 +26,7 @@ test.describe('CallCall Room Device', () => { await page.goto(SERVER_URL) roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -181,7 +181,7 @@ test.describe('CallCall Room Device', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -242,7 +242,7 @@ test.describe('CallCall Room Device', () => { await page.goto(SERVER_URL) roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -373,7 +373,7 @@ test.describe('CallCall Room Device', () => { await page.goto(SERVER_URL) roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) // Set up initial mock for enumerate devices with default speaker await expectPageEvalToPass(page, { diff --git a/internal/e2e-client/tests/callfabric/deviceState.spec.ts b/internal/e2e-client/tests/callfabric/deviceState.spec.ts index 336241900..1d4514134 100644 --- a/internal/e2e-client/tests/callfabric/deviceState.spec.ts +++ b/internal/e2e-client/tests/callfabric/deviceState.spec.ts @@ -44,7 +44,7 @@ test.describe('CallCall - Device State', () => { await page.goto(SERVER_URL) roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -169,7 +169,7 @@ test.describe('CallCall - Device State', () => { await page.goto(SERVER_URL) roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) diff --git a/internal/e2e-client/tests/callfabric/holdunhold.spec.ts b/internal/e2e-client/tests/callfabric/holdunhold.spec.ts index 1c395f7be..e968fe31c 100644 --- a/internal/e2e-client/tests/callfabric/holdunhold.spec.ts +++ b/internal/e2e-client/tests/callfabric/holdunhold.spec.ts @@ -22,7 +22,7 @@ test.describe('CallCall Hold/Unhold Call', () => { await pageTwo.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await test.step('[page-one] should create a client and dial a call', async () => { await createCFClient(pageOne) diff --git a/internal/e2e-client/tests/callfabric/mirrorVideo.spec.ts b/internal/e2e-client/tests/callfabric/mirrorVideo.spec.ts index a32f2cbad..4a681133d 100644 --- a/internal/e2e-client/tests/callfabric/mirrorVideo.spec.ts +++ b/internal/e2e-client/tests/callfabric/mirrorVideo.spec.ts @@ -47,7 +47,7 @@ test.describe('CallCall Mirror Video', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) diff --git a/internal/e2e-client/tests/callfabric/muteUnmuteAll.spec.ts b/internal/e2e-client/tests/callfabric/muteUnmuteAll.spec.ts index c778b5f3d..534192147 100644 --- a/internal/e2e-client/tests/callfabric/muteUnmuteAll.spec.ts +++ b/internal/e2e-client/tests/callfabric/muteUnmuteAll.spec.ts @@ -31,7 +31,7 @@ const joinAllPages = async ( ) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) const address = `/public/${roomName}?channel=${channel}` const roomSessions = [] diff --git a/internal/e2e-client/tests/callfabric/raiseHand.spec.ts b/internal/e2e-client/tests/callfabric/raiseHand.spec.ts index 86a4820d4..4f669abf8 100644 --- a/internal/e2e-client/tests/callfabric/raiseHand.spec.ts +++ b/internal/e2e-client/tests/callfabric/raiseHand.spec.ts @@ -17,7 +17,7 @@ test.describe('CallCall Raise/Lower Hand', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -94,7 +94,7 @@ test.describe('CallCall Raise/Lower Hand', () => { await pageTwo.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) // Create client, dial an address and join a video room from page-one await createCFClient(pageOne) diff --git a/internal/e2e-client/tests/callfabric/reattach.spec.ts b/internal/e2e-client/tests/callfabric/reattach.spec.ts index a50ea560e..f865ffb25 100644 --- a/internal/e2e-client/tests/callfabric/reattach.spec.ts +++ b/internal/e2e-client/tests/callfabric/reattach.spec.ts @@ -21,7 +21,7 @@ test.describe('CallCall Reattach', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -77,7 +77,7 @@ test.describe('CallCall Reattach', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -375,7 +375,7 @@ test.describe('CallCall Reattach', () => { await pageTwo.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await test.step('[pageOne] create client and join a room', async () => { await createCFClient(pageOne) @@ -663,7 +663,7 @@ test.describe('CallCall Reattach', () => { // await page.goto(SERVER_URL) // const roomName = `e2e_${uuid()}` - // await resource.createVideoRoomResource(roomName) + // await resource.createCallSessionResource(roomName) // const resourceName = `e2e_${uuid()}` // await resource.createSWMLAppResource({ // name: resourceName, diff --git a/internal/e2e-client/tests/callfabric/relayApp.spec.ts b/internal/e2e-client/tests/callfabric/relayApp.spec.ts index 0ee451f32..90e432034 100644 --- a/internal/e2e-client/tests/callfabric/relayApp.spec.ts +++ b/internal/e2e-client/tests/callfabric/relayApp.spec.ts @@ -10,6 +10,7 @@ import { SERVER_URL, } from '../../utils' import { test, expect } from '../../fixtures' +import { CallSession } from '@signalwire/client' test.describe('CallFabric Relay Application', () => { test('should connect to the relay app and expect an audio playback', async ({ @@ -67,8 +68,7 @@ test.describe('CallFabric Relay Application', () => { }) const callPlayStarted = page.evaluate(async () => { - // @ts-expect-error - const callObj: Video.RoomSession = window._callObj + const callObj: CallSession = window._callObj! return new Promise((resolve) => { callObj.on('call.play', (params: any) => { if (params.state === 'playing') resolve(true) @@ -79,8 +79,7 @@ test.describe('CallFabric Relay Application', () => { const expectInitialEvents = expectCFInitialEvents(page, [callPlayStarted]) await page.evaluate(async () => { - // @ts-expect-error - const call = window._callObj + const call = window._callObj! await call.start() }) @@ -94,8 +93,7 @@ test.describe('CallFabric Relay Application', () => { await expectPageReceiveAudio(page) const callPlayFinished = page.evaluate(async () => { - // @ts-expect-error - const callObj: Video.RoomSession = window._callObj + const callObj: CallSession = window._callObj! return new Promise((resolve) => { callObj.on('call.play', (params: any) => { if (params.state === 'finished') resolve(true) @@ -112,8 +110,7 @@ test.describe('CallFabric Relay Application', () => { // Hangup the call await page.evaluate(async () => { - // @ts-expect-error - const call = window._callObj + const call = window._callObj! await call.hangup() }) @@ -174,8 +171,7 @@ test.describe('CallFabric Relay Application', () => { }) const callPlayStarted = page.evaluate(async () => { - // @ts-expect-error - const callObj: Video.RoomSession = window._callObj + const callObj: CallSession = window._callObj! return new Promise((resolve) => { callObj.on('call.play', (params: any) => { if (params.state === 'playing') resolve(true) @@ -186,8 +182,7 @@ test.describe('CallFabric Relay Application', () => { const expectInitialEvents = expectCFInitialEvents(page, [callPlayStarted]) await page.evaluate(async () => { - // @ts-expect-error - const call = window._callObj + const call = window._callObj! await call.start() }) @@ -214,8 +209,7 @@ test.describe('CallFabric Relay Application', () => { } playback!.stop() await page.evaluate(async () => { - // @ts-expect-error - const callObj: Video.RoomSession = window._callObj + const callObj: CallSession = window._callObj! return new Promise((resolve) => { callObj.on('call.play', (params: any) => { if (params.state === 'finished') resolve(true) @@ -227,8 +221,7 @@ test.describe('CallFabric Relay Application', () => { // Hangup the call await page.evaluate(async () => { - // @ts-expect-error - const call = window._callObj + const call = window._callObj! await call.hangup() }) @@ -289,8 +282,7 @@ test.describe('CallFabric Relay Application', () => { const expectFinalEvents = expectCFFinalEvents(page) await page.evaluate(async () => { - // @ts-expect-error - const call = window._callObj + const call = window._callObj! await call.start() }) diff --git a/internal/e2e-client/tests/callfabric/renegotiateAudio.spec.ts b/internal/e2e-client/tests/callfabric/renegotiateAudio.spec.ts index 157cae452..f1c6b052c 100644 --- a/internal/e2e-client/tests/callfabric/renegotiateAudio.spec.ts +++ b/internal/e2e-client/tests/callfabric/renegotiateAudio.spec.ts @@ -20,7 +20,7 @@ test.describe('CallCall Audio Renegotiation', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -44,8 +44,8 @@ test.describe('CallCall Audio Renegotiation', () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.setAudioDirection('sendrecv') + const callSession: CallSession = window._callObj + await callSession.setAudioDirection('sendrecv') }) await expectStatWithPolling(page, { @@ -72,8 +72,8 @@ test.describe('CallCall Audio Renegotiation', () => { await test.step('it should disable the audio with "inactive"', async () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.updateMedia({ + const callSession: CallSession = window._callObj + await callSession.updateMedia({ audio: { direction: 'inactive' }, }) }) @@ -105,7 +105,7 @@ test.describe('CallCall Audio Renegotiation', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -129,8 +129,8 @@ test.describe('CallCall Audio Renegotiation', () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.setAudioDirection('sendonly') + const callSession: CallSession = window._callObj + await callSession.setAudioDirection('sendonly') }) const stats2 = await getStats(page) @@ -146,8 +146,8 @@ test.describe('CallCall Audio Renegotiation', () => { await test.step('it should disable the audio with "recvonly"', async () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.updateMedia({ + const callSession: CallSession = window._callObj + await callSession.updateMedia({ audio: { direction: 'recvonly' }, }) }) @@ -174,7 +174,7 @@ test.describe('CallCall Audio Renegotiation', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -198,8 +198,8 @@ test.describe('CallCall Audio Renegotiation', () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.setAudioDirection('recvonly') + const callSession: CallSession = window._callObj + await callSession.setAudioDirection('recvonly') }) const stats2 = await getStats(page) @@ -223,8 +223,8 @@ test.describe('CallCall Audio Renegotiation', () => { await test.step('it should disable the audio with "inactive"', async () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.updateMedia({ + const callSession: CallSession = window._callObj + await callSession.updateMedia({ audio: { direction: 'inactive' }, }) }) diff --git a/internal/e2e-client/tests/callfabric/renegotiateVideo.spec.ts b/internal/e2e-client/tests/callfabric/renegotiateVideo.spec.ts index 14bf1b9b9..cb5021aec 100644 --- a/internal/e2e-client/tests/callfabric/renegotiateVideo.spec.ts +++ b/internal/e2e-client/tests/callfabric/renegotiateVideo.spec.ts @@ -22,7 +22,7 @@ test.describe('CallCall Video Renegotiation', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -39,8 +39,8 @@ test.describe('CallCall Video Renegotiation', () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.setVideoDirection('sendrecv') + const callSession: CallSession = window._callObj + await callSession.setVideoDirection('sendrecv') }) // Wait for the MCU to be visible @@ -55,8 +55,8 @@ test.describe('CallCall Video Renegotiation', () => { await test.step('it should disable the video with "inactive"', async () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.updateMedia({ + const callSession: CallSession = window._callObj + await callSession.updateMedia({ video: { direction: 'inactive' }, }) }) @@ -81,7 +81,7 @@ test.describe('CallCall Video Renegotiation', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -98,8 +98,8 @@ test.describe('CallCall Video Renegotiation', () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.setVideoDirection('sendonly') + const callSession: CallSession = window._callObj + await callSession.setVideoDirection('sendonly') }) // Verify the MCU is not visible @@ -122,8 +122,8 @@ test.describe('CallCall Video Renegotiation', () => { await test.step('it should disable the video with "recvonly"', async () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.updateMedia({ + const callSession: CallSession = window._callObj + await callSession.updateMedia({ video: { direction: 'recvonly' }, }) }) @@ -150,7 +150,7 @@ test.describe('CallCall Video Renegotiation', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -167,8 +167,8 @@ test.describe('CallCall Video Renegotiation', () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.setVideoDirection('recvonly') + const callSession: CallSession = window._callObj + await callSession.setVideoDirection('recvonly') }) // Expect incoming video stream is visible @@ -187,8 +187,8 @@ test.describe('CallCall Video Renegotiation', () => { await test.step('it should disable the video with "inactive"', async () => { await page.evaluate(async () => { // @ts-expect-error - const cfRoomSession: CallSession = window._callObj - await cfRoomSession.updateMedia({ + const callSession: CallSession = window._callObj + await callSession.updateMedia({ video: { direction: 'inactive' }, }) }) diff --git a/internal/e2e-client/tests/callfabric/swml.spec.ts b/internal/e2e-client/tests/callfabric/swml.spec.ts index 982cd1900..ab48d1c82 100644 --- a/internal/e2e-client/tests/callfabric/swml.spec.ts +++ b/internal/e2e-client/tests/callfabric/swml.spec.ts @@ -9,6 +9,7 @@ import { expectPageReceiveAudio, expectPageEvalToPass, } from '../../utils' +import { CallSession } from '@signalwire/client' test.describe('CallCall SWML', () => { const swmlTTS = { @@ -166,8 +167,7 @@ test.describe('CallCall SWML', () => { const expectFinalEvents = expectCFFinalEvents(page) await page.evaluate(async () => { - // @ts-expect-error - const call = window._callObj + const call = window._callObj! await call.start() }) diff --git a/internal/e2e-client/tests/callfabric/videoRoom.spec.ts b/internal/e2e-client/tests/callfabric/videoRoom.spec.ts index 79f59b43a..8275dab17 100644 --- a/internal/e2e-client/tests/callfabric/videoRoom.spec.ts +++ b/internal/e2e-client/tests/callfabric/videoRoom.spec.ts @@ -14,7 +14,7 @@ import { } from '../../utils' import { JSHandle } from '@playwright/test' -test.describe('CallCall VideoRoom', () => { +test.describe('Call Room Address', () => { test('should handle joining a room, perform actions and then leave the room', async ({ createCustomPage, resource, @@ -28,7 +28,7 @@ test.describe('CallCall VideoRoom', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -623,7 +623,7 @@ test.describe('CallCall VideoRoom', () => { throw new Error('Client not found') } - const call = client.dial({ + const call = await client.dial({ to: `/public/invalid-address?channel=video`, rootElement: document.getElementById('rootElement'), }) @@ -655,7 +655,7 @@ test.describe('CallCall VideoRoom', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) diff --git a/internal/e2e-client/tests/callfabric/videoRoomLayout.spec.ts b/internal/e2e-client/tests/callfabric/videoRoomLayout.spec.ts index eef6af05d..39aaa0be5 100644 --- a/internal/e2e-client/tests/callfabric/videoRoomLayout.spec.ts +++ b/internal/e2e-client/tests/callfabric/videoRoomLayout.spec.ts @@ -1,7 +1,7 @@ import { uuid, VideoPosition, - VideoRoomSubscribedEventParams, + CallSessionSubscribedEventParams, } from '@signalwire/core' import { test, expect } from '../../fixtures' import { @@ -21,7 +21,7 @@ test.describe('CallCall Video Room Layout', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -68,7 +68,7 @@ test.describe('CallCall Video Room Layout', () => { await page.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) await createCFClient(page) @@ -140,13 +140,13 @@ test.describe('CallCall Video Room Layout', () => { await pageTwo.goto(SERVER_URL) const roomName = `e2e_${uuid()}` - await resource.createVideoRoomResource(roomName) + await resource.createCallSessionResource(roomName) // Create client for pageOne and Dial an address to join a video room await createCFClient(pageOne) const callSessionOne = (await dialAddress(pageOne, { address: `/public/${roomName}?channel=video`, - })) as VideoRoomSubscribedEventParams + })) as CallSessionSubscribedEventParams expect(callSessionOne.room_session).toBeDefined() await expectMCUVisible(pageOne) @@ -154,7 +154,7 @@ test.describe('CallCall Video Room Layout', () => { await createCFClient(pageTwo) const callSessionTwo = (await dialAddress(pageTwo, { address: `/public/${roomName}?channel=video`, - })) as VideoRoomSubscribedEventParams + })) as CallSessionSubscribedEventParams expect(callSessionTwo.room_session).toBeDefined() await expectMCUVisible(pageTwo) diff --git a/internal/e2e-client/utils.ts b/internal/e2e-client/utils.ts index bd5a8a782..337ffba2a 100644 --- a/internal/e2e-client/utils.ts +++ b/internal/e2e-client/utils.ts @@ -1545,7 +1545,7 @@ export interface CXMLApplication { // and other things } -export const createVideoRoomResource = async (name?: string) => { +export const createCallSessionResource = async (name?: string) => { const response = await fetch( `https://${process.env.API_HOST}/api/fabric/resources/conference_rooms`, { diff --git a/packages/client/src/BaseRoomSession.ts b/packages/client/src/BaseCallSession.ts similarity index 82% rename from packages/client/src/BaseRoomSession.ts rename to packages/client/src/BaseCallSession.ts index e9ca856c8..a04e3493d 100644 --- a/packages/client/src/BaseRoomSession.ts +++ b/packages/client/src/BaseCallSession.ts @@ -17,35 +17,35 @@ import { import { LocalVideoOverlay, OverlayMap } from './VideoOverlays' import { AudioElement, - BaseRoomSessionContract, + BaseCallSessionContract, StartScreenShareOptions, } from './utils/interfaces' import { SCREENSHARE_AUDIO_CONSTRAINTS } from './utils/constants' -import { addOverlayPrefix } from './utils/roomSession' +import { addOverlayPrefix } from './utils/callSession' import { audioSetSpeakerAction } from './features/actions' import { - RoomSessionScreenShare, - RoomSessionScreenShareAPI, - RoomSessionScreenShareConnection, - RoomSessionScreenShareEvents, -} from './RoomSessionScreenShare' -import * as workers from './video/workers' - -export interface BaseRoomSession< - EventTypes extends EventEmitter.ValidEventTypes = BaseRoomSessionEvents -> extends BaseRoomSessionContract, + CallSessionScreenShare, + CallSessionScreenShareAPI, + CallSessionScreenShareConnection, + CallSessionScreenShareEvents, +} from './CallSessionScreenShare' +import * as workers from './workers' + +export interface BaseCallSession< + EventTypes extends EventEmitter.ValidEventTypes = BaseCallSessionEvents +> extends BaseCallSessionContract, BaseConnection, BaseComponentContract {} -export interface BaseRoomSessionOptions extends BaseConnectionOptions {} +export interface BaseCallSessionOptions extends BaseConnectionOptions {} -export class BaseRoomSessionConnection< - EventTypes extends EventEmitter.ValidEventTypes = BaseRoomSessionEvents +export class BaseCallSessionConnection< + EventTypes extends EventEmitter.ValidEventTypes = BaseCallSessionEvents > extends BaseConnection - implements BaseRoomSessionContract + implements BaseCallSessionContract { - private _screenShareList = new Set() + private _screenShareList = new Set() private _audioEl: AudioElement private _overlayMap: OverlayMap private _localVideoOverlay: LocalVideoOverlay @@ -170,7 +170,7 @@ export class BaseRoomSessionConnection< * Allow sharing the screen within the room. */ async startScreenShare(opts: StartScreenShareOptions = {}) { - return new Promise(async (resolve, reject) => { + return new Promise(async (resolve, reject) => { const { autoJoin = true, audio = false, @@ -199,12 +199,12 @@ export class BaseRoomSessionConnection< } const screenShare = connect< - RoomSessionScreenShareEvents, - RoomSessionScreenShareConnection, - RoomSessionScreenShare + CallSessionScreenShareEvents, + CallSessionScreenShareConnection, + CallSessionScreenShare >({ store: this.store, - Component: RoomSessionScreenShareAPI, + Component: CallSessionScreenShareAPI, })(options) /** @@ -274,29 +274,29 @@ export class BaseRoomSessionConnection< } } -type BaseRoomSessionEventsHandlerMap = Record< +type BaseCallSessionEventsHandlerMap = Record< BaseConnectionState, - (params: BaseRoomSession) => void + (params: BaseCallSession) => void > & Record void> -export type BaseRoomSessionEvents = { - [k in keyof BaseRoomSessionEventsHandlerMap]: BaseRoomSessionEventsHandlerMap[k] +export type BaseCallSessionEvents = { + [k in keyof BaseCallSessionEventsHandlerMap]: BaseCallSessionEventsHandlerMap[k] } /** @internal */ -export const createBaseRoomSessionObject = ( - params: BaseRoomSessionOptions -): BaseRoomSession => { - const room = connect< - BaseRoomSessionEvents, - BaseRoomSessionConnection, - BaseRoomSession +export const createBaseCallSessionObject = ( + params: BaseCallSessionOptions +): BaseCallSession => { + const instance = connect< + BaseCallSessionEvents, + BaseCallSessionConnection, + BaseCallSession >({ store: params.store, customSagas: params.customSagas, - Component: BaseRoomSessionConnection, + Component: BaseCallSessionConnection, })(params) - return room + return instance } diff --git a/packages/client/src/CallSessionDevice.test.ts b/packages/client/src/CallSessionDevice.test.ts new file mode 100644 index 000000000..cfcc68e00 --- /dev/null +++ b/packages/client/src/CallSessionDevice.test.ts @@ -0,0 +1,25 @@ +import { CallSessionDeviceAPI } from './CallSessionDevice' +import type { CallSessionDevice } from './CallSessionDevice' +import { configureJestStore } from './testUtils' + +describe('CallDevice Object', () => { + let callDevice: CallSessionDevice + + beforeEach(() => { + callDevice = new CallSessionDeviceAPI({ + store: configureJestStore(), + }) as any as CallSessionDevice + // @ts-expect-error + callDevice.execute = jest.fn() + }) + + it('should have all the custom methods defined', () => { + expect(callDevice.audioMute).toBeDefined() + expect(callDevice.audioUnmute).toBeDefined() + expect(callDevice.videoMute).toBeDefined() + expect(callDevice.videoUnmute).toBeDefined() + expect(callDevice.setMicrophoneVolume).toBeDefined() + expect(callDevice.setInputVolume).toBeDefined() + expect(callDevice.setInputSensitivity).toBeDefined() + }) +}) diff --git a/packages/client/src/RoomSessionDevice.ts b/packages/client/src/CallSessionDevice.ts similarity index 53% rename from packages/client/src/RoomSessionDevice.ts rename to packages/client/src/CallSessionDevice.ts index 8e2e8ca9b..e9742135f 100644 --- a/packages/client/src/RoomSessionDevice.ts +++ b/packages/client/src/CallSessionDevice.ts @@ -7,31 +7,31 @@ import { BaseConnectionState, } from '@signalwire/core' import { BaseConnection, MediaEventNames } from '@signalwire/webrtc' -import { RoomSessionDeviceMethods } from './utils/interfaces' +import { CallSessionDeviceMethods } from './utils/interfaces' -type RoomSessionDeviceEventsHandlerMap = Record< +type CallSessionDeviceEventsHandlerMap = Record< BaseConnectionState, - (params: RoomSessionDevice) => void + (params: CallSessionDevice) => void > & Record void> & Record void> -export type RoomSessionDeviceEvents = { - [k in keyof RoomSessionDeviceEventsHandlerMap]: RoomSessionDeviceEventsHandlerMap[k] +export type CallSessionDeviceEvents = { + [k in keyof CallSessionDeviceEventsHandlerMap]: CallSessionDeviceEventsHandlerMap[k] } -/** @deprecated Use {@link RoomSessionDevice} instead */ -export interface RoomDevice extends RoomSessionDevice {} -export interface RoomSessionDevice - extends RoomSessionDeviceMethods, - BaseConnectionContract { +/** @deprecated Use {@link CallSessionDevice} instead */ +export interface CallDevice extends CallSessionDevice {} +export interface CallSessionDevice + extends CallSessionDeviceMethods, + BaseConnectionContract { join(): Promise leave(): Promise /** @internal */ - runWorker: BaseConnection['runWorker'] + runWorker: BaseConnection['runWorker'] } -export class RoomSessionDeviceConnection extends BaseConnection { +export class CallSessionDeviceConnection extends BaseConnection { join() { return super.invite() } @@ -42,16 +42,16 @@ export class RoomSessionDeviceConnection extends BaseConnection(RoomSessionDeviceConnection, { +export const CallSessionDeviceAPI = extendComponent< + CallSessionDeviceConnection, + CallSessionDeviceMethods +>(CallSessionDeviceConnection, { audioMute: Rooms.audioMuteMember, audioUnmute: Rooms.audioUnmuteMember, videoMute: Rooms.videoMuteMember, diff --git a/packages/client/src/RoomSessionScreenShare.test.ts b/packages/client/src/CallSessionScreenShare.test.ts similarity index 72% rename from packages/client/src/RoomSessionScreenShare.test.ts rename to packages/client/src/CallSessionScreenShare.test.ts index 5b51cfbd1..a93cd219f 100644 --- a/packages/client/src/RoomSessionScreenShare.test.ts +++ b/packages/client/src/CallSessionScreenShare.test.ts @@ -1,14 +1,14 @@ -import { RoomSessionScreenShareAPI } from './RoomSessionScreenShare' -import type { RoomSessionScreenShare } from './RoomSessionScreenShare' +import { CallSessionScreenShareAPI } from './CallSessionScreenShare' +import type { CallSessionScreenShare } from './CallSessionScreenShare' import { configureJestStore } from './testUtils' describe('RoomScreenShare Object', () => { - let roomScreenShare: RoomSessionScreenShare + let roomScreenShare: CallSessionScreenShare beforeEach(() => { - roomScreenShare = new RoomSessionScreenShareAPI({ + roomScreenShare = new CallSessionScreenShareAPI({ store: configureJestStore(), - }) as any as RoomSessionScreenShare + }) as any as CallSessionScreenShare // @ts-expect-error roomScreenShare.execute = jest.fn() }) diff --git a/packages/client/src/RoomSessionScreenShare.ts b/packages/client/src/CallSessionScreenShare.ts similarity index 50% rename from packages/client/src/RoomSessionScreenShare.ts rename to packages/client/src/CallSessionScreenShare.ts index 64e7bed22..487f396c3 100644 --- a/packages/client/src/RoomSessionScreenShare.ts +++ b/packages/client/src/CallSessionScreenShare.ts @@ -7,31 +7,31 @@ import { BaseConnectionState, } from '@signalwire/core' import { BaseConnection, MediaEventNames } from '@signalwire/webrtc' -import { RoomScreenShareMethods } from './utils/interfaces' +import { CallSessionScreenShareMethods } from './utils/interfaces' -type RoomSessionScreenShareEventsHandlerMap = Record< +type CallSessionScreenShareEventsHandlerMap = Record< BaseConnectionState, - (params: RoomSessionScreenShare) => void + (params: CallSessionScreenShare) => void > & Record void> & Record void> -export type RoomSessionScreenShareEvents = { - [k in keyof RoomSessionScreenShareEventsHandlerMap]: RoomSessionScreenShareEventsHandlerMap[k] +export type CallSessionScreenShareEvents = { + [k in keyof CallSessionScreenShareEventsHandlerMap]: CallSessionScreenShareEventsHandlerMap[k] } -/** @deprecated Use {@link RoomSessionScreenShare} instead */ -export interface RoomScreenShare extends RoomSessionScreenShare {} -export interface RoomSessionScreenShare - extends RoomScreenShareMethods, - BaseConnectionContract { +/** @deprecated Use {@link CallSessionScreenShare} instead */ +export interface RoomScreenShare extends CallSessionScreenShare {} +export interface CallSessionScreenShare + extends CallSessionScreenShareMethods, + BaseConnectionContract { join(): Promise leave(): Promise /** @internal */ - runWorker: BaseConnection['runWorker'] + runWorker: BaseConnection['runWorker'] } -export class RoomSessionScreenShareConnection extends BaseConnection { +export class CallSessionScreenShareConnection extends BaseConnection { join() { return super.invite() } @@ -44,10 +44,10 @@ export class RoomSessionScreenShareConnection extends BaseConnection(RoomSessionScreenShareConnection, { +export const CallSessionScreenShareAPI = extendComponent< + CallSessionScreenShareConnection, + CallSessionScreenShareMethods +>(CallSessionScreenShareConnection, { audioMute: Rooms.audioMuteMember, audioUnmute: Rooms.audioUnmuteMember, videoMute: Rooms.videoMuteMember, diff --git a/packages/client/src/RoomSessionDevice.test.ts b/packages/client/src/RoomSessionDevice.test.ts deleted file mode 100644 index c05e01c8e..000000000 --- a/packages/client/src/RoomSessionDevice.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { RoomSessionDeviceAPI } from './RoomSessionDevice' -import type { RoomSessionDevice } from './RoomSessionDevice' -import { configureJestStore } from './testUtils' - -describe('RoomDevice Object', () => { - let roomDevice: RoomSessionDevice - - beforeEach(() => { - roomDevice = new RoomSessionDeviceAPI({ - store: configureJestStore(), - }) as any as RoomSessionDevice - // @ts-expect-error - roomDevice.execute = jest.fn() - }) - - it('should have all the custom methods defined', () => { - expect(roomDevice.audioMute).toBeDefined() - expect(roomDevice.audioUnmute).toBeDefined() - expect(roomDevice.videoMute).toBeDefined() - expect(roomDevice.videoUnmute).toBeDefined() - expect(roomDevice.setMicrophoneVolume).toBeDefined() - expect(roomDevice.setInputVolume).toBeDefined() - expect(roomDevice.setInputSensitivity).toBeDefined() - }) -}) diff --git a/packages/client/src/VideoOverlays.ts b/packages/client/src/VideoOverlays.ts index d1e45abb5..cc9b8c45a 100644 --- a/packages/client/src/VideoOverlays.ts +++ b/packages/client/src/VideoOverlays.ts @@ -1,8 +1,6 @@ import { getLogger, MemberUpdatedEventParams } from '@signalwire/core' -import { VideoRoomSession, isVideoRoomSession } from './video/VideoRoomSession' -import { CallSession, isCallSession } from './unified/CallSession' -import { VideoMemberUpdatedHandlerParams } from './utils/interfaces' -import { OVERLAY_PREFIX, SDK_PREFIX } from './utils/roomSession' +import { CallSession } from './unified/CallSession' +import { OVERLAY_PREFIX, SDK_PREFIX } from './utils/callSession' export type OverlayMap = Map interface UserOverlayOptions { @@ -62,12 +60,12 @@ export class UserOverlay { interface LocalVideoOverlayOptions { id: string mirrorLocalVideoOverlay: boolean - room: CallSession | VideoRoomSession + room: CallSession } export class LocalVideoOverlay extends UserOverlay { private _mirrored: boolean - private _room: CallSession | VideoRoomSession + private _room: CallSession constructor(options: LocalVideoOverlayOptions) { super(options) @@ -77,8 +75,6 @@ export class LocalVideoOverlay extends UserOverlay { // Bind the handler to preserve context this.fabricMemberVideoMutedHandler = this.fabricMemberVideoMutedHandler.bind(this) - this.videoMemberVideoMutedHandler = - this.videoMemberVideoMutedHandler.bind(this) this.attachListeners() } @@ -92,32 +88,18 @@ export class LocalVideoOverlay extends UserOverlay { } private attachListeners() { - if (isCallSession(this._room)) { - this._room.on( - 'member.updated.videoMuted', - this.fabricMemberVideoMutedHandler - ) - } else if (isVideoRoomSession(this._room)) { - this._room.on( - 'member.updated.videoMuted', - this.videoMemberVideoMutedHandler - ) - } + this._room.on( + 'member.updated.videoMuted', + this.fabricMemberVideoMutedHandler + ) } /** @internal */ public detachListeners() { - if (isCallSession(this._room)) { - this._room.off( - 'member.updated.videoMuted', - this.fabricMemberVideoMutedHandler - ) - } else if (isVideoRoomSession(this._room)) { - this._room.off( - 'member.updated.videoMuted', - this.videoMemberVideoMutedHandler - ) - } + this._room.off( + 'member.updated.videoMuted', + this.fabricMemberVideoMutedHandler + ) } private memberVideoMutedHandler(memberId: string, videoMuted: boolean) { @@ -137,12 +119,6 @@ export class LocalVideoOverlay extends UserOverlay { ) } - private videoMemberVideoMutedHandler( - params: VideoMemberUpdatedHandlerParams - ) { - this.memberVideoMutedHandler(params.member.id, params.member.video_muted) - } - public setMediaStream(stream: MediaStream) { if (!this.domElement) { return getLogger().warn('Missing local overlay to set the stream') diff --git a/packages/client/src/buildVideoElement.test.ts b/packages/client/src/buildVideoElement.test.ts index 6ce41922c..60d5547fe 100644 --- a/packages/client/src/buildVideoElement.test.ts +++ b/packages/client/src/buildVideoElement.test.ts @@ -1,21 +1,13 @@ import { JSDOM } from 'jsdom' import { actions } from '@signalwire/core' -import { - configureFullStack, - dispatchMockedCallJoined, - dispatchMockedRoomSubscribed, -} from './testUtils' +import { configureFullStack, dispatchMockedCallJoined } from './testUtils' import { buildVideoElement, BuildVideoElementParams } from './buildVideoElement' import { CallSession, createCallSessionObject } from './unified/CallSession' -import { - createVideoRoomSessionObject, - VideoRoomSession, -} from './video/VideoRoomSession' -import { addOverlayPrefix, SDK_PREFIX } from './utils/roomSession' +import { addOverlayPrefix, SDK_PREFIX } from './utils/callSession' describe('buildVideoElement', () => { describe('with CallSession', () => { - let room: CallSession + let callSessionInstance: CallSession let stack: ReturnType let store: any let jsdom: JSDOM @@ -27,15 +19,15 @@ describe('buildVideoElement', () => { const setupRoomForTests = () => { // @ts-expect-error - room.getRTCPeerById = jest.fn((_id: string) => mockPeer) + callSessionInstance.getRTCPeerById = jest.fn((_id: string) => mockPeer) // @ts-expect-error - room.runRTCPeerWorkers(callId) + callSessionInstance.runRTCPeerWorkers(callId) } beforeEach(() => { stack = configureFullStack() store = stack.store - room = createCallSessionObject({ + callSessionInstance = createCallSessionObject({ store, }) setupRoomForTests() @@ -55,7 +47,10 @@ describe('buildVideoElement', () => { it('should take a video element and return it with unsubscribe function', async () => { const mockRootEl = document.createElement('div') - const result = await buildVideoElement({ room, rootElement: mockRootEl }) + const result = await buildVideoElement({ + callSessionInstance, + rootElement: mockRootEl, + }) expect(result).toHaveProperty('element') expect(result).toHaveProperty('unsubscribe') @@ -64,7 +59,7 @@ describe('buildVideoElement', () => { }) it('should create a video element and return it with unsubscribe function', async () => { - const result = await buildVideoElement({ room }) + const result = await buildVideoElement({ callSessionInstance }) expect(result).toHaveProperty('element') expect(result).toHaveProperty('unsubscribe') @@ -74,23 +69,32 @@ describe('buildVideoElement', () => { it('should unsubscribe from all events on unsubscribe', async () => { const mockVideoEl = document.createElement('div') - const result = await buildVideoElement({ room, rootElement: mockVideoEl }) + const result = await buildVideoElement({ + callSessionInstance, + rootElement: mockVideoEl, + }) // Mock the room.off function to spy on it - room.off = jest.fn() + callSessionInstance.off = jest.fn() result.unsubscribe() - expect(room.off).toHaveBeenCalledWith('track', expect.any(Function)) - expect(room.off).toHaveBeenCalledWith( + expect(callSessionInstance.off).toHaveBeenCalledWith( + 'track', + expect.any(Function) + ) + expect(callSessionInstance.off).toHaveBeenCalledWith( 'layout.changed', expect.any(Function) ) - expect(room.off).toHaveBeenCalledWith( + expect(callSessionInstance.off).toHaveBeenCalledWith( 'member.updated.videoMuted', expect.any(Function) ) - expect(room.off).toHaveBeenCalledWith('destroy', expect.any(Function)) + expect(callSessionInstance.off).toHaveBeenCalledWith( + 'destroy', + expect.any(Function) + ) }) describe('with remoteVideoTrack', () => { @@ -152,7 +156,7 @@ describe('buildVideoElement', () => { } const renderAndStartVideo = ( - params?: Omit + params?: Omit ) => { const { rootElement: element } = params || {} let mockRootEl: HTMLElement @@ -165,7 +169,7 @@ describe('buildVideoElement', () => { document.body.appendChild(mockRootEl) const promise = buildVideoElement({ - room, + callSessionInstance, rootElement: mockRootEl, ...params, }) @@ -189,9 +193,11 @@ describe('buildVideoElement', () => { remoteVideoTrack: new MediaStreamTrack('video'), } // @ts-expect-error - room.getRTCPeerById = jest.fn((_id: string) => newPeerMock) + callSessionInstance.getRTCPeerById = jest.fn( + (_id: string) => newPeerMock + ) // @ts-expect-error - room.localStream = new MediaStream([]) + callSessionInstance.localStream = new MediaStream([]) }) it('should make video element that contains a remote video track', async () => { @@ -204,8 +210,10 @@ describe('buildVideoElement', () => { // Check if srcObject contains the remote video track expect(videoElement!.srcObject).toBeInstanceOf(MediaStream) const mediaStream = videoElement!.srcObject as MediaStream - // @ts-expect-error - expect(mediaStream.getTracks()).toContain(room.peer.remoteVideoTrack) + expect(mediaStream.getTracks()).toContain( + // @ts-expect-error + callSessionInstance.peer.remoteVideoTrack + ) const mcuLayers = result.element.querySelector('.mcuLayers') expect(mcuLayers).not.toBeNull() @@ -439,15 +447,15 @@ describe('buildVideoElement', () => { const mcuLayers = result.element.querySelector('.mcuLayers') const videoElement = mcuLayers!.querySelector('video') - expect(room.localVideoOverlay!.mirrored).toBe(true) + expect(callSessionInstance.localVideoOverlay!.mirrored).toBe(true) expect(videoElement!.style.transform).toBe('scale(-1, 1)') - room.localVideoOverlay!.setMirror(false) - expect(room.localVideoOverlay!.mirrored).toBe(false) + callSessionInstance.localVideoOverlay!.setMirror(false) + expect(callSessionInstance.localVideoOverlay!.mirrored).toBe(false) expect(videoElement!.style.transform).toBe('scale(1, 1)') - room.localVideoOverlay!.setMirror(true) - expect(room.localVideoOverlay!.mirrored).toBe(true) + callSessionInstance.localVideoOverlay!.setMirror(true) + expect(callSessionInstance.localVideoOverlay!.mirrored).toBe(true) expect(videoElement!.style.transform).toBe('scale(-1, 1)') }) @@ -468,462 +476,13 @@ describe('buildVideoElement', () => { await renderAndStartVideo() - expect(room.localVideoOverlay!.status).toBe('hidden') - - room.localVideoOverlay!.show() - expect(room.localVideoOverlay!.status).toBe('visible') - - room.localVideoOverlay!.hide() - expect(room.localVideoOverlay!.status).toBe('hidden') - }) - }) - }) - - describe('with VideoRoomSession', () => { - let room: VideoRoomSession - let stack: ReturnType - let store: any - let jsdom: JSDOM - const callId = 'call-id-1' - const mockPeer = { - uuid: callId, - onRemoteSdp: jest.fn(), - } - - const setupRoomForTests = () => { - // @ts-expect-error - room.getRTCPeerById = jest.fn((_id: string) => mockPeer) - - // @ts-expect-error - room.runRTCPeerWorkers(callId) - } - - beforeEach(() => { - stack = configureFullStack() - store = stack.store - room = createVideoRoomSessionObject({ - store, - }) - setupRoomForTests() - - jsdom = new JSDOM('') - global.document = jsdom.window.document - global.HTMLDivElement = jsdom.window.HTMLDivElement - }) - - afterEach(() => { - jest.clearAllMocks() - // @ts-expect-error - delete global.document - // @ts-expect-error - delete global.HTMLDivElement - }) - - it('should take a video element and return it with unsubscribe function', async () => { - const mockRootEl = document.createElement('div') - const result = await buildVideoElement({ room, rootElement: mockRootEl }) - - expect(result).toHaveProperty('element') - expect(result).toHaveProperty('unsubscribe') - expect(result.element).toBeInstanceOf(HTMLDivElement) - expect(result.unsubscribe).toBeInstanceOf(Function) - }) - - it('should create a video element and return it with unsubscribe function', async () => { - const result = await buildVideoElement({ room }) - - expect(result).toHaveProperty('element') - expect(result).toHaveProperty('unsubscribe') - expect(result.element).toBeInstanceOf(HTMLDivElement) - expect(result.unsubscribe).toBeInstanceOf(Function) - }) - - it('should unsubscribe from all events on unsubscribe', async () => { - const mockVideoEl = document.createElement('div') - const result = await buildVideoElement({ room, rootElement: mockVideoEl }) - - // Mock the room.off function to spy on it - room.off = jest.fn() - - result.unsubscribe() - - expect(room.off).toHaveBeenCalledWith('track', expect.any(Function)) - expect(room.off).toHaveBeenCalledWith( - 'layout.changed', - expect.any(Function) - ) - expect(room.off).toHaveBeenCalledWith( - 'member.updated.videoMuted', - expect.any(Function) - ) - expect(room.off).toHaveBeenCalledWith('destroy', expect.any(Function)) - }) - - describe('with remoteVideoTrack', () => { - const layoutEventPayload = { - jsonrpc: '2.0', - id: '79996d32-aefd-4e37-b1c1-382144334122', - method: 'signalwire.event', - params: { - event_type: 'video.layout.changed', - event_channel: 'd97016d7-6eaa-441e-a1c5-e817672a9dcd', - timestamp: 1716832047176777, - params: { - room_id: '8facb303-63da-41c5-9f6b-4c97255cdec2', - room_session_id: callId, - layout: { - layers: [ - { - layer_index: 0, - x: 0, - y: 0, - z_index: 0, - height: 100, - width: 100, - member_id: 'member-id-1', - reservation: 'standard-1', - position: 'standard-1', - playing_file: false, - visible: true, - }, - { - layer_index: 1, - x: 0, - y: 0, - z_index: 1, - height: 100, - width: 100, - member_id: null, - reservation: 'playback', - position: 'playback', - playing_file: false, - visible: true, - }, - { - layer_index: 2, - x: 0, - y: 0, - z_index: 2, - height: 100, - width: 100, - member_id: null, - reservation: 'full-screen', - position: 'full-screen', - playing_file: false, - visible: true, - }, - ], - name: 'grid-responsive', - room_id: '8facb303-63da-41c5-9f6b-4c97255cdec2', - room_session_id: callId, - }, - }, - }, - } - - const renderAndStartVideo = ( - params?: Omit - ) => { - const { rootElement: element } = params || {} - let mockRootEl: HTMLElement - if (!element) { - mockRootEl = document.createElement('div') - mockRootEl.id = 'rootElement' - } else { - mockRootEl = element - } - document.body.appendChild(mockRootEl) - - const promise = buildVideoElement({ - room, - rootElement: mockRootEl, - ...params, - }) - - const videoElement = mockRootEl.querySelector('video') - expect(videoElement).not.toBeNull() - - // Create and dispatch the 'canplay' event using a more specific Event - const canplayEvent = document.createEvent('HTMLEvents') - canplayEvent.initEvent('canplay', true, true) - videoElement!.dispatchEvent(canplayEvent) - - return promise - } - - beforeEach(() => { - const newPeerMock = { - ...mockPeer, - hasVideoSender: true, - // @ts-expect-error - remoteVideoTrack: new MediaStreamTrack('video'), - } - // @ts-expect-error - room.getRTCPeerById = jest.fn((_id: string) => newPeerMock) - // @ts-expect-error - room.localStream = new MediaStream([]) - }) - - it('should make video element that contains a remote video track', async () => { - const result = await renderAndStartVideo() - - expect(result).toHaveProperty('element') - expect(result).toHaveProperty('unsubscribe') - - const videoElement = result.element.querySelector('video') - // Check if srcObject contains the remote video track - expect(videoElement!.srcObject).toBeInstanceOf(MediaStream) - const mediaStream = videoElement!.srcObject as MediaStream - // @ts-expect-error - expect(mediaStream.getTracks()).toContain(room.peer.remoteVideoTrack) - - const mcuLayers = result.element.querySelector('.mcuLayers') - expect(mcuLayers).not.toBeNull() - expect(mcuLayers!.childElementCount).toBe(0) - }) - - it('should render the mcuLayers with video and member overlay', async () => { - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session: stack.session, - callId: callId, - roomId: 'room-id-1', - roomSessionId: callId, - memberId: 'member-id-1', - }) - - // @ts-expect-error - stack.session.dispatch(actions.socketMessageAction(layoutEventPayload)) - - const result = await renderAndStartVideo() - - expect(result).toHaveProperty('element') - expect(result).toHaveProperty('unsubscribe') - - const mcuLayers = result.element.querySelector('.mcuLayers') - expect(mcuLayers).not.toBeNull() - expect(mcuLayers!.childElementCount).toBe(2) - - const children = Array.from(mcuLayers!.children) - - // Check if the video overlay is present - const videoOverlay = children.find((child) => - child.id.startsWith(SDK_PREFIX) - ) - expect(videoOverlay).toBeDefined() - expect(videoOverlay?.id.startsWith(SDK_PREFIX)).toBe(true) - - // Check if the member overlay is present - const memberOverlay = children.find((child) => - child.id.startsWith(addOverlayPrefix('member-id-1')) - ) - expect(memberOverlay).toBeDefined() - expect( - memberOverlay?.id.startsWith(addOverlayPrefix('member-id-1')) - ).toBe(true) - }) - - it('should not render the video overlay if applyLocalVideoOverlay is false', async () => { - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session: stack.session, - callId: callId, - roomId: 'room-id-1', - roomSessionId: callId, - memberId: 'member-id-1', - }) - - // @ts-expect-error - stack.session.dispatch(actions.socketMessageAction(layoutEventPayload)) - - const result = await renderAndStartVideo({ - applyLocalVideoOverlay: false, - }) - - expect(result).toHaveProperty('element') - expect(result).toHaveProperty('unsubscribe') - - const mcuLayers = result.element.querySelector('.mcuLayers') - expect(mcuLayers).not.toBeNull() - expect(mcuLayers!.childElementCount).toBe(1) - - const children = Array.from(mcuLayers!.children) - - // Check if the video overlay is present - const videoOverlay = children.find((child) => - child.id.startsWith(SDK_PREFIX) - ) - expect(videoOverlay).not.toBeDefined() - - // Check if the member overlay is present - const memberOverlay = children.find((child) => - child.id.startsWith(addOverlayPrefix('member-id-1')) - ) - expect(memberOverlay).toBeDefined() - expect( - memberOverlay?.id.startsWith(addOverlayPrefix('member-id-1')) - ).toBe(true) - }) - - it('should not render the member overlay if applyMemberOverlay is false', async () => { - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session: stack.session, - callId: callId, - roomId: 'room-id-1', - roomSessionId: callId, - memberId: 'member-id-1', - }) - - // @ts-expect-error - stack.session.dispatch(actions.socketMessageAction(layoutEventPayload)) - - const result = await renderAndStartVideo({ applyMemberOverlay: false }) - - expect(result).toHaveProperty('element') - expect(result).toHaveProperty('unsubscribe') - - const mcuLayers = result.element.querySelector('.mcuLayers') - expect(mcuLayers).not.toBeNull() - expect(mcuLayers!.childElementCount).toBe(1) - - const children = Array.from(mcuLayers!.children) - - // Check if the video overlay is present - const videoOverlay = children.find((child) => - child.id.startsWith(SDK_PREFIX) - ) - expect(videoOverlay).toBeDefined() - expect(videoOverlay?.id.startsWith(SDK_PREFIX)).toBe(true) - - // Check if the member overlay is present - const memberOverlay = children.find((child) => - child.id.startsWith(addOverlayPrefix('member-id-1')) - ) - expect(memberOverlay).not.toBeDefined() - }) - - it('should render two elements if the IDs are different', async () => { - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session: stack.session, - callId: callId, - roomId: 'room-id-1', - roomSessionId: callId, - memberId: 'member-id-1', - }) - - // @ts-expect-error - stack.session.dispatch(actions.socketMessageAction(layoutEventPayload)) - - const mockRootEl1 = document.createElement('div') - mockRootEl1.id = 'rootElement1' - - const result1 = await renderAndStartVideo({ rootElement: mockRootEl1 }) - expect(result1).toHaveProperty('element') - expect(result1).toHaveProperty('unsubscribe') - - const mcuLayers1 = result1.element.querySelector('.mcuLayers') - expect(mcuLayers1).not.toBeNull() - expect(mcuLayers1!.childElementCount).toBe(2) - - const mockRootEl2 = document.createElement('div') - mockRootEl2.id = 'rootElement2' - - const result2 = await renderAndStartVideo({ rootElement: mockRootEl2 }) - expect(result2).toHaveProperty('element') - expect(result2).toHaveProperty('unsubscribe') - - const mcuLayers2 = result2.element.querySelector('.mcuLayers') - expect(mcuLayers2).not.toBeNull() - expect(mcuLayers2!.childElementCount).toBe(2) - - expect(mcuLayers1).not.toBe(mcuLayers2) - }) - - it('should render only one element if the IDs are same', async () => { - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session: stack.session, - callId: callId, - roomId: 'room-id-1', - roomSessionId: callId, - memberId: 'member-id-1', - }) - // @ts-expect-error - stack.session.dispatch(actions.socketMessageAction(layoutEventPayload)) - - const mockRootEl = document.createElement('div') - mockRootEl.id = 'rootElement1' - - const result1 = await renderAndStartVideo({ rootElement: mockRootEl }) - expect(result1).toHaveProperty('element') - expect(result1).toHaveProperty('unsubscribe') - - const mcuLayers1 = result1.element.querySelector('.mcuLayers') - expect(mcuLayers1).not.toBeNull() - expect(mcuLayers1!.childElementCount).toBe(2) - - const result2 = await renderAndStartVideo({ rootElement: mockRootEl }) - expect(result2).toHaveProperty('element') - expect(result2).toHaveProperty('unsubscribe') - - const mcuLayers2 = result2.element.querySelector('.mcuLayers') - expect(mcuLayers2).not.toBeNull() - expect(mcuLayers2!.childElementCount).toBe(2) - - expect(mcuLayers1).toBe(mcuLayers2) - }) - - it('should mirror the video overlay', async () => { - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session: stack.session, - callId: callId, - roomId: 'room-id-1', - roomSessionId: callId, - memberId: 'member-id-1', - }) - // @ts-expect-error - stack.session.dispatch(actions.socketMessageAction(layoutEventPayload)) - - const result = await renderAndStartVideo() - const mcuLayers = result.element.querySelector('.mcuLayers') - const videoElement = mcuLayers!.querySelector('video') - - expect(room.localVideoOverlay!.mirrored).toBe(true) - expect(videoElement!.style.transform).toBe('scale(-1, 1)') - - room.localVideoOverlay!.setMirror(false) - expect(room.localVideoOverlay!.mirrored).toBe(false) - expect(videoElement!.style.transform).toBe('scale(1, 1)') - - room.localVideoOverlay!.setMirror(true) - expect(room.localVideoOverlay!.mirrored).toBe(true) - expect(videoElement!.style.transform).toBe('scale(-1, 1)') - }) - - it('should hide the video overlay', async () => { - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session: stack.session, - callId: callId, - roomId: 'room-id-1', - roomSessionId: callId, - memberId: 'member-id-1', - }) - // @ts-expect-error - stack.session.dispatch(actions.socketMessageAction(layoutEventPayload)) - - await renderAndStartVideo() - - expect(room.localVideoOverlay!.status).toBe('hidden') + expect(callSessionInstance.localVideoOverlay!.status).toBe('hidden') - room.localVideoOverlay!.show() - expect(room.localVideoOverlay!.status).toBe('visible') + callSessionInstance.localVideoOverlay!.show() + expect(callSessionInstance.localVideoOverlay!.status).toBe('visible') - room.localVideoOverlay!.hide() - expect(room.localVideoOverlay!.status).toBe('hidden') + callSessionInstance.localVideoOverlay!.hide() + expect(callSessionInstance.localVideoOverlay!.status).toBe('hidden') }) }) }) diff --git a/packages/client/src/buildVideoElement.ts b/packages/client/src/buildVideoElement.ts index c4b41408e..8ff8a20e0 100644 --- a/packages/client/src/buildVideoElement.ts +++ b/packages/client/src/buildVideoElement.ts @@ -11,16 +11,15 @@ import { setVideoMediaTrack, waitForVideoReady, } from './utils/videoElement' -import { addSDKPrefix } from './utils/roomSession' +import { addSDKPrefix } from './utils/callSession' import { OverlayMap, LocalVideoOverlay } from './VideoOverlays' -import { CallSession, isCallSession } from './unified/CallSession' -import { VideoRoomSession, isVideoRoomSession } from './video/VideoRoomSession' +import { CallSession } from './unified/CallSession' export interface BuildVideoElementParams { applyLocalVideoOverlay?: boolean applyMemberOverlay?: boolean mirrorLocalVideoOverlay?: boolean - room: CallSession | VideoRoomSession + callSessionInstance: CallSession rootElement?: HTMLElement } @@ -36,7 +35,7 @@ export const buildVideoElement = async ( ): Promise => { try { const { - room, + callSessionInstance, rootElement: element, applyLocalVideoOverlay = true, applyMemberOverlay = true, @@ -63,7 +62,7 @@ export const buildVideoElement = async ( const localVideoOverlay = new LocalVideoOverlay({ id: overlayId, mirrorLocalVideoOverlay, - room, + room: callSessionInstance, }) if (applyLocalVideoOverlay) { overlayMap.set(overlayId, localVideoOverlay) @@ -80,11 +79,11 @@ export const buildVideoElement = async ( const processLayoutChanged = (params: any) => { // @ts-expect-error - if (room.peer?.hasVideoSender && room.localStream) { + if (callSessionInstance.peer?.hasVideoSender && callSessionInstance.localStream) { makeLayout({ layout: params.layout, - localStream: room.localStream, - memberId: room.memberId, + localStream: callSessionInstance.localStream, + memberId: callSessionInstance.memberId, }) } else { localVideoOverlay.hide() @@ -109,16 +108,16 @@ export const buildVideoElement = async ( track, }) - const roomCurrentLayoutEvent = room.currentLayoutEvent + const currentLayoutEvent = callSessionInstance.currentLayoutEvent // If the `layout.changed` has already been received, process the layout - if (roomCurrentLayoutEvent) { - processLayoutChanged(roomCurrentLayoutEvent) + if (currentLayoutEvent) { + processLayoutChanged(currentLayoutEvent) } } // If the remote video already exist, inject the remote stream to the video element // @ts-expect-error - const videoTrack = room.peer?.remoteVideoTrack as MediaStreamTrack | null + const videoTrack = callSessionInstance.peer?.remoteVideoTrack as MediaStreamTrack | null if (videoTrack) { await processVideoTrack(videoTrack) } @@ -133,16 +132,12 @@ export const buildVideoElement = async ( const unsubscribe = () => { cleanupElement(rootElement) overlayMap.clear() // Use "delete" rather than "clear" if we want to update the reference - room.overlayMap = overlayMap - if (isCallSession(room)) { - room.off('track', trackHandler) - room.off('layout.changed', layoutChangedHandler) - room.off('destroy', unsubscribe) - } else if (isVideoRoomSession(room)) { - room.off('track', trackHandler) - room.off('layout.changed', layoutChangedHandler) - room.off('destroy', unsubscribe) - } + callSessionInstance.overlayMap = overlayMap + + callSessionInstance.off('track', trackHandler) + callSessionInstance.off('layout.changed', layoutChangedHandler) + callSessionInstance.off('destroy', unsubscribe) + localVideoOverlay.detachListeners() } @@ -151,15 +146,9 @@ export const buildVideoElement = async ( * there are cases (promote/demote) where we need to handle multiple `track` * events and update the videoEl with the new track. */ - if (isCallSession(room)) { - room.on('track', trackHandler) - room.on('layout.changed', layoutChangedHandler) - room.once('destroy', unsubscribe) - } else if (isVideoRoomSession(room)) { - room.on('track', trackHandler) - room.on('layout.changed', layoutChangedHandler) - room.once('destroy', unsubscribe) - } + callSessionInstance.on('track', trackHandler) + callSessionInstance.on('layout.changed', layoutChangedHandler) + callSessionInstance.once('destroy', unsubscribe) /** * The room object is only being used to listen for events. @@ -167,8 +156,8 @@ export const buildVideoElement = async ( * Currently, we are overriding the following room properties in case the user calls "buildVideoElement" more than once. * However, this can be moved out of here easily if we prefer not to override. */ - room.overlayMap = overlayMap - room.localVideoOverlay = localVideoOverlay + callSessionInstance.overlayMap = overlayMap + callSessionInstance.localVideoOverlay = localVideoOverlay return { element: rootElement, overlayMap, localVideoOverlay, unsubscribe } } catch (error) { diff --git a/packages/client/src/features/mediaElements/mediaElementsSagas.ts b/packages/client/src/features/mediaElements/mediaElementsSagas.ts index bddc900f3..3c9b2d6ba 100644 --- a/packages/client/src/features/mediaElements/mediaElementsSagas.ts +++ b/packages/client/src/features/mediaElements/mediaElementsSagas.ts @@ -9,13 +9,13 @@ import type { SagaIterator, Task } from '@signalwire/core' import { setMediaElementSinkId } from '@signalwire/webrtc' import { setAudioMediaTrack } from '../../utils/audioElement' import { audioSetSpeakerAction } from '../actions' -import { VideoRoomSessionConnection } from '../../video/VideoRoomSession' +import { CallSessionConnection } from '../../unified/CallSession' export const makeAudioElementSaga = ({ speakerId }: { speakerId?: string }) => { return function* audioElementSaga({ instance: room, runSaga, - }: CustomSagaParams): SagaIterator { + }: CustomSagaParams): SagaIterator { if (typeof Audio === 'undefined') { getLogger().warn('`Audio` is not supported on this environment.') return @@ -59,7 +59,7 @@ function* audioElementActionsWatcher({ room, }: { element: HTMLAudioElement - room: VideoRoomSessionConnection + room: CallSessionConnection }): SagaIterator { // TODO: For now we're handling individual actions but in the future // we might want to have a single action per custom saga and use it @@ -114,7 +114,7 @@ function* audioElementSetupWorker({ track: MediaStreamTrack element: HTMLAudioElement speakerId?: string - room: VideoRoomSessionConnection + room: CallSessionConnection }): SagaIterator { setAudioMediaTrack({ track, element }) if (speakerId) { diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 0852db0d5..016b9e731 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -48,9 +48,6 @@ export type { } from '@signalwire/webrtc' export type { CallJoinedEventParams, - RoomSessionObjectEventsHandlerMap, - RoomSessionObjectEvents, - RoomEventNames, StartScreenShareOptions, CallSessionEvents, } from './utils/interfaces' @@ -135,8 +132,8 @@ export * as Call from './unified' export { SignalWire } from './unified' export * from './unified' -export { RoomSessionScreenShare } from './RoomSessionScreenShare' -export { RoomSessionDevice } from './RoomSessionDevice' +export { CallSessionScreenShare } from './CallSessionScreenShare' +export { CallSessionDevice } from './CallSessionDevice' /** * The WebRTC namespace includes functions that give you access to the input and diff --git a/packages/client/src/unified/CallSession.ts b/packages/client/src/unified/CallSession.ts index caa7eedfa..25ca8bb55 100644 --- a/packages/client/src/unified/CallSession.ts +++ b/packages/client/src/unified/CallSession.ts @@ -16,11 +16,11 @@ import { CallSessionMethods, } from '@signalwire/core' import { - BaseRoomSessionConnection, - BaseRoomSessionOptions, -} from '../BaseRoomSession' + BaseCallSessionConnection, + BaseCallSessionOptions, +} from '../BaseCallSession' import { - BaseRoomSessionContract, + BaseCallSessionContract, ExecuteMemberActionParams, CallSessionContract, CallSessionEvents, @@ -33,19 +33,18 @@ import { makeAudioElementSaga } from '../features/mediaElements/mediaElementsSag import { CallCapabilitiesContract } from './interfaces/capabilities' import { createCallSessionValidateProxy } from './utils/validationProxy' - export interface CallSession extends CallSessionContract, CallSessionMethods, - BaseRoomSessionContract, + BaseCallSessionContract, BaseConnectionContract, BaseComponentContract {} export interface CallSessionOptions - extends Omit {} + extends Omit {} export class CallSessionConnection - extends BaseRoomSessionConnection + extends BaseCallSessionConnection implements CallSessionContract { // this is "self" parameter required by the RPC, and is always "the member" on the 1st call segment @@ -443,10 +442,10 @@ export const isCallSession = (room: unknown): room is CallSession => { export const createCallSessionObject = ( params: CallSessionOptions ): CallSession => { - const room = connect({ + const instance = connect({ store: params.store, Component: CallSessionConnection, })(params) - return createCallSessionValidateProxy(room) + return createCallSessionValidateProxy(instance) } diff --git a/packages/client/src/unified/WSClient.ts b/packages/client/src/unified/WSClient.ts index 881df1872..3e1b2c43c 100644 --- a/packages/client/src/unified/WSClient.ts +++ b/packages/client/src/unified/WSClient.ts @@ -8,7 +8,7 @@ import { VertoSubscribe, } from '@signalwire/core' import { sessionConnectionPoolWorker } from '@signalwire/webrtc' -import { MakeRoomOptions } from '../video' +import { MakeRoomOptions } from '../Client' import { createCallSessionObject, CallSession } from './CallSession' import { buildVideoElement } from '../buildVideoElement' import { @@ -70,7 +70,7 @@ export class WSClient extends BaseClient<{}> implements WSClientContract { ...options } = makeRoomOptions - const room = createCallSessionObject({ + const instance = createCallSessionObject({ ...options, store: this.store, }) @@ -85,7 +85,7 @@ export class WSClient extends BaseClient<{}> implements WSClientContract { applyLocalVideoOverlay, applyMemberOverlay, mirrorLocalVideoOverlay, - room, + callSessionInstance: instance, rootElement, }) } catch (error) { @@ -100,12 +100,12 @@ export class WSClient extends BaseClient<{}> implements WSClientContract { */ const joinMutedHandler = (params: InternalCallJoinedEventParams) => { const member = params.room_session.members?.find( - (m) => m.member_id === room.memberId + (m) => m.member_id === instance.memberId ) if (member?.audio_muted) { try { - room.stopOutboundAudio() + instance.stopOutboundAudio() } catch (error) { this.logger.error('Error handling audio_muted', error) } @@ -113,28 +113,31 @@ export class WSClient extends BaseClient<{}> implements WSClientContract { if (member?.video_muted) { try { - room.stopOutboundVideo() + instance.stopOutboundVideo() } catch (error) { this.logger.error('Error handling video_muted', error) } } } - room.on('room.subscribed', joinMutedHandler) + instance.on('room.subscribed', joinMutedHandler) /** * Stop or Restore outbound audio on "member.updated" event */ if (stopMicrophoneWhileMuted) { - room.on( + instance.on( 'member.updated.audioMuted', (params: MemberUpdatedEventParams) => { const { member } = params try { - if (member.member_id === room.memberId && 'audio_muted' in member) { + if ( + member.member_id === instance.memberId && + 'audio_muted' in member + ) { member.audio_muted - ? room.stopOutboundAudio() - : room.restoreOutboundAudio() + ? instance.stopOutboundAudio() + : instance.restoreOutboundAudio() } } catch (error) { this.logger.error('Error handling audio_muted', error) @@ -147,14 +150,17 @@ export class WSClient extends BaseClient<{}> implements WSClientContract { * Stop or Restore outbound video on "member.updated" event */ if (stopCameraWhileMuted) { - room.on( + instance.on( 'member.updated.videoMuted', ({ member }: MemberUpdatedEventParams) => { try { - if (member.member_id === room.memberId && 'video_muted' in member) { + if ( + member.member_id === instance.memberId && + 'video_muted' in member + ) { member.video_muted - ? room.stopOutboundVideo() - : room.restoreOutboundVideo() + ? instance.stopOutboundVideo() + : instance.restoreOutboundVideo() } } catch (error) { this.logger.error('Error handling video_muted', error) @@ -163,7 +169,7 @@ export class WSClient extends BaseClient<{}> implements WSClientContract { ) } - return room + return instance } private buildOutboundCall(params: ReattachParams & { attach?: boolean }) { diff --git a/packages/client/src/unified/utils/eventMappers.ts b/packages/client/src/unified/utils/eventMappers.ts index 5fdcd32ed..6390e8b8d 100644 --- a/packages/client/src/unified/utils/eventMappers.ts +++ b/packages/client/src/unified/utils/eventMappers.ts @@ -25,7 +25,7 @@ import { MemberUpdatedEvent, MemberUpdatedEventParams, } from '@signalwire/core' -import {} from '../../utils/interfaces/fabric' +import {} from '../../utils/interfaces/call' /** * Map the InternalMemberEntity to InternalVideoMemberEntity diff --git a/packages/client/src/unified/workers/callJoinWorker.ts b/packages/client/src/unified/workers/callJoinWorker.ts index a582ec3f5..d2af18614 100644 --- a/packages/client/src/unified/workers/callJoinWorker.ts +++ b/packages/client/src/unified/workers/callJoinWorker.ts @@ -19,7 +19,7 @@ export const callJoinWorker = function* ( options: CallWorkerParams ): SagaIterator { getLogger().trace('callJoinWorker started') - const { action, instanceMap, instance: cfRoomSession } = options + const { action, instanceMap, instance: callSession } = options const { payload } = action const { get, set } = instanceMap @@ -56,7 +56,7 @@ export const callJoinWorker = function* ( let memberInstance = get(member.member_id!) if (!memberInstance) { memberInstance = createCallSessionMemberObject({ - store: cfRoomSession.store, + store: callSession.store, payload: { member: member, room_id: payload.room_id, @@ -73,16 +73,16 @@ export const callJoinWorker = function* ( set(member.member_id, memberInstance) }) - cfRoomSession.member = get(payload.member_id) + callSession.member = get(payload.member_id) // the server send the capabilities payload as an array of string - cfRoomSession.capabilities = mapCapabilityPayload(payload.capabilities) + callSession.capabilities = mapCapabilityPayload(payload.capabilities) const fabricEvent = { ...payload, - capabilities: cfRoomSession.capabilities, + capabilities: callSession.capabilities, } - cfRoomSession.emit('call.joined', fabricEvent) + callSession.emit('call.joined', fabricEvent) getLogger().trace('callJoinWorker ended') } diff --git a/packages/client/src/unified/workers/callLeftWorker.ts b/packages/client/src/unified/workers/callLeftWorker.ts index 3b800379a..ba21b362b 100644 --- a/packages/client/src/unified/workers/callLeftWorker.ts +++ b/packages/client/src/unified/workers/callLeftWorker.ts @@ -9,7 +9,7 @@ export const callLeftWorker = function* ( const { action: { payload }, - instance: cfRoomSession, + instance: callSession, instanceMap, } = options @@ -25,8 +25,8 @@ export const callLeftWorker = function* ( } }) - cfRoomSession.emit('call.left', payload) - cfRoomSession.emit('room.left', payload) + callSession.emit('call.left', payload) + callSession.emit('room.left', payload) getLogger().trace('callLeftWorker ended') } diff --git a/packages/client/src/unified/workers/callSegmentWorker.ts b/packages/client/src/unified/workers/callSegmentWorker.ts index 6ca6b4ef1..f29a52f36 100644 --- a/packages/client/src/unified/workers/callSegmentWorker.ts +++ b/packages/client/src/unified/workers/callSegmentWorker.ts @@ -22,7 +22,7 @@ export const callSegmentWorker = function* ( const { action, channels: { swEventChannel }, - instance: cfRoomSession, + instance: callSession, } = options const segmentCallId = action.payload.call_id const segmentRooSessionId = action.payload.room_session_id @@ -52,17 +52,17 @@ export const callSegmentWorker = function* ( }) return true case 'call.updated': - cfRoomSession.emit(type, payload) - cfRoomSession.emit('room.updated', payload) + callSession.emit(type, payload) + callSession.emit('room.updated', payload) break case 'call.play': - cfRoomSession.emit(type, payload) + callSession.emit(type, payload) break case 'call.connect': - cfRoomSession.emit(type, payload) + callSession.emit(type, payload) break case 'call.room': - cfRoomSession.emit(type, payload) + callSession.emit(type, payload) break /** @@ -97,8 +97,8 @@ export const callSegmentWorker = function* ( } case 'layout.changed': { // We need to update the layout event which is needed for rootElement. - cfRoomSession.currentLayoutEvent = action.payload - cfRoomSession.emit(type, payload) + callSession.currentLayoutEvent = action.payload + callSession.emit(type, payload) const videoAction = mapCallLayoutActionToVideoLayoutAction(action) yield sagaEffects.put(swEventChannel, videoAction) break diff --git a/packages/client/src/unified/workers/fabricMemberWorker.ts b/packages/client/src/unified/workers/fabricMemberWorker.ts index 156f0015f..bab4623fd 100644 --- a/packages/client/src/unified/workers/fabricMemberWorker.ts +++ b/packages/client/src/unified/workers/fabricMemberWorker.ts @@ -6,7 +6,7 @@ import { MemberEvent, MemberUpdatedEventParams, } from '@signalwire/core' -import {} from '../../utils/interfaces/fabric' +import {} from '../../utils/interfaces/call' import { CallWorkerParams } from './fabricWorker' import { createCallSessionMemberObject, diff --git a/packages/client/src/unified/workers/fabricWorker.ts b/packages/client/src/unified/workers/fabricWorker.ts index ca3cf85d2..10ec53043 100644 --- a/packages/client/src/unified/workers/fabricWorker.ts +++ b/packages/client/src/unified/workers/fabricWorker.ts @@ -22,7 +22,7 @@ export const fabricWorker: SDKWorker = function* ( getLogger().trace('fabricWorker started') const { channels: { swEventChannel }, - instance: cfRoomSession, + instance: callSession, } = options function* worker(action: CallAction) { @@ -30,12 +30,12 @@ export const fabricWorker: SDKWorker = function* ( switch (type) { case 'call.joined': { - // since we depend on `cfRoomSession.selfMember` on the take logic - // we need to make sure we update the `cfRoomSession.selfMember` + // since we depend on `callSession.selfMember` on the take logic + // we need to make sure we update the `callSession.selfMember` // in this worker or have a race condition. - if (!cfRoomSession.selfMember) { + if (!callSession.selfMember) { const memberInstance = createCallSessionMemberObject({ - store: cfRoomSession.store, + store: callSession.store, payload: { member: action.payload.room_session.members.find( (m) => m.member_id === action.payload.member_id @@ -44,19 +44,19 @@ export const fabricWorker: SDKWorker = function* ( room_session_id: action.payload.room_session_id, }, }) - cfRoomSession.selfMember = memberInstance + callSession.selfMember = memberInstance } // Segment worker for each call_id yield sagaEffects.fork(callSegmentWorker, { ...options, - instance: cfRoomSession, + instance: callSession, action, }) break } case 'call.state': - cfRoomSession.emit(type, payload) + callSession.emit(type, payload) break } } @@ -73,7 +73,7 @@ export const fabricWorker: SDKWorker = function* ( // If this is the first call.joined event, verify the call origin ID if (!firstCallJoinedReceived) { - if (action.payload.call_id === cfRoomSession.callId) { + if (action.payload.call_id === callSession.callId) { firstCallJoinedReceived = true return true } diff --git a/packages/client/src/utils/roomSession.test.ts b/packages/client/src/utils/callSession.test.ts similarity index 94% rename from packages/client/src/utils/roomSession.test.ts rename to packages/client/src/utils/callSession.test.ts index 3a91cb83e..0d3bfa329 100644 --- a/packages/client/src/utils/roomSession.test.ts +++ b/packages/client/src/utils/callSession.test.ts @@ -1,6 +1,6 @@ import type { VideoAuthorization } from '@signalwire/core' -import { BaseRoomSessionJoinParams } from './interfaces' -import { getJoinMediaParams } from './roomSession' +import { BaseCallSessionDialParams } from './interfaces' +import { getJoinMediaParams } from './callSession' describe('getJoinMediaParams', () => { const authorization: VideoAuthorization = { @@ -52,7 +52,7 @@ describe('getJoinMediaParams', () => { * Build all the combinations of arguments that `getJoinMediaParams()` * could receive. */ - const keys: (keyof BaseRoomSessionJoinParams)[] = [ + const keys: (keyof BaseCallSessionDialParams)[] = [ 'sendAudio', 'receiveAudio', 'sendVideo', @@ -60,11 +60,11 @@ describe('getJoinMediaParams', () => { 'audio', 'video', ] - const cases: BaseRoomSessionJoinParams[] = [] + const cases: BaseCallSessionDialParams[] = [] const booleans = [true, false] keys.forEach((key, index) => { booleans.forEach((bool) => { - const tmp: BaseRoomSessionJoinParams = {} + const tmp: BaseCallSessionDialParams = {} for (let i = index; i < keys.length; i++) { tmp[keys[i]] = bool } diff --git a/packages/client/src/utils/roomSession.ts b/packages/client/src/utils/callSession.ts similarity index 95% rename from packages/client/src/utils/roomSession.ts rename to packages/client/src/utils/callSession.ts index a3ddf9581..0a49b647c 100644 --- a/packages/client/src/utils/roomSession.ts +++ b/packages/client/src/utils/callSession.ts @@ -1,8 +1,8 @@ import { getLogger } from '@signalwire/core' import type { VideoAuthorization } from '@signalwire/core' -import type { BaseRoomSessionJoinParams } from './interfaces' +import type { BaseCallSessionDialParams } from './interfaces' -type GetJoinMediaParamsOptions = BaseRoomSessionJoinParams & { +type GetJoinMediaParamsOptions = BaseCallSessionDialParams & { authorization: VideoAuthorization } /** diff --git a/packages/client/src/utils/interfaces/base.ts b/packages/client/src/utils/interfaces/base.ts index db83ce30e..60749578c 100644 --- a/packages/client/src/utils/interfaces/base.ts +++ b/packages/client/src/utils/interfaces/base.ts @@ -1,8 +1,8 @@ -import { RoomSessionScreenShare } from '../../RoomSessionScreenShare' +import { CallSessionScreenShare } from '../../CallSessionScreenShare' import { LocalVideoOverlay, OverlayMap, UserOverlay } from '../../VideoOverlays' -import { StartScreenShareOptions } from './video' +import { StartScreenShareOptions } from './call' -export interface BaseRoomSessionContract { +export interface BaseCallSessionContract { /** * A JS Map containing all the layers on top of the Root Element */ @@ -14,7 +14,7 @@ export interface BaseRoomSessionContract { /** * List of screen share objects */ - screenShareList: RoomSessionScreenShare[] + screenShareList: CallSessionScreenShare[] /** * Leaves the room. This detaches all the locally originating streams from the room. */ @@ -27,7 +27,7 @@ export interface BaseRoomSessionContract { * Adds a screen sharing instance to the room. You can create multiple screen * sharing instances and add all of them to the room. * @param opts - {@link StartScreenShareOptions} - * @returns - {@link RoomSessionScreenShare} + * @returns - {@link CallSessionScreenShare} * * @example Sharing the screen together with the associated audio: * ```js @@ -36,7 +36,7 @@ export interface BaseRoomSessionContract { */ startScreenShare( opts?: StartScreenShareOptions - ): Promise + ): Promise /** * Replaces the current speaker with a different one. * diff --git a/packages/client/src/utils/interfaces/fabric.ts b/packages/client/src/utils/interfaces/call.ts similarity index 72% rename from packages/client/src/utils/interfaces/fabric.ts rename to packages/client/src/utils/interfaces/call.ts index 2d2695de1..9fbc5257d 100644 --- a/packages/client/src/utils/interfaces/fabric.ts +++ b/packages/client/src/utils/interfaces/call.ts @@ -8,8 +8,8 @@ import { RoomSubscribed, RTCTrackEventName, VideoLayoutEventNames, - VideoRoomDeviceDisconnectedEventNames, - VideoRoomDeviceUpdatedEventNames, + CallSessionDeviceDisconnectedEventNames, + CallSessionDeviceUpdatedEventNames, CallJoined, CallJoinedEventParams as InternalCallJoinedEventParams, CallState, @@ -39,6 +39,8 @@ import { CallSessionEventParams, MemberUpdatedEventParams, MemberUpdatedEventNames, + type VideoPositions, + type Rooms, } from '@signalwire/core' import { MediaEventNames } from '@signalwire/webrtc' import { CallCapabilitiesContract, CallSession } from '../../unified' @@ -78,11 +80,11 @@ export type CallJoinedEventParams = { } & Omit export type CallSessionEventsHandlerMap = Record< - VideoRoomDeviceUpdatedEventNames, + CallSessionDeviceUpdatedEventNames, (params: DeviceUpdatedEventParams) => void > & Record< - VideoRoomDeviceDisconnectedEventNames, + CallSessionDeviceDisconnectedEventNames, (params: DeviceDisconnectedEventParams) => void > & Record void> & @@ -240,3 +242,111 @@ export interface CallSessionContract { */ hangup(id?: string): Promise } +export type StartScreenShareOptions = { + /** Whether the screen share object should automatically join the room */ + autoJoin?: boolean + /** Audio constraints to use when joining the room. Default: `true`. */ + audio?: MediaStreamConstraints['audio'] + /** Video constraints to use when joining the room. Default: `true`. */ + video?: MediaStreamConstraints['video'] + layout?: string + positions?: VideoPositions +} +interface CallMemberSelfMethodsInterface { + /** + * Puts the microphone on mute. The other participants will not hear audio + * from the muted device anymore. + * @example Muting the microphone: + * ```typescript + * await roomdevice.audioMute() + * ``` + */ + audioMute(): Rooms.AudioMuteMember + + /** + * Unmutes the microphone if it had been previously muted. + * + * @example Unmuting the microphone: + * ```typescript + * await calldevice.audioUnmute() + * ``` + */ + audioUnmute(): Rooms.AudioUnmuteMember + + /** + * Puts the video on mute. Participants will see a mute image instead of the + * video stream. + * + * @example Muting the camera: + * ```typescript + * await roomdevice.videoMute() + * ``` + */ + videoMute(): Rooms.VideoMuteMember + + /** + * Unmutes the video if it had been previously muted. Participants will start + * seeing the video stream again. + * + * @example Unmuting the camera: + * ```typescript + * await roomdevice.videoUnmute() + * ``` + */ + videoUnmute(): Rooms.VideoUnmuteMember + + /** + * @deprecated Use {@link setInputVolume} instead. + */ + setMicrophoneVolume(params: { volume: number }): Rooms.SetInputVolumeMember + + /** + * Sets the input volume level (e.g. for the microphone). + * @param params + * @param params.volume desired volume. Values range from -50 to 50, with a + * default of 0. + * + * @example + * ```typescript + * await roomdevice.setMicrophoneVolume({volume: -10}) + * ``` + */ + setInputVolume(params: { volume: number }): Rooms.SetInputVolumeMember + + /** + * Sets the input level at which the participant is identified as currently + * speaking. + * @param params + * @param params.value desired sensitivity. The default value is 30 and the + * scale goes from 0 (lowest sensitivity, essentially muted) to 100 (highest + * sensitivity). + * + * @example + * ```typescript + * await roomdevice.setInputSensitivity({value: 80}) + * ``` + */ + setInputSensitivity(params: { + value: number + }): Rooms.SetInputSensitivityMember +} + +export interface CallSessionDeviceMethods + extends CallMemberSelfMethodsInterface {} + +export interface CallSessionScreenShareMethods + extends CallMemberSelfMethodsInterface {} + +export interface BaseCallSessionDialParams { + audio?: MediaStreamConstraints['audio'] + video?: MediaStreamConstraints['video'] + receiveAudio?: boolean + receiveVideo?: boolean + sendAudio?: boolean + sendVideo?: boolean +} + +export type AudioElement = HTMLAudioElement & { + sinkId?: string + setSinkId?: (id: string) => Promise +} diff --git a/packages/client/src/utils/interfaces/index.ts b/packages/client/src/utils/interfaces/index.ts index f916bfd29..9954af4fa 100644 --- a/packages/client/src/utils/interfaces/index.ts +++ b/packages/client/src/utils/interfaces/index.ts @@ -1,3 +1,2 @@ export * from './base' -export * from './video' -export * from './fabric' +export * from './call' diff --git a/packages/client/src/utils/interfaces/video.ts b/packages/client/src/utils/interfaces/video.ts deleted file mode 100644 index 47bb135aa..000000000 --- a/packages/client/src/utils/interfaces/video.ts +++ /dev/null @@ -1,440 +0,0 @@ -import type { - Rooms, - VideoLayoutEventNames, - VideoRoomSessionEventNames, - VideoRoomEventParams, - InternalVideoMemberEntity, - InternalVideoMemberEntityUpdated, - VideoMemberEventNames, - MemberUpdated, - CoreMemberUpdatedEventNames, - MemberTalkingEventNames, - VideoMemberTalkingEventParams, - RTCTrackEventName, - InternalVideoMemberUpdatableProps, - VideoRecordingEventNames, - VideoPlaybackEventNames, - RoomSessionRecording, - RoomSessionPlayback, - VideoRoomSessionContract as CoreVideoRoomSessionContract, - OnlyFunctionProperties, - MemberListUpdated, - VideoPositions, - RoomAudienceCount, - VideoRoomAudienceCountEventParams, - RoomLeft, - RoomLeftEventParams, - VideoStreamEventNames, - RoomSessionStream, - RoomJoined, - RoomSubscribed, - VideoRoomSubscribedEventParams, - VideoAuthorization, - VideoRoomDeviceUpdatedEventNames, - DeviceUpdatedEventParams, - VideoRoomDeviceDisconnectedEventNames, - DeviceDisconnectedEventParams, - VideoRoomDeviceEventNames, - VideoLayoutChangedEventParams, - VideoPosition, - BaseConnectionState, -} from '@signalwire/core' -import { INTERNAL_MEMBER_UPDATABLE_PROPS } from '@signalwire/core' -import type { MediaEventNames } from '@signalwire/webrtc' -import type { RoomSessionDevice } from '../../RoomSessionDevice' -import type { RoomSessionScreenShare } from '../../RoomSessionScreenShare' -import { RoomSession } from '../../video/RoomSession' - -/** - * @privateRemarks - * Every other package exposing a `VideoMemberEntity` is - * transforming the server payload into something else, with - * the most significant change being converting properties - * from snake to camel case. The `js` package, on the other - * hand, exposes the server payload pretty much as is (as of - * v3) so what we consider internal (sdk and server) in - * other packages is external (user facing) for `js`. Same - * applies to `VideoMemberEntityUpdated` since it's just a - * derived type. - */ -type VideoMemberEntity = InternalVideoMemberEntity -type VideoMemberEntityUpdated = InternalVideoMemberEntityUpdated - -const INTERNAL_MEMBER_UPDATED_EVENTS = Object.keys( - INTERNAL_MEMBER_UPDATABLE_PROPS -).map((key) => { - return `member.updated.${ - key as keyof InternalVideoMemberUpdatableProps - }` as const -}) -/** @deprecated */ -export type DeprecatedMemberUpdatableProps = - (typeof INTERNAL_MEMBER_UPDATED_EVENTS)[number] -/** @deprecated */ -export type DeprecatedVideoMemberHandlerParams = { - member: InternalVideoMemberEntity -} -export type VideoMemberHandlerParams = { member: VideoMemberEntity } -export type VideoMemberUpdatedHandlerParams = { - member: VideoMemberEntityUpdated - room_id?: string - room_session_id?: string -} -export type VideoMemberListUpdatedParams = { members: VideoMemberEntity[] } - -/** - * List of all the events a RoomObject can listen to - */ -export type RoomEventNames = - | VideoRoomSessionEventNames - | VideoMemberEventNames - | VideoLayoutEventNames - | VideoRecordingEventNames - | VideoPlaybackEventNames - | VideoStreamEventNames - | RTCTrackEventName - -export type RoomSessionObjectEventsHandlerMap = Record< - VideoRoomDeviceEventNames, - (params: DeviceUpdatedEventParams) => void -> & - Record< - VideoLayoutEventNames, - (params: VideoLayoutChangedEventParams) => void - > & - Record< - Exclude< - VideoMemberEventNames, - MemberUpdated | CoreMemberUpdatedEventNames | MemberListUpdated - >, - (params: VideoMemberHandlerParams) => void - > & - Record< - Extract, - (params: VideoMemberUpdatedHandlerParams) => void - > & - Record< - Extract, - (params: VideoMemberListUpdatedParams) => void - > & - Record< - DeprecatedMemberUpdatableProps, - (params: DeprecatedVideoMemberHandlerParams) => void - > & - Record< - MemberTalkingEventNames, - (params: VideoMemberTalkingEventParams) => void - > & - Record< - Exclude, - (params: VideoRoomEventParams) => void - > & - Record< - RoomJoined | RoomSubscribed, - (params: VideoRoomSubscribedEventParams) => void - > & - Record void> & - Record void> & - Record< - VideoRoomDeviceUpdatedEventNames, - (params: DeviceUpdatedEventParams) => void - > & - Record< - VideoRoomDeviceDisconnectedEventNames, - (params: DeviceDisconnectedEventParams) => void - > & - Record< - RoomAudienceCount, - (params: VideoRoomAudienceCountEventParams) => void - > & - Record void> & - Record void> & - Record void> & - Record void> & - Record void> - -// @deprecated Please use {@link VideoRoomSessionEvents} -export type RoomSessionObjectEvents = { - [k in keyof RoomSessionObjectEventsHandlerMap]: RoomSessionObjectEventsHandlerMap[k] -} - -export type VideoRoomSessionEvents = { - [k in keyof RoomSessionObjectEventsHandlerMap]: RoomSessionObjectEventsHandlerMap[k] -} - -export type StartScreenShareOptions = { - /** Whether the screen share object should automatically join the room */ - autoJoin?: boolean - /** Audio constraints to use when joining the room. Default: `true`. */ - audio?: MediaStreamConstraints['audio'] - /** Video constraints to use when joining the room. Default: `true`. */ - video?: MediaStreamConstraints['video'] - layout?: string - positions?: VideoPositions -} - -/** - * @deprecated Use {@link StartScreenShareOptions} instead. - */ -export interface CreateScreenShareObjectOptions - extends StartScreenShareOptions {} - -export type AddDeviceOptions = { - autoJoin?: boolean - audio?: MediaStreamConstraints['audio'] - video?: MediaStreamConstraints['video'] -} - -export type AddCameraOptions = MediaTrackConstraints & { - autoJoin?: boolean -} -export type AddMicrophoneOptions = MediaTrackConstraints & { - autoJoin?: boolean -} - -export interface BaseRoomInterface { - join(): Promise - leave(): Promise -} - -interface RoomMemberSelfMethodsInterface { - /** - * Puts the microphone on mute. The other participants will not hear audio - * from the muted device anymore. - * - * @permissions - * - `room.self.audio_mute` - * - * You need to specify the permissions when [creating the Video Room - * Token](https://developer.signalwire.com/apis/reference/create_room_token) - * on the server side. - * - * @example Muting the microphone: - * ```typescript - * await roomdevice.audioMute() - * ``` - */ - audioMute(): Rooms.AudioMuteMember - - /** - * Unmutes the microphone if it had been previously muted. - * - * @permissions - * - `room.self.audio_unmute` - * - * You need to specify the permissions when [creating the Video Room - * Token](https://developer.signalwire.com/apis/reference/create_room_token) - * on the server side. - * - * @example Unmuting the microphone: - * ```typescript - * await roomdevice.audioUnmute() - * ``` - */ - audioUnmute(): Rooms.AudioUnmuteMember - - /** - * Puts the video on mute. Participants will see a mute image instead of the - * video stream. - * - * @permissions - * - `room.self.video_mute` - * - * You need to specify the permissions when [creating the Video Room - * Token](https://developer.signalwire.com/apis/reference/create_room_token) - * on the server side. - * - * @example Muting the camera: - * ```typescript - * await roomdevice.videoMute() - * ``` - */ - videoMute(): Rooms.VideoMuteMember - - /** - * Unmutes the video if it had been previously muted. Participants will start - * seeing the video stream again. - * - * @permissions - * - `room.self.video_unmute` - * - * You need to specify the permissions when [creating the Video Room - * Token](https://developer.signalwire.com/apis/reference/create_room_token) - * on the server side. - * - * @example Unmuting the camera: - * ```typescript - * await roomdevice.videoUnmute() - * ``` - */ - videoUnmute(): Rooms.VideoUnmuteMember - - /** - * @deprecated Use {@link setInputVolume} instead. - */ - setMicrophoneVolume(params: { volume: number }): Rooms.SetInputVolumeMember - - /** - * Sets the input volume level (e.g. for the microphone). - * @param params - * @param params.volume desired volume. Values range from -50 to 50, with a - * default of 0. - * - * @permissions - * - `room.self.set_input_volume` - * - * You need to specify the permissions when [creating the Video Room - * Token](https://developer.signalwire.com/apis/reference/create_room_token) - * on the server side. - * - * @example - * ```typescript - * await roomdevice.setMicrophoneVolume({volume: -10}) - * ``` - */ - setInputVolume(params: { volume: number }): Rooms.SetInputVolumeMember - - /** - * Sets the input level at which the participant is identified as currently - * speaking. - * @param params - * @param params.value desired sensitivity. The default value is 30 and the - * scale goes from 0 (lowest sensitivity, essentially muted) to 100 (highest - * sensitivity). - * - * @permissions - * - `room.self.set_input_sensitivity` - * - * You need to specify the permissions when [creating the Video Room - * Token](https://developer.signalwire.com/apis/reference/create_room_token) - * on the server side. - * - * @example - * ```typescript - * await roomdevice.setInputSensitivity({value: 80}) - * ``` - */ - setInputSensitivity(params: { - value: number - }): Rooms.SetInputSensitivityMember -} - -/** - * We are using these interfaces in combination of - * Object.defineProperties() to avoid code duplication and - * expose a nice documentation via TypeDoc. The interface - * forces TS checking while Object.defineProperties allow us - * flexibility across different objects. - */ -export interface VideoRoomSessionMethods - extends OnlyFunctionProperties { - /** @deprecated Use {@link setVideoMuted} instead */ - hideVideoMuted(): Rooms.HideVideoMuted - /** @deprecated Use {@link setVideoMuted} instead */ - showVideoMuted(): Rooms.ShowVideoMuted -} - -export interface VideoRoomSessionContract { - deviceList: RoomSessionDevice[] - interactivityMode: VideoAuthorization['join_as'] - permissions: VideoAuthorization['scopes'] - /** The `layout.changed` event based on the current room layout */ - currentLayoutEvent: VideoLayoutChangedEventParams - /** The layout returned from the `layout.changed` event based on the current room layout */ - currentLayout: VideoLayoutChangedEventParams['layout'] - /** The current position of the member returned from the `layout.changed` event */ - currentPosition: VideoPosition | undefined - /** - * Joins the room session. - */ - join(options?: BaseRoomSessionJoinParams): Promise - /** - * Adds a screen sharing instance to the room. You can create multiple screen - * sharing instances and add all of them to the room. - * @param opts - {@link CreateScreenShareObjectOptions} - * @returns - {@link RoomSessionScreenShare} - * - * @deprecated Use {@link startScreenShare} instead. - */ - createScreenShareObject( - opts?: CreateScreenShareObjectOptions - ): Promise - /** - * Adds a camera device to the room. Using this method, a user can stream - * multiple video sources at the same time. - * - * @param opts - Specify the constraints for the device. In addition, you can - * add the `autoJoin` key to specify whether the device should immediately - * join the room or joining will be performed manually later. {@link AddCameraOptions} - * @returns - {@link RoomSessionDevice} - * - * @example Adding a specific camera: - * ```typescript - * await roomSession.addCamera({deviceId: "gOtMHwZdoA6wMlAnhbfTmeRgPAsqa7iw1OwgKYtbTLA="}) - * ``` - */ - addCamera(opts: AddCameraOptions): Promise - /** - * Adds a microphone device to the room. Using this method, a user can stream - * multiple video sources at the same time. - * - * @param opts Specify the constraints for the device. In addition, you can - * add the `autoJoin` key to specify whether the device should immediately - * join the room or joining will be performed manually later. {@link AddMicrophoneOptions} - * @returns - {@link RoomSessionDevice} - * - * @example Adding a specific microphone: - * ```typescript - * await roomSession.addMicrophone({deviceId: "PIn/IIDDgBUHzJkhRncv1m85hX1gC67xYIgJvvThB3Q="}) - * ``` - */ - addMicrophone(opts: AddMicrophoneOptions): Promise - /** - * Adds a device to the room. Using this method, a user can stream multiple - * sources at the same time. If you need to add a camera device or a - * microphone device, you can alternatively use the more specific methods - * {@link addCamera} and {@link addMicrophone}. - * - * @param opts Specify the constraints for the device. In addition, you can - * add the `autoJoin` key to specify whether the device should immediately - * join the room or joining will be performed manually later. {@link AddDeviceOptions} - * @returns - {@link RoomSessionDevice} - * - * @example Adding any of the microphone devices to the room (duplicate - * streams are possible): - * ```typescript - * await roomSession.addDevice({audio: true}) - * ``` - */ - addDevice(opts: AddDeviceOptions): Promise -} - -export interface RoomSessionDeviceMethods - extends RoomMemberSelfMethodsInterface {} - -export interface RoomScreenShareMethods - extends RoomMemberSelfMethodsInterface {} - -export interface BaseRoomSessionJoinParams { - audio?: MediaStreamConstraints['audio'] - video?: MediaStreamConstraints['video'] - receiveAudio?: boolean - receiveVideo?: boolean - sendAudio?: boolean - sendVideo?: boolean -} - -export type PagingCursor = - | { - before: string - after?: never - } - | { - before?: never - after: string - } - -export type AudioElement = HTMLAudioElement & { - sinkId?: string - setSinkId?: (id: string) => Promise -} diff --git a/packages/client/src/utils/videoElement.ts b/packages/client/src/utils/videoElement.ts index a827d5a93..ea01875c8 100644 --- a/packages/client/src/utils/videoElement.ts +++ b/packages/client/src/utils/videoElement.ts @@ -6,7 +6,7 @@ import { uuid, } from '@signalwire/core' import { OverlayMap, LocalVideoOverlay, UserOverlay } from '../VideoOverlays' -import { addOverlayPrefix } from './roomSession' +import { addOverlayPrefix } from './callSession' const buildVideo = () => { const video = document.createElement('video') diff --git a/packages/client/src/video/RoomSession.ts b/packages/client/src/video/RoomSession.ts deleted file mode 100644 index 32f671bde..000000000 --- a/packages/client/src/video/RoomSession.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { UserOptions } from '@signalwire/core' - -import type { MakeRoomOptions } from '../Client' -import { VideoRoomSession } from './VideoRoomSession' - -/** - * List of properties/methods the user shouldn't be able to - * use until they sucessfully call `roomSession.join()`. - */ -export const UNSAFE_PROP_ACCESS = [ - 'audioMute', - 'audioUnmute', - 'deaf', - 'getLayouts', - 'getMembers', - 'getRecordings', - 'hideVideoMuted', - 'leave', - 'removerMember', - 'restoreOutboundAudio', - 'restoreOutboundVideo', - 'setInputSensitivity', - 'setInputVolume', - 'setLayout', - 'setPositions', - 'setMemberPosition', - 'setOutputVolume', - 'showVideoMuted', - 'startRecording', - 'stopOutboundAudio', - 'stopOutboundVideo', - 'undeaf', - 'videoMute', - 'videoUnmute', - 'setMicrophoneVolume', - 'setSpeakerVolume', - 'getMeta', - 'setMeta', - 'updateMeta', - 'deleteMeta', - 'getMemberMeta', - 'setMemberMeta', - 'updateMemberMeta', - 'deleteMemberMeta', - 'promote', - 'demote', - 'lock', - 'unlock', -] - -export interface RoomSessionOptions extends UserOptions, MakeRoomOptions {} - -export interface RoomSession extends VideoRoomSession { - new (opts: RoomSessionOptions): this -} diff --git a/packages/client/src/video/VideoRoomSession.test.ts b/packages/client/src/video/VideoRoomSession.test.ts deleted file mode 100644 index c6e80a632..000000000 --- a/packages/client/src/video/VideoRoomSession.test.ts +++ /dev/null @@ -1,672 +0,0 @@ -import { actions, componentActions } from '@signalwire/core' -import { - VideoRoomSession, - createVideoRoomSessionObject, -} from './VideoRoomSession' -import { configureFullStack, dispatchMockedRoomSubscribed } from '../testUtils' - -describe('Room Object', () => { - let store: any - let room: VideoRoomSession - let stack: ReturnType - const callId = 'call-id' - - const setupRoomForTests = () => { - const mockPeer = { - uuid: callId, - onRemoteSdp: jest.fn(), - } - // @ts-expect-error - room.getRTCPeerById = jest.fn((_id: string) => mockPeer) - // @ts-expect-error - room.runRTCPeerWorkers(callId) - } - - beforeEach(() => { - stack = configureFullStack() - store = stack.store - room = createVideoRoomSessionObject({ - store, - }) - store.dispatch( - componentActions.upsert({ - id: callId, - nodeId: 'node-id', - roomId: 'room-id', - roomSessionId: 'room-session-id', - memberId: 'member-id', - }) - ) - // @ts-expect-error - room.execute = jest.fn() - - setupRoomForTests() - - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session: stack.session, - callId, - roomId: 'room-id', - roomSessionId: 'room-session-id', - memberId: 'member-id', - }) - }) - - afterEach(() => { - stack.destroy() - }) - - it('should have all the custom methods defined', () => { - expect(room.audioMute).toBeDefined() - expect(room.audioUnmute).toBeDefined() - expect(room.videoMute).toBeDefined() - expect(room.videoUnmute).toBeDefined() - expect(room.deaf).toBeDefined() - expect(room.undeaf).toBeDefined() - expect(room.setInputVolume).toBeDefined() - expect(room.setOutputVolume).toBeDefined() - expect(room.setMicrophoneVolume).toBeDefined() - expect(room.setSpeakerVolume).toBeDefined() - expect(room.setInputSensitivity).toBeDefined() - expect(room.removeMember).toBeDefined() - expect(room.getMembers).toBeDefined() - expect(room.getLayouts).toBeDefined() - expect(room.setLayout).toBeDefined() - expect(room.hideVideoMuted).toBeDefined() - expect(room.showVideoMuted).toBeDefined() - expect(room.lock).toBeDefined() - expect(room.unlock).toBeDefined() - expect(room.getRecordings).toBeDefined() - expect(room.startRecording).toBeDefined() - expect(room.getPlaybacks).toBeDefined() - expect(room.play).toBeDefined() - expect(room.setMeta).toBeDefined() - expect(room.setMemberMeta).toBeDefined() - }) - - describe('getRecordings', () => { - it('should return an array of recordings', async () => { - const recordingList = [{ id: 'recordingOne' }, { id: 'recordingTwo' }] - - // @ts-expect-error - ;(room.execute as jest.Mock).mockResolvedValueOnce({ - recordings: recordingList, - }) - - store.dispatch( - componentActions.upsert({ - id: callId, - nodeId: 'node-id', - roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', - roomSessionId: '8e03ac25-8622-411a-95fc-f897b34ac9e7', - memberId: 'member-id', - }) - ) - - const result = await room.getRecordings() - result.recordings.forEach((recording, index) => { - expect(recording.id).toEqual(recordingList[index].id) - expect(typeof recording.pause).toBe('function') - expect(typeof recording.resume).toBe('function') - expect(typeof recording.stop).toBe('function') - }) - }) - }) - - describe('startRecording', () => { - it('should return an interactive object', async () => { - // @ts-expect-error - ;(room.execute as jest.Mock).mockResolvedValueOnce({ - code: '200', - message: 'Recording started', - recording: { - id: 'c22d7223-5a01-49fe-8da0-46bec8e75e32', - }, - }) - - const recording = await room.startRecording() - // @ts-expect-error - expect(room.execute).toHaveBeenLastCalledWith({ - method: 'video.recording.start', - params: { - room_session_id: 'room-session-id', - }, - }) - - // @ts-expect-error - recording.execute = jest.fn() - expect(recording.id).toEqual('c22d7223-5a01-49fe-8da0-46bec8e75e32') - expect(recording.roomSessionId).toEqual('room-session-id') - expect(recording.pause).toBeDefined() - expect(recording.resume).toBeDefined() - expect(recording.stop).toBeDefined() - - const baseExecuteParams = { - method: '', - params: { - room_session_id: 'room-session-id', - recording_id: 'c22d7223-5a01-49fe-8da0-46bec8e75e32', - }, - } - await recording.pause() - // @ts-expect-error - expect(recording.execute).toHaveBeenLastCalledWith({ - ...baseExecuteParams, - method: 'video.recording.pause', - }) - await recording.resume() - // @ts-expect-error - expect(recording.execute).toHaveBeenLastCalledWith({ - ...baseExecuteParams, - method: 'video.recording.resume', - }) - await recording.stop() - // @ts-expect-error - expect(recording.execute).toHaveBeenLastCalledWith({ - ...baseExecuteParams, - method: 'video.recording.stop', - }) - }) - - it('should work with simulataneous recordings', async () => { - // @ts-expect-error - ;(room.execute as jest.Mock).mockResolvedValueOnce({ - code: '200', - message: 'Recording started', - recording: { - id: 'first-recording', - }, - }) - // @ts-expect-error - ;(room.execute as jest.Mock).mockResolvedValueOnce({ - code: '200', - message: 'Recording started', - recording: { - id: 'second-recording', - }, - }) - - const firstRecording = await room.startRecording() - // @ts-expect-error - firstRecording.execute = jest.fn() - const secondRecording = await room.startRecording() - // @ts-expect-error - secondRecording.execute = jest.fn() - - expect(firstRecording.id).toEqual('first-recording') - expect(firstRecording.roomSessionId).toEqual('room-session-id') - await firstRecording.stop() - // @ts-expect-error - expect(firstRecording.execute).toHaveBeenLastCalledWith({ - method: 'video.recording.stop', - params: { - room_session_id: 'room-session-id', - recording_id: 'first-recording', - }, - }) - - expect(secondRecording.id).toEqual('second-recording') - expect(secondRecording.roomSessionId).toEqual('room-session-id') - await secondRecording.stop() - // @ts-expect-error - expect(secondRecording.execute).toHaveBeenLastCalledWith({ - method: 'video.recording.stop', - params: { - room_session_id: 'room-session-id', - recording_id: 'second-recording', - }, - }) - }) - }) - - describe('playback methods', () => { - it('getPlaybacks should return an array of playbacks', async () => { - const playbacks = [{ id: 'playbackOne' }, { id: 'playbackTwo' }] - - // @ts-expect-error - ;(room.execute as jest.Mock).mockResolvedValueOnce({ - playbacks, - }) - - store.dispatch( - componentActions.upsert({ - id: callId, - nodeId: 'node-id', - roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', - roomSessionId: '8e03ac25-8622-411a-95fc-f897b34ac9e7', - memberId: 'member-id', - }) - ) - - const result = await room.getPlaybacks() - result.playbacks.forEach((playback, index) => { - expect(playback.id).toEqual(playbacks[index].id) - expect(typeof playback.forward).toBe('function') - expect(typeof playback.pause).toBe('function') - expect(typeof playback.resume).toBe('function') - expect(typeof playback.rewind).toBe('function') - expect(typeof playback.seek).toBe('function') - expect(typeof playback.setVolume).toBe('function') - expect(typeof playback.stop).toBe('function') - }) - }) - - it('play should return an interactive object', async () => { - // @ts-expect-error - ;(room.execute as jest.Mock).mockResolvedValueOnce({ - code: '200', - message: 'Playback started', - playback: { - id: 'c22d7223-5a01-49fe-8da0-46bec8e75e32', - state: 'playing', - started_at: 1234, - }, - }) - - const playback = await room.play({ - url: 'rtmp://jest.example.com/bla', - volume: 5, - }) - // @ts-expect-error - playback.execute = jest.fn() - - // @ts-expect-error - expect(room.execute).toHaveBeenLastCalledWith({ - method: 'video.playback.start', - params: { - room_session_id: 'room-session-id', - url: 'rtmp://jest.example.com/bla', - volume: 5, - }, - }) - - expect(playback.id).toEqual('c22d7223-5a01-49fe-8da0-46bec8e75e32') - expect(playback.roomSessionId).toEqual('room-session-id') - expect(playback.pause).toBeDefined() - expect(playback.resume).toBeDefined() - expect(playback.stop).toBeDefined() - - const baseExecuteParams = { - method: '', - params: { - room_session_id: 'room-session-id', - playback_id: 'c22d7223-5a01-49fe-8da0-46bec8e75e32', - }, - } - await playback.pause() - // @ts-expect-error - expect(playback.execute).toHaveBeenLastCalledWith({ - ...baseExecuteParams, - method: 'video.playback.pause', - }) - await playback.resume() - // @ts-expect-error - expect(playback.execute).toHaveBeenLastCalledWith({ - ...baseExecuteParams, - method: 'video.playback.resume', - }) - await playback.stop() - // @ts-expect-error - expect(playback.execute).toHaveBeenLastCalledWith({ - ...baseExecuteParams, - method: 'video.playback.stop', - }) - await playback.setVolume(30) - // @ts-expect-error - expect(playback.execute).toHaveBeenLastCalledWith({ - method: 'video.playback.set_volume', - params: { - room_session_id: 'room-session-id', - playback_id: 'c22d7223-5a01-49fe-8da0-46bec8e75e32', - volume: 30, - }, - }) - }) - - it('play should work with simulataneous playbacks', async () => { - // @ts-expect-error - ;(room.execute as jest.Mock).mockResolvedValueOnce({ - code: '200', - message: 'Playback started', - playback: { - id: 'first-playback', - state: 'playing', - started_at: 1234, - }, - }) - // @ts-expect-error - ;(room.execute as jest.Mock).mockResolvedValueOnce({ - code: '200', - message: 'Playback started', - playback: { - id: 'second-playback', - state: 'playing', - started_at: 1234, - }, - }) - - const firstPlayback = await room.play({ - url: 'url-one', - }) - // @ts-expect-error - firstPlayback.execute = jest.fn() - const secondPlayback = await room.play({ - url: 'url-two', - }) - // @ts-expect-error - secondPlayback.execute = jest.fn() - - expect(firstPlayback.id).toEqual('first-playback') - expect(firstPlayback.roomSessionId).toEqual('room-session-id') - await firstPlayback.stop() - // @ts-expect-error - expect(firstPlayback.execute).toHaveBeenLastCalledWith({ - method: 'video.playback.stop', - params: { - room_session_id: 'room-session-id', - playback_id: 'first-playback', - }, - }) - - expect(secondPlayback.id).toEqual('second-playback') - expect(secondPlayback.roomSessionId).toEqual('room-session-id') - await secondPlayback.stop() - // @ts-expect-error - expect(secondPlayback.execute).toHaveBeenLastCalledWith({ - method: 'video.playback.stop', - params: { - room_session_id: 'room-session-id', - playback_id: 'second-playback', - }, - }) - }) - }) - - describe('as event emitter', () => { - it('should listen on the talking events', () => { - const { store, session, emitter, destroy } = configureFullStack() - room = createVideoRoomSessionObject({ - store, - // @ts-expect-error - emitter, - }) - // @ts-expect-error - room.execute = jest.fn() - store.dispatch( - componentActions.upsert({ - id: callId, - nodeId: 'node-id', - roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', - roomSessionId: '8e03ac25-8622-411a-95fc-f897b34ac9e7', - memberId: 'member-id', - }) - ) - - setupRoomForTests() - - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session, - callId, - roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', - roomSessionId: '8e03ac25-8622-411a-95fc-f897b34ac9e7', - memberId: 'member-id', - }) - - const startedHandler = jest.fn() - room.on('member.talking.started', startedHandler) - // deprecated - room.on('member.talking.start', startedHandler) - - const endedHandler = jest.fn() - room.on('member.talking.ended', endedHandler) - // deprecated - room.on('member.talking.stop', endedHandler) - - const globalHandler = jest.fn() - room.on('member.talking', globalHandler) - - const talkingTrue = JSON.parse( - '{"jsonrpc":"2.0","id":"9050e4f8-b08e-4e39-9796-bfb6e83c2a2d","method":"signalwire.event","params":{"params":{"room_session_id":"8e03ac25-8622-411a-95fc-f897b34ac9e7","room_id":"6e83849b-5cc2-4fc6-80ed-448113c8a426","member":{"id":"a3693340-6f42-4cab-b18e-8e2a22695698","room_session_id":"8e03ac25-8622-411a-95fc-f897b34ac9e7","room_id":"6e83849b-5cc2-4fc6-80ed-448113c8a426","talking":true}},"timestamp":1627374612.9585,"event_type":"video.member.talking","event_channel":"room.0a324e3c-5e2f-443a-a333-10bf005f249e"}}' - ) - session.dispatch(actions.socketMessageAction(talkingTrue)) - - expect(startedHandler).toHaveBeenCalledTimes(2) - expect(globalHandler).toHaveBeenCalledTimes(1) - - const talkingFalse = JSON.parse( - '{"jsonrpc":"2.0","id":"9050e4f8-b08e-4e39-9796-bfb6e83c2a2d","method":"signalwire.event","params":{"params":{"room_session_id":"8e03ac25-8622-411a-95fc-f897b34ac9e7","room_id":"6e83849b-5cc2-4fc6-80ed-448113c8a426","member":{"id":"a3693340-6f42-4cab-b18e-8e2a22695698","room_session_id":"8e03ac25-8622-411a-95fc-f897b34ac9e7","room_id":"6e83849b-5cc2-4fc6-80ed-448113c8a426","talking":false}},"timestamp":1627374612.9585,"event_type":"video.member.talking","event_channel":"room.0a324e3c-5e2f-443a-a333-10bf005f249e"}}' - ) - session.dispatch(actions.socketMessageAction(talkingFalse)) - - expect(endedHandler).toHaveBeenCalledTimes(2) - expect(globalHandler).toHaveBeenCalledTimes(2) - - expect(globalHandler).toHaveBeenNthCalledWith( - 1, - talkingTrue.params.params - ) - expect(globalHandler).toHaveBeenNthCalledWith( - 2, - talkingFalse.params.params - ) - destroy() - }) - - it('should handle the room.subscribed event with nested fields', (done) => { - const roomId = 'd8caec4b-ddc9-4806-b2d0-e7c7d5cefe79' - const roomSessionId = '638a54a7-61d8-4db0-bc24-426aee5cebcd' - const recordingId = 'd1ae1822-5a5d-4950-8693-e59dc5dd96e0' - const memberId = '465ea212-c456-423b-9bcc-838c5e1b2851' - - const { store, session, emitter, destroy } = configureFullStack() - room = createVideoRoomSessionObject({ - store, - // @ts-expect-error - emitter, - }) - // @ts-expect-error - room.execute = jest.fn() - - setupRoomForTests() - - room.on('room.joined', (params) => { - /** Test same keys between room_session and room for backwards compat. */ - const keys = ['room_session', 'room'] as const - keys.forEach(async (key) => { - expect(params[key].name).toEqual('bu') - expect(params[key].room_id).toEqual(roomId) - expect(params[key].recording).toBe(true) - expect(params[key].hide_video_muted).toBe(false) - // @ts-expect-error - expect(params[key].meta).toStrictEqual({}) - const { members, recordings } = params[key] - - // Test members and member object - expect(members).toHaveLength(1) - expect(members[0].id).toEqual(memberId) - expect(members[0].name).toEqual('edo') - expect(members[0].visible).toEqual(false) - expect(members[0].audio_muted).toEqual(false) - expect(members[0].video_muted).toEqual(false) - expect(members[0].deaf).toEqual(false) - expect(members[0].input_volume).toEqual(0) - expect(members[0].output_volume).toEqual(0) - expect(members[0].input_sensitivity).toEqual(11.11111111111111) - expect(members[0].meta).toStrictEqual({}) - - // Test recordings and recording object - expect(recordings).toHaveLength(1) - const recordingObj = recordings?.[0] - expect(recordingObj.id).toEqual(recordingId) - expect(recordingObj.state).toEqual('recording') - expect(recordingObj.duration).toBeNull() - expect(recordingObj.startedAt).toBeInstanceOf(Date) - expect(recordingObj.endedAt).toBeUndefined() // When state is recording - - const execMock = jest.fn() - const _clearMock = () => { - execMock.mockClear() - recordingObj.execute = execMock - } - _clearMock() - await recordingObj.pause() - expect(execMock).toHaveBeenCalledTimes(1) - expect(execMock).toHaveBeenCalledWith({ - method: 'video.recording.pause', - params: { - recording_id: recordingId, - room_session_id: roomSessionId, - }, - }) - - _clearMock() - await recordingObj.resume() - expect(execMock).toHaveBeenCalledTimes(1) - expect(execMock).toHaveBeenCalledWith({ - method: 'video.recording.resume', - params: { - recording_id: recordingId, - room_session_id: roomSessionId, - }, - }) - - _clearMock() - await recordingObj.stop() - expect(execMock).toHaveBeenCalledTimes(1) - expect(execMock).toHaveBeenCalledWith({ - method: 'video.recording.stop', - params: { - recording_id: recordingId, - room_session_id: roomSessionId, - }, - }) - }) - /** Test specific keys between room_session and room for backwards compat. */ - expect(params.room.room_session_id).toEqual(roomSessionId) - expect(params.room_session.id).toEqual(roomSessionId) - - /** Test RoomSession properties */ - expect(room.roomId).toEqual(roomId) - expect(room.roomSessionId).toEqual(roomSessionId) - expect(room.memberId).toEqual(memberId) - - destroy() - done() - }) - - /** - * Mock `call_id` to match the event with "room.__uuid" - */ - const roomSubscribed = JSON.parse( - `{"jsonrpc":"2.0","id":"d8a9fb9a-ad28-4a0a-8caa-5e06ec22f856","method":"signalwire.event","params":{"event_type":"video.room.subscribed","timestamp":1650960870.216,"event_channel":"EC_4d2c491d-bf96-4802-9008-c360a51155a2","params":{"call_id":"${callId}","member_id":"465ea212-c456-423b-9bcc-838c5e1b2851","room_session":{"room_id":"d8caec4b-ddc9-4806-b2d0-e7c7d5cefe79","id":"638a54a7-61d8-4db0-bc24-426aee5cebcd","event_channel":"EC_4d2c491d-bf96-4802-9008-c360a51155a2","name":"bu","recording":true,"hide_video_muted":false,"display_name":"bu","meta":{},"recordings":[{"id":"d1ae1822-5a5d-4950-8693-e59dc5dd96e0","state":"recording","duration":null,"started_at":1650960870.033,"ended_at":null}],"members":[{"id":"465ea212-c456-423b-9bcc-838c5e1b2851","room_id":"d8caec4b-ddc9-4806-b2d0-e7c7d5cefe79","room_session_id":"638a54a7-61d8-4db0-bc24-426aee5cebcd","name":"edo","type":"member","parent_id":"","requested_position":"auto","visible":false,"audio_muted":false,"video_muted":false,"deaf":false,"input_volume":0,"output_volume":0,"input_sensitivity":11.11111111111111,"meta":{}}]},"room":{"room_id":"d8caec4b-ddc9-4806-b2d0-e7c7d5cefe79","event_channel":"EC_4d2c491d-bf96-4802-9008-c360a51155a2","name":"bu","recording":true,"hide_video_muted":false,"display_name":"bu","meta":{},"recordings":[{"id":"d1ae1822-5a5d-4950-8693-e59dc5dd96e0","state":"recording","duration":null,"started_at":1650960870.033,"ended_at":null}],"members":[{"id":"465ea212-c456-423b-9bcc-838c5e1b2851","room_id":"d8caec4b-ddc9-4806-b2d0-e7c7d5cefe79","room_session_id":"638a54a7-61d8-4db0-bc24-426aee5cebcd","name":"edo","type":"member","parent_id":"","requested_position":"auto","visible":false,"audio_muted":false,"video_muted":false,"deaf":false,"input_volume":0,"output_volume":0,"input_sensitivity":11.11111111111111,"meta":{}}],"room_session_id":"638a54a7-61d8-4db0-bc24-426aee5cebcd"}}}}` - ) - // mock a room.subscribed event - session.dispatch(actions.socketMessageAction(roomSubscribed)) - }) - }) - - describe('meta methods', () => { - it('should allow to set the meta field on the RoomSession', async () => { - const { store, session, emitter, destroy } = configureFullStack() - - session.execute = jest.fn().mockResolvedValue({ - code: '200', - message: 'OK', - }) - - room = createVideoRoomSessionObject({ - store, - // @ts-expect-error - emitter, - }) - store.dispatch( - componentActions.upsert({ - id: callId, - nodeId: 'node-id', - roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', - roomSessionId: '8e03ac25-8622-411a-95fc-f897b34ac9e7', - memberId: 'member-id', - }) - ) - setupRoomForTests() - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session, - callId, - roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', - roomSessionId: '8e03ac25-8622-411a-95fc-f897b34ac9e7', - memberId: 'member-id', - }) - - const result = await room.setMeta({ foo: 'bar' }) - expect(result).toBeUndefined() - - expect(session.execute).toHaveBeenLastCalledWith({ - jsonrpc: '2.0', - id: expect.any(String), - method: 'video.set_meta', - params: { - room_session_id: '8e03ac25-8622-411a-95fc-f897b34ac9e7', - meta: { foo: 'bar' }, - }, - }) - - destroy() - }) - - it('should allow to set the meta field on the Member', async () => { - const { store, session, emitter, destroy } = configureFullStack() - - session.execute = jest.fn().mockResolvedValue({ - code: '200', - message: 'OK', - }) - - room = createVideoRoomSessionObject({ - store, - // @ts-expect-error - emitter, - }) - store.dispatch( - componentActions.upsert({ - id: callId, - nodeId: 'node-id', - roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', - roomSessionId: '8e03ac25-8622-411a-95fc-f897b34ac9e7', - memberId: 'member-id', - }) - ) - - setupRoomForTests() - - // mock a room.subscribed event - dispatchMockedRoomSubscribed({ - session, - callId, - roomId: '6e83849b-5cc2-4fc6-80ed-448113c8a426', - roomSessionId: '8e03ac25-8622-411a-95fc-f897b34ac9e7', - memberId: 'member-id', - }) - - const result = await room.setMemberMeta({ - memberId: 'uuid', - meta: { displayName: 'jest' }, - }) - expect(result).toBeUndefined() - - expect(session.execute).toHaveBeenLastCalledWith({ - jsonrpc: '2.0', - id: expect.any(String), - method: 'video.member.set_meta', - params: { - room_session_id: '8e03ac25-8622-411a-95fc-f897b34ac9e7', - member_id: 'uuid', - meta: { displayName: 'jest' }, - }, - }) - - destroy() - }) - }) -}) diff --git a/packages/client/src/video/VideoRoomSession.ts b/packages/client/src/video/VideoRoomSession.ts deleted file mode 100644 index 716ae5f2c..000000000 --- a/packages/client/src/video/VideoRoomSession.ts +++ /dev/null @@ -1,304 +0,0 @@ -import { - BaseComponentContract, - BaseConnectionContract, - connect, - EventEmitter, - extendComponent, - Rooms, - validateEventsToSubscribe, - VideoAuthorization, - VideoLayoutChangedEventParams, -} from '@signalwire/core' -import { BaseConnectionOptions } from '@signalwire/webrtc' -import { - BaseRoomSessionConnection, - BaseRoomSessionOptions, -} from '../BaseRoomSession' -import { - RoomSessionDevice, - RoomSessionDeviceAPI, - RoomSessionDeviceConnection, - RoomSessionDeviceEvents, -} from '../RoomSessionDevice' -import * as workers from './workers' -import { - AddCameraOptions, - AddDeviceOptions, - AddMicrophoneOptions, - CreateScreenShareObjectOptions, - VideoRoomSessionMethods, - VideoRoomSessionEvents, - VideoRoomSessionContract, - BaseRoomSessionContract, -} from '../utils/interfaces' - -export interface VideoRoomSession - extends VideoRoomSessionContract, - VideoRoomSessionMethods, - BaseRoomSessionContract, - BaseConnectionContract, - BaseComponentContract {} - -export interface VideoRoomSessionOptions extends BaseRoomSessionOptions {} - -export class VideoRoomSessionConnection - extends BaseRoomSessionConnection - implements VideoRoomSessionContract -{ - private _deviceList = new Set() - private _currentLayoutEvent: VideoLayoutChangedEventParams - - constructor(options: VideoRoomSessionOptions) { - super(options) - - this.initWorker() - } - - set currentLayoutEvent(event: VideoLayoutChangedEventParams) { - this._currentLayoutEvent = event - } - - get currentLayoutEvent() { - return this._currentLayoutEvent - } - - get currentLayout() { - return this._currentLayoutEvent?.layout - } - - get currentPosition() { - return this._currentLayoutEvent?.layout.layers.find( - (layer) => layer.member_id === this.memberId - )?.position - } - - get deviceList() { - return Array.from(this._deviceList) - } - - get interactivityMode() { - return this.select(({ session }) => { - const { authorization } = session - return (authorization as VideoAuthorization)?.join_as ?? '' - }) - } - - get permissions() { - return this.select(({ session }) => { - const { authorization } = session - return (authorization as VideoAuthorization)?.room?.scopes ?? [] - }) - } - - private initWorker() { - this.runWorker('videoWorker', { - worker: workers.videoWorker, - }) - } - - /** @internal */ - protected override getSubscriptions() { - const eventNamesWithPrefix = this.eventNames().map((event) => { - return `video.${String(event)}` - }) - return validateEventsToSubscribe( - eventNamesWithPrefix - ) as EventEmitter.EventNames<{}>[] - } - - /** @internal */ - protected _finalize() { - this._deviceList.clear() - - super._finalize() - } - - /** @internal */ - override async hangup(id?: string) { - this._deviceList.forEach((device) => { - device.leave() - }) - - return super.hangup(id) - } - - join() { - return super.invite() - } - - /** - * @deprecated Use {@link getLayouts} instead. `getLayoutList` will - * be removed in v3.0.0 - */ - getLayoutList() { - // @ts-expect-error - return this.getLayouts() - } - - /** - * @deprecated Use {@link getMembers} instead. `getMemberList` will - * be removed in v3.0.0 - */ - getMemberList() { - // @ts-expect-error - return this.getMembers() - } - - /** @deprecated Use {@link startScreenShare} instead. */ - async createScreenShareObject(opts: CreateScreenShareObjectOptions = {}) { - return this.startScreenShare(opts) - } - - /** - * Allow to add a camera to the room. - */ - addCamera(opts: AddCameraOptions = {}) { - const { autoJoin = true, ...video } = opts - return this.addDevice({ - autoJoin, - video, - }) - } - - /** - * Allow to add a microphone to the room. - */ - addMicrophone(opts: AddMicrophoneOptions = {}) { - const { autoJoin = true, ...audio } = opts - return this.addDevice({ - autoJoin, - audio, - }) - } - - /** - * Allow to add additional devices to the room like cameras or microphones. - */ - async addDevice(opts: AddDeviceOptions = {}) { - return new Promise(async (resolve, reject) => { - const { autoJoin = true, audio = false, video = false } = opts - if (!audio && !video) { - throw new TypeError( - 'At least one of `audio` or `video` must be requested.' - ) - } - - const options: BaseConnectionOptions = { - ...this.options, - localStream: undefined, - remoteStream: undefined, - audio, - video, - additionalDevice: true, - recoverCall: false, - userVariables: { - ...(this.options?.userVariables || {}), - memberCallId: this.callId, - memberId: this.memberId, - }, - } - - const roomDevice = connect< - RoomSessionDeviceEvents, - RoomSessionDeviceConnection, - RoomSessionDevice - >({ - store: this.store, - Component: RoomSessionDeviceAPI, - })(options) - - roomDevice.once('destroy', () => { - roomDevice.emit('room.left') - this._deviceList.delete(roomDevice) - }) - - try { - roomDevice.runWorker('childMemberJoinedWorker', { - worker: workers.childMemberJoinedWorker, - onDone: () => resolve(roomDevice), - onFail: reject, - initialState: { - parentId: this.memberId, - }, - }) - - this._deviceList.add(roomDevice) - if (autoJoin) { - return await roomDevice.join() - } - return resolve(roomDevice) - } catch (error) { - this.logger.error('RoomDevice Error', error) - reject(error) - } - }) - } -} - -export const VideoRoomSessionAPI = extendComponent< - VideoRoomSessionConnection, - VideoRoomSessionMethods ->(VideoRoomSessionConnection, { - audioMute: Rooms.audioMuteMember, - audioUnmute: Rooms.audioUnmuteMember, - videoMute: Rooms.videoMuteMember, - videoUnmute: Rooms.videoUnmuteMember, - deaf: Rooms.deafMember, - undeaf: Rooms.undeafMember, - setInputVolume: Rooms.setInputVolumeMember, - setOutputVolume: Rooms.setOutputVolumeMember, - setMicrophoneVolume: Rooms.setInputVolumeMember, - setSpeakerVolume: Rooms.setOutputVolumeMember, - setInputSensitivity: Rooms.setInputSensitivityMember, - removeMember: Rooms.removeMember, - removeAllMembers: Rooms.removeAllMembers, - getMembers: Rooms.getMembers, - getLayouts: Rooms.getLayouts, - setLayout: Rooms.setLayout, - setPositions: Rooms.setPositions, - setMemberPosition: Rooms.setMemberPosition, - hideVideoMuted: Rooms.hideVideoMuted, - showVideoMuted: Rooms.showVideoMuted, - getRecordings: Rooms.getRecordings, - startRecording: Rooms.startRecording, - getPlaybacks: Rooms.getPlaybacks, - play: Rooms.play, - setHideVideoMuted: Rooms.setHideVideoMuted, - getMeta: Rooms.getMeta, - setMeta: Rooms.setMeta, - updateMeta: Rooms.updateMeta, - deleteMeta: Rooms.deleteMeta, - getMemberMeta: Rooms.getMemberMeta, - setMemberMeta: Rooms.setMemberMeta, - updateMemberMeta: Rooms.updateMemberMeta, - deleteMemberMeta: Rooms.deleteMemberMeta, - promote: Rooms.promote, - demote: Rooms.demote, - getStreams: Rooms.getStreams, - startStream: Rooms.startStream, - lock: Rooms.lock, - unlock: Rooms.unlock, - setRaisedHand: Rooms.setRaisedHand, - setPrioritizeHandraise: Rooms.setPrioritizeHandraise, -}) - -export const isVideoRoomSession = (room: unknown): room is VideoRoomSession => { - return room instanceof VideoRoomSessionConnection -} - -/** @internal */ -export const createVideoRoomSessionObject = ( - params: VideoRoomSessionOptions -): VideoRoomSession => { - const room = connect< - VideoRoomSessionEvents, - VideoRoomSessionConnection, - VideoRoomSession - >({ - store: params.store, - customSagas: params.customSagas, - Component: VideoRoomSessionAPI, - })(params) - - return room -} diff --git a/packages/client/src/video/index.ts b/packages/client/src/video/index.ts deleted file mode 100644 index f4c121a50..000000000 --- a/packages/client/src/video/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { MakeRoomOptions } from '../Client' -export type { MakeRoomOptions } diff --git a/packages/client/src/video/workers/videoWorker.ts b/packages/client/src/video/workers/videoWorker.ts deleted file mode 100644 index 6fc9bfd8f..000000000 --- a/packages/client/src/video/workers/videoWorker.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { - SDKActions, - SDKWorker, - SagaIterator, - getLogger, - sagaEffects, - SDKWorkerParams, - MemberPosition, - stripNamespacePrefix, - VideoAction, - VideoRoomDeviceEventNames, - BaseConnectionState, -} from '@signalwire/core' - -import { VideoRoomSessionConnection } from '../VideoRoomSession' -import { MediaEventNames } from '@signalwire/webrtc' - -export type VideoWorkerParams = - SDKWorkerParams & { - action: T - } - -export const videoWorker: SDKWorker = function* ( - options -): SagaIterator { - getLogger().trace('videoWorker started') - - const { channels, instance: roomSession } = options - const { swEventChannel } = channels - - function* worker(action: VideoAction) { - const { type, payload } = action - - switch (type) { - case 'video.room.subscribed': - yield sagaEffects.fork(MemberPosition.memberPositionWorker, { - ...options, - instance: roomSession, - initialState: payload, - }) - return - case 'video.playback.started': - case 'video.playback.updated': - case 'video.playback.ended': - // TODO - return // Return since we don't need to handle the raw event for this - case 'video.recording.started': - case 'video.recording.updated': - case 'video.recording.ended': - // TODO - return - case 'video.stream.ended': - case 'video.stream.started': - // TODO - return - case 'video.room.audience_count': { - roomSession.emit('room.audienceCount', payload) - return - } - case 'video.member.talking': { - const { member } = payload - if ('talking' in member) { - const suffix = member.talking ? 'started' : 'ended' - roomSession.emit(`member.talking.${suffix}`, payload) - - // Keep for backwards compat. - const deprecatedSuffix = member.talking ? 'start' : 'stop' - roomSession.emit(`member.talking.${deprecatedSuffix}`, payload) - } - break // Break here since we do need the raw event sent to the client - } - case 'video.layout.changed': { - // Upsert the layout event which is needed for buildVideoElement - roomSession.currentLayoutEvent = action.payload - break - } - default: - break - } - - const event = stripNamespacePrefix(type, 'video') as - | VideoRoomDeviceEventNames - | MediaEventNames - | BaseConnectionState - roomSession.emit(event, payload) - } - - const isVideoEvent = (action: SDKActions) => { - return action.type.startsWith('video.') - } - - while (true) { - const action: VideoAction = yield sagaEffects.take( - swEventChannel, - isVideoEvent - ) - - yield sagaEffects.fork(worker, action) - } - - getLogger().trace('videoWorker ended') -} diff --git a/packages/client/src/video/workers/childMemberJoinedWorker.test.ts b/packages/client/src/workers/childMemberJoinedWorker.test.ts similarity index 100% rename from packages/client/src/video/workers/childMemberJoinedWorker.test.ts rename to packages/client/src/workers/childMemberJoinedWorker.test.ts diff --git a/packages/client/src/video/workers/childMemberJoinedWorker.ts b/packages/client/src/workers/childMemberJoinedWorker.ts similarity index 100% rename from packages/client/src/video/workers/childMemberJoinedWorker.ts rename to packages/client/src/workers/childMemberJoinedWorker.ts diff --git a/packages/client/src/video/workers/index.ts b/packages/client/src/workers/index.ts similarity index 73% rename from packages/client/src/video/workers/index.ts rename to packages/client/src/workers/index.ts index a33d647f6..cfb1d9e9c 100644 --- a/packages/client/src/video/workers/index.ts +++ b/packages/client/src/workers/index.ts @@ -1,3 +1,3 @@ export * from './memberListUpdatedWorker' export * from './childMemberJoinedWorker' -export * from './videoWorker' + diff --git a/packages/client/src/video/workers/memberListUpdatedWorker.ts b/packages/client/src/workers/memberListUpdatedWorker.ts similarity index 94% rename from packages/client/src/video/workers/memberListUpdatedWorker.ts rename to packages/client/src/workers/memberListUpdatedWorker.ts index cb0f0fbb8..6e1bad145 100644 --- a/packages/client/src/video/workers/memberListUpdatedWorker.ts +++ b/packages/client/src/workers/memberListUpdatedWorker.ts @@ -14,8 +14,8 @@ import { InternalVideoRoomJoinedEvent, MapToPubSubShape, } from '@signalwire/core' -import type { VideoMemberListUpdatedParams } from '../../utils/interfaces' -import { VideoRoomSession } from '../VideoRoomSession' +import type { CallMemberListUpdatedParams } from '../utils/interfaces' +import { CallSession } from '../unified/CallSession' const noop = () => {} @@ -104,7 +104,7 @@ export const getUpdatedMembers = ({ } const initMemberListSubscriptions = ( - room: VideoRoomSession, + room: CallSession, subscriptions: MemberListUpdatedTargetActions['type'][] ) => { const events = getMemberListEventsToSubscribe(subscriptions) @@ -129,7 +129,7 @@ const initMemberListSubscriptions = ( * This handler will act as a simple bridge between * synthetic events and external events. */ - const eventBridgeHandler = ({ members }: VideoMemberListUpdatedParams) => { + const eventBridgeHandler = ({ members }: CallMemberListUpdatedParams) => { room.emit(EXTERNAL_MEMBER_LIST_UPDATED_EVENT, { members }) } @@ -192,7 +192,7 @@ function* membersListUpdatedWatcher({ } } -export const memberListUpdatedWorker: SDKWorker = +export const memberListUpdatedWorker: SDKWorker = function* membersChangedWorker({ channels: { swEventChannel }, instance, diff --git a/packages/core/src/memberPosition/workers.ts b/packages/core/src/memberPosition/workers.ts index 3200d3902..0feaaf7ca 100644 --- a/packages/core/src/memberPosition/workers.ts +++ b/packages/core/src/memberPosition/workers.ts @@ -13,7 +13,7 @@ import { VideoMemberUpdatedEvent, VideoMemberUpdatedEventParams, VideoPosition, - VideoRoomSubscribedEventParams, + CallSessionSubscribedEventParams, } from '..' /** @@ -257,7 +257,7 @@ const mutateMemberCurrentPosition = ({ return updatedMemberEventParams } -const initializeMemberList = (payload: VideoRoomSubscribedEventParams) => { +const initializeMemberList = (payload: CallSessionSubscribedEventParams) => { const members = payload.room_session.members const memberList: MemberEventParamsList = new Map() diff --git a/packages/core/src/types/video.ts b/packages/core/src/types/video.ts index 7e2920f99..282116d7b 100644 --- a/packages/core/src/types/video.ts +++ b/packages/core/src/types/video.ts @@ -30,7 +30,7 @@ import { InternalVideoStreamEventNames, VideoStreamEventNames, } from './videoStream' -import { VideoRoomAudienceCountEvent, VideoRoomDeviceEventNames } from '.' +import { VideoRoomAudienceCountEvent, CallSessionDeviceEventNames } from '.' import { MapToPubSubShape } from '..' export * from './videoRoomSession' @@ -119,7 +119,7 @@ export type VideoAPIEventNames = | VideoLayoutEventNames | VideoPlaybackEventNames | VideoRecordingEventNames - | VideoRoomDeviceEventNames + | CallSessionDeviceEventNames | VideoStreamEventNames export type VideoAction = MapToPubSubShape< diff --git a/packages/core/src/types/videoRoomDevice.ts b/packages/core/src/types/videoRoomDevice.ts index d9d50d42a..71a44b9ac 100644 --- a/packages/core/src/types/videoRoomDevice.ts +++ b/packages/core/src/types/videoRoomDevice.ts @@ -9,19 +9,19 @@ export type SpeakerDisconnected = 'speaker.disconnected' * List of public event names */ -export type VideoRoomDeviceUpdatedEventNames = +export type CallSessionDeviceUpdatedEventNames = | CameraUpdated | MicrophoneUpdated | SpeakerUpdated -export type VideoRoomDeviceDisconnectedEventNames = +export type CallSessionDeviceDisconnectedEventNames = | CameraDisconnected | MicrophoneDisconnected | SpeakerDisconnected -export type VideoRoomDeviceEventNames = - | VideoRoomDeviceUpdatedEventNames - | VideoRoomDeviceDisconnectedEventNames +export type CallSessionDeviceEventNames = + | CallSessionDeviceUpdatedEventNames + | CallSessionDeviceDisconnectedEventNames export interface VideoRoomMediaDeviceInfo { deviceId: MediaDeviceInfo['deviceId'] | undefined @@ -35,6 +35,6 @@ export interface DeviceUpdatedEventParams { export type DeviceDisconnectedEventParams = VideoRoomMediaDeviceInfo -export type VideoRoomDeviceEventParams = +export type CallSessionDeviceEventParams = | DeviceUpdatedEventParams | DeviceDisconnectedEventParams diff --git a/packages/core/src/types/videoRoomSession.ts b/packages/core/src/types/videoRoomSession.ts index 95d9f1b41..7c031ba3e 100644 --- a/packages/core/src/types/videoRoomSession.ts +++ b/packages/core/src/types/videoRoomSession.ts @@ -890,7 +890,7 @@ export type InternalVideoRoomUpdated = export interface InternalVideoRoomJoinedEvent extends SwEvent { event_type: ToInternalVideoEvent - params: VideoRoomSubscribedEventParams + params: CallSessionSubscribedEventParams } export interface InternalVideoRoomAudienceCountEvent extends SwEvent { @@ -929,7 +929,7 @@ export interface VideoRoomStartedEvent extends SwEvent { /** * 'video.room.subscribed' */ -export interface VideoRoomSubscribedEventParams { +export interface CallSessionSubscribedEventParams { // keep room for backward compat room: InternalVideoRoomEntity & { members: InternalVideoMemberEntity[] @@ -944,9 +944,9 @@ export interface VideoRoomSubscribedEventParams { member_id: string } -export interface VideoRoomSubscribedEvent extends SwEvent { +export interface CallSessionSubscribedEvent extends SwEvent { event_type: ToInternalVideoEvent - params: VideoRoomSubscribedEventParams + params: CallSessionSubscribedEventParams } /** @@ -997,13 +997,13 @@ export interface VideoRoomAudienceCountEvent extends SwEvent { export type VideoRoomEvent = | VideoRoomStartedEvent - | VideoRoomSubscribedEvent + | CallSessionSubscribedEvent | VideoRoomUpdatedEvent | VideoRoomEndedEvent export type VideoRoomEventParams = | VideoRoomStartedEventParams - | VideoRoomSubscribedEventParams + | CallSessionSubscribedEventParams | VideoRoomUpdatedEventParams | VideoRoomEndedEventParams @@ -1013,5 +1013,5 @@ export type VideoRoomEndedAction = MapToPubSubShape export type VideoRoomUpdatedAction = MapToPubSubShape -export type VideoRoomSubscribedAction = - MapToPubSubShape +export type CallSessionSubscribedAction = + MapToPubSubShape diff --git a/packages/webrtc/src/utils/interfaces.ts b/packages/webrtc/src/utils/interfaces.ts index 80d740ea2..681b9415b 100644 --- a/packages/webrtc/src/utils/interfaces.ts +++ b/packages/webrtc/src/utils/interfaces.ts @@ -1,8 +1,8 @@ import type { VideoPositions } from '@signalwire/core' import { BaseConnectionState, - VideoRoomDeviceEventParams, - VideoRoomDeviceEventNames, + CallSessionDeviceEventParams, + CallSessionDeviceEventNames, } from '@signalwire/core' export interface ConnectionOptions { @@ -157,8 +157,8 @@ type BaseConnectionEventsHandlerMap = Record< > & Record void> & Record< - VideoRoomDeviceEventNames, - (params: VideoRoomDeviceEventParams) => void + CallSessionDeviceEventNames, + (params: CallSessionDeviceEventParams) => void > export type BaseConnectionEvents = { diff --git a/packages/webrtc/src/workers/roomSubscribedWorker.ts b/packages/webrtc/src/workers/roomSubscribedWorker.ts index e433629d5..6a5d8a41d 100644 --- a/packages/webrtc/src/workers/roomSubscribedWorker.ts +++ b/packages/webrtc/src/workers/roomSubscribedWorker.ts @@ -6,9 +6,9 @@ import { SDKActions, MapToPubSubShape, SDKWorkerHooks, - VideoRoomSubscribedEvent, + CallSessionSubscribedEvent, componentActions, - VideoRoomSubscribedEventParams, + CallSessionSubscribedEventParams, Rooms, RoomSessionStream, RoomSessionPlayback, @@ -37,16 +37,17 @@ export const roomSubscribedWorker: SDKWorker< throw new Error('Missing rtcPeerId for roomSubscribedWorker') } try { - const action: MapToPubSubShape = - yield sagaEffects.take(swEventChannel, (action: SDKActions) => { - if ( - action.type === 'video.room.subscribed' || - action.type === 'call.joined' - ) { - return action.payload.call_id === rtcPeerId - } - return false - }) + const action: MapToPubSubShape< + CallSessionSubscribedEvent | CallJoinedEvent + > = yield sagaEffects.take(swEventChannel, (action: SDKActions) => { + if ( + action.type === 'video.room.subscribed' || + action.type === 'call.joined' + ) { + return action.payload.call_id === rtcPeerId + } + return false + }) // New emitter should not change the payload by reference const clonedPayload = JSON.parse(JSON.stringify(action.payload)) @@ -84,7 +85,7 @@ export const roomSubscribedWorker: SDKWorker< function transformPayload( this: BaseConnection, - payload: VideoRoomSubscribedEventParams + payload: CallSessionSubscribedEventParams ) { const keys = ['room_session', 'room'] as const keys.forEach((key) => {