import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { MatIcon } from '@angular/material/icon'; import { RecordingDeleteRequestedEvent, RecordingStartRequestedEvent, RecordingStopRequestedEvent, ApiDirectiveModule, ParticipantLeftEvent, ParticipantModel, OpenViduComponentsUiModule, ParticipantLeftReason } from 'openvidu-components-angular'; import { MeetChatPreferences, MeetRecordingAccess, MeetRecordingPreferences, MeetVirtualBackgroundPreferences } from '@lib/typings/ce'; import { HttpService, WebComponentManagerService, ContextService, RoomService, SessionStorageService } from '../../services'; import { OutboundEventMessage } from 'webcomponent/src/models/message.type'; import { WebComponentEvent } from 'webcomponent/src/models/event.model'; @Component({ selector: 'app-video-room', templateUrl: './video-room.component.html', styleUrls: ['./video-room.component.scss'], standalone: true, imports: [OpenViduComponentsUiModule, ApiDirectiveModule, MatIcon] }) export class VideoRoomComponent implements OnInit, OnDestroy { roomId = ''; participantName = ''; token = ''; serverError = ''; loading = true; chatPreferences: MeetChatPreferences = { enabled: true }; recordingPreferences: MeetRecordingPreferences = { enabled: true, allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER }; virtualBackgroundPreferences: MeetVirtualBackgroundPreferences = { enabled: true }; featureFlags = { videoEnabled: true, audioEnabled: true, showMicrophone: true, showCamera: true, showScreenShare: true, showPrejoin: true, showChat: true, showRecording: true, showBackgrounds: true }; constructor( protected httpService: HttpService, protected router: Router, protected ctxService: ContextService, protected roomService: RoomService, protected wcManagerService: WebComponentManagerService, protected sessionStorageService: SessionStorageService, protected cdr: ChangeDetectorRef ) {} async ngOnInit() { try { this.roomId = this.ctxService.getRoomId(); this.participantName = this.ctxService.getParticipantName(); if (this.ctxService.isEmbeddedMode()) { this.featureFlags.showPrejoin = false; } // TODO: Apply room preferences from saved room using context service // await this.loadRoomPreferences(); // TODO: Extract permissions from token and apply them to the component this.applyParticipantPermissions(); } catch (error: any) { console.error('Error fetching room preferences', error); this.serverError = error.error.message || error.message || error.error; } this.loading = false; } ngOnDestroy(): void { // Clean up the context service // this.contextService.clearContext(); this.wcManagerService.stopCommandsListener(); } async onTokenRequested(participantName: string) { try { if (this.ctxService.isStandaloneMode()) { // As token is not provided, we need to set the participant name from // ov-videoconference event this.ctxService.setParticipantName(participantName); } this.token = this.ctxService.getToken(); } catch (error: any) { console.error(error); this.serverError = error.error; } this.loading = false; this.cdr.detectChanges(); } onParticipantConnected(event: ParticipantModel) { const message: OutboundEventMessage = { event: WebComponentEvent.JOIN, payload: { roomId: event.getProperties().room?.name || '', participantName: event.name! } }; this.wcManagerService.sendMessageToParent(message); } onParticipantLeft(event: ParticipantLeftEvent) { console.warn('Participant left the room. Redirecting to:'); const redirectURL = this.ctxService.getLeaveRedirectURL() || '/disconnected'; const isExternalURL = /^https?:\/\//.test(redirectURL); const isRoomDeleted = event.reason === ParticipantLeftReason.ROOM_DELETED; let message: OutboundEventMessage; if (isRoomDeleted) { message = { event: WebComponentEvent.MEETING_ENDED, payload: { roomId: event.roomName } } as OutboundEventMessage; } else { message = { event: WebComponentEvent.LEFT, payload: { roomId: event.roomName, participantName: event.participantName, reason: event.reason } } as OutboundEventMessage; } this.wcManagerService.sendMessageToParent(message); this.sessionStorageService.removeModeratorSecret(event.roomName); //if (this.contextService.isEmbeddedMode()) this.sendMessageToParent(event); this.redirectTo(redirectURL, isExternalURL); // Stop listening to commands from the parent this.wcManagerService.stopCommandsListener(); } async onRecordingStartRequested(event: RecordingStartRequestedEvent) { try { const { roomName: roomId } = event; await this.httpService.startRecording(roomId); } catch (error) { console.error(error); } } async onRecordingStopRequested(event: RecordingStopRequestedEvent) { try { const { recordingId } = event; if (!recordingId) throw new Error('Recording ID not found when stopping recording'); await this.httpService.stopRecording(recordingId); } catch (error) { console.error(error); } } async onRecordingDeleteRequested(event: RecordingDeleteRequestedEvent) { try { const { recordingId } = event; if (!recordingId) throw new Error('Recording ID not found when deleting recording'); await this.httpService.deleteRecording(recordingId); } catch (error) { console.error(error); } } /** * Loads the room preferences from the global preferences service and assigns them to the component. * * This method fetches the room preferences asynchronously and updates the component's properties * based on the fetched preferences. It also updates the UI flags to show or hide certain features * like chat, recording, and activity panel based on the preferences. * * @returns {Promise} A promise that resolves when the room preferences have been loaded and applied. */ private async loadRoomPreferences() { const preferences = await this.roomService.getRoomPreferences(); // Assign the preferences to the component Object.assign(this, preferences); this.featureFlags.showChat = this.chatPreferences.enabled; this.featureFlags.showRecording = this.recordingPreferences.enabled; this.featureFlags.showBackgrounds = this.virtualBackgroundPreferences.enabled; } /** * Configures the feature flags based on the token permissions. * * This method checks the token permissions and sets the feature flags accordingly. */ private applyParticipantPermissions() { if (this.featureFlags.showChat) { this.featureFlags.showChat = this.ctxService.canChat(); } if (this.featureFlags.showRecording) { this.featureFlags.showRecording = this.ctxService.canRecord(); } } private redirectTo(url: string, isExternal: boolean) { if (isExternal) { console.log('Redirecting to external URL:', url); window.location.href = url; } else { console.log('Redirecting to internal route:', url); this.router.navigate([url], { replaceUrl: true }); } } }