diff --git a/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.ts b/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.ts index bdbe2b9..f86fca1 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.ts +++ b/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.ts @@ -25,7 +25,8 @@ import { RoomService, SessionStorageService } from '../../services'; -import { OpenViduMeetMessage, WebComponentEventType } from 'webcomponent/src/types/message.type'; +import { OutboundEventMessage } from 'webcomponent/src/models/message.type'; +import { WebComponentEvent } from 'webcomponent/src/models/event.model'; @Component({ selector: 'app-video-room', @@ -114,11 +115,11 @@ export class VideoRoomComponent implements OnInit, OnDestroy { } onParticipantConnected(event: ParticipantModel) { - const message: OpenViduMeetMessage = { - eventType: WebComponentEventType.LOCAL_PARTICIPANT_CONNECTED, + const message: OutboundEventMessage = { + event: WebComponentEvent.JOIN, payload: { - roomId: event.getProperties().room?.name, - participantName: event.name + roomId: event.getProperties().room?.name || '', + participantName: event.name! } }; this.wcManagerService.sendMessageToParent(message); @@ -129,8 +130,8 @@ export class VideoRoomComponent implements OnInit, OnDestroy { const redirectURL = this.ctxService.getLeaveRedirectURL() || '/disconnected'; const isExternalURL = /^https?:\/\//.test(redirectURL); - const message: OpenViduMeetMessage = { - eventType: WebComponentEventType.LOCAL_PARTICIPANT_LEFT, + const message: OutboundEventMessage = { + event: WebComponentEvent.LEFT, payload: { roomId: event.roomName, participantName: event.participantName diff --git a/frontend/projects/shared-meet-components/src/lib/services/webcomponent-manager/webcomponent-manager.service.ts b/frontend/projects/shared-meet-components/src/lib/services/webcomponent-manager/webcomponent-manager.service.ts index e5c7792..c7eb46c 100644 --- a/frontend/projects/shared-meet-components/src/lib/services/webcomponent-manager/webcomponent-manager.service.ts +++ b/frontend/projects/shared-meet-components/src/lib/services/webcomponent-manager/webcomponent-manager.service.ts @@ -8,12 +8,8 @@ import { OpenViduService, LoggerService } from 'projects/shared-meet-components/src/public-api'; -import { - OpenViduMeetMessage, - ParentMessage, - WebComponentActionType, - WebComponentEventType -} from 'webcomponent/src/types/message.type'; +import { WebComponentCommand } from 'webcomponent/src/models/command.model'; +import { OutboundEventMessage, InboundCommandMessage } from 'webcomponent/src/models/message.type'; /** * Service to manage the commands from OpenVidu Meet WebComponent/Iframe. @@ -43,12 +39,12 @@ export class WebComponentManagerService { this.isListenerStarted = true; // Listen for messages from the iframe window.addEventListener('message', async (event) => { - const message: ParentMessage = event.data; + const message: InboundCommandMessage = event.data; const parentDomain = this.contextService.getParentDomain(); - const { action, payload } = message; + const { command, payload } = message; if (!parentDomain) { - if (action === WebComponentActionType.INITIALIZE) { + if (command === WebComponentCommand.INITIALIZE) { if (!payload || !('domain' in payload)) { console.error('Parent domain not provided in message payload'); return; @@ -66,19 +62,19 @@ export class WebComponentManagerService { console.debug('Message received from parent:', event.data); // TODO: reject if room is not connected - switch (action) { - case WebComponentActionType.END_MEETING: + switch (command) { + case WebComponentCommand.END_MEETING: // Moderator only if (this.contextService.isModeratorParticipant()) { const roomId = this.contextService.getRoomId(); await this.httpService.endMeeting(roomId); } break; - case WebComponentActionType.TOGGLE_CHAT: - // Toggle chat - this.panelService.togglePanel(PanelType.CHAT); - break; - case WebComponentActionType.LEAVE_ROOM: + // case WebComponentCommand.TOGGLE_CHAT: + // Toggle chat + // this.panelService.togglePanel(PanelType.CHAT); + // break; + case WebComponentCommand.LEAVE_ROOM: // Leave room. await this.openviduService.disconnectRoom(); break; @@ -94,7 +90,7 @@ export class WebComponentManagerService { window.removeEventListener('message', this.startCommandsListener); } - sendMessageToParent(event: OpenViduMeetMessage /*| RoomDisconnectedEvent*/) { + sendMessageToParent(event: OutboundEventMessage) { if (!this.contextService.isEmbeddedMode()) return; this.log.d('Sending message to parent :', event); const origin = this.contextService.getParentDomain(); diff --git a/frontend/webcomponent/src/components/CommandsManager.ts b/frontend/webcomponent/src/components/CommandsManager.ts index 6a721b9..0270a91 100644 --- a/frontend/webcomponent/src/components/CommandsManager.ts +++ b/frontend/webcomponent/src/components/CommandsManager.ts @@ -1,4 +1,4 @@ -import { ParentMessage } from '../types/message.type'; +import { InboundCommandMessage } from '../models/message.type'; /** * Handles sending messages to the iframe. @@ -12,7 +12,7 @@ export class CommandsManager { this.allowedOrigin = allowedOrigin; } - public sendMessage(message: ParentMessage, targetOrigin?: string): void { + public sendMessage(message: InboundCommandMessage, targetOrigin?: string): void { targetOrigin = targetOrigin || this.allowedOrigin; this.iframe.contentWindow?.postMessage(message, targetOrigin); } diff --git a/frontend/webcomponent/src/components/EventsManager.ts b/frontend/webcomponent/src/components/EventsManager.ts index 69966c1..d484705 100644 --- a/frontend/webcomponent/src/components/EventsManager.ts +++ b/frontend/webcomponent/src/components/EventsManager.ts @@ -1,4 +1,4 @@ -import { OpenViduMeetMessage } from '../types/message.type'; +import { OutboundEventMessage } from '../models/message.type'; export class EventsManager { private element: HTMLElement; @@ -12,9 +12,9 @@ export class EventsManager { } private handleMessage(event: MessageEvent) { - const message: OpenViduMeetMessage = event.data; + const message: OutboundEventMessage = event.data; // Validate message origin (security measure) - if (!message || !message.eventType) { + if (!message || !message.event) { // console.warn('Invalid message:', message); return; } @@ -22,8 +22,8 @@ export class EventsManager { this.dispatchEvent(message); } - private dispatchEvent(message: OpenViduMeetMessage) { - const event = new CustomEvent(message.eventType, { + private dispatchEvent(message: OutboundEventMessage) { + const event = new CustomEvent(message.event, { detail: message.payload, bubbles: true, composed: true diff --git a/frontend/webcomponent/src/components/OpenViduMeet.ts b/frontend/webcomponent/src/components/OpenViduMeet.ts index 1a561fb..8c31672 100644 --- a/frontend/webcomponent/src/components/OpenViduMeet.ts +++ b/frontend/webcomponent/src/components/OpenViduMeet.ts @@ -1,4 +1,5 @@ -import { ParentMessage, WebComponentActionType } from '../types/message.type'; +import { WebComponentCommand } from '../models/command.model'; +import { InboundCommandMessage } from '../models/message.type'; import { CommandsManager } from './CommandsManager'; import { EventsManager } from './EventsManager'; @@ -68,8 +69,8 @@ export class OpenViduMeet extends HTMLElement { this.shadowRoot?.appendChild(style); this.shadowRoot?.appendChild(this.iframe); this.iframe.onload = () => { - const message: ParentMessage = { - action: WebComponentActionType.INITIALIZE, + const message: InboundCommandMessage = { + command: WebComponentCommand.INITIALIZE, payload: { domain: window.location.origin } }; this.commandsManager.sendMessage(message); @@ -101,17 +102,17 @@ export class OpenViduMeet extends HTMLElement { // Public methods public endMeeting() { - const message: ParentMessage = { action: WebComponentActionType.END_MEETING }; + const message: InboundCommandMessage = { command: WebComponentCommand.END_MEETING }; this.commandsManager.sendMessage(message); } public leaveRoom() { - const message: ParentMessage = { action: WebComponentActionType.LEAVE_ROOM }; + const message: InboundCommandMessage = { command: WebComponentCommand.LEAVE_ROOM }; this.commandsManager.sendMessage(message); } - public toggleChat() { - const message: ParentMessage = { action: WebComponentActionType.TOGGLE_CHAT }; - this.commandsManager.sendMessage(message); - } + // public toggleChat() { + // const message: ParentMessage = { action: WebComponentActionType.TOGGLE_CHAT }; + // this.commandsManager.sendMessage(message); + // } } diff --git a/frontend/webcomponent/src/models/command.model.ts b/frontend/webcomponent/src/models/command.model.ts new file mode 100644 index 0000000..b4393f0 --- /dev/null +++ b/frontend/webcomponent/src/models/command.model.ts @@ -0,0 +1,49 @@ +/** + * All available commands that can be sent to the WebComponent. + */ +export enum WebComponentCommand { + /** + * Initializes the WebComponent with the given configuration. + * @private + */ + INITIALIZE = 'INITIALIZE', + + /** + * Ends the current meeting for all participants. + * This command is only available for the moderator. + */ + END_MEETING = 'END_MEETING', + /** + * Disconnects the local participant from the current room. + */ + LEAVE_ROOM = 'LEAVE_ROOM' + // TOGGLE_CHAT = 'TOGGLE_CHAT' +} + +/** + * Type definitions for command payloads. + * Each property corresponds to a command in {@link WebComponentCommand}. + * @category Communication + */ +export interface CommandPayloads { + /** + * Payload for the INITIALIZE command. + * @private + */ + [WebComponentCommand.INITIALIZE]: { + domain: string; + }; + [WebComponentCommand.END_MEETING]: void; + [WebComponentCommand.LEAVE_ROOM]: void; + // [WebComponentCommand.TOGGLE_CHAT]: void; +} + +/** + * Gets the type-safe payload for a specific command. + * This type allows TypeScript to infer the correct payload type based on the command. + * @category Type Helpers + * @private + */ +export type CommandPayloadFor = T extends keyof CommandPayloads + ? CommandPayloads[T] + : never; diff --git a/frontend/webcomponent/src/models/event.model.ts b/frontend/webcomponent/src/models/event.model.ts new file mode 100644 index 0000000..8281fdc --- /dev/null +++ b/frontend/webcomponent/src/models/event.model.ts @@ -0,0 +1,45 @@ +/** + * All available events that can be emitted by the WebComponent. + * @category Communication + */ +export enum WebComponentEvent { + /** + * Event emitted when the local participant joins the room. + */ + JOIN = 'JOIN', + /** + * Event emitted when the local participant leaves the room. + */ + LEFT = 'LEFT', + /** + * Event emitted when a moderator ends the meeting. + */ + MEETING_ENDED = 'MEETING_ENDED' +} + +/** + * Type definitions for event payloads. + * Each property corresponds to an event in {@link WebComponentEvent}. + * @category Communication + */ +export interface EventPayloads { + [WebComponentEvent.JOIN]: { + roomId: string; + participantName: string; + }; + [WebComponentEvent.LEFT]: { + roomId: string; + participantName: string; + }; + [WebComponentEvent.MEETING_ENDED]: { + roomId: string; + }; +} + +/** + * Gets the type-safe payload for a specific event. + * This type allows TypeScript to infer the correct payload type based on the event. + * @category Type Helpers + * @private + */ +export type EventPayloadFor = T extends keyof EventPayloads ? EventPayloads[T] : never; diff --git a/frontend/webcomponent/src/models/message.type.ts b/frontend/webcomponent/src/models/message.type.ts new file mode 100644 index 0000000..08808b3 --- /dev/null +++ b/frontend/webcomponent/src/models/message.type.ts @@ -0,0 +1,62 @@ +import { CommandPayloadFor, WebComponentCommand } from './command.model'; +import { EventPayloadFor, WebComponentEvent } from './event.model'; + +/** + * Represents all possible messages exchanged between the host application and WebComponent. + * @category Communication + */ +export type WebComponentMessage = InboundCommandMessage | OutboundEventMessage; + +/** + * Message sent from the host application to the WebComponent. + * Contains a command with an optional type-safe payload. + * @category Communication + */ +export interface InboundCommandMessage { + /** The command to execute in the WebComponent */ + command: T; + /** Optional payload with additional data for the command */ + payload?: CommandPayloadFor; +} + +/** + * Message sent from the WebComponent to the host application. + * Contains an event type with an optional type-safe payload. + * @category Communication + */ +export interface OutboundEventMessage { + /** The type of event being emitted */ + event: T; + /** Optional payload with additional data about the event */ + payload?: EventPayloadFor; +} + +/** + * Helper function to create a properly typed command message. + * @param command The command to send + * @param payload The payload for the command + * @returns A properly formatted command message + * @category Utilities + * @private + */ +export function createCommandMessage( + command: T, + payload?: CommandPayloadFor +): InboundCommandMessage { + return { command, payload }; +} + +/** + * Helper function to create a properly typed event message. + * @param event The event type + * @param payload The payload for the event + * @returns A properly formatted event message + * @category Utilities + * @private + */ +export function createEventMessage( + event: T, + payload?: EventPayloadFor +): OutboundEventMessage { + return { event, payload }; +} diff --git a/frontend/webcomponent/src/types/message.type.ts b/frontend/webcomponent/src/types/message.type.ts deleted file mode 100644 index 4281a87..0000000 --- a/frontend/webcomponent/src/types/message.type.ts +++ /dev/null @@ -1,29 +0,0 @@ -export type WebComponentMessage = ParentMessage | OpenViduMeetMessage; - -/** - * Message sent from the parent to the OpenViduMeet component. - */ -export interface ParentMessage { - action: WebComponentActionType; - payload?: Record; -} - -/** - * Message sent from the OpenViduMeet component to the parent. - */ -export interface OpenViduMeetMessage { - eventType: WebComponentEventType; - payload?: Record; -} - -export enum WebComponentActionType { - INITIALIZE = 'initialize', - END_MEETING = 'endMeeting', - LEAVE_ROOM = 'leaveRoom', - TOGGLE_CHAT = 'toggleChat' -} - -export enum WebComponentEventType { - LOCAL_PARTICIPANT_CONNECTED = 'join', - LOCAL_PARTICIPANT_LEFT = 'left' -}