frontend: enhance navigation and error handling
This commit is contained in:
parent
4053cfd572
commit
d2371120d8
@ -1,10 +1,11 @@
|
|||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { ErrorReason } from '@lib/models/navigation.model';
|
||||||
import { AuthMode, ParticipantRole } from '@lib/typings/ce';
|
import { AuthMode, ParticipantRole } from '@lib/typings/ce';
|
||||||
import { AuthService, ContextService, HttpService, SessionStorageService } from '../services';
|
import { AuthService, ContextService, HttpService, NavigationService, SessionStorageService } from '../services';
|
||||||
|
|
||||||
export const checkUserAuthenticatedGuard: CanActivateFn = async (
|
export const checkUserAuthenticatedGuard: CanActivateFn = async (
|
||||||
route: ActivatedRouteSnapshot,
|
_route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot
|
state: RouterStateSnapshot
|
||||||
) => {
|
) => {
|
||||||
const authService = inject(AuthService);
|
const authService = inject(AuthService);
|
||||||
@ -23,11 +24,29 @@ export const checkUserAuthenticatedGuard: CanActivateFn = async (
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const checkUserNotAuthenticatedGuard: CanActivateFn = async (
|
||||||
|
_route: ActivatedRouteSnapshot,
|
||||||
|
_state: RouterStateSnapshot
|
||||||
|
) => {
|
||||||
|
const authService = inject(AuthService);
|
||||||
|
const router = inject(Router);
|
||||||
|
|
||||||
|
// Check if user is not authenticated
|
||||||
|
const isAuthenticated = await authService.isUserAuthenticated();
|
||||||
|
if (isAuthenticated) {
|
||||||
|
// Redirect to the console page
|
||||||
|
return router.createUrlTree(['console']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow access to the requested page
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
export const checkParticipantRoleAndAuthGuard: CanActivateFn = async (
|
export const checkParticipantRoleAndAuthGuard: CanActivateFn = async (
|
||||||
_route: ActivatedRouteSnapshot,
|
_route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot
|
state: RouterStateSnapshot
|
||||||
) => {
|
) => {
|
||||||
const router = inject(Router);
|
const navigationService = inject(NavigationService);
|
||||||
const authService = inject(AuthService);
|
const authService = inject(AuthService);
|
||||||
const contextService = inject(ContextService);
|
const contextService = inject(ContextService);
|
||||||
const sessionStorageService = inject(SessionStorageService);
|
const sessionStorageService = inject(SessionStorageService);
|
||||||
@ -49,12 +68,12 @@ export const checkParticipantRoleAndAuthGuard: CanActivateFn = async (
|
|||||||
switch (error.status) {
|
switch (error.status) {
|
||||||
case 400:
|
case 400:
|
||||||
// Invalid secret
|
// Invalid secret
|
||||||
return redirectToErrorPage(router, 'invalid-secret');
|
return navigationService.createRedirectionToErrorPage(ErrorReason.INVALID_ROOM_SECRET);
|
||||||
case 404:
|
case 404:
|
||||||
// Room not found
|
// Room not found
|
||||||
return redirectToErrorPage(router, 'invalid-room');
|
return navigationService.createRedirectionToErrorPage(ErrorReason.INVALID_ROOM);
|
||||||
default:
|
default:
|
||||||
return redirectToErrorPage(router, 'internal-error');
|
return navigationService.createRedirectionToErrorPage(ErrorReason.INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,34 +91,10 @@ export const checkParticipantRoleAndAuthGuard: CanActivateFn = async (
|
|||||||
const isAuthenticated = await authService.isUserAuthenticated();
|
const isAuthenticated = await authService.isUserAuthenticated();
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
// Redirect to the login page with query param to redirect back to the room
|
// Redirect to the login page with query param to redirect back to the room
|
||||||
return router.createUrlTree(['login'], {
|
return navigationService.createRedirectionToLoginPage(state.url);
|
||||||
queryParams: { redirectTo: state.url }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow access to the room
|
// Allow access to the room
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkUserNotAuthenticatedGuard: CanActivateFn = async (
|
|
||||||
route: ActivatedRouteSnapshot,
|
|
||||||
_state: RouterStateSnapshot
|
|
||||||
) => {
|
|
||||||
const authService = inject(AuthService);
|
|
||||||
const router = inject(Router);
|
|
||||||
|
|
||||||
// Check if user is not authenticated
|
|
||||||
const isAuthenticated = await authService.isUserAuthenticated();
|
|
||||||
if (isAuthenticated) {
|
|
||||||
// Redirect to the console page
|
|
||||||
return router.createUrlTree(['console']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow access to the requested page
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const redirectToErrorPage = (router: Router, reason: string): UrlTree => {
|
|
||||||
return router.createUrlTree(['error'], { queryParams: { reason } });
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivateFn } from '@angular/router';
|
||||||
import { ContextService } from '../services';
|
import { ContextService, NavigationService } from '../services';
|
||||||
|
import { ErrorReason } from '@lib/models/navigation.model';
|
||||||
|
|
||||||
export const extractRoomQueryParamsGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => {
|
export const extractRoomQueryParamsGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => {
|
||||||
const router = inject(Router);
|
const navigationService = inject(NavigationService);
|
||||||
const contextService = inject(ContextService);
|
const contextService = inject(ContextService);
|
||||||
const { roomId, participantName, secret, leaveRedirectUrl, viewRecordings } = extractParams(route);
|
const { roomId, participantName, secret, leaveRedirectUrl, viewRecordings } = extractParams(route);
|
||||||
|
|
||||||
@ -11,24 +12,33 @@ export const extractRoomQueryParamsGuard: CanActivateFn = (route: ActivatedRoute
|
|||||||
contextService.setLeaveRedirectUrl(leaveRedirectUrl);
|
contextService.setLeaveRedirectUrl(leaveRedirectUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!secret) {
|
||||||
|
// If no secret is provided, redirect to the error page
|
||||||
|
return navigationService.createRedirectionToErrorPage(ErrorReason.MISSING_ROOM_SECRET);
|
||||||
|
}
|
||||||
|
|
||||||
contextService.setRoomId(roomId);
|
contextService.setRoomId(roomId);
|
||||||
contextService.setParticipantName(participantName);
|
contextService.setParticipantName(participantName);
|
||||||
contextService.setSecret(secret);
|
contextService.setSecret(secret);
|
||||||
|
|
||||||
if (viewRecordings === 'true') {
|
if (viewRecordings === 'true') {
|
||||||
// Redirect to the room recordings page
|
// Redirect to the room recordings page
|
||||||
return router.createUrlTree([`room/${roomId}/recordings`], {
|
return navigationService.createRedirectionToRecordingsPage(roomId, secret);
|
||||||
queryParams: { secret }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const extractRecordingQueryParamsGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => {
|
export const extractRecordingQueryParamsGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => {
|
||||||
|
const navigationService = inject(NavigationService);
|
||||||
const contextService = inject(ContextService);
|
const contextService = inject(ContextService);
|
||||||
const { roomId, secret } = extractParams(route);
|
const { roomId, secret } = extractParams(route);
|
||||||
|
|
||||||
|
if (!secret) {
|
||||||
|
// If no secret is provided, redirect to the error page
|
||||||
|
return navigationService.createRedirectionToErrorPage(ErrorReason.MISSING_ROOM_SECRET);
|
||||||
|
}
|
||||||
|
|
||||||
contextService.setRoomId(roomId);
|
contextService.setRoomId(roomId);
|
||||||
contextService.setSecret(secret);
|
contextService.setSecret(secret);
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { Location } from '@angular/common';
|
import { CanActivateFn, NavigationEnd, Router } from '@angular/router';
|
||||||
import { CanActivateFn, NavigationEnd } from '@angular/router';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { ContextService, HttpService, SessionStorageService } from '../services';
|
|
||||||
import { filter, take } from 'rxjs';
|
import { filter, take } from 'rxjs';
|
||||||
|
import { ContextService, NavigationService, SessionStorageService } from '../services';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guard that intercepts navigation to remove the 'secret' query parameter from the URL
|
* Guard that intercepts navigation to remove the 'secret' query parameter from the URL
|
||||||
@ -12,10 +10,9 @@ import { filter, take } from 'rxjs';
|
|||||||
* enhance security.
|
* enhance security.
|
||||||
*/
|
*/
|
||||||
export const removeModeratorSecretGuard: CanActivateFn = (route, _state) => {
|
export const removeModeratorSecretGuard: CanActivateFn = (route, _state) => {
|
||||||
const httpService = inject(HttpService);
|
|
||||||
const contextService = inject(ContextService);
|
const contextService = inject(ContextService);
|
||||||
|
const navigationService = inject(NavigationService);
|
||||||
const router = inject(Router);
|
const router = inject(Router);
|
||||||
const location: Location = inject(Location);
|
|
||||||
const sessionStorageService = inject(SessionStorageService);
|
const sessionStorageService = inject(SessionStorageService);
|
||||||
|
|
||||||
router.events
|
router.events
|
||||||
@ -28,36 +25,12 @@ export const removeModeratorSecretGuard: CanActivateFn = (route, _state) => {
|
|||||||
const roomId = contextService.getRoomId();
|
const roomId = contextService.getRoomId();
|
||||||
const storedSecret = sessionStorageService.getModeratorSecret(roomId);
|
const storedSecret = sessionStorageService.getModeratorSecret(roomId);
|
||||||
const moderatorSecret = storedSecret || contextService.getSecret();
|
const moderatorSecret = storedSecret || contextService.getSecret();
|
||||||
|
|
||||||
|
// Store the moderator secret in session storage for the current room and remove it from the URL
|
||||||
sessionStorageService.setModeratorSecret(roomId, moderatorSecret);
|
sessionStorageService.setModeratorSecret(roomId, moderatorSecret);
|
||||||
|
navigationService.removeModeratorSecretFromUrl(route.queryParams);
|
||||||
// Remove secret from URL
|
|
||||||
const queryParams = { ...route.queryParams };
|
|
||||||
delete queryParams['secret'];
|
|
||||||
const urlTree = router.createUrlTree([], { queryParams });
|
|
||||||
const newUrl = router.serializeUrl(urlTree);
|
|
||||||
|
|
||||||
location.replaceState(newUrl);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUrlSecret = async (
|
|
||||||
httpService: HttpService,
|
|
||||||
roomId: string
|
|
||||||
): Promise<{ moderatorSecret: string; publisherSecret: string }> => {
|
|
||||||
const { moderatorRoomUrl, publisherRoomUrl } = await httpService.getRoom(roomId);
|
|
||||||
|
|
||||||
const extractSecret = (urlString: string, type: string): string => {
|
|
||||||
const url = new URL(urlString);
|
|
||||||
const secret = url.searchParams.get('secret');
|
|
||||||
if (!secret) throw new Error(`${type} secret not found`);
|
|
||||||
return secret;
|
|
||||||
};
|
|
||||||
|
|
||||||
const publisherSecret = extractSecret(publisherRoomUrl, 'Publisher');
|
|
||||||
const moderatorSecret = extractSecret(moderatorRoomUrl, 'Moderator');
|
|
||||||
|
|
||||||
return { publisherSecret, moderatorSecret };
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,13 +1,7 @@
|
|||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import {
|
import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from '@angular/router';
|
||||||
ActivatedRouteSnapshot,
|
import { ErrorReason } from '@lib/models/navigation.model';
|
||||||
Router,
|
import { ContextService, HttpService, NavigationService, SessionStorageService } from '../services';
|
||||||
RouterStateSnapshot,
|
|
||||||
CanActivateFn,
|
|
||||||
UrlTree,
|
|
||||||
RedirectCommand
|
|
||||||
} from '@angular/router';
|
|
||||||
import { ContextService, HttpService, SessionStorageService } from '../services';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guard to validate the access to recordings.
|
* Guard to validate the access to recordings.
|
||||||
@ -18,7 +12,7 @@ export const validateRecordingAccessGuard: CanActivateFn = async (
|
|||||||
) => {
|
) => {
|
||||||
const httpService = inject(HttpService);
|
const httpService = inject(HttpService);
|
||||||
const contextService = inject(ContextService);
|
const contextService = inject(ContextService);
|
||||||
const router = inject(Router);
|
const navigationService = inject(NavigationService);
|
||||||
const sessionStorageService = inject(SessionStorageService);
|
const sessionStorageService = inject(SessionStorageService);
|
||||||
|
|
||||||
const roomId = contextService.getRoomId();
|
const roomId = contextService.getRoomId();
|
||||||
@ -32,7 +26,7 @@ export const validateRecordingAccessGuard: CanActivateFn = async (
|
|||||||
|
|
||||||
if (!contextService.canRetrieveRecordings()) {
|
if (!contextService.canRetrieveRecordings()) {
|
||||||
// If the user does not have permission to retrieve recordings, redirect to the error page
|
// If the user does not have permission to retrieve recordings, redirect to the error page
|
||||||
return redirectToErrorPage(router, 'unauthorized-recording-access');
|
return navigationService.createRedirectionToErrorPage(ErrorReason.UNAUTHORIZED_RECORDING_ACCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -41,19 +35,15 @@ export const validateRecordingAccessGuard: CanActivateFn = async (
|
|||||||
switch (error.status) {
|
switch (error.status) {
|
||||||
case 400:
|
case 400:
|
||||||
// Invalid secret
|
// Invalid secret
|
||||||
return redirectToErrorPage(router, 'invalid-secret');
|
return navigationService.createRedirectionToErrorPage(ErrorReason.INVALID_RECORDING_SECRET);
|
||||||
case 403:
|
case 403:
|
||||||
// Recording access is configured for admins only
|
// Recording access is configured for admins only
|
||||||
return redirectToErrorPage(router, 'recordings-admin-only-access');
|
return navigationService.createRedirectionToErrorPage(ErrorReason.RECORDINGS_ADMIN_ONLY_ACCESS);
|
||||||
case 404:
|
case 404:
|
||||||
// There are no recordings in the room or the room does not exist
|
// There are no recordings in the room or the room does not exist
|
||||||
return redirectToErrorPage(router, 'no-recordings');
|
return navigationService.createRedirectionToErrorPage(ErrorReason.NO_RECORDINGS);
|
||||||
default:
|
default:
|
||||||
return redirectToErrorPage(router, 'internal-error');
|
return navigationService.createRedirectionToErrorPage(ErrorReason.INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const redirectToErrorPage = (router: Router, reason: string): UrlTree => {
|
|
||||||
return router.createUrlTree(['error'], { queryParams: { reason } });
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
export interface ErrorRedirectReason {
|
export const enum ErrorReason {
|
||||||
'invalid-secret': string;
|
MISSING_ROOM_SECRET = 'missing-room-secret',
|
||||||
'invalid-room': string;
|
MISSING_RECORDING_SECRET = 'missing-recording-secret',
|
||||||
'internal-error': string;
|
INVALID_ROOM_SECRET = 'invalid-room-secret',
|
||||||
|
INVALID_RECORDING_SECRET = 'invalid-recording-secret',
|
||||||
|
INVALID_ROOM = 'invalid-room',
|
||||||
|
INVALID_RECORDING = 'invalid-recording',
|
||||||
|
NO_RECORDINGS = 'no-recordings',
|
||||||
|
UNAUTHORIZED_RECORDING_ACCESS = 'unauthorized-recording-access',
|
||||||
|
RECORDINGS_ADMIN_ONLY_ACCESS = 'recordings-admin-only-access',
|
||||||
|
INTERNAL_ERROR = 'internal-error'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { ErrorReason } from '@lib/models/navigation.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ov-error',
|
selector: 'ov-error',
|
||||||
@ -19,24 +20,40 @@ export class ErrorComponent implements OnInit {
|
|||||||
this.route.queryParams.subscribe((params) => {
|
this.route.queryParams.subscribe((params) => {
|
||||||
const reason = params['reason'];
|
const reason = params['reason'];
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case 'invalid-secret':
|
case ErrorReason.MISSING_ROOM_SECRET:
|
||||||
|
this.errorName = 'Missing secret';
|
||||||
|
this.message = 'You need to provide a secret to join the room as a moderator or publisher';
|
||||||
|
break;
|
||||||
|
case ErrorReason.MISSING_RECORDING_SECRET:
|
||||||
|
this.errorName = 'Missing secret';
|
||||||
|
this.message = 'You need to provide a secret to access the recording';
|
||||||
|
break;
|
||||||
|
case ErrorReason.INVALID_ROOM_SECRET:
|
||||||
this.errorName = 'Invalid secret';
|
this.errorName = 'Invalid secret';
|
||||||
this.message =
|
this.message =
|
||||||
'The secret provided to join the room is neither valid for moderators nor publishers';
|
'The secret provided to join the room is neither valid for moderators nor publishers';
|
||||||
break;
|
break;
|
||||||
case 'invalid-room':
|
case ErrorReason.INVALID_RECORDING_SECRET:
|
||||||
|
this.errorName = 'Invalid secret';
|
||||||
|
this.message = 'The secret provided to access the recording is invalid';
|
||||||
|
break;
|
||||||
|
case ErrorReason.INVALID_ROOM:
|
||||||
this.errorName = 'Invalid room';
|
this.errorName = 'Invalid room';
|
||||||
this.message = 'The room you are trying to join does not exist or has been deleted';
|
this.message = 'The room you are trying to join does not exist or has been deleted';
|
||||||
break;
|
break;
|
||||||
case 'no-recordings':
|
case ErrorReason.INVALID_RECORDING:
|
||||||
|
this.errorName = 'Invalid recording';
|
||||||
|
this.message = 'The recording you are trying to access does not exist or has been deleted';
|
||||||
|
break;
|
||||||
|
case ErrorReason.NO_RECORDINGS:
|
||||||
this.errorName = 'No recordings';
|
this.errorName = 'No recordings';
|
||||||
this.message = 'There are no recordings in this room or the room does not exist';
|
this.message = 'There are no recordings in this room or the room does not exist';
|
||||||
break;
|
break;
|
||||||
case 'unauthorized-recording-access':
|
case ErrorReason.UNAUTHORIZED_RECORDING_ACCESS:
|
||||||
this.errorName = 'Unauthorized recording access';
|
this.errorName = 'Unauthorized recording access';
|
||||||
this.message = 'You are not authorized to access the recordings in this room';
|
this.message = 'You are not authorized to access the recordings in this room';
|
||||||
break;
|
break;
|
||||||
case 'recordings-admin-only-access':
|
case ErrorReason.RECORDINGS_ADMIN_ONLY_ACCESS:
|
||||||
this.errorName = 'Unauthorized recording access';
|
this.errorName = 'Unauthorized recording access';
|
||||||
this.message = 'Recordings access is configured for admins only in this room';
|
this.message = 'Recordings access is configured for admins only in this room';
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -1,12 +1,19 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
|
||||||
import { AsyncPipe } from '@angular/common';
|
import { AsyncPipe } from '@angular/common';
|
||||||
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { ErrorReason } from '@lib/models/navigation.model';
|
||||||
|
import {
|
||||||
|
ApplicationFeatures,
|
||||||
|
FeatureConfigurationService
|
||||||
|
} from '@lib/services/feature-configuration/feature-configuration.service';
|
||||||
|
import { NavigationService } from '@lib/services/navigation/navigation.service';
|
||||||
|
import { ParticipantTokenService } from '@lib/services/participant-token/participant-token.service';
|
||||||
|
import { RecordingManagerService } from '@lib/services/recording-manager/recording-manager.service';
|
||||||
import { OpenViduMeetPermissions, ParticipantRole } from '@lib/typings/ce';
|
import { OpenViduMeetPermissions, ParticipantRole } from '@lib/typings/ce';
|
||||||
import {
|
import {
|
||||||
ApiDirectiveModule,
|
ApiDirectiveModule,
|
||||||
@ -17,6 +24,7 @@ import {
|
|||||||
RecordingStartRequestedEvent,
|
RecordingStartRequestedEvent,
|
||||||
RecordingStopRequestedEvent
|
RecordingStopRequestedEvent
|
||||||
} from 'openvidu-components-angular';
|
} from 'openvidu-components-angular';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
import { WebComponentEvent } from 'webcomponent/src/models/event.model';
|
import { WebComponentEvent } from 'webcomponent/src/models/event.model';
|
||||||
import { OutboundEventMessage } from 'webcomponent/src/models/message.type';
|
import { OutboundEventMessage } from 'webcomponent/src/models/message.type';
|
||||||
import {
|
import {
|
||||||
@ -26,14 +34,6 @@ import {
|
|||||||
SessionStorageService,
|
SessionStorageService,
|
||||||
WebComponentManagerService
|
WebComponentManagerService
|
||||||
} from '../../services';
|
} from '../../services';
|
||||||
import { ParticipantTokenService } from '@lib/services/participant-token/participant-token.service';
|
|
||||||
import { RecordingManagerService } from '@lib/services/recording-manager/recording-manager.service';
|
|
||||||
import { NavigationService } from '@lib/services/navigation/navigation.service';
|
|
||||||
import {
|
|
||||||
ApplicationFeatures,
|
|
||||||
FeatureConfigurationService
|
|
||||||
} from '@lib/services/feature-configuration/feature-configuration.service';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-video-room',
|
selector: 'app-video-room',
|
||||||
@ -155,7 +155,7 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
private async generateParticipantToken() {
|
private async generateParticipantToken() {
|
||||||
try {
|
try {
|
||||||
const { token, role, permissions } = await this.participantTokenService.generateToken(
|
const { role, permissions } = await this.participantTokenService.generateToken(
|
||||||
this.roomId,
|
this.roomId,
|
||||||
this.participantName,
|
this.participantName,
|
||||||
this.roomSecret
|
this.roomSecret
|
||||||
@ -169,11 +169,11 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
|
|||||||
switch (error.status) {
|
switch (error.status) {
|
||||||
case 400:
|
case 400:
|
||||||
// Invalid secret
|
// Invalid secret
|
||||||
await this.navigationService.redirectToErrorPage('invalid-secret');
|
await this.navigationService.redirectToErrorPage(ErrorReason.INVALID_ROOM_SECRET);
|
||||||
break;
|
break;
|
||||||
case 404:
|
case 404:
|
||||||
// Room not found
|
// Room not found
|
||||||
await this.navigationService.redirectToErrorPage('invalid-room');
|
await this.navigationService.redirectToErrorPage(ErrorReason.INVALID_ROOM);
|
||||||
break;
|
break;
|
||||||
case 409:
|
case 409:
|
||||||
// Participant already exists.
|
// Participant already exists.
|
||||||
@ -181,7 +181,7 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
|
|||||||
this.participantForm.get('name')?.setErrors({ participantExists: true });
|
this.participantForm.get('name')?.setErrors({ participantExists: true });
|
||||||
throw new Error('Participant already exists in the room');
|
throw new Error('Participant already exists in the room');
|
||||||
default:
|
default:
|
||||||
await this.navigationService.redirectToErrorPage('internal-error');
|
await this.navigationService.redirectToErrorPage(ErrorReason.INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,14 +202,14 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Replace secret and participant name in the URL query parameters
|
// Replace secret and participant name in the URL query parameters
|
||||||
this.navigationService.updateUrlQueryParams(this.route, {
|
this.navigationService.updateUrlQueryParams(this.route.snapshot.queryParams, {
|
||||||
secret: secretQueryParam,
|
secret: secretQueryParam,
|
||||||
'participant-name': this.participantName
|
'participant-name': this.participantName
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async goToRecordings() {
|
async goToRecordings() {
|
||||||
await this.navigationService.goToRecordings(this.roomId, this.roomSecret);
|
await this.navigationService.redirectToRecordingsPage(this.roomId, this.roomSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
onParticipantConnected(event: ParticipantModel) {
|
onParticipantConnected(event: ParticipantModel) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Params, Router, UrlTree } from '@angular/router';
|
||||||
import { ErrorRedirectReason } from '@lib/models/navigation.model';
|
import { ErrorReason } from '@lib/models/navigation.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -32,7 +32,7 @@ export class NavigationService {
|
|||||||
/**
|
/**
|
||||||
* Redirects to error page with specific reason
|
* Redirects to error page with specific reason
|
||||||
*/
|
*/
|
||||||
async redirectToErrorPage(reason: keyof ErrorRedirectReason): Promise<void> {
|
async redirectToErrorPage(reason: ErrorReason): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await this.router.navigate(['error'], { queryParams: { reason } });
|
await this.router.navigate(['error'], { queryParams: { reason } });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -40,12 +40,48 @@ export class NavigationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a URL tree for redirecting to error page
|
||||||
|
*/
|
||||||
|
createRedirectionToErrorPage(reason: ErrorReason): UrlTree {
|
||||||
|
return this.router.createUrlTree(['error'], { queryParams: { reason } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a URL tree for redirecting to login page with `redirectTo` query parameter
|
||||||
|
*/
|
||||||
|
createRedirectionToLoginPage(redirectTo: string): UrlTree {
|
||||||
|
return this.router.createUrlTree(['login'], { queryParams: { redirectTo } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates to recordings page
|
||||||
|
*/
|
||||||
|
async redirectToRecordingsPage(roomId: string, secret: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.router.navigate([`room/${roomId}/recordings`], {
|
||||||
|
queryParams: { secret }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error navigating to recordings:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a URL tree for redirecting to recordings page
|
||||||
|
*/
|
||||||
|
createRedirectionToRecordingsPage(roomId: string, secret: string): UrlTree {
|
||||||
|
return this.router.createUrlTree([`room/${roomId}/recordings`], {
|
||||||
|
queryParams: { secret }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates URL query parameters without navigation
|
* Updates URL query parameters without navigation
|
||||||
*/
|
*/
|
||||||
updateUrlQueryParams(route: ActivatedRoute, newParams: Record<string, any>): void {
|
updateUrlQueryParams(oldParams: Params, newParams: Record<string, any>): void {
|
||||||
const queryParams = {
|
const queryParams = {
|
||||||
...route.snapshot.queryParams,
|
...oldParams,
|
||||||
...newParams
|
...newParams
|
||||||
};
|
};
|
||||||
const urlTree = this.router.createUrlTree([], {
|
const urlTree = this.router.createUrlTree([], {
|
||||||
@ -57,15 +93,12 @@ export class NavigationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates to recordings page
|
* Removes the 'secret' query parameter from the URL
|
||||||
*/
|
*/
|
||||||
async goToRecordings(roomId: string, secret: string): Promise<void> {
|
removeModeratorSecretFromUrl(queryParams: Params): void {
|
||||||
try {
|
delete queryParams['secret'];
|
||||||
await this.router.navigate([`room/${roomId}/recordings`], {
|
const urlTree = this.router.createUrlTree([], { queryParams });
|
||||||
queryParams: { secret }
|
const newUrl = this.router.serializeUrl(urlTree);
|
||||||
});
|
this.location.replaceState(newUrl);
|
||||||
} catch (error) {
|
|
||||||
console.error('Error navigating to recordings:', error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user