From b659400c882ce370684791d131d614e2cbf9bc9c Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Fri, 18 Jul 2025 12:08:49 +0200 Subject: [PATCH] ov-components: implement read-only mode and customizable controls for recording activity --- .../activities-panel.component.html | 2 + .../activities-panel.component.ts | 15 ++ .../recording-activity.component.html | 208 ++++++++++++------ .../recording-activity.component.scss | 14 +- .../recording-activity.component.ts | 84 ++++++- .../components/toolbar/toolbar.component.html | 8 + .../components/toolbar/toolbar.component.ts | 58 +++-- .../videoconference.component.html | 3 + .../videoconference.component.ts | 156 ++++++------- .../directives/api/api.directive.module.ts | 8 +- .../lib/directives/api/internals.directive.ts | 178 +++++++++++++++ .../src/lib/lang/cn.json | 10 +- .../src/lib/lang/en.json | 14 +- .../src/lib/lang/es.json | 10 +- .../src/lib/lang/fr.json | 10 +- .../src/lib/lang/hi.json | 10 +- .../src/lib/lang/it.json | 10 +- .../src/lib/lang/ja.json | 10 +- .../src/lib/lang/nl.json | 10 +- .../src/lib/lang/pt.json | 10 +- .../config/directive-config.service.ts | 43 ++++ 21 files changed, 686 insertions(+), 185 deletions(-) diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.html index 7291a2b3..d5f823ed 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/activities-panel.component.html @@ -17,6 +17,8 @@ (onRecordingDeleteRequested)="onRecordingDeleteRequested.emit($event)" (onRecordingDownloadClicked)="onRecordingDownloadClicked.emit($event)" (onRecordingPlayClicked)="onRecordingPlayClicked.emit($event)" + (onViewRecordingClicked)="onViewRecordingClicked.emit($event)" + (onViewRecordingsClicked)="onViewRecordingsClicked.emit()" > = new EventEmitter(); + /** + * @internal + * Provides event notifications that fire when view recordings button has been clicked. + * This event is triggered when the user wants to view all recordings in an external page. + */ + @Output() onViewRecordingsClicked: EventEmitter = new EventEmitter(); + + /** + * @internal + * Provides event notifications that fire when view recording button has been clicked. + * This event is triggered when the user wants to view a specific recording in an external page. + * It provides the recording ID as event data. + */ + @Output() onViewRecordingClicked: EventEmitter = new EventEmitter(); + /** * Provides event notifications that fire when start broadcasting button is clicked. * It provides the {@link BroadcastingStartRequestedEvent} payload as event data. diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html index 0012366e..e7a89449 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.html @@ -27,7 +27,10 @@ radio_button_checked

{{ 'PANEL.RECORDING.TITLE' | translate }}

-

{{ 'PANEL.RECORDING.SUBTITLE' | translate }}

+ +

+ {{ isReadOnlyMode ? ('PANEL.RECORDING.VIEW_ONLY_SUBTITLE' | translate) : ('PANEL.RECORDING.SUBTITLE' | translate) }} +

-

{{ 'PANEL.RECORDING.CONTENT_TITLE' | translate }}

- {{ 'PANEL.RECORDING.CONTENT_SUBTITLE' | translate }} +

+ {{ + isReadOnlyMode + ? ('PANEL.RECORDING.VIEW_ONLY_CONTENT_TITLE' | translate) + : ('PANEL.RECORDING.CONTENT_TITLE' | translate) + }} +

+ + {{ + isReadOnlyMode + ? recordingList.length === 0 + ? ('PANEL.RECORDING.NO_RECORDINGS_AVAILABLE' | translate) + : ('PANEL.RECORDING.VIEW_ONLY_CONTENT_SUBTITLE' | translate) + : ('PANEL.RECORDING.CONTENT_SUBTITLE' | translate) + }} +
-
- - - - -
- -
- -
- - {{ 'PANEL.RECORDING.STARTING' | translate }} - - - - {{ 'PANEL.RECORDING.STOPPING' | translate }} - - -
- Message: {{ recordingError }} -
+ + +
+ +
+ + +
+ + {{ 'PANEL.RECORDING.STARTING' | translate }} + + + + {{ 'PANEL.RECORDING.STOPPING' | translate }} + + +
+ Message: {{ recordingError }} + +
+
-
+ } @else { + +
+ +
+ }
@@ -139,37 +174,72 @@
-
- + @if (!isReadOnlyMode) { +
+ - + @if (showControls.externalView) { +
+ +
+ } - -
+ + + +
+ } @else { +
+ +
+ } diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.scss index 5d31d187..eee53e93 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.scss +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.scss @@ -145,8 +145,7 @@ } .recording-action-buttons { - margin-top: 20px; - margin-bottom: 20px; + margin: 5px 0px; } #start-recording-btn { @@ -155,6 +154,17 @@ color: var(--ov-secondary-action-color); } + #view-recordings-btn { + width: 100%; + background-color: var(--ov-accent-action-color); + color: var(--ov-secondary-action-color); + margin-bottom: 10px; + + mat-icon { + margin-right: 8px; + } + } + .start-recording-button-container { width: 100%; display: inline-block; diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts index 76ebfcbb..ccb4be4a 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, OnDestroy, Output } from '@angular/core'; import { Subject, takeUntil } from 'rxjs'; import { RecordingDeleteRequestedEvent, @@ -16,6 +16,7 @@ import { RecordingService } from '../../../../services/recording/recording.servi import { OpenViduService } from '../../../../services/openvidu/openvidu.service'; import { ILogger } from '../../../../models/logger.model'; import { LoggerService } from '../../../../services/logger/logger.service'; +import { OpenViduComponentsConfigService } from '../../../../services/config/directive-config.service'; /** * The **RecordingActivityComponent** is the component that allows showing the recording activity. @@ -31,7 +32,7 @@ import { LoggerService } from '../../../../services/logger/logger.service'; // TODO: Allow to add more than one recording type // TODO: Allow to choose where the recording is stored (s3, google cloud, etc) // TODO: Allow to choose the layout of the recording -export class RecordingActivityComponent implements OnInit { +export class RecordingActivityComponent implements OnInit, OnDestroy { /** * @internal */ @@ -67,6 +68,20 @@ export class RecordingActivityComponent implements OnInit { */ @Output() onRecordingPlayClicked: EventEmitter = new EventEmitter(); + /** + * @internal + * Provides event notifications that fire when view recordings button has been clicked. + * This event is triggered when the user wants to view all recordings in an external page. + */ + @Output() onViewRecordingsClicked: EventEmitter = new EventEmitter(); + + /** + * @internal + * This event is fired when the user clicks on the view recording button. + * It provides the recording ID as event data. + */ + @Output() onViewRecordingClicked: EventEmitter = new EventEmitter(); + /** * @internal */ @@ -108,6 +123,27 @@ export class RecordingActivityComponent implements OnInit { * @internal */ mouseHovering: boolean = false; + + /** + * @internal + */ + isReadOnlyMode: boolean = false; + + /** + * @internal + */ + viewButtonText: string = 'PANEL.RECORDING.VIEW'; + + /** + * @internal + */ + showControls: { play?: boolean; download?: boolean; delete?: boolean; externalView?: boolean } = { + play: true, + download: true, + delete: true, + externalView: false + }; + private log: ILogger; private destroy$ = new Subject(); @@ -120,7 +156,8 @@ export class RecordingActivityComponent implements OnInit { private actionService: ActionService, private openviduService: OpenViduService, private cd: ChangeDetectorRef, - private loggerSrv: LoggerService + private loggerSrv: LoggerService, + private libService: OpenViduComponentsConfigService ) { this.log = this.loggerSrv.get('RecordingActivityComponent'); } @@ -131,6 +168,7 @@ export class RecordingActivityComponent implements OnInit { ngOnInit(): void { this.subscribeToRecordingStatus(); this.subscribeToTracksChanges(); + this.subscribeToConfigChanges(); } /** @@ -240,6 +278,46 @@ export class RecordingActivityComponent implements OnInit { this.recordingService.playRecording(recording); } + /** + * @internal + */ + viewRecording(recording: RecordingInfo) { + // This method can be overridden or emit a custom event for navigation + // For now, it uses the same behavior as play, but can be customized + if (!recording.filename) { + this.log.e('Error viewing recording. Recording filename is undefined'); + return; + } + const payload: RecordingPlayClickedEvent = { + roomName: this.openviduService.getRoomName(), + recordingId: recording.id + }; + this.onRecordingPlayClicked.emit(payload); + // You can customize this to navigate to a different page instead + this.recordingService.playRecording(recording); + } + + /** + * @internal + */ + viewAllRecordings() { + this.onViewRecordingsClicked.emit(); + } + + private subscribeToConfigChanges() { + this.libService.recordingActivityReadOnly$.pipe(takeUntil(this.destroy$)).subscribe((readOnly: boolean) => { + this.isReadOnlyMode = readOnly; + this.cd.markForCheck(); + }); + + this.libService.recordingActivityShowControls$ + .pipe(takeUntil(this.destroy$)) + .subscribe((controls: { play?: boolean; download?: boolean; delete?: boolean; externalView?: boolean }) => { + this.showControls = controls; + this.cd.markForCheck(); + }); + } + private subscribeToRecordingStatus() { this.recordingService.recordingStatusObs.pipe(takeUntil(this.destroy$)).subscribe((event: RecordingStatusInfo) => { const { status, recordingList, error } = event; diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar.component.html index 4cbd1d6e..d0ea4b2c 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar.component.html @@ -137,6 +137,14 @@ + + @if (!isMinimal && showViewRecordingsButton) { + + } +