mirror of https://github.com/OpenVidu/openvidu.git
openvidu-components: Added settings panel for configuring media devices
parent
3908dfc146
commit
c8264fdb1b
|
@ -2,7 +2,7 @@ import { Component, OnInit, Output, EventEmitter, ChangeDetectionStrategy, Chang
|
|||
import { Subscription } from 'rxjs';
|
||||
import { PanelType } from '../../../models/panel.model';
|
||||
import { OpenViduAngularConfigService } from '../../../services/config/openvidu-angular.config.service';
|
||||
import { PanelService } from '../../../services/panel/panel.service';
|
||||
import { PanelEvent, PanelService } from '../../../services/panel/panel.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-activities-panel',
|
||||
|
@ -114,8 +114,8 @@ export class ActivitiesPanelComponent implements OnInit {
|
|||
|
||||
private subscribeToPanelToggling() {
|
||||
this.panelSubscription = this.panelService.panelOpenedObs.subscribe(
|
||||
(ev: { opened: boolean; type?: PanelType | string; expand?: string }) => {
|
||||
if (ev.type === PanelType.ACTIVITIES) {
|
||||
(ev: PanelEvent) => {
|
||||
if (ev.type === PanelType.ACTIVITIES && !!ev.expand) {
|
||||
this.expandedPanel = ev.expand;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
<ng-container *ngTemplateOutlet="backgroundEffectsPanelTemplate"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<!-- Settings panel -->
|
||||
<ng-container *ngIf="isSettingsPanelOpened">
|
||||
<ng-container *ngTemplateOutlet="settingsPanelTemplate"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<!-- Activities panel -->
|
||||
<ng-container *ngIf="isActivitiesPanelOpened">
|
||||
<ng-container *ngTemplateOutlet="activitiesPanelTemplate"></ng-container>
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
ActivitiesPanelDirective
|
||||
} from '../../directives/template/openvidu-angular.directive';
|
||||
import { PanelType } from '../../models/panel.model';
|
||||
import { PanelService } from '../../services/panel/panel.service';
|
||||
import { PanelEvent, PanelService } from '../../services/panel/panel.service';
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -64,6 +64,11 @@ export class PanelComponent implements OnInit {
|
|||
*/
|
||||
@ContentChild('backgroundEffectsPanel', { read: TemplateRef }) backgroundEffectsPanelTemplate: TemplateRef<any>;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
@ContentChild('settingsPanel', { read: TemplateRef }) settingsPanelTemplate: TemplateRef<any>;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
|
@ -91,11 +96,22 @@ export class PanelComponent implements OnInit {
|
|||
set externalBackgroundEffectsPanel(externalBackgroundEffectsPanel: BackgroundEffectsPanelDirective) {
|
||||
// This directive will has value only when BACKGROUND EFFECTS PANEL component tagged with '*ovBackgroundEffectsPanel'
|
||||
// is inside of the PANEL component tagged with '*ovPanel'
|
||||
if (externalBackgroundEffectsPanel) {
|
||||
this.backgroundEffectsPanelTemplate = externalBackgroundEffectsPanel.template;
|
||||
}
|
||||
// TODO: backgroundEffectsPanel does not provides customization
|
||||
// if (externalBackgroundEffectsPanel) {
|
||||
// this.backgroundEffectsPanelTemplate = externalBackgroundEffectsPanel.template;
|
||||
// }
|
||||
}
|
||||
|
||||
// TODO: settingsPanel does not provides customization
|
||||
// @ContentChild(SettingsPanelDirective)
|
||||
// set externalSettingsPanel(externalSettingsPanel: SettingsPanelDirective) {
|
||||
// This directive will has value only when SETTINGS PANEL component tagged with '*ovSettingsPanel'
|
||||
// is inside of the PANEL component tagged with '*ovPanel'
|
||||
// if (externalSettingsPanel) {
|
||||
// this.settingsPanelTemplate = externalSettingsPanel.template;
|
||||
// }
|
||||
// }
|
||||
|
||||
@ContentChild(ActivitiesPanelDirective)
|
||||
set externalActivitiesPanel(externalActivitiesPanel: ActivitiesPanelDirective) {
|
||||
// This directive will has value only when ACTIVITIES PANEL component tagged with '*ovActivitiesPanel'
|
||||
|
@ -126,6 +142,7 @@ export class PanelComponent implements OnInit {
|
|||
isParticipantsPanelOpened: boolean;
|
||||
isChatPanelOpened: boolean;
|
||||
isBackgroundEffectsPanelOpened: boolean;
|
||||
isSettingsPanelOpened: boolean;
|
||||
isActivitiesPanelOpened: boolean;
|
||||
|
||||
/**
|
||||
|
@ -150,15 +167,14 @@ export class PanelComponent implements OnInit {
|
|||
}
|
||||
|
||||
private subscribeToPanelToggling() {
|
||||
this.panelSubscription = this.panelService.panelOpenedObs
|
||||
.pipe(skip(1))
|
||||
.subscribe((ev: { opened: boolean; type?: PanelType | string }) => {
|
||||
this.isChatPanelOpened = ev.opened && ev.type === PanelType.CHAT;
|
||||
this.isParticipantsPanelOpened = ev.opened && ev.type === PanelType.PARTICIPANTS;
|
||||
this.isBackgroundEffectsPanelOpened = ev.opened && ev.type === PanelType.BACKGROUND_EFFECTS;
|
||||
this.isActivitiesPanelOpened = ev.opened && ev.type === PanelType.ACTIVITIES;
|
||||
this.isExternalPanelOpened = ev.opened && ev.type !== PanelType.PARTICIPANTS && ev.type !== PanelType.CHAT;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
this.panelSubscription = this.panelService.panelOpenedObs.pipe(skip(1)).subscribe((ev: PanelEvent) => {
|
||||
this.isChatPanelOpened = ev.opened && ev.type === PanelType.CHAT;
|
||||
this.isParticipantsPanelOpened = ev.opened && ev.type === PanelType.PARTICIPANTS;
|
||||
this.isBackgroundEffectsPanelOpened = ev.opened && ev.type === PanelType.BACKGROUND_EFFECTS;
|
||||
this.isSettingsPanelOpened = ev.opened && ev.type === PanelType.SETTINGS;
|
||||
this.isActivitiesPanelOpened = ev.opened && ev.type === PanelType.ACTIVITIES;
|
||||
this.isExternalPanelOpened = ev.opened && ev.type !== PanelType.PARTICIPANTS && ev.type !== PanelType.CHAT;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
.settings-container {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.item-menu {
|
||||
padding-right: 5px;
|
||||
border-right: 1px solid var(--ov-secondary-color);
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
padding: 16px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.option {
|
||||
border-radius: var(--ov-panel-radius);
|
||||
}
|
||||
.lang-container button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
mat-list-option[aria-selected='true'] {
|
||||
background: var(--ov-light-color);
|
||||
}
|
||||
::ng-deep .mat-list-item-content {
|
||||
padding: 5px !important;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<div class="panel-container" id="background-effects-container" fxLayout="column" fxLayoutAlign="space-evenly none">
|
||||
<div class="panel-header-container" fxFlex="55px" fxLayoutAlign="start center">
|
||||
<h3 class="panel-title">{{ 'PANEL.SETTINGS.TITLE' | translate }}</h3>
|
||||
<button class="panel-close-button" mat-icon-button matTooltip="{{ 'PANEL.CLOSE' | translate }}" (click)="close()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-container" fxFlex="100%" fxLayoutAlign="space-evenly none">
|
||||
<div class="item-menu">
|
||||
<mat-selection-list #settings [multiple]="false">
|
||||
<mat-list-option class="option" [selected]="true" value="general">
|
||||
<mat-icon mat-list-icon>manage_accounts</mat-icon>
|
||||
<div mat-line>{{ 'PANEL.SETTINGS.GENERAL' | translate }}</div>
|
||||
</mat-list-option>
|
||||
<mat-list-option class="option" value="video">
|
||||
<mat-icon mat-list-icon>videocam</mat-icon>
|
||||
<div mat-line>{{ 'PANEL.SETTINGS.VIDEO' | translate }}</div>
|
||||
</mat-list-option>
|
||||
<mat-list-option class="option" value="audio">
|
||||
<mat-icon mat-list-icon>mic</mat-icon>
|
||||
<div mat-line>{{ 'PANEL.SETTINGS.AUDIO' | translate }}</div>
|
||||
</mat-list-option>
|
||||
</mat-selection-list>
|
||||
</div>
|
||||
|
||||
<div class="item-content">
|
||||
<div *ngIf="settings.selectedOptions.selected[0]?.value === 'general'">
|
||||
<ov-nickname-input></ov-nickname-input>
|
||||
|
||||
<mat-list>
|
||||
<mat-list-item>
|
||||
<mat-icon mat-list-icon>language</mat-icon>
|
||||
<div mat-line>{{ 'PANEL.SETTINGS.LANGUAGE' | translate }}:</div>
|
||||
<ov-lang-selector></ov-lang-selector>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
<ov-video-devices-select *ngIf="settings.selectedOptions.selected[0]?.value === 'video'"></ov-video-devices-select>
|
||||
<ov-audio-devices-select *ngIf="settings.selectedOptions.selected[0]?.value === 'audio'"></ov-audio-devices-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SettingsPanelComponent } from './settings-panel.component';
|
||||
|
||||
describe('SettingsPanelComponent', () => {
|
||||
let component: SettingsPanelComponent;
|
||||
let fixture: ComponentFixture<SettingsPanelComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SettingsPanelComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SettingsPanelComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
import { Component, HostListener, OnInit } from '@angular/core';
|
||||
import { MatOptionSelectionChange } from '@angular/material/core';
|
||||
import { PanelType } from '../../../models/panel.model';
|
||||
import { PanelService } from '../../../services/panel/panel.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-settings-panel',
|
||||
templateUrl: './settings-panel.component.html',
|
||||
styleUrls: ['../panel.component.css', './settings-panel.component.css']
|
||||
})
|
||||
export class SettingsPanelComponent implements OnInit {
|
||||
|
||||
selectedOption: string;
|
||||
constructor(private panelService: PanelService) {}
|
||||
ngOnInit() {}
|
||||
|
||||
close() {
|
||||
this.panelService.togglePanel(PanelType.SETTINGS);
|
||||
}
|
||||
}
|
|
@ -11,11 +11,6 @@
|
|||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.lang-button {
|
||||
background-color: var(--ov-logo-background-color);
|
||||
color: var(--ov-text-color);
|
||||
}
|
||||
|
||||
#branding-logo {
|
||||
background-color: var(--ov-logo-background-color);
|
||||
border-radius: var(--ov-panel-radius);
|
||||
|
@ -81,31 +76,6 @@ hr {
|
|||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
#nickname-input-container,
|
||||
.device-container-element {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#nickname-input-container button,
|
||||
.device-container-element button {
|
||||
margin: auto 10px auto auto;
|
||||
}
|
||||
|
||||
#nickname-input-container button.mat-button-disabled {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
#nickname-input-container mat-form-field,
|
||||
.device-container-element mat-form-field {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#nickname-input-container mat-form-field {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.mat-form-field-appearance-fill .mat-form-field-flex {
|
||||
/* background-color: var(--ov-text-color); */
|
||||
border-radius: var(--ov-video-radius);
|
||||
|
@ -118,11 +88,6 @@ hr {
|
|||
display: block !important;
|
||||
}
|
||||
|
||||
#camera-button {
|
||||
border-radius: var(--ov-buttons-radius);
|
||||
/* background-color: var(--ov-secondary-color) !important; */
|
||||
/* color: var(--ov-text-color) !important; */
|
||||
}
|
||||
|
||||
.join-btn-container {
|
||||
width: inherit;
|
||||
|
@ -137,37 +102,6 @@ hr {
|
|||
border-radius: var(--ov-video-radius);
|
||||
}
|
||||
|
||||
.warn-btn {
|
||||
color: var(--ov-text-color);
|
||||
background-color: var(--ov-warn-color) !important;
|
||||
}
|
||||
|
||||
.active-btn {
|
||||
color: var(--ov-text-color);
|
||||
background-color: var(--ov-tertiary-color) !important;
|
||||
}
|
||||
.media-btn {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
::ng-deep .mat-button-toggle-appearance-standard .mat-button-toggle-label-content {
|
||||
padding: 1px !important;
|
||||
}
|
||||
|
||||
::ng-deep .mat-input-element {
|
||||
caret-color: #000000;
|
||||
}
|
||||
::ng-deep .mat-primary .mat-option.mat-selected:not(.mat-option-disabled) {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
::ng-deep .mat-form-field-label {
|
||||
color: var(--ov-panel-text-color) !important;
|
||||
}
|
||||
|
||||
::ng-deep .mat-form-field.mat-focused .mat-form-field-ripple {
|
||||
background-color: var(--ov-panel-text-color) !important;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 480px) {
|
||||
.container,
|
||||
|
|
|
@ -3,15 +3,8 @@
|
|||
<!-- <span>OpenVidu Call</span> -->
|
||||
<span class="spacer"></span>
|
||||
|
||||
<button mat-flat-button [matMenuTriggerFor]="menu" class="lang-button" *ngIf="!isMinimal">
|
||||
<span>{{langSelected?.name}}</span>
|
||||
<mat-icon>expand_more</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item *ngFor="let lang of languages" (click)="onLangSelected(lang.ISO)">
|
||||
<span>{{lang.name}}</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
<ov-lang-selector *ngIf="!isMinimal"></ov-lang-selector>
|
||||
|
||||
</mat-toolbar>
|
||||
|
||||
<div class="container" id="prejoin-container" fxLayout.gt-sm="row" fxLayout.lt-md="column">
|
||||
|
@ -50,98 +43,19 @@
|
|||
<div fxFlex.gt-sm="100%" fxFlex.lt-md="33%" fxLayoutAlign="center center" fxFlexFill class="nickname-container">
|
||||
<h4 *ngIf="windowSize >= 960">{{ 'PREJOIN.NICKNAME_SECTION' | translate }}</h4>
|
||||
<hr *ngIf="windowSize >= 960" />
|
||||
<div id="nickname-input-container">
|
||||
<button mat-icon-button disabled>
|
||||
<mat-icon>person</mat-icon>
|
||||
</button>
|
||||
<mat-form-field appearance="standard">
|
||||
<mat-label>{{ 'PREJOIN.NICKNAME' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
maxlength="20"
|
||||
[(ngModel)]="nickname"
|
||||
(change)="updateNickname()"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<!-- <mat-button-toggle-group style="border-radius: 20px">
|
||||
<button mat-icon-button class="media-btn">
|
||||
<mat-icon matTooltip="Mute your audio">mic</mat-icon>
|
||||
</button>
|
||||
<mat-button-toggle class="split-button-1 drop-down-button" [matMenuTriggerFor]="dropdownMenuOne">
|
||||
<mat-icon>arrow_drop_down</mat-icon>
|
||||
</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
|
||||
<mat-menu #dropdownMenuOne="matMenu">
|
||||
<button mat-menu-item>One</button>
|
||||
<button mat-menu-item>Two</button>
|
||||
<button mat-menu-item>Three</button>
|
||||
</mat-menu> -->
|
||||
<ov-nickname-input></ov-nickname-input>
|
||||
</div>
|
||||
|
||||
<div fxFlex.gt-sm="100%" fxFlex.lt-md="33%" fxLayoutAlign="center center" fxFlexFill class="buttons-container">
|
||||
<h4 *ngIf="windowSize >= 960">{{ 'PREJOIN.DEVICE_SECTION' | translate }}</h4>
|
||||
<hr *ngIf="windowSize >= 960" />
|
||||
|
||||
<!-- Camera -->
|
||||
<div class="device-container-element">
|
||||
<button
|
||||
mat-icon-button
|
||||
id="camera-button"
|
||||
[disabled]="!hasVideoDevices || videoMuteChanging"
|
||||
[class.warn-btn]="isVideoMuted"
|
||||
(click)="toggleCam()"
|
||||
>
|
||||
<mat-icon *ngIf="!isVideoMuted" matTooltip="{{ 'TOOLBAR.MUTE_VIDEO' | translate }}" id="videocam"
|
||||
>videocam</mat-icon
|
||||
>
|
||||
<mat-icon *ngIf="isVideoMuted" matTooltip="{{ 'TOOLBAR.UNMUTE_VIDEO' | translate }}" id="videocam_off"
|
||||
>videocam_off</mat-icon
|
||||
>
|
||||
</button>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'PREJOIN.VIDEO_DEVICE' | translate }}</mat-label>
|
||||
<mat-select
|
||||
[disabled]="isVideoMuted || !hasVideoDevices"
|
||||
[value]="cameraSelected?.device"
|
||||
(selectionChange)="onCameraSelected($event)"
|
||||
>
|
||||
<mat-option *ngFor="let camera of cameras" [value]="camera.device">
|
||||
{{ camera.label }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<ov-video-devices-select></ov-video-devices-select>
|
||||
|
||||
<!-- Microphone -->
|
||||
<div class="device-container-element">
|
||||
<button
|
||||
mat-icon-button
|
||||
id="microhpone-button"
|
||||
[disabled]="!hasAudioDevices"
|
||||
[class.warn-btn]="isAudioMuted"
|
||||
(click)="toggleMic()"
|
||||
>
|
||||
<mat-icon *ngIf="!isAudioMuted" matTooltip="{{ 'TOOLBAR.MUTE_AUDIO' | translate }}" id="mic">mic</mat-icon>
|
||||
<mat-icon *ngIf="isAudioMuted" matTooltip="{{ 'TOOLBAR.UNMUTE_AUDIO' | translate }}" id="mic_off"
|
||||
>mic_off</mat-icon
|
||||
>
|
||||
</button>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'PREJOIN.AUDIO_DEVICE' | translate }}</mat-label>
|
||||
<mat-select
|
||||
[disabled]="isAudioMuted || !hasAudioDevices"
|
||||
[value]="microphoneSelected?.device"
|
||||
(selectionChange)="onMicrophoneSelected($event)"
|
||||
>
|
||||
<mat-option *ngFor="let microphone of microphones" [value]="microphone.device">
|
||||
{{ microphone.label }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<ov-audio-devices-select></ov-audio-devices-select>
|
||||
|
||||
</div>
|
||||
|
||||
<div fxFlex.gt-sm="60%" fxLayout.lt-md="column" fxLayoutAlign="center center" fxFlexFill class="join-btn-container">
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Component, HostListener, OnDestroy, OnInit, Output, EventEmitter, ViewC
|
|||
import { MatMenuTrigger } from '@angular/material/menu';
|
||||
import { MatSelect } from '@angular/material/select';
|
||||
|
||||
import { PublisherProperties } from 'openvidu-browser';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CustomDevice } from '../../models/device.model';
|
||||
import { ILogger } from '../../models/logger.model';
|
||||
|
@ -10,15 +9,10 @@ import { PanelType } from '../../models/panel.model';
|
|||
import { ParticipantAbstractModel } from '../../models/participant.model';
|
||||
import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service';
|
||||
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
|
||||
import { DeviceService } from '../../services/device/device.service';
|
||||
import { LayoutService } from '../../services/layout/layout.service';
|
||||
import { LoggerService } from '../../services/logger/logger.service';
|
||||
import { OpenViduService } from '../../services/openvidu/openvidu.service';
|
||||
import { PanelService } from '../../services/panel/panel.service';
|
||||
import { ParticipantService } from '../../services/participant/participant.service';
|
||||
import { StorageService } from '../../services/storage/storage.service';
|
||||
import { TranslateService } from '../../services/translate/translate.service';
|
||||
import { VirtualBackgroundService } from '../../services/virtual-background/virtual-background.service';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -39,8 +33,6 @@ export class PreJoinComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
@ViewChild(MatSelect) matSelect: MatSelect;
|
||||
|
||||
@Output() onJoinButtonClicked = new EventEmitter<any>();
|
||||
languages: { name: string; ISO: string }[] = [];
|
||||
langSelected: { name: string; ISO: string };
|
||||
cameras: CustomDevice[];
|
||||
microphones: CustomDevice[];
|
||||
cameraSelected: CustomDevice;
|
||||
|
@ -79,15 +71,10 @@ export class PreJoinComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
|
||||
constructor(
|
||||
private layoutService: LayoutService,
|
||||
private deviceSrv: DeviceService,
|
||||
private loggerSrv: LoggerService,
|
||||
private openviduService: OpenViduService,
|
||||
private participantService: ParticipantService,
|
||||
protected panelService: PanelService,
|
||||
private libService: OpenViduAngularConfigService,
|
||||
private storageSrv: StorageService,
|
||||
private backgroundService: VirtualBackgroundService,
|
||||
private translateService: TranslateService,
|
||||
protected cdkSrv: CdkOverlayService
|
||||
) {
|
||||
this.log = this.loggerSrv.get('PreJoinComponent');
|
||||
|
@ -96,20 +83,8 @@ export class PreJoinComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
ngOnInit() {
|
||||
this.subscribeToPrejoinDirectives();
|
||||
this.subscribeToLocalParticipantEvents();
|
||||
this.languages = this.translateService.getLanguagesInfo();
|
||||
this.langSelected = this.translateService.getLangSelected();
|
||||
|
||||
this.windowSize = window.innerWidth;
|
||||
this.hasVideoDevices = this.deviceSrv.hasVideoDeviceAvailable();
|
||||
this.hasAudioDevices = this.deviceSrv.hasAudioDeviceAvailable();
|
||||
this.microphones = this.deviceSrv.getMicrophones();
|
||||
this.cameras = this.deviceSrv.getCameras();
|
||||
this.cameraSelected = this.deviceSrv.getCameraSelected();
|
||||
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected();
|
||||
|
||||
this.isVideoMuted = this.deviceSrv.isVideoMuted();
|
||||
this.isAudioMuted = this.deviceSrv.isAudioMuted();
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
|
@ -133,79 +108,6 @@ export class PreJoinComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.panelService.closePanel();
|
||||
}
|
||||
|
||||
async onCameraSelected(event: any) {
|
||||
const videoSource = event?.value;
|
||||
// Is New deviceId different from the old one?
|
||||
if (this.deviceSrv.needUpdateVideoTrack(videoSource)) {
|
||||
const mirror = this.deviceSrv.cameraNeedsMirror(videoSource);
|
||||
//TODO: Uncomment this when replaceTrack issue is fixed
|
||||
// const pp: PublisherProperties = { videoSource, audioSource: false, mirror };
|
||||
// await this.openviduService.replaceTrack(VideoType.CAMERA, pp);
|
||||
// TODO: Remove this when replaceTrack issue is fixed
|
||||
const pp: PublisherProperties = { videoSource, audioSource: this.microphoneSelected.device, mirror };
|
||||
|
||||
// Reapply Virtual Background to new Publisher if necessary
|
||||
const backgroundSelected = this.backgroundService.backgroundSelected.getValue();
|
||||
if (this.backgroundService.isBackgroundApplied()) {
|
||||
await this.backgroundService.removeBackground();
|
||||
}
|
||||
await this.openviduService.republishTrack(pp);
|
||||
if (this.backgroundService.isBackgroundApplied()) {
|
||||
await this.backgroundService.applyBackground(this.backgroundService.backgrounds.find((b) => b.id === backgroundSelected));
|
||||
}
|
||||
|
||||
this.deviceSrv.setCameraSelected(videoSource);
|
||||
this.cameraSelected = this.deviceSrv.getCameraSelected();
|
||||
}
|
||||
}
|
||||
|
||||
async onMicrophoneSelected(event: any) {
|
||||
const audioSource = event?.value;
|
||||
if (this.deviceSrv.needUpdateAudioTrack(audioSource)) {
|
||||
//TODO: Uncomment this when replaceTrack issue is fixed
|
||||
// const pp: PublisherProperties = { audioSource, videoSource: false };
|
||||
// await this.openviduService.replaceTrack(VideoType.CAMERA, pp);
|
||||
// TODO: Remove this when replaceTrack issue is fixed
|
||||
const mirror = this.deviceSrv.cameraNeedsMirror(this.cameraSelected.device);
|
||||
const pp: PublisherProperties = { videoSource: this.cameraSelected.device, audioSource, mirror };
|
||||
await this.openviduService.republishTrack(pp);
|
||||
|
||||
this.deviceSrv.setMicSelected(audioSource);
|
||||
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected();
|
||||
}
|
||||
}
|
||||
|
||||
onLangSelected(lang: string) {
|
||||
this.translateService.setLanguage(lang);
|
||||
this.storageSrv.setLang(lang);
|
||||
this.langSelected = this.translateService.getLangSelected();
|
||||
}
|
||||
|
||||
async toggleCam() {
|
||||
this.videoMuteChanging = true;
|
||||
const publish = this.isVideoMuted;
|
||||
await this.openviduService.publishVideo(publish);
|
||||
this.isVideoMuted = !this.isVideoMuted;
|
||||
this.storageSrv.setVideoMuted(this.isVideoMuted);
|
||||
if (this.isVideoMuted && this.panelService.isExternalPanelOpened()) {
|
||||
this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS);
|
||||
}
|
||||
this.videoMuteChanging = false;
|
||||
}
|
||||
|
||||
toggleMic() {
|
||||
const publish = this.isAudioMuted;
|
||||
this.openviduService.publishAudio(publish);
|
||||
this.isAudioMuted = !this.isAudioMuted;
|
||||
this.storageSrv.setAudioMuted(this.isAudioMuted);
|
||||
}
|
||||
|
||||
updateNickname() {
|
||||
this.nickname = this.nickname === '' ? this.participantService.getMyNickname() : this.nickname;
|
||||
this.participantService.setMyNickname(this.nickname);
|
||||
this.storageSrv.setNickname(this.nickname);
|
||||
}
|
||||
|
||||
joinSession() {
|
||||
this.onJoinButtonClicked.emit();
|
||||
this.panelService.closePanel();
|
||||
|
|
|
@ -24,6 +24,11 @@
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
.big {
|
||||
width: 650px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.mat-drawer.mat-drawer-side {
|
||||
z-index: 0 !important;
|
||||
}
|
||||
|
@ -44,7 +49,8 @@
|
|||
background-color: var(--ov-primary-color);
|
||||
}
|
||||
|
||||
#toolbar-container, #footer-container {
|
||||
#toolbar-container,
|
||||
#footer-container {
|
||||
background-color: var(--ov-primary-color);
|
||||
min-width: 400px !important;
|
||||
width: 100%;
|
||||
|
@ -63,9 +69,7 @@
|
|||
min-width: 400px !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.reconnecting-container{
|
||||
.reconnecting-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1000;
|
||||
|
@ -74,10 +78,28 @@
|
|||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
#session-container {
|
||||
width: 100%;
|
||||
/* position: fixed; */
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .mat-button-toggle-appearance-standard .mat-button-toggle-label-content {
|
||||
padding: 1px !important;
|
||||
}
|
||||
|
||||
::ng-deep .mat-input-element {
|
||||
caret-color: #000000;
|
||||
}
|
||||
::ng-deep .mat-primary .mat-option.mat-selected:not(.mat-option-disabled) {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
::ng-deep .mat-form-field-label {
|
||||
color: var(--ov-panel-text-color) !important;
|
||||
}
|
||||
|
||||
::ng-deep .mat-form-field.mat-focused .mat-form-field-ripple {
|
||||
background-color: var(--ov-panel-text-color) !important;
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
<div id="session-container">
|
||||
<mat-sidenav-container #videoContainer class="sidenav-container">
|
||||
<mat-sidenav-container #container #videoContainer class="sidenav-container">
|
||||
<mat-sidenav
|
||||
#sidenav
|
||||
mode="{{ sidenavMode }}"
|
||||
position="end"
|
||||
class="sidenav-menu"
|
||||
[ngClass]="{big: settingsPanelOpened}"
|
||||
fixedInViewport="true"
|
||||
fixedTopGap="0"
|
||||
fixedBottomGap="0"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ContentChild,
|
||||
ElementRef,
|
||||
|
@ -31,12 +32,12 @@ import { TokenService } from '../../services/token/token.service';
|
|||
import { ActionService } from '../../services/action/action.service';
|
||||
import { Signal } from '../../models/signal.model';
|
||||
import { ParticipantService } from '../../services/participant/participant.service';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
import { MatDrawerContainer, MatSidenav } from '@angular/material/sidenav';
|
||||
import { SidenavMode } from '../../models/layout.model';
|
||||
import { LayoutService } from '../../services/layout/layout.service';
|
||||
import { Subscription, skip } from 'rxjs';
|
||||
import { PanelType } from '../../models/panel.model';
|
||||
import { PanelService } from '../../services/panel/panel.service';
|
||||
import { PanelEvent, PanelService } from '../../services/panel/panel.service';
|
||||
import { RecordingService } from '../../services/recording/recording.service';
|
||||
import { TranslateService } from '../../services/translate/translate.service';
|
||||
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
|
||||
|
@ -66,6 +67,9 @@ export class SessionComponent implements OnInit {
|
|||
sideMenu: MatSidenav;
|
||||
|
||||
sidenavMode: SidenavMode = SidenavMode.SIDE;
|
||||
settingsPanelOpened: boolean;
|
||||
drawer: MatDrawerContainer;
|
||||
|
||||
protected readonly SIDENAV_WIDTH_LIMIT_MODE = 790;
|
||||
|
||||
protected menuSubscription: Subscription;
|
||||
|
@ -87,7 +91,8 @@ export class SessionComponent implements OnInit {
|
|||
protected panelService: PanelService,
|
||||
private recordingService: RecordingService,
|
||||
private translateService: TranslateService,
|
||||
private platformService: PlatformService
|
||||
private platformService: PlatformService,
|
||||
private cd: ChangeDetectorRef
|
||||
) {
|
||||
this.log = this.loggerSrv.get('SessionComponent');
|
||||
}
|
||||
|
@ -123,9 +128,25 @@ export class SessionComponent implements OnInit {
|
|||
}, 0);
|
||||
}
|
||||
|
||||
@ViewChild('container')
|
||||
set container(container: MatDrawerContainer) {
|
||||
setTimeout(() => {
|
||||
if (container) {
|
||||
this.drawer = container;
|
||||
this.drawer._contentMarginChanges.subscribe(() => {
|
||||
setTimeout(() => {
|
||||
this.stopUpdateLayoutInterval();
|
||||
this.layoutService.update();
|
||||
this.drawer.autosize = false;
|
||||
}, 250);
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
if (!this.usedInPrejoinPage) {
|
||||
if(!this.tokenService.getScreenToken()){
|
||||
if (!this.tokenService.getScreenToken()) {
|
||||
// Hide screenshare button if screen token does not exist
|
||||
this.libService.screenshareButton.next(false);
|
||||
}
|
||||
|
@ -170,27 +191,34 @@ export class SessionComponent implements OnInit {
|
|||
|
||||
protected subscribeToTogglingMenu() {
|
||||
this.sideMenu.openedChange.subscribe(() => {
|
||||
if (this.updateLayoutInterval) {
|
||||
clearInterval(this.updateLayoutInterval);
|
||||
}
|
||||
this.stopUpdateLayoutInterval();
|
||||
this.layoutService.update();
|
||||
});
|
||||
|
||||
this.sideMenu.openedStart.subscribe(() => {
|
||||
this.updateLayoutInterval = setInterval(() => this.layoutService.update(), 50);
|
||||
this.startUpdateLayoutInterval();
|
||||
});
|
||||
|
||||
this.sideMenu.closedStart.subscribe(() => {
|
||||
this.updateLayoutInterval = setInterval(() => this.layoutService.update(), 50);
|
||||
this.startUpdateLayoutInterval();
|
||||
});
|
||||
|
||||
this.menuSubscription = this.panelService.panelOpenedObs
|
||||
.pipe(skip(1))
|
||||
.subscribe((ev: { opened: boolean; type?: PanelType | string }) => {
|
||||
if (this.sideMenu) {
|
||||
ev.opened ? this.sideMenu.open() : this.sideMenu.close();
|
||||
this.menuSubscription = this.panelService.panelOpenedObs.pipe(skip(1)).subscribe((ev: PanelEvent) => {
|
||||
if (this.sideMenu) {
|
||||
this.settingsPanelOpened = ev.opened && ev.type === PanelType.SETTINGS;
|
||||
|
||||
if (this.sideMenu.opened && ev.opened) {
|
||||
if (ev.type === PanelType.SETTINGS || ev.oldType === PanelType.SETTINGS) {
|
||||
// Switch from SETTINGS to another panel and vice versa.
|
||||
// As the SETTINGS panel will be bigger than others, the sidenav container must be updated.
|
||||
// Setting autosize to 'true' allows update it.
|
||||
this.drawer.autosize = true;
|
||||
this.startUpdateLayoutInterval();
|
||||
}
|
||||
}
|
||||
});
|
||||
ev.opened ? this.sideMenu.open() : this.sideMenu.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected subscribeToLayoutWidth() {
|
||||
|
@ -324,4 +352,16 @@ export class SessionComponent implements OnInit {
|
|||
this.recordingService.stopRecording(event);
|
||||
});
|
||||
}
|
||||
|
||||
private startUpdateLayoutInterval() {
|
||||
this.updateLayoutInterval = setInterval(() => {
|
||||
this.layoutService.update();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
private stopUpdateLayoutInterval() {
|
||||
if (this.updateLayoutInterval) {
|
||||
clearInterval(this.updateLayoutInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
.device-container-element mat-form-field {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
color: #000000;
|
||||
}
|
||||
.device-container-element button {
|
||||
margin: auto 10px auto auto;
|
||||
}
|
||||
|
||||
.device-container-element {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.warn-btn {
|
||||
color: var(--ov-text-color);
|
||||
background-color: var(--ov-warn-color) !important;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<div class="device-container-element">
|
||||
<button
|
||||
mat-icon-button
|
||||
id="microhpone-button"
|
||||
[disabled]="!hasAudioDevices"
|
||||
[class.warn-btn]="isAudioMuted"
|
||||
(click)="toggleMic()"
|
||||
>
|
||||
<mat-icon *ngIf="!isAudioMuted" matTooltip="{{ 'TOOLBAR.MUTE_AUDIO' | translate }}" id="mic">mic</mat-icon>
|
||||
<mat-icon *ngIf="isAudioMuted" matTooltip="{{ 'TOOLBAR.UNMUTE_AUDIO' | translate }}" id="mic_off"
|
||||
>mic_off</mat-icon
|
||||
>
|
||||
</button>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'PREJOIN.AUDIO_DEVICE' | translate }}</mat-label>
|
||||
<mat-select
|
||||
[disabled]="isAudioMuted || !hasAudioDevices"
|
||||
[value]="microphoneSelected?.device"
|
||||
(selectionChange)="onMicrophoneSelected($event)"
|
||||
>
|
||||
<mat-option *ngFor="let microphone of microphones" [value]="microphone.device">
|
||||
{{ microphone.label }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AudioDevicesComponent } from './audio-devices.component';
|
||||
|
||||
describe('AudioDevicesComponent', () => {
|
||||
let component: AudioDevicesComponent;
|
||||
let fixture: ComponentFixture<AudioDevicesComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AudioDevicesComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AudioDevicesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,80 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { PublisherProperties } from 'openvidu-browser';
|
||||
import { DeviceService } from '../../../services/device/device.service';
|
||||
import { OpenViduService } from '../../../services/openvidu/openvidu.service';
|
||||
import { StorageService } from '../../../services/storage/storage.service';
|
||||
import { CustomDevice } from '../../../models/device.model';
|
||||
import { ParticipantAbstractModel } from '../../../models/participant.model';
|
||||
import { ParticipantService } from '../../../services/participant/participant.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ov-audio-devices-select',
|
||||
templateUrl: './audio-devices.component.html',
|
||||
styleUrls: ['./audio-devices.component.css']
|
||||
})
|
||||
export class AudioDevicesComponent implements OnInit, OnDestroy {
|
||||
hasAudioDevices: boolean;
|
||||
isAudioMuted: boolean;
|
||||
microphoneSelected: CustomDevice;
|
||||
microphones: CustomDevice[] = [];
|
||||
private localParticipantSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private openviduService: OpenViduService,
|
||||
private deviceSrv: DeviceService,
|
||||
private storageSrv: StorageService,
|
||||
protected participantService: ParticipantService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subscribeToParticipantMediaProperties();
|
||||
|
||||
this.hasAudioDevices = this.deviceSrv.hasAudioDeviceAvailable();
|
||||
this.microphones = this.deviceSrv.getMicrophones();
|
||||
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected();
|
||||
this.isAudioMuted = this.deviceSrv.isAudioMuted();
|
||||
if (this.openviduService.isSessionConnected()) {
|
||||
this.isAudioMuted = !this.participantService.isMyAudioActive();
|
||||
} else {
|
||||
this.isAudioMuted = this.deviceSrv.isAudioMuted();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
toggleMic() {
|
||||
const publish = this.isAudioMuted;
|
||||
this.openviduService.publishAudio(publish);
|
||||
}
|
||||
|
||||
async onMicrophoneSelected(event: any) {
|
||||
const audioSource = event?.value;
|
||||
if (this.deviceSrv.needUpdateAudioTrack(audioSource)) {
|
||||
//TODO: Uncomment this when replaceTrack issue is fixed
|
||||
// const pp: PublisherProperties = { audioSource, videoSource: false };
|
||||
// await this.openviduService.replaceTrack(VideoType.CAMERA, pp);
|
||||
// TODO: Remove this when replaceTrack issue is fixed
|
||||
const mirror = this.deviceSrv.cameraNeedsMirror(this.deviceSrv.getCameraSelected().device);
|
||||
const pp: PublisherProperties = { videoSource: this.deviceSrv.getCameraSelected().device, audioSource, mirror };
|
||||
await this.openviduService.republishTrack(pp);
|
||||
|
||||
this.deviceSrv.setMicSelected(audioSource);
|
||||
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected();
|
||||
}
|
||||
}
|
||||
|
||||
private subscribeToParticipantMediaProperties() {
|
||||
this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => {
|
||||
if (p) {
|
||||
this.isAudioMuted = !p.hasAudioActive();
|
||||
this.storageSrv.setAudioMuted(this.isAudioMuted);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
.lang-button {
|
||||
background-color: var(--ov-logo-background-color);
|
||||
color: var(--ov-text-color);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<button mat-flat-button [matMenuTriggerFor]="menu" class="lang-button">
|
||||
<span>{{langSelected?.name}}</span>
|
||||
<mat-icon>expand_more</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item *ngFor="let lang of languages" (click)="onLangSelected(lang.ISO)">
|
||||
<span>{{lang.name}}</span>
|
||||
</button>
|
||||
</mat-menu>
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LangSelectorComponent } from './lang-selector.component';
|
||||
|
||||
describe('LangSelectorComponent', () => {
|
||||
let component: LangSelectorComponent;
|
||||
let fixture: ComponentFixture<LangSelectorComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LangSelectorComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LangSelectorComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { StorageService } from '../../../services/storage/storage.service';
|
||||
import { TranslateService } from '../../../services/translate/translate.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-lang-selector',
|
||||
templateUrl: './lang-selector.component.html',
|
||||
styleUrls: ['./lang-selector.component.css']
|
||||
})
|
||||
export class LangSelectorComponent implements OnInit {
|
||||
langSelected: { name: string; ISO: string };
|
||||
languages: { name: string; ISO: string }[] = [];
|
||||
|
||||
constructor(private translateService: TranslateService, private storageSrv: StorageService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.languages = this.translateService.getLanguagesInfo();
|
||||
this.langSelected = this.translateService.getLangSelected();
|
||||
}
|
||||
|
||||
onLangSelected(lang: string) {
|
||||
this.translateService.setLanguage(lang);
|
||||
this.storageSrv.setLang(lang);
|
||||
this.langSelected = this.translateService.getLangSelected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#nickname-input-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#nickname-input-container button {
|
||||
margin: auto 10px auto auto;
|
||||
}
|
||||
|
||||
#nickname-input-container button.mat-button-disabled {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
#nickname-input-container mat-form-field {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#nickname-input-container mat-form-field {
|
||||
color: #000000;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<div id="nickname-input-container">
|
||||
<button mat-icon-button disabled>
|
||||
<mat-icon>person</mat-icon>
|
||||
</button>
|
||||
<mat-form-field appearance="standard">
|
||||
<mat-label>{{ 'PREJOIN.NICKNAME' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
maxlength="20"
|
||||
[(ngModel)]="nickname"
|
||||
(change)="updateNickname()"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NicknameInputComponent } from './nickname-input.component';
|
||||
|
||||
describe('NicknameInputComponent', () => {
|
||||
let component: NicknameInputComponent;
|
||||
let fixture: ComponentFixture<NicknameInputComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ NicknameInputComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NicknameInputComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ParticipantAbstractModel } from '../../../models/participant.model';
|
||||
import { ParticipantService } from '../../../services/participant/participant.service';
|
||||
import { StorageService } from '../../../services/storage/storage.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-nickname-input',
|
||||
templateUrl: './nickname-input.component.html',
|
||||
styleUrls: ['./nickname-input.component.css']
|
||||
})
|
||||
export class NicknameInputComponent implements OnInit {
|
||||
nickname: string;
|
||||
localParticipantSubscription: Subscription;
|
||||
|
||||
constructor(private participantService: ParticipantService, private storageSrv: StorageService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subscribeToParticipantProperties();
|
||||
this.nickname = this.participantService.getMyNickname();
|
||||
}
|
||||
|
||||
updateNickname() {
|
||||
this.nickname = this.nickname === '' ? this.participantService.getMyNickname() : this.nickname;
|
||||
this.participantService.setMyNickname(this.nickname);
|
||||
this.storageSrv.setNickname(this.nickname);
|
||||
}
|
||||
|
||||
private subscribeToParticipantProperties() {
|
||||
this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => {
|
||||
if (p) {
|
||||
this.nickname = p.getNickname();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#camera-button {
|
||||
border-radius: var(--ov-buttons-radius);
|
||||
/* background-color: var(--ov-secondary-color) !important; */
|
||||
/* color: var(--ov-text-color) !important; */
|
||||
}
|
||||
|
||||
.device-container-element mat-form-field {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.device-container-element button {
|
||||
margin: auto 10px auto auto;
|
||||
}
|
||||
|
||||
.device-container-element {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.warn-btn {
|
||||
color: var(--ov-text-color);
|
||||
background-color: var(--ov-warn-color) !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<div class="device-container-element">
|
||||
<button
|
||||
mat-icon-button
|
||||
id="camera-button"
|
||||
[disabled]="!hasVideoDevices || videoMuteChanging"
|
||||
[class.warn-btn]="isVideoMuted"
|
||||
(click)="toggleCam()"
|
||||
>
|
||||
<mat-icon *ngIf="!isVideoMuted" matTooltip="{{ 'TOOLBAR.MUTE_VIDEO' | translate }}" id="videocam">videocam</mat-icon>
|
||||
<mat-icon *ngIf="isVideoMuted" matTooltip="{{ 'TOOLBAR.UNMUTE_VIDEO' | translate }}" id="videocam_off">videocam_off</mat-icon>
|
||||
</button>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'PREJOIN.VIDEO_DEVICE' | translate }}</mat-label>
|
||||
<mat-select
|
||||
[disabled]="isVideoMuted || !hasVideoDevices"
|
||||
[value]="cameraSelected?.device"
|
||||
(selectionChange)="onCameraSelected($event)"
|
||||
>
|
||||
<mat-option *ngFor="let camera of cameras" [value]="camera.device">
|
||||
{{ camera.label }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { VideoDevicesComponent } from './video-devices.component';
|
||||
|
||||
describe('VideoDevicesComponent', () => {
|
||||
let component: VideoDevicesComponent;
|
||||
let fixture: ComponentFixture<VideoDevicesComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ VideoDevicesComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(VideoDevicesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,102 @@
|
|||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { PublisherProperties } from 'openvidu-browser';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CustomDevice } from '../../../models/device.model';
|
||||
import { PanelType } from '../../../models/panel.model';
|
||||
import { ParticipantAbstractModel } from '../../../models/participant.model';
|
||||
import { DeviceService } from '../../../services/device/device.service';
|
||||
import { OpenViduService } from '../../../services/openvidu/openvidu.service';
|
||||
import { PanelService } from '../../../services/panel/panel.service';
|
||||
import { ParticipantService } from '../../../services/participant/participant.service';
|
||||
import { StorageService } from '../../../services/storage/storage.service';
|
||||
import { VirtualBackgroundService } from '../../../services/virtual-background/virtual-background.service';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ov-video-devices-select',
|
||||
templateUrl: './video-devices.component.html',
|
||||
styleUrls: ['./video-devices.component.css']
|
||||
})
|
||||
export class VideoDevicesComponent implements OnInit, OnDestroy {
|
||||
videoMuteChanging: boolean;
|
||||
isVideoMuted: boolean;
|
||||
cameraSelected: CustomDevice;
|
||||
hasVideoDevices: boolean;
|
||||
cameras: CustomDevice[];
|
||||
localParticipantSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private openviduService: OpenViduService,
|
||||
protected panelService: PanelService,
|
||||
private storageSrv: StorageService,
|
||||
private deviceSrv: DeviceService,
|
||||
protected participantService: ParticipantService,
|
||||
private backgroundService: VirtualBackgroundService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subscribeToParticipantMediaProperties();
|
||||
this.hasVideoDevices = this.deviceSrv.hasVideoDeviceAvailable();
|
||||
this.cameras = this.deviceSrv.getCameras();
|
||||
this.cameraSelected = this.deviceSrv.getCameraSelected();
|
||||
if (this.openviduService.isSessionConnected()) {
|
||||
this.isVideoMuted = !this.participantService.getLocalParticipant().isCameraVideoActive();
|
||||
} else {
|
||||
this.isVideoMuted = this.deviceSrv.isVideoMuted();
|
||||
}
|
||||
}
|
||||
async ngOnDestroy() {
|
||||
this.cameras = [];
|
||||
if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
async toggleCam() {
|
||||
this.videoMuteChanging = true;
|
||||
const publish = this.isVideoMuted;
|
||||
await this.openviduService.publishVideo(publish);
|
||||
this.storageSrv.setVideoMuted(this.isVideoMuted);
|
||||
if (this.isVideoMuted && this.panelService.isExternalPanelOpened()) {
|
||||
this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS);
|
||||
}
|
||||
this.videoMuteChanging = false;
|
||||
}
|
||||
|
||||
async onCameraSelected(event: any) {
|
||||
const videoSource = event?.value;
|
||||
// Is New deviceId different from the old one?
|
||||
if (this.deviceSrv.needUpdateVideoTrack(videoSource)) {
|
||||
const mirror = this.deviceSrv.cameraNeedsMirror(videoSource);
|
||||
//TODO: Uncomment this when replaceTrack issue is fixed
|
||||
// const pp: PublisherProperties = { videoSource, audioSource: false, mirror };
|
||||
// await this.openviduService.replaceTrack(VideoType.CAMERA, pp);
|
||||
// TODO: Remove this when replaceTrack issue is fixed
|
||||
const pp: PublisherProperties = { videoSource, audioSource: this.deviceSrv.getMicrophoneSelected().device, mirror };
|
||||
|
||||
// Reapply Virtual Background to new Publisher if necessary
|
||||
const backgroundSelected = this.backgroundService.backgroundSelected.getValue();
|
||||
if (this.backgroundService.isBackgroundApplied()) {
|
||||
await this.backgroundService.removeBackground();
|
||||
}
|
||||
await this.openviduService.republishTrack(pp);
|
||||
if (this.backgroundService.isBackgroundApplied()) {
|
||||
const bgSelected = this.backgroundService.backgrounds.find((b) => b.id === backgroundSelected);
|
||||
if (bgSelected) {
|
||||
await this.backgroundService.applyBackground(bgSelected);
|
||||
}
|
||||
}
|
||||
|
||||
this.deviceSrv.setCameraSelected(videoSource);
|
||||
this.cameraSelected = this.deviceSrv.getCameraSelected();
|
||||
}
|
||||
}
|
||||
|
||||
protected subscribeToParticipantMediaProperties() {
|
||||
this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => {
|
||||
if (p) {
|
||||
this.isVideoMuted = !p.isCameraVideoActive();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -212,7 +212,7 @@ export class StreamComponent implements OnInit {
|
|||
* @ignore
|
||||
*/
|
||||
toggleNicknameForm() {
|
||||
if (this._stream.participant.local) {
|
||||
if (this._stream?.participant?.local) {
|
||||
this.toggleNickname = !this.toggleNickname;
|
||||
}
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ export class StreamComponent implements OnInit {
|
|||
this.showAudioDetection = value;
|
||||
// this.cd.markForCheck();
|
||||
});
|
||||
this.settingsButtonSub = this.libService.settingsButtonObs.subscribe((value: boolean) => {
|
||||
this.settingsButtonSub = this.libService.streamSettingsButtonObs.subscribe((value: boolean) => {
|
||||
this.showSettingsButton = value;
|
||||
// this.cd.markForCheck();
|
||||
});
|
||||
|
|
|
@ -83,8 +83,8 @@
|
|||
.recording-tag mat-icon {
|
||||
font-size: 16px;
|
||||
display: inline;
|
||||
vertical-align: sub;
|
||||
margin-right: 5px;
|
||||
vertical-align: sub;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.blink {
|
||||
|
@ -124,6 +124,15 @@
|
|||
.mat-icon-button[disabled] {
|
||||
color: #fff;
|
||||
}
|
||||
.divider {
|
||||
margin: 8px 0px;
|
||||
}
|
||||
::ng-deep .mat-menu-item {
|
||||
/* margin-bottom: 10px; */
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
@media (max-width: 750px) {
|
||||
#session-name {
|
||||
display: none;
|
||||
|
@ -143,10 +152,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
::ng-deep .mat-menu-panel {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
50% { opacity: 0.3; }
|
||||
50% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
|
@ -102,6 +102,14 @@
|
|||
<mat-icon>auto_awesome</mat-icon>
|
||||
<span>{{ 'TOOLBAR.BACKGROUND' | translate }}</span>
|
||||
</button>
|
||||
|
||||
<mat-divider class="divider" *ngIf="!isMinimal && showSettingsButton"></mat-divider>
|
||||
|
||||
<!-- Settings button -->
|
||||
<button *ngIf="!isMinimal && showSettingsButton" mat-menu-item id="settings-btn" (click)="toggleSettings()">
|
||||
<mat-icon>settings</mat-icon>
|
||||
<span>{{ 'TOOLBAR.SETTINGS' | translate }}</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<!-- External additional buttons -->
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
import { first, skip, Subscription } from 'rxjs';
|
||||
import { TokenService } from '../../services/token/token.service';
|
||||
import { ChatService } from '../../services/chat/chat.service';
|
||||
import { PanelService } from '../../services/panel/panel.service';
|
||||
import { PanelEvent, PanelService } from '../../services/panel/panel.service';
|
||||
import { DocumentService } from '../../services/document/document.service';
|
||||
|
||||
import { OpenViduService } from '../../services/openvidu/openvidu.service';
|
||||
|
@ -272,6 +272,11 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
*/
|
||||
showRecordingButton: boolean = true;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
showSettingsButton: boolean = true;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
|
@ -344,6 +349,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
private displayLogoSub: Subscription;
|
||||
private displaySessionNameSub: Subscription;
|
||||
private screenSizeSub: Subscription;
|
||||
private settingsButtonSub: Subscription;
|
||||
private currentWindowHeight = window.innerHeight;
|
||||
|
||||
/**
|
||||
|
@ -432,6 +438,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
if (this.activitiesPanelButtonSub) this.activitiesPanelButtonSub.unsubscribe();
|
||||
if (this.recordingSubscription) this.recordingSubscription.unsubscribe();
|
||||
if (this.screenSizeSub) this.screenSizeSub.unsubscribe();
|
||||
if (this.settingsButtonSub) this.settingsButtonSub.unsubscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -522,6 +529,13 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
toggleSettings() {
|
||||
this.panelService.togglePanel(PanelType.SETTINGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
|
@ -564,17 +578,15 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
});
|
||||
}
|
||||
protected subscribeToMenuToggling() {
|
||||
this.panelTogglingSubscription = this.panelService.panelOpenedObs.subscribe(
|
||||
(ev: { opened: boolean; type?: PanelType | string }) => {
|
||||
this.isChatOpened = ev.opened && ev.type === PanelType.CHAT;
|
||||
this.isParticipantsOpened = ev.opened && ev.type === PanelType.PARTICIPANTS;
|
||||
this.isActivitiesOpened = ev.opened && ev.type === PanelType.ACTIVITIES;
|
||||
if (this.isChatOpened) {
|
||||
this.unreadMessages = 0;
|
||||
}
|
||||
this.cd.markForCheck();
|
||||
this.panelTogglingSubscription = this.panelService.panelOpenedObs.subscribe((ev: PanelEvent) => {
|
||||
this.isChatOpened = ev.opened && ev.type === PanelType.CHAT;
|
||||
this.isParticipantsOpened = ev.opened && ev.type === PanelType.PARTICIPANTS;
|
||||
this.isActivitiesOpened = ev.opened && ev.type === PanelType.ACTIVITIES;
|
||||
if (this.isChatOpened) {
|
||||
this.unreadMessages = 0;
|
||||
}
|
||||
);
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
protected subscribeToChatMessages() {
|
||||
|
@ -590,7 +602,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => {
|
||||
if (p) {
|
||||
this.isWebcamVideoActive = p.isCameraVideoActive();
|
||||
this.isAudioActive = p.isCameraAudioActive() || p.isScreenAudioActive();
|
||||
this.isAudioActive = p.hasAudioActive();
|
||||
this.isScreenShareActive = p.isScreenActive();
|
||||
this.isSessionCreator = p.getRole() === OpenViduRole.MODERATOR;
|
||||
this.cd.markForCheck();
|
||||
|
@ -627,11 +639,17 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.cd.markForCheck();
|
||||
});
|
||||
|
||||
this.recordingButtonSub = this.libService.recordingButton.subscribe((value: boolean) => {
|
||||
this.recordingButtonSub = this.libService.recordingButtonObs.subscribe((value: boolean) => {
|
||||
this.showRecordingButton = value;
|
||||
this.checkDisplayMoreOptions();
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
|
||||
this.settingsButtonSub = this.libService.toolbarSettingsButtonObs.subscribe((value: boolean) => {
|
||||
this.showSettingsButton = value;
|
||||
this.checkDisplayMoreOptions();
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
this.chatPanelButtonSub = this.libService.chatPanelButtonObs.subscribe((value: boolean) => {
|
||||
this.showChatPanelButton = value;
|
||||
this.cd.markForCheck();
|
||||
|
@ -661,12 +679,12 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
|
||||
private subscribeToScreenSize() {
|
||||
this.screenSizeSub = this.documentService.screenSizeObs.subscribe((change: MediaChange[]) => {
|
||||
console.log(change[0].mqAlias)
|
||||
this.screenSize = change[0].mqAlias;
|
||||
});
|
||||
}
|
||||
|
||||
private checkDisplayMoreOptions() {
|
||||
this.showMoreOptionsButton = this.showFullscreenButton || this.showBackgroundEffectsButton || this.showRecordingButton;
|
||||
this.showMoreOptionsButton =
|
||||
this.showFullscreenButton || this.showBackgroundEffectsButton || this.showRecordingButton || this.showSettingsButton;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,10 @@
|
|||
<ov-background-effects-panel id="default-background-effects-panel"></ov-background-effects-panel>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #settingsPanel>
|
||||
<ov-settings-panel id="default-settings-panel"></ov-settings-panel>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #activitiesPanel>
|
||||
<ng-container *ngTemplateOutlet="openviduAngularActivitiesPanelTemplate"></ng-container>
|
||||
</ng-template>
|
||||
|
|
|
@ -688,7 +688,7 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
|
|||
// The devices are initialized without labels in Firefox.
|
||||
// We need to force an update after publisher is allowed.
|
||||
if (this.deviceSrv.areEmptyLabels()) {
|
||||
await this.deviceSrv.forceUpdate();
|
||||
await this.deviceSrv.initializeDevices();
|
||||
if (this.deviceSrv.hasAudioDeviceAvailable()) {
|
||||
const audioLabel = this.participantService.getMyCameraPublisher()?.stream?.getMediaStream()?.getAudioTracks()[0]?.label;
|
||||
this.deviceSrv.setMicSelected(audioLabel);
|
||||
|
|
|
@ -19,7 +19,8 @@ import {
|
|||
ToolbarDisplayLogoDirective,
|
||||
ToolbarActivitiesPanelButtonDirective,
|
||||
ToolbarBackgroundEffectsButtonDirective,
|
||||
ToolbarRecordingButtonDirective
|
||||
ToolbarRecordingButtonDirective,
|
||||
ToolbarSettingsButtonDirective
|
||||
} from './toolbar.directive';
|
||||
import {
|
||||
AudioMutedDirective,
|
||||
|
@ -47,6 +48,7 @@ import {
|
|||
ToolbarActivitiesPanelButtonDirective,
|
||||
ToolbarDisplaySessionNameDirective,
|
||||
ToolbarDisplayLogoDirective,
|
||||
ToolbarSettingsButtonDirective,
|
||||
StreamDisplayParticipantNameDirective,
|
||||
StreamDisplayAudioDetectionDirective,
|
||||
StreamSettingsButtonDirective,
|
||||
|
@ -75,6 +77,7 @@ import {
|
|||
ToolbarActivitiesPanelButtonDirective,
|
||||
ToolbarDisplaySessionNameDirective,
|
||||
ToolbarDisplayLogoDirective,
|
||||
ToolbarSettingsButtonDirective,
|
||||
StreamDisplayParticipantNameDirective,
|
||||
StreamDisplayAudioDetectionDirective,
|
||||
StreamSettingsButtonDirective,
|
||||
|
|
|
@ -142,8 +142,8 @@ export class StreamSettingsButtonDirective implements AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
update(value: boolean) {
|
||||
if (this.libService.settingsButton.getValue() !== value) {
|
||||
this.libService.settingsButton.next(value);
|
||||
if (this.libService.streamSettingsButton.getValue() !== value) {
|
||||
this.libService.streamSettingsButton.next(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -242,6 +242,65 @@ export class ToolbarBackgroundEffectsButtonDirective implements AfterViewInit, O
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The **settingsButton** directive allows show/hide the settings toolbar button.
|
||||
*
|
||||
* Default: `true`
|
||||
*
|
||||
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
|
||||
*
|
||||
* @example
|
||||
* <ov-videoconference [toolbarSettingsButton]="false"></ov-videoconference>
|
||||
*
|
||||
* \
|
||||
* And it also can be used in the {@link ToolbarComponent}.
|
||||
* @example
|
||||
* <ov-toolbar [settingsButton]="false"></ov-toolbar>
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'ov-videoconference[toolbarSettingsButton], ov-toolbar[settingsButton]'
|
||||
})
|
||||
export class ToolbarSettingsButtonDirective implements AfterViewInit, OnDestroy {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
@Input() set toolbarSettingsButton(value: boolean) {
|
||||
this.settingsValue = value;
|
||||
this.update(this.settingsValue);
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
@Input() set settingsButton(value: boolean) {
|
||||
this.settingsValue = value;
|
||||
this.update(this.settingsValue);
|
||||
}
|
||||
|
||||
private settingsValue: boolean = true;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.update(this.settingsValue);
|
||||
}
|
||||
ngOnDestroy(): void {
|
||||
this.clear();
|
||||
}
|
||||
private clear() {
|
||||
this.settingsValue = true;
|
||||
this.update(true);
|
||||
}
|
||||
|
||||
private update(value: boolean) {
|
||||
if (this.libService.toolbarSettingsButton.getValue() !== value) {
|
||||
this.libService.toolbarSettingsButton.next(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The **leaveButton** directive allows show/hide the leave toolbar button.
|
||||
*
|
||||
|
|
|
@ -259,7 +259,7 @@ export class PanelDirective {
|
|||
* this.subscribeToPanelToggling();
|
||||
* }
|
||||
* subscribeToPanelToggling() {
|
||||
* this.panelService.panelOpenedObs.subscribe((ev: { opened: boolean; type?: PanelType | string }) => {
|
||||
* this.panelService.panelOpenedObs.subscribe((ev: PanelEvent) => {
|
||||
* this.showExternalPanel = ev.opened && ev.type === 'my-panel';
|
||||
* this.showExternalPanel2 = ev.opened && ev.type === 'my-panel2';
|
||||
* });
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"BACKGROUND":"背景效果",
|
||||
"START_RECORDING": "开始录音",
|
||||
"STOP_RECORDING": "停止录制",
|
||||
"SETTINGS":"设置",
|
||||
"LEAVE":"离开会议",
|
||||
"PARTICIPANTS":"参与者",
|
||||
"CHAT":"聊天",
|
||||
|
@ -64,6 +65,13 @@
|
|||
"CAMERA": "摄像头",
|
||||
"SCREEN": "屏幕"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "设置",
|
||||
"GENERAL": "一般的",
|
||||
"VIDEO": "视频",
|
||||
"AUDIO": "声音的",
|
||||
"LANGUAGE": "语"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "背景效果",
|
||||
"BLURRED_SECTION": "没有效果和模糊的背景",
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"BACKGROUND": "Hintergrund-Effekte",
|
||||
"START_RECORDING": "Aufzeichnung starten",
|
||||
"STOP_RECORDING": "Aufzeichnung stoppen",
|
||||
"SETTINGS": "Einstellungen",
|
||||
"LEAVE": "Die Sitzung verlassen",
|
||||
"PARTICIPANTS": "Teilnehmer",
|
||||
"CHAT": "Chat",
|
||||
|
@ -64,6 +65,13 @@
|
|||
"CAMERA": "KAMERA",
|
||||
"SCREEN": "BILDSCHIRM"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "Einstellungen",
|
||||
"GENERAL": "Allgemein",
|
||||
"VIDEO": "Video",
|
||||
"AUDIO": "Audio",
|
||||
"LANGUAGE": "Sprache"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Hintergrund-Effekte",
|
||||
"BLURRED_SECTION": "Keine Effekte und unscharfer Hintergrund",
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
"BACKGROUND": "Background effects",
|
||||
"START_RECORDING": "Start recording",
|
||||
"STOP_RECORDING": "Stop recording",
|
||||
"SETTINGS": "Settings",
|
||||
"LEAVE": "Leave the session",
|
||||
"PARTICIPANTS": "Participants",
|
||||
"CHAT": "Chat",
|
||||
|
@ -65,6 +66,13 @@
|
|||
"CAMERA": "CAMERA",
|
||||
"SCREEN": "SCREEN"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "Settings",
|
||||
"GENERAL": "General",
|
||||
"VIDEO": "Video",
|
||||
"AUDIO": "Audio",
|
||||
"LANGUAGE": "Language"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Background effects",
|
||||
"BLURRED_SECTION": "No effects and blurred background",
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"BACKGROUND": "Efectos de fondo",
|
||||
"START_RECORDING": "Iniciar grabación",
|
||||
"STOP_RECORDING": "Detener grabación",
|
||||
"SETTINGS": "Configuración",
|
||||
"LEAVE": "Salir de la sesión",
|
||||
"PARTICIPANTS": "Participantes",
|
||||
"CHAT": "Chat",
|
||||
|
@ -64,6 +65,13 @@
|
|||
"CAMERA": "CÁMARA",
|
||||
"SCREEN": "PANTALLA"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "Configuración",
|
||||
"GENERAL": "General",
|
||||
"VIDEO": "Video",
|
||||
"AUDIO": "Audio",
|
||||
"LANGUAGE": "Idioma"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Efectos de fondo",
|
||||
"BLURRED_SECTION": "Sin efectos y fondo desenfocado",
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"BACKGROUND": "Effets de fond",
|
||||
"START_RECORDING": "démarrer l'enregistrement",
|
||||
"STOP_RECORDING": "Arrêter l'enregistrement",
|
||||
"SETTINGS": "Paramètres",
|
||||
"LEAVE": "Quitter la session",
|
||||
"PARTICIPANTS": "Participants",
|
||||
"CHAT": "Chat",
|
||||
|
@ -64,6 +65,13 @@
|
|||
"CAMERA": "CAMÉRA",
|
||||
"SCREEN": "ÉCRAN"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "Paramètres",
|
||||
"GENERAL": "Général",
|
||||
"VIDEO": "Vidéo",
|
||||
"AUDIO": "l'audio",
|
||||
"LANGUAGE": "Langue"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Effets de fond",
|
||||
"BLURRED_SECTION": "Aucun effet et arrière-plan flou",
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"BACKGROUND": "पृष्ठभूमि प्रभाव",
|
||||
"START_RECORDING": "रिकॉर्डिंग प्रारंभ करें",
|
||||
"STOP_RECORDING": "रिकॉर्डिंग रोकें",
|
||||
"SETTINGS": "सेटिंग्स",
|
||||
"LEAVE": "सत्र छोड़ें",
|
||||
"PARTICIPANTS": "सदस्य",
|
||||
"CHAT": "बातचीत",
|
||||
|
@ -64,6 +65,13 @@
|
|||
"CAMERA": "कैमरा",
|
||||
"SCREEN": "स्क्रीन"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "सेटिंग्स",
|
||||
"GENERAL": "सामान्य",
|
||||
"VIDEO": "वीडियो",
|
||||
"AUDIO": "ऑडियो",
|
||||
"LANGUAGE": "भाषा"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "पृष्ठभूमि प्रभाव",
|
||||
"BLURRED_SECTION": "कोई प्रभाव नहीं है और पृष्ठभूमि धुंधली है",
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"BACKGROUND": "Effetti di sfondo",
|
||||
"START_RECORDING": "Avvia registrazione",
|
||||
"STOP_RECORDING": "Interrompi registrazione",
|
||||
"SETTINGS": "Impostazioni",
|
||||
"LEAVE": "Abbandona la sessione",
|
||||
"PARTICIPANTS": "Partecipanti",
|
||||
"CHAT": "Chat",
|
||||
|
@ -64,6 +65,13 @@
|
|||
"CAMERA": "CAMERA",
|
||||
"SCREEN": "SCREEN"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "Impostazioni",
|
||||
"GENERAL": "Generale",
|
||||
"VIDEO": "video",
|
||||
"AUDIO": "Audio",
|
||||
"LANGUAGE":"Lingua"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Effetti di sfondo",
|
||||
"BLURRED_SECTION": "Nessun effetto e sfondo sfocato",
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"BACKGROUND": "背景効果",
|
||||
"START_RECORDING": "録画開始",
|
||||
"STOP_RECORDING": "録画の停止",
|
||||
"SETTINGS": "設定",
|
||||
"LEAVE": "セッションを終了する",
|
||||
"PARTICIPANTS": "参加者",
|
||||
"CHAT": "チャット",
|
||||
|
@ -64,6 +65,13 @@
|
|||
"CAMERA": "カメラ",
|
||||
"SCREEN": "スクリーン"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "設定",
|
||||
"GENERAL": "全般的",
|
||||
"VIDEO": "ビデオ",
|
||||
"AUDIO": "オーディオ",
|
||||
"LANGUAGE":"言語"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "背景効果",
|
||||
"BLURRED_SECTION": "エフェクトなし、ぼやけた背景",
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"BACKGROUND": "Achtergrondeffecten",
|
||||
"START_RECORDING": "Start opname",
|
||||
"STOP_RECORDING": "Stop opname",
|
||||
"SETTINGS": "Instellingen",
|
||||
"LEAVE": "Verlaat de sessie",
|
||||
"PARTICIPANTS": "Deelnemers",
|
||||
"CHAT": "Chat",
|
||||
|
@ -64,6 +65,13 @@
|
|||
"CAMERA": "CAMERA",
|
||||
"SCREEN": "SCHERM"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "Instellingen",
|
||||
"GENERAL": "Algemeen",
|
||||
"VIDEO": "Video",
|
||||
"AUDIO": "Audio",
|
||||
"LANGUAGE":"Taal"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Achtergrondeffecten",
|
||||
"BLURRED_SECTION": "Geen effecten en onscherpe achtergrond",
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"BACKGROUND": "Efeitos de fundo",
|
||||
"START_RECORDING": "Iniciar_gravação",
|
||||
"STOP_RECORDING": "Parar de gravar",
|
||||
"SETTINGS": "Configurações",
|
||||
"LEAVE": "Sair da sessão",
|
||||
"PARTICIPANTS": "Participantes",
|
||||
"CHAT": "Chat",
|
||||
|
@ -64,6 +65,13 @@
|
|||
"CAMERA": "CÂMERA",
|
||||
"SCREEN": "TELA"
|
||||
},
|
||||
"SETTINGS": {
|
||||
"TITLE": "Configurações",
|
||||
"GENERAL": "Em geral",
|
||||
"VIDEO": "Vídeo",
|
||||
"AUDIO": "Áudio",
|
||||
"LANGUAGE":"Linguagem"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Efeitos de fundo",
|
||||
"BLURRED_SECTION": "Sem efeitos e fundo desfocado",
|
||||
|
|
|
@ -2,6 +2,7 @@ export enum PanelType {
|
|||
CHAT = 'chat',
|
||||
PARTICIPANTS = 'participants',
|
||||
BACKGROUND_EFFECTS = 'background-effects',
|
||||
ACTIVITIES = 'activities'
|
||||
ACTIVITIES = 'activities',
|
||||
SETTINGS = 'settings'
|
||||
|
||||
}
|
|
@ -95,12 +95,27 @@ export abstract class ParticipantAbstractModel {
|
|||
/**
|
||||
* @internal
|
||||
*/
|
||||
public isCameraAudioActive(): boolean {
|
||||
hasAudioActive(): boolean {
|
||||
const cameraConnection = this.getCameraConnection();
|
||||
if (cameraConnection) {
|
||||
return cameraConnection.connected && cameraConnection.streamManager?.stream?.audioActive;
|
||||
const screenConnection = this.getScreenConnection();
|
||||
|
||||
if (cameraConnection.connected) {
|
||||
return this.isCameraAudioActive();
|
||||
} else if (screenConnection.connected) {
|
||||
return this.isScreenAudioActive();
|
||||
}
|
||||
return this.isScreenAudioActive();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private isCameraAudioActive(): boolean {
|
||||
const cameraConnection = this.getCameraConnection();
|
||||
if (cameraConnection?.connected) {
|
||||
return cameraConnection.streamManager?.stream?.audioActive;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,8 +131,8 @@ export abstract class ParticipantAbstractModel {
|
|||
*/
|
||||
isScreenAudioActive(): boolean {
|
||||
const screenConnection = this.getScreenConnection();
|
||||
if (screenConnection) {
|
||||
return screenConnection?.connected && screenConnection?.streamManager?.stream?.audioActive;
|
||||
if (screenConnection?.connected) {
|
||||
return screenConnection?.streamManager?.stream?.audioActive;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -48,11 +48,16 @@ import { AvatarProfileComponent } from './components/avatar-profile/avatar-profi
|
|||
import { OpenViduAngularDirectiveModule } from './directives/template/openvidu-angular.directive.module';
|
||||
import { ApiDirectiveModule } from './directives/api/api.directive.module';
|
||||
import { BackgroundEffectsPanelComponent } from './components/panel/background-effects-panel/background-effects-panel.component';
|
||||
import { SettingsPanelComponent } from './components/panel/settings-panel/settings-panel.component';
|
||||
import { ActivitiesPanelComponent } from './components/panel/activities-panel/activities-panel.component';
|
||||
import { RecordingActivityComponent } from './components/panel/activities-panel/recording-activity-panel/recording-activity.component';
|
||||
import { AdminDashboardComponent } from './admin/dashboard/dashboard.component';
|
||||
import { AdminLoginComponent } from './admin/login/login.component';
|
||||
import { AppMaterialModule } from './openvidu-angular.material.module';
|
||||
import { VideoDevicesComponent } from './components/settings/video-devices/video-devices.component';
|
||||
import { AudioDevicesComponent } from './components/settings/audio-devices/audio-devices.component';
|
||||
import { NicknameInputComponent } from './components/settings/nickname-input/nickname-input.component';
|
||||
import { LangSelectorComponent } from './components/settings/lang-selector/lang-selector.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -78,7 +83,12 @@ import { AppMaterialModule } from './openvidu-angular.material.module';
|
|||
PanelComponent,
|
||||
AvatarProfileComponent,
|
||||
PreJoinComponent,
|
||||
VideoDevicesComponent,
|
||||
AudioDevicesComponent,
|
||||
NicknameInputComponent,
|
||||
LangSelectorComponent,
|
||||
BackgroundEffectsPanelComponent,
|
||||
SettingsPanelComponent,
|
||||
ActivitiesPanelComponent,
|
||||
RecordingActivityComponent,
|
||||
AdminDashboardComponent,
|
||||
|
@ -118,6 +128,7 @@ import { AppMaterialModule } from './openvidu-angular.material.module';
|
|||
ParticipantsPanelComponent,
|
||||
ParticipantPanelItemComponent,
|
||||
BackgroundEffectsPanelComponent,
|
||||
SettingsPanelComponent,
|
||||
ActivitiesPanelComponent,
|
||||
ChatPanelComponent,
|
||||
SessionComponent,
|
||||
|
|
|
@ -28,6 +28,9 @@ export class OpenViduAngularConfigService {
|
|||
fullscreenButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
fullscreenButtonObs: Observable<boolean>;
|
||||
|
||||
toolbarSettingsButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
toolbarSettingsButtonObs: Observable<boolean>;
|
||||
|
||||
leaveButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
leaveButtonObs: Observable<boolean>;
|
||||
|
||||
|
@ -49,8 +52,8 @@ export class OpenViduAngularConfigService {
|
|||
displayParticipantNameObs: Observable<boolean>;
|
||||
displayAudioDetection = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
displayAudioDetectionObs: Observable<boolean>;
|
||||
settingsButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
settingsButtonObs: Observable<boolean>;
|
||||
streamSettingsButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
streamSettingsButtonObs: Observable<boolean>;
|
||||
participantItemMuteButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
participantItemMuteButtonObs: Observable<boolean>;
|
||||
backgroundEffectsButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
|
@ -88,10 +91,11 @@ export class OpenViduAngularConfigService {
|
|||
this.displaySessionNameObs = this.displaySessionName.asObservable();
|
||||
this.displayLogoObs = this.displayLogo.asObservable();
|
||||
this.recordingButtonObs = this.recordingButton.asObservable();
|
||||
this.toolbarSettingsButtonObs = this.toolbarSettingsButton.asObservable();
|
||||
//Stream observables
|
||||
this.displayParticipantNameObs = this.displayParticipantName.asObservable();
|
||||
this.displayAudioDetectionObs = this.displayAudioDetection.asObservable();
|
||||
this.settingsButtonObs = this.settingsButton.asObservable();
|
||||
this.streamSettingsButtonObs = this.streamSettingsButton.asObservable();
|
||||
// Participant item observables
|
||||
this.participantItemMuteButtonObs = this.participantItemMuteButton.asObservable();
|
||||
// Recording activity observables
|
||||
|
@ -107,7 +111,7 @@ export class OpenViduAngularConfigService {
|
|||
return this.configuration;
|
||||
}
|
||||
isProduction(): boolean {
|
||||
return this.configuration?.production;
|
||||
return this.configuration?.production || false;
|
||||
}
|
||||
|
||||
hasParticipantFactory(): boolean {
|
||||
|
|
|
@ -39,16 +39,15 @@ export class DeviceService {
|
|||
private libSrv: OpenViduAngularConfigService
|
||||
) {
|
||||
this.log = this.loggerSrv.get('DevicesService');
|
||||
|
||||
}
|
||||
|
||||
async forceUpdate() {
|
||||
this.cameras = [];
|
||||
this.microphones = [];
|
||||
await this.initializeDevices();
|
||||
}
|
||||
// async forceUpdate() {
|
||||
// await this.initializeDevices();
|
||||
// }
|
||||
|
||||
async initializeDevices() {
|
||||
this.cameras = [];
|
||||
this.microphones = [];
|
||||
try {
|
||||
this.OV = new OpenVidu();
|
||||
// Forcing media permissions request.
|
||||
|
|
|
@ -76,6 +76,10 @@ export class OpenViduService {
|
|||
this.ovEdition = edition;
|
||||
}
|
||||
|
||||
isSessionConnected(): boolean {
|
||||
return !!this.webcamSession.connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
|
|
@ -4,6 +4,13 @@ import { ILogger } from '../../models/logger.model';
|
|||
import { PanelType } from '../../models/panel.model';
|
||||
import { LoggerService } from '../logger/logger.service';
|
||||
|
||||
export interface PanelEvent {
|
||||
opened: boolean;
|
||||
type?: PanelType | string;
|
||||
expand?: string;
|
||||
oldType?: PanelType | string;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
|
@ -11,14 +18,15 @@ export class PanelService {
|
|||
/**
|
||||
* Panel Observable which pushes the panel status in every update.
|
||||
*/
|
||||
panelOpenedObs: Observable<{ opened: boolean; type?: PanelType | string }>;
|
||||
panelOpenedObs: Observable<PanelEvent>;
|
||||
protected log: ILogger;
|
||||
protected isChatOpened: boolean = false;
|
||||
protected isParticipantsOpened: boolean = false;
|
||||
protected isActivitiesOpened: boolean = false;
|
||||
private isExternalOpened: boolean = false;
|
||||
private externalType: string;
|
||||
protected _panelOpened = <BehaviorSubject<{ opened: boolean; type?: PanelType | string, expand?: string }>>new BehaviorSubject({ opened: false });
|
||||
protected _panelOpened = <BehaviorSubject<PanelEvent>>new BehaviorSubject({ opened: false });
|
||||
private panelMap: Map<string, boolean> = new Map();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -26,6 +34,7 @@ export class PanelService {
|
|||
constructor(protected loggerSrv: LoggerService) {
|
||||
this.log = this.loggerSrv.get('PanelService');
|
||||
this.panelOpenedObs = this._panelOpened.asObservable();
|
||||
Object.values(PanelType).forEach((panel) => this.panelMap.set(panel, false));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,45 +42,43 @@ export class PanelService {
|
|||
* If the type is differente, it will switch to the properly panel.
|
||||
*/
|
||||
togglePanel(type: PanelType | string, expand?: string) {
|
||||
this.log.d(`Toggling ${type} menu`);
|
||||
let opened: boolean;
|
||||
if (type === PanelType.CHAT) {
|
||||
this.isChatOpened = !this.isChatOpened;
|
||||
this.isParticipantsOpened = false;
|
||||
this.isExternalOpened = false;
|
||||
this.isActivitiesOpened = false
|
||||
opened = this.isChatOpened;
|
||||
} else if (type === PanelType.PARTICIPANTS) {
|
||||
this.isParticipantsOpened = !this.isParticipantsOpened;
|
||||
this.isChatOpened = false;
|
||||
this.isExternalOpened = false;
|
||||
this.isActivitiesOpened = false;
|
||||
opened = this.isParticipantsOpened;
|
||||
} else if (type === PanelType.ACTIVITIES) {
|
||||
this.isActivitiesOpened = !this.isActivitiesOpened;
|
||||
this.isChatOpened = false;
|
||||
this.isExternalOpened = false;
|
||||
this.isParticipantsOpened = false;
|
||||
opened = this.isActivitiesOpened;
|
||||
let nextOpenedValue: boolean = false;
|
||||
if (this.panelMap.has(type)) {
|
||||
this.log.d(`Toggling ${type} menu`);
|
||||
|
||||
this.panelMap.forEach((opened: boolean, panel: string) => {
|
||||
if (panel === type) {
|
||||
// Toggle panel
|
||||
this.panelMap.set(panel, !opened);
|
||||
nextOpenedValue = !opened;
|
||||
} else {
|
||||
// Close others
|
||||
this.panelMap.set(panel, false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Panel is external
|
||||
this.log.d('Toggling external panel');
|
||||
this.isChatOpened = false;
|
||||
this.isParticipantsOpened = false;
|
||||
this.isActivitiesOpened = false;
|
||||
// Open when is close or is opened with another type
|
||||
// Open when is closed or is opened with another type
|
||||
this.isExternalOpened = !this.isExternalOpened || this.externalType !== type;
|
||||
this.externalType = !this.isExternalOpened ? '' : type;
|
||||
opened = this.isExternalOpened;
|
||||
nextOpenedValue = this.isExternalOpened;
|
||||
}
|
||||
|
||||
this._panelOpened.next({ opened, type, expand });
|
||||
const oldType = this._panelOpened.getValue().type;
|
||||
this._panelOpened.next({ opened: nextOpenedValue, type, expand, oldType });
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
isPanelOpened(): boolean {
|
||||
return this.isChatPanelOpened() || this.isParticipantsPanelOpened() || this.isActivitiesPanelOpened() || this.isExternalPanelOpened();
|
||||
return (
|
||||
this.isChatPanelOpened() || this.isParticipantsPanelOpened() || this.isActivitiesPanelOpened() || this.isExternalPanelOpened()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,7 +109,7 @@ export class PanelService {
|
|||
/**
|
||||
* Whether the activities panel is opened or not.
|
||||
*/
|
||||
isActivitiesPanelOpened(): boolean {
|
||||
isActivitiesPanelOpened(): boolean {
|
||||
return this.isActivitiesOpened;
|
||||
}
|
||||
|
||||
|
|
|
@ -196,7 +196,7 @@ export class ParticipantService {
|
|||
}
|
||||
|
||||
isMyAudioActive(): boolean {
|
||||
return this.localParticipant?.isCameraAudioActive() || this.localParticipant?.isScreenAudioActive();
|
||||
return this.localParticipant?.hasAudioActive();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ export * from './lib/components/videoconference/videoconference.component';
|
|||
export * from './lib/components/toolbar/toolbar.component';
|
||||
export * from './lib/components/panel/panel.component';
|
||||
export * from './lib/components/panel/chat-panel/chat-panel.component';
|
||||
export * from './lib/components/panel/settings-panel/settings-panel.component';
|
||||
export * from './lib/components/panel/background-effects-panel/background-effects-panel.component';
|
||||
export * from './lib/components/panel/activities-panel/activities-panel.component';
|
||||
export * from './lib/components/panel/participants-panel/participants-panel/participants-panel.component';
|
||||
|
|
Loading…
Reference in New Issue