diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/meeting/meeting.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/meeting/meeting.component.ts index 9e71258d..c7c2f0d0 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/meeting/meeting.component.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/pages/meeting/meeting.component.ts @@ -5,7 +5,6 @@ import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { OpenViduComponentsUiModule, OpenViduThemeMode, OpenViduThemeService, Room } from 'openvidu-components-angular'; import { Subject } from 'rxjs'; -import { GlobalConfigService } from '../../../../shared/services/global-config.service'; import { NotificationService } from '../../../../shared/services/notification.service'; import { RoomFeatureService } from '../../../../shared/services/room-feature.service'; import { RuntimeConfigService } from '../../../../shared/services/runtime-config.service'; @@ -37,7 +36,6 @@ export class MeetingComponent implements OnInit { protected lobbyService = inject(MeetingLobbyService); protected eventHandlerService = inject(MeetingEventHandlerService); protected captionsService = inject(MeetingCaptionsService); - protected configService = inject(GlobalConfigService); protected roomFeatureService = inject(RoomFeatureService); protected ovThemeService = inject(OpenViduThemeService); protected notificationService = inject(NotificationService); @@ -95,12 +93,7 @@ export class MeetingComponent implements OnInit { effect(async () => { const token = this.roomMemberToken(); if (token && this.showLobby) { - // The meeting view must be shown before loading the appearance config this.showLobby = false; - await Promise.all([ - this.configService.loadRoomsAppearanceConfig(), - this.configService.loadCaptionsConfig() - ]); } }); } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/room-member-context.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/room-member-context.service.ts index 9289cdc1..9d265cfe 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/room-member-context.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/room-members/services/room-member-context.service.ts @@ -2,11 +2,11 @@ import { computed, Injectable, signal } from '@angular/core'; import { MeetRoomMember, MeetRoomMemberPermissions, + MeetRoomMemberRole, MeetRoomMemberTokenMetadata, MeetRoomMemberTokenOptions } from '@openvidu-meet/typings'; import { E2eeService, LoggerService } from 'openvidu-components-angular'; -import { RoomFeatureService } from '../../../shared/services/room-feature.service'; import { TokenStorageService } from '../../../shared/services/token-storage.service'; import { decodeToken } from '../../../shared/utils/token.utils'; import { RoomMemberService } from './room-member.service'; @@ -24,6 +24,7 @@ export class RoomMemberContextService { private readonly _participantName = signal(undefined); private readonly _isParticipantNameFromUrl = signal(false); private readonly _participantIdentity = signal(undefined); + private readonly _role = signal(undefined); private readonly _permissions = signal(undefined); private readonly _member = signal(undefined); @@ -35,6 +36,8 @@ export class RoomMemberContextService { readonly isParticipantNameFromUrl = this._isParticipantNameFromUrl.asReadonly(); /** Readonly signal for the participant identity */ readonly participantIdentity = this._participantIdentity.asReadonly(); + /** Readonly signal for the room member role */ + readonly role = this._role.asReadonly(); /** Readonly signal for the room member permissions */ readonly permissions = this._permissions.asReadonly(); /** Readonly signal for the room member info (when memberId is set) */ @@ -47,7 +50,6 @@ export class RoomMemberContextService { constructor( protected loggerService: LoggerService, protected roomMemberService: RoomMemberService, - protected roomFeatureService: RoomFeatureService, protected tokenStorageService: TokenStorageService, protected e2eeService: E2eeService ) { @@ -134,6 +136,7 @@ export class RoomMemberContextService { this._participantIdentity.set(decodedToken.sub); } + this._role.set(metadata.baseRole); this._permissions.set(metadata.effectivePermissions); // If token contains memberId, fetch and store member info @@ -146,9 +149,6 @@ export class RoomMemberContextService { } } - // Update feature configuration - this.roomFeatureService.setRoomMemberRole(metadata.baseRole); - this.roomFeatureService.setRoomMemberPermissions(metadata.effectivePermissions); } catch (error) { this.log.e('Error decoding room member token:', error); throw new Error('Invalid room member token'); @@ -163,6 +163,7 @@ export class RoomMemberContextService { this._participantName.set(undefined); this._isParticipantNameFromUrl.set(false); this._participantIdentity.set(undefined); + this._role.set(undefined); this._permissions.set(undefined); this._member.set(undefined); } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/global-config.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/global-config.service.ts index 616bd311..123e03db 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/global-config.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/global-config.service.ts @@ -1,8 +1,7 @@ -import { inject, Injectable } from '@angular/core'; +import { inject, Injectable, signal } from '@angular/core'; import { MeetAppearanceConfig, SecurityConfig, WebhookConfig } from '@openvidu-meet/typings'; import { ILogger, LoggerService } from 'openvidu-components-angular'; import { HttpService } from './http.service'; -import { RoomFeatureService } from './room-feature.service'; @Injectable({ providedIn: 'root' @@ -12,10 +11,15 @@ export class GlobalConfigService { protected loggerService: LoggerService = inject(LoggerService); protected httpService: HttpService = inject(HttpService); - protected roomFeatureService: RoomFeatureService = inject(RoomFeatureService); protected log: ILogger = this.loggerService.get('OpenVidu Meet - GlobalConfigService'); + private readonly _roomAppearanceConfig = signal(undefined); + private readonly _captionsGlobalEnabled = signal(false); + + readonly roomAppearanceConfig = this._roomAppearanceConfig.asReadonly(); + readonly captionsGlobalEnabled = this._captionsGlobalEnabled.asReadonly(); + constructor() {} async getSecurityConfig(): Promise { @@ -50,14 +54,24 @@ export class GlobalConfigService { async loadRoomsAppearanceConfig(): Promise { try { - const config = await this.getRoomsAppearanceConfig(); - this.roomFeatureService.setAppearanceConfig(config.appearance); + const { appearance } = await this.getRoomsAppearanceConfig(); + this._roomAppearanceConfig.set(appearance); } catch (error) { this.log.e('Error loading rooms appearance config:', error); throw error; } } + async loadCaptionsConfig(): Promise { + try { + const { enabled } = await this.getCaptionsConfig(); + this._captionsGlobalEnabled.set(enabled); + } catch (error) { + this.log.e('Error loading captions config:', error); + throw error; + } + } + async saveRoomsAppearanceConfig(config: MeetAppearanceConfig) { const path = `${this.GLOBAL_CONFIG_API}/rooms/appearance`; await this.httpService.putRequest(path, { appearance: config }); @@ -67,14 +81,4 @@ export class GlobalConfigService { const path = `${this.GLOBAL_CONFIG_API}/captions`; return await this.httpService.getRequest<{ enabled: boolean }>(path); } - - async loadCaptionsConfig(): Promise { - try { - const config = await this.getCaptionsConfig(); - this.roomFeatureService.setCaptionsGlobalConfig(config.enabled); - } catch (error) { - this.log.e('Error loading captions config:', error); - throw error; - } - } } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/room-feature.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/room-feature.service.ts index a16515da..3e39dff0 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/room-feature.service.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/room-feature.service.ts @@ -1,13 +1,9 @@ -import { computed, Injectable, signal } from '@angular/core'; -import { - MeetAppearanceConfig, - MeetRoomCaptionsConfig, - MeetRoomConfig, - MeetRoomMemberPermissions, - MeetRoomMemberRole -} from '@openvidu-meet/typings'; +import { computed, inject, Injectable, signal } from '@angular/core'; +import { MeetAppearanceConfig, MeetRoomCaptionsConfig, MeetRoomConfig, MeetRoomMemberPermissions, MeetRoomMemberRole } from '@openvidu-meet/typings'; import { LoggerService } from 'openvidu-components-angular'; +import { RoomMemberContextService } from '../../domains/room-members/services/room-member-context.service'; import { CaptionsStatus, RoomFeatures } from '../models/app.model'; +import { GlobalConfigService } from './global-config.service'; /** * Base configuration for features, used as a starting point before applying room-specific and user-specific configurations @@ -51,27 +47,26 @@ const DEFAULT_FEATURES: RoomFeatures = { }) export class RoomFeatureService { protected log; + protected globalConfigService = inject(GlobalConfigService); + protected roomMemberContextService = inject(RoomMemberContextService); // Signals to handle reactive state protected roomConfig = signal(undefined); - protected roomMemberRole = signal(undefined); - protected roomMemberPermissions = signal(undefined); - protected appearanceConfig = signal(undefined); - protected captionsGlobalConfig = signal(false); // Computed signal to derive features based on current configurations public readonly features = computed(() => this.calculateFeatures( this.roomConfig(), - this.roomMemberRole(), - this.roomMemberPermissions(), - this.appearanceConfig(), - this.captionsGlobalConfig() + this.roomMemberContextService.role(), + this.roomMemberContextService.permissions(), + this.globalConfigService.roomAppearanceConfig(), + this.globalConfigService.captionsGlobalEnabled() ) ); constructor(protected loggerService: LoggerService) { this.log = this.loggerService.get('OpenVidu Meet - RoomFeatureService'); + void this.loadGlobalFeatureConfigs(); } /** @@ -82,36 +77,18 @@ export class RoomFeatureService { this.roomConfig.set(config); } - /** - * Updates room member role - */ - setRoomMemberRole(role: MeetRoomMemberRole): void { - this.log.d('Updating room member role', role); - this.roomMemberRole.set(role); - } + protected async loadGlobalFeatureConfigs(): Promise { + const [appearanceResult, captionsResult] = await Promise.allSettled([ + this.globalConfigService.loadRoomsAppearanceConfig(), + this.globalConfigService.loadCaptionsConfig() + ]); - /** - * Updates room member permissions - */ - setRoomMemberPermissions(permissions: MeetRoomMemberPermissions): void { - this.log.d('Updating room member permissions', permissions); - this.roomMemberPermissions.set(permissions); - } - - /** - * Updates appearance config - */ - setAppearanceConfig(config: MeetAppearanceConfig): void { - this.log.d('Updating appearance config', config); - this.appearanceConfig.set(config); - } - - /** - * Updates captions global config - */ - setCaptionsGlobalConfig(enabled: boolean): void { - this.log.d('Updating captions global config', enabled); - this.captionsGlobalConfig.set(enabled); + if (appearanceResult.status === 'rejected') { + this.log.e('Could not load room appearance config for features:', appearanceResult.reason); + } + if (captionsResult.status === 'rejected') { + this.log.e('Could not load captions config for features:', captionsResult.reason); + } } /** @@ -205,9 +182,5 @@ export class RoomFeatureService { */ reset(): void { this.roomConfig.set(undefined); - this.roomMemberRole.set(undefined); - this.roomMemberPermissions.set(undefined); - this.appearanceConfig.set(undefined); - this.captionsGlobalConfig.set(false); } }