diff --git a/src/common/types/events.types.ts b/src/common/types/events.types.ts index e1a15263..accb41ef 100644 --- a/src/common/types/events.types.ts +++ b/src/common/types/events.types.ts @@ -17,6 +17,7 @@ export enum MeetingEvent { MY_PARTICIPANT_UPDATED = 'my-participant.update', MY_PARTICIPANT_LEFT = 'my-participant.left', MY_PARTICIPANT_JOINED = 'my-participant.joined', + MEETING_KICK_PARTICIPANT = 'meeting.kick-participant', DESTROY = 'destroy', } @@ -53,11 +54,13 @@ export enum RealtimeEvent { REALTIME_FOLLOW_PARTICIPANT = 'realtime.follow-participant', REALTIME_SET_AVATAR = 'realtime.set-avatar', REALTIME_DRAWING_CHANGE = 'realtime.drawing-change', + REALTIME_TRANSCRIPT_CHANGE = 'realtime.transcript-change', } -export enum TranscriptionEvent { - TRANSCRIPTION_START = 'transcription.start', - TRANSCRIPTION_STOP = 'transcription.stop', +export enum TranscriptState { + TRANSCRIPT_START = 'transcript.start', + TRANSCRIPT_RUNNING = 'transcript.running', + TRANSCRIPT_STOP = 'transcript.stop', } export enum MeetingState { diff --git a/src/index.test.ts b/src/index.test.ts index 5cfa4ec3..7b088d01 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -38,8 +38,6 @@ const COMMUNICATOR_INSTANCE_MOCK = { toggleScreenShare: jest.fn(), hangUp: jest.fn(), toggleChat: jest.fn(), - startTranscription: jest.fn(), - stopTranscription: jest.fn(), loadPlugin: jest.fn(), unloadPlugin: jest.fn(), }; @@ -83,11 +81,7 @@ jest.mock('./services/api'); jest.mock('./services/auth-service', () => ({ __esModule: true, default: jest.fn().mockImplementation((_, apiKey: string) => { - if (apiKey === UNIT_TEST_API_KEY) { - return true; - } - - return false; + return apiKey === UNIT_TEST_API_KEY; }), })); jest.mock('./services/remote-config-service'); diff --git a/src/services/communicator/ index.test.ts b/src/services/communicator/ index.test.ts index bb0bcb67..aaf02849 100644 --- a/src/services/communicator/ index.test.ts +++ b/src/services/communicator/ index.test.ts @@ -1,7 +1,4 @@ -import exp from 'constants'; - import { MOCK_OBSERVER_HELPER } from '../../../__mocks__/observer-helper.mock'; -import { MOCK_LOCAL_PARTICIPANT } from '../../../__mocks__/participants.mock'; import { MOCK_AVATAR_CONFIG, MOCK_PARTICIPANT_TO_3D, @@ -10,7 +7,6 @@ import { import { MeetingControlsEvent, RealtimeEvent, - TranscriptionEvent, } from '../../common/types/events.types'; import { AblyRealtimeService } from '../realtime'; @@ -54,6 +50,7 @@ const AblyRealtimeMock = { start: jest.fn(), leave: jest.fn(), setFollowParticipant: jest.fn(), + setTranscript: jest.fn(), fetchSyncClientProperty: jest.fn((key?: string) => { if (key) { return createRealtimeMessage(key); @@ -69,6 +66,7 @@ const AblyRealtimeMock = { hostAvailabilityObserver: MOCK_OBSERVER_HELPER, syncPropertiesObserver: MOCK_OBSERVER_HELPER, kickAllParticipantsObserver: MOCK_OBSERVER_HELPER, + kickParticipantObserver: MOCK_OBSERVER_HELPER, authenticationObserver: MOCK_OBSERVER_HELPER, }; @@ -79,6 +77,7 @@ const VideoManagerMock = { frameSizeObserver: MOCK_OBSERVER_HELPER, realtimeObserver: MOCK_OBSERVER_HELPER, hostChangeObserver: MOCK_OBSERVER_HELPER, + kickParticipantObserver: MOCK_OBSERVER_HELPER, gridModeChangeObserver: MOCK_OBSERVER_HELPER, drawingChangeObserver: MOCK_OBSERVER_HELPER, followParticipantObserver: MOCK_OBSERVER_HELPER, @@ -91,6 +90,7 @@ const VideoManagerMock = { meetingConnectionObserver: MOCK_OBSERVER_HELPER, participantJoinedObserver: MOCK_OBSERVER_HELPER, participantLeftObserver: MOCK_OBSERVER_HELPER, + transcriptChangeObserver: MOCK_OBSERVER_HELPER, }; jest.mock('../realtime', () => ({ @@ -106,7 +106,7 @@ describe('Communicator', () => { expect(Communicator).toBeDefined(); }); - test('should exprt a function', () => { + test('should expect a function', () => { expect(typeof Communicator).toBe('function'); }); @@ -130,8 +130,6 @@ describe('Communicator', () => { expect(communicator).toHaveProperty('hangUp'); expect(communicator).toHaveProperty('toggleCam'); expect(communicator).toHaveProperty('toggleChat'); - expect(communicator).toHaveProperty('startTranscription'); - expect(communicator).toHaveProperty('stopTranscription'); expect(communicator).toHaveProperty('loadPlugin'); expect(communicator).toHaveProperty('unloadPlugin'); }); @@ -221,11 +219,13 @@ describe('Communicator', () => { expect(AblyRealtimeMock.hostAvailabilityObserver.unsubscribe).toBeCalled(); expect(AblyRealtimeMock.syncPropertiesObserver.unsubscribe).toBeCalled(); expect(AblyRealtimeMock.kickAllParticipantsObserver.unsubscribe).toBeCalled(); + expect(AblyRealtimeMock.kickParticipantObserver.unsubscribe).toBeCalled(); expect(AblyRealtimeMock.authenticationObserver.unsubscribe).toBeCalled(); expect(VideoManagerMock.frameStateObserver.unsubscribe).toBeCalled(); expect(VideoManagerMock.frameSizeObserver.unsubscribe).toBeCalled(); expect(VideoManagerMock.realtimeObserver.unsubscribe).toBeCalled(); expect(VideoManagerMock.hostChangeObserver.unsubscribe).toBeCalled(); + expect(VideoManagerMock.kickParticipantObserver.unsubscribe).toBeCalled(); expect(VideoManagerMock.followParticipantObserver.unsubscribe).toBeCalled(); expect(VideoManagerMock.goToParticipantObserver.unsubscribe).toBeCalled(); expect(VideoManagerMock.gatherParticipantsObserver.unsubscribe).toBeCalled(); @@ -236,6 +236,7 @@ describe('Communicator', () => { expect(VideoManagerMock.participantLeftObserver.unsubscribe).toBeCalled(); expect(VideoManagerMock.meetingStateObserver.unsubscribe).toBeCalled(); expect(VideoManagerMock.meetingConnectionObserver.unsubscribe).toBeCalled(); + expect(VideoManagerMock.transcriptChangeObserver.unsubscribe).toBeCalled(); }); }); @@ -399,46 +400,6 @@ describe('Communicator', () => { }); }); - describe('startTranscription', () => { - let communicator: SuperVizSdk; - - beforeEach(() => { - jest.clearAllMocks(); - communicator = Communicator(COMMUNICATOR_INITIALIZATION_MOCK); - }); - - test('should call the startTranscription method', () => { - jest.spyOn(communicator, 'startTranscription'); - communicator.startTranscription('en-US'); - - expect(communicator.startTranscription).toBeCalled(); - expect(VideoManagerMock.publishMessageToFrame).toBeCalledWith( - TranscriptionEvent.TRANSCRIPTION_START, - 'en-US', - ); - }); - }); - - describe('stopTranscription', () => { - let communicator: SuperVizSdk; - - beforeEach(() => { - jest.clearAllMocks(); - communicator = Communicator(COMMUNICATOR_INITIALIZATION_MOCK); - }); - - test('should call the stopTranscription method', () => { - jest.spyOn(communicator, 'stopTranscription'); - communicator.stopTranscription(); - - expect(communicator.stopTranscription).toBeCalled(); - expect(VideoManagerMock.publishMessageToFrame).toBeCalledWith( - TranscriptionEvent.TRANSCRIPTION_STOP, - undefined, - ); - }); - }); - describe('loadPlugin', () => { let communicator: SuperVizSdk; diff --git a/src/services/communicator/index.ts b/src/services/communicator/index.ts index 2c7b6765..f0b950ae 100644 --- a/src/services/communicator/index.ts +++ b/src/services/communicator/index.ts @@ -9,7 +9,7 @@ import { MeetingEvent, MeetingState, RealtimeEvent, - TranscriptionEvent, + TranscriptState, } from '../../common/types/events.types'; import { Participant, Group } from '../../common/types/participant.types'; import { Observer, logger } from '../../common/utils'; @@ -144,6 +144,7 @@ class Communicator { this.realtime.hostObserver.subscribe(this.onHostParticipantDidChange); this.realtime.syncPropertiesObserver.subscribe(this.onSyncPropertiesDidChange); this.realtime.kickAllParticipantsObserver.subscribe(this.onKickAllParticipantsDidChange); + this.realtime.kickParticipantObserver.subscribe(this.onMyParticipantLeft); this.realtime.authenticationObserver.subscribe(this.onAuthenticationFailed); this.realtime.hostAvailabilityObserver.subscribe(this.onHostAvailabilityDidChange); @@ -192,9 +193,11 @@ class Communicator { this.videoManager.realtimeObserver.unsubscribe(this.onRealtimeJoin); this.videoManager.hostChangeObserver.unsubscribe(this.onHostDidChange); + this.videoManager.kickParticipantObserver.unsubscribe(this.onKickParticipant); this.videoManager.followParticipantObserver.unsubscribe(this.onFollowParticipantDidChange); this.videoManager.gridModeChangeObserver.unsubscribe(this.onGridModeDidChange); this.videoManager.drawingChangeObserver.unsubscribe(this.onDrawingDidChange); + this.videoManager.transcriptChangeObserver.unsubscribe(this.onTranscriptDidChange); this.videoManager.goToParticipantObserver.unsubscribe(this.onGoToParticipantDidChange); this.videoManager.gatherParticipantsObserver.unsubscribe(this.onGatherDidChange); @@ -212,6 +215,7 @@ class Communicator { this.realtime.hostObserver.unsubscribe(this.onHostParticipantDidChange); this.realtime.syncPropertiesObserver.unsubscribe(this.onSyncPropertiesDidChange); this.realtime.kickAllParticipantsObserver.unsubscribe(this.onKickAllParticipantsDidChange); + this.realtime.kickParticipantObserver.unsubscribe(this.onMyParticipantLeft); this.realtime.authenticationObserver.unsubscribe(this.onAuthenticationFailed); this.realtime.participantJoinedObserver.unsubscribe(this.onParticipantJoined); this.realtime.participantLeaveObserver.unsubscribe(this.onParticipantLeft); @@ -266,8 +270,8 @@ class Communicator { /** * @function publishMeetingControlEvent - * @param {MeetingControlsEvent} event * @description publish event to meeting controls + * @param {MeetingControlsEvent} event * @returns {void} */ public publishMeetingControlEvent(event: MeetingControlsEvent): void { @@ -288,7 +292,7 @@ class Communicator { /** * @function follow * @description - send follow message to all participants on plugin - * @param participantId: string + * @param {string | undefined} participantId * @returns {void} */ public follow(participantId?: string): void { @@ -311,23 +315,13 @@ class Communicator { /** * @function goTo * @description call goTo on plugin - * @param participantId: string + * @param {string} participantId * @returns {void} */ public goTo(participantId: string): void { this.integrationManager.goToParticipant(participantId); } - /** - * @function publishTranscriptionEvent - * @description publish transcription event to transcription service - * @param {TranscriptionEvent} event - event to be published - * @param {unknown} payload - payload to be sent to transcription service - */ - public publishTranscriptionEvent = (event: TranscriptionEvent, payload?: unknown): void => { - this.videoManager.publishMessageToFrame(event, payload); - }; - /** * @function startVideo * @description start video manager @@ -343,11 +337,13 @@ class Communicator { this.videoManager.realtimeObserver.subscribe(this.onRealtimeJoin); this.videoManager.hostChangeObserver.subscribe(this.onHostDidChange); + this.videoManager.kickParticipantObserver.subscribe(this.onKickParticipant); this.videoManager.followParticipantObserver.subscribe(this.onFollowParticipantDidChange); this.videoManager.goToParticipantObserver.subscribe(this.onGoToParticipantDidChange); this.videoManager.gatherParticipantsObserver.subscribe(this.onGatherDidChange); this.videoManager.gridModeChangeObserver.subscribe(this.onGridModeDidChange); this.videoManager.drawingChangeObserver.subscribe(this.onDrawingDidChange); + this.videoManager.transcriptChangeObserver.subscribe(this.onTranscriptDidChange); this.videoManager.sameAccountErrorObserver.subscribe(this.onSameAccountError); this.videoManager.waitingForHostObserver.subscribe(this.onWaitingForHost); @@ -417,6 +413,16 @@ class Communicator { this.realtime.setHost(hostId); }; + /** + * @function onKickParticipant + * @description on kick a participant event + * @param {string} participantId - participant Id + * @returns {void} + */ + private onKickParticipant = (participantId: string): void => { + this.realtime.setKickParticipant(participantId); + }; + /** * @function onFollowParticipantDidChange * @description handler for follow participant change event @@ -487,13 +493,14 @@ class Communicator { * @returns {void} * */ private onRoomInfoUpdated = (room: AblyRealtimeData): void => { - const { isGridModeEnable, followParticipantId, gather, drawing } = room; + const { isGridModeEnable, followParticipantId, gather, drawing, transcript } = room; this.videoManager.publishMessageToFrame( RealtimeEvent.REALTIME_GRID_MODE_CHANGE, isGridModeEnable, ); this.videoManager.publishMessageToFrame(RealtimeEvent.REALTIME_DRAWING_CHANGE, drawing); + this.videoManager.publishMessageToFrame(RealtimeEvent.REALTIME_TRANSCRIPT_CHANGE, transcript); this.videoManager.publishMessageToFrame( RealtimeEvent.REALTIME_FOLLOW_PARTICIPANT, followParticipantId, @@ -605,6 +612,16 @@ class Communicator { this.realtime.setDrawing(drawing); }; + /** + * @function onTranscriptDidChange + * @description handler when transcript state changes + * @param {TranscriptState} state + * @returns {void} + * */ + private onTranscriptDidChange = (state: TranscriptState): void => { + this.realtime.setTranscript(state); + }; + /** * @function updateParticipantListFromAblyList * @description update participant list from ably participant list @@ -846,16 +863,6 @@ export default (params: CommunicatorOptions): SuperVizSdk => { return communicator.publishMeetingControlEvent(MeetingControlsEvent.TOGGLE_MEETING_CHAT); }, - startTranscription: (language) => { - return communicator.publishTranscriptionEvent( - TranscriptionEvent.TRANSCRIPTION_START, - language, - ); - }, - stopTranscription: () => { - return communicator.publishTranscriptionEvent(TranscriptionEvent.TRANSCRIPTION_STOP); - }, - loadPlugin: (plugin, props) => communicator.loadPlugin(plugin, props), unloadPlugin: () => communicator.unloadPlugin(), }; diff --git a/src/services/communicator/types.ts b/src/services/communicator/types.ts index d8cc666f..b171515d 100644 --- a/src/services/communicator/types.ts +++ b/src/services/communicator/types.ts @@ -2,7 +2,7 @@ import { SuperVizSdkOptions } from '../../common/types/sdk-options.types'; import { Plugin, PluginMethods, DefaultPluginOptions } from '../integration/base-plugin/types'; import { AvatarConfig } from '../integration/participants/types'; import { RealtimeMessage } from '../realtime/ably/types'; -import { LayoutPosition, WaterMark } from '../video-conference-manager/types'; +import { WaterMark } from '../video-conference-manager/types'; export interface CommunicatorOptions extends SuperVizSdkOptions { apiKey: string; @@ -43,9 +43,6 @@ export type SuperVizSdk = { toggleCam: () => void; toggleChat: () => void; - startTranscription: (language: string) => void; - stopTranscription: () => void; - loadPlugin: (plugin: Plugin, props: PluginOptions) => PluginMethods; unloadPlugin: () => void; }; diff --git a/src/services/realtime/ably/index.test.ts b/src/services/realtime/ably/index.test.ts index 847c5fb0..4059cc6b 100644 --- a/src/services/realtime/ably/index.test.ts +++ b/src/services/realtime/ably/index.test.ts @@ -3,6 +3,7 @@ import { TextEncoder } from 'util'; import Ably from 'ably'; import { MOCK_AVATAR, MOCK_LOCAL_PARTICIPANT } from '../../../../__mocks__/participants.mock'; +import { TranscriptState } from '../../../common/types/events.types'; import { ParticipantType } from '../../../common/types/participant.types'; import { RealtimeStateTypes } from '../../../common/types/realtime.types'; import { ParticipantInfo } from '../base/types'; @@ -191,7 +192,7 @@ describe('AblyRealtimeService', () => { expect(AblyRealtimeMock.connection.on).toHaveBeenCalledTimes(1); }); - test('should subscribe to broadcast channe if participant is audience', () => { + test('should subscribe to broadcast channel if participant is audience', () => { expect(AblyRealtimeServiceInstance.join).toBeDefined(); const participant: ParticipantInfo = { @@ -339,6 +340,19 @@ describe('AblyRealtimeService', () => { drawing, }); }); + + test('should update the room properties with transcript state', () => { + AblyRealtimeServiceInstance['updateRoomProperties'] = jest.fn(); + + const transcriptionState = TranscriptState.TRANSCRIPT_START; + + AblyRealtimeServiceInstance.setTranscript(transcriptionState); + + expect(AblyRealtimeServiceInstance['updateRoomProperties']).toHaveBeenCalledWith({ + transcript: transcriptionState, + }); + }); + /** * initializeRoomProperties */ @@ -1096,6 +1110,30 @@ describe('AblyRealtimeService', () => { }); }); + describe('kick participant event', () => { + test('should update the kickParticipant in the room properties', async () => { + const participantId = 'participant1'; + const participant: AblyParticipant = { + clientId: 'client1', + action: 'present', + connectionId: 'connection1', + encoding: 'h264', + id: 'unit-test-participant-ably-id', + timestamp: new Date().getTime(), + data: { + participantId, + }, + }; + AblyRealtimeServiceInstance['participants'][participantId] = participant; + AblyRealtimeServiceInstance['updateRoomProperties'] = jest.fn(); + await AblyRealtimeServiceInstance.setKickParticipant(participantId); + + expect(AblyRealtimeServiceInstance['updateRoomProperties']).toHaveBeenCalledWith({ + kickParticipant: participant, + }); + }); + }); + describe('presence events handlers', () => { beforeEach(() => { const participant: ParticipantInfo = { diff --git a/src/services/realtime/ably/index.ts b/src/services/realtime/ably/index.ts index 5b5fc858..c2221928 100644 --- a/src/services/realtime/ably/index.ts +++ b/src/services/realtime/ably/index.ts @@ -1,7 +1,7 @@ import Ably from 'ably'; import throttle from 'lodash/throttle'; -import { RealtimeEvent } from '../../../common/types/events.types'; +import { RealtimeEvent, TranscriptState } from '../../../common/types/events.types'; import { ParticipantType } from '../../../common/types/participant.types'; import { RealtimeStateTypes } from '../../../common/types/realtime.types'; import { logger } from '../../../common/utils'; @@ -234,6 +234,21 @@ export default class AblyRealtimeService extends RealtimeService implements Ably }); }; + /** + * @function setKickParticipant + * @param {string} kickParticipantId + * @description set a participant to be kicked from the room + * @returns {void} + */ + public setKickParticipant = (kickParticipantId: string): Promise => { + if (!kickParticipantId) return; + + const participant = this.participants[kickParticipantId]; + this.updateRoomProperties({ + kickParticipant: participant, + }); + }; + /** * @function setGridMode * @param {boolean} isGridModeEnable @@ -256,6 +271,17 @@ export default class AblyRealtimeService extends RealtimeService implements Ably this.updateRoomProperties(Object.assign({}, roomProperties, { drawing })); } + /** + * @function setTranscript + * @param state {TranscriptState} + * @description synchronizes the transcript state in the room + * @returns {void} + */ + public setTranscript(state: TranscriptState): void { + const roomProperties = this.localRoomProperties; + this.updateRoomProperties(Object.assign({}, roomProperties, { transcript: state })); + } + /** * @function setSyncProperty * @param {string} name @@ -521,6 +547,11 @@ export default class AblyRealtimeService extends RealtimeService implements Ably } this.updateParticipants(); + + if (data.kickParticipant && data.kickParticipant.clientId === this.myParticipant.clientId) { + this.updateRoomProperties({ kickParticipant: null }); + this.kickParticipantObserver.publish(this.myParticipant.clientId); + } }; /** @@ -787,13 +818,13 @@ export default class AblyRealtimeService extends RealtimeService implements Ably * @function fetchSyncClientProperty * @description * @param {string} eventName - name event to be fetched - * @returns {ClientRealtimeData} + * @returns {Promise} */ public async fetchSyncClientProperty( eventName?: string, ): Promise> { try { - const clienthistory: Record = await new Promise( + const clientHistory: Record = await new Promise( (resolve, reject) => { this.clientRoomStateChannel.history((error, resultPage) => { if (error) reject(error); @@ -809,15 +840,15 @@ export default class AblyRealtimeService extends RealtimeService implements Ably }, ); - if (eventName && !clienthistory[eventName]) { + if (eventName && !clientHistory[eventName]) { throw new Error(`Event ${eventName} not found in the history`); } if (eventName) { - return clienthistory[eventName]; + return clientHistory[eventName]; } - return clienthistory; + return clientHistory; } catch (error) { logger.log('REALTIME', 'Error in fetch client realtime data', error.message); this.throw(error.message); @@ -1116,6 +1147,7 @@ export default class AblyRealtimeService extends RealtimeService implements Ably * @function isMessageTooBig * @description calculates the size of a sync message and checks if it's bigger than limit * @param {unknown} msg + * @param {number} limit * @returns {boolean} */ private isMessageTooBig = (msg: unknown, limit: number = MESSAGE_SIZE_LIMIT): boolean => { diff --git a/src/services/realtime/ably/types.ts b/src/services/realtime/ably/types.ts index 452262fd..3d86eeb0 100644 --- a/src/services/realtime/ably/types.ts +++ b/src/services/realtime/ably/types.ts @@ -1,5 +1,6 @@ import type Ably from 'ably'; +import { TranscriptState } from '../../../common/types/events.types'; import { DrawingData } from '../../video-conference-manager/types'; import { DefaultRealtimeMethods } from '../base/types'; @@ -17,6 +18,8 @@ export interface AblyRealtimeData { followParticipantId?: string; gather?: boolean; drawing?: DrawingData; + kickParticipant?: AblyParticipant; + transcript?: TranscriptState; } export type AblyTokenCallBack = ( diff --git a/src/services/realtime/base/index.ts b/src/services/realtime/base/index.ts index b49d48d5..e6ddf16c 100644 --- a/src/services/realtime/base/index.ts +++ b/src/services/realtime/base/index.ts @@ -16,6 +16,7 @@ export class RealtimeService implements DefaultRealtimeService { public realtimeStateObserver: Observer; public syncPropertiesObserver: Observer; public kickAllParticipantsObserver: Observer; + public kickParticipantObserver: Observer; public authenticationObserver: Observer; constructor() { @@ -34,6 +35,7 @@ export class RealtimeService implements DefaultRealtimeService { this.hostAvailabilityObserver = new Observer({ logger }); this.realtimeStateObserver = new Observer({ logger }); this.kickAllParticipantsObserver = new Observer({ logger }); + this.kickParticipantObserver = new Observer({ logger }); this.authenticationObserver = new Observer({ logger }); } diff --git a/src/services/realtime/base/types.ts b/src/services/realtime/base/types.ts index a6b8adb1..35dd298f 100644 --- a/src/services/realtime/base/types.ts +++ b/src/services/realtime/base/types.ts @@ -14,6 +14,7 @@ export interface DefaultRealtimeService { realtimeStateObserver: Observer; syncPropertiesObserver: Observer; kickAllParticipantsObserver: Observer; + kickParticipantObserver: Observer; authenticationObserver: Observer; } diff --git a/src/services/video-conference-manager/index.test.ts b/src/services/video-conference-manager/index.test.ts index 5d733435..fbe37dd0 100644 --- a/src/services/video-conference-manager/index.test.ts +++ b/src/services/video-conference-manager/index.test.ts @@ -6,7 +6,6 @@ import { MeetingControlsEvent, MeetingEvent, MeetingState, - TranscriptionEvent, } from '../../common/types/events.types'; import { Participant } from '../../common/types/participant.types'; import { BrowserService } from '../browser'; @@ -504,6 +503,17 @@ describe('VideoConferenceManager', () => { }); }); + describe('onMeetingKickParticipant', () => { + test('should publish the participant ID to be kicked', () => { + const participantId = '1'; + const spy = jest.spyOn(VideoConferenceManagerInstance.kickParticipantObserver, 'publish'); + + VideoConferenceManagerInstance['onMeetingKickParticipant'](participantId); + + expect(spy).toHaveBeenCalledWith(participantId); + }); + }); + describe('onFollowParticipantDidChange', () => { test('should publish the new participant ID', () => { const participantId = '1'; diff --git a/src/services/video-conference-manager/index.ts b/src/services/video-conference-manager/index.ts index bf8efe9d..21cc8681 100644 --- a/src/services/video-conference-manager/index.ts +++ b/src/services/video-conference-manager/index.ts @@ -7,8 +7,7 @@ import { RealtimeEvent, Dimensions, MeetingControlsEvent, - FrameEvent, - TranscriptionEvent, + FrameEvent, TranscriptState, } from '../../common/types/events.types'; import { StartMeetingOptions } from '../../common/types/meeting.types'; import { Participant, Avatar } from '../../common/types/participant.types'; @@ -33,7 +32,7 @@ import { const FRAME_ID = 'sv-video-frame'; -export default class VideoConfereceManager { +export default class VideoConferenceManager { private messageBridge: MessageBridge; private bricklayer: FrameBricklayer; private browserService: BrowserService; @@ -51,8 +50,10 @@ export default class VideoConfereceManager { public readonly realtimeObserver = new Observer({ logger }); public readonly hostChangeObserver = new Observer({ logger }); + public readonly kickParticipantObserver = new Observer({ logger }); public readonly gridModeChangeObserver = new Observer({ logger }); public readonly drawingChangeObserver = new Observer({ logger }); + public readonly transcriptChangeObserver = new Observer({ logger }); public readonly followParticipantObserver = new Observer({ logger }); public readonly goToParticipantObserver = new Observer({ logger }); @@ -252,6 +253,7 @@ export default class VideoConfereceManager { ); this.messageBridge.listen(MeetingEvent.MEETING_PARTICIPANT_LEFT, this.onParticipantLeft); this.messageBridge.listen(MeetingEvent.MEETING_HOST_CHANGE, this.onMeetingHostChange); + this.messageBridge.listen(MeetingEvent.MEETING_KICK_PARTICIPANT, this.onMeetingKickParticipant); this.messageBridge.listen(MeetingEvent.MEETING_SAME_PARTICIPANT_ERROR, this.onSameAccountError); this.messageBridge.listen(MeetingEvent.MEETING_STATE_UPDATE, this.meetingStateUpdate); this.messageBridge.listen( @@ -262,6 +264,7 @@ export default class VideoConfereceManager { this.messageBridge.listen(RealtimeEvent.REALTIME_JOIN, this.realtimeJoin); this.messageBridge.listen(RealtimeEvent.REALTIME_GRID_MODE_CHANGE, this.onGridModeChange); this.messageBridge.listen(RealtimeEvent.REALTIME_DRAWING_CHANGE, this.onDrawingChange); + this.messageBridge.listen(RealtimeEvent.REALTIME_TRANSCRIPT_CHANGE, this.onTranscriptChange); this.messageBridge.listen(FrameEvent.FRAME_DIMENSIONS_UPDATE, this.onFrameDimensionsUpdate); this.messageBridge.listen( @@ -406,7 +409,7 @@ export default class VideoConfereceManager { }; /** - * @function updateMeetingAvatar + * @function updateMeetingAvatars * @description update list of avatars * @returns {void} */ @@ -463,6 +466,15 @@ export default class VideoConfereceManager { this.hostChangeObserver.publish(hostId); }; + /** + * @function onMeetingKickParticipant + * @param {string} participantId - ID of the participant + * @returns {void} + */ + private onMeetingKickParticipant = (participantId: string): void => { + this.kickParticipantObserver.publish(participantId); + }; + /** * @function onFollowParticipantDidChange * @param {string} participantId @@ -507,6 +519,15 @@ export default class VideoConfereceManager { this.drawingChangeObserver.publish(drawing); }; + /** + * @function onTranscriptChange + * @param state {TranscriptState} + * @returns {void} + */ + private onTranscriptChange = (state: TranscriptState): void => { + this.transcriptChangeObserver.publish(state); + }; + /** * @function onSameAccountError * @param {string} error @@ -580,8 +601,10 @@ export default class VideoConfereceManager { this.frameSizeObserver.destroy(); this.realtimeObserver.destroy(); this.hostChangeObserver.destroy(); + this.kickParticipantObserver.destroy(); this.gridModeChangeObserver.destroy(); this.drawingChangeObserver.destroy(); + this.transcriptChangeObserver.destroy(); this.followParticipantObserver.destroy(); this.goToParticipantObserver.destroy(); @@ -600,11 +623,11 @@ export default class VideoConfereceManager { /** * @function publishMessageToFrame * @description Publishes a message to the frame - * @param message - The event to publish + * @param event - The event to publish * @param payload - The payload to publish */ public publishMessageToFrame( - event: MeetingControlsEvent | MeetingEvent | RealtimeEvent | TranscriptionEvent, + event: MeetingControlsEvent | MeetingEvent | RealtimeEvent, payload?: unknown, ): void { this.messageBridge.publish(event, payload);