ov-components: Refactor prejoin logic and improve observable handling in configuration service

master
Carlos Santos 2025-08-21 19:01:51 +02:00
parent bcbf24b84d
commit 1762c43769
5 changed files with 87 additions and 46 deletions

View File

@ -10,7 +10,7 @@ import {
Output
} from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { filter, Subject, takeUntil, tap } from 'rxjs';
import { filter, Subject, take, takeUntil, tap } from 'rxjs';
import { ILogger } from '../../models/logger.model';
import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service';
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
@ -175,15 +175,12 @@ export class PreJoinComponent implements OnInit, OnDestroy {
if (this.participantName?.trim()) {
this.libService.updateGeneralConfig({ participantName: this.participantName.trim() });
// Wait for the next tick to ensure the participant name propagates
// through the observable before emitting onReadyToJoin
this.libService.participantName$
.pipe(
takeUntil(this.destroy$),
filter((name) => name === this.participantName?.trim()),
tap(() => this.onReadyToJoin.emit())
take(1)
)
.subscribe();
.subscribe(() => this.onReadyToJoin.emit());
} else {
// No participant name to set, emit immediately
this.onReadyToJoin.emit();

View File

@ -8,7 +8,7 @@
[(ngModel)]="name"
autocomplete="off"
[disabled]="!isPrejoinPage"
(change)="updateName()"
(input)="updateName()"
(keypress)="eventKeyPress($event)"
[placeholder]="'PREJOIN.NICKNAME' | translate"
class="name-input-field"

View File

@ -836,7 +836,6 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
*/
_onReadyToJoin(): void {
this.log.d('Ready to join - initializing room and handling prejoin flow');
try {
// Mark that user has initiated the join process
this.updateComponentState({
@ -921,15 +920,7 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
this.openviduService.initializeAndSetToken(token, livekitUrl);
this.log.d('Token has been successfully set. Room is ready to join');
// Only update showPrejoin if user hasn't initiated join process yet
// This prevents prejoin from showing again after user clicked join
if (!this.hasUserInitiatedJoin()) {
this.updateComponentState({
state: VideoconferenceState.PREJOIN_SHOWN,
isRoomReady: true,
showPrejoin: this.libService.showPrejoin()
});
} else {
if (this.hasUserInitiatedJoin()) {
// User has initiated join, proceed to hide prejoin and continue
this.log.d('User has initiated join, hiding prejoin and proceeding');
this.updateComponentState({
@ -937,6 +928,21 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
isRoomReady: true,
showPrejoin: false
});
console.log(this.componentState);
console.warn(
this.componentState.isRoomReady &&
!this.componentState.showPrejoin &&
!this.componentState.isLoading &&
!this.componentState.error?.hasError
);
} else {
// Only update showPrejoin if user hasn't initiated join process yet
// This prevents prejoin from showing again after user clicked join
this.updateComponentState({
state: VideoconferenceState.PREJOIN_SHOWN,
isRoomReady: true,
showPrejoin: this.libService.showPrejoin()
});
}
} catch (error) {
this.log.e('Error trying to set token', error);

View File

@ -199,10 +199,7 @@ export class OpenViduComponentsConfigService {
*/
private createGeneralConfigItem(initialValue: GeneralConfig): ConfigItem<GeneralConfig> {
const subject = new BehaviorSubject<GeneralConfig>(initialValue);
const observable$ = subject.asObservable().pipe(
distinctUntilChanged((prev, curr) => this.compareGeneralConfig(prev, curr)),
shareReplay(1)
);
const observable$ = subject.asObservable();
return { subject, observable$ };
}
@ -297,7 +294,7 @@ export class OpenViduComponentsConfigService {
* Compare GeneralConfig efficiently
*/
private compareGeneralConfig(prev: GeneralConfig, curr: GeneralConfig): boolean {
return (
const equal =
prev.token === curr.token &&
prev.livekitUrl === curr.livekitUrl &&
prev.tokenError === curr.tokenError &&
@ -306,8 +303,12 @@ export class OpenViduComponentsConfigService {
prev.prejoin === curr.prejoin &&
prev.prejoinDisplayParticipantName === curr.prejoinDisplayParticipantName &&
prev.showDisconnectionDialog === curr.showDisconnectionDialog &&
prev.recordingStreamBaseUrl === curr.recordingStreamBaseUrl
);
prev.recordingStreamBaseUrl === curr.recordingStreamBaseUrl;
if (!equal) {
console.log('GeneralConfig cambió', { prev, curr });
}
return equal;
}
// Grouped configuration items by domain
@ -380,17 +381,51 @@ export class OpenViduComponentsConfigService {
private layoutRemoteParticipantsConfig = this.createConfigItem<ParticipantModel[] | undefined>(undefined);
// General observables
token$: Observable<string> = this.generalConfig.observable$.pipe(map((config) => config.token));
livekitUrl$: Observable<string> = this.generalConfig.observable$.pipe(map((config) => config.livekitUrl));
tokenError$: Observable<any> = this.generalConfig.observable$.pipe(map((config) => config.tokenError));
minimal$: Observable<boolean> = this.generalConfig.observable$.pipe(map((config) => config.minimal));
participantName$: Observable<string> = this.generalConfig.observable$.pipe(map((config) => config.participantName));
prejoin$: Observable<boolean> = this.generalConfig.observable$.pipe(map((config) => config.prejoin));
prejoinDisplayParticipantName$: Observable<boolean> = this.generalConfig.observable$.pipe(
map((config) => config.prejoinDisplayParticipantName)
token$: Observable<string> = this.generalConfig.observable$.pipe(
map((config) => config.token),
distinctUntilChanged(),
shareReplay(1)
);
livekitUrl$: Observable<string> = this.generalConfig.observable$.pipe(
map((config) => config.livekitUrl),
distinctUntilChanged(),
shareReplay(1)
);
tokenError$: Observable<any> = this.generalConfig.observable$.pipe(
map((config) => config.tokenError),
distinctUntilChanged(),
shareReplay(1)
);
minimal$: Observable<boolean> = this.generalConfig.observable$.pipe(
map((config) => config.minimal),
distinctUntilChanged(),
shareReplay(1)
);
participantName$: Observable<string> = this.generalConfig.observable$.pipe(
map((config) => config.participantName),
distinctUntilChanged(),
shareReplay(1)
);
prejoin$: Observable<boolean> = this.generalConfig.observable$.pipe(
map((config) => config.prejoin),
distinctUntilChanged(),
shareReplay(1)
);
prejoinDisplayParticipantName$: Observable<boolean> = this.generalConfig.observable$.pipe(
map((config) => config.prejoinDisplayParticipantName),
distinctUntilChanged(),
shareReplay(1)
);
showDisconnectionDialog$: Observable<boolean> = this.generalConfig.observable$.pipe(
map((config) => config.showDisconnectionDialog),
distinctUntilChanged(),
shareReplay(1)
);
recordingStreamBaseUrl$: Observable<string> = this.generalConfig.observable$.pipe(
map((config) => config.recordingStreamBaseUrl),
distinctUntilChanged(),
shareReplay(1)
);
showDisconnectionDialog$: Observable<boolean> = this.generalConfig.observable$.pipe(map((config) => config.showDisconnectionDialog));
recordingStreamBaseUrl$: Observable<string> = this.generalConfig.observable$.pipe(map((config) => config.recordingStreamBaseUrl));
// Stream observables
videoEnabled$: Observable<boolean> = this.streamConfig.observable$.pipe(map((config) => config.videoEnabled));

View File

@ -177,16 +177,20 @@ export class OpenViduService {
/**
* @internal
*/
initializeAndSetToken(token: string, livekitUrl: string): void {
const livekitData = this.extractLivekitData(token, livekitUrl);
this.livekitToken = token;
this.livekitUrl = livekitData.livekitUrl;
initializeAndSetToken(token: string, livekitUrl?: string): void {
const { livekitUrl: urlFromToken } = this.extractLivekitData(token);
if (!this.livekitUrl) {
this.livekitToken = token;
const url = livekitUrl || urlFromToken;
if (!url) {
this.log.e('LiveKit URL is not defined. Please, check the livekitUrl parameter of the VideoConferenceComponent');
throw new Error('Livekit URL is not defined');
}
this.livekitUrl = url;
// this.livekitRoomAdmin = !!livekitRoomAdmin;
// Initialize room if it doesn't exist yet
// This ensures that getRoom() won't fail if token is set before onTokenRequested
if (!this.room) {
@ -370,9 +374,8 @@ export class OpenViduService {
* @throws Error if there is an error decoding and parsing the token.
* @internal
*/
private extractLivekitData(token: string, livekitUrl: string): { livekitUrl: string; livekitRoomAdmin: boolean } {
private extractLivekitData(token: string): { livekitUrl?: string; livekitRoomAdmin: boolean } {
try {
const response = { livekitUrl, livekitRoomAdmin: false };
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
@ -388,13 +391,13 @@ export class OpenViduService {
const payload = JSON.parse(jsonPayload);
if (payload?.metadata) {
const tokenMetadata = JSON.parse(payload.metadata);
if (tokenMetadata.livekitUrl) {
response.livekitUrl = tokenMetadata.livekitUrl;
}
response.livekitRoomAdmin = tokenMetadata.roomAdmin;
return {
livekitUrl: tokenMetadata.livekitUrl,
livekitRoomAdmin: !!tokenMetadata.roomAdmin
};
}
return response;
return { livekitRoomAdmin: false };
} catch (error) {
throw new Error('Error decoding and parsing token: ' + error);
}