diff --git a/frontend/webcomponent/src/components/OpenViduMeet.ts b/frontend/webcomponent/src/components/OpenViduMeet.ts index f030076..2a8e394 100644 --- a/frontend/webcomponent/src/components/OpenViduMeet.ts +++ b/frontend/webcomponent/src/components/OpenViduMeet.ts @@ -3,6 +3,7 @@ import { InboundCommandMessage } from '../models/message.type'; import { CommandsManager } from './CommandsManager'; import { EventsManager } from './EventsManager'; import styles from '../assets/css/styles.css'; +import { WebComponentEvent } from '../models/event.model'; /** * The `OpenViduMeet` web component provides an interface for embedding an OpenVidu Meet room within a web page. @@ -33,6 +34,14 @@ export class OpenViduMeet extends HTMLElement { private loadTimeout: any; private iframeLoaded = false; private errorMessage: string | null = null; + /** + * A map to store event handlers for different events. + * This allows for dynamic event handling and can be used to add or remove event listeners. + * + * @private + * @type {Map>} + */ + private eventHandlers: Map> = new Map(); constructor() { super(); @@ -110,7 +119,6 @@ export class OpenViduMeet extends HTMLElement { // Set up load handlers this.iframe.onload = (event: Event) => { - console.warn('Iframe loaded', event); const message: InboundCommandMessage = { command: WebComponentCommand.INITIALIZE, payload: { domain: window.location.origin } @@ -167,6 +175,96 @@ export class OpenViduMeet extends HTMLElement { this.render(); } + /** + * Subscribe to an event + * @param eventName Name of the event to listen for + * @param callback Function to be called when the event is triggered + * @returns The component instance for chaining + */ + public on(eventName: string, callback: (detail: any) => void): this { + if (!(Object.values(WebComponentEvent) as string[]).includes(eventName)) { + console.warn(`Event "${eventName}" is not supported.`); + return this; + } + + // Create event listener that will call the callback + const listener = ((event: CustomEvent) => { + callback(event.detail); + }) as EventListener; + + // Store reference to original callback for off() method + if (!this.eventHandlers.has(eventName)) { + this.eventHandlers.set(eventName, new Set()); + } + + // Store both the callback and listener to match them later + const handlers = this.eventHandlers.get(eventName); + // @ts-ignore - To store both values together + callback._listener = listener; + handlers?.add(callback); + + // Register with standard DOM API + + this.addEventListener(eventName, listener); + + return this; + } + + /** + * Subscribe to an event that will be triggered only once + * @param eventName Name of the event to listen for + * @param callback Function to be called when the event is triggered + * @returns The component instance for chaining + */ + public once(eventName: string, callback: (detail: any) => void): this { + if (!(Object.values(WebComponentEvent) as string[]).includes(eventName)) { + console.warn(`Event "${eventName}" is not supported.`); + return this; + } + + // Create a wrapper that will call the callback and then unsubscribe + const wrapperCallback = (detail: any) => { + // Unsubscribe first to prevent any possibility of duplicate calls + this.off(eventName, wrapperCallback); + // Call the original callback + callback(detail); + }; + + this.on(eventName, wrapperCallback); + + return this; + } + + /** + * Unsubscribe from an event + * @param eventName Name of the event to stop listening for + * @param callback Optional callback to remove (if not provided, removes all handlers for this event) + * @returns The component instance for chaining + */ + public off(eventName: string, callback?: (detail: any) => void): this { + if (!callback) { + // Remove all handlers for this event + const handlers = this.eventHandlers.get(eventName); + if (handlers) { + handlers.forEach((handler) => { + // @ts-ignore - Access stored listener + this.removeEventListener(eventName, handler._listener); + }); + handlers.clear(); + } + } else { + // Remove specific handler + const handlers = this.eventHandlers.get(eventName); + if (handlers && handlers.has(callback)) { + // @ts-ignore - Access stored listener + this.removeEventListener(eventName, callback._listener); + handlers.delete(callback); + } + } + + return this; + } + // ---- WebComponent Commands ---- // These methods send commands to the OpenVidu Meet iframe. diff --git a/frontend/webcomponent/src/models/event.model.ts b/frontend/webcomponent/src/models/event.model.ts index 72f416c..72ffb15 100644 --- a/frontend/webcomponent/src/models/event.model.ts +++ b/frontend/webcomponent/src/models/event.model.ts @@ -30,6 +30,7 @@ export interface EventPayloads { [WebComponentEvent.LEFT]: { roomId: string; participantName: string; + reason: string; }; [WebComponentEvent.MEETING_ENDED]: { roomId: string;