frontend: update recording permissions handling and improve UI feature flags
This commit is contained in:
parent
5f71b0c28a
commit
2478845fb6
@ -11,16 +11,15 @@
|
||||
[toolbarScreenshareButton]="features().showScreenShare"
|
||||
[toolbarLeaveButton]="!features().canModerateRoom"
|
||||
[toolbarRecordingButton]="features().canRecordRoom"
|
||||
[toolbarViewRecordingsButton]="features().showRecordings"
|
||||
[toolbarViewRecordingsButton]="features().canRetrieveRecordings && hasRecordings"
|
||||
[toolbarBroadcastingButton]="false"
|
||||
[toolbarChatPanelButton]="features().showChat"
|
||||
[toolbarBackgroundEffectsButton]="features().showBackgrounds"
|
||||
[toolbarParticipantsPanelButton]="features().showParticipantList"
|
||||
[toolbarSettingsButton]="features().showSettings"
|
||||
[toolbarFullscreenButton]="features().showFullscreen"
|
||||
[toolbarActivitiesPanelButton]="features().showRecordings"
|
||||
[activitiesPanelRecordingActivity]="features().showRecordings"
|
||||
[recordingActivityReadOnly]="!features().canRecordRoom"
|
||||
[toolbarActivitiesPanelButton]="features().showRecordingPanel"
|
||||
[activitiesPanelRecordingActivity]="features().showRecordingPanel"
|
||||
[recordingActivityShowControls]="{
|
||||
play: false,
|
||||
download: false,
|
||||
@ -28,7 +27,7 @@
|
||||
externalView: true
|
||||
}"
|
||||
[recordingActivityStartStopRecordingButton]="features().canRecordRoom"
|
||||
[recordingActivityViewRecordingsButton]="features().showRecordings"
|
||||
[recordingActivityViewRecordingsButton]="features().canRetrieveRecordings && hasRecordings"
|
||||
[recordingActivityShowRecordingsList]="false"
|
||||
[activitiesPanelBroadcastingActivity]="false"
|
||||
[showDisconnectionDialog]="false"
|
||||
@ -37,8 +36,7 @@
|
||||
(onParticipantLeft)="onParticipantLeft($event)"
|
||||
(onRecordingStartRequested)="onRecordingStartRequested($event)"
|
||||
(onRecordingStopRequested)="onRecordingStopRequested($event)"
|
||||
(onViewRecordingsClicked)="onViewRecordingsClicked(undefined)"
|
||||
(onViewRecordingClicked)="onViewRecordingsClicked($event)"
|
||||
(onViewRecordingsClicked)="onViewRecordingsClicked()"
|
||||
>
|
||||
@if (features().canModerateRoom) {
|
||||
<div *ovToolbarAdditionalButtons>
|
||||
|
||||
@ -84,6 +84,8 @@ export class MeetingComponent implements OnInit {
|
||||
participantForm = new FormGroup({
|
||||
name: new FormControl('', [Validators.required, Validators.minLength(4)])
|
||||
});
|
||||
|
||||
hasRecordings = false;
|
||||
showRecordingCard = false;
|
||||
|
||||
showBackButton = true;
|
||||
@ -175,13 +177,23 @@ export class MeetingComponent implements OnInit {
|
||||
*/
|
||||
private async checkForRecordings() {
|
||||
try {
|
||||
await this.recordingService.generateRecordingToken(this.roomId, this.roomSecret);
|
||||
const { canRetrieveRecordings } = await this.recordingService.generateRecordingToken(
|
||||
this.roomId,
|
||||
this.roomSecret
|
||||
);
|
||||
|
||||
if (!canRetrieveRecordings) {
|
||||
this.showRecordingCard = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const { recordings } = await this.recordingService.listRecordings({
|
||||
maxItems: 1,
|
||||
roomId: this.roomId,
|
||||
fields: 'recordingId'
|
||||
});
|
||||
this.showRecordingCard = recordings.length > 0;
|
||||
this.hasRecordings = recordings.length > 0;
|
||||
this.showRecordingCard = this.hasRecordings;
|
||||
} catch (error) {
|
||||
console.error('Error checking for recordings:', error);
|
||||
this.showRecordingCard = false;
|
||||
@ -326,10 +338,34 @@ export class MeetingComponent implements OnInit {
|
||||
const event = JSON.parse(new TextDecoder().decode(payload));
|
||||
|
||||
switch (topic) {
|
||||
case 'recordingStopped': {
|
||||
// If a 'recordingStopped' event is received and there was no previous recordings,
|
||||
// update the hasRecordings flag and refresh the recording token
|
||||
if (this.hasRecordings) return;
|
||||
|
||||
this.hasRecordings = true;
|
||||
|
||||
try {
|
||||
await this.recordingService.generateRecordingToken(this.roomId, this.roomSecret);
|
||||
} catch (error) {
|
||||
console.error('Error refreshing recording token:', error);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MeetSignalType.MEET_ROOM_PREFERENCES_UPDATED: {
|
||||
// Update room preferences
|
||||
const { preferences } = event as MeetRoomPreferencesUpdatedPayload;
|
||||
this.featureConfService.setRoomPreferences(preferences);
|
||||
|
||||
// Refresh recording token if recording is enabled
|
||||
if (preferences.recordingPreferences.enabled) {
|
||||
try {
|
||||
await this.recordingService.generateRecordingToken(this.roomId, this.roomSecret);
|
||||
} catch (error) {
|
||||
console.error('Error refreshing recording token:', error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MeetSignalType.MEET_PARTICIPANT_ROLE_UPDATED: {
|
||||
@ -504,11 +540,7 @@ Remember that by default, a recording uses 4 CPUs for each room.`
|
||||
}
|
||||
}
|
||||
|
||||
async onViewRecordingsClicked(recordingId?: any) {
|
||||
if (recordingId) {
|
||||
await this.recordingService.playRecording(recordingId);
|
||||
} else {
|
||||
window.open(`/room/${this.roomId}/recordings?secret=${this.roomSecret}`, '_blank');
|
||||
}
|
||||
async onViewRecordingsClicked() {
|
||||
window.open(`/room/${this.roomId}/recordings?secret=${this.roomSecret}`, '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
import { computed, Injectable, signal } from '@angular/core';
|
||||
import { MeetRoomPreferences, ParticipantPermissions, ParticipantRole, TrackSource } from '@lib/typings/ce';
|
||||
import {
|
||||
MeetRoomPreferences,
|
||||
ParticipantPermissions,
|
||||
ParticipantRole,
|
||||
RecordingPermissions,
|
||||
TrackSource
|
||||
} from '@lib/typings/ce';
|
||||
import { LoggerService } from 'openvidu-components-angular';
|
||||
|
||||
/**
|
||||
@ -14,7 +20,7 @@ export interface ApplicationFeatures {
|
||||
showScreenShare: boolean;
|
||||
|
||||
// UI Features
|
||||
showRecordings: boolean;
|
||||
showRecordingPanel: boolean;
|
||||
showChat: boolean;
|
||||
showBackgrounds: boolean;
|
||||
showParticipantList: boolean;
|
||||
@ -24,6 +30,7 @@ export interface ApplicationFeatures {
|
||||
// Permissions
|
||||
canModerateRoom: boolean;
|
||||
canRecordRoom: boolean;
|
||||
canRetrieveRecordings: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +43,7 @@ const DEFAULT_FEATURES: ApplicationFeatures = {
|
||||
showMicrophone: true,
|
||||
showScreenShare: true,
|
||||
|
||||
showRecordings: true,
|
||||
showRecordingPanel: true,
|
||||
showChat: true,
|
||||
showBackgrounds: true,
|
||||
showParticipantList: true,
|
||||
@ -44,7 +51,8 @@ const DEFAULT_FEATURES: ApplicationFeatures = {
|
||||
showFullscreen: true,
|
||||
|
||||
canModerateRoom: false,
|
||||
canRecordRoom: false
|
||||
canRecordRoom: false,
|
||||
canRetrieveRecordings: false
|
||||
};
|
||||
|
||||
/**
|
||||
@ -61,10 +69,16 @@ export class FeatureConfigurationService {
|
||||
protected roomPreferences = signal<MeetRoomPreferences | undefined>(undefined);
|
||||
protected participantPermissions = signal<ParticipantPermissions | undefined>(undefined);
|
||||
protected participantRole = signal<ParticipantRole | undefined>(undefined);
|
||||
protected recordingPermissions = signal<RecordingPermissions | undefined>(undefined);
|
||||
|
||||
// Computed signal to derive features based on current configurations
|
||||
public readonly features = computed<ApplicationFeatures>(() =>
|
||||
this.calculateFeatures(this.roomPreferences(), this.participantPermissions(), this.participantRole())
|
||||
this.calculateFeatures(
|
||||
this.roomPreferences(),
|
||||
this.participantPermissions(),
|
||||
this.participantRole(),
|
||||
this.recordingPermissions()
|
||||
)
|
||||
);
|
||||
|
||||
constructor(protected loggerService: LoggerService) {
|
||||
@ -95,6 +109,14 @@ export class FeatureConfigurationService {
|
||||
this.participantRole.set(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates recording permissions
|
||||
*/
|
||||
setRecordingPermissions(permissions: RecordingPermissions): void {
|
||||
this.log.d('Updating recording permissions', permissions);
|
||||
this.recordingPermissions.set(permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specific feature is enabled
|
||||
*/
|
||||
@ -108,14 +130,15 @@ export class FeatureConfigurationService {
|
||||
protected calculateFeatures(
|
||||
roomPrefs?: MeetRoomPreferences,
|
||||
participantPerms?: ParticipantPermissions,
|
||||
role?: ParticipantRole
|
||||
role?: ParticipantRole,
|
||||
recordingPerms?: RecordingPermissions
|
||||
): ApplicationFeatures {
|
||||
// Start with default configuration
|
||||
const features: ApplicationFeatures = { ...DEFAULT_FEATURES };
|
||||
|
||||
// Apply room configurations
|
||||
if (roomPrefs) {
|
||||
features.showRecordings = roomPrefs.recordingPreferences.enabled;
|
||||
features.showRecordingPanel = roomPrefs.recordingPreferences.enabled;
|
||||
features.showChat = roomPrefs.chatPreferences.enabled;
|
||||
features.showBackgrounds = roomPrefs.virtualBackgroundPreferences.enabled;
|
||||
}
|
||||
@ -123,8 +146,7 @@ export class FeatureConfigurationService {
|
||||
// Apply participant permissions (these can restrict enabled features)
|
||||
if (participantPerms) {
|
||||
// Only restrict if the feature is already enabled
|
||||
if (features.showRecordings) {
|
||||
// features.showRecordings = !!recordingRole;
|
||||
if (features.showRecordingPanel) {
|
||||
features.canRecordRoom = participantPerms.openvidu.canRecord;
|
||||
}
|
||||
if (features.showChat) {
|
||||
@ -149,6 +171,11 @@ export class FeatureConfigurationService {
|
||||
features.canModerateRoom = role === ParticipantRole.MODERATOR;
|
||||
}
|
||||
|
||||
// Apply recording permissions
|
||||
if (recordingPerms) {
|
||||
features.canRetrieveRecordings = recordingPerms.canRetrieveRecordings;
|
||||
}
|
||||
|
||||
this.log.d('Calculated features', features);
|
||||
return features;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ShareRecordingDialogComponent } from '@lib/components';
|
||||
import { AuthService, HttpService, ParticipantService } from '@lib/services';
|
||||
import { AuthService, FeatureConfigurationService, HttpService, ParticipantService } from '@lib/services';
|
||||
import { MeetRecordingFilters, MeetRecordingInfo, RecordingPermissions } from '@lib/typings/ce';
|
||||
import { getValidDecodedToken } from '@lib/utils';
|
||||
import { LoggerService } from 'openvidu-components-angular';
|
||||
@ -25,6 +25,7 @@ export class RecordingService {
|
||||
private httpService: HttpService,
|
||||
protected participantService: ParticipantService,
|
||||
protected authService: AuthService,
|
||||
protected featureConfService: FeatureConfigurationService,
|
||||
protected dialog: MatDialog
|
||||
) {
|
||||
this.log = this.loggerService.get('OpenVidu Meet - RecordingManagerService');
|
||||
@ -159,10 +160,18 @@ export class RecordingService {
|
||||
*/
|
||||
async generateRecordingToken(roomId: string, secret: string): Promise<RecordingPermissions> {
|
||||
const path = `${HttpService.INTERNAL_API_PATH_PREFIX}/rooms/${roomId}/recording-token`;
|
||||
const { token } = await this.httpService.postRequest<{ token: string }>(path, { secret });
|
||||
|
||||
this.setRecordingPermissionsFromToken(token);
|
||||
return this.recordingPermissions;
|
||||
try {
|
||||
const { token } = await this.httpService.postRequest<{ token: string }>(path, { secret });
|
||||
this.setRecordingPermissionsFromToken(token);
|
||||
return this.recordingPermissions;
|
||||
} catch (error) {
|
||||
this.featureConfService.setRecordingPermissions({
|
||||
canRetrieveRecordings: false,
|
||||
canDeleteRecordings: false
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -174,6 +183,9 @@ export class RecordingService {
|
||||
try {
|
||||
const decodedToken = getValidDecodedToken(token);
|
||||
this.recordingPermissions = decodedToken.metadata.recordingPermissions;
|
||||
|
||||
// Update feature configuration
|
||||
this.featureConfService.setRecordingPermissions(this.recordingPermissions);
|
||||
} catch (error) {
|
||||
this.log.e('Error setting recording permissions from token', error);
|
||||
throw new Error('Error setting recording permissions from token');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user