diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.ts
index 5bbec423..282a635a 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.ts
@@ -133,9 +133,21 @@ export class PreJoinComponent implements OnInit, OnDestroy {
this.shouldRemoveTracksWhenComponentIsDestroyed = false;
// Assign participant name to the observable if it is defined
- if(this.participantName) this.libService.setParticipantName(this.participantName);
+ if (this.participantName) {
+ this.libService.updateGeneralConfig({ participantName: this.participantName });
- this.onReadyToJoin.emit();
+ // Wait for the next tick to ensure the participant name propagates
+ // through the observable before emitting onReadyToJoin
+ const sub = this.libService.participantName$.pipe(takeUntil(this.destroy$)).subscribe((name) => {
+ if (name === this.participantName) {
+ this.onReadyToJoin.emit();
+ sub.unsubscribe();
+ }
+ });
+ } else {
+ // No participant name to set, emit immediately
+ this.onReadyToJoin.emit();
+ }
}
onParticipantNameChanged(name: string) {
@@ -147,49 +159,37 @@ export class PreJoinComponent implements OnInit, OnDestroy {
}
private subscribeToPrejoinDirectives() {
- this.libService.minimal$
- .pipe(takeUntil(this.destroy$))
- .subscribe((value: boolean) => {
- this.isMinimal = value;
- this.changeDetector.markForCheck();
- });
+ this.libService.minimal$.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => {
+ this.isMinimal = value;
+ this.changeDetector.markForCheck();
+ });
- this.libService.cameraButton$
- .pipe(takeUntil(this.destroy$))
- .subscribe((value: boolean) => {
- this.showCameraButton = value;
- this.changeDetector.markForCheck();
- });
+ this.libService.cameraButton$.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => {
+ this.showCameraButton = value;
+ this.changeDetector.markForCheck();
+ });
- this.libService.microphoneButton$
- .pipe(takeUntil(this.destroy$))
- .subscribe((value: boolean) => {
- this.showMicrophoneButton = value;
- this.changeDetector.markForCheck();
- });
+ this.libService.microphoneButton$.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => {
+ this.showMicrophoneButton = value;
+ this.changeDetector.markForCheck();
+ });
- this.libService.displayLogo$
- .pipe(takeUntil(this.destroy$))
- .subscribe((value: boolean) => {
- this.showLogo = value;
- this.changeDetector.markForCheck();
- });
+ this.libService.displayLogo$.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => {
+ this.showLogo = value;
+ this.changeDetector.markForCheck();
+ });
- this.libService.participantName$
- .pipe(takeUntil(this.destroy$))
- .subscribe((value: string) => {
- if (value) {
- this.participantName = value;
- this.changeDetector.markForCheck();
- }
- });
-
- this.libService.prejoinDisplayParticipantName$
- .pipe(takeUntil(this.destroy$))
- .subscribe((value: boolean) => {
- this.showParticipantName = value;
+ this.libService.participantName$.pipe(takeUntil(this.destroy$)).subscribe((value: string) => {
+ if (value) {
+ this.participantName = value;
this.changeDetector.markForCheck();
- });
+ }
+ });
+
+ this.libService.prejoinDisplayParticipantName$.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => {
+ this.showParticipantName = value;
+ this.changeDetector.markForCheck();
+ });
}
async videoEnabledChanged(enabled: boolean) {
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.ts
index 5e238003..eb9d7c8f 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.ts
@@ -235,13 +235,13 @@ export class SessionComponent implements OnInit, OnDestroy {
this.subscribeToReconnection();
this.subscribeToVirtualBackground();
- if (this.libService.isRecordingEnabled()) {
+ // if (this.libService.isRecordingEnabled()) {
// this.subscribeToRecordingEvents();
- }
+ // }
- if (this.libService.isBroadcastingEnabled()) {
+ // if (this.libService.isBroadcastingEnabled()) {
// this.subscribeToBroadcastingEvents();
- }
+ // }
try {
await this.participantService.connect();
// Send room created after participant connect for avoiding to send incomplete room payload
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/videoconference/videoconference.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/videoconference/videoconference.component.ts
index 87e97752..2270a537 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/videoconference/videoconference.component.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/videoconference/videoconference.component.ts
@@ -591,7 +591,9 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
// Always initialize the room when ready to join
this.openviduService.initRoom();
- const participantName = this.latestParticipantName;
+ // Get the most current participant name from the service
+ // This ensures we have the latest value after any batch updates
+ const participantName = this.libService.getCurrentParticipantName() || this.latestParticipantName;
if (this.componentState.isRoomReady) {
// Room is ready, hide prejoin and proceed
@@ -607,6 +609,16 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
this.onTokenRequested.emit(participantName);
} else {
this.log.w('No participant name available when requesting token');
+ // Wait a bit and try again in case name is still propagating
+ setTimeout(() => {
+ const retryName = this.libService.getCurrentParticipantName()|| this.latestParticipantName;
+ if (retryName) {
+ this.log.d(`Retrying token request for participant: ${retryName}`);
+ this.onTokenRequested.emit(retryName);
+ } else {
+ this.log.e('Still no participant name available after retry');
+ }
+ }, 10);
}
}
@@ -734,7 +746,7 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
const storedName = this.storageSrv.getParticipantName();
if (storedName) {
this.latestParticipantName = storedName;
- this.libService.setParticipantName(storedName);
+ this.libService.updateGeneralConfig({ participantName: storedName });
}
this._onReadyToJoin();
}
@@ -747,7 +759,18 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
if (name) {
this.latestParticipantName = name;
this.storageSrv.setParticipantName(name);
+
+ // If we're waiting for a participant name to proceed with joining, do it now
+ if (this.componentState.state === VideoconferenceState.JOINING &&
+ this.componentState.isRoomReady &&
+ !this.componentState.showPrejoin) {
+ this.log.d('Participant name received, proceeding to join');
+ this.updateComponentState({
+ state: VideoconferenceState.READY_TO_CONNECT,
+ showPrejoin: false
+ });
+ }
}
});
}
-}
+}
\ No newline at end of file
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/activities-panel.directive.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/activities-panel.directive.ts
index c3145b91..18b74347 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/activities-panel.directive.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/activities-panel.directive.ts
@@ -49,9 +49,7 @@ export class ActivitiesPanelRecordingActivityDirective implements AfterViewInit,
}
update(value: boolean) {
- if (this.libService.showRecordingActivity() !== value) {
- this.libService.setRecordingActivity(value);
- }
+ this.libService.updateRecordingActivityConfig({ enabled: value });
}
}
@@ -103,8 +101,6 @@ export class ActivitiesPanelBroadcastingActivityDirective implements AfterViewIn
}
update(value: boolean) {
- if (this.libService.showBroadcastingActivity() !== value) {
- this.libService.setBroadcastingActivity(value);
- }
+ this.libService.setBroadcastingActivity(value);
}
}
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/admin.directive.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/admin.directive.ts
index 6bb5ef3f..f8cd47ca 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/admin.directive.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/admin.directive.ts
@@ -16,15 +16,17 @@ import { OpenViduComponentsConfigService } from '../../services/config/directive
standalone: false
})
export class AdminDashboardRecordingsListDirective implements AfterViewInit, OnDestroy {
-
@Input() set recordingsList(value: RecordingInfo[]) {
this.recordingsValue = value;
this.update(this.recordingsValue);
}
- recordingsValue: RecordingInfo [] = [];
+ recordingsValue: RecordingInfo[] = [];
- constructor(public elementRef: ElementRef, private libService: OpenViduComponentsConfigService) {}
+ constructor(
+ public elementRef: ElementRef,
+ private libService: OpenViduComponentsConfigService
+ ) {}
ngAfterViewInit() {
this.update(this.recordingsValue);
@@ -38,9 +40,7 @@ export class AdminDashboardRecordingsListDirective implements AfterViewInit, OnD
}
update(value: RecordingInfo[]) {
- if (this.libService.getAdminRecordingsList() !== value) {
- this.libService.setAdminRecordingsList(value);
- }
+ this.libService.updateAdminConfig({ recordingsList: value });
}
}
@@ -58,7 +58,6 @@ export class AdminDashboardRecordingsListDirective implements AfterViewInit, OnD
standalone: false
})
export class AdminDashboardTitleDirective implements AfterViewInit, OnDestroy {
-
@Input() set navbarTitle(value: string) {
this.navbarTitleValue = value;
this.update(this.navbarTitleValue);
@@ -66,7 +65,10 @@ export class AdminDashboardTitleDirective implements AfterViewInit, OnDestroy {
navbarTitleValue: string = 'OpenVidu Dashboard';
- constructor(public elementRef: ElementRef, private libService: OpenViduComponentsConfigService) {}
+ constructor(
+ public elementRef: ElementRef,
+ private libService: OpenViduComponentsConfigService
+ ) {}
ngAfterViewInit() {
this.update(this.navbarTitleValue);
@@ -80,13 +82,10 @@ export class AdminDashboardTitleDirective implements AfterViewInit, OnDestroy {
}
update(value: any) {
- if (this.libService.getAdminDashboardTitle() !== value) {
- this.libService.setAdminDashboardTitle(value);
- }
+ this.libService.updateAdminConfig({ dashboardTitle: value });
}
}
-
/**
* The **navbarTitle** directive allows customize the title of the navbar in {@link AdminLoginComponent}.
*
@@ -101,7 +100,6 @@ export class AdminDashboardTitleDirective implements AfterViewInit, OnDestroy {
standalone: false
})
export class AdminLoginTitleDirective implements AfterViewInit, OnDestroy {
-
@Input() set navbarTitle(value: any) {
this.navbarTitleValue = value;
this.update(this.navbarTitleValue);
@@ -109,7 +107,10 @@ export class AdminLoginTitleDirective implements AfterViewInit, OnDestroy {
navbarTitleValue: any = null;
- constructor(public elementRef: ElementRef, private libService: OpenViduComponentsConfigService) {}
+ constructor(
+ public elementRef: ElementRef,
+ private libService: OpenViduComponentsConfigService
+ ) {}
ngAfterViewInit() {
this.update(this.navbarTitleValue);
@@ -123,14 +124,10 @@ export class AdminLoginTitleDirective implements AfterViewInit, OnDestroy {
}
update(value: any) {
- if (this.libService.getAdminLoginTitle() !== value) {
- this.libService.setAdminLoginTitle(value);
- }
+ this.libService.updateAdminConfig({ loginTitle: value });
}
}
-
-
/**
* The **error** directive allows show the authentication error in {@link AdminLoginComponent}.
*
@@ -140,12 +137,11 @@ export class AdminLoginTitleDirective implements AfterViewInit, OnDestroy {
*
*
*/
- @Directive({
+@Directive({
selector: 'ov-admin-login[error]',
standalone: false
})
export class AdminLoginErrorDirective implements AfterViewInit, OnDestroy {
-
@Input() set error(value: any) {
this.errorValue = value;
this.update(this.errorValue);
@@ -153,7 +149,10 @@ export class AdminLoginErrorDirective implements AfterViewInit, OnDestroy {
errorValue: any = null;
- constructor(public elementRef: ElementRef, private libService: OpenViduComponentsConfigService) {}
+ constructor(
+ public elementRef: ElementRef,
+ private libService: OpenViduComponentsConfigService
+ ) {}
ngAfterViewInit() {
this.update(this.errorValue);
@@ -167,9 +166,6 @@ export class AdminLoginErrorDirective implements AfterViewInit, OnDestroy {
}
update(value: any) {
- if (this.libService.getAdminLoginError() !== value) {
- this.libService.setAdminLoginError(value);
- }
+ this.libService.updateAdminConfig({ loginError: value });
}
}
-
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/internals.directive.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/internals.directive.ts
index 6caecbe5..b108a5a6 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/internals.directive.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/internals.directive.ts
@@ -122,7 +122,7 @@ export class ToolbarBrandingLogoDirective implements AfterViewInit, OnDestroy {
}
private update(value: string) {
- this.libService.setBrandingLogo(value);
+ this.libService.updateToolbarConfig({ brandingLogo: value });
}
}
@@ -158,7 +158,7 @@ export class PrejoinDisplayParticipantName implements OnDestroy {
}
private update(value: boolean) {
- this.libService.setPrejoinDisplayParticipantName(value);
+ this.libService.updateGeneralConfig({ prejoinDisplayParticipantName: value });
}
}
@@ -213,7 +213,7 @@ export class RecordingActivityReadOnlyDirective implements OnDestroy {
* @ignore
*/
update(value: boolean) {
- this.libService.setRecordingActivityReadOnly(value);
+ this.libService.updateRecordingActivityConfig({ readOnly: value });
}
}
@@ -239,7 +239,7 @@ export class RecordingActivityShowControlsDirective implements OnDestroy {
/**
* @ignore
*/
- @Input() set recordingActivityShowControls(value: { play?: boolean; download?: boolean; delete?: boolean; externalView?: boolean }) {
+ @Input() set recordingActivityShowControls(value: { play: boolean; download: boolean; delete: boolean; externalView: boolean }) {
this.update(value);
}
@@ -268,8 +268,8 @@ export class RecordingActivityShowControlsDirective implements OnDestroy {
/**
* @ignore
*/
- update(value: { play?: boolean; download?: boolean; delete?: boolean; externalView?: boolean }) {
- this.libService.setRecordingActivityShowControls(value);
+ update(value: { play: boolean; download: boolean; delete: boolean; externalView: boolean }) {
+ this.libService.updateRecordingActivityConfig({ showControls: value });
}
}
@@ -334,9 +334,7 @@ export class ToolbarViewRecordingsButtonDirective implements AfterViewInit, OnDe
}
private update(value: boolean) {
- if (this.libService.getToolbarViewRecordingsButton() !== value) {
- this.libService.setToolbarViewRecordingsButton(value);
- }
+ this.libService.updateToolbarConfig({ viewRecordings: value });
}
}
@@ -381,7 +379,7 @@ export class StartStopRecordingButtonsDirective implements OnDestroy {
}
private update(value: boolean) {
- this.libService.setRecordingActivityStartStopRecordingButton(value);
+ this.libService.updateRecordingActivityConfig({ startStopButton: value });
}
}
@@ -427,7 +425,7 @@ export class RecordingActivityViewRecordingsButtonDirective implements AfterView
}
private update(value: boolean) {
- this.libService.setRecordingActivityViewRecordingsButton(value);
+ this.libService.updateRecordingActivityConfig({ viewRecordingsButton: value });
}
}
@@ -473,6 +471,6 @@ export class RecordingActivityShowRecordingsListDirective implements AfterViewIn
}
private update(value: boolean) {
- this.libService.setRecordingActivityShowRecordingsList(value);
+ this.libService.updateRecordingActivityConfig({ showRecordingsList: value });
}
-}
\ No newline at end of file
+}
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/participant-panel-item.directive.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/participant-panel-item.directive.ts
index 26480467..31876575 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/participant-panel-item.directive.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/participant-panel-item.directive.ts
@@ -32,7 +32,10 @@ export class ParticipantPanelItemMuteButtonDirective implements AfterViewInit, O
muteValue: boolean = true;
- constructor(public elementRef: ElementRef, private libService: OpenViduComponentsConfigService) {}
+ constructor(
+ public elementRef: ElementRef,
+ private libService: OpenViduComponentsConfigService
+ ) {}
ngAfterViewInit() {
this.update(this.muteValue);
@@ -46,8 +49,6 @@ export class ParticipantPanelItemMuteButtonDirective implements AfterViewInit, O
}
update(value: boolean) {
- if (this.libService.showParticipantItemMuteButton() !== value) {
- this.libService.setParticipantItemMuteButton(value);
- }
+ this.libService.updateStreamConfig({ participantItemMuteButton: value });
}
-}
\ No newline at end of file
+}
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/stream.directive.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/stream.directive.ts
index 14040f6e..a0418699 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/stream.directive.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/stream.directive.ts
@@ -46,9 +46,7 @@ export class StreamDisplayParticipantNameDirective implements AfterViewInit, OnD
}
update(value: boolean) {
- if (this.libService.isParticipantNameDisplayed() !== value) {
- this.libService.setDisplayParticipantName(value);
- }
+ this.libService.updateStreamConfig({ displayParticipantName: value });
}
clear() {
@@ -100,9 +98,7 @@ export class StreamDisplayAudioDetectionDirective implements AfterViewInit, OnDe
}
update(value: boolean) {
- if (this.libService.isAudioDetectionDisplayed() !== value) {
- this.libService.setDisplayAudioDetection(value);
- }
+ this.libService.updateStreamConfig({ displayAudioDetection: value });
}
clear() {
this.update(true);
@@ -154,9 +150,7 @@ export class StreamVideoControlsDirective implements AfterViewInit, OnDestroy {
}
update(value: boolean) {
- if (this.libService.showStreamVideoControls() !== value) {
- this.libService.setStreamVideoControls(value);
- }
+ this.libService.updateStreamConfig({ videoControls: value });
}
clear() {
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/toolbar.directive.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/toolbar.directive.ts
index c7495a3f..d8a27b80 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/toolbar.directive.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/toolbar.directive.ts
@@ -62,9 +62,7 @@ export class ToolbarCameraButtonDirective implements AfterViewInit, OnDestroy {
}
private update(value: boolean) {
- if (this.libService.showCameraButton() !== value) {
- this.libService.setCameraButton(value);
- }
+ this.libService.updateToolbarConfig({ camera: value });
}
}
@@ -128,9 +126,7 @@ export class ToolbarMicrophoneButtonDirective implements AfterViewInit, OnDestro
}
private update(value: boolean) {
- if (this.libService.showMicrophoneButton() !== value) {
- this.libService.setMicrophoneButton(value);
- }
+ this.libService.updateToolbarConfig({ microphone: value });
}
}
@@ -194,9 +190,7 @@ export class ToolbarScreenshareButtonDirective implements AfterViewInit, OnDestr
}
private update(value: boolean) {
- if (this.libService.showScreenshareButton() !== value) {
- this.libService.setScreenshareButton(value);
- }
+ this.libService.updateToolbarConfig({ screenshare: value });
}
}
@@ -257,9 +251,7 @@ export class ToolbarRecordingButtonDirective implements AfterViewInit, OnDestroy
}
private update(value: boolean) {
- if (this.libService.showRecordingButton() !== value) {
- this.libService.setRecordingButton(value);
- }
+ this.libService.updateToolbarConfig({ recording: value });
}
}
@@ -321,9 +313,7 @@ export class ToolbarBroadcastingButtonDirective implements AfterViewInit, OnDest
}
private update(value: boolean) {
- if (this.libService.showBroadcastingButton() !== value) {
- this.libService.setBroadcastingButton(value);
- }
+ this.libService.setBroadcastingButton(value);
}
}
@@ -384,9 +374,7 @@ export class ToolbarFullscreenButtonDirective implements AfterViewInit, OnDestro
}
private update(value: boolean) {
- if (this.libService.showFullscreenButton() !== value) {
- this.libService.setFullscreenButton(value);
- }
+ this.libService.updateToolbarConfig({ fullscreen: value });
}
}
@@ -447,9 +435,7 @@ export class ToolbarBackgroundEffectsButtonDirective implements AfterViewInit, O
}
private update(value: boolean) {
- if (this.libService.showBackgroundEffectsButton() !== value) {
- this.libService.setBackgroundEffectsButton(value);
- }
+ this.libService.updateToolbarConfig({ backgroundEffects: value });
}
}
@@ -569,9 +555,7 @@ export class ToolbarSettingsButtonDirective implements AfterViewInit, OnDestroy
}
private update(value: boolean) {
- if (this.libService.showToolbarSettingsButton() !== value) {
- this.libService.setToolbarSettingsButton(value);
- }
+ this.libService.updateToolbarConfig({ settings: value });
}
}
@@ -633,9 +617,7 @@ export class ToolbarLeaveButtonDirective implements AfterViewInit, OnDestroy {
}
private update(value: boolean) {
- if (this.libService.showLeaveButton() !== value) {
- this.libService.setLeaveButton(value);
- }
+ this.libService.updateToolbarConfig({ leave: value });
}
}
@@ -698,9 +680,7 @@ export class ToolbarParticipantsPanelButtonDirective implements AfterViewInit, O
}
private update(value: boolean) {
- if (this.libService.showParticipantsPanelButton() !== value) {
- this.libService.setParticipantsPanelButton(value);
- }
+ this.libService.updateToolbarConfig({ participantsPanel: value });
}
}
@@ -761,9 +741,7 @@ export class ToolbarChatPanelButtonDirective implements AfterViewInit, OnDestroy
}
private update(value: boolean) {
- if (this.libService.showChatPanelButton() !== value) {
- this.libService.setChatPanelButton(value);
- }
+ this.libService.updateToolbarConfig({ chatPanel: value });
}
}
@@ -824,9 +802,7 @@ export class ToolbarActivitiesPanelButtonDirective implements AfterViewInit, OnD
}
private update(value: boolean) {
- if (this.libService.showActivitiesPanelButton() !== value) {
- this.libService.setActivitiesPanelButton(value);
- }
+ this.libService.updateToolbarConfig({ activitiesPanel: value });
}
}
@@ -888,9 +864,7 @@ export class ToolbarDisplayRoomNameDirective implements AfterViewInit, OnDestroy
}
private update(value: boolean) {
- if (this.libService.showRoomName() !== value) {
- this.libService.setDisplayRoomName(value);
- }
+ this.libService.updateToolbarConfig({ displayRoomName: value });
}
}
@@ -952,9 +926,7 @@ export class ToolbarDisplayLogoDirective implements AfterViewInit, OnDestroy {
}
private update(value: boolean) {
- if (this.libService.showLogo() !== value) {
- this.libService.setDisplayLogo(value);
- }
+ this.libService.updateToolbarConfig({ displayLogo: value });
}
}
@@ -1009,8 +981,6 @@ export class ToolbarAdditionalButtonsPossitionDirective implements AfterViewInit
}
private update(value: ToolbarAdditionalButtonsPosition) {
- if (this.libService.getToolbarAdditionalButtonsPosition() !== value) {
- this.libService.setToolbarAdditionalButtonsPosition(value);
- }
+ this.libService.updateToolbarConfig({ additionalButtonsPosition: value });
}
}
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/videoconference.directive.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/videoconference.directive.ts
index 48ff7022..4a8036b4 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/videoconference.directive.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/directives/api/videoconference.directive.ts
@@ -55,7 +55,7 @@ export class LivekitUrlDirective implements OnDestroy {
* @ignore
*/
update(value: string) {
- this.libService.setLivekitUrl(value);
+ this.libService.updateGeneralConfig({ livekitUrl: value });
}
}
@@ -108,7 +108,7 @@ export class TokenDirective implements OnDestroy {
* @ignore
*/
update(value: string) {
- this.libService.setToken(value);
+ this.libService.updateGeneralConfig({ token: value });
}
}
@@ -160,7 +160,7 @@ export class TokenErrorDirective implements OnDestroy {
* @ignore
*/
update(value: any) {
- this.libService.setTokenError(value);
+ this.libService.updateGeneralConfig({ tokenError: value });
}
}
@@ -212,9 +212,7 @@ export class MinimalDirective implements OnDestroy {
* @ignore
*/
update(value: boolean) {
- if (this.libService.isMinimal() !== value) {
- this.libService.setMinimal(value);
- }
+ this.libService.updateGeneralConfig({ minimal: value });
}
}
@@ -538,7 +536,7 @@ export class ParticipantNameDirective implements AfterViewInit, OnDestroy {
* @ignore
*/
update(value: string) {
- if (value) this.libService.setParticipantName(value);
+ if (value) this.libService.updateGeneralConfig({ participantName: value });
}
}
@@ -590,9 +588,7 @@ export class PrejoinDirective implements OnDestroy {
* @ignore
*/
update(value: boolean) {
- if (this.libService.showPrejoin() !== value) {
- this.libService.setPrejoin(value);
- }
+ this.libService.updateGeneralConfig({ prejoin: value });
}
}
@@ -663,7 +659,7 @@ export class VideoEnabledDirective implements OnDestroy {
// Ensure libService state is consistent with the final enabled state
if (this.libService.isVideoEnabled() !== finalEnabledState) {
- this.libService.setVideoEnabled(finalEnabledState);
+ this.libService.updateStreamConfig({ videoEnabled: finalEnabledState });
}
}
}
@@ -731,7 +727,7 @@ export class AudioEnabledDirective implements OnDestroy {
this.storageService.setMicrophoneEnabled(finalEnabledState);
if (this.libService.isAudioEnabled() !== enabled) {
- this.libService.setAudioEnabled(enabled);
+ this.libService.updateStreamConfig({ audioEnabled: enabled });
}
}
}
@@ -785,7 +781,7 @@ export class ShowDisconnectionDialogDirective implements OnDestroy {
*/
update(value: boolean) {
if (this.libService.getShowDisconnectionDialog() !== value) {
- this.libService.setShowDisconnectionDialog(value);
+ this.libService.updateGeneralConfig({ showDisconnectionDialog: value });
}
}
}
@@ -857,6 +853,6 @@ export class RecordingStreamBaseUrlDirective implements AfterViewInit, OnDestroy
* @ignore
*/
update(value: string) {
- if (value) this.libService.setRecordingStreamBaseUrl(value);
+ if (value) this.libService.updateGeneralConfig({ recordingStreamBaseUrl: value });
}
}
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/config/directive-config.service.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/config/directive-config.service.ts
index 95d3f0ed..a4e6bcff 100644
--- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/config/directive-config.service.ts
+++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/config/directive-config.service.ts
@@ -1,9 +1,101 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
+import { distinctUntilChanged, shareReplay, map } from 'rxjs/operators';
import { RecordingInfo } from '../../models/recording.model';
import { ToolbarAdditionalButtonsPosition } from '../../models/toolbar.model';
import { ParticipantModel } from '../../models/participant.model';
+/**
+ * Configuration item for the service
+ */
+interface ConfigItem {
+ subject: BehaviorSubject;
+ observable$: Observable;
+}
+
+/**
+ * Recording activity controls configuration
+ */
+interface RecordingControls {
+ play: boolean;
+ download: boolean;
+ delete: boolean;
+ externalView: boolean;
+}
+
+/**
+ * Toolbar configuration grouped by domain
+ */
+interface ToolbarConfig {
+ camera: boolean;
+ microphone: boolean;
+ screenshare: boolean;
+ fullscreen: boolean;
+ captions: boolean;
+ settings: boolean;
+ leave: boolean;
+ participantsPanel: boolean;
+ chatPanel: boolean;
+ activitiesPanel: boolean;
+ displayRoomName: boolean;
+ displayLogo: boolean;
+ backgroundEffects: boolean;
+ recording: boolean;
+ viewRecordings: boolean;
+ broadcasting: boolean;
+ brandingLogo: string;
+ additionalButtonsPosition: ToolbarAdditionalButtonsPosition;
+}
+
+/**
+ * Stream/Video configuration
+ */
+interface StreamConfig {
+ videoEnabled: boolean;
+ audioEnabled: boolean;
+ displayParticipantName: boolean;
+ displayAudioDetection: boolean;
+ videoControls: boolean;
+ participantItemMuteButton: boolean;
+}
+
+/**
+ * Recording activity configuration
+ */
+interface RecordingActivityConfig {
+ enabled: boolean;
+ readOnly: boolean;
+ showControls: RecordingControls;
+ startStopButton: boolean;
+ viewRecordingsButton: boolean;
+ showRecordingsList: boolean;
+}
+
+/**
+ * Admin dashboard configuration
+ */
+interface AdminConfig {
+ recordingsList: RecordingInfo[];
+ loginError: any;
+ loginTitle: string;
+ dashboardTitle: string;
+}
+
+/**
+ * General application configuration
+ */
+interface GeneralConfig {
+ token: string;
+ livekitUrl: string;
+ tokenError: any;
+ minimal: boolean;
+ participantName: string;
+ prejoin: boolean;
+ prejoinDisplayParticipantName: boolean;
+ showDisconnectionDialog: boolean;
+ recordingStreamBaseUrl: string;
+}
+
/**
* @internal
*/
@@ -11,530 +103,476 @@ import { ParticipantModel } from '../../models/participant.model';
providedIn: 'root'
})
export class OpenViduComponentsConfigService {
- private token = >new BehaviorSubject('');
- token$: Observable;
+ /**
+ * Helper method to create a configuration item with BehaviorSubject and Observable
+ */
+ private createConfigItem(initialValue: T): ConfigItem {
+ const subject = new BehaviorSubject(initialValue);
+ const observable$ = subject.asObservable().pipe(distinctUntilChanged(), shareReplay(1));
+ return { subject, observable$ };
+ }
- private livekitUrl = >new BehaviorSubject('');
- livekitUrl$: Observable;
+ /**
+ * Helper method for array configurations with optimized comparison
+ */
+ private createArrayConfigItem(initialValue: T[]): ConfigItem {
+ const subject = new BehaviorSubject(initialValue);
+ const observable$ = subject.asObservable().pipe(
+ distinctUntilChanged((prev, curr) => {
+ if (prev.length !== curr.length) return false;
+ return prev.every((item, index) => this.deepEqual(item, curr[index]));
+ }),
+ shareReplay(1)
+ );
+ return { subject, observable$ };
+ }
- private tokenError = >new BehaviorSubject(null);
- tokenError$: Observable;
- private minimal = >new BehaviorSubject(false);
- minimal$: Observable;
- private participantName = >new BehaviorSubject('');
- participantName$: Observable;
- private prejoin = >new BehaviorSubject(true);
- prejoin$: Observable;
- private prejoinDisplayParticipantName = >new BehaviorSubject(true);
- prejoinDisplayParticipantName$: Observable;
+ /**
+ * Helper method for RecordingControls with specific comparison
+ */
+ private createRecordingControlsConfigItem(initialValue: RecordingControls): ConfigItem {
+ const subject = new BehaviorSubject(initialValue);
+ const observable$ = subject.asObservable().pipe(
+ distinctUntilChanged(
+ (prev, curr) =>
+ prev.play === curr.play &&
+ prev.download === curr.download &&
+ prev.delete === curr.delete &&
+ prev.externalView === curr.externalView
+ ),
+ shareReplay(1)
+ );
+ return { subject, observable$ };
+ }
- private videoEnabled = >new BehaviorSubject(true);
- videoEnabled$: Observable;
- private audioEnabled = >new BehaviorSubject(true);
- audioEnabled$: Observable;
+ /**
+ * Helper method for ToolbarConfig with specific comparison
+ */
+ private createToolbarConfigItem(initialValue: ToolbarConfig): ConfigItem {
+ const subject = new BehaviorSubject(initialValue);
+ const observable$ = subject.asObservable().pipe(
+ distinctUntilChanged((prev, curr) => this.compareToolbarConfig(prev, curr)),
+ shareReplay(1)
+ );
+ return { subject, observable$ };
+ }
- private showDisconnectionDialog = >new BehaviorSubject(true);
- showDisconnectionDialog$: Observable;
+ /**
+ * Helper method for StreamConfig with specific comparison
+ */
+ private createStreamConfigItem(initialValue: StreamConfig): ConfigItem {
+ const subject = new BehaviorSubject(initialValue);
+ const observable$ = subject.asObservable().pipe(
+ distinctUntilChanged((prev, curr) => this.compareStreamConfig(prev, curr)),
+ shareReplay(1)
+ );
+ return { subject, observable$ };
+ }
- private recordingStreamBaseUrl = >new BehaviorSubject('call/api/recordings');
- recordingStreamBaseUrl$: Observable;
+ /**
+ * Helper method for RecordingActivityConfig with specific comparison
+ */
+ private createRecordingActivityConfigItem(initialValue: RecordingActivityConfig): ConfigItem {
+ const subject = new BehaviorSubject(initialValue);
+ const observable$ = subject.asObservable().pipe(
+ distinctUntilChanged((prev, curr) => this.compareRecordingActivityConfig(prev, curr)),
+ shareReplay(1)
+ );
+ return { subject, observable$ };
+ }
- //Toolbar settings
- private cameraButton = >new BehaviorSubject(true);
- cameraButton$: Observable;
+ /**
+ * Helper method for AdminConfig with specific comparison
+ */
+ private createAdminConfigItem(initialValue: AdminConfig): ConfigItem {
+ const subject = new BehaviorSubject(initialValue);
+ const observable$ = subject.asObservable().pipe(
+ distinctUntilChanged((prev, curr) => this.compareAdminConfig(prev, curr)),
+ shareReplay(1)
+ );
+ return { subject, observable$ };
+ }
- private microphoneButton = >new BehaviorSubject(true);
- microphoneButton$: Observable;
+ /**
+ * Helper method for GeneralConfig with specific comparison
+ */
+ private createGeneralConfigItem(initialValue: GeneralConfig): ConfigItem {
+ const subject = new BehaviorSubject(initialValue);
+ const observable$ = subject.asObservable().pipe(
+ distinctUntilChanged((prev, curr) => this.compareGeneralConfig(prev, curr)),
+ shareReplay(1)
+ );
+ return { subject, observable$ };
+ }
- private screenshareButton = >new BehaviorSubject(true);
- screenshareButton$: Observable;
+ /**
+ * Optimized deep equality check
+ */
+ private deepEqual(a: any, b: any): boolean {
+ if (a === b) return true;
+ if (a == null || b == null) return a === b;
+ if (typeof a !== typeof b) return false;
+ if (typeof a !== 'object') return a === b;
- private fullscreenButton = >new BehaviorSubject(true);
- fullscreenButton$: Observable;
+ const keysA = Object.keys(a);
+ const keysB = Object.keys(b);
+ if (keysA.length !== keysB.length) return false;
- private captionsButton = >new BehaviorSubject(true);
- captionsButton$: Observable;
+ return keysA.every((key) => this.deepEqual(a[key], b[key]));
+ }
- private toolbarSettingsButton = >new BehaviorSubject(true);
- toolbarSettingsButton$: Observable;
+ /**
+ * Compare ToolbarConfig efficiently
+ */
+ private compareToolbarConfig(prev: ToolbarConfig, curr: ToolbarConfig): boolean {
+ return (
+ prev.camera === curr.camera &&
+ prev.microphone === curr.microphone &&
+ prev.screenshare === curr.screenshare &&
+ prev.fullscreen === curr.fullscreen &&
+ prev.captions === curr.captions &&
+ prev.settings === curr.settings &&
+ prev.leave === curr.leave &&
+ prev.participantsPanel === curr.participantsPanel &&
+ prev.chatPanel === curr.chatPanel &&
+ prev.activitiesPanel === curr.activitiesPanel &&
+ prev.displayRoomName === curr.displayRoomName &&
+ prev.displayLogo === curr.displayLogo &&
+ prev.backgroundEffects === curr.backgroundEffects &&
+ prev.recording === curr.recording &&
+ prev.viewRecordings === curr.viewRecordings &&
+ prev.broadcasting === curr.broadcasting &&
+ prev.brandingLogo === curr.brandingLogo &&
+ prev.additionalButtonsPosition === curr.additionalButtonsPosition
+ );
+ }
- private leaveButton = >new BehaviorSubject(true);
- leaveButton$: Observable;
+ /**
+ * Compare StreamConfig efficiently
+ */
+ private compareStreamConfig(prev: StreamConfig, curr: StreamConfig): boolean {
+ return (
+ prev.videoEnabled === curr.videoEnabled &&
+ prev.audioEnabled === curr.audioEnabled &&
+ prev.displayParticipantName === curr.displayParticipantName &&
+ prev.displayAudioDetection === curr.displayAudioDetection &&
+ prev.videoControls === curr.videoControls &&
+ prev.participantItemMuteButton === curr.participantItemMuteButton
+ );
+ }
- private participantsPanelButton = >new BehaviorSubject(true);
- participantsPanelButton$: Observable;
+ /**
+ * Compare RecordingActivityConfig efficiently
+ */
+ private compareRecordingActivityConfig(prev: RecordingActivityConfig, curr: RecordingActivityConfig): boolean {
+ return (
+ prev.enabled === curr.enabled &&
+ prev.readOnly === curr.readOnly &&
+ prev.startStopButton === curr.startStopButton &&
+ prev.viewRecordingsButton === curr.viewRecordingsButton &&
+ prev.showRecordingsList === curr.showRecordingsList &&
+ prev.showControls.play === curr.showControls.play &&
+ prev.showControls.download === curr.showControls.download &&
+ prev.showControls.delete === curr.showControls.delete &&
+ prev.showControls.externalView === curr.showControls.externalView
+ );
+ }
- private chatPanelButton = >new BehaviorSubject(true);
- chatPanelButton$: Observable;
+ /**
+ * Compare AdminConfig efficiently
+ */
+ private compareAdminConfig(prev: AdminConfig, curr: AdminConfig): boolean {
+ return (
+ prev.loginError === curr.loginError &&
+ prev.loginTitle === curr.loginTitle &&
+ prev.dashboardTitle === curr.dashboardTitle &&
+ prev.recordingsList.length === curr.recordingsList.length &&
+ prev.recordingsList.every((item, index) => this.deepEqual(item, curr.recordingsList[index]))
+ );
+ }
- private activitiesPanelButton = >new BehaviorSubject(true);
- activitiesPanelButton$: Observable;
+ /**
+ * Compare GeneralConfig efficiently
+ */
+ private compareGeneralConfig(prev: GeneralConfig, curr: GeneralConfig): boolean {
+ return (
+ prev.token === curr.token &&
+ prev.livekitUrl === curr.livekitUrl &&
+ prev.tokenError === curr.tokenError &&
+ prev.minimal === curr.minimal &&
+ prev.participantName === curr.participantName &&
+ prev.prejoin === curr.prejoin &&
+ prev.prejoinDisplayParticipantName === curr.prejoinDisplayParticipantName &&
+ prev.showDisconnectionDialog === curr.showDisconnectionDialog &&
+ prev.recordingStreamBaseUrl === curr.recordingStreamBaseUrl
+ );
+ }
- private displayRoomName = >new BehaviorSubject(true);
- displayRoomName$: Observable;
+ // Grouped configuration items by domain
+ private generalConfig = this.createGeneralConfigItem({
+ token: '',
+ livekitUrl: '',
+ tokenError: null,
+ minimal: false,
+ participantName: '',
+ prejoin: true,
+ prejoinDisplayParticipantName: true,
+ showDisconnectionDialog: true,
+ recordingStreamBaseUrl: 'call/api/recordings'
+ });
- private brandingLogo = >new BehaviorSubject('');
- brandingLogo$: Observable;
+ private toolbarConfig = this.createToolbarConfigItem({
+ camera: true,
+ microphone: true,
+ screenshare: true,
+ fullscreen: true,
+ captions: true,
+ settings: true,
+ leave: true,
+ participantsPanel: true,
+ chatPanel: true,
+ activitiesPanel: true,
+ displayRoomName: true,
+ displayLogo: true,
+ backgroundEffects: true,
+ recording: true,
+ viewRecordings: false,
+ broadcasting: true,
+ brandingLogo: '',
+ additionalButtonsPosition: ToolbarAdditionalButtonsPosition.AFTER_MENU
+ });
- private displayLogo = >new BehaviorSubject(true);
- displayLogo$: Observable;
+ private streamConfig = this.createStreamConfigItem({
+ videoEnabled: true,
+ audioEnabled: true,
+ displayParticipantName: true,
+ displayAudioDetection: true,
+ videoControls: true,
+ participantItemMuteButton: true
+ });
- private toolbarAdditionalButtonsPosition = >(
- new BehaviorSubject(ToolbarAdditionalButtonsPosition.AFTER_MENU)
+ private recordingActivityConfig = this.createRecordingActivityConfigItem({
+ enabled: true,
+ readOnly: false,
+ showControls: {
+ play: true,
+ download: true,
+ delete: true,
+ externalView: false
+ },
+ startStopButton: true,
+ viewRecordingsButton: false,
+ showRecordingsList: false
+ });
+
+ private adminConfig = this.createAdminConfigItem({
+ recordingsList: [],
+ loginError: null,
+ loginTitle: '',
+ dashboardTitle: ''
+ });
+
+ // Individual configs that don't fit into groups
+ private broadcastingActivityConfig = this.createConfigItem(true);
+ private layoutRemoteParticipantsConfig = this.createConfigItem(undefined);
+
+ // General observables
+ token$: Observable = this.generalConfig.observable$.pipe(map((config) => config.token));
+ livekitUrl$: Observable = this.generalConfig.observable$.pipe(map((config) => config.livekitUrl));
+ tokenError$: Observable = this.generalConfig.observable$.pipe(map((config) => config.tokenError));
+ minimal$: Observable = this.generalConfig.observable$.pipe(map((config) => config.minimal));
+ participantName$: Observable = this.generalConfig.observable$.pipe(map((config) => config.participantName));
+ prejoin$: Observable = this.generalConfig.observable$.pipe(map((config) => config.prejoin));
+ prejoinDisplayParticipantName$: Observable = this.generalConfig.observable$.pipe(
+ map((config) => config.prejoinDisplayParticipantName)
);
- toolbarAdditionalButtonsPosition$: Observable;
+ showDisconnectionDialog$: Observable = this.generalConfig.observable$.pipe(map((config) => config.showDisconnectionDialog));
+ recordingStreamBaseUrl$: Observable = this.generalConfig.observable$.pipe(map((config) => config.recordingStreamBaseUrl));
- private displayParticipantName = >new BehaviorSubject(true);
- displayParticipantName$: Observable;
- private displayAudioDetection = >new BehaviorSubject(true);
- displayAudioDetection$: Observable;
- private streamVideoControls = >new BehaviorSubject(true);
- streamVideoControls$: Observable;
- private participantItemMuteButton = >new BehaviorSubject(true);
- participantItemMuteButton$: Observable;
- private backgroundEffectsButton = >new BehaviorSubject(true);
- backgroundEffectsButton$: Observable;
- private recordingButton = >new BehaviorSubject(true);
- recordingButton$: Observable;
- private toolbarViewRecordingsButton = >new BehaviorSubject(false);
- toolbarViewRecordingsButton$: Observable;
- private broadcastingButton = >new BehaviorSubject(true);
- broadcastingButton$: Observable;
- private recordingActivity = >new BehaviorSubject(true);
- recordingActivity$: Observable;
- private broadcastingActivity = >new BehaviorSubject(true);
- broadcastingActivity$: Observable;
+ // Stream observables
+ videoEnabled$: Observable = this.streamConfig.observable$.pipe(map((config) => config.videoEnabled));
+ audioEnabled$: Observable = this.streamConfig.observable$.pipe(map((config) => config.audioEnabled));
+ displayParticipantName$: Observable = this.streamConfig.observable$.pipe(map((config) => config.displayParticipantName));
+ displayAudioDetection$: Observable = this.streamConfig.observable$.pipe(map((config) => config.displayAudioDetection));
+ streamVideoControls$: Observable = this.streamConfig.observable$.pipe(map((config) => config.videoControls));
+ participantItemMuteButton$: Observable = this.streamConfig.observable$.pipe(map((config) => config.participantItemMuteButton));
- // Recording activity configuration
- private recordingActivityReadOnly = >new BehaviorSubject(false);
- recordingActivityReadOnly$: Observable;
- private recordingActivityShowControls = >(
- new BehaviorSubject({ play: true, download: true, delete: true })
+ // Toolbar observables
+ cameraButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.camera));
+ microphoneButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.microphone));
+ screenshareButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.screenshare));
+ fullscreenButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.fullscreen));
+ captionsButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.captions));
+ toolbarSettingsButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.settings));
+ leaveButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.leave));
+ participantsPanelButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.participantsPanel));
+ chatPanelButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.chatPanel));
+ activitiesPanelButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.activitiesPanel));
+ displayRoomName$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.displayRoomName));
+ brandingLogo$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.brandingLogo));
+ displayLogo$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.displayLogo));
+ toolbarAdditionalButtonsPosition$: Observable = this.toolbarConfig.observable$.pipe(
+ map((config) => config.additionalButtonsPosition)
);
- recordingActivityShowControls$: Observable<{ play?: boolean; download?: boolean; delete?: boolean; externalView?: boolean }>;
+ backgroundEffectsButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.backgroundEffects));
+ recordingButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.recording));
+ toolbarViewRecordingsButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.viewRecordings));
+ broadcastingButton$: Observable = this.toolbarConfig.observable$.pipe(map((config) => config.broadcasting));
- private recordingActivityStartStopRecordingButton = >new BehaviorSubject(true);
- recordingActivityStartStopRecordingButton$: Observable;
+ // Recording activity observables
+ recordingActivity$: Observable = this.recordingActivityConfig.observable$.pipe(map((config) => config.enabled));
+ recordingActivityReadOnly$: Observable = this.recordingActivityConfig.observable$.pipe(map((config) => config.readOnly));
+ recordingActivityShowControls$: Observable = this.recordingActivityConfig.observable$.pipe(
+ map((config) => config.showControls)
+ );
+ recordingActivityStartStopRecordingButton$: Observable = this.recordingActivityConfig.observable$.pipe(
+ map((config) => config.startStopButton)
+ );
+ recordingActivityViewRecordingsButton$: Observable = this.recordingActivityConfig.observable$.pipe(
+ map((config) => config.viewRecordingsButton)
+ );
+ recordingActivityShowRecordingsList$: Observable = this.recordingActivityConfig.observable$.pipe(
+ map((config) => config.showRecordingsList)
+ );
- private recordingActivityViewRecordingsButton = >new BehaviorSubject(false);
- recordingActivityViewRecordingsButton$: Observable;
+ // Admin observables
+ adminRecordingsList$: Observable = this.adminConfig.observable$.pipe(map((config) => config.recordingsList));
+ adminLoginError$: Observable = this.adminConfig.observable$.pipe(map((config) => config.loginError));
+ adminLoginTitle$: Observable = this.adminConfig.observable$.pipe(map((config) => config.loginTitle));
+ adminDashboardTitle$: Observable = this.adminConfig.observable$.pipe(map((config) => config.dashboardTitle));
- private recordingActivityShowRecordingsList = >new BehaviorSubject(false);
- recordingActivityShowRecordingsList$: Observable;
- // Admin
- private adminRecordingsList: BehaviorSubject = new BehaviorSubject([]);
- adminRecordingsList$: Observable;
- private adminLoginError = >new BehaviorSubject(null);
- private adminLoginTitle = >new BehaviorSubject('');
- private adminDashboardTitle = >new BehaviorSubject('');
- adminLoginTitle$: Observable;
- adminDashboardTitle$: Observable;
- adminLoginError$: Observable;
-
- // Internals
- private layoutRemoteParticipants: BehaviorSubject = new BehaviorSubject(undefined);
- layoutRemoteParticipants$: Observable;
+ // Individual observables that don't fit into groups
+ broadcastingActivity$: Observable = this.broadcastingActivityConfig.observable$;
+ layoutRemoteParticipants$: Observable = this.layoutRemoteParticipantsConfig.observable$;
constructor() {
- this.token$ = this.token.asObservable();
- this.livekitUrl$ = this.livekitUrl.asObservable();
- this.tokenError$ = this.tokenError.asObservable();
- this.minimal$ = this.minimal.asObservable();
- this.participantName$ = this.participantName.asObservable();
- this.prejoin$ = this.prejoin.asObservable();
- this.prejoinDisplayParticipantName$ = this.prejoinDisplayParticipantName.asObservable();
- this.videoEnabled$ = this.videoEnabled.asObservable();
- this.audioEnabled$ = this.audioEnabled.asObservable();
- this.recordingStreamBaseUrl$ = this.recordingStreamBaseUrl.asObservable();
- //Toolbar observables
- this.cameraButton$ = this.cameraButton.asObservable();
- this.microphoneButton$ = this.microphoneButton.asObservable();
- this.screenshareButton$ = this.screenshareButton.asObservable();
- this.fullscreenButton$ = this.fullscreenButton.asObservable();
- this.backgroundEffectsButton$ = this.backgroundEffectsButton.asObservable();
- this.leaveButton$ = this.leaveButton.asObservable();
- this.participantsPanelButton$ = this.participantsPanelButton.asObservable();
- this.chatPanelButton$ = this.chatPanelButton.asObservable();
- this.activitiesPanelButton$ = this.activitiesPanelButton.asObservable();
- this.displayRoomName$ = this.displayRoomName.asObservable();
- this.displayLogo$ = this.displayLogo.asObservable();
- this.brandingLogo$ = this.brandingLogo.asObservable();
- this.recordingButton$ = this.recordingButton.asObservable();
- this.toolbarViewRecordingsButton$ = this.toolbarViewRecordingsButton.asObservable();
- this.broadcastingButton$ = this.broadcastingButton.asObservable();
- this.toolbarSettingsButton$ = this.toolbarSettingsButton.asObservable();
- this.captionsButton$ = this.captionsButton.asObservable();
- this.toolbarAdditionalButtonsPosition$ = this.toolbarAdditionalButtonsPosition.asObservable();
- //Stream observables
- this.displayParticipantName$ = this.displayParticipantName.asObservable();
- this.displayAudioDetection$ = this.displayAudioDetection.asObservable();
- this.streamVideoControls$ = this.streamVideoControls.asObservable();
- // Participant item observables
- this.participantItemMuteButton$ = this.participantItemMuteButton.asObservable();
- // Recording activity observables
- this.recordingActivity$ = this.recordingActivity.asObservable();
- this.recordingActivityReadOnly$ = this.recordingActivityReadOnly.asObservable();
- this.recordingActivityShowControls$ = this.recordingActivityShowControls.asObservable();
- this.recordingActivityStartStopRecordingButton$ = this.recordingActivityStartStopRecordingButton.asObservable();
- this.recordingActivityViewRecordingsButton$ = this.recordingActivityViewRecordingsButton.asObservable();
- this.recordingActivityShowRecordingsList$ = this.recordingActivityShowRecordingsList.asObservable();
- // Broadcasting activity
- this.broadcastingActivity$ = this.broadcastingActivity.asObservable();
- // Admin dashboard
- this.adminRecordingsList$ = this.adminRecordingsList.asObservable();
- this.adminLoginError$ = this.adminLoginError.asObservable();
- this.adminLoginTitle$ = this.adminLoginTitle.asObservable();
- this.adminDashboardTitle$ = this.adminDashboardTitle.asObservable();
- // Internals
- this.layoutRemoteParticipants$ = this.layoutRemoteParticipants.asObservable();
+ // Constructor no longer needed - all observables are initialized directly
}
- setToken(token: string) {
- this.token.next(token);
+ // ============================================
+ // BATCH UPDATE METHODS
+ // ============================================
+
+ /**
+ * Update multiple general configuration properties at once
+ */
+ updateGeneralConfig(partialConfig: Partial): void {
+ const current = this.generalConfig.subject.getValue();
+ this.generalConfig.subject.next({ ...current, ...partialConfig });
}
- setLivekitUrl(livekitUrl: string) {
- this.livekitUrl.next(livekitUrl);
+ /**
+ * Update multiple toolbar configuration properties at once
+ */
+ updateToolbarConfig(partialConfig: Partial): void {
+ const current = this.toolbarConfig.subject.getValue();
+ this.toolbarConfig.subject.next({ ...current, ...partialConfig });
}
+ /**
+ * Update multiple stream configuration properties at once
+ */
+ updateStreamConfig(partialConfig: Partial): void {
+ const current = this.streamConfig.subject.getValue();
+ this.streamConfig.subject.next({ ...current, ...partialConfig });
+ }
+
+ /**
+ * Update multiple recording activity configuration properties at once
+ */
+ updateRecordingActivityConfig(partialConfig: Partial): void {
+ const current = this.recordingActivityConfig.subject.getValue();
+ this.recordingActivityConfig.subject.next({ ...current, ...partialConfig });
+ }
+
+ /**
+ * Update multiple admin configuration properties at once
+ */
+ updateAdminConfig(partialConfig: Partial): void {
+ const current = this.adminConfig.subject.getValue();
+ this.adminConfig.subject.next({ ...current, ...partialConfig });
+ }
+
+ /**
+ * Update recording controls specifically with batch support
+ */
+ updateRecordingControls(partialControls: Partial): void {
+ const current = this.recordingActivityConfig.subject.getValue();
+ const updatedControls = { ...current.showControls, ...partialControls };
+ this.updateRecordingActivityConfig({ showControls: updatedControls });
+ }
+
+ // ============================================
+ // DIRECT ACCESS METHODS (for internal use)
+ // ============================================
+
+ /**
+ * @internal
+ * Get current participant name directly
+ */
+ getCurrentParticipantName(): string {
+ return this.generalConfig.subject.getValue().participantName;
+ }
+
+ // ============================================
+ // INDIVIDUAL GETTER/SETTER METHODS
+ // ============================================
+
+ // General configuration methods
+
getLivekitUrl(): string {
- return this.livekitUrl.getValue();
- }
-
- setTokenError(error: any) {
- this.tokenError.next(error);
- }
-
- setMinimal(minimal: boolean) {
- this.minimal.next(minimal);
- }
-
- isMinimal(): boolean {
- return this.minimal.getValue();
- }
-
- setParticipantName(participantName: string) {
- this.participantName.next(participantName);
- }
-
- setPrejoin(prejoin: boolean) {
- this.prejoin.next(prejoin);
- }
-
- setPrejoinDisplayParticipantName(prejoinDisplayParticipantName: boolean) {
- this.prejoinDisplayParticipantName.next(prejoinDisplayParticipantName);
+ return this.generalConfig.subject.getValue().livekitUrl;
}
showPrejoin(): boolean {
- return this.prejoin.getValue();
- }
-
- setVideoEnabled(videoEnabled: boolean) {
- this.videoEnabled.next(videoEnabled);
- }
-
- isVideoEnabled(): boolean {
- return this.videoEnabled.getValue();
- }
-
- setAudioEnabled(audioEnabled: boolean) {
- this.audioEnabled.next(audioEnabled);
- }
-
- isAudioEnabled(): boolean {
- return this.audioEnabled.getValue();
+ return this.generalConfig.subject.getValue().prejoin;
}
getShowDisconnectionDialog(): boolean {
- return this.showDisconnectionDialog.getValue();
- }
-
- setShowDisconnectionDialog(showDisconnectionDialog: boolean) {
- this.showDisconnectionDialog.next(showDisconnectionDialog);
- }
-
- setRecordingStreamBaseUrl(recordingStreamBaseUrl: string) {
- this.recordingStreamBaseUrl.next(recordingStreamBaseUrl);
+ return this.generalConfig.subject.getValue().showDisconnectionDialog;
}
getRecordingStreamBaseUrl(): string {
- let baseUrl = this.recordingStreamBaseUrl.getValue();
+ let baseUrl = this.generalConfig.subject.getValue().recordingStreamBaseUrl;
// Add trailing slash if not present
baseUrl += baseUrl.endsWith('/') ? '' : '/';
return baseUrl;
}
- //Toolbar settings
+ // Stream configuration methods
- setCameraButton(cameraButton: boolean) {
- this.cameraButton.next(cameraButton);
+ isVideoEnabled(): boolean {
+ return this.streamConfig.subject.getValue().videoEnabled;
}
- showCameraButton(): boolean {
- return this.cameraButton.getValue();
+ isAudioEnabled(): boolean {
+ return this.streamConfig.subject.getValue().audioEnabled;
}
- setMicrophoneButton(microphoneButton: boolean) {
- this.microphoneButton.next(microphoneButton);
- }
-
- showMicrophoneButton(): boolean {
- return this.microphoneButton.getValue();
- }
-
- setScreenshareButton(screenshareButton: boolean) {
- this.screenshareButton.next(screenshareButton);
- }
-
- showScreenshareButton(): boolean {
- return this.screenshareButton.getValue();
- }
-
- setFullscreenButton(fullscreenButton: boolean) {
- this.fullscreenButton.next(fullscreenButton);
- }
-
- showFullscreenButton(): boolean {
- return this.fullscreenButton.getValue();
- }
-
- setCaptionsButton(captionsButton: boolean) {
- this.captionsButton.next(captionsButton);
- }
-
- showCaptionsButton(): boolean {
- return this.captionsButton.getValue();
- }
-
- setToolbarSettingsButton(toolbarSettingsButton: boolean) {
- this.toolbarSettingsButton.next(toolbarSettingsButton);
- }
-
- showToolbarSettingsButton(): boolean {
- return this.toolbarSettingsButton.getValue();
- }
-
- setLeaveButton(leaveButton: boolean) {
- this.leaveButton.next(leaveButton);
- }
-
- showLeaveButton(): boolean {
- return this.leaveButton.getValue();
- }
-
- setParticipantsPanelButton(participantsPanelButton: boolean) {
- this.participantsPanelButton.next(participantsPanelButton);
- }
-
- showParticipantsPanelButton(): boolean {
- return this.participantsPanelButton.getValue();
- }
-
- setChatPanelButton(chatPanelButton: boolean) {
- this.chatPanelButton.next(chatPanelButton);
- }
-
- showChatPanelButton(): boolean {
- return this.chatPanelButton.getValue();
- }
-
- setActivitiesPanelButton(activitiesPanelButton: boolean) {
- this.activitiesPanelButton.next(activitiesPanelButton);
- }
-
- showActivitiesPanelButton(): boolean {
- return this.activitiesPanelButton.getValue();
- }
-
- setDisplayRoomName(displayRoomName: boolean) {
- this.displayRoomName.next(displayRoomName);
- }
-
- setBrandingLogo(brandingLogo: string) {
- this.brandingLogo.next(brandingLogo);
- }
-
- showRoomName(): boolean {
- return this.displayRoomName.getValue();
- }
-
- setDisplayLogo(displayLogo: boolean) {
- this.displayLogo.next(displayLogo);
- }
-
- showLogo(): boolean {
- return this.displayLogo.getValue();
- }
- getToolbarAdditionalButtonsPosition(): ToolbarAdditionalButtonsPosition {
- return this.toolbarAdditionalButtonsPosition.getValue();
- }
-
- setToolbarAdditionalButtonsPosition(toolbarAdditionalButtonsPosition: ToolbarAdditionalButtonsPosition) {
- this.toolbarAdditionalButtonsPosition.next(toolbarAdditionalButtonsPosition);
- }
-
- setRecordingButton(recordingButton: boolean) {
- this.recordingButton.next(recordingButton);
- }
-
- showRecordingButton(): boolean {
- return this.recordingButton.getValue();
- }
-
- setToolbarViewRecordingsButton(toolbarViewRecordingsButton: boolean) {
- this.toolbarViewRecordingsButton.next(toolbarViewRecordingsButton);
- }
-
- getToolbarViewRecordingsButton(): boolean {
- return this.toolbarViewRecordingsButton.getValue();
- }
-
- showToolbarViewRecordingsButton(): boolean {
- return this.getToolbarViewRecordingsButton();
- }
+ // Toolbar configuration methods
setBroadcastingButton(broadcastingButton: boolean) {
- this.broadcastingButton.next(broadcastingButton);
- }
-
- showBroadcastingButton(): boolean {
- return this.broadcastingButton.getValue();
- }
-
- setRecordingActivity(recordingActivity: boolean) {
- this.recordingActivity.next(recordingActivity);
- }
-
- showRecordingActivity(): boolean {
- return this.recordingActivity.getValue();
- }
-
- setBroadcastingActivity(broadcastingActivity: boolean) {
- this.broadcastingActivity.next(broadcastingActivity);
- }
-
- showBroadcastingActivity(): boolean {
- return this.broadcastingActivity.getValue();
- }
-
- //Stream settings
- setDisplayParticipantName(displayParticipantName: boolean) {
- this.displayParticipantName.next(displayParticipantName);
- }
-
- isParticipantNameDisplayed(): boolean {
- return this.displayParticipantName.getValue();
- }
-
- setDisplayAudioDetection(displayAudioDetection: boolean) {
- this.displayAudioDetection.next(displayAudioDetection);
- }
-
- isAudioDetectionDisplayed(): boolean {
- return this.displayAudioDetection.getValue();
- }
-
- setStreamVideoControls(streamVideoControls: boolean) {
- this.streamVideoControls.next(streamVideoControls);
- }
-
- showStreamVideoControls(): boolean {
- return this.streamVideoControls.getValue();
- }
-
- setParticipantItemMuteButton(participantItemMuteButton: boolean) {
- this.participantItemMuteButton.next(participantItemMuteButton);
- }
-
- showParticipantItemMuteButton(): boolean {
- return this.participantItemMuteButton.getValue();
- }
-
- setBackgroundEffectsButton(backgroundEffectsButton: boolean) {
- this.backgroundEffectsButton.next(backgroundEffectsButton);
+ this.updateToolbarConfig({ broadcasting: broadcastingButton });
}
showBackgroundEffectsButton(): boolean {
- return this.backgroundEffectsButton.getValue();
+ return this.toolbarConfig.subject.getValue().backgroundEffects;
}
- // Admin dashboard
+ // Activity methods (these remain individual as they don't fit cleanly into toolbar config)
- setAdminRecordingsList(adminRecordingsList: RecordingInfo[]) {
- this.adminRecordingsList.next(adminRecordingsList);
- }
-
- getAdminRecordingsList(): RecordingInfo[] {
- return this.adminRecordingsList.getValue();
- }
-
- setAdminLoginError(adminLoginError: any) {
- this.adminLoginError.next(adminLoginError);
- }
-
- getAdminLoginError(): any {
- return this.adminLoginError.getValue();
- }
-
- getAdminLoginTitle(): string {
- return this.adminLoginTitle.getValue();
- }
-
- setAdminLoginTitle(title: string) {
- this.adminLoginTitle.next(title);
- }
-
- getAdminDashboardTitle(): string {
- return this.adminDashboardTitle.getValue();
- }
-
- setAdminDashboardTitle(title: string) {
- this.adminDashboardTitle.next(title);
- }
-
- isRecordingEnabled(): boolean {
- return this.recordingButton.getValue() && this.recordingActivity.getValue();
- }
-
- isBroadcastingEnabled(): boolean {
- return this.broadcastingButton.getValue() && this.broadcastingActivity.getValue();
+ setBroadcastingActivity(broadcastingActivity: boolean) {
+ this.broadcastingActivityConfig.subject.next(broadcastingActivity);
}
// Internals
setLayoutRemoteParticipants(participants: ParticipantModel[] | undefined) {
- this.layoutRemoteParticipants.next(participants);
+ this.layoutRemoteParticipantsConfig.subject.next(participants);
}
- // Recording Activity Configuration
- setRecordingActivityReadOnly(readOnly: boolean) {
- this.recordingActivityReadOnly.next(readOnly);
- }
-
- isRecordingActivityReadOnly(): boolean {
- return this.recordingActivityReadOnly.getValue();
- }
-
- setRecordingActivityShowControls(controls: { play?: boolean; download?: boolean; delete?: boolean }) {
- this.recordingActivityShowControls.next(controls);
- }
-
- getRecordingActivityShowControls(): { play?: boolean; download?: boolean; delete?: boolean } {
- return this.recordingActivityShowControls.getValue();
- }
-
- setRecordingActivityStartStopRecordingButton(show: boolean) {
- this.recordingActivityStartStopRecordingButton.next(show);
- }
-
- setRecordingActivityViewRecordingsButton(show: boolean) {
- this.recordingActivityViewRecordingsButton.next(show);
- }
-
- setRecordingActivityShowRecordingsList(show: boolean) {
- this.recordingActivityShowRecordingsList.next(show);
- }
+ // Recording Activity Configuration methods
showRecordingActivityRecordingsList(): boolean {
- return this.recordingActivityShowRecordingsList.getValue();
+ return this.recordingActivityConfig.subject.getValue().showRecordingsList;
}
-}
+}
\ No newline at end of file