ov-components: add camera and microphone button visibility controls in toolbar and settings panel

master
Carlos Santos 2025-03-10 10:11:38 +01:00
parent 5fba250b1d
commit 2bf212ccfe
10 changed files with 217 additions and 10 deletions

View File

@ -25,6 +25,7 @@
<div mat-line *ngIf="!isMobile">{{ 'PANEL.SETTINGS.GENERAL' | translate }}</div> <div mat-line *ngIf="!isMobile">{{ 'PANEL.SETTINGS.GENERAL' | translate }}</div>
</mat-list-option> </mat-list-option>
<mat-list-option <mat-list-option
*ngIf="showCameraButton"
class="option" class="option"
id="video-opt" id="video-opt"
[selected]="selectedOption === settingsOptions.VIDEO" [selected]="selectedOption === settingsOptions.VIDEO"
@ -34,6 +35,7 @@
<div mat-line *ngIf="!isMobile">{{ 'PANEL.SETTINGS.VIDEO' | translate }}</div> <div mat-line *ngIf="!isMobile">{{ 'PANEL.SETTINGS.VIDEO' | translate }}</div>
</mat-list-option> </mat-list-option>
<mat-list-option <mat-list-option
*ngIf="showMicrophoneButton"
class="option" class="option"
id="audio-opt" id="audio-opt"
[selected]="selectedOption === settingsOptions.AUDIO" [selected]="selectedOption === settingsOptions.AUDIO"
@ -68,12 +70,12 @@
</mat-list> </mat-list>
</div> </div>
<ov-video-devices-select <ov-video-devices-select
*ngIf="selectedOption === settingsOptions.VIDEO" *ngIf="showCameraButton && selectedOption === settingsOptions.VIDEO"
(onVideoDeviceChanged)="onVideoDeviceChanged.emit($event)" (onVideoDeviceChanged)="onVideoDeviceChanged.emit($event)"
(onVideoEnabledChanged)="onVideoEnabledChanged.emit($event)" (onVideoEnabledChanged)="onVideoEnabledChanged.emit($event)"
></ov-video-devices-select> ></ov-video-devices-select>
<ov-audio-devices-select <ov-audio-devices-select
*ngIf="selectedOption === settingsOptions.AUDIO" *ngIf="showMicrophoneButton && selectedOption === settingsOptions.AUDIO"
(onAudioDeviceChanged)="onAudioDeviceChanged.emit($event)" (onAudioDeviceChanged)="onAudioDeviceChanged.emit($event)"
(onAudioEnabledChanged)="onAudioEnabledChanged.emit($event)" (onAudioEnabledChanged)="onAudioEnabledChanged.emit($event)"
></ov-audio-devices-select> ></ov-audio-devices-select>

View File

@ -23,9 +23,13 @@ export class SettingsPanelComponent implements OnInit {
@Output() onLangChanged = new EventEmitter<LangOption>(); @Output() onLangChanged = new EventEmitter<LangOption>();
settingsOptions: typeof PanelSettingsOptions = PanelSettingsOptions; settingsOptions: typeof PanelSettingsOptions = PanelSettingsOptions;
selectedOption: PanelSettingsOptions = PanelSettingsOptions.GENERAL; selectedOption: PanelSettingsOptions = PanelSettingsOptions.GENERAL;
showCameraButton: boolean = true;
showMicrophoneButton: boolean = true;
showCaptions: boolean = true; showCaptions: boolean = true;
panelSubscription: Subscription; panelSubscription: Subscription;
isMobile: boolean = false; isMobile: boolean = false;
private cameraButtonSub: Subscription;
private microphoneButtonSub: Subscription;
private captionsSubs: Subscription; private captionsSubs: Subscription;
constructor( constructor(
private panelService: PanelService, private panelService: PanelService,
@ -39,6 +43,9 @@ export class SettingsPanelComponent implements OnInit {
} }
ngOnDestroy() { ngOnDestroy() {
if (this.panelSubscription) this.panelSubscription.unsubscribe();
if (this.cameraButtonSub) this.cameraButtonSub.unsubscribe();
if (this.microphoneButtonSub) this.microphoneButtonSub.unsubscribe();
if (this.captionsSubs) this.captionsSubs.unsubscribe(); if (this.captionsSubs) this.captionsSubs.unsubscribe();
} }
@ -50,9 +57,9 @@ export class SettingsPanelComponent implements OnInit {
} }
private subscribeToDirectives() { private subscribeToDirectives() {
this.captionsSubs = this.libService.captionsButton$.subscribe((value: boolean) => { this.cameraButtonSub = this.libService.cameraButton$.subscribe((value: boolean) => (this.showCameraButton = value));
this.showCaptions = value; this.microphoneButtonSub = this.libService.microphoneButton$.subscribe((value: boolean) => (this.showMicrophoneButton = value));
}); this.captionsSubs = this.libService.captionsButton$.subscribe((value: boolean) => (this.showCaptions = value));
} }
private subscribeToPanelToggling() { private subscribeToPanelToggling() {

View File

@ -24,7 +24,7 @@
<div class="media-controls-container"> <div class="media-controls-container">
<!-- Camera --> <!-- Camera -->
<div class="video-controls-container"> <div class="video-controls-container" *ngIf="showCameraButton">
<ov-video-devices-select <ov-video-devices-select
(onVideoDeviceChanged)="onVideoDeviceChanged.emit($event)" (onVideoDeviceChanged)="onVideoDeviceChanged.emit($event)"
(onVideoEnabledChanged)="videoEnabledChanged($event)" (onVideoEnabledChanged)="videoEnabledChanged($event)"
@ -32,7 +32,7 @@
</div> </div>
<!-- Microphone --> <!-- Microphone -->
<div class="audio-controls-container"> <div class="audio-controls-container" *ngIf="showMicrophoneButton">
<ov-audio-devices-select <ov-audio-devices-select
(onAudioDeviceChanged)="onAudioDeviceChanged.emit($event)" (onAudioDeviceChanged)="onAudioDeviceChanged.emit($event)"
(onAudioEnabledChanged)="audioEnabledChanged($event)" (onAudioEnabledChanged)="audioEnabledChanged($event)"

View File

@ -40,13 +40,16 @@ export class PreJoinComponent implements OnInit, OnDestroy {
* @ignore * @ignore
*/ */
isMinimal: boolean = false; isMinimal: boolean = false;
showCameraButton: boolean = true;
showMicrophoneButton: boolean = true;
showLogo: boolean = true; showLogo: boolean = true;
videoTrack: LocalTrack | undefined; videoTrack: LocalTrack | undefined;
audioTrack: LocalTrack | undefined; audioTrack: LocalTrack | undefined;
private tracks: LocalTrack[]; private tracks: LocalTrack[];
private log: ILogger; private log: ILogger;
private screenShareStateSubscription: Subscription; private cameraButtonSub: Subscription;
private microphoneButtonSub: Subscription;
private minimalSub: Subscription; private minimalSub: Subscription;
private displayLogoSub: Subscription; private displayLogoSub: Subscription;
private shouldRemoveTracksWhenComponentIsDestroyed: boolean = true; private shouldRemoveTracksWhenComponentIsDestroyed: boolean = true;
@ -81,8 +84,9 @@ export class PreJoinComponent implements OnInit, OnDestroy {
async ngOnDestroy() { async ngOnDestroy() {
this.cdkSrv.setSelector('body'); this.cdkSrv.setSelector('body');
if (this.screenShareStateSubscription) this.screenShareStateSubscription.unsubscribe();
if (this.minimalSub) this.minimalSub.unsubscribe(); if (this.minimalSub) this.minimalSub.unsubscribe();
if (this.cameraButtonSub) this.cameraButtonSub.unsubscribe();
if (this.microphoneButtonSub) this.microphoneButtonSub.unsubscribe();
if (this.displayLogoSub) this.displayLogoSub.unsubscribe(); if (this.displayLogoSub) this.displayLogoSub.unsubscribe();
if (this.shouldRemoveTracksWhenComponentIsDestroyed) { if (this.shouldRemoveTracksWhenComponentIsDestroyed) {
@ -133,6 +137,12 @@ export class PreJoinComponent implements OnInit, OnDestroy {
this.isMinimal = value; this.isMinimal = value;
// this.cd.markForCheck(); // this.cd.markForCheck();
}); });
this.cameraButtonSub = this.libService.cameraButton$.subscribe((value: boolean) => {
this.showCameraButton = value;
});
this.microphoneButtonSub = this.libService.microphoneButton$.subscribe((value: boolean) => {
this.showMicrophoneButton = value;
});
this.displayLogoSub = this.libService.displayLogo$.subscribe((value: boolean) => { this.displayLogoSub = this.libService.displayLogo$.subscribe((value: boolean) => {
this.showLogo = value; this.showLogo = value;
// this.cd.markForCheck(); // this.cd.markForCheck();

View File

@ -32,6 +32,7 @@
<button <button
id="camera-btn" id="camera-btn"
mat-icon-button mat-icon-button
*ngIf="showCameraButton"
(click)="toggleCamera()" (click)="toggleCamera()"
[disabled]="isConnectionLost || !hasVideoDevices || cameraMuteChanging" [disabled]="isConnectionLost || !hasVideoDevices || cameraMuteChanging"
[class.warn-btn]="!isCameraEnabled" [class.warn-btn]="!isCameraEnabled"
@ -46,6 +47,7 @@
<button <button
id="mic-btn" id="mic-btn"
mat-icon-button mat-icon-button
*ngIf="showMicrophoneButton"
(click)="toggleMicrophone()" (click)="toggleMicrophone()"
[disabled]="isConnectionLost || !hasAudioDevices || microphoneMuteChanging" [disabled]="isConnectionLost || !hasAudioDevices || microphoneMuteChanging"
[class.warn-btn]="!isMicrophoneEnabled" [class.warn-btn]="!isMicrophoneEnabled"

View File

@ -201,6 +201,14 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
* @ignore * @ignore
*/ */
isMinimal: boolean = false; isMinimal: boolean = false;
/**
* @ignore
*/
showCameraButton: boolean = true;
/**
* @ignore
*/
showMicrophoneButton: boolean = true;
/** /**
* @ignore * @ignore
*/ */
@ -329,6 +337,8 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
private panelTogglingSubscription: Subscription; private panelTogglingSubscription: Subscription;
private chatMessagesSubscription: Subscription; private chatMessagesSubscription: Subscription;
private localParticipantSubscription: Subscription; private localParticipantSubscription: Subscription;
private cameraButtonSub: Subscription;
private microphoneButtonSub: Subscription;
private screenshareButtonSub: Subscription; private screenshareButtonSub: Subscription;
private fullscreenButtonSub: Subscription; private fullscreenButtonSub: Subscription;
private backgroundEffectsButtonSub: Subscription; private backgroundEffectsButtonSub: Subscription;
@ -423,6 +433,8 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
if (this.panelTogglingSubscription) this.panelTogglingSubscription.unsubscribe(); if (this.panelTogglingSubscription) this.panelTogglingSubscription.unsubscribe();
if (this.chatMessagesSubscription) this.chatMessagesSubscription.unsubscribe(); if (this.chatMessagesSubscription) this.chatMessagesSubscription.unsubscribe();
if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe(); if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe();
if (this.cameraButtonSub) this.cameraButtonSub.unsubscribe();
if (this.microphoneButtonSub) this.microphoneButtonSub.unsubscribe();
if (this.screenshareButtonSub) this.screenshareButtonSub.unsubscribe(); if (this.screenshareButtonSub) this.screenshareButtonSub.unsubscribe();
if (this.fullscreenButtonSub) this.fullscreenButtonSub.unsubscribe(); if (this.fullscreenButtonSub) this.fullscreenButtonSub.unsubscribe();
if (this.backgroundEffectsButtonSub) this.backgroundEffectsButtonSub.unsubscribe(); if (this.backgroundEffectsButtonSub) this.backgroundEffectsButtonSub.unsubscribe();
@ -712,6 +724,14 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
this.brandingLogo = value; this.brandingLogo = value;
this.cd.markForCheck(); this.cd.markForCheck();
}); });
this.cameraButtonSub = this.libService.cameraButton$.subscribe((value: boolean) => {
this.showCameraButton = value;
this.cd.markForCheck();
});
this.microphoneButtonSub = this.libService.microphoneButton$.subscribe((value: boolean) => {
this.showMicrophoneButton = value;
this.cd.markForCheck();
});
this.screenshareButtonSub = this.libService.screenshareButton$.subscribe((value: boolean) => { this.screenshareButtonSub = this.libService.screenshareButton$.subscribe((value: boolean) => {
this.showScreenshareButton = value && !this.platformService.isMobile(); this.showScreenshareButton = value && !this.platformService.isMobile();
this.cd.markForCheck(); this.cd.markForCheck();

View File

@ -22,7 +22,9 @@ import {
ToolbarRecordingButtonDirective, ToolbarRecordingButtonDirective,
ToolbarScreenshareButtonDirective, ToolbarScreenshareButtonDirective,
ToolbarSettingsButtonDirective, ToolbarSettingsButtonDirective,
ToolbarAdditionalButtonsPossitionDirective ToolbarAdditionalButtonsPossitionDirective,
ToolbarCameraButtonDirective,
ToolbarMicrophoneButtonDirective
} from './toolbar.directive'; } from './toolbar.directive';
import { import {
AudioEnabledDirective, AudioEnabledDirective,
@ -53,6 +55,8 @@ import {
PrejoinDirective, PrejoinDirective,
VideoEnabledDirective, VideoEnabledDirective,
AudioEnabledDirective, AudioEnabledDirective,
ToolbarCameraButtonDirective,
ToolbarMicrophoneButtonDirective,
ToolbarScreenshareButtonDirective, ToolbarScreenshareButtonDirective,
ToolbarFullscreenButtonDirective, ToolbarFullscreenButtonDirective,
ToolbarBackgroundEffectsButtonDirective, ToolbarBackgroundEffectsButtonDirective,
@ -94,6 +98,8 @@ import {
PrejoinDirective, PrejoinDirective,
VideoEnabledDirective, VideoEnabledDirective,
AudioEnabledDirective, AudioEnabledDirective,
ToolbarCameraButtonDirective,
ToolbarMicrophoneButtonDirective,
ToolbarScreenshareButtonDirective, ToolbarScreenshareButtonDirective,
ToolbarFullscreenButtonDirective, ToolbarFullscreenButtonDirective,
ToolbarBackgroundEffectsButtonDirective, ToolbarBackgroundEffectsButtonDirective,

View File

@ -2,6 +2,137 @@ import { AfterViewInit, Directive, ElementRef, Input, OnDestroy } from '@angular
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service'; import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
import { ToolbarAdditionalButtonsPosition } from '../../models/toolbar.model'; import { ToolbarAdditionalButtonsPosition } from '../../models/toolbar.model';
/**
* The **cameraButton** directive allows show/hide the camera toolbar button.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
*
* @example
* <ov-videoconference [toolbarCameraButton]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ToolbarComponent}.
* @example
* <ov-toolbar [cameraButton]="false"></ov-toolbar>
*/
@Directive({
selector: 'ov-videoconference[toolbarCameraButton], ov-toolbar[cameraButton]'
})
export class ToolbarCameraButtonDirective implements AfterViewInit, OnDestroy {
/**
* @ignore
*/
@Input() set toolbarCameraButton(value: boolean) {
this.cameraValue = value;
this.update(this.cameraValue);
}
/**
* @ignore
*/
@Input() set cameraButton(value: boolean) {
this.cameraValue = value;
this.update(this.cameraValue);
}
private cameraValue: boolean = true;
/**
* @ignore
*/
constructor(
public elementRef: ElementRef,
private libService: OpenViduComponentsConfigService
) {}
ngAfterViewInit() {
this.update(this.cameraValue);
}
ngOnDestroy(): void {
this.clear();
}
private clear() {
this.cameraValue = true;
this.update(true);
}
private update(value: boolean) {
if (this.libService.showCameraButton() !== value) {
this.libService.setCameraButton(value);
}
}
}
/**
* The **microphoneButton** directive allows show/hide the microphone toolbar button.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
*
* @example
* <ov-videoconference [toolbarMicrophoneButton]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ToolbarComponent}.
* @example
* <ov-toolbar [microphoneButton]="false"></ov-toolbar>
*/
@Directive({
selector: 'ov-videoconference[toolbarMicrophoneButton], ov-toolbar[microphoneButton]'
})
export class ToolbarMicrophoneButtonDirective implements AfterViewInit, OnDestroy {
/**
* @ignore
*/
@Input() set toolbarMicrophoneButton(value: boolean) {
this.microphoneValue = value;
this.update(this.microphoneValue);
}
/**
* @ignore
*/
@Input() set microphoneButton(value: boolean) {
this.microphoneValue = value;
this.update(this.microphoneValue);
}
private microphoneValue: boolean = true;
/**
* @ignore
*/
constructor(
public elementRef: ElementRef,
private libService: OpenViduComponentsConfigService
) {}
ngAfterViewInit() {
this.update(this.microphoneValue);
}
ngOnDestroy(): void {
this.clear();
}
private clear() {
this.microphoneValue = true;
this.update(true);
}
private update(value: boolean) {
if (this.libService.showMicrophoneButton() !== value) {
this.libService.setMicrophoneButton(value);
}
}
}
/** /**
* The **screenshareButton** directive allows show/hide the screenshare toolbar button. * The **screenshareButton** directive allows show/hide the screenshare toolbar button.
* *

View File

@ -30,6 +30,14 @@ export class OpenViduComponentsConfigService {
videoEnabled$: Observable<boolean>; videoEnabled$: Observable<boolean>;
private audioEnabled = <BehaviorSubject<boolean>>new BehaviorSubject(true); private audioEnabled = <BehaviorSubject<boolean>>new BehaviorSubject(true);
audioEnabled$: Observable<boolean>; audioEnabled$: Observable<boolean>;
//Toolbar settings
private cameraButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
cameraButton$: Observable<boolean>;
private microphoneButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
microphoneButton$: Observable<boolean>;
private screenshareButton = <BehaviorSubject<boolean>>new BehaviorSubject(true); private screenshareButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
screenshareButton$: Observable<boolean>; screenshareButton$: Observable<boolean>;
@ -111,6 +119,8 @@ export class OpenViduComponentsConfigService {
this.videoEnabled$ = this.videoEnabled.asObservable(); this.videoEnabled$ = this.videoEnabled.asObservable();
this.audioEnabled$ = this.audioEnabled.asObservable(); this.audioEnabled$ = this.audioEnabled.asObservable();
//Toolbar observables //Toolbar observables
this.cameraButton$ = this.cameraButton.asObservable();
this.microphoneButton$ = this.microphoneButton.asObservable();
this.screenshareButton$ = this.screenshareButton.asObservable(); this.screenshareButton$ = this.screenshareButton.asObservable();
this.fullscreenButton$ = this.fullscreenButton.asObservable(); this.fullscreenButton$ = this.fullscreenButton.asObservable();
this.backgroundEffectsButton$ = this.backgroundEffectsButton.asObservable(); this.backgroundEffectsButton$ = this.backgroundEffectsButton.asObservable();
@ -198,6 +208,23 @@ export class OpenViduComponentsConfigService {
} }
//Toolbar settings //Toolbar settings
setCameraButton(cameraButton: boolean) {
this.cameraButton.next(cameraButton);
}
showCameraButton(): boolean {
return this.cameraButton.getValue();
}
setMicrophoneButton(microphoneButton: boolean) {
this.microphoneButton.next(microphoneButton);
}
showMicrophoneButton(): boolean {
return this.microphoneButton.getValue();
}
setScreenshareButton(screenshareButton: boolean) { setScreenshareButton(screenshareButton: boolean) {
this.screenshareButton.next(screenshareButton); this.screenshareButton.next(screenshareButton);
} }

View File

@ -11,6 +11,8 @@
[participantName]="'Participant'" [participantName]="'Participant'"
[videoEnabled]="true" [videoEnabled]="true"
[audioEnabled]="true" [audioEnabled]="true"
[toolbarCameraButton]="true"
[toolbarMicrophoneButton]="true"
[toolbarScreenshareButton]="true" [toolbarScreenshareButton]="true"
[toolbarFullscreenButton]="true" [toolbarFullscreenButton]="true"
[toolbarRecordingButton]="true" [toolbarRecordingButton]="true"