frontend: refactor room member context and related services for improved state management
This commit is contained in:
parent
599a744302
commit
ab907bb0e8
@ -40,8 +40,13 @@ export const extractRoomParamsGuard: CanActivateFn = (route: ActivatedRouteSnaps
|
||||
meetingContextService.loadE2eeKeyFromStorage();
|
||||
}
|
||||
|
||||
// Handle participant name: prioritize query param, fallback to storage
|
||||
if (participantName) {
|
||||
roomMemberContextService.setParticipantName(participantName);
|
||||
// Participant name came from URL parameter
|
||||
roomMemberContextService.setParticipantName(participantName, true);
|
||||
} else {
|
||||
// Try to load participant name from storage
|
||||
roomMemberContextService.loadParticipantNameFromStorage();
|
||||
}
|
||||
|
||||
// If the showOnlyRecordings flag is set, redirect to the recordings page for the room
|
||||
|
||||
@ -190,7 +190,7 @@ export class MeetingEventHandlerService {
|
||||
const { participantIdentity, newRole, secret } = event;
|
||||
const roomId = this.meetingContext.roomId();
|
||||
const local = this.meetingContext.localParticipant();
|
||||
const participantName = this.roomMemberContextService.getParticipantName();
|
||||
const participantName = this.roomMemberContextService.participantName();
|
||||
|
||||
// Check if the role update is for the local participant
|
||||
if (local && participantIdentity === local.identity) {
|
||||
|
||||
@ -131,7 +131,7 @@ export class RecordingService {
|
||||
params.append('accessToken', accessToken);
|
||||
}
|
||||
|
||||
const roomMemberToken = this.roomMemberContextService.getRoomMemberToken();
|
||||
const roomMemberToken = this.roomMemberContextService.roomMemberToken();
|
||||
if (roomMemberToken) {
|
||||
params.append('roomMemberToken', roomMemberToken);
|
||||
}
|
||||
@ -227,7 +227,7 @@ export class RecordingService {
|
||||
params.append('accessToken', accessToken);
|
||||
}
|
||||
|
||||
const roomMemberToken = this.roomMemberContextService.getRoomMemberToken();
|
||||
const roomMemberToken = this.roomMemberContextService.roomMemberToken();
|
||||
if (roomMemberToken) {
|
||||
params.append('roomMemberToken', roomMemberToken);
|
||||
}
|
||||
|
||||
@ -72,8 +72,8 @@ export class RoomMemberInterceptorErrorHandlerService implements HttpErrorHandle
|
||||
}
|
||||
|
||||
const secret = this.meetingContextService.roomSecret();
|
||||
const participantName = this.roomMemberContextService.getParticipantName();
|
||||
const participantIdentity = this.roomMemberContextService.getParticipantIdentity();
|
||||
const participantName = this.roomMemberContextService.participantName();
|
||||
const participantIdentity = this.roomMemberContextService.participantIdentity();
|
||||
const joinMeeting = !!participantIdentity; // Grant join permission if identity is set
|
||||
|
||||
return from(
|
||||
@ -86,7 +86,7 @@ export class RoomMemberInterceptorErrorHandlerService implements HttpErrorHandle
|
||||
).pipe(
|
||||
switchMap(() => {
|
||||
console.log('Room member token refreshed');
|
||||
|
||||
|
||||
// Update the request with the new token
|
||||
const headers = this.roomMemberHeaderProvider.provideHeaders();
|
||||
const updatedRequest = headers ? originalRequest.clone({ setHeaders: headers }) : originalRequest;
|
||||
|
||||
@ -31,14 +31,14 @@ export class RoomMemberHeaderProviderService implements HttpHeaderProvider {
|
||||
// Only provide if:
|
||||
// 1. Room member token exists
|
||||
// 2. Current page is a room page (starts with /room/)
|
||||
return !!this.roomMemberContextService.getRoomMemberToken() && context.pageUrl.startsWith('/room/');
|
||||
return !!this.roomMemberContextService.roomMemberToken() && context.pageUrl.startsWith('/room/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the room member token header
|
||||
*/
|
||||
provideHeaders(): Record<string, string> | null {
|
||||
const roomMemberToken = this.roomMemberContextService.getRoomMemberToken();
|
||||
const roomMemberToken = this.roomMemberContextService.roomMemberToken();
|
||||
if (!roomMemberToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { computed, Injectable, signal } from '@angular/core';
|
||||
import {
|
||||
MeetRoomMember,
|
||||
MeetRoomMemberPermissions,
|
||||
MeetRoomMemberTokenMetadata,
|
||||
MeetRoomMemberTokenOptions
|
||||
@ -16,10 +17,30 @@ import { RoomMemberService } from './room-member.service';
|
||||
export class RoomMemberContextService {
|
||||
protected readonly PARTICIPANT_NAME_KEY = 'ovMeet-participantName';
|
||||
|
||||
protected roomMemberToken?: string;
|
||||
protected participantName?: string;
|
||||
protected participantIdentity?: string;
|
||||
protected permissions?: MeetRoomMemberPermissions;
|
||||
/**
|
||||
* Individual signals for room member context
|
||||
*/
|
||||
private readonly _roomMemberToken = signal<string | undefined>(undefined);
|
||||
private readonly _participantName = signal<string | undefined>(undefined);
|
||||
private readonly _isParticipantNameFromUrl = signal<boolean>(false);
|
||||
private readonly _participantIdentity = signal<string | undefined>(undefined);
|
||||
private readonly _permissions = signal<MeetRoomMemberPermissions | undefined>(undefined);
|
||||
private readonly _member = signal<MeetRoomMember | undefined>(undefined);
|
||||
|
||||
/** Readonly signal for the room member token */
|
||||
readonly roomMemberToken = this._roomMemberToken.asReadonly();
|
||||
/** Readonly signal for the participant name */
|
||||
readonly participantName = this._participantName.asReadonly();
|
||||
/** Readonly signal for whether the participant name came from a URL parameter */
|
||||
readonly isParticipantNameFromUrl = this._isParticipantNameFromUrl.asReadonly();
|
||||
/** Readonly signal for the participant identity */
|
||||
readonly participantIdentity = this._participantIdentity.asReadonly();
|
||||
/** Readonly signal for the room member permissions */
|
||||
readonly permissions = this._permissions.asReadonly();
|
||||
/** Readonly signal for the room member info (when memberId is set) */
|
||||
readonly member = this._member.asReadonly();
|
||||
/** Computed signal for the room member's display name */
|
||||
readonly memberName = computed(() => this._member()?.name);
|
||||
|
||||
protected log;
|
||||
|
||||
@ -34,40 +55,34 @@ export class RoomMemberContextService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current room member token.
|
||||
* Sets the participant's display name and optionally stores it in localStorage.
|
||||
*
|
||||
* @returns The room member token, or undefined if not set
|
||||
* @param participantName - The display name of the participant
|
||||
* @param fromUrl - Whether the name came from a URL parameter
|
||||
*/
|
||||
getRoomMemberToken(): string | undefined {
|
||||
return this.roomMemberToken;
|
||||
setParticipantName(participantName: string, fromUrl = false) {
|
||||
this._participantName.set(participantName);
|
||||
this._isParticipantNameFromUrl.set(fromUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the participant's display name and stores it in localStorage.
|
||||
* Saves the participant name to localStorage
|
||||
*
|
||||
* @param participantName - The display name of the participant
|
||||
* @param participantName - The display name of the participant to save
|
||||
*/
|
||||
setParticipantName(participantName: string): void {
|
||||
this.participantName = participantName;
|
||||
saveParticipantNameToStorage(participantName: string) {
|
||||
localStorage.setItem(this.PARTICIPANT_NAME_KEY, participantName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the participant's display name from memory or localStorage.
|
||||
*
|
||||
* @returns The display name of the participant, or undefined if not set
|
||||
* Loads the participant name from localStorage
|
||||
*/
|
||||
getParticipantName(): string | undefined {
|
||||
return this.participantName || localStorage.getItem(this.PARTICIPANT_NAME_KEY) || undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the participant's identity.
|
||||
*
|
||||
* @returns The identity of the participant, or undefined if not set
|
||||
*/
|
||||
getParticipantIdentity(): string | undefined {
|
||||
return this.participantIdentity;
|
||||
loadParticipantNameFromStorage() {
|
||||
const storedName = localStorage.getItem(this.PARTICIPANT_NAME_KEY);
|
||||
if (storedName) {
|
||||
this._participantName.set(storedName);
|
||||
this._isParticipantNameFromUrl.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,7 +92,7 @@ export class RoomMemberContextService {
|
||||
* @returns True if the member has the permission, false otherwise
|
||||
*/
|
||||
hasPermission(permission: keyof MeetRoomMemberPermissions): boolean {
|
||||
return this.permissions?.[permission] ?? false;
|
||||
return this._permissions()?.[permission] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,7 +112,7 @@ export class RoomMemberContextService {
|
||||
}
|
||||
|
||||
const { token } = await this.roomMemberService.generateRoomMemberToken(roomId, tokenOptions);
|
||||
this.roomMemberToken = token;
|
||||
this._roomMemberToken.set(token);
|
||||
await this.updateContextFromToken(token);
|
||||
return token;
|
||||
}
|
||||
@ -115,15 +130,25 @@ export class RoomMemberContextService {
|
||||
|
||||
if (decodedToken.sub && decodedToken.name) {
|
||||
const decryptedName = await this.e2eeService.decrypt(decodedToken.name);
|
||||
this.setParticipantName(decryptedName);
|
||||
this.participantIdentity = decodedToken.sub;
|
||||
this._participantName.set(decryptedName);
|
||||
this._participantIdentity.set(decodedToken.sub);
|
||||
}
|
||||
|
||||
this.permissions = metadata.effectivePermissions;
|
||||
this._permissions.set(metadata.effectivePermissions);
|
||||
|
||||
// If token contains memberId, fetch and store member info
|
||||
if (metadata.memberId) {
|
||||
try {
|
||||
const member = await this.roomMemberService.getRoomMember(metadata.roomId, metadata.memberId);
|
||||
this._member.set(member);
|
||||
} catch (error) {
|
||||
this.log.w('Could not fetch member info:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Update feature configuration
|
||||
this.roomFeatureService.setRoomMemberRole(metadata.baseRole);
|
||||
this.roomFeatureService.setRoomMemberPermissions(this.permissions);
|
||||
this.roomFeatureService.setRoomMemberPermissions(metadata.effectivePermissions);
|
||||
} catch (error) {
|
||||
this.log.e('Error decoding room member token:', error);
|
||||
throw new Error('Invalid room member token');
|
||||
@ -131,12 +156,14 @@ export class RoomMemberContextService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the room member context, including token, participant info, role, and permissions.
|
||||
* Clears the room member context, including token, participant info, member, role, and permissions.
|
||||
*/
|
||||
clearContext(): void {
|
||||
this.roomMemberToken = undefined;
|
||||
this.participantName = undefined;
|
||||
this.participantIdentity = undefined;
|
||||
this.permissions = undefined;
|
||||
this._roomMemberToken.set(undefined);
|
||||
this._participantName.set(undefined);
|
||||
this._isParticipantNameFromUrl.set(false);
|
||||
this._participantIdentity.set(undefined);
|
||||
this._permissions.set(undefined);
|
||||
this._member.set(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user