frontend: refactor ErrorComponent to improve error handling and back navigation logic

This commit is contained in:
juancarmore 2025-07-19 00:25:32 +02:00
parent bf0ccf9c39
commit 621064b251
3 changed files with 115 additions and 57 deletions

View File

@ -1,4 +1,4 @@
export const enum ErrorReason {
export enum ErrorReason {
MISSING_ROOM_SECRET = 'missing-room-secret',
MISSING_RECORDING_SECRET = 'missing-recording-secret',
INVALID_ROOM_SECRET = 'invalid-room-secret',

View File

@ -11,11 +11,11 @@
</div>
<div class="error-actions">
@if (isAdmin) {
@if (showBackButton) {
<div class="fade-in-delayed-more">
<button mat-button (click)="goToConsole()">
<button mat-button (click)="goBack()">
<mat-icon>arrow_back</mat-icon>
<span>Back to Console</span>
<span>{{ backButtonText }}</span>
</button>
</div>
}

View File

@ -2,9 +2,9 @@ import { Component, OnInit } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
import { ErrorReason } from '@lib/models';
import { AuthService, NavigationService } from '@lib/services';
import { AppDataService, AuthService, NavigationService, WebComponentManagerService } from '@lib/services';
@Component({
selector: 'ov-error',
@ -17,67 +17,125 @@ export class ErrorComponent implements OnInit {
errorName = 'Error';
message = '';
showBackButton = true;
backButtonText = 'Back';
constructor(
private route: ActivatedRoute,
protected authService: AuthService,
protected navService: NavigationService
protected navService: NavigationService,
protected appDataService: AppDataService,
protected wcManagerService: WebComponentManagerService
) {}
ngOnInit(): void {
this.route.queryParams.subscribe((params) => {
const reason = params['reason'];
switch (reason) {
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.message =
'The secret provided to join the room is neither valid for moderators nor publishers';
break;
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.message = 'The room you are trying to join does not exist or has been deleted';
break;
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.message = 'There are no recordings in this room or the room does not exist';
break;
case ErrorReason.UNAUTHORIZED_RECORDING_ACCESS:
this.errorName = 'Unauthorized recording access';
this.message = 'You are not authorized to access the recordings in this room';
break;
case ErrorReason.RECORDINGS_ADMIN_ONLY_ACCESS:
this.errorName = 'Unauthorized recording access';
this.message = 'Recordings access is configured for admins only in this room';
break;
default:
this.errorName = 'Internal error';
this.message = 'Something went wrong...';
break;
ngOnInit() {
this.setErrorReason();
this.setBackButtonText();
}
/**
* Retrieves the error reason from URL query parameters
*/
private setErrorReason() {
const reason = this.route.snapshot.queryParams['reason'];
if (reason) {
const { title, message } = this.mapReasonToNameAndMessage(reason);
this.errorName = title;
this.message = message;
}
}
/**
* Maps technical error reasons to user-friendly names and messages
*/
private mapReasonToNameAndMessage(reason: string): { title: string; message: string } {
const reasonMap: { [key in ErrorReason]: { title: string; message: string } } = {
[ErrorReason.MISSING_ROOM_SECRET]: {
title: 'Missing secret',
message: 'You need to provide a secret to join the room as a moderator or publisher'
},
[ErrorReason.MISSING_RECORDING_SECRET]: {
title: 'Missing secret',
message: 'You need to provide a secret to access the recording'
},
[ErrorReason.INVALID_ROOM_SECRET]: {
title: 'Invalid secret',
message: 'The secret provided to join the room is neither valid for moderators nor publishers'
},
[ErrorReason.INVALID_RECORDING_SECRET]: {
title: 'Invalid secret',
message: 'The secret provided to access the recording is invalid'
},
[ErrorReason.INVALID_ROOM]: {
title: 'Invalid room',
message: 'The room you are trying to join does not exist or has been deleted'
},
[ErrorReason.INVALID_RECORDING]: {
title: 'Invalid recording',
message: 'The recording you are trying to access does not exist or has been deleted'
},
[ErrorReason.NO_RECORDINGS]: {
title: 'No recordings',
message: 'There are no recordings in this room or the room does not exist'
},
[ErrorReason.UNAUTHORIZED_RECORDING_ACCESS]: {
title: 'Unauthorized recording access',
message: 'You are not authorized to access the recordings in this room'
},
[ErrorReason.RECORDINGS_ADMIN_ONLY_ACCESS]: {
title: 'Unauthorized recording access',
message: 'Recordings access is configured for admins only in this room'
},
[ErrorReason.INTERNAL_ERROR]: {
title: 'Internal error',
message: 'An unexpected error occurred, please try again later'
}
});
};
const normalizedReason = Object.values(ErrorReason).find((enumValue) => enumValue === reason) as
| ErrorReason
| undefined;
return reasonMap[normalizedReason ?? ErrorReason.INTERNAL_ERROR];
}
get isAdmin(): boolean {
return this.authService.isAdmin();
/**
* Sets the back button text based on the application mode and user role
*/
async setBackButtonText() {
const isStandaloneMode = this.appDataService.isStandaloneMode();
const redirection = this.navService.getLeaveRedirectURL();
const isAdmin = await this.authService.isAdmin();
if (isStandaloneMode && !redirection && !isAdmin) {
// If in standalone mode, no redirection URL and not an admin, hide the back button
this.showBackButton = false;
return;
}
this.showBackButton = true;
this.backButtonText = isStandaloneMode && !redirection && isAdmin ? 'Back to Console' : 'Back';
}
async goToConsole(): Promise<void> {
/**
* Handles the back button click event and navigates accordingly
* If in embedded mode, it closes the WebComponentManagerService
* If in standalone mode, it navigates to the redirect URL or to the admin console
*/
async goBack() {
if (this.appDataService.isEmbeddedMode()) {
this.wcManagerService.close();
return;
}
// Standalone mode handling
const redirectTo = this.navService.getLeaveRedirectURL();
if (redirectTo) {
// Navigate to the specified redirect URL
await this.navService.redirectTo(redirectTo);
return;
}
// Navigate to the admin console
await this.navService.redirectTo('/overview', false);
await this.navService.navigateTo('/overview', undefined, true);
}
}