frontend: project restructuring by domain
- centralize HTTP interceptor error handling via registry and handlers
This commit is contained in:
parent
c42a3ce1cf
commit
dac64bb1a9
@ -1,25 +0,0 @@
|
||||
export * from './console-nav/console-nav.component';
|
||||
export * from './dialogs/basic-dialog/dialog.component';
|
||||
export * from './dialogs/delete-room-dialog/delete-room-dialog.component';
|
||||
export * from './dialogs/share-recording-dialog/share-recording-dialog.component';
|
||||
export * from './logo-selector/logo-selector.component';
|
||||
export * from './pro-feature-badge/pro-feature-badge.component';
|
||||
export * from './recording-lists/recording-lists.component';
|
||||
export * from './recording-video-player/recording-video-player.component';
|
||||
export * from './rooms-lists/rooms-lists.component';
|
||||
export * from './selectable-card/selectable-card.component';
|
||||
export * from './share-meeting-link/share-meeting-link.component';
|
||||
export * from './spinner/spinner.component';
|
||||
export * from './step-indicator/step-indicator.component';
|
||||
export * from './wizard-nav/wizard-nav.component';
|
||||
|
||||
// Meeting modular components
|
||||
export * from './meeting-lobby/meeting-lobby.component';
|
||||
export * from './meeting-share-link-overlay/meeting-share-link-overlay.component';
|
||||
|
||||
|
||||
// Meeting components
|
||||
export * from './hidden-participants-indicator/hidden-participants-indicator.component';
|
||||
export * from './meeting-lobby/meeting-lobby.component';
|
||||
export * from './meeting-share-link-overlay/meeting-share-link-overlay.component';
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export * from './components/index';
|
||||
@ -0,0 +1,4 @@
|
||||
export * from './interceptor-handlers';
|
||||
export * from './pages';
|
||||
export * from './services';
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
# Interceptor Handlers - Auth Domain
|
||||
|
||||
Este directorio contiene los handlers que gestionan la lógica de dominio relacionada con errores HTTP de autenticación.
|
||||
|
||||
## Propósito
|
||||
|
||||
Separar la lógica de negocio del interceptor HTTP principal, siguiendo el principio de responsabilidad única y la arquitectura de dominios.
|
||||
|
||||
## Componentes
|
||||
|
||||
### `AuthErrorHandlerService`
|
||||
|
||||
Servicio responsable de manejar errores 401 relacionados con tokens de acceso expirados.
|
||||
|
||||
**Responsabilidades:**
|
||||
- Refrescar el access token cuando expira
|
||||
- Reintrentar la petición original con el nuevo token
|
||||
- Manejar errores de refresh token (logout si es necesario)
|
||||
|
||||
**NO es responsable de:**
|
||||
- Decidir cuándo debe ejecutarse (eso lo hace el interceptor)
|
||||
- Conocer sobre tokens de room members (eso es del dominio rooms)
|
||||
- Agregar headers a las peticiones (eso lo hace el interceptor)
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
HTTP Interceptor (detecta error 401)
|
||||
↓
|
||||
Notifica → HttpErrorNotifierService
|
||||
↓
|
||||
Decide qué handler ejecutar según contexto
|
||||
↓
|
||||
AuthErrorHandlerService.createRetryStrategy()
|
||||
↓
|
||||
Ejecuta lógica de dominio (refresh token)
|
||||
↓
|
||||
Retorna Observable para reintentar la petición
|
||||
```
|
||||
|
||||
## Uso
|
||||
|
||||
El servicio se inyecta automáticamente en el interceptor HTTP y se invoca cuando:
|
||||
- Se recibe un error 401
|
||||
- No es un error de token de room member
|
||||
- No estamos en la página de login O existe un refresh token disponible
|
||||
@ -0,0 +1,108 @@
|
||||
import { HttpErrorResponse, HttpEvent } from '@angular/common/http';
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable, catchError, from, switchMap } from 'rxjs';
|
||||
import { HttpErrorContext, HttpErrorHandler, HttpErrorNotifierService } from '../../../shared/services/http-error-notifier.service';
|
||||
import { TokenStorageService } from '../../../shared/services/token-storage.service';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
|
||||
/**
|
||||
* Handler for authentication-related HTTP errors.
|
||||
* Registers itself with HttpErrorNotifierService to autonomously handle access token refresh.
|
||||
* The interceptor doesn't know about this service - it discovers itself via registration.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthInterceptorErrorHandlerService implements HttpErrorHandler {
|
||||
private readonly authService = inject(AuthService);
|
||||
private readonly tokenStorageService = inject(TokenStorageService);
|
||||
private readonly router = inject(Router);
|
||||
private readonly httpErrorNotifier = inject(HttpErrorNotifierService);
|
||||
|
||||
/**
|
||||
* Registers this handler with the error notifier service
|
||||
*/
|
||||
init(): void {
|
||||
this.httpErrorNotifier.register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this handler can handle the given error context
|
||||
*/
|
||||
canHandle(context: HttpErrorContext): boolean {
|
||||
const { error, pageUrl } = context;
|
||||
|
||||
// Only handle 401 errors
|
||||
if (error.status !== 401) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't handle if it's already a token refresh endpoint error (avoid infinite loop)
|
||||
if (error.url?.includes('/auth/refresh')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Special case: room member token generation failed, need to refresh access token first
|
||||
if (error.url?.includes('/token')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle if not on login page OR if there's a refresh token available
|
||||
return !pageUrl.startsWith('/login') || !!this.tokenStorageService.getRefreshToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the error and returns a recovery Observable
|
||||
*/
|
||||
handle(context: HttpErrorContext): Observable<HttpEvent<unknown>> {
|
||||
const { error } = context;
|
||||
|
||||
// Special case: room member token generation failed
|
||||
if (error.url?.includes('/token')) {
|
||||
console.log('Generating room member token failed. Refreshing access token first...');
|
||||
}
|
||||
|
||||
return this.refreshAccessToken(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the access token and retries the original request
|
||||
*/
|
||||
private refreshAccessToken(context: HttpErrorContext): Observable<HttpEvent<unknown>> {
|
||||
const { request: originalRequest, error: originalError, pageUrl, next } = context;
|
||||
console.log('Refreshing access token...');
|
||||
|
||||
return from(this.authService.refreshToken()).pipe(
|
||||
switchMap(() => {
|
||||
console.log('Access token refreshed');
|
||||
// Update the request with the new token
|
||||
const newToken = this.tokenStorageService.getAccessToken();
|
||||
const updatedRequest = newToken
|
||||
? originalRequest.clone({
|
||||
setHeaders: {
|
||||
authorization: `Bearer ${newToken}`
|
||||
}
|
||||
})
|
||||
: originalRequest;
|
||||
|
||||
return next(updatedRequest);
|
||||
}),
|
||||
catchError(async (error: HttpErrorResponse) => {
|
||||
if (error.url?.includes('/auth/refresh')) {
|
||||
console.error('Error refreshing access token');
|
||||
|
||||
// If the original request was not to the profile endpoint, logout and redirect to the login page
|
||||
if (!originalRequest.url.includes('/profile')) {
|
||||
console.log('Logging out...');
|
||||
await this.authService.logout(pageUrl);
|
||||
}
|
||||
|
||||
throw originalError;
|
||||
}
|
||||
|
||||
throw error;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
export * from './auth-error-handler.service';
|
||||
@ -0,0 +1 @@
|
||||
export * from './login/login.component';
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
// Login Page Container - Full height centered layout
|
||||
.ov-page-container {
|
||||
@ -8,7 +8,8 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { ActivatedRoute, RouterModule } from '@angular/router';
|
||||
import { AuthService, NavigationService } from '../../services';
|
||||
import { NavigationService } from '../../../../shared';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-login',
|
||||
@ -1,7 +1,7 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MeetUserDTO, MeetUserRole } from '@openvidu-meet/typings';
|
||||
import { HttpService, NavigationService, TokenStorageService } from '../services';
|
||||
import { HttpService, NavigationService, TokenStorageService } from '../../../shared/services';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -9,7 +9,6 @@ import { HttpService, NavigationService, TokenStorageService } from '../services
|
||||
export class AuthService {
|
||||
protected readonly AUTH_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/auth`;
|
||||
protected readonly USERS_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/users`;
|
||||
|
||||
protected hasCheckAuth = false;
|
||||
protected user: MeetUserDTO | null = null;
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export * from './auth.service';
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
#dashboard-container,
|
||||
mat-sidenav-container {
|
||||
@ -7,8 +7,7 @@ import { MatSidenav, MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { ConsoleNavLink } from '../../models';
|
||||
import { AppDataService, ThemeService } from '../../services';
|
||||
import { AppDataService, ConsoleNavLink, ThemeService } from '../../../../shared';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-console-nav',
|
||||
@ -41,7 +40,7 @@ export class ConsoleNavComponent {
|
||||
private appDataService: AppDataService,
|
||||
private themeService: ThemeService
|
||||
) {
|
||||
this.version = `v${this.appDataService.getVersion()} (${this.appDataService.getEdition()})`;
|
||||
this.version = `v${this.appDataService.version()} (${this.appDataService.edition()})`;
|
||||
this.isDarkMode = this.themeService.isDark;
|
||||
}
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export * from './console-nav/console-nav.component';
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { ProFeatureBadgeComponent } from '../../components';
|
||||
import { ProFeatureBadgeComponent } from '../../../../shared';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-logo-selector',
|
||||
@ -0,0 +1,3 @@
|
||||
export * from './components';
|
||||
export * from './pages';
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.ov-page-container {
|
||||
button {
|
||||
@ -12,8 +12,8 @@ import { MatSlideToggleChange, MatSlideToggleModule } from '@angular/material/sl
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MeetAppearanceConfig, MeetRoomTheme, MeetRoomThemeMode } from '@openvidu-meet/typings';
|
||||
import { OPENVIDU_COMPONENTS_DARK_THEME, OPENVIDU_COMPONENTS_LIGHT_THEME } from 'openvidu-components-angular';
|
||||
import { ColorField, ThemeColors } from '../../../models';
|
||||
import { GlobalConfigService, NotificationService } from '../../../services';
|
||||
import { ColorField, ThemeColors } from '../../../../shared/models';
|
||||
import { GlobalConfigService, NotificationService } from '../../../../shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-config',
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
@use '../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
::ng-deep {
|
||||
mat-slide-toggle {
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ConsoleNavLink } from '../../../../shared';
|
||||
import { AuthService } from '../../../auth/services/auth.service';
|
||||
import { ConsoleNavComponent } from '../../components';
|
||||
import { ConsoleNavLink } from '../../models';
|
||||
import { AuthService } from '../../services';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-console',
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.ov-page-container {
|
||||
button {
|
||||
@ -9,8 +9,8 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { ApiKeyService, GlobalConfigService, NotificationService } from '../../../services';
|
||||
import { MeetApiKey } from '@openvidu-meet/typings';
|
||||
import { ApiKeyService, GlobalConfigService, NotificationService } from '../../../../shared';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-embedded',
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.error-page {
|
||||
@include design-tokens.ov-theme-transition;
|
||||
@ -3,8 +3,10 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ErrorReason } from '../../models';
|
||||
import { AppDataService, AuthService, NavigationService, WebComponentManagerService } from '../../services';
|
||||
import { AppDataService, NavigationService, } from '../../../../shared';
|
||||
import { NavigationErrorReason } from '../../../../shared/models/navigation.model';
|
||||
import { AuthService } from '../../../auth/services/auth.service';
|
||||
import { MeetingWebComponentManagerService } from '../../../meeting/services';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-error',
|
||||
@ -24,7 +26,7 @@ export class ErrorComponent implements OnInit {
|
||||
protected authService: AuthService,
|
||||
protected navService: NavigationService,
|
||||
protected appDataService: AppDataService,
|
||||
protected wcManagerService: WebComponentManagerService
|
||||
protected wcManagerService: MeetingWebComponentManagerService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -48,51 +50,51 @@ export class ErrorComponent implements OnInit {
|
||||
* 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.CLOSED_ROOM]: {
|
||||
const reasonMap: { [key in NavigationErrorReason]: { title: string; message: string } } = {
|
||||
[NavigationErrorReason.CLOSED_ROOM]: {
|
||||
title: 'Closed room',
|
||||
message: 'The room you are trying to access is closed'
|
||||
},
|
||||
[ErrorReason.MISSING_ROOM_SECRET]: {
|
||||
[NavigationErrorReason.MISSING_ROOM_SECRET]: {
|
||||
title: 'Invalid link',
|
||||
message:
|
||||
'The link you used to access this room is not valid. Please ask the moderator to share the correct link using the share buttons available in the room. Note: Sharing the URL from the browser address bar is not valid'
|
||||
},
|
||||
[ErrorReason.MISSING_RECORDING_SECRET]: {
|
||||
[NavigationErrorReason.MISSING_RECORDING_SECRET]: {
|
||||
title: 'Invalid link',
|
||||
message: 'The link you used to access this recording is not valid'
|
||||
},
|
||||
[ErrorReason.INVALID_ROOM_SECRET]: {
|
||||
[NavigationErrorReason.INVALID_ROOM_SECRET]: {
|
||||
title: 'Invalid link',
|
||||
message:
|
||||
'The link you used to access this room is not valid. Please ask the moderator to share the correct link using the share buttons available in the room. Note: Sharing the URL from the browser address bar is not valid'
|
||||
},
|
||||
[ErrorReason.INVALID_RECORDING_SECRET]: {
|
||||
[NavigationErrorReason.INVALID_RECORDING_SECRET]: {
|
||||
title: 'Invalid link',
|
||||
message: 'The link you used to access this recording is not valid'
|
||||
},
|
||||
[ErrorReason.INVALID_ROOM]: {
|
||||
[NavigationErrorReason.INVALID_ROOM]: {
|
||||
title: 'Invalid room',
|
||||
message: 'The room you are trying to access does not exist or has been deleted'
|
||||
},
|
||||
[ErrorReason.INVALID_RECORDING]: {
|
||||
[NavigationErrorReason.INVALID_RECORDING]: {
|
||||
title: 'Invalid recording',
|
||||
message: 'The recording you are trying to access does not exist or has been deleted'
|
||||
},
|
||||
[ErrorReason.UNAUTHORIZED_RECORDING_ACCESS]: {
|
||||
[NavigationErrorReason.UNAUTHORIZED_RECORDING_ACCESS]: {
|
||||
title: 'Unauthorized recording access',
|
||||
message: 'You are not authorized to access the recordings in this room'
|
||||
},
|
||||
[ErrorReason.INTERNAL_ERROR]: {
|
||||
[NavigationErrorReason.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
|
||||
const normalizedReason = Object.values(NavigationErrorReason).find((enumValue) => enumValue === reason) as
|
||||
| NavigationErrorReason
|
||||
| undefined;
|
||||
return reasonMap[normalizedReason ?? ErrorReason.INTERNAL_ERROR];
|
||||
return reasonMap[normalizedReason ?? NavigationErrorReason.INTERNAL_ERROR];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -0,0 +1,7 @@
|
||||
export * from './about/about.component';
|
||||
export * from './config/config.component';
|
||||
export * from './console/console.component';
|
||||
export * from './embedded/embedded.component';
|
||||
export * from './overview/overview.component';
|
||||
export * from './users-permissions/users-permissions.component';
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
// Welcome State Styles
|
||||
.welcome-content {
|
||||
@ -4,7 +4,7 @@ import { MatCardModule } from '@angular/material/card';
|
||||
import { MatGridListModule } from '@angular/material/grid-list';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MeetAnalytics } from '@openvidu-meet/typings';
|
||||
import { AnalyticsService, NavigationService } from '../../../services';
|
||||
import { AnalyticsService, NavigationService } from '../../../../shared';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-overview',
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.form-field-header {
|
||||
position: relative;
|
||||
@ -18,8 +18,9 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
// import { ProFeatureBadgeComponent } from '../components';
|
||||
import { AuthService, GlobalConfigService, NotificationService } from '../../../services';
|
||||
import { AuthMode } from '@openvidu-meet/typings';
|
||||
import { GlobalConfigService, NotificationService } from '../../../../shared';
|
||||
import { AuthService } from '../../../auth/services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-users-permissions',
|
||||
@ -0,0 +1,5 @@
|
||||
export * from './hidden-participants-indicator/hidden-participants-indicator.component';
|
||||
export * from './meeting-lobby/meeting-lobby.component';
|
||||
export * from './meeting-share-link-overlay/meeting-share-link-overlay.component';
|
||||
export * from './share-meeting-link/share-meeting-link.component';
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
// Room Access Container - Main layout using design tokens
|
||||
.room-access-container {
|
||||
@ -7,8 +7,8 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { ShareMeetingLinkComponent } from '../../components';
|
||||
import { MeetingLobbyService } from '../../services/meeting/meeting-lobby.service';
|
||||
import { MeetingService } from '../../services/meeting/meeting.service';
|
||||
import { MeetingLobbyService } from '../../services/meeting-lobby.service';
|
||||
import { MeetingService } from '../../services/meeting.service';
|
||||
|
||||
/**
|
||||
* Reusable component for the meeting lobby page.
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.main-share-meeting-link-container {
|
||||
background-color: var(--ov-surface-color); // Use ov-components variable
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.meeting-url-badge {
|
||||
margin: var(--ov-meet-spacing-sm) auto;
|
||||
@ -1,7 +1,8 @@
|
||||
export * from './meeting-toolbar-leave-button/meeting-toolbar-leave-button.component';
|
||||
export * from './meeting-toolbar-extra-buttons/meeting-toolbar-extra-buttons.component';
|
||||
export * from './meeting-custom-layout/meeting-custom-layout.component';
|
||||
export * from './meeting-invite-panel/meeting-invite-panel.component';
|
||||
export * from './meeting-participant-item/meeting-participant-item.component';
|
||||
export * from './meeting-custom-layout/meeting-custom-layout.component';
|
||||
export * from './meeting-settings-extensions/meeting-settings-extensions.component';
|
||||
export * from './meeting-toolbar-extra-buttons/meeting-toolbar-extra-buttons.component';
|
||||
export * from './meeting-toolbar-leave-button/meeting-toolbar-leave-button.component';
|
||||
export * from './meeting-toolbar-more-options-menu/meeting-toolbar-more-options-menu.component';
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.remote-participant {
|
||||
height: -webkit-fill-available;
|
||||
@ -1,20 +1,20 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Participant } from 'livekit-client';
|
||||
import {
|
||||
ParticipantModel,
|
||||
LoggerService,
|
||||
ParticipantService,
|
||||
OpenViduService
|
||||
OpenViduService,
|
||||
ParticipantModel,
|
||||
ParticipantService
|
||||
} from 'openvidu-components-angular';
|
||||
import { Subject } from 'rxjs';
|
||||
import { MeetLayoutMode } from '../../models/layout.model';
|
||||
import { MeetingLayoutService } from '../../services/meeting-layout.service';
|
||||
import { MeetingCustomLayoutComponent } from './meeting-custom-layout.component';
|
||||
import { MeetLayoutService } from '../../../services/layout.service';
|
||||
import { MeetLayoutMode } from '../../../models/layout.model';
|
||||
|
||||
describe('MeetingLayoutComponent', () => {
|
||||
let component: MeetingCustomLayoutComponent;
|
||||
let fixture: ComponentFixture<MeetingCustomLayoutComponent>;
|
||||
let mockLayoutService: jasmine.SpyObj<MeetLayoutService>;
|
||||
let mockLayoutService: jasmine.SpyObj<MeetingLayoutService>;
|
||||
let mockParticipantService: jasmine.SpyObj<ParticipantService>;
|
||||
let mockOpenViduService: jasmine.SpyObj<OpenViduService>;
|
||||
let mockLoggerService: jasmine.SpyObj<LoggerService>;
|
||||
@ -58,7 +58,7 @@ describe('MeetingLayoutComponent', () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MeetingCustomLayoutComponent],
|
||||
providers: [
|
||||
{ provide: MeetLayoutService, useValue: mockLayoutService },
|
||||
{ provide: MeetingLayoutService, useValue: mockLayoutService },
|
||||
{ provide: ParticipantService, useValue: mockParticipantService },
|
||||
{ provide: OpenViduService, useValue: mockOpenViduService },
|
||||
{ provide: LoggerService, useValue: mockLoggerService }
|
||||
@ -1,9 +1,9 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, computed, effect, inject, signal, untracked } from '@angular/core';
|
||||
import { ILogger, LoggerService, OpenViduComponentsUiModule, PanelService, PanelType, ParticipantModel } from 'openvidu-components-angular';
|
||||
import { HiddenParticipantsIndicatorComponent, ShareMeetingLinkComponent } from '../../../components';
|
||||
import { CustomParticipantModel } from '../../../models';
|
||||
import { MeetingContextService, MeetingService, MeetLayoutService } from '../../../services';
|
||||
import { HiddenParticipantsIndicatorComponent, ShareMeetingLinkComponent } from '../../components';
|
||||
import { CustomParticipantModel } from '../../models';
|
||||
import { MeetingContextService, MeetingLayoutService, MeetingService } from '../../services';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-meeting-custom-layout',
|
||||
@ -18,7 +18,7 @@ import { MeetingContextService, MeetingService, MeetLayoutService } from '../../
|
||||
})
|
||||
export class MeetingCustomLayoutComponent {
|
||||
private readonly logger: ILogger = inject(LoggerService).get('MeetingCustomLayoutComponent');
|
||||
protected readonly layoutService = inject(MeetLayoutService);
|
||||
protected readonly layoutService = inject(MeetingLayoutService);
|
||||
protected readonly meetingContextService = inject(MeetingContextService);
|
||||
protected readonly meetingService = inject(MeetingService);
|
||||
protected readonly panelService = inject(PanelService);
|
||||
@ -1,9 +1,9 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { ShareMeetingLinkComponent } from '../../../components/share-meeting-link/share-meeting-link.component';
|
||||
import { MeetingContextService } from '../../../services/meeting/meeting-context.service';
|
||||
import { MeetingService } from '../../../services/meeting/meeting.service';
|
||||
import { LoggerService } from 'openvidu-components-angular';
|
||||
import { ShareMeetingLinkComponent } from '../../components/share-meeting-link/share-meeting-link.component';
|
||||
import { MeetingContextService } from '../../services/meeting-context.service';
|
||||
import { MeetingService } from '../../services/meeting.service';
|
||||
|
||||
/**
|
||||
* Reusable component for displaying the share meeting link panel
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.participant-item-container {
|
||||
width: 100%;
|
||||
@ -3,10 +3,10 @@ import { Component, TemplateRef, ViewChild, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { LoggerService, OpenViduComponentsUiModule } from 'openvidu-components-angular';
|
||||
import { CustomParticipantModel } from '../../../models';
|
||||
import { MeetingService } from '../../../services/meeting/meeting.service';
|
||||
import { MeetRoomMemberRole } from '@openvidu-meet/typings';
|
||||
import { LoggerService, OpenViduComponentsUiModule } from 'openvidu-components-angular';
|
||||
import { CustomParticipantModel } from '../../models';
|
||||
import { MeetingService } from '../../services/meeting.service';
|
||||
|
||||
/**
|
||||
* Interface for computed participant display properties
|
||||
@ -1,15 +1,15 @@
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MeetLayoutMode } from '../../../models/layout.model';
|
||||
import { MeetLayoutService } from '../../../services/layout.service';
|
||||
import { MeetingContextService } from '../../../services/meeting/meeting-context.service';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { MeetLayoutMode } from '../../models/layout.model';
|
||||
import { MeetingContextService } from '../../services/meeting-context.service';
|
||||
import { MeetingLayoutService } from '../../services/meeting-layout.service';
|
||||
|
||||
/**
|
||||
* Component for additional settings in the Settings Panel.
|
||||
@ -30,7 +30,7 @@ import { MeetingContextService } from '../../../services/meeting/meeting-context
|
||||
styleUrl: './meeting-settings-extensions.component.scss'
|
||||
})
|
||||
export class MeetingSettingsExtensionsComponent {
|
||||
private readonly layoutService = inject(MeetLayoutService);
|
||||
private readonly layoutService = inject(MeetingLayoutService);
|
||||
protected readonly meetingContextService = inject(MeetingContextService);
|
||||
|
||||
/**
|
||||
@ -1,12 +1,12 @@
|
||||
import { Component, inject, computed } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MeetingContextService } from '../../../services/meeting/meeting-context.service';
|
||||
import { MeetingService } from '../../../services/meeting/meeting.service';
|
||||
import { LoggerService } from 'openvidu-components-angular';
|
||||
import { MeetingContextService } from '../../services/meeting-context.service';
|
||||
import { MeetingService } from '../../services/meeting.service';
|
||||
|
||||
/**
|
||||
* Component for extra toolbar buttons (like copy meeting link).
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.button-text {
|
||||
margin-left: 8px;
|
||||
@ -1,13 +1,13 @@
|
||||
import { Component, inject, computed } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MeetingContextService } from '../../../services/meeting/meeting-context.service';
|
||||
import { MeetingService } from '../../../services/meeting/meeting.service';
|
||||
import { LoggerService, OpenViduService } from 'openvidu-components-angular';
|
||||
import { MeetingContextService } from '../../services/meeting-context.service';
|
||||
import { MeetingService } from '../../services/meeting.service';
|
||||
|
||||
/**
|
||||
* Reusable component for meeting toolbar Leave button.
|
||||
@ -1,11 +1,11 @@
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { PanelService, ViewportService, PanelType } from 'openvidu-components-angular';
|
||||
import { MeetingContextService } from '../../../services/meeting/meeting-context.service';
|
||||
import { PanelService, PanelType, ViewportService } from 'openvidu-components-angular';
|
||||
import { MeetingContextService } from '../../services/meeting-context.service';
|
||||
|
||||
/**
|
||||
* Component for additional menu items in the toolbar's "More Options" menu.
|
||||
@ -0,0 +1,5 @@
|
||||
export * from './components';
|
||||
export * from './customization';
|
||||
export * from './models';
|
||||
export * from './pages';
|
||||
export * from './services';
|
||||
@ -0,0 +1,3 @@
|
||||
export * from './custom-participant.model';
|
||||
export * from './layout.model';
|
||||
export * from './lobby.model';
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.disconnected-container {
|
||||
@include design-tokens.ov-theme-transition;
|
||||
@ -4,8 +4,10 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AppDataService, AuthService, NavigationService, WebComponentManagerService } from '../../../services';
|
||||
import { LeftEventReason } from '@openvidu-meet/typings';
|
||||
import { AppDataService, NavigationService, } from '../../../../shared';
|
||||
import { AuthService } from '../../../auth/services/auth.service';
|
||||
import { MeetingWebComponentManagerService } from '../../services';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-end-meeting',
|
||||
@ -25,7 +27,7 @@ export class EndMeetingComponent implements OnInit {
|
||||
protected authService: AuthService,
|
||||
protected navService: NavigationService,
|
||||
protected appDataService: AppDataService,
|
||||
protected wcManagerService: WebComponentManagerService
|
||||
protected wcManagerService: MeetingWebComponentManagerService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -0,0 +1,4 @@
|
||||
export * from './end-meeting/end-meeting.component';
|
||||
export * from './meeting/meeting.component';
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
@use '../../../../../../src/assets/styles/design-tokens';
|
||||
@use '../../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
.prejoin-loading-container,
|
||||
.prejoin-error-container {
|
||||
@ -13,20 +13,16 @@ import {
|
||||
ViewportService
|
||||
} from 'openvidu-components-angular';
|
||||
import { Subject } from 'rxjs';
|
||||
import { MeetingLobbyComponent } from '../../components/meeting-lobby/meeting-lobby.component';
|
||||
import { MeetingParticipantItemComponent } from '../../customization';
|
||||
import { ApplicationFeatures } from '../../models/app.model';
|
||||
import {
|
||||
ApplicationFeatures,
|
||||
FeatureConfigurationService,
|
||||
GlobalConfigService,
|
||||
MeetingContextService,
|
||||
MeetingEventHandlerService,
|
||||
MeetingLobbyService,
|
||||
MeetingService,
|
||||
NotificationService,
|
||||
RoomMemberService,
|
||||
WebComponentManagerService
|
||||
} from '../../services';
|
||||
NotificationService
|
||||
} from '../../../../shared';
|
||||
import { RoomMemberService, } from '../../../rooms/services';
|
||||
import { MeetingLobbyComponent } from '../../components/meeting-lobby/meeting-lobby.component';
|
||||
import { MeetingParticipantItemComponent } from '../../customization';
|
||||
import { MeetingContextService, MeetingEventHandlerService, MeetingLobbyService, MeetingService, MeetingWebComponentManagerService } from '../../services';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-meeting',
|
||||
@ -64,7 +60,7 @@ export class MeetingComponent implements OnInit {
|
||||
protected meetingService = inject(MeetingService);
|
||||
protected participantService = inject(RoomMemberService);
|
||||
protected featureConfService = inject(FeatureConfigurationService);
|
||||
protected wcManagerService = inject(WebComponentManagerService);
|
||||
protected wcManagerService = inject(MeetingWebComponentManagerService);
|
||||
protected openviduService = inject(OpenViduService);
|
||||
protected viewportService = inject(ViewportService);
|
||||
protected ovThemeService = inject(OpenViduThemeService);
|
||||
@ -0,0 +1,7 @@
|
||||
export * from './meeting-context.service';
|
||||
export * from './meeting-event-handler.service';
|
||||
export * from './meeting-layout.service';
|
||||
export * from './meeting-lobby.service';
|
||||
export * from './meeting-webcomponent-manager.service';
|
||||
export * from './meeting.service';
|
||||
|
||||
@ -2,9 +2,9 @@ import { computed, DestroyRef, effect, inject, Injectable, signal } from '@angul
|
||||
|
||||
import { MeetRoom } from 'node_modules/@openvidu-meet/typings/dist/room';
|
||||
import { ParticipantService, Room, ViewportService } from 'openvidu-components-angular';
|
||||
import { CustomParticipantModel } from '../../models';
|
||||
import { FeatureConfigurationService } from '../feature-configuration.service';
|
||||
import { SessionStorageService } from '../session-storage.service';
|
||||
import { FeatureConfigurationService } from '../../../shared/services/feature-configuration.service';
|
||||
import { SessionStorageService } from '../../../shared/services/session-storage.service';
|
||||
import { CustomParticipantModel } from '../models';
|
||||
|
||||
/**
|
||||
* Central service for managing meeting context and state during the MEETING PHASE.
|
||||
@ -18,17 +18,15 @@ import {
|
||||
Room,
|
||||
RoomEvent
|
||||
} from 'openvidu-components-angular';
|
||||
import { CustomParticipantModel } from '../../models';
|
||||
import { MeetingContextService, MeetingWebComponentManagerService } from '.';
|
||||
import {
|
||||
FeatureConfigurationService,
|
||||
MeetingContextService,
|
||||
NavigationService,
|
||||
RecordingService,
|
||||
RoomMemberService,
|
||||
SessionStorageService,
|
||||
TokenStorageService,
|
||||
WebComponentManagerService
|
||||
} from '../../services';
|
||||
TokenStorageService
|
||||
} from '../../../shared';
|
||||
import { RecordingService } from '../../recordings/services';
|
||||
import { RoomMemberService } from '../../rooms/services';
|
||||
|
||||
/**
|
||||
* Service that handles all LiveKit/OpenVidu room events.
|
||||
@ -44,7 +42,7 @@ export class MeetingEventHandlerService {
|
||||
protected roomMemberService = inject(RoomMemberService);
|
||||
protected sessionStorageService = inject(SessionStorageService);
|
||||
protected tokenStorageService = inject(TokenStorageService);
|
||||
protected wcManagerService = inject(WebComponentManagerService);
|
||||
protected wcManagerService = inject(MeetingWebComponentManagerService);
|
||||
protected navigationService = inject(NavigationService);
|
||||
|
||||
// ============================================
|
||||
@ -1,11 +1,11 @@
|
||||
import { computed, DestroyRef, effect, inject, Injectable, signal } from '@angular/core';
|
||||
import { Participant, Room } from 'livekit-client';
|
||||
import { LayoutService, LoggerService, ViewportService } from 'openvidu-components-angular';
|
||||
import { MeetStorageService } from '.';
|
||||
import { MeetStorageService } from '../../../shared';
|
||||
import { MeetLayoutMode } from '../models';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class MeetLayoutService extends LayoutService {
|
||||
export class MeetingLayoutService extends LayoutService {
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly INITIAL_SPEAKERS_COUNT = 4;
|
||||
readonly MIN_REMOTE_SPEAKERS = 1;
|
||||
@ -4,18 +4,20 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { MeetRoomStatus } from '@openvidu-meet/typings';
|
||||
import { LoggerService } from 'openvidu-components-angular';
|
||||
import {
|
||||
AppDataService,
|
||||
AuthService,
|
||||
MeetingContextService,
|
||||
MeetingService,
|
||||
NavigationService,
|
||||
RecordingService,
|
||||
|
||||
MeetingWebComponentManagerService
|
||||
} from '.';
|
||||
import { AppDataService, NavigationService } from '../../../shared';
|
||||
import { NavigationErrorReason } from '../../../shared/models/navigation.model';
|
||||
import { AuthService } from '../../auth/services/auth.service';
|
||||
import { RecordingService } from '../../recordings/services';
|
||||
import {
|
||||
RoomMemberService,
|
||||
RoomService,
|
||||
WebComponentManagerService
|
||||
} from '..';
|
||||
import { ErrorReason } from '../../models';
|
||||
import { LobbyState } from '../../models/lobby.model';
|
||||
} from '../../rooms/services';
|
||||
import { LobbyState } from '../models/lobby.model';
|
||||
|
||||
/**
|
||||
* Service that manages the meeting lobby phase state and operations.
|
||||
@ -51,7 +53,7 @@ export class MeetingLobbyService {
|
||||
protected roomMemberService: RoomMemberService = inject(RoomMemberService);
|
||||
protected navigationService: NavigationService = inject(NavigationService);
|
||||
protected appDataService: AppDataService = inject(AppDataService);
|
||||
protected wcManagerService: WebComponentManagerService = inject(WebComponentManagerService);
|
||||
protected wcManagerService: MeetingWebComponentManagerService = inject(MeetingWebComponentManagerService);
|
||||
protected loggerService = inject(LoggerService);
|
||||
protected log = this.loggerService.get('OpenVidu Meet - MeetingLobbyService');
|
||||
protected route: ActivatedRoute = inject(ActivatedRoute);
|
||||
@ -366,18 +368,18 @@ export class MeetingLobbyService {
|
||||
switch (error.status) {
|
||||
case 400:
|
||||
// Invalid secret
|
||||
await this.navigationService.redirectToErrorPage(ErrorReason.INVALID_ROOM_SECRET, true);
|
||||
await this.navigationService.redirectToErrorPage(NavigationErrorReason.INVALID_ROOM_SECRET, true);
|
||||
break;
|
||||
case 404:
|
||||
// Room not found
|
||||
await this.navigationService.redirectToErrorPage(ErrorReason.INVALID_ROOM, true);
|
||||
await this.navigationService.redirectToErrorPage(NavigationErrorReason.INVALID_ROOM, true);
|
||||
break;
|
||||
case 409:
|
||||
// Room is closed
|
||||
await this.navigationService.redirectToErrorPage(ErrorReason.CLOSED_ROOM, true);
|
||||
await this.navigationService.redirectToErrorPage(NavigationErrorReason.CLOSED_ROOM, true);
|
||||
break;
|
||||
default:
|
||||
await this.navigationService.redirectToErrorPage(ErrorReason.INTERNAL_ERROR, true);
|
||||
await this.navigationService.redirectToErrorPage(NavigationErrorReason.INTERNAL_ERROR, true);
|
||||
}
|
||||
|
||||
throw new Error('Error generating room member token');
|
||||
@ -1,4 +1,5 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { effect, inject, Injectable } from '@angular/core';
|
||||
import { AppDataService } from '@openvidu-meet/shared-components';
|
||||
import {
|
||||
WebComponentCommand,
|
||||
WebComponentEvent,
|
||||
@ -6,7 +7,8 @@ import {
|
||||
WebComponentOutboundEventMessage
|
||||
} from '@openvidu-meet/typings';
|
||||
import { LoggerService, OpenViduService } from 'openvidu-components-angular';
|
||||
import { MeetingContextService, MeetingService, RoomMemberService } from '../services';
|
||||
import { MeetingContextService, MeetingService } from '.';
|
||||
import { RoomMemberService } from '../../rooms/services';
|
||||
|
||||
/**
|
||||
* Service to manage the commands from OpenVidu Meet WebComponent/Iframe.
|
||||
@ -16,9 +18,8 @@ import { MeetingContextService, MeetingService, RoomMemberService } from '../ser
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class WebComponentManagerService {
|
||||
export class MeetingWebComponentManagerService {
|
||||
protected isInitialized = false;
|
||||
|
||||
protected parentDomain: string = '';
|
||||
protected boundHandleMessage: (event: MessageEvent) => Promise<void>;
|
||||
|
||||
@ -28,10 +29,16 @@ export class WebComponentManagerService {
|
||||
protected readonly openviduService = inject(OpenViduService);
|
||||
protected readonly meetingService = inject(MeetingService);
|
||||
protected readonly loggerService = inject(LoggerService);
|
||||
protected readonly appDataService = inject(AppDataService);
|
||||
|
||||
constructor() {
|
||||
this.log = this.loggerService.get('OpenVidu Meet - WebComponentManagerService');
|
||||
this.boundHandleMessage = this.handleMessage.bind(this);
|
||||
effect(() => {
|
||||
if (this.appDataService.isEmbeddedMode()) {
|
||||
this.initialize();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initialize() {
|
||||
@ -1,9 +1,9 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { Clipboard } from '@angular/cdk/clipboard';
|
||||
import { LoggerService } from 'openvidu-components-angular';
|
||||
import { HttpService } from '../http.service';
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { MeetRoom } from 'node_modules/@openvidu-meet/typings/dist/room';
|
||||
import { NotificationService } from '../notification.service';
|
||||
import { LoggerService } from 'openvidu-components-angular';
|
||||
import { HttpService } from '../../../shared/services/http.service';
|
||||
import { NotificationService } from '../../../shared/services/notification.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -0,0 +1,4 @@
|
||||
export * from './recording-lists/recording-lists.component';
|
||||
export * from './recording-share-dialog/recording-share-dialog.component';
|
||||
export * from './recording-video-player/recording-video-player.component';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user