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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,137 @@ import { AfterViewInit, Directive, ElementRef, Input, OnDestroy } from '@angular
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
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.
*

View File

@ -30,6 +30,14 @@ export class OpenViduComponentsConfigService {
videoEnabled$: Observable<boolean>;
private audioEnabled = <BehaviorSubject<boolean>>new BehaviorSubject(true);
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);
screenshareButton$: Observable<boolean>;
@ -111,6 +119,8 @@ export class OpenViduComponentsConfigService {
this.videoEnabled$ = this.videoEnabled.asObservable();
this.audioEnabled$ = this.audioEnabled.asObservable();
//Toolbar observables
this.cameraButton$ = this.cameraButton.asObservable();
this.microphoneButton$ = this.microphoneButton.asObservable();
this.screenshareButton$ = this.screenshareButton.asObservable();
this.fullscreenButton$ = this.fullscreenButton.asObservable();
this.backgroundEffectsButton$ = this.backgroundEffectsButton.asObservable();
@ -198,6 +208,23 @@ export class OpenViduComponentsConfigService {
}
//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) {
this.screenshareButton.next(screenshareButton);
}

View File

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