diff --git a/frontend/projects/shared-meet-components/src/lib/guards/auth.guard.ts b/frontend/projects/shared-meet-components/src/lib/guards/auth.guard.ts index 38571b4..b111022 100644 --- a/frontend/projects/shared-meet-components/src/lib/guards/auth.guard.ts +++ b/frontend/projects/shared-meet-components/src/lib/guards/auth.guard.ts @@ -1,12 +1,5 @@ import { inject } from '@angular/core'; -import { - ActivatedRouteSnapshot, - CanActivateFn, - RedirectCommand, - Router, - RouterStateSnapshot, - UrlTree -} from '@angular/router'; +import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { AuthMode, ParticipantRole } from '@lib/typings/ce'; import { AuthService, ContextService, HttpService, SessionStorageService } from '../services'; @@ -79,12 +72,9 @@ export const checkParticipantRoleAndAuthGuard: CanActivateFn = async ( const isAuthenticated = await authService.isUserAuthenticated(); if (!isAuthenticated) { // Redirect to the login page with query param to redirect back to the room - const loginRoute = router.createUrlTree(['login'], { + return router.createUrlTree(['login'], { queryParams: { redirectTo: state.url } }); - return new RedirectCommand(loginRoute, { - skipLocationChange: true - }); } } diff --git a/frontend/projects/shared-meet-components/src/lib/guards/index.ts b/frontend/projects/shared-meet-components/src/lib/guards/index.ts index c495167..7e93399 100644 --- a/frontend/projects/shared-meet-components/src/lib/guards/index.ts +++ b/frontend/projects/shared-meet-components/src/lib/guards/index.ts @@ -2,7 +2,5 @@ export * from './application-mode.guard'; export * from './auth.guard'; export * from './extract-query-params.guard'; export * from './moderator-secret.guard'; -export * from './participant-name.guard'; export * from './run-serially.guard'; -export * from './validate-room-access.guard'; export * from './validate-recording-access.guard'; diff --git a/frontend/projects/shared-meet-components/src/lib/guards/moderator-secret.guard.ts b/frontend/projects/shared-meet-components/src/lib/guards/moderator-secret.guard.ts index 0301f51..67c3092 100644 --- a/frontend/projects/shared-meet-components/src/lib/guards/moderator-secret.guard.ts +++ b/frontend/projects/shared-meet-components/src/lib/guards/moderator-secret.guard.ts @@ -5,54 +5,6 @@ import { Router } from '@angular/router'; import { ContextService, HttpService, SessionStorageService } from '../services'; import { filter, take } from 'rxjs'; -/** - * Guard that replaces the moderator secret in the URL with the publisher secret. - * - * This guard checks if the current participant is a moderator. If so, it retrieves the moderator and publisher secrets - * for the current room and updates the session storage with the moderator secret. It then replaces the secret in the URL - * with the publisher secret. - * - * @param route - The activated route snapshot. - * @param state - The router state snapshot. - * @returns A promise that resolves to `true` if the operation is successful, otherwise `false`. - * - * @throws Will log an error and return `false` if an error occurs during the process. - */ -export const replaceModeratorSecretGuard: CanActivateFn = (route, _state) => { - const httpService = inject(HttpService); - const contextService = inject(ContextService); - const router = inject(Router); - const location: Location = inject(Location); - const sessionStorageService = inject(SessionStorageService); - - try { - router.events - .pipe( - filter((event) => event instanceof NavigationEnd), - take(1) - ) - .subscribe(async () => { - if (contextService.isModeratorParticipant()) { - const roomId = contextService.getRoomId(); - const { moderatorSecret, publisherSecret } = await getUrlSecret(httpService, roomId); - - sessionStorageService.setModeratorSecret(roomId, moderatorSecret); - // Replace secret in URL by the publisher secret - const queryParams = { ...route.queryParams, secret: publisherSecret }; - const urlTree = router.createUrlTree([], { queryParams, queryParamsHandling: 'merge' }); - const newUrl = router.serializeUrl(urlTree); - - location.replaceState(newUrl); - } - }); - - return true; - } catch (error) { - console.error('error', error); - return false; - } -}; - /** * Guard that intercepts navigation to remove the 'secret' query parameter from the URL * when a moderator participant is detected. The secret is stored in session storage diff --git a/frontend/projects/shared-meet-components/src/lib/guards/participant-name.guard.ts b/frontend/projects/shared-meet-components/src/lib/guards/participant-name.guard.ts deleted file mode 100644 index 1ece264..0000000 --- a/frontend/projects/shared-meet-components/src/lib/guards/participant-name.guard.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { inject } from '@angular/core'; -import { CanActivateFn, RedirectCommand } from '@angular/router'; -import { Router } from '@angular/router'; -import { ContextService } from '../services'; - -export const checkParticipantNameGuard: CanActivateFn = (_route, state) => { - const router = inject(Router); - const contextService = inject(ContextService); - const roomId = contextService.getRoomId(); - const hasParticipantName = !!contextService.getParticipantName(); - - // Check if participant name exists in the service - if (!hasParticipantName) { - // Redirect to a page where the participant can input their participant name - const participantNameRoute = router.createUrlTree([`room/${roomId}/participant-name`], { - queryParams: { originUrl: state.url, t: Date.now() } - }); - return new RedirectCommand(participantNameRoute, { - skipLocationChange: true - }); - } - - // Proceed if the name exists - return true; -}; diff --git a/frontend/projects/shared-meet-components/src/lib/guards/validate-room-access.guard.ts b/frontend/projects/shared-meet-components/src/lib/guards/validate-room-access.guard.ts deleted file mode 100644 index 7a62eaf..0000000 --- a/frontend/projects/shared-meet-components/src/lib/guards/validate-room-access.guard.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { inject } from '@angular/core'; -import { - ActivatedRouteSnapshot, - Router, - RouterStateSnapshot, - CanActivateFn, - UrlTree, - RedirectCommand -} from '@angular/router'; -import { ContextService, HttpService, SessionStorageService } from '../services'; - -/** - * Guard to validate the access to a room. - */ -export const validateRoomAccessGuard: CanActivateFn = async ( - _route: ActivatedRouteSnapshot, - state: RouterStateSnapshot -) => { - const httpService = inject(HttpService); - const contextService = inject(ContextService); - const router = inject(Router); - const sessionStorageService = inject(SessionStorageService); - - const roomId = contextService.getRoomId(); - const participantName = contextService.getParticipantName(); - const secret = contextService.getSecret(); - const storageSecret = sessionStorageService.getModeratorSecret(roomId); - - try { - // Generate a participant token - const response = await httpService.generateParticipantToken({ - roomId, - participantName, - secret: storageSecret || secret - }); - contextService.setParticipantToken(response.token); - return true; - } catch (error: any) { - console.error('Error generating participant token:', error); - switch (error.status) { - case 400: - // Invalid secret - return redirectToErrorPage(router, 'invalid-secret'); - case 404: - // Room not found - return redirectToErrorPage(router, 'invalid-room'); - case 409: - // Participant already exists. - // Send a timestamp to force update the query params and show the error message in participant name input form - const participantNameRoute = router.createUrlTree([`room/${roomId}/participant-name`], { - queryParams: { originUrl: state.url, accessError: 'participant-exists', t: Date.now() } - }); - return new RedirectCommand(participantNameRoute, { - skipLocationChange: true - }); - default: - return redirectToErrorPage(router, 'internal-error'); - } - } -}; - -const redirectToErrorPage = (router: Router, reason: string): UrlTree => { - return router.createUrlTree(['error'], { queryParams: { reason } }); -}; diff --git a/frontend/projects/shared-meet-components/src/lib/pages/index.ts b/frontend/projects/shared-meet-components/src/lib/pages/index.ts index c88b758..9d311c0 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/index.ts +++ b/frontend/projects/shared-meet-components/src/lib/pages/index.ts @@ -10,6 +10,5 @@ export * from './console/security-preferences/security-preferences.component'; export * from './disconnected/disconnected.component'; export * from './error/error.component'; export * from './login/login.component'; -export * from './participant-name-form/participant-name-form.component'; export * from './room-recordings/room-recordings.component'; export * from './video-room/video-room.component'; diff --git a/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.html b/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.html deleted file mode 100644 index cf977f0..0000000 --- a/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.html +++ /dev/null @@ -1,24 +0,0 @@ -
- -

What is your name?

-
- - Name - - @if (name?.hasError('minlength')) { - The name must be at least 4 characters - } - @if (name?.hasError('required')) { - The name is required - } - @if (name?.hasError('participantExists')) { - The name is already taken. Please choose another name - } - - - -
-
-
diff --git a/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.scss b/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.scss deleted file mode 100644 index 5ce78a8..0000000 --- a/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.scss +++ /dev/null @@ -1,36 +0,0 @@ -.form-container { - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - background-color: var(--ov-background-color); -} - -.form-card { - width: 400px; - padding: 20px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); - border-radius: var(--ov-surface-radius); - background-color: var(--ov-surface-color); -} - -.form-title { - text-align: center; - margin-bottom: 20px; - color: var(--ov-text-surface-color); -} - -.full-width { - width: 100%; - margin-bottom: 20px; -} - -button { - height: 56px; - border-radius: var(--ov-surface-radius); -} - -button:not([disabled]) { - background-color: var(--ov-accent-action-color); - color: var(--ov-text-primary-color); -} diff --git a/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.spec.ts b/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.spec.ts deleted file mode 100644 index 74c69a5..0000000 --- a/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ParticipantNameFormComponent } from './participant-name-form.component'; - -describe('ParticipantNameFormComponent', () => { - let component: ParticipantNameFormComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ParticipantNameFormComponent] - }) - .compileComponents(); - - fixture = TestBed.createComponent(ParticipantNameFormComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.ts b/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.ts deleted file mode 100644 index a7f94f4..0000000 --- a/frontend/projects/shared-meet-components/src/lib/pages/participant-name-form/participant-name-form.component.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { FormGroup, Validators, FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { ActivatedRoute, Router } from '@angular/router'; -import { AuthService } from 'shared-meet-components'; - -@Component({ - selector: 'ov-participant-name-form', - standalone: true, - imports: [ - MatFormFieldModule, - MatInputModule, - FormsModule, - ReactiveFormsModule, - MatCardModule, - MatButtonModule - // MatIconModule - ], - templateUrl: './participant-name-form.component.html', - styleUrl: './participant-name-form.component.scss' -}) -export class ParticipantNameFormComponent implements OnInit { - participantForm = new FormGroup({ - name: new FormControl('', [Validators.required, Validators.minLength(4)]) - }); - protected originUrl: string = ''; - protected error = ''; - - constructor( - protected router: Router, - protected route: ActivatedRoute, - protected authService: AuthService - ) {} - - async ngOnInit() { - this.route.queryParams.subscribe((params) => { - if (params['originUrl']) { - this.originUrl = params['originUrl']; - this.error = params['accessError']; - this.applyErrorToForm(); - } - }); - - // Set the username if authenticated as default value - const username = await this.authService.getUsername(); - if (username) { - this.participantForm.get('name')?.setValue(username); - } - } - - get name() { - return this.participantForm.get('name'); - } - - async onSubmit() { - if (this.participantForm.valid) { - const participantName = this.participantForm.value.name; - - let urlTree = this.router.parseUrl(this.originUrl); - urlTree.queryParams = { ...urlTree.queryParams, 'participant-name': participantName }; - - await this.router.navigateByUrl(urlTree); - } - } - - private applyErrorToForm() { - if (this.error === 'participant-exists') { - this.participantForm.get('name')?.setErrors({ participantExists: true }); - } - } -} diff --git a/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.html b/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.html index 714c97d..46510ed 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.html +++ b/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.html @@ -1,7 +1,45 @@ -@if (!serverError && !loading) { +@if (!showRoom) { +
+ +

What is your name?

+
+ + Name + + @if (participantForm.get('name')?.hasError('minlength')) { + The name must be at least 4 characters + } + @if (participantForm.get('name')?.hasError('required')) { + The name is required + } + @if (participantForm.get('name')?.hasError('participantExists')) { + + The name is already taken. Please choose another name + + } + + + +
+
+
+} @else { } - -@if (!loading && serverError) { -
- error - - {{ serverError }} - -
-} diff --git a/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.scss b/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.scss index e94ef4a..5ce78a8 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.scss +++ b/frontend/projects/shared-meet-components/src/lib/pages/video-room/video-room.component.scss @@ -1,9 +1,36 @@ -.error { - height: 100%; - width: 100%; - text-align: center; - margin-top: 20px; - mat-icon { - vertical-align: bottom; - } +.form-container { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: var(--ov-background-color); +} + +.form-card { + width: 400px; + padding: 20px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); + border-radius: var(--ov-surface-radius); + background-color: var(--ov-surface-color); +} + +.form-title { + text-align: center; + margin-bottom: 20px; + color: var(--ov-text-surface-color); +} + +.full-width { + width: 100%; + margin-bottom: 20px; +} + +button { + height: 56px; + border-radius: var(--ov-surface-radius); +} + +button:not([disabled]) { + background-color: var(--ov-accent-action-color); + color: var(--ov-text-primary-color); } 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 916ab63..2f24f26 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 @@ -1,53 +1,74 @@ -import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { MatIcon } from '@angular/material/icon'; +import { Location } from '@angular/common'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { ActivatedRoute, Router } from '@angular/router'; +import { MeetRecordingAccess, MeetRoomPreferences, OpenViduMeetPermissions, ParticipantRole } from '@lib/typings/ce'; import { - RecordingDeleteRequestedEvent, - RecordingStartRequestedEvent, - RecordingStopRequestedEvent, ApiDirectiveModule, - ParticipantLeftEvent, - ParticipantModel, OpenViduComponentsUiModule, - ParticipantLeftReason + ParticipantLeftEvent, + ParticipantLeftReason, + ParticipantModel, + RecordingStartRequestedEvent, + RecordingStopRequestedEvent } 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'; +import { OutboundEventMessage } from 'webcomponent/src/models/message.type'; +import { + AuthService, + ContextService, + HttpService, + RoomService, + SessionStorageService, + WebComponentManagerService +} from '../../services'; @Component({ selector: 'app-video-room', templateUrl: './video-room.component.html', styleUrls: ['./video-room.component.scss'], standalone: true, - imports: [OpenViduComponentsUiModule, ApiDirectiveModule, MatIcon] + imports: [ + OpenViduComponentsUiModule, + ApiDirectiveModule, + MatFormFieldModule, + MatInputModule, + FormsModule, + ReactiveFormsModule, + MatCardModule, + MatButtonModule + ] }) export class VideoRoomComponent implements OnInit, OnDestroy { + participantForm = new FormGroup({ + name: new FormControl('', [Validators.required, Validators.minLength(4)]) + }); + showRoom = false; + roomId = ''; + roomSecret = ''; participantName = ''; - token = ''; - serverError = ''; - loading = true; - chatPreferences: MeetChatPreferences = { enabled: true }; - recordingPreferences: MeetRecordingPreferences = { - enabled: true, - allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER + participantToken = ''; + participantRole: ParticipantRole = ParticipantRole.PUBLISHER; + participantPermissions: OpenViduMeetPermissions = { + canRecord: false, + canChat: false, + canChangeVirtualBackground: false, + canPublishScreen: false + }; + + roomPreferences: MeetRoomPreferences = { + recordingPreferences: { + enabled: true, + allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER + }, + chatPreferences: { enabled: true }, + virtualBackgroundPreferences: { enabled: true } }; - virtualBackgroundPreferences: MeetVirtualBackgroundPreferences = { enabled: true }; featureFlags = { videoEnabled: true, audioEnabled: true, @@ -63,56 +84,132 @@ export class VideoRoomComponent implements OnInit, OnDestroy { constructor( protected httpService: HttpService, protected router: Router, + protected route: ActivatedRoute, + protected location: Location, + protected authService: AuthService, protected ctxService: ContextService, protected roomService: RoomService, protected wcManagerService: WebComponentManagerService, - protected sessionStorageService: SessionStorageService, - protected cdr: ChangeDetectorRef + protected sessionStorageService: SessionStorageService ) {} async ngOnInit() { - try { - this.roomId = this.ctxService.getRoomId(); - this.participantName = this.ctxService.getParticipantName(); + this.roomId = this.ctxService.getRoomId(); + const secret = this.ctxService.getSecret(); + const storageSecret = this.sessionStorageService.getModeratorSecret(this.roomId); + this.roomSecret = storageSecret || secret; - if (this.ctxService.isEmbeddedMode()) { - this.featureFlags.showPrejoin = false; - } + // Apply participant name from context if set, otherwise use authenticated username + const contextParticipantName = this.ctxService.getParticipantName(); + const username = await this.authService.getUsername(); + const participantName = contextParticipantName || username; - // 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; + if (participantName) { + this.participantForm.get('name')?.setValue(participantName); } - 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.getParticipantToken(); - } catch (error: any) { - console.error(error); - this.serverError = error.error; + async accessRoom() { + if (!this.participantForm.valid) { + return; } - this.loading = false; - this.cdr.detectChanges(); + this.participantName = this.participantForm.value.name!; + + try { + await this.generateParticipantToken(); + await this.replaceUrlQueryParams(); + // await this.loadRoomPreferences(); + this.applyParticipantPermissions(); + this.showRoom = true; + } catch (error) { + console.error('Error accessing room:', error); + } + } + + async onTokenRequested() { + // Participant token must be set only when requested + this.participantToken = this.ctxService.getParticipantToken(); + } + + private async generateParticipantToken() { + try { + const response = await this.httpService.generateParticipantToken({ + roomId: this.roomId, + participantName: this.participantName, + secret: this.roomSecret + }); + this.setParticipantToken(response.token); + } catch (error: any) { + console.error('Error generating participant token:', error); + switch (error.status) { + case 400: + // Invalid secret + this.redirectToErrorPage('invalid-secret'); + break; + case 404: + // Room not found + this.redirectToErrorPage('invalid-room'); + break; + case 409: + // Participant already exists. + // Show the error message in participant name input form + this.participantForm.get('name')?.setErrors({ participantExists: true }); + throw new Error('Participant already exists in the room'); + default: + this.redirectToErrorPage('internal-error'); + } + } + } + + private setParticipantToken(token: string): void { + try { + this.ctxService.setParticipantToken(token); + this.participantRole = this.ctxService.getParticipantRole(); + this.participantPermissions = this.ctxService.getParticipantPermissions(); + } catch (error: any) { + console.error('Error setting token in context', error); + } + } + + private async replaceUrlQueryParams() { + let secretQueryParam = this.roomSecret; + + // If participant is moderator, store the moderator secret in session storage + // and replace the secret in the URL with the publisher secret + if (this.participantRole === ParticipantRole.MODERATOR) { + try { + const { moderatorSecret, publisherSecret } = await this.getRoomSecrets(); + this.sessionStorageService.setModeratorSecret(this.roomId, moderatorSecret); + secretQueryParam = publisherSecret; + } catch (error) { + console.error('error', error); + } + } + + // Replace secret and participant name in the URL query parameters + const queryParams = { + ...this.route.snapshot.queryParams, + secret: secretQueryParam, + 'participant-name': this.participantName + }; + const urlTree = this.router.createUrlTree([], { queryParams, queryParamsHandling: 'merge' }); + const newUrl = this.router.serializeUrl(urlTree); + this.location.replaceState(newUrl); + } + + private async getRoomSecrets(): Promise<{ moderatorSecret: string; publisherSecret: string }> { + const { moderatorRoomUrl, publisherRoomUrl } = await this.httpService.getRoom(this.roomId); + + const publisherUrl = new URL(publisherRoomUrl); + const publisherSecret = publisherUrl.searchParams.get('secret') || ''; + const moderatorUrl = new URL(moderatorRoomUrl); + const moderatorSecret = moderatorUrl.searchParams.get('secret') || ''; + return { publisherSecret, moderatorSecret }; } onParticipantConnected(event: ParticipantModel) { @@ -158,11 +255,7 @@ export class VideoRoomComponent implements OnInit, OnDestroy { 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) { @@ -186,18 +279,6 @@ export class VideoRoomComponent implements OnInit, OnDestroy { } } - 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. * @@ -208,26 +289,26 @@ export class VideoRoomComponent implements OnInit, OnDestroy { * @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); + try { + this.roomPreferences = await this.roomService.getRoomPreferences(); + } catch (error) { + console.error('Error loading room preferences:', error); + } - this.featureFlags.showChat = this.chatPreferences.enabled; - this.featureFlags.showRecording = this.recordingPreferences.enabled; - this.featureFlags.showBackgrounds = this.virtualBackgroundPreferences.enabled; + this.featureFlags.showChat = this.roomPreferences.chatPreferences.enabled; + this.featureFlags.showRecording = this.roomPreferences.recordingPreferences.enabled; + this.featureFlags.showBackgrounds = this.roomPreferences.virtualBackgroundPreferences.enabled; } /** - * Configures the feature flags based on the token permissions. - * - * This method checks the token permissions and sets the feature flags accordingly. + * Configures the feature flags based on participant permissions. */ private applyParticipantPermissions() { if (this.featureFlags.showChat) { - this.featureFlags.showChat = this.ctxService.canChat(); + this.featureFlags.showChat = this.participantPermissions.canChat; } if (this.featureFlags.showRecording) { - this.featureFlags.showRecording = this.ctxService.canRecord(); + this.featureFlags.showRecording = this.participantPermissions.canRecord; } } @@ -240,4 +321,8 @@ export class VideoRoomComponent implements OnInit, OnDestroy { this.router.navigate([url], { replaceUrl: true }); } } + + private redirectToErrorPage(reason: string) { + this.router.navigate(['error'], { queryParams: { reason } }); + } } diff --git a/frontend/projects/shared-meet-components/src/lib/routes/base-routes.ts b/frontend/projects/shared-meet-components/src/lib/routes/base-routes.ts index 6bd937c..c18e9b5 100644 --- a/frontend/projects/shared-meet-components/src/lib/routes/base-routes.ts +++ b/frontend/projects/shared-meet-components/src/lib/routes/base-routes.ts @@ -1,17 +1,14 @@ import { Routes } from '@angular/router'; import { applicationModeGuard, - checkParticipantNameGuard, checkParticipantRoleAndAuthGuard, checkUserAuthenticatedGuard, checkUserNotAuthenticatedGuard, extractRecordingQueryParamsGuard, extractRoomQueryParamsGuard, removeModeratorSecretGuard, - replaceModeratorSecretGuard, runGuardsSerially, - validateRecordingAccessGuard, - validateRoomAccessGuard + validateRecordingAccessGuard } from '../guards'; import { ConsoleComponent, @@ -19,7 +16,6 @@ import { ErrorComponent, LoginComponent, OverviewComponent, - ParticipantNameFormComponent, RecordingsComponent, RoomFormComponent, RoomRecordingsComponent, @@ -83,20 +79,9 @@ export const baseRoutes: Routes = [ path: 'room/:room-id', component: VideoRoomComponent, canActivate: [ - runGuardsSerially( - applicationModeGuard, - extractRoomQueryParamsGuard, - checkParticipantRoleAndAuthGuard, - checkParticipantNameGuard, - validateRoomAccessGuard, - replaceModeratorSecretGuard - ) + runGuardsSerially(applicationModeGuard, extractRoomQueryParamsGuard, checkParticipantRoleAndAuthGuard) ] }, - { - path: 'room/:room-id/participant-name', - component: ParticipantNameFormComponent - }, { path: 'room/:room-id/recordings', component: RoomRecordingsComponent, diff --git a/frontend/projects/shared-meet-components/src/lib/services/context/context.service.ts b/frontend/projects/shared-meet-components/src/lib/services/context/context.service.ts index 0d2b081..0ebeff4 100644 --- a/frontend/projects/shared-meet-components/src/lib/services/context/context.service.ts +++ b/frontend/projects/shared-meet-components/src/lib/services/context/context.service.ts @@ -150,12 +150,8 @@ export class ContextService { return this.context.participantRole === ParticipantRole.MODERATOR; } - canRecord(): boolean { - return this.context.participantPermissions.canRecord; - } - - canChat(): boolean { - return this.context.participantPermissions.canChat; + getParticipantPermissions() { + return this.context.participantPermissions; } setRecordingPermissionsFromToken(token: string): void {