openvidu-components: Added openvidu-angular documentation

pull/707/head
csantosm 2022-03-23 13:48:17 +01:00
parent d81124150a
commit ff2dc6d86b
63 changed files with 5581 additions and 344 deletions

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,8 @@
"lib:test": "ng test openvidu-angular --no-watch --code-coverage", "lib:test": "ng test openvidu-angular --no-watch --code-coverage",
"lib:e2e": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/angular.test.js", "lib:e2e": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/angular.test.js",
"lib:e2e-ci": "cross-env LAUNCH_MODE=CI npm run lib:e2e", "lib:e2e-ci": "cross-env LAUNCH_MODE=CI npm run lib:e2e",
"lib:doc-build": "npx compodoc -p src/doc/tsconfig.doc.json -c src/doc/.compodocrc.json",
"lib:doc-serve": "npx compodoc -p src/doc/tsconfig.doc.json --watch --serve -c src/doc/.compodocrc.json",
"webcomponent:build": "./node_modules/@angular/cli/bin/ng.js build openvidu-webcomponent --configuration production && node ./openvidu-webcomponent-build.js", "webcomponent:build": "./node_modules/@angular/cli/bin/ng.js build openvidu-webcomponent --configuration production && node ./openvidu-webcomponent-build.js",
"webcomponent:serve-testapp": "npx http-server ./e2e/webcomponent-app/", "webcomponent:serve-testapp": "npx http-server ./e2e/webcomponent-app/",
"webcomponent:e2e": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/webcomponent.test.js", "webcomponent:e2e": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/webcomponent.test.js",
@ -43,6 +45,7 @@
"@angular/compiler": "13.0.0", "@angular/compiler": "13.0.0",
"@angular/compiler-cli": "13.0.0", "@angular/compiler-cli": "13.0.0",
"@angular/elements": "13.0.0", "@angular/elements": "13.0.0",
"@compodoc/compodoc": "^1.1.19",
"@types/chai": "4.3.0", "@types/chai": "4.3.0",
"@types/mocha": "9.1.0", "@types/mocha": "9.1.0",
"@types/node": "16.11.6", "@types/node": "16.11.6",

View File

@ -2,6 +2,10 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { PublisherSpeakingEvent, StreamManager } from 'openvidu-browser'; import { PublisherSpeakingEvent, StreamManager } from 'openvidu-browser';
/**
* @internal
*/
@Component({ @Component({
selector: 'ov-audio-wave', selector: 'ov-audio-wave',
templateUrl: './audio-wave.component.html', templateUrl: './audio-wave.component.html',
@ -9,7 +13,7 @@ import { PublisherSpeakingEvent, StreamManager } from 'openvidu-browser';
}) })
export class AudioWaveComponent implements OnInit, OnDestroy { export class AudioWaveComponent implements OnInit, OnDestroy {
isSpeaking: boolean = false; isSpeaking: boolean = false;
audioVolume: number = 0; // audioVolume: number = 0;
private _streamManager: StreamManager; private _streamManager: StreamManager;
@ -17,7 +21,7 @@ export class AudioWaveComponent implements OnInit, OnDestroy {
set streamManager(streamManager: StreamManager) { set streamManager(streamManager: StreamManager) {
this._streamManager = streamManager; this._streamManager = streamManager;
if(this._streamManager) { if (this._streamManager) {
this._streamManager.on('publisherStartSpeaking', (event: PublisherSpeakingEvent) => { this._streamManager.on('publisherStartSpeaking', (event: PublisherSpeakingEvent) => {
this.isSpeaking = true; this.isSpeaking = true;
}); });
@ -34,12 +38,11 @@ export class AudioWaveComponent implements OnInit, OnDestroy {
// console.log('AUDIO VOLUME', this.audioVolume); // console.log('AUDIO VOLUME', this.audioVolume);
// }); // });
} }
} }
constructor() {} constructor() {}
ngOnDestroy(): void { ngOnDestroy(): void {
if(this._streamManager){ if (this._streamManager) {
this._streamManager.off('publisherStartSpeaking'); this._streamManager.off('publisherStartSpeaking');
this._streamManager.off('publisherStopSpeaking'); this._streamManager.off('publisherStopSpeaking');
} }

View File

@ -1,5 +1,9 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
/**
* @internal
*/
@Component({ @Component({
selector: 'ov-avatar-profile', selector: 'ov-avatar-profile',
template: ` template: `

View File

@ -13,8 +13,40 @@ import { ParticipantService } from '../../services/participant/participant.servi
import { ParticipantAbstractModel } from '../../models/participant.model'; import { ParticipantAbstractModel } from '../../models/participant.model';
import { LayoutService } from '../../services/layout/layout.service'; import { LayoutService } from '../../services/layout/layout.service';
import { StreamDirective } from '../../directives/template/openvidu-angular.directive'; import { StreamDirective } from '../../directives/template/openvidu-angular.directive';
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
/**
*
* The **LayoutComponent** is hosted inside of the {@link VideoconferenceComponent}.
* It is in charge of displaying the participants streams layout.
*
* <div class="custom-table-container">
*
* <div>
* <h3>OpenVidu Angular Directives</h3>
*
* The LayoutComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives}
* for doing this.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovLayout** | {@link LayoutDirective} |
*
* </br>
*
* It is also providing us a way to **replace the {@link StreamComponent Stream Component}** (<span class="italic">which is hosted inside of it</span>) with a custom one.
* It will recognise the following directive in a child element.
*
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovStream** | {@link StreamDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link OpenViduAngularDirectiveModule OpenVidu Angular Directives}</span>
* </p>
* </div>
* </div>
*/
@Component({ @Component({
selector: 'ov-layout', selector: 'ov-layout',
templateUrl: './layout.component.html', templateUrl: './layout.component.html',
@ -22,8 +54,14 @@ import { OpenViduAngularConfigService } from '../../services/config/openvidu-ang
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit { export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
/**
* @ignore
*/
@ContentChild('stream', { read: TemplateRef }) streamTemplate: TemplateRef<any>; @ContentChild('stream', { read: TemplateRef }) streamTemplate: TemplateRef<any>;
/**
* @ignore
*/
@ContentChild(StreamDirective) @ContentChild(StreamDirective)
set externalStream(externalStream: StreamDirective) { set externalStream(externalStream: StreamDirective) {
// This directive will has value only when STREAM component tagget with '*ovStream' directive // This directive will has value only when STREAM component tagget with '*ovStream' directive
@ -38,12 +76,10 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
protected localParticipantSubs: Subscription; protected localParticipantSubs: Subscription;
protected remoteParticipantsSubs: Subscription; protected remoteParticipantsSubs: Subscription;
constructor( /**
protected layoutService: LayoutService, * @ignore
protected participantService: ParticipantService, */
private libService: OpenViduAngularConfigService, constructor(protected layoutService: LayoutService, protected participantService: ParticipantService, private cd: ChangeDetectorRef) {}
private cd: ChangeDetectorRef
) {}
ngOnInit(): void { ngOnInit(): void {
this.subscribeToParticipants(); this.subscribeToParticipants();
@ -51,10 +87,6 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
ngAfterViewInit() { ngAfterViewInit() {
let timeout: number = 0; let timeout: number = 0;
// if (this.libService.isWebcomponent()) {
// timeout = 0;
// }
this.layoutService.initialize(timeout); this.layoutService.initialize(timeout);
this.layoutService.update(timeout); this.layoutService.update(timeout);
} }
@ -67,7 +99,7 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
this.layoutService.clear(); this.layoutService.clear();
} }
protected subscribeToParticipants() { private subscribeToParticipants() {
this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p) => { this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p) => {
this.localParticipant = p; this.localParticipant = p;
this.layoutService.update(); this.layoutService.update();

View File

@ -1,11 +1,17 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
/**
* @internal
*/
export interface DialogData { export interface DialogData {
title: string; title: string;
description: string; description: string;
showActionButtons: boolean; showActionButtons: boolean;
} }
/**
* @internal
*/
@Component({ @Component({
selector: 'ov-dialog-template', selector: 'ov-dialog-template',

View File

@ -1,10 +1,43 @@
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
HostListener,
OnInit,
ViewChild
} from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ChatMessage } from '../../../models/chat.model'; import { ChatMessage } from '../../../models/chat.model';
import { MenuType } from '../../../models/menu.model'; import { MenuType } from '../../../models/menu.model';
import { ChatService } from '../../../services/chat/chat.service'; import { ChatService } from '../../../services/chat/chat.service';
import { SidenavMenuService } from '../../../services/sidenav-menu/sidenav-menu.service'; import { SidenavMenuService } from '../../../services/sidenav-menu/sidenav-menu.service';
/**
*
* The **ChatPanelComponent** is hosted inside of the {@link PanelComponent}.
* It is in charge of displaying the session chat.
*
* <div class="custom-table-container">
* <div>
*
* <h3>OpenVidu Angular Directives</h3>
*
* The ChatPanelComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives}
* for doing this.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovChatPanel** | {@link ChatPanelDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link OpenViduAngularDirectiveModule OpenVidu Angular Directives}</span>
* </p>
* </div>
* </div>
*/
@Component({ @Component({
selector: 'ov-chat-panel', selector: 'ov-chat-panel',
templateUrl: './chat-panel.component.html', templateUrl: './chat-panel.component.html',
@ -12,16 +45,31 @@ import { SidenavMenuService } from '../../../services/sidenav-menu/sidenav-menu.
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class ChatPanelComponent implements OnInit, AfterViewInit { export class ChatPanelComponent implements OnInit, AfterViewInit {
/**
* @ignore
*/
@ViewChild('chatScroll') chatScroll: ElementRef; @ViewChild('chatScroll') chatScroll: ElementRef;
/**
* @ignore
*/
@ViewChild('chatInput') chatInput: ElementRef; @ViewChild('chatInput') chatInput: ElementRef;
/**
* @ignore
*/
message: string; message: string;
messageList: ChatMessage[] = []; messageList: ChatMessage[] = [];
isMenuOpened: boolean;
private chatMessageSubscription: Subscription; private chatMessageSubscription: Subscription;
/**
* @ignore
*/
constructor(private chatService: ChatService, private menuService: SidenavMenuService, private cd: ChangeDetectorRef) {} constructor(private chatService: ChatService, private menuService: SidenavMenuService, private cd: ChangeDetectorRef) {}
/**
* @ignore
*/
@HostListener('document:keydown.escape', ['$event']) @HostListener('document:keydown.escape', ['$event'])
onKeydownHandler(event: KeyboardEvent) { onKeydownHandler(event: KeyboardEvent) {
if (this.menuService.isMenuOpened()) { if (this.menuService.isMenuOpened()) {
@ -44,6 +92,9 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
if (this.chatMessageSubscription) this.chatMessageSubscription.unsubscribe(); if (this.chatMessageSubscription) this.chatMessageSubscription.unsubscribe();
} }
/**
* @ignore
*/
eventKeyPress(event) { eventKeyPress(event) {
// Pressed 'Enter' key // Pressed 'Enter' key
if (event && event.keyCode === 13) { if (event && event.keyCode === 13) {
@ -53,7 +104,7 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
} }
sendMessage(): void { sendMessage(): void {
if(!!this.message) { if (!!this.message) {
this.chatService.sendMessage(this.message); this.chatService.sendMessage(this.message);
this.message = ''; this.message = '';
} }

View File

@ -4,6 +4,42 @@ import { ChatPanelDirective, ParticipantsPanelDirective } from '../../directives
import { MenuType } from '../../models/menu.model'; import { MenuType } from '../../models/menu.model';
import { SidenavMenuService } from '../../services/sidenav-menu/sidenav-menu.service'; import { SidenavMenuService } from '../../services/sidenav-menu/sidenav-menu.service';
/**
*
* The **PanelComponent** is hosted inside of the {@link VideoconferenceComponent}.
* It is in charge of displaying the videoconference panels providing functionalities to the videoconference app
* such as the chat ({@link ChatPanelComponent}) and list of participants ({@link ParticipantsPanelComponent}) .
*
* <div class="custom-table-container">
* <div>
*
* <h3>OpenVidu Angular Directives</h3>
*
* The PanelComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives}
* for doing this.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovPanel** | {@link PanelDirective} |
*
* </br>
*
* It is also providing us a way to **replace the children panels** to the default panel.
* It will recognise the following directive in a child element.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovChatPanel** | {@link ChatPanelDirective} |
* | ***ovParticipantsPanel** | {@link ParticipantsPanelDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link OpenViduAngularDirectiveModule OpenVidu Angular Directives}</span>
* </p>
* </div>
* </div>
*/
@Component({ @Component({
selector: 'ov-panel', selector: 'ov-panel',
templateUrl: './panel.component.html', templateUrl: './panel.component.html',
@ -11,7 +47,14 @@ import { SidenavMenuService } from '../../services/sidenav-menu/sidenav-menu.ser
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class PanelComponent implements OnInit { export class PanelComponent implements OnInit {
/**
* @ignore
*/
@ContentChild('participantsPanel', { read: TemplateRef }) participantsPanelTemplate: TemplateRef<any>; @ContentChild('participantsPanel', { read: TemplateRef }) participantsPanelTemplate: TemplateRef<any>;
/**
* @ignore
*/
@ContentChild('chatPanel', { read: TemplateRef }) chatPanelTemplate: TemplateRef<any>; @ContentChild('chatPanel', { read: TemplateRef }) chatPanelTemplate: TemplateRef<any>;
@ContentChild(ParticipantsPanelDirective) @ContentChild(ParticipantsPanelDirective)
@ -34,23 +77,28 @@ export class PanelComponent implements OnInit {
isParticipantsPanelOpened: boolean; isParticipantsPanelOpened: boolean;
isChatPanelOpened: boolean; isChatPanelOpened: boolean;
menuSubscription: Subscription; private menuSubscription: Subscription;
/**
* @ignore
*/
constructor(protected menuService: SidenavMenuService, private cd: ChangeDetectorRef) {} constructor(protected menuService: SidenavMenuService, private cd: ChangeDetectorRef) {}
ngOnInit(): void { ngOnInit(): void {
this.subscribeToPanelToggling(); this.subscribeToPanelToggling();
} }
subscribeToPanelToggling() {
this.menuSubscription = this.menuService.menuOpenedObs.pipe(skip(1)).subscribe((ev: { opened: boolean; type?: MenuType }) => {
this.isChatPanelOpened = ev.opened && ev.type === MenuType.CHAT;
this.isParticipantsPanelOpened = ev.opened && ev.type === MenuType.PARTICIPANTS;
this.cd.markForCheck();
});
}
ngOnDestroy() { ngOnDestroy() {
this.isChatPanelOpened = false; this.isChatPanelOpened = false;
this.isParticipantsPanelOpened = false; this.isParticipantsPanelOpened = false;
if (this.menuSubscription) this.menuSubscription.unsubscribe(); if (this.menuSubscription) this.menuSubscription.unsubscribe();
} }
private subscribeToPanelToggling() {
this.menuSubscription = this.menuService.menuOpenedObs.pipe(skip(1)).subscribe((ev: { opened: boolean; type?: MenuType }) => {
this.isChatPanelOpened = ev.opened && ev.type === MenuType.CHAT;
this.isParticipantsPanelOpened = ev.opened && ev.type === MenuType.PARTICIPANTS;
this.cd.markForCheck();
});
}
} }

View File

@ -4,6 +4,54 @@ import { ParticipantPanelItemElementsDirective } from '../../../../directives/te
import { ParticipantAbstractModel } from '../../../../models/participant.model'; import { ParticipantAbstractModel } from '../../../../models/participant.model';
import { OpenViduAngularConfigService } from '../../../../services/config/openvidu-angular.config.service'; import { OpenViduAngularConfigService } from '../../../../services/config/openvidu-angular.config.service';
/**
*
* The **ParticipantPanelItemComponent** is hosted inside of the {@link ParticipantsPanelComponent}.
* It is in charge of displaying the participants information inside of the ParticipansPanelComponent.
*
* <div class="custom-table-container">
* <div>
* <h3>API Directives</h3>
*
* This component allows us to show or hide certain HTML elements with the following {@link https://angular.io/guide/attribute-directives Angular attribute directives}
* with the aim of fully customizing the ToolbarComponent.
*
* | **Name** | **Type** | **Reference** |
* | :----------------------------: | :-------: | :---------------------------------------------: |
* | **muteButton** | `boolean` | {@link ParticipantPanelItemMuteButtonDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link ApiDirectiveModule API Directives}</span>
* </p>
*
* </div>
* <div>
*
* <h3>OpenVidu Angular Directives</h3>
*
* The ParticipantPanelItemComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives}
* for doing this.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovParticipantPanelItem** | {@link ParticipantPanelItemDirective} |
*
* </br>
*
* It is also providing us a way to **add additional buttons** to the default participant panel item.
* It will recognise the following directive in a child element.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovParticipantPanelItemElements** | {@link ParticipantPanelItemElementsDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link OpenViduAngularDirectiveModule OpenVidu Angular Directives}</span>
* </p>
* </div>
* </div>
*/
@Component({ @Component({
selector: 'ov-participant-panel-item', selector: 'ov-participant-panel-item',
templateUrl: './participant-panel-item.component.html', templateUrl: './participant-panel-item.component.html',
@ -11,10 +59,20 @@ import { OpenViduAngularConfigService } from '../../../../services/config/openvi
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class ParticipantPanelItemComponent implements OnInit, OnDestroy { export class ParticipantPanelItemComponent implements OnInit, OnDestroy {
/**
* @ignore
*/
@ContentChild('participantPanelItemElements', { read: TemplateRef }) participantPanelItemElementsTemplate: TemplateRef<any>; @ContentChild('participantPanelItemElements', { read: TemplateRef }) participantPanelItemElementsTemplate: TemplateRef<any>;
/**
* @ignore
*/
showMuteButton: boolean = true; showMuteButton: boolean = true;
private muteButtonSub: Subscription; private muteButtonSub: Subscription;
/**
* @ignore
*/
@ContentChild(ParticipantPanelItemElementsDirective) @ContentChild(ParticipantPanelItemElementsDirective)
set externalItemElements(externalItemElements: ParticipantPanelItemElementsDirective) { set externalItemElements(externalItemElements: ParticipantPanelItemElementsDirective) {
// This directive will has value only when ITEM ELEMENTS component tagget with '*ovParticipantPanelItemElements' directive // This directive will has value only when ITEM ELEMENTS component tagget with '*ovParticipantPanelItemElements' directive
@ -25,11 +83,18 @@ export class ParticipantPanelItemComponent implements OnInit, OnDestroy {
} }
@Input() @Input()
set participant(p: ParticipantAbstractModel) { set participant(participant: ParticipantAbstractModel) {
this._participant = p; this._participant = participant;
} }
/**
* @ignore
*/
_participant: ParticipantAbstractModel; _participant: ParticipantAbstractModel;
/**
* @ignore
*/
constructor(private libService: OpenViduAngularConfigService, private cd: ChangeDetectorRef) {} constructor(private libService: OpenViduAngularConfigService, private cd: ChangeDetectorRef) {}
ngOnInit(): void { ngOnInit(): void {
@ -39,6 +104,9 @@ export class ParticipantPanelItemComponent implements OnInit, OnDestroy {
if (this.muteButtonSub) this.muteButtonSub.unsubscribe(); if (this.muteButtonSub) this.muteButtonSub.unsubscribe();
} }
/**
* @ignore
*/
toggleMuteForcibly() { toggleMuteForcibly() {
this._participant.setMutedForcibly(!this._participant.isMutedForcibly); this._participant.setMutedForcibly(!this._participant.isMutedForcibly);
} }

View File

@ -5,6 +5,39 @@ import { SidenavMenuService } from '../../../..//services/sidenav-menu/sidenav-m
import { ParticipantPanelItemDirective } from '../../../../directives/template/openvidu-angular.directive'; import { ParticipantPanelItemDirective } from '../../../../directives/template/openvidu-angular.directive';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
/**
*
* The **ParticipantsPanelComponent** is hosted inside of the {@link PanelComponent}.
* It is in charge of displaying the participants connected to the session.
* This component is composed by the {@link ParticipantPanelItemComponent}.
*
* <div class="custom-table-container">
* <div>
*
* <h3>OpenVidu Angular Directives</h3>
*
* The ParticipantsPanelComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives}
* for doing this.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovParticipantsPanel** | {@link ParticipantsPanelDirective} |
*
* </br>
*
* As the ParticipantsPanelComponent is composed by ParticipantPanelItemComponent, it is also providing us a way to **replace the participant item** with a custom one.
* It will recognise the following directive in a child element.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovParticipantPanelItem** | {@link ParticipantPanelItemDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link OpenViduAngularDirectiveModule OpenVidu Angular Directives}</span>
* </p>
* </div>
* </div>
*/
@Component({ @Component({
selector: 'ov-participants-panel', selector: 'ov-participants-panel',
templateUrl: './participants-panel.component.html', templateUrl: './participants-panel.component.html',
@ -15,8 +48,9 @@ export class ParticipantsPanelComponent implements OnInit, OnDestroy {
localParticipant: any; localParticipant: any;
remoteParticipants: ParticipantAbstractModel[] = []; remoteParticipants: ParticipantAbstractModel[] = [];
private localParticipantSubs: Subscription; /**
private remoteParticipantsSubs: Subscription; * @ignore
*/
@ContentChild('participantPanelItem', { read: TemplateRef }) participantPanelItemTemplate: TemplateRef<any>; @ContentChild('participantPanelItem', { read: TemplateRef }) participantPanelItemTemplate: TemplateRef<any>;
@ContentChild(ParticipantPanelItemDirective) @ContentChild(ParticipantPanelItemDirective)
@ -28,16 +62,19 @@ export class ParticipantsPanelComponent implements OnInit, OnDestroy {
} }
} }
private localParticipantSubs: Subscription;
private remoteParticipantsSubs: Subscription;
/**
* @ignore
*/
constructor( constructor(
protected participantService: ParticipantService, protected participantService: ParticipantService,
protected menuService: SidenavMenuService, protected menuService: SidenavMenuService,
private cd: ChangeDetectorRef private cd: ChangeDetectorRef
) { ) {}
}
ngOnInit(): void { ngOnInit(): void {
this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => { this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => {
this.localParticipant = p; this.localParticipant = p;
// Mark for re-rendering using an impure pipe 'streamsTypesEnabled' // Mark for re-rendering using an impure pipe 'streamsTypesEnabled'

View File

@ -13,6 +13,9 @@ import { OpenViduService } from '../../services/openvidu/openvidu.service';
import { ParticipantService } from '../../services/participant/participant.service'; import { ParticipantService } from '../../services/participant/participant.service';
import { StorageService } from '../../services/storage/storage.service'; import { StorageService } from '../../services/storage/storage.service';
/**
* @internal
*/
@Component({ @Component({
selector: 'ov-pre-join', selector: 'ov-pre-join',
templateUrl: './pre-join.component.html', templateUrl: './pre-join.component.html',

View File

@ -27,6 +27,11 @@ import { LayoutService } from '../../services/layout/layout.service';
import { Subscription, skip } from 'rxjs'; import { Subscription, skip } from 'rxjs';
import { MenuType } from '../../models/menu.model'; import { MenuType } from '../../models/menu.model';
import { SidenavMenuService } from '../../services/sidenav-menu/sidenav-menu.service'; import { SidenavMenuService } from '../../services/sidenav-menu/sidenav-menu.service';
import { PlatformService } from '../../services/platform/platform.service';
/**
* @internal
*/
@Component({ @Component({
selector: 'ov-session', selector: 'ov-session',
@ -68,7 +73,8 @@ export class SessionComponent implements OnInit {
protected chatService: ChatService, protected chatService: ChatService,
protected tokenService: TokenService, protected tokenService: TokenService,
protected layoutService: LayoutService, protected layoutService: LayoutService,
protected menuService: SidenavMenuService protected menuService: SidenavMenuService,
private platformService: PlatformService
) { ) {
this.log = this.loggerSrv.get('SessionComponent'); this.log = this.loggerSrv.get('SessionComponent');
} }
@ -107,9 +113,9 @@ export class SessionComponent implements OnInit {
await this.connectToSession(); await this.connectToSession();
// Workaround, firefox does not have audio when publisher join with muted camera // Workaround, firefox does not have audio when publisher join with muted camera
// if (this.platformService.isFirefox() && !this.localUserService.hasCameraVideoActive()) { // if (this.platformService.isFirefox() && !this.participantService.hasCameraVideoActive()) {
// this.openviduService.publishVideo(this.localUserService.getMyCameraPublisher(), true); // this.openviduService.publishVideo(this.participantService.getMyCameraPublisher(), true);
// this.openviduService.publishVideo(this.localUserService.getMyCameraPublisher(), false); // this.openviduService.publishVideo(this.participantService.getMyCameraPublisher(), false);
// } // }
} }

View File

@ -55,7 +55,7 @@
<span *ngIf="videoSizeIcon === videoSizeIconEnum.NORMAL">Zoom out</span> <span *ngIf="videoSizeIcon === videoSizeIconEnum.NORMAL">Zoom out</span>
<span *ngIf="videoSizeIcon === videoSizeIconEnum.BIG">Zoom in</span> <span *ngIf="videoSizeIcon === videoSizeIconEnum.BIG">Zoom in</span>
</button> </button>
<button mat-menu-item id="volumeButton" *ngIf="!this._stream.local" (click)="toggleSound()"> <button mat-menu-item id="volumeButton" *ngIf="!this._stream.local" (click)="toggleMuteForcibly()">
<mat-icon *ngIf="!_stream.participant.isMutedForcibly">volume_up</mat-icon> <mat-icon *ngIf="!_stream.participant.isMutedForcibly">volume_up</mat-icon>
<span *ngIf="!_stream.participant.isMutedForcibly">Mute sound</span> <span *ngIf="!_stream.participant.isMutedForcibly">Mute sound</span>

View File

@ -14,44 +14,105 @@ import { StreamModel } from '../../models/participant.model';
import { ParticipantService } from '../../services/participant/participant.service'; import { ParticipantService } from '../../services/participant/participant.service';
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
/**
* The **StreamComponent** is hosted inside of the {@link LayoutComponent}.
* It is in charge of displaying the participant video stream in the videoconference layout.
*
* <div class="custom-table-container">
* <div>
* <h3>API Directives</h3>
*
* This component allows us to show or hide certain HTML elements with the following {@link https://angular.io/guide/attribute-directives Angular attribute directives}
* with the aim of fully customizing the StreamComponent.
*
* | **Parameter** | **Type** | **Reference** |
* | :----------------------------: | :-------: | :---------------------------------------------: |
* | **displayParticipantName** | `boolean` | {@link StreamDisplayParticipantNameDirective} |
* | **displayAudioDetection** | `boolean` | {@link StreamDisplayAudioDetectionDirective} |
* | **settingsButton** | `boolean` | {@link StreamSettingsButtonDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link ApiDirectiveModule API Directives}</span>
* </p>
* </div>
*
* <div>
* <h3>OpenVidu Angular Directives</h3>
*
* The StreamComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives}
* for doing this.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovStream** | {@link StreamDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link OpenViduAngularDirectiveModule OpenVidu Angular Directives}</span>
* </p>
* </div>
*
* </div>
*/
@Component({ @Component({
selector: 'ov-stream', selector: 'ov-stream',
templateUrl: './stream.component.html', templateUrl: './stream.component.html',
styleUrls: ['./stream.component.css'] styleUrls: ['./stream.component.css']
}) })
export class StreamComponent implements OnInit { export class StreamComponent implements OnInit {
/**
* @ignore
*/
@ViewChild(MatMenuTrigger) public menuTrigger: MatMenuTrigger; @ViewChild(MatMenuTrigger) public menuTrigger: MatMenuTrigger;
/**
* @ignore
*/
@ViewChild('menu') menu: MatMenuPanel; @ViewChild('menu') menu: MatMenuPanel;
/**
* @ignore
*/
videoSizeIconEnum = VideoSizeIcon; videoSizeIconEnum = VideoSizeIcon;
/**
* @ignore
*/
videoTypeEnum = VideoType; videoTypeEnum = VideoType;
/**
* @ignore
*/
videoSizeIcon: VideoSizeIcon = VideoSizeIcon.BIG; videoSizeIcon: VideoSizeIcon = VideoSizeIcon.BIG;
/**
* @ignore
*/
toggleNickname: boolean; toggleNickname: boolean;
/**
* @ignore
*/
_stream: StreamModel; _stream: StreamModel;
/**
* @ignore
*/
nickname: string; nickname: string;
/**
* @ignore
*/
isMinimal: boolean = false; isMinimal: boolean = false;
/**
* @ignore
*/
showNickname: boolean = true; showNickname: boolean = true;
/**
* @ignore
*/
showAudioDetection: boolean = true; showAudioDetection: boolean = true;
/**
* @ignore
*/
showSettingsButton: boolean = true; showSettingsButton: boolean = true;
private _streamContainer: ElementRef; /**
* @ignore
private minimalSub: Subscription; */
private displayParticipantNameSub: Subscription;
private displayAudioDetectionSub: Subscription;
private settingsButtonSub: Subscription;
constructor(
protected documentService: DocumentService,
protected openviduService: OpenViduService,
protected layoutService: LayoutService,
protected participantService: ParticipantService,
protected storageService: StorageService,
protected cdkSrv: CdkOverlayService,
private libService: OpenViduAngularConfigService
) {}
@ViewChild('streamContainer', { static: false, read: ElementRef }) @ViewChild('streamContainer', { static: false, read: ElementRef })
set streamContainer(streamContainer: ElementRef) { set streamContainer(streamContainer: ElementRef) {
setTimeout(() => { setTimeout(() => {
@ -73,6 +134,9 @@ export class StreamComponent implements OnInit {
} }
} }
/**
* @ignore
*/
@ViewChild('nicknameInput') @ViewChild('nicknameInput')
set nicknameInputElement(element: ElementRef) { set nicknameInputElement(element: ElementRef) {
setTimeout(() => { setTimeout(() => {
@ -80,6 +144,25 @@ export class StreamComponent implements OnInit {
}); });
} }
private _streamContainer: ElementRef;
private minimalSub: Subscription;
private displayParticipantNameSub: Subscription;
private displayAudioDetectionSub: Subscription;
private settingsButtonSub: Subscription;
/**
* @ignore
*/
constructor(
protected documentService: DocumentService,
protected openviduService: OpenViduService,
protected layoutService: LayoutService,
protected participantService: ParticipantService,
protected storageService: StorageService,
protected cdkSrv: CdkOverlayService,
private libService: OpenViduAngularConfigService
) {}
ngOnInit() { ngOnInit() {
this.subscribeToStreamDirectives(); this.subscribeToStreamDirectives();
} }
@ -89,8 +172,12 @@ export class StreamComponent implements OnInit {
if (this.settingsButtonSub) this.settingsButtonSub.unsubscribe(); if (this.settingsButtonSub) this.settingsButtonSub.unsubscribe();
if (this.displayAudioDetectionSub) this.displayAudioDetectionSub.unsubscribe(); if (this.displayAudioDetectionSub) this.displayAudioDetectionSub.unsubscribe();
if (this.displayParticipantNameSub) this.displayParticipantNameSub.unsubscribe(); if (this.displayParticipantNameSub) this.displayParticipantNameSub.unsubscribe();
if (this.minimalSub) this.minimalSub.unsubscribe();
} }
/**
* @ignore
*/
toggleVideoEnlarged() { toggleVideoEnlarged() {
if (!!this._stream.streamManager?.stream?.connection?.connectionId) { if (!!this._stream.streamManager?.stream?.connection?.connectionId) {
if (this.openviduService.isMyOwnConnection(this._stream.streamManager?.stream?.connection?.connectionId)) { if (this.openviduService.isMyOwnConnection(this._stream.streamManager?.stream?.connection?.connectionId)) {
@ -102,6 +189,9 @@ export class StreamComponent implements OnInit {
this.layoutService.update(); this.layoutService.update();
} }
/**
* @ignore
*/
toggleVideoMenu(event) { toggleVideoMenu(event) {
if (this.menuTrigger.menuOpen) { if (this.menuTrigger.menuOpen) {
this.menuTrigger.closeMenu(); this.menuTrigger.closeMenu();
@ -111,16 +201,25 @@ export class StreamComponent implements OnInit {
this.menuTrigger.openMenu(); this.menuTrigger.openMenu();
} }
toggleSound() { /**
* @ignore
*/
toggleMuteForcibly() {
this._stream.participant.setMutedForcibly(!this._stream.participant.isMutedForcibly); this._stream.participant.setMutedForcibly(!this._stream.participant.isMutedForcibly);
} }
/**
* @ignore
*/
toggleNicknameForm() { toggleNicknameForm() {
if (this._stream.participant.local) { if (this._stream.participant.local) {
this.toggleNickname = !this.toggleNickname; this.toggleNickname = !this.toggleNickname;
} }
} }
/**
* @ignore
*/
updateNickname(event) { updateNickname(event) {
if (event?.keyCode === 13 || event?.type === 'focusout') { if (event?.keyCode === 13 || event?.type === 'focusout') {
if (!!this.nickname) { if (!!this.nickname) {
@ -132,6 +231,9 @@ export class StreamComponent implements OnInit {
} }
} }
/**
* @ignore
*/
async replaceScreenTrack() { async replaceScreenTrack() {
const properties: PublisherProperties = { const properties: PublisherProperties = {
videoSource: ScreenType.SCREEN, videoSource: ScreenType.SCREEN,
@ -147,7 +249,6 @@ export class StreamComponent implements OnInit {
} }
private subscribeToStreamDirectives() { private subscribeToStreamDirectives() {
this.minimalSub = this.libService.minimalObs.subscribe((value: boolean) => { this.minimalSub = this.libService.minimalObs.subscribe((value: boolean) => {
this.isMinimal = value; this.isMinimal = value;
}); });

View File

@ -29,6 +29,60 @@ import { MenuType } from '../../models/menu.model';
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
import { ToolbarAdditionalButtonsDirective } from '../../directives/template/openvidu-angular.directive'; import { ToolbarAdditionalButtonsDirective } from '../../directives/template/openvidu-angular.directive';
/**
*
* The **ToolbarComponent** is hosted inside of the {@link VideoconferenceComponent}.
* It is in charge of displaying the participants controlls for handling the media, panels and more videoconference features.
*
* <div class="custom-table-container">
* <div>
* <h3>API Directives</h3>
*
* This component allows us to show or hide certain HTML elements with the following {@link https://angular.io/guide/attribute-directives Angular attribute directives}
* with the aim of fully customizing the ToolbarComponent.
*
* | **Name** | **Type** | **Reference** |
* | :----------------------------: | :-------: | :---------------------------------------------: |
* | **screenshareButton** | `boolean` | {@link ToolbarScreenshareButtonDirective} |
* | **fullscreenButton** | `boolean` | {@link ToolbarFullscreenButtonDirective} |
* | **leaveButton** | `boolean` | {@link ToolbarLeaveButtonDirective} |
* | **chatPanelButton** | `boolean` | {@link ToolbarChatPanelButtonDirective} |
* | **participantsPanelButton** | `boolean` | {@link ToolbarParticipantsPanelButtonDirective} |
* | **displayLogo** | `boolean` | {@link ToolbarDisplayLogoDirective} |
* | **displaySessionName** | `boolean` | {@link ToolbarDisplaySessionNameDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link ApiDirectiveModule API Directives}</span>
* </p>
*
* </div>
* <div>
*
* <h3>OpenVidu Angular Directives</h3>
*
* The ToolbarComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives}
* for doing this.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovToolbar** | {@link ToolbarDirective} |
*
* </br>
*
* It is also providing us a way to **add additional buttons** to the default toolbar.
* It will recognise the following directive in a child element.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovToolbarAdditionalButtons** | {@link ToolbarAdditionalButtonsDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link OpenViduAngularDirectiveModule OpenVidu Angular Directives}</span>
* </p>
* </div>
* </div>
*/
@Component({ @Component({
selector: 'ov-toolbar', selector: 'ov-toolbar',
templateUrl: './toolbar.component.html', templateUrl: './toolbar.component.html',
@ -36,8 +90,14 @@ import { ToolbarAdditionalButtonsDirective } from '../../directives/template/ope
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class ToolbarComponent implements OnInit, OnDestroy { export class ToolbarComponent implements OnInit, OnDestroy {
/**
* @ignore
*/
@ContentChild('toolbarAdditionalButtons', { read: TemplateRef }) toolbarAdditionalButtonsTemplate: TemplateRef<any>; @ContentChild('toolbarAdditionalButtons', { read: TemplateRef }) toolbarAdditionalButtonsTemplate: TemplateRef<any>;
/**
* @ignore
*/
@ContentChild(ToolbarAdditionalButtonsDirective) @ContentChild(ToolbarAdditionalButtonsDirective)
set externalAdditionalButtons(externalAdditionalButtons: ToolbarAdditionalButtonsDirective) { set externalAdditionalButtons(externalAdditionalButtons: ToolbarAdditionalButtonsDirective) {
// This directive will has value only when ADDITIONAL BUTTONS component tagget with '*ovToolbarAdditionalButtons' directive // This directive will has value only when ADDITIONAL BUTTONS component tagget with '*ovToolbarAdditionalButtons' directive
@ -55,26 +115,86 @@ export class ToolbarComponent implements OnInit, OnDestroy {
@Output() onParticipantsPanelButtonClicked = new EventEmitter<any>(); @Output() onParticipantsPanelButtonClicked = new EventEmitter<any>();
@Output() onChatPanelButtonClicked = new EventEmitter<any>(); @Output() onChatPanelButtonClicked = new EventEmitter<any>();
/**
* @ignore
*/
session: Session; session: Session;
/**
* @ignore
*/
unreadMessages: number = 0; unreadMessages: number = 0;
/**
* @ignore
*/
messageList: ChatMessage[] = []; messageList: ChatMessage[] = [];
/**
* @ignore
*/
isScreenShareActive: boolean; isScreenShareActive: boolean;
/**
* @ignore
*/
isWebcamVideoActive: boolean; isWebcamVideoActive: boolean;
/**
* @ignore
*/
isWebcamAudioActive: boolean; isWebcamAudioActive: boolean;
/**
* @ignore
*/
isConnectionLost: boolean; isConnectionLost: boolean;
/**
* @ignore
*/
hasVideoDevices: boolean; hasVideoDevices: boolean;
/**
* @ignore
*/
hasAudioDevices: boolean; hasAudioDevices: boolean;
/**
* @ignore
*/
isFullscreenActive: boolean = false; isFullscreenActive: boolean = false;
/**
* @ignore
*/
isChatOpened: boolean = false; isChatOpened: boolean = false;
/**
* @ignore
*/
isParticipantsOpened: boolean = false; isParticipantsOpened: boolean = false;
/**
* @ignore
*/
isMinimal: boolean = false; isMinimal: boolean = false;
/**
* @ignore
*/
showScreenshareButton = true; showScreenshareButton = true;
/**
* @ignore
*/
showFullscreenButton: boolean = true; showFullscreenButton: boolean = true;
/**
* @ignore
*/
showLeaveButton: boolean = true; showLeaveButton: boolean = true;
/**
* @ignore
*/
showParticipantsPanelButton: boolean = true; showParticipantsPanelButton: boolean = true;
/**
* @ignore
*/
showChatPanelButton: boolean = true; showChatPanelButton: boolean = true;
/**
* @ignore
*/
showLogo: boolean = true; showLogo: boolean = true;
/**
* @ignore
*/
showSessionName: boolean = true; showSessionName: boolean = true;
private log: ILogger; private log: ILogger;
@ -91,25 +211,9 @@ export class ToolbarComponent implements OnInit, OnDestroy {
private displaySessionNameSub: Subscription; private displaySessionNameSub: Subscription;
private currentWindowHeight = window.innerHeight; private currentWindowHeight = window.innerHeight;
@HostListener('window:resize', ['$event']) /**
sizeChange(event) { * @ignore
if (this.currentWindowHeight >= window.innerHeight) { */
// The user has exit the fullscreen mode
this.isFullscreenActive = false;
this.currentWindowHeight = window.innerHeight;
}
}
@HostListener('document:keydown', ['$event'])
keyDown(event: KeyboardEvent) {
if (event.key === 'F11') {
event.preventDefault();
this.toggleFullscreen();
this.currentWindowHeight = window.innerHeight;
return false;
}
}
constructor( constructor(
protected documentService: DocumentService, protected documentService: DocumentService,
protected chatService: ChatService, protected chatService: ChatService,
@ -125,6 +229,30 @@ export class ToolbarComponent implements OnInit, OnDestroy {
) { ) {
this.log = this.loggerSrv.get('ToolbarComponent'); this.log = this.loggerSrv.get('ToolbarComponent');
} }
/**
* @ignore
*/
@HostListener('window:resize', ['$event'])
sizeChange(event) {
if (this.currentWindowHeight >= window.innerHeight) {
// The user has exit the fullscreen mode
this.isFullscreenActive = false;
this.currentWindowHeight = window.innerHeight;
}
}
/**
* @ignore
*/
@HostListener('document:keydown', ['$event'])
keyDown(event: KeyboardEvent) {
if (event.key === 'F11') {
event.preventDefault();
this.toggleFullscreen();
this.currentWindowHeight = window.innerHeight;
return false;
}
}
async ngOnInit() { async ngOnInit() {
this.subscribeToToolbarDirectives(); this.subscribeToToolbarDirectives();
@ -151,8 +279,12 @@ export class ToolbarComponent implements OnInit, OnDestroy {
if (this.chatPanelButtonSub) this.chatPanelButtonSub.unsubscribe(); if (this.chatPanelButtonSub) this.chatPanelButtonSub.unsubscribe();
if (this.displayLogoSub) this.displayLogoSub.unsubscribe(); if (this.displayLogoSub) this.displayLogoSub.unsubscribe();
if (this.displaySessionNameSub) this.displaySessionNameSub.unsubscribe(); if (this.displaySessionNameSub) this.displaySessionNameSub.unsubscribe();
if (this.minimalSub) this.minimalSub.unsubscribe();
} }
/**
* @ignore
*/
toggleMicrophone() { toggleMicrophone() {
this.onMicrophoneButtonClicked.emit(); this.onMicrophoneButtonClicked.emit();
@ -166,6 +298,9 @@ export class ToolbarComponent implements OnInit, OnDestroy {
this.openviduService.publishAudio(this.participantService.getMyScreenPublisher(), !this.participantService.hasScreenAudioActive()); this.openviduService.publishAudio(this.participantService.getMyScreenPublisher(), !this.participantService.hasScreenAudioActive());
} }
/**
* @ignore
*/
async toggleCamera() { async toggleCamera() {
this.onCameraButtonClicked.emit(); this.onCameraButtonClicked.emit();
@ -200,6 +335,9 @@ export class ToolbarComponent implements OnInit, OnDestroy {
} }
} }
/**
* @ignore
*/
async toggleScreenShare() { async toggleScreenShare() {
this.onScreenshareButtonClicked.emit(); this.onScreenshareButtonClicked.emit();
@ -277,22 +415,34 @@ export class ToolbarComponent implements OnInit, OnDestroy {
} }
} }
/**
* @ignore
*/
leaveSession() { leaveSession() {
this.log.d('Leaving session...'); this.log.d('Leaving session...');
this.openviduService.disconnect(); this.openviduService.disconnect();
this.onLeaveButtonClicked.emit(); this.onLeaveButtonClicked.emit();
} }
/**
* @ignore
*/
toggleParticipantsPanel() { toggleParticipantsPanel() {
this.onParticipantsPanelButtonClicked.emit(); this.onParticipantsPanelButtonClicked.emit();
this.menuService.toggleMenu(MenuType.PARTICIPANTS); this.menuService.toggleMenu(MenuType.PARTICIPANTS);
} }
/**
* @ignore
*/
toggleChatPanel() { toggleChatPanel() {
this.onChatPanelButtonClicked.emit(); this.onChatPanelButtonClicked.emit();
this.menuService.toggleMenu(MenuType.CHAT); this.menuService.toggleMenu(MenuType.CHAT);
} }
/**
* @ignore
*/
toggleFullscreen() { toggleFullscreen() {
this.isFullscreenActive = !this.isFullscreenActive; this.isFullscreenActive = !this.isFullscreenActive;
this.documentService.toggleFullscreen('session-container'); this.documentService.toggleFullscreen('session-container');

View File

@ -19,6 +19,9 @@ import { ActionService } from '../../services/action/action.service';
import { ParticipantService } from '../../services/participant/participant.service'; import { ParticipantService } from '../../services/participant/participant.service';
import { ParticipantAbstractModel } from '../../models/participant.model'; import { ParticipantAbstractModel } from '../../models/participant.model';
/**
* @internal
*/
@Component({ @Component({
selector: 'ov-user-settings', selector: 'ov-user-settings',
templateUrl: './user-settings.component.html', templateUrl: './user-settings.component.html',

View File

@ -2,6 +2,9 @@ import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular
import { StreamManager } from 'openvidu-browser'; import { StreamManager } from 'openvidu-browser';
import { VideoType } from '../../models/video-type.model'; import { VideoType } from '../../models/video-type.model';
/**
* @internal
*/
@Component({ @Component({
selector: 'ov-video', selector: 'ov-video',
template: ` template: `

View File

@ -48,12 +48,10 @@
(onParticipantsPanelButtonClicked)="onParticipantsPanelButtonClicked()" (onParticipantsPanelButtonClicked)="onParticipantsPanelButtonClicked()"
(onChatPanelButtonClicked)="onChatPanelButtonClicked()" (onChatPanelButtonClicked)="onChatPanelButtonClicked()"
> >
<ng-template #toolbarAdditionalButtons>
<ng-template #toolbarAdditionalButtons> <ng-container *ngTemplateOutlet="openviduAngularToolbarAdditionalButtonsTemplate"></ng-container>
<ng-container *ngTemplateOutlet="openviduAngularToolbarAdditionalButtonsTemplate"></ng-container> </ng-template>
</ng-template> </ov-toolbar>
</ov-toolbar>
</ng-template> </ng-template>
<ng-template #defaultPanel> <ng-template #defaultPanel>
@ -84,11 +82,11 @@
<ng-template #defaultParticipantPanelItem let-participant> <ng-template #defaultParticipantPanelItem let-participant>
<ov-participant-panel-item [participant]="participant" id="default-participant-panel-item"> <ov-participant-panel-item [participant]="participant" id="default-participant-panel-item">
<ng-template #participantPanelItemElements> <ng-template #participantPanelItemElements>
<ng-container *ngTemplateOutlet="openviduAngularParticipantPanelItemElementsTemplate; context: { $implicit: participant }"></ng-container> <ng-container
*ngTemplateOutlet="openviduAngularParticipantPanelItemElementsTemplate; context: { $implicit: participant }"
></ng-container>
</ng-template> </ng-template>
</ov-participant-panel-item> </ov-participant-panel-item>
</ng-template> </ng-template>

View File

@ -25,6 +25,7 @@ import {
} from '../../directives/template/openvidu-angular.directive'; } from '../../directives/template/openvidu-angular.directive';
import { ILogger } from '../../models/logger.model'; import { ILogger } from '../../models/logger.model';
import { ParticipantProperties } from '../../models/participant.model'; import { ParticipantProperties } from '../../models/participant.model';
import { TokenModel } from '../../models/token.model';
import { ActionService } from '../../services/action/action.service'; import { ActionService } from '../../services/action/action.service';
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
import { DeviceService } from '../../services/device/device.service'; import { DeviceService } from '../../services/device/device.service';
@ -34,6 +35,68 @@ import { ParticipantService } from '../../services/participant/participant.servi
import { StorageService } from '../../services/storage/storage.service'; import { StorageService } from '../../services/storage/storage.service';
import { TokenService } from '../../services/token/token.service'; import { TokenService } from '../../services/token/token.service';
/**
* The **VideoconferenceComponent** is the parent of all OpenVidu components.
* It allow us to create a modern, useful and powerful videoconference apps with ease.
*
* <div class="custom-table-container">
* <div>
* <h3>API Directives</h3>
*
* This component allows us to show or hide certain HTML elements with the following {@link https://angular.io/guide/attribute-directives Angular attribute directives}
* with the aim of fully customizing the videoconference application.
*
* | **Parameter** | **Type** | **Reference** |
* | :----------------------------: | :-------: | :---------------------------------------------: |
* | **minimal** | `boolean` | {@link MinimalDirective} |
* | **prejoin** | `boolean` | {@link PrejoinDirective} |
* | **participantName** | `string` | {@link ParticipantNameDirective} |
* | **videoMuted** | `boolean` | {@link VideoMutedDirective} |
* | **audioMuted** | `boolean` | {@link AudioMutedDirective} |
* | **toolbarScreenshareButton** | `boolean` | {@link ToolbarScreenshareButtonDirective} |
* | **toolbarFullscreenButton** | `boolean` | {@link ToolbarFullscreenButtonDirective} |
* | **toolbarLeaveButton** | `boolean` | {@link ToolbarLeaveButtonDirective} |
* | **toolbarChatPanelButton** | `boolean` | {@link ToolbarChatPanelButtonDirective} |
* | **toolbarParticipantsPanelButton** | `boolean` | {@link ToolbarParticipantsPanelButtonDirective} |
* | **toolbarDisplayLogo** | `boolean` | {@link ToolbarDisplayLogoDirective} |
* | **toolbarDisplaySessionName** | `boolean` | {@link ToolbarDisplaySessionNameDirective} |
* | **streamDisplayParticipantName** | `boolean` | {@link StreamDisplayParticipantNameDirective} |
* | **streamDisplayAudioDetection** | `boolean` | {@link StreamDisplayAudioDetectionDirective} |
* | **streamSettingsButton** | `boolean` | {@link StreamSettingsButtonDirective} |
* | **participantPanelItemMuteButton** | `boolean` | {@link ParticipantPanelItemMuteButtonDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link ApiDirectiveModule API Directives}</span>
* </p>
* </div>
*
* <div>
*
* <h3>OpenVidu Angular Directives</h3>
*
*
* The VideoconferenceComponent is also providing us a way to **replace the default templates** with a custom one.
* It will recognise the following {@link https://angular.io/guide/structural-directives Angular structural directives}
* in the elements added as children.
*
* | **Directive** | **Reference** |
* |:----------------------------------:|:---------------------------------------------:|
* | ***ovToolbar** | {@link ToolbarDirective} |
* | ***ovToolbarAdditionalButtons** | {@link ToolbarAdditionalButtonsDirective} |
* | ***ovPanel** | {@link PanelDirective} |
* | ***ovChatPanel** | {@link ChatPanelDirective} |
* | ***ovParticipantsPanel** | {@link ParticipantsPanelDirective} |
* | ***ovParticipantPanelItem** | {@link ParticipantPanelItemDirective} |
* | ***ovParticipantPanelItemElements** | {@link ParticipantPanelItemElementsDirective} |
* | ***ovLayout** | {@link LayoutDirective} |
* | ***ovStream** | {@link StreamDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link OpenViduAngularDirectiveModule OpenVidu Angular Directives}</span>
* </p>
* </div>
* </div>
*/
@Component({ @Component({
selector: 'ov-videoconference', selector: 'ov-videoconference',
templateUrl: './videoconference.component.html', templateUrl: './videoconference.component.html',
@ -41,40 +104,124 @@ import { TokenService } from '../../services/token/token.service';
}) })
export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewInit { export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewInit {
// *** Toolbar *** // *** Toolbar ***
/**
* @internal
*/
@ContentChild(ToolbarDirective) externalToolbar: ToolbarDirective; @ContentChild(ToolbarDirective) externalToolbar: ToolbarDirective;
/**
* @internal
*/
@ContentChild(ToolbarAdditionalButtonsDirective) externalToolbarAdditionalButtons: ToolbarAdditionalButtonsDirective; @ContentChild(ToolbarAdditionalButtonsDirective) externalToolbarAdditionalButtons: ToolbarAdditionalButtonsDirective;
// *** Panels *** // *** Panels ***
/**
* @internal
*/
@ContentChild(PanelDirective) externalPanel: PanelDirective; @ContentChild(PanelDirective) externalPanel: PanelDirective;
/**
* @internal
*/
@ContentChild(ChatPanelDirective) externalChatPanel: ChatPanelDirective; @ContentChild(ChatPanelDirective) externalChatPanel: ChatPanelDirective;
/**
* @internal
*/
@ContentChild(ParticipantsPanelDirective) externalParticipantsPanel: ParticipantsPanelDirective; @ContentChild(ParticipantsPanelDirective) externalParticipantsPanel: ParticipantsPanelDirective;
/**
* @internal
*/
@ContentChild(ParticipantPanelItemDirective) externalParticipantPanelItem: ParticipantPanelItemDirective; @ContentChild(ParticipantPanelItemDirective) externalParticipantPanelItem: ParticipantPanelItemDirective;
/**
* @internal
*/
@ContentChild(ParticipantPanelItemElementsDirective) externalParticipantPanelItemElements: ParticipantPanelItemElementsDirective; @ContentChild(ParticipantPanelItemElementsDirective) externalParticipantPanelItemElements: ParticipantPanelItemElementsDirective;
// *** Layout *** // *** Layout ***
/**
* @internal
*/
@ContentChild(LayoutDirective) externalLayout: LayoutDirective; @ContentChild(LayoutDirective) externalLayout: LayoutDirective;
/**
* @internal
*/
@ContentChild(StreamDirective) externalStream: StreamDirective; @ContentChild(StreamDirective) externalStream: StreamDirective;
/**
* @internal
*/
@ViewChild('defaultToolbar', { static: false, read: TemplateRef }) defaultToolbarTemplate: TemplateRef<any>; @ViewChild('defaultToolbar', { static: false, read: TemplateRef }) defaultToolbarTemplate: TemplateRef<any>;
/**
* @internal
*/
@ViewChild('defaultPanel', { static: false, read: TemplateRef }) defaultPanelTemplate: TemplateRef<any>; @ViewChild('defaultPanel', { static: false, read: TemplateRef }) defaultPanelTemplate: TemplateRef<any>;
/**
* @internal
*/
@ViewChild('defaultChatPanel', { static: false, read: TemplateRef }) defaultChatPanelTemplate: TemplateRef<any>; @ViewChild('defaultChatPanel', { static: false, read: TemplateRef }) defaultChatPanelTemplate: TemplateRef<any>;
/**
* @internal
*/
@ViewChild('defaultParticipantsPanel', { static: false, read: TemplateRef }) defaultParticipantsPanelTemplate: TemplateRef<any>; @ViewChild('defaultParticipantsPanel', { static: false, read: TemplateRef }) defaultParticipantsPanelTemplate: TemplateRef<any>;
/**
* @internal
*/
@ViewChild('defaultParticipantPanelItem', { static: false, read: TemplateRef }) defaultParticipantPanelItemTemplate: TemplateRef<any>; @ViewChild('defaultParticipantPanelItem', { static: false, read: TemplateRef }) defaultParticipantPanelItemTemplate: TemplateRef<any>;
/**
* @internal
*/
@ViewChild('defaultLayout', { static: false, read: TemplateRef }) defaultLayoutTemplate: TemplateRef<any>; @ViewChild('defaultLayout', { static: false, read: TemplateRef }) defaultLayoutTemplate: TemplateRef<any>;
/**
* @internal
*/
@ViewChild('defaultStream', { static: false, read: TemplateRef }) defaultStreamTemplate: TemplateRef<any>; @ViewChild('defaultStream', { static: false, read: TemplateRef }) defaultStreamTemplate: TemplateRef<any>;
/**
* @internal
*/
openviduAngularToolbarTemplate: TemplateRef<any>; openviduAngularToolbarTemplate: TemplateRef<any>;
/**
* @internal
*/
openviduAngularToolbarAdditionalButtonsTemplate: TemplateRef<any>; openviduAngularToolbarAdditionalButtonsTemplate: TemplateRef<any>;
/**
* @internal
*/
openviduAngularPanelTemplate: TemplateRef<any>; openviduAngularPanelTemplate: TemplateRef<any>;
/**
* @internal
*/
openviduAngularChatPanelTemplate: TemplateRef<any>; openviduAngularChatPanelTemplate: TemplateRef<any>;
/**
* @internal
*/
openviduAngularParticipantsPanelTemplate: TemplateRef<any>; openviduAngularParticipantsPanelTemplate: TemplateRef<any>;
/**
* @internal
*/
openviduAngularParticipantPanelItemTemplate: TemplateRef<any>; openviduAngularParticipantPanelItemTemplate: TemplateRef<any>;
/**
* @internal
*/
openviduAngularParticipantPanelItemElementsTemplate: TemplateRef<any>; openviduAngularParticipantPanelItemElementsTemplate: TemplateRef<any>;
/**
* @internal
*/
openviduAngularLayoutTemplate: TemplateRef<any>; openviduAngularLayoutTemplate: TemplateRef<any>;
/**
* @internal
*/
openviduAngularStreamTemplate: TemplateRef<any>; openviduAngularStreamTemplate: TemplateRef<any>;
// *** Parameters ***
// @Input() sessionName: string;
// @Input() participantName: string;
/**
* @param {TokenModel} tokens The tokens parameter must be an object with `webcam` and `screen` fields.
* Both of them are `string` type. See {@link TokenModel}
*/
@Input() @Input()
set tokens(tokens: { webcam: string; screen: string }) { set tokens(tokens: TokenModel) {
if (!tokens || (!tokens.webcam && !tokens.screen)) { if (!tokens || (!tokens.webcam && !tokens.screen)) {
//No tokens received //No tokens received
// throw new Error('No tokens received'); // throw new Error('No tokens received');
@ -109,18 +256,38 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
// Event sent when participant has been created // Event sent when participant has been created
@Output() onParticipantCreated = new EventEmitter<any>(); @Output() onParticipantCreated = new EventEmitter<any>();
/**
* @internal
*/
joinSessionClicked: boolean = false; joinSessionClicked: boolean = false;
/**
* @internal
*/
participantReady: boolean = false; participantReady: boolean = false;
/**
* @internal
*/
canPublish: boolean = false; canPublish: boolean = false;
/**
* @internal
*/
error: boolean = false; error: boolean = false;
/**
* @internal
*/
errorMessage: string = ''; errorMessage: string = '';
/**
* @internal
*/
showPrejoin: boolean = true; showPrejoin: boolean = true;
private externalParticipantName: string; private externalParticipantName: string;
private prejoinSub: Subscription; private prejoinSub: Subscription;
private participantNameSub: Subscription; private participantNameSub: Subscription;
private log: ILogger; private log: ILogger;
/**
* @internal
*/
constructor( constructor(
private loggerSrv: LoggerService, private loggerSrv: LoggerService,
private storageSrv: StorageService, private storageSrv: StorageService,
@ -174,6 +341,9 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
if (this.participantNameSub) this.participantNameSub.unsubscribe(); if (this.participantNameSub) this.participantNameSub.unsubscribe();
} }
/**
* @internal
*/
ngAfterViewInit() { ngAfterViewInit() {
if (this.externalToolbar) { if (this.externalToolbar) {
this.openviduAngularToolbarTemplate = this.externalToolbar.template; this.openviduAngularToolbarTemplate = this.externalToolbar.template;
@ -239,34 +409,60 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
} }
} }
/**
* @internal
*/
_onJoinButtonClicked() { _onJoinButtonClicked() {
this.joinSessionClicked = true; this.joinSessionClicked = true;
this.onJoinButtonClicked.emit(); this.onJoinButtonClicked.emit();
} }
/**
* @internal
*/
onLeaveButtonClicked() { onLeaveButtonClicked() {
this.joinSessionClicked = false; this.joinSessionClicked = false;
this.participantReady = false; this.participantReady = false;
this.onToolbarLeaveButtonClicked.emit(); this.onToolbarLeaveButtonClicked.emit();
} }
/**
* @internal
*/
onCameraButtonClicked() { onCameraButtonClicked() {
this.onToolbarCameraButtonClicked.emit(); this.onToolbarCameraButtonClicked.emit();
} }
/**
* @internal
*/
onMicrophoneButtonClicked() { onMicrophoneButtonClicked() {
this.onToolbarMicrophoneButtonClicked.emit(); this.onToolbarMicrophoneButtonClicked.emit();
} }
/**
* @internal
*/
onScreenshareButtonClicked() { onScreenshareButtonClicked() {
this.onToolbarScreenshareButtonClicked.emit(); this.onToolbarScreenshareButtonClicked.emit();
} }
/**
* @internal
*/
onFullscreenButtonClicked() { onFullscreenButtonClicked() {
this.onToolbarFullscreenButtonClicked.emit(); this.onToolbarFullscreenButtonClicked.emit();
} }
/**
* @internal
*/
onParticipantsPanelButtonClicked() { onParticipantsPanelButtonClicked() {
this.onToolbarParticipantsPanelButtonClicked.emit(); this.onToolbarParticipantsPanelButtonClicked.emit();
} }
/**
* @internal
*/
onChatPanelButtonClicked() { onChatPanelButtonClicked() {
this.onToolbarChatPanelButtonClicked.emit(); this.onToolbarChatPanelButtonClicked.emit();
} }
/**
* @internal
*/
_onSessionCreated(event: Session) { _onSessionCreated(event: Session) {
this.onSessionCreated.emit(event); this.onSessionCreated.emit(event);
} }

View File

@ -23,6 +23,7 @@ import {
ParticipantNameDirective ParticipantNameDirective
} from './videoconference.directive'; } from './videoconference.directive';
@NgModule({ @NgModule({
declarations: [ declarations: [
MinimalDirective, MinimalDirective,

View File

@ -1,6 +1,21 @@
import { Directive, AfterViewInit, OnDestroy, Input, ElementRef } from '@angular/core'; import { Directive, AfterViewInit, OnDestroy, Input, ElementRef } from '@angular/core';
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
/**
* The **muteButton** directive allows show/hide the muted button in participant panel item component.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `participantPanelItem` component:
*
* @example
* <ov-videoconference [participantPanelItemMuteButton]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ParticipantPanelItemComponent}.
* @example
* <ov-participant-panel-item [muteButton]="false"></ov-participant-panel-item>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[participantPanelItemMuteButton], ov-participant-panel-item[muteButton]' selector: 'ov-videoconference[participantPanelItemMuteButton], ov-participant-panel-item[muteButton]'
}) })

View File

@ -1,6 +1,21 @@
import { AfterViewInit, Directive, ElementRef, Input, OnDestroy } from '@angular/core'; import { AfterViewInit, Directive, ElementRef, Input, OnDestroy } from '@angular/core';
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
/**
* The **displayParticipantName** directive allows show/hide the participants name in stream component.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `stream` component:
*
* @example
* <ov-videoconference [streamDisplayParticipantName]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link StreamComponent}.
* @example
* <ov-stream [displayParticipantName]="false"></ov-stream>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[streamDisplayParticipantName], ov-stream[displayParticipantName]' selector: 'ov-videoconference[streamDisplayParticipantName], ov-stream[displayParticipantName]'
}) })
@ -37,6 +52,21 @@ export class StreamDisplayParticipantNameDirective implements AfterViewInit, OnD
} }
} }
/**
* The **displayAudioDetection** directive allows show/hide the participants audio detection in stream component.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `stream` component:
*
* @example
* <ov-videoconference [streamDisplayAudioDetection]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link StreamComponent}.
* @example
* <ov-stream [displayAudioDetection]="false"></ov-stream>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[streamDisplayAudioDetection], ov-stream[displayAudioDetection]' selector: 'ov-videoconference[streamDisplayAudioDetection], ov-stream[displayAudioDetection]'
}) })
@ -71,6 +101,21 @@ export class StreamDisplayAudioDetectionDirective implements AfterViewInit, OnDe
} }
} }
/**
* The **settingsButton** directive allows show/hide the participants settings button in stream component.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `stream` component:
*
* @example
* <ov-videoconference [streamSettingsButton]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link StreamComponent}.
* @example
* <ov-stream [settingsButton]="false"></ov-stream>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[streamSettingsButton], ov-stream[settingsButton]' selector: 'ov-videoconference[streamSettingsButton], ov-stream[settingsButton]'
}) })

View File

@ -1,21 +1,46 @@
import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnDestroy } from '@angular/core'; import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnDestroy } from '@angular/core';
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
/**
* The **screenshareButton** directive allows show/hide the screenshare toolbar button.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
*
* @example
* <ov-videoconference [toolbarScreenshareButton]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ToolbarComponent}.
* @example
* <ov-toolbar [screenshareButton]="false"></ov-toolbar>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[toolbarScreenshareButton], ov-toolbar[screenshareButton]' selector: 'ov-videoconference[toolbarScreenshareButton], ov-toolbar[screenshareButton]'
}) })
export class ToolbarScreenshareButtonDirective implements AfterViewInit, OnDestroy { export class ToolbarScreenshareButtonDirective implements AfterViewInit, OnDestroy {
/**
* @ignore
*/
@Input() set toolbarScreenshareButton(value: boolean) { @Input() set toolbarScreenshareButton(value: boolean) {
this.screenshareValue = value; this.screenshareValue = value;
this.update(this.screenshareValue); this.update(this.screenshareValue);
} }
/**
* @ignore
*/
@Input() set screenshareButton(value: boolean) { @Input() set screenshareButton(value: boolean) {
this.screenshareValue = value; this.screenshareValue = value;
this.update(this.screenshareValue); this.update(this.screenshareValue);
} }
screenshareValue: boolean = true; private screenshareValue: boolean = true;
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngAfterViewInit() { ngAfterViewInit() {
@ -25,33 +50,58 @@ export class ToolbarScreenshareButtonDirective implements AfterViewInit, OnDestr
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
clear() {
private clear() {
this.screenshareValue = true; this.screenshareValue = true;
this.update(true); this.update(true);
} }
update(value: boolean) { private update(value: boolean) {
if (this.libService.screenshareButton.getValue() !== value) { if (this.libService.screenshareButton.getValue() !== value) {
this.libService.screenshareButton.next(value); this.libService.screenshareButton.next(value);
} }
} }
} }
/**
* The **fullscreenButton** directive allows show/hide the fullscreen toolbar button.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
*
* @example
* <ov-videoconference [toolbarFullscreenButton]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ToolbarComponent}.
* @example
* <ov-toolbar [fullscreenButton]="false"></ov-toolbar>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[toolbarFullscreenButton], ov-toolbar[fullscreenButton]' selector: 'ov-videoconference[toolbarFullscreenButton], ov-toolbar[fullscreenButton]'
}) })
export class ToolbarFullscreenButtonDirective implements AfterViewInit, OnDestroy { export class ToolbarFullscreenButtonDirective implements AfterViewInit, OnDestroy {
/**
* @ignore
*/
@Input() set toolbarFullscreenButton(value: boolean) { @Input() set toolbarFullscreenButton(value: boolean) {
this.fullscreenValue = value; this.fullscreenValue = value;
this.update(this.fullscreenValue); this.update(this.fullscreenValue);
} }
/**
* @ignore
*/
@Input() set fullscreenButton(value: boolean) { @Input() set fullscreenButton(value: boolean) {
this.fullscreenValue = value; this.fullscreenValue = value;
this.update(this.fullscreenValue); this.update(this.fullscreenValue);
} }
fullscreenValue: boolean = true; private fullscreenValue: boolean = true;
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngAfterViewInit() { ngAfterViewInit() {
@ -60,33 +110,57 @@ export class ToolbarFullscreenButtonDirective implements AfterViewInit, OnDestro
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
clear() { private clear() {
this.fullscreenValue = true; this.fullscreenValue = true;
this.update(true); this.update(true);
} }
update(value: boolean) { private update(value: boolean) {
if (this.libService.fullscreenButton.getValue() !== value) { if (this.libService.fullscreenButton.getValue() !== value) {
this.libService.fullscreenButton.next(value); this.libService.fullscreenButton.next(value);
} }
} }
} }
/**
* The **leaveButton** directive allows show/hide the leave toolbar button.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
*
* @example
* <ov-videoconference [toolbarLeaveButton]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ToolbarComponent}.
* @example
* <ov-toolbar [leaveButton]="false"></ov-toolbar>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[toolbarLeaveButton], ov-toolbar[leaveButton]' selector: 'ov-videoconference[toolbarLeaveButton], ov-toolbar[leaveButton]'
}) })
export class ToolbarLeaveButtonDirective implements AfterViewInit, OnDestroy { export class ToolbarLeaveButtonDirective implements AfterViewInit, OnDestroy {
/**
* @ignore
*/
@Input() set toolbarLeaveButton(value: boolean) { @Input() set toolbarLeaveButton(value: boolean) {
this.leaveValue = value; this.leaveValue = value;
this.update(this.leaveValue); this.update(this.leaveValue);
} }
/**
* @ignore
*/
@Input() set leaveButton(value: boolean) { @Input() set leaveButton(value: boolean) {
this.leaveValue = value; this.leaveValue = value;
this.update(this.leaveValue); this.update(this.leaveValue);
} }
leaveValue: boolean = true; private leaveValue: boolean = true;
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngAfterViewInit() { ngAfterViewInit() {
@ -96,33 +170,58 @@ export class ToolbarLeaveButtonDirective implements AfterViewInit, OnDestroy {
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
clear() { private clear() {
this.leaveValue = true; this.leaveValue = true;
this.update(true); this.update(true);
} }
update(value: boolean) { private update(value: boolean) {
if (this.libService.leaveButton.getValue() !== value) { if (this.libService.leaveButton.getValue() !== value) {
this.libService.leaveButton.next(value); this.libService.leaveButton.next(value);
} }
} }
} }
/**
* The **participantsPanelButton** directive allows show/hide the participants panel toolbar button.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
*
* @example
* <ov-videoconference [toolbarParticipantsPanelButton]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ToolbarComponent}.
* @example
* <ov-toolbar [participantsPanelButton]="false"></ov-toolbar>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[toolbarParticipantsPanelButton], ov-toolbar[participantsPanelButton]' selector: 'ov-videoconference[toolbarParticipantsPanelButton], ov-toolbar[participantsPanelButton]'
}) })
export class ToolbarParticipantsPanelButtonDirective implements AfterViewInit, OnDestroy { export class ToolbarParticipantsPanelButtonDirective implements AfterViewInit, OnDestroy {
/**
* @ignore
*/
@Input() set toolbarParticipantsPanelButton(value: boolean) { @Input() set toolbarParticipantsPanelButton(value: boolean) {
this.participantsPanelValue = value; this.participantsPanelValue = value;
this.update(this.participantsPanelValue); this.update(this.participantsPanelValue);
} }
/**
* @ignore
*/
@Input() set participantsPanelButton(value: boolean) { @Input() set participantsPanelButton(value: boolean) {
this.participantsPanelValue = value; this.participantsPanelValue = value;
this.update(this.participantsPanelValue); this.update(this.participantsPanelValue);
} }
participantsPanelValue: boolean = true; private participantsPanelValue: boolean = true;
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngAfterViewInit() { ngAfterViewInit() {
@ -132,32 +231,56 @@ export class ToolbarParticipantsPanelButtonDirective implements AfterViewInit, O
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
clear() { private clear() {
this.participantsPanelValue = true; this.participantsPanelValue = true;
this.update(true); this.update(true);
} }
update(value: boolean) { private update(value: boolean) {
if (this.libService.participantsPanelButton.getValue() !== value) { if (this.libService.participantsPanelButton.getValue() !== value) {
this.libService.participantsPanelButton.next(value); this.libService.participantsPanelButton.next(value);
} }
} }
} }
/**
* The **chatPanelButton** directive allows show/hide the chat panel toolbar button.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
*
* @example
* <ov-videoconference [toolbarChatPanelButton]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ToolbarComponent}.
* @example
* <ov-toolbar [chatPanelButton]="false"></ov-toolbar>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[toolbarChatPanelButton], ov-toolbar[chatPanelButton]' selector: 'ov-videoconference[toolbarChatPanelButton], ov-toolbar[chatPanelButton]'
}) })
export class ToolbarChatPanelButtonDirective implements AfterViewInit, OnDestroy { export class ToolbarChatPanelButtonDirective implements AfterViewInit, OnDestroy {
/**
* @ignore
*/
@Input() set toolbarChatPanelButton(value: boolean) { @Input() set toolbarChatPanelButton(value: boolean) {
this.toolbarChatPanelValue = value; this.toolbarChatPanelValue = value;
this.update(this.toolbarChatPanelValue); this.update(this.toolbarChatPanelValue);
} }
/**
* @ignore
*/
@Input() set chatPanelButton(value: boolean) { @Input() set chatPanelButton(value: boolean) {
this.toolbarChatPanelValue = value; this.toolbarChatPanelValue = value;
this.update(this.toolbarChatPanelValue); this.update(this.toolbarChatPanelValue);
} }
toolbarChatPanelValue: boolean = true; private toolbarChatPanelValue: boolean = true;
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngAfterViewInit() { ngAfterViewInit() {
@ -167,33 +290,57 @@ export class ToolbarChatPanelButtonDirective implements AfterViewInit, OnDestroy
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
clear() { private clear() {
this.toolbarChatPanelValue = true; this.toolbarChatPanelValue = true;
this.update(true); this.update(true);
} }
update(value: boolean) { private update(value: boolean) {
if (this.libService.chatPanelButton.getValue() !== value) { if (this.libService.chatPanelButton.getValue() !== value) {
this.libService.chatPanelButton.next(value); this.libService.chatPanelButton.next(value);
} }
} }
} }
/**
* The **displaySessionName** directive allows show/hide the session name.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
*
* @example
* <ov-videoconference [toolbarDisplaySessionName]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ToolbarComponent}.
* @example
* <ov-toolbar [displaySessionName]="false"></ov-toolbar>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[toolbarDisplaySessionName], ov-toolbar[displaySessionName]' selector: 'ov-videoconference[toolbarDisplaySessionName], ov-toolbar[displaySessionName]'
}) })
export class ToolbarDisplaySessionNameDirective implements AfterViewInit, OnDestroy { export class ToolbarDisplaySessionNameDirective implements AfterViewInit, OnDestroy {
/**
* @ignore
*/
@Input() set toolbarDisplaySessionName(value: boolean) { @Input() set toolbarDisplaySessionName(value: boolean) {
this.displaySessionValue = value; this.displaySessionValue = value;
this.update(this.displaySessionValue); this.update(this.displaySessionValue);
} }
/**
* @ignore
*/
@Input() set displaySessionName(value: boolean) { @Input() set displaySessionName(value: boolean) {
this.displaySessionValue = value; this.displaySessionValue = value;
this.update(this.displaySessionValue); this.update(this.displaySessionValue);
} }
displaySessionValue: boolean = true; private displaySessionValue: boolean = true;
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngAfterViewInit() { ngAfterViewInit() {
@ -203,33 +350,57 @@ export class ToolbarDisplaySessionNameDirective implements AfterViewInit, OnDest
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
clear() { private clear() {
this.displaySessionValue = true; this.displaySessionValue = true;
this.update(true); this.update(true);
} }
update(value: boolean) { private update(value: boolean) {
if (this.libService.displaySessionName.getValue() !== value) { if (this.libService.displaySessionName.getValue() !== value) {
this.libService.displaySessionName.next(value); this.libService.displaySessionName.next(value);
} }
} }
} }
/**
* The **displayLogo** directive allows show/hide the branding logo.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
*
* @example
* <ov-videoconference [toolbarDisplayLogo]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ToolbarComponent}.
* @example
* <ov-toolbar [displayLogo]="false"></ov-toolbar>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[toolbarDisplayLogo], ov-toolbar[displayLogo]' selector: 'ov-videoconference[toolbarDisplayLogo], ov-toolbar[displayLogo]'
}) })
export class ToolbarDisplayLogoDirective implements AfterViewInit, OnDestroy { export class ToolbarDisplayLogoDirective implements AfterViewInit, OnDestroy {
/**
* @ignore
*/
@Input() set toolbarDisplayLogo(value: boolean) { @Input() set toolbarDisplayLogo(value: boolean) {
this.displayLogoValue = value; this.displayLogoValue = value;
this.update(this.displayLogoValue); this.update(this.displayLogoValue);
} }
/**
* @ignore
*/
@Input() set displayLogo(value: boolean) { @Input() set displayLogo(value: boolean) {
this.displayLogoValue = value; this.displayLogoValue = value;
this.update(this.displayLogoValue); this.update(this.displayLogoValue);
} }
displayLogoValue: boolean = true; private displayLogoValue: boolean = true;
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngAfterViewInit() { ngAfterViewInit() {
@ -239,12 +410,12 @@ export class ToolbarDisplayLogoDirective implements AfterViewInit, OnDestroy {
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
clear() { private clear() {
this.displayLogoValue = true; this.displayLogoValue = true;
this.update(true); this.update(true);
} }
update(value: boolean) { private update(value: boolean) {
if (this.libService.displayLogo.getValue() !== value) { if (this.libService.displayLogo.getValue() !== value) {
this.libService.displayLogo.next(value); this.libService.displayLogo.next(value);
} }
@ -253,7 +424,10 @@ export class ToolbarDisplayLogoDirective implements AfterViewInit, OnDestroy {
// * Private directives * // * Private directives *
// Load default OpenVidu logo if custom one is not exist /**
* Load default OpenVidu logo if custom one is not exist
* @internal
*/
@Directive({ @Directive({
selector: 'img[ovLogo]' selector: 'img[ovLogo]'
}) })

View File

@ -1,21 +1,49 @@
import { Directive, Input, ElementRef, OnDestroy, OnInit } from '@angular/core'; import { Directive, Input, ElementRef, OnDestroy, OnInit } from '@angular/core';
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
/**
* The **minimal** directive applies a minimal UI hiding all controls except for cam and mic.
*
* It is only available for {@link VideoconferenceComponent}.
*
* Default: `false`
*
* @example
* <ov-videoconference [minimal]="true"></ov-videoconference>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[minimal]' selector: 'ov-videoconference[minimal]'
}) })
export class MinimalDirective implements OnDestroy { export class MinimalDirective implements OnDestroy {
/**
* @ignore
*/
@Input() set minimal(value: boolean) { @Input() set minimal(value: boolean) {
this.update(value); this.update(value);
} }
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
/**
* @ignore
*/
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
/**
* @ignore
*/
clear() { clear() {
this.update(false); this.update(false);
} }
/**
* @ignore
*/
update(value: boolean) { update(value: boolean) {
if (this.libService.minimal.getValue() !== value) { if (this.libService.minimal.getValue() !== value) {
this.libService.minimal.next(value); this.libService.minimal.next(value);
@ -23,44 +51,102 @@ export class MinimalDirective implements OnDestroy {
} }
} }
/**
* The **participantName** directive sets the participant name. It can be useful for aplications which doesn't need the prejoin page or applications which uses the **openvidu-webcomponent**
*
* It is only available for {@link VideoconferenceComponent}.
*
* @example
* <ov-videoconference [participantName]="'OpenVidu'"></ov-videoconference>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[participantName]' selector: 'ov-videoconference[participantName]'
}) })
export class ParticipantNameDirective implements OnInit { export class ParticipantNameDirective implements OnInit {
// Avoiding update participantName dynamically. // Avoiding update participantName dynamically.
// The participantName must be updated from UI // The participantName must be updated from UI
/**
* @ignore
*/
@Input() participantName: string; @Input() participantName: string;
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
/**
* @ignore
*/
ngOnInit(): void { ngOnInit(): void {
this.update(this.participantName); this.update(this.participantName);
} }
/**
* @ignore
*/
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
/**
* @ignore
*/
clear() { clear() {
this.update(''); this.update('');
} }
/**
* @ignore
*/
update(value: string) { update(value: string) {
this.libService.participantName.next(value); this.libService.participantName.next(value);
} }
} }
/**
* The **prejoin** directive allows show/hide the prejoin page for selecting media devices.
*
* It is only available for {@link VideoconferenceComponent}.
*
* Default: `true`
*
* @example
* <ov-videoconference [prejoin]="false"></ov-videoconference>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[prejoin]' selector: 'ov-videoconference[prejoin]'
}) })
export class PrejoinDirective implements OnDestroy { export class PrejoinDirective implements OnDestroy {
/**
* @ignore
*/
@Input() set prejoin(value: boolean) { @Input() set prejoin(value: boolean) {
this.update(value); this.update(value);
} }
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
/**
* @ignore
*/
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
/**
* @ignore
*/
clear() { clear() {
this.update(true); this.update(true);
} }
/**
* @ignore
*/
update(value: boolean) { update(value: boolean) {
if (this.libService.prejoin.getValue() !== value) { if (this.libService.prejoin.getValue() !== value) {
this.libService.prejoin.next(value); this.libService.prejoin.next(value);
@ -68,20 +154,50 @@ export class PrejoinDirective implements OnDestroy {
} }
} }
/**
* The **videoMuted** directive allows to join the session with camera muted/unmuted.
*
* It is only available for {@link VideoconferenceComponent}.
*
* Default: `false`
*
*
* @example
* <ov-videoconference [videoMuted]="true"></ov-videoconference>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[videoMuted]' selector: 'ov-videoconference[videoMuted]'
}) })
export class VideoMutedDirective implements OnDestroy { export class VideoMutedDirective implements OnDestroy {
/**
* @ignore
*/
@Input() set videoMuted(value: boolean) { @Input() set videoMuted(value: boolean) {
this.update(value); this.update(value);
} }
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
/**
* @ignore
*/
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
/**
* @ignore
*/
clear() { clear() {
this.update(false); this.update(false);
} }
/**
* @ignore
*/
update(value: boolean) { update(value: boolean) {
if (this.libService.videoMuted.getValue() !== value) { if (this.libService.videoMuted.getValue() !== value) {
this.libService.videoMuted.next(value); this.libService.videoMuted.next(value);
@ -89,21 +205,48 @@ export class VideoMutedDirective implements OnDestroy {
} }
} }
/**
* The **audioMuted** directive allows to join the session with microphone muted/unmuted.
*
* It is only available for {@link VideoconferenceComponent}.
*
* Default: `false`
*
* @example
* <ov-videoconference [audioMuted]="true"></ov-videoconference>
*/
@Directive({ @Directive({
selector: 'ov-videoconference[audioMuted]' selector: 'ov-videoconference[audioMuted]'
}) })
export class AudioMutedDirective implements OnDestroy { export class AudioMutedDirective implements OnDestroy {
/**
* @ignore
*/
@Input() set audioMuted(value: boolean) { @Input() set audioMuted(value: boolean) {
this.update(value); this.update(value);
} }
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {} constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngOnDestroy(): void { ngOnDestroy(): void {
this.clear(); this.clear();
} }
/**
* @ignore
*/
clear() { clear() {
this.update(false); this.update(false);
} }
/**
* @ignore
*/
update(value: boolean) { update(value: boolean) {
if (this.libService.audioMuted.getValue() !== value) { if (this.libService.audioMuted.getValue() !== value) {
this.libService.audioMuted.next(value); this.libService.audioMuted.next(value);

View File

@ -1,19 +1,71 @@
import { Directive, TemplateRef, ViewContainerRef } from '@angular/core'; import { Directive, TemplateRef, ViewContainerRef } from '@angular/core';
/**
* The ***ovToolbar** directive allows to replace the default toolbar component injecting your custom template.
*
* @example
* <my-custom-toolbar *ovToolbar></my-custom-toolbar>
*
*/
@Directive({ @Directive({
selector: '[ovToolbar]' selector: '[ovToolbar]'
}) })
export class ToolbarDirective { export class ToolbarDirective {
/**
* @ignore
*/
constructor(public template: TemplateRef<any>, public viewContainer: ViewContainerRef) {} constructor(public template: TemplateRef<any>, public viewContainer: ViewContainerRef) {}
} }
/**
* The ***ovToolbarAdditionalButtons** directive allows to add additional buttons to the toolbar.
* There are two ways to use this directive:
*
* 1. Adding it to an element as a child of the parent element **_ov-videoconference_** {@link VideoconferenceComponent}
* @example
* <ov-videoconference>
* <div *ovToolbarAdditionalButtons>
* <button>Additional button</button>
* <button>Click Me</button>
* </div>
* </ov-videoconference>
*
* <br>
* 2. Adding it to an element as child of the element tagged with the {@link ToolbarDirective}
* @example
* <ov-videoconference>
* <my-toolbar *ovToolbar>
* <div *ovToolbarAdditionalButtons>
* <button>Additional button</button>
* <button>Click Me</button>
* </div>
* </my-toolbar>
* </ov-videoconference>
*
*/
@Directive({ @Directive({
selector: '[ovToolbarAdditionalButtons]' selector: '[ovToolbarAdditionalButtons]'
}) })
export class ToolbarAdditionalButtonsDirective { export class ToolbarAdditionalButtonsDirective {
/**
* @ignore
*/
constructor(public template: TemplateRef<any>, public viewContainer: ViewContainerRef) {} constructor(public template: TemplateRef<any>, public viewContainer: ViewContainerRef) {}
} }
/**
* The ***ovPanel** directive allows to replace the default panel component injecting your custom template.
*
* This directive is closely related to {@link ChatPanelDirective} and {@link ParticipantsPanelDirective}.
*
*
* @example
* <my-custom-panel *ovPanel>
* ...
* </my-custom-panel>
*
*/
@Directive({ @Directive({
selector: '[ovPanel]' selector: '[ovPanel]'
}) })
@ -21,6 +73,32 @@ export class PanelDirective {
constructor(public template: TemplateRef<any>, public viewContainer: ViewContainerRef) {} constructor(public template: TemplateRef<any>, public viewContainer: ViewContainerRef) {}
} }
/**
* The ***ovChatPanel** directive allows to replace the defaultchat panel template injecting your own component.
* There are two ways to use this directive:
*
* 1. Adding it to an element as a child of the parent element **_ov-videoconference_** {@link VideoconferenceComponent}
* @example
* <ov-videoconference>
* <my-chat-panel *ovChatPanel></my-chat-panel>
* </ov-videoconference>
*
* <br>
* 2. Adding it to an element as child of the element tagged with the {@link ToolbarDirective}
* @example
* <ov-videoconference>
* <my-panel *ovPanel>
* <my-chat-panel *ovChatPanel></my-chat-panel>
* </my-panel>
* </ov-videoconference>
*
* <div class="info-container">
* <span>INFO:</span>
* You also can use the default components adsada dasda d asd
* </div>
*
*/
@Directive({ @Directive({
selector: '[ovChatPanel]' selector: '[ovChatPanel]'
}) })
@ -56,10 +134,37 @@ export class LayoutDirective {
constructor(public template: TemplateRef<any>, public container: ViewContainerRef) {} constructor(public template: TemplateRef<any>, public container: ViewContainerRef) {}
} }
/**
* The ***ovStream** directive allows to replace the default stream component template injecting your own component.
* There are two ways to use this directive:
*
* 1. Adding it to an element as a child of the parent element **_ov-videoconference_** {@link VideoconferenceComponent}
* @example
* <ov-videoconference>
*
*
* <my-chat-panel *ovChatPanel></my-chat-panel>
* </ov-videoconference>
*
* <br>
* 2. Adding it to an element as child of the element tagged with the {@link ToolbarDirective}
* @example
* <ov-videoconference>
* <my-panel *ovPanel>
* <my-chat-panel *ovChatPanel></my-chat-panel>
* </my-panel>
* </ov-videoconference>
*
* <div class="info-container">
* <span>INFO:</span>
* You also can use the default components adsada dasda d asd
* </div>
*
*/
@Directive({ @Directive({
selector: '[ovStream]' selector: '[ovStream]'
}) })
export class StreamDirective { export class StreamDirective {
constructor(public template: TemplateRef<any>, public container: ViewContainerRef) {} constructor(public template: TemplateRef<any>, public container: ViewContainerRef) {}
} }

View File

@ -2,6 +2,9 @@ import { FormControl, FormGroupDirective, NgForm } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
/** Error when invalid control is dirty, touched, or submitted. */ /** Error when invalid control is dirty, touched, or submitted. */
/**
* @internal
*/
export class NicknameMatcher implements ErrorStateMatcher { export class NicknameMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted = form && form.submitted; const isSubmitted = form && form.submitted;

View File

@ -1,8 +1,6 @@
export enum AvatarType { /**
DEFAULT = 'default', * @internal
CAPTURED = 'captured' */
}
export interface ChatMessage { export interface ChatMessage {
isLocal: boolean; isLocal: boolean;
nickname: string; nickname: string;

View File

@ -1,15 +1,24 @@
/**
* @internal
*/
export interface CustomDevice { export interface CustomDevice {
label: string; label: string;
device: string; device: string;
type?: CameraType; type?: CameraType;
} }
/**
* @internal
*/
export enum CameraType { export enum CameraType {
FRONT = 'FRONT', FRONT = 'FRONT',
BACK = 'BACK' BACK = 'BACK'
} }
/**
* @internal
*/
export enum DeviceType { export enum DeviceType {
AUDIO_INPUT = 'audioinput', AUDIO_INPUT = 'audioinput',
VIDEO_INPUT = 'videoinput' VIDEO_INPUT = 'videoinput'
} }

View File

@ -1,3 +1,6 @@
/**
* @internal
*/
export enum VideoSizeIcon { export enum VideoSizeIcon {
BIG = 'zoom_in', BIG = 'zoom_in',
NORMAL = 'zoom_out' NORMAL = 'zoom_out'

View File

@ -1,3 +1,6 @@
/**
* @internal
*/
export enum LayoutClass { export enum LayoutClass {
ROOT_ELEMENT = 'OT_root', ROOT_ELEMENT = 'OT_root',
BIG_ELEMENT = 'OV_big', BIG_ELEMENT = 'OV_big',
@ -6,15 +9,17 @@ export enum LayoutClass {
NO_SIZE_ELEMENT = 'no-size' NO_SIZE_ELEMENT = 'no-size'
} }
/**
* @internal
*/
export enum SidenavMode { export enum SidenavMode {
OVER = 'over', OVER = 'over',
SIDE = 'side' SIDE = 'side'
} }
/** /**
* @hidden * @internal
*/ */
export interface OpenViduLayoutOptions { export interface OpenViduLayoutOptions {
/** /**
* The narrowest ratio that will be used (*2x3* by default) * The narrowest ratio that will be used (*2x3* by default)
@ -70,6 +75,9 @@ export interface OpenViduLayoutOptions {
bigFirst: any; bigFirst: any;
} }
/**
* @internal
*/
export class OpenViduLayout { export class OpenViduLayout {
/** /**
* @hidden * @hidden

View File

@ -1,5 +1,8 @@
import { Autolinker, AutolinkerConfig, HashtagMatch } from 'autolinker'; import { Autolinker, AutolinkerConfig, HashtagMatch } from 'autolinker';
/**
* @internal
*/
const AUTOLINKER_CFGS: AutolinkerConfig = { const AUTOLINKER_CFGS: AutolinkerConfig = {
urls: { urls: {
schemeMatches: true, schemeMatches: true,
@ -20,6 +23,9 @@ const AUTOLINKER_CFGS: AutolinkerConfig = {
decodePercentEncoding: true decodePercentEncoding: true
}; };
/**
* @internal
*/
export class Linkifier { export class Linkifier {
private autolinker: Autolinker; private autolinker: Autolinker;

View File

@ -1,9 +1,15 @@
/**
* @internal
*/
export interface ILogger { export interface ILogger {
d(...args: any[]): void; d(...args: any[]): void;
w(...args: any[]): void; w(...args: any[]): void;
e(...args: any[]): void; e(...args: any[]): void;
} }
/**
* @internal
*/
export interface ILogService { export interface ILogService {
get(name: string): ILogger; get(name: string): ILogger;
} }

View File

@ -1,3 +1,6 @@
/**
* @internal
*/
export enum MenuType { export enum MenuType {
CHAT = 'chat', CHAT = 'chat',
PARTICIPANTS = 'participants' PARTICIPANTS = 'participants'

View File

@ -1,5 +1,8 @@
import { MatSnackBarConfig } from '@angular/material/snack-bar'; import { MatSnackBarConfig } from '@angular/material/snack-bar';
/**
* @internal
*/
export interface INotificationOptions { export interface INotificationOptions {
message: string; message: string;
buttonActionText?: string; buttonActionText?: string;

View File

@ -2,19 +2,52 @@ import { Publisher, StreamManager } from 'openvidu-browser';
import { VideoType } from './video-type.model'; import { VideoType } from './video-type.model';
export interface StreamModel { export interface StreamModel {
/**
* Whether the stream is available or not
*/
connected: boolean; connected: boolean;
/**
* The stream type.{@link VideoType}
*/
type: VideoType; type: VideoType;
/**
* The streamManager object from openvidu-browser library.{@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/StreamManager.html}
*/
streamManager: StreamManager; streamManager: StreamManager;
/**
* Whether the stream is enlarged or not
*/
videoEnlarged: boolean; videoEnlarged: boolean;
/**
* Unique identifier of the stream
*/
connectionId: string; connectionId: string;
participant?: ParticipantAbstractModel /**
* The participant object
*/
participant?: ParticipantAbstractModel;
} }
export interface ParticipantProperties { export interface ParticipantProperties {
/**
* Whether the participant is local or not
*/
local: boolean; local: boolean;
/**
* The participant nickname
*/
nickname: string; nickname: string;
/**
* Unique identifier of the participant
*/
id?: string; id?: string;
/**
* The participant color profile
*/
colorProfile?: string; colorProfile?: string;
/**
* Whether the participant is muted forcibly or not
*/
isMutedForcibly?: boolean; isMutedForcibly?: boolean;
} }
@ -30,7 +63,7 @@ export abstract class ParticipantAbstractModel {
this.id = props.id ? props.id : new Date().getTime().toString(); this.id = props.id ? props.id : new Date().getTime().toString();
this.local = props.local; this.local = props.local;
this.nickname = props.nickname; this.nickname = props.nickname;
this.colorProfile = !!props.colorProfile ? props.colorProfile : `hsl(${Math.random()*360}, 100%, 80%)`; this.colorProfile = !!props.colorProfile ? props.colorProfile : `hsl(${Math.random() * 360}, 100%, 80%)`;
this.isMutedForcibly = typeof props.isMutedForcibly === 'boolean' ? props.isMutedForcibly : false; this.isMutedForcibly = typeof props.isMutedForcibly === 'boolean' ? props.isMutedForcibly : false;
let streamModel: StreamModel = { let streamModel: StreamModel = {
connected: model ? model.connected : true, connected: model ? model.connected : true,
@ -43,43 +76,68 @@ export abstract class ParticipantAbstractModel {
this.streams.set(streamModel.type, streamModel); this.streams.set(streamModel.type, streamModel);
} }
/**
* @internal
*/
addConnection(streamModel: StreamModel) { addConnection(streamModel: StreamModel) {
streamModel.participant = this; streamModel.participant = this;
this.streams.set(streamModel.type, streamModel); this.streams.set(streamModel.type, streamModel);
} }
/**
* @internal
*/
public isCameraAudioActive(): boolean { public isCameraAudioActive(): boolean {
const cameraConnection = this.getCameraConnection(); const cameraConnection = this.getCameraConnection();
if(cameraConnection) { if (cameraConnection) {
return cameraConnection.connected && cameraConnection.streamManager?.stream?.audioActive; return cameraConnection.connected && cameraConnection.streamManager?.stream?.audioActive;
} }
return this.isScreenAudioActive();; return this.isScreenAudioActive();
} }
/**
* @internal
*/
public isCameraVideoActive(): boolean { public isCameraVideoActive(): boolean {
const cameraConnection = this.getCameraConnection(); const cameraConnection = this.getCameraConnection();
return cameraConnection?.connected && cameraConnection?.streamManager?.stream?.videoActive; return cameraConnection?.connected && cameraConnection?.streamManager?.stream?.videoActive;
} }
/**
* @internal
*/
isScreenAudioActive(): boolean { isScreenAudioActive(): boolean {
const screenConnection = this.getScreenConnection(); const screenConnection = this.getScreenConnection();
if(screenConnection){ if (screenConnection) {
return screenConnection?.connected && screenConnection?.streamManager?.stream?.audioActive; return screenConnection?.connected && screenConnection?.streamManager?.stream?.audioActive;
} }
return false; return false;
} }
/**
* @internal
*/
hasConnectionType(type: VideoType): boolean { hasConnectionType(type: VideoType): boolean {
return this.streams.has(type); return this.streams.has(type);
} }
/**
* @internal
*/
public getCameraConnection(): StreamModel { public getCameraConnection(): StreamModel {
return this.streams.get(VideoType.CAMERA); return this.streams.get(VideoType.CAMERA);
} }
/**
* @internal
*/
public getScreenConnection(): StreamModel { public getScreenConnection(): StreamModel {
return this.streams.get(VideoType.SCREEN); return this.streams.get(VideoType.SCREEN);
} }
/**
* @internal
*/
getConnectionTypesActive(): VideoType[] { getConnectionTypesActive(): VideoType[] {
let connType = []; let connType = [];
if (this.isCameraActive()) connType.push(VideoType.CAMERA); if (this.isCameraActive()) connType.push(VideoType.CAMERA);
@ -88,116 +146,168 @@ export abstract class ParticipantAbstractModel {
return connType; return connType;
} }
/**
* @internal
*/
setCameraConnectionId(connectionId: string) { setCameraConnectionId(connectionId: string) {
this.getCameraConnection().connectionId = connectionId; this.getCameraConnection().connectionId = connectionId;
} }
/**
* @internal
*/
setScreenConnectionId(connectionId: string) { setScreenConnectionId(connectionId: string) {
this.getScreenConnection().connectionId = connectionId; this.getScreenConnection().connectionId = connectionId;
} }
/**
* @internal
*/
removeConnection(connectionId: string): StreamModel { removeConnection(connectionId: string): StreamModel {
const removeStream = this.getConnectionById(connectionId); const removeStream = this.getConnectionById(connectionId);
this.streams.delete(removeStream.type); this.streams.delete(removeStream.type);
return removeStream; return removeStream;
} }
/**
* @internal
*/
hasConnectionId(connectionId: string): boolean { hasConnectionId(connectionId: string): boolean {
return Array.from(this.streams.values()).some((conn) => conn.connectionId === connectionId); return Array.from(this.streams.values()).some((conn) => conn.connectionId === connectionId);
} }
/**
* @internal
*/
getConnectionById(connectionId: string): StreamModel { getConnectionById(connectionId: string): StreamModel {
return Array.from(this.streams.values()).find((conn) => conn.connectionId === connectionId); return Array.from(this.streams.values()).find((conn) => conn.connectionId === connectionId);
} }
/**
* @internal
*/
getAvailableConnections(): StreamModel[] { getAvailableConnections(): StreamModel[] {
return Array.from(this.streams.values()).filter((conn) => conn.connected); return Array.from(this.streams.values()).filter((conn) => conn.connected);
} }
/**
* @internal
*/
isLocal(): boolean { isLocal(): boolean {
return this.local; return this.local;
// return Array.from(this.streams.values()).every((conn) => conn.local); // return Array.from(this.streams.values()).every((conn) => conn.local);
} }
/**
* @internal
*/
setNickname(nickname: string) { setNickname(nickname: string) {
this.nickname = nickname; this.nickname = nickname;
// this.streams.forEach((conn) => {
// if (conn.type === VideoType.CAMERA) {
// conn.nickname = nickname;
// } else {
// conn.nickname = `${nickname}_${conn.type}`;
// }
// });
} }
/**
* @internal
*/
getNickname() { getNickname() {
return this.nickname; return this.nickname;
} }
// getCameraNickname(): string { /**
// return this.getCameraConnection()?.nickname; * @internal
// } */
// getScreenNickname(): string {
// return this.getScreenConnection()?.nickname;
// }
setCameraPublisher(publisher: Publisher) { setCameraPublisher(publisher: Publisher) {
const cameraConnection = this.getCameraConnection(); const cameraConnection = this.getCameraConnection();
if (cameraConnection) cameraConnection.streamManager = publisher; if (cameraConnection) cameraConnection.streamManager = publisher;
} }
/**
* @internal
*/
setScreenPublisher(publisher: Publisher) { setScreenPublisher(publisher: Publisher) {
const screenConnection = this.getScreenConnection(); const screenConnection = this.getScreenConnection();
if (screenConnection) screenConnection.streamManager = publisher; if (screenConnection) screenConnection.streamManager = publisher;
} }
/**
* @internal
*/
setPublisher(connType: VideoType, publisher: StreamManager) { setPublisher(connType: VideoType, publisher: StreamManager) {
const connection = this.streams.get(connType); const connection = this.streams.get(connType);
if(connection) { if (connection) {
connection.streamManager = publisher; connection.streamManager = publisher;
} }
} }
/**
* @internal
*/
isCameraActive(): boolean { isCameraActive(): boolean {
return this.getCameraConnection()?.connected; return this.getCameraConnection()?.connected;
} }
/**
* @internal
*/
enableCamera() { enableCamera() {
const cameraConnection = this.getCameraConnection(); const cameraConnection = this.getCameraConnection();
if (cameraConnection) cameraConnection.connected = true; if (cameraConnection) cameraConnection.connected = true;
} }
/**
* @internal
*/
disableCamera() { disableCamera() {
const cameraConnection = this.getCameraConnection(); const cameraConnection = this.getCameraConnection();
if (cameraConnection) cameraConnection.connected = false; if (cameraConnection) cameraConnection.connected = false;
} }
/**
* @internal
*/
isScreenActive(): boolean { isScreenActive(): boolean {
return this.getScreenConnection()?.connected; return this.getScreenConnection()?.connected;
} }
/**
* @internal
*/
enableScreen() { enableScreen() {
const screenConnection = this.getScreenConnection(); const screenConnection = this.getScreenConnection();
if (screenConnection) screenConnection.connected = true; if (screenConnection) screenConnection.connected = true;
} }
/**
* @internal
*/
disableScreen() { disableScreen() {
const screenConnection = this.getScreenConnection(); const screenConnection = this.getScreenConnection();
if (screenConnection) screenConnection.connected = false; if (screenConnection) screenConnection.connected = false;
} }
/**
* @internal
*/
setAllVideoEnlarged(enlarged: boolean) { setAllVideoEnlarged(enlarged: boolean) {
this.streams.forEach((conn) => (conn.videoEnlarged = enlarged)); this.streams.forEach((conn) => (conn.videoEnlarged = enlarged));
} }
/**
* @internal
*/
setCameraEnlarged(enlarged: boolean) { setCameraEnlarged(enlarged: boolean) {
this.streams.get(VideoType.CAMERA).videoEnlarged = enlarged; this.streams.get(VideoType.CAMERA).videoEnlarged = enlarged;
} }
/**
* @internal
*/
setScreenEnlarged(enlarged: boolean) { setScreenEnlarged(enlarged: boolean) {
this.streams.get(VideoType.SCREEN).videoEnlarged = enlarged; this.streams.get(VideoType.SCREEN).videoEnlarged = enlarged;
} }
/**
* @internal
*/
toggleVideoEnlarged(connectionId: string) { toggleVideoEnlarged(connectionId: string) {
this.streams.forEach((conn) => { this.streams.forEach((conn) => {
if (conn.connectionId === connectionId) { if (conn.connectionId === connectionId) {
@ -206,17 +316,22 @@ export abstract class ParticipantAbstractModel {
}); });
} }
/**
* @internal
*/
someHasVideoEnlarged(): boolean { someHasVideoEnlarged(): boolean {
return Array.from(this.streams.values()).some((conn) => conn.videoEnlarged); return Array.from(this.streams.values()).some((conn) => conn.videoEnlarged);
} }
setMutedForcibly(muted: boolean){ /**
* @internal
*/
setMutedForcibly(muted: boolean) {
this.isMutedForcibly = muted; this.isMutedForcibly = muted;
} }
} }
export class ParticipantModel extends ParticipantAbstractModel { /**
* @internal
*/
} export class ParticipantModel extends ParticipantAbstractModel {}

View File

@ -1,3 +1,6 @@
/**
* @internal
*/
export enum Signal { export enum Signal {
NICKNAME_CHANGED = 'nicknameChanged', NICKNAME_CHANGED = 'nicknameChanged',
CHAT = 'chat' CHAT = 'chat'

View File

@ -1,3 +1,6 @@
/**
* @internal
*/
export enum Storage{ export enum Storage{
USER_NICKNAME = 'openviduCallNickname', USER_NICKNAME = 'openviduCallNickname',
VIDEO_DEVICE = 'openviduCallVideoDevice', VIDEO_DEVICE = 'openviduCallVideoDevice',

View File

@ -0,0 +1,7 @@
/**
* @internal
*/
export interface TokenModel {
webcam: string;
screen: string;
}

View File

@ -4,6 +4,9 @@ export enum VideoType {
CUSTOM = 'CUSTOM' CUSTOM = 'CUSTOM'
} }
/**
* @internal
*/
export enum ScreenType { export enum ScreenType {
WINDOW = 'window', WINDOW = 'window',
SCREEN = 'screen' SCREEN = 'screen'

View File

@ -1,13 +0,0 @@
import { ISettings } from './settings.model';
export interface ISessionConfig {
sessionName: string;
user: string;
tokens: string[];
ovSettings: ISettings;
}
export enum Theme {
DARK = 'dark',
LIGHT = 'light'
}

View File

@ -1,6 +1,9 @@
import { Pipe, PipeTransform } from '@angular/core'; import { Pipe, PipeTransform } from '@angular/core';
import { Linkifier } from '../models/linkifier.model'; import { Linkifier } from '../models/linkifier.model';
/**
* @internal
*/
@Pipe({ name: 'linkify' }) @Pipe({ name: 'linkify' })
export class LinkifyPipe implements PipeTransform { export class LinkifyPipe implements PipeTransform {
private linkifer: Linkifier; private linkifer: Linkifier;

View File

@ -1,6 +1,9 @@
import { Pipe, PipeTransform } from '@angular/core'; import { Pipe, PipeTransform } from '@angular/core';
import { StreamModel, ParticipantAbstractModel } from '../models/participant.model'; import { StreamModel, ParticipantAbstractModel } from '../models/participant.model';
/**
* @internal
*/
@Pipe({ name: 'streams' }) @Pipe({ name: 'streams' })
export class ParticipantStreamsPipe implements PipeTransform { export class ParticipantStreamsPipe implements PipeTransform {
constructor() {} constructor() {}
@ -21,6 +24,9 @@ export class ParticipantStreamsPipe implements PipeTransform {
} }
} }
/**
* @internal
*/
@Pipe({ name: 'streamTypesEnabled' }) @Pipe({ name: 'streamTypesEnabled' })
export class StreamTypesEnabledPipe implements PipeTransform { export class StreamTypesEnabledPipe implements PipeTransform {
constructor() {} constructor() {}

View File

@ -5,6 +5,9 @@ import { DialogTemplateComponent } from '../../components/material/dialog.compon
import { INotificationOptions } from '../../models/notification-options.model'; import { INotificationOptions } from '../../models/notification-options.model';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -1,5 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import {CdkOverlayContainer } from '../../config/custom-cdk-overlay'; import {CdkOverlayContainer } from '../../config/custom-cdk-overlay';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -14,6 +14,9 @@ import { SidenavMenuService } from '../sidenav-menu/sidenav-menu.service';
import { ParticipantService } from '../participant/participant.service'; import { ParticipantService } from '../participant/participant.service';
import { MenuType } from '../../models/menu.model'; import { MenuType } from '../../models/menu.model';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -4,7 +4,9 @@ import { OpenViduAngularConfig, ParticipantFactoryFunction } from '../../config/
// import { version } from '../../../../package.json'; // import { version } from '../../../../package.json';
/**
* @internal
*/
@Injectable() @Injectable()
export class OpenViduAngularConfigService { export class OpenViduAngularConfigService {
private configuration: OpenViduAngularConfig; private configuration: OpenViduAngularConfig;
@ -51,7 +53,7 @@ export class OpenViduAngularConfigService {
constructor(@Inject('OPENVIDU_ANGULAR_CONFIG') config: OpenViduAngularConfig) { constructor(@Inject('OPENVIDU_ANGULAR_CONFIG') config: OpenViduAngularConfig) {
this.configuration = config; this.configuration = config;
console.log(this.configuration); console.log(this.configuration);
if(this.isProduction()) console.log('OpenVidu Angular Production Mode'); if (this.isProduction()) console.log('OpenVidu Angular Production Mode');
this.minimalObs = this.minimal.asObservable(); this.minimalObs = this.minimal.asObservable();
this.participantNameObs = this.participantName.asObservable(); this.participantNameObs = this.participantName.asObservable();
this.prejoinObs = this.prejoin.asObservable(); this.prejoinObs = this.prejoin.asObservable();
@ -85,7 +87,7 @@ export class OpenViduAngularConfigService {
// } // }
hasParticipantFactory(): boolean { hasParticipantFactory(): boolean {
return typeof this.getConfig().participantFactory === "function"; return typeof this.getConfig().participantFactory === 'function';
} }
getParticipantFactory(): ParticipantFactoryFunction { getParticipantFactory(): ParticipantFactoryFunction {

View File

@ -1,13 +1,13 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CameraType, IDevice } from '../../models/device.model'; import { CameraType, CustomDevice } from '../../models/device.model';
@Injectable() @Injectable()
export class DeviceServiceMock { export class DeviceServiceMock {
audioDevice: IDevice = { audioDevice: CustomDevice = {
label: 'audio', label: 'audio',
device: 'mockDevice' device: 'mockDevice'
}; };
videodevice: IDevice = { videodevice: CustomDevice = {
label: 'video', label: 'video',
device: 'mockDevice', device: 'mockDevice',
type: CameraType.FRONT type: CameraType.FRONT
@ -17,11 +17,11 @@ export class DeviceServiceMock {
async initDevices() {} async initDevices() {}
getCamSelected(): IDevice { getCamSelected(): CustomDevice {
return this.videodevice; return this.videodevice;
} }
getMicSelected(): IDevice { getMicSelected(): CustomDevice {
return this.audioDevice; return this.audioDevice;
} }
@ -37,11 +37,11 @@ export class DeviceServiceMock {
return false; return false;
} }
getCameras(): IDevice[] { getCameras(): CustomDevice[] {
return [this.videodevice]; return [this.videodevice];
} }
getMicrophones(): IDevice[] { getMicrophones(): CustomDevice[] {
return [this.audioDevice]; return [this.audioDevice];
} }

View File

@ -9,6 +9,9 @@ import { LoggerService } from '../logger/logger.service';
import { PlatformService } from '../platform/platform.service'; import { PlatformService } from '../platform/platform.service';
import { StorageService } from '../storage/storage.service'; import { StorageService } from '../storage/storage.service';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
@ -60,7 +63,7 @@ export class DeviceService {
this.microphones = customDevices.microphones; this.microphones = customDevices.microphones;
this._isVideoMuted = this.storageSrv.isVideoMuted() || this.libSrv.videoMuted.getValue(); this._isVideoMuted = this.storageSrv.isVideoMuted() || this.libSrv.videoMuted.getValue();
this._isAudioMuted = this.storageSrv.isAudioMuted() || this.libSrv.audioMuted.getValue();; this._isAudioMuted = this.storageSrv.isAudioMuted() || this.libSrv.audioMuted.getValue();
this.log.d('Media devices', customDevices); this.log.d('Media devices', customDevices);
} }

View File

@ -1,6 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { LayoutClass } from '../../models/layout.model'; import { LayoutClass } from '../../models/layout.model';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -3,6 +3,9 @@ import { BehaviorSubject, Observable } from 'rxjs';
import { LayoutClass, OpenViduLayout, OpenViduLayoutOptions } from '../../models/layout.model'; import { LayoutClass, OpenViduLayout, OpenViduLayoutOptions } from '../../models/layout.model';
import { DocumentService } from '../document/document.service'; import { DocumentService } from '../document/document.service';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -3,6 +3,9 @@ import { ILogService } from '../../models/logger.model';
import { OpenViduAngularConfigService } from '../config/openvidu-angular.config.service'; import { OpenViduAngularConfigService } from '../config/openvidu-angular.config.service';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -33,6 +33,9 @@ export class OpenViduService {
protected audioSource = undefined; protected audioSource = undefined;
protected log: ILogger; protected log: ILogger;
/**
* @internal
*/
constructor( constructor(
protected openviduAngularConfigSrv: OpenViduAngularConfigService, protected openviduAngularConfigSrv: OpenViduAngularConfigService,
protected platformService: PlatformService, protected platformService: PlatformService,
@ -43,6 +46,9 @@ export class OpenViduService {
this.log = this.loggerSrv.get('OpenViduService'); this.log = this.loggerSrv.get('OpenViduService');
} }
/**
* @internal
*/
initialize() { initialize() {
this.OV = new OpenVidu(); this.OV = new OpenVidu();
if (this.openviduAngularConfigSrv.isProduction()) this.OV.enableProdMode(); if (this.openviduAngularConfigSrv.isProduction()) this.OV.enableProdMode();
@ -60,22 +66,37 @@ export class OpenViduService {
return this.getWebcamSession(); return this.getWebcamSession();
} }
/**
* @internal
*/
getWebcamSession(): Session { getWebcamSession(): Session {
return this.webcamSession; return this.webcamSession;
} }
/**
* @internal
*/
isWebcamSessionConnected(): boolean { isWebcamSessionConnected(): boolean {
return !!this.webcamSession.capabilities; return !!this.webcamSession.capabilities;
} }
/**
* @internal
*/
getScreenSession(): Session { getScreenSession(): Session {
return this.screenSession; return this.screenSession;
} }
/**
* @internal
*/
isScreenSessionConnected(): boolean { isScreenSessionConnected(): boolean {
return !!this.screenSession.capabilities; return !!this.screenSession.capabilities;
} }
/**
* @internal
*/
async connectSession(session: Session, token: string): Promise<void> { async connectSession(session: Session, token: string): Promise<void> {
if (!!token && session) { if (!!token && session) {
const nickname = this.participantService.getMyNickname(); const nickname = this.participantService.getMyNickname();
@ -101,6 +122,9 @@ export class OpenViduService {
} }
} }
/**
* @internal
*/
disconnect() { disconnect() {
this.disconnectSession(this.webcamSession); this.disconnectSession(this.webcamSession);
this.disconnectSession(this.screenSession); this.disconnectSession(this.screenSession);
@ -111,6 +135,7 @@ export class OpenViduService {
} }
/** /**
* @internal
* Initialize a publisher checking devices saved on storage or if participant have devices available. * Initialize a publisher checking devices saved on storage or if participant have devices available.
*/ */
async initDefaultPublisher(targetElement: string | HTMLElement): Promise<Publisher> { async initDefaultPublisher(targetElement: string | HTMLElement): Promise<Publisher> {
@ -158,11 +183,17 @@ export class OpenViduService {
} }
} }
/**
* @internal
*/
async initPublisher(targetElement: string | HTMLElement, properties: PublisherProperties): Promise<Publisher> { async initPublisher(targetElement: string | HTMLElement, properties: PublisherProperties): Promise<Publisher> {
this.log.d('Initializing publisher with properties: ', properties); this.log.d('Initializing publisher with properties: ', properties);
return await this.OV.initPublisherAsync(targetElement, properties); return await this.OV.initPublisherAsync(targetElement, properties);
} }
/**
* @internal
*/
async publish(publisher: Publisher): Promise<void> { async publish(publisher: Publisher): Promise<void> {
if (!!publisher) { if (!!publisher) {
if (publisher === this.participantService.getMyCameraPublisher()) { if (publisher === this.participantService.getMyCameraPublisher()) {
@ -179,6 +210,9 @@ export class OpenViduService {
} }
} }
/**
* @internal
*/
unpublish(publisher: Publisher): void { unpublish(publisher: Publisher): void {
if (!!publisher) { if (!!publisher) {
if (publisher === this.participantService.getMyCameraPublisher()) { if (publisher === this.participantService.getMyCameraPublisher()) {
@ -190,6 +224,9 @@ export class OpenViduService {
} }
} }
/**
* @internal
*/
publishVideo(publisher: Publisher, value: boolean): void { publishVideo(publisher: Publisher, value: boolean): void {
if (!!publisher) { if (!!publisher) {
publisher.publishVideo(value); publisher.publishVideo(value);
@ -197,6 +234,9 @@ export class OpenViduService {
} }
} }
/**
* @internal
*/
publishAudio(publisher: Publisher, value: boolean): void { publishAudio(publisher: Publisher, value: boolean): void {
if (!!publisher) { if (!!publisher) {
publisher.publishAudio(value); publisher.publishAudio(value);
@ -204,8 +244,11 @@ export class OpenViduService {
} }
} }
// TODO: Remove this method when replaceTrack issue is fixed /**
// https://github.com/OpenVidu/openvidu/pull/700 * TODO: Remove this method when replaceTrack issue is fixed
* https://github.com/OpenVidu/openvidu/pull/700
* @internal
*/
republishTrack(properties: PublisherProperties): Promise<void> { republishTrack(properties: PublisherProperties): Promise<void> {
const { videoSource, audioSource, mirror } = properties; const { videoSource, audioSource, mirror } = properties;
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
@ -240,6 +283,9 @@ export class OpenViduService {
}); });
} }
/**
* @internal
*/
sendSignal(type: Signal, connections?: Connection[], data?: any): void { sendSignal(type: Signal, connections?: Connection[], data?: any): void {
const signalOptions: SignalOptions = { const signalOptions: SignalOptions = {
data: JSON.stringify(data), data: JSON.stringify(data),
@ -255,6 +301,9 @@ export class OpenViduService {
// } // }
} }
/**
* @internal
*/
async replaceTrack(videoType: VideoType, props: PublisherProperties) { async replaceTrack(videoType: VideoType, props: PublisherProperties) {
try { try {
this.log.d(`Replacing ${videoType} track`, props); this.log.d(`Replacing ${videoType} track`, props);
@ -346,6 +395,9 @@ export class OpenViduService {
} }
} }
/**
* @internal
*/
needSendNicknameSignal(): boolean { needSendNicknameSignal(): boolean {
let oldNickname: string; let oldNickname: string;
try { try {
@ -357,6 +409,9 @@ export class OpenViduService {
return oldNickname !== this.participantService.getMyNickname(); return oldNickname !== this.participantService.getMyNickname();
} }
/**
* @internal
*/
isMyOwnConnection(connectionId: string): boolean { isMyOwnConnection(connectionId: string): boolean {
return ( return (
this.webcamSession?.connection?.connectionId === connectionId || this.screenSession?.connection?.connectionId === connectionId this.webcamSession?.connection?.connectionId === connectionId || this.screenSession?.connection?.connectionId === connectionId

View File

@ -1,17 +1,17 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject } from 'rxjs'; import { Observable, BehaviorSubject } from 'rxjs';
import { Publisher } from 'openvidu-browser/lib/OpenVidu/Publisher'; import { Publisher } from 'openvidu-browser/lib/OpenVidu/Publisher';
import { UserModel } from '../../models/user.model'; import { ParticipantAbstractModel } from '../../models/participant.model';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class ParticipantServiceMock { export class ParticipantServiceMock {
OVUsers: Observable<UserModel[]>; OVUsers: Observable<ParticipantAbstractModel[]>;
screenShareState: Observable<boolean>; screenShareState: Observable<boolean>;
webcamVideoActive: Observable<boolean>; webcamVideoActive: Observable<boolean>;
private _OVUsers = <BehaviorSubject<UserModel[]>>new BehaviorSubject([]); private _OVUsers = <BehaviorSubject<ParticipantAbstractModel[]>>new BehaviorSubject([]);
private _screenShareState = <BehaviorSubject<boolean>>new BehaviorSubject(false); private _screenShareState = <BehaviorSubject<boolean>>new BehaviorSubject(false);
private _webcamVideoActive = <BehaviorSubject<boolean>>new BehaviorSubject(true); private _webcamVideoActive = <BehaviorSubject<boolean>>new BehaviorSubject(true);

View File

@ -11,11 +11,17 @@ import { LoggerService } from '../logger/logger.service';
providedIn: 'root' providedIn: 'root'
}) })
export class ParticipantService { export class ParticipantService {
//Local participants observables /**
* @internal
* Local participants observables
*/
localParticipantObs: Observable<ParticipantAbstractModel>; localParticipantObs: Observable<ParticipantAbstractModel>;
protected _localParticipant = <BehaviorSubject<ParticipantAbstractModel>>new BehaviorSubject(null); protected _localParticipant = <BehaviorSubject<ParticipantAbstractModel>>new BehaviorSubject(null);
//Remote participants observable /**
* @internal
* Remote participants observables
*/
remoteParticipantsObs: Observable<ParticipantAbstractModel[]>; remoteParticipantsObs: Observable<ParticipantAbstractModel[]>;
protected _remoteParticipants = <BehaviorSubject<ParticipantAbstractModel[]>>new BehaviorSubject([]); protected _remoteParticipants = <BehaviorSubject<ParticipantAbstractModel[]>>new BehaviorSubject([]);
@ -24,6 +30,9 @@ export class ParticipantService {
protected log: ILogger; protected log: ILogger;
/**
* @internal
*/
constructor(protected openviduAngularConfigSrv: OpenViduAngularConfigService, protected loggerSrv: LoggerService) { constructor(protected openviduAngularConfigSrv: OpenViduAngularConfigService, protected loggerSrv: LoggerService) {
this.log = this.loggerSrv.get('ParticipantService'); this.log = this.loggerSrv.get('ParticipantService');
@ -31,7 +40,10 @@ export class ParticipantService {
this.remoteParticipantsObs = this._remoteParticipants.asObservable(); this.remoteParticipantsObs = this._remoteParticipants.asObservable();
} }
initLocalParticipant(props: ParticipantProperties) { /**
* @internal
*/
initLocalParticipant(props: ParticipantProperties) {
this.localParticipant = this.newParticipant(props); this.localParticipant = this.newParticipant(props);
this.updateLocalParticipant(); this.updateLocalParticipant();
} }
@ -40,13 +52,22 @@ export class ParticipantService {
return this.localParticipant; return this.localParticipant;
} }
/**
* @internal
*/
getMyCameraPublisher(): Publisher { getMyCameraPublisher(): Publisher {
return <Publisher>this.localParticipant.getCameraConnection().streamManager; return <Publisher>this.localParticipant.getCameraConnection().streamManager;
} }
/**
* @internal
*/
setMyCameraPublisher(publisher: Publisher) { setMyCameraPublisher(publisher: Publisher) {
this.localParticipant.setCameraPublisher(publisher); this.localParticipant.setCameraPublisher(publisher);
} }
/**
* @internal
*/
setMyCameraConnectionId(connectionId: string) { setMyCameraConnectionId(connectionId: string) {
this.localParticipant.setCameraConnectionId(connectionId); this.localParticipant.setCameraConnectionId(connectionId);
} }
@ -58,26 +79,39 @@ export class ParticipantService {
return <Publisher>this.localParticipant.getScreenConnection()?.streamManager; return <Publisher>this.localParticipant.getScreenConnection()?.streamManager;
} }
/**
* @internal
*/
setMyScreenPublisher(publisher: Publisher) { setMyScreenPublisher(publisher: Publisher) {
this.localParticipant.setScreenPublisher(publisher); this.localParticipant.setScreenPublisher(publisher);
} }
/**
* @internal
*/
setMyScreenConnectionId(connectionId: string) { setMyScreenConnectionId(connectionId: string) {
this.localParticipant.setScreenConnectionId(connectionId); this.localParticipant.setScreenConnectionId(connectionId);
} }
/** /**
* @internal
*/
enableWebcamStream() { enableWebcamStream() {
this.localParticipant.enableCamera(); this.localParticipant.enableCamera();
this.updateLocalParticipant(); this.updateLocalParticipant();
} }
/** /**
* @internal
*/
disableWebcamStream() { disableWebcamStream() {
this.localParticipant.disableCamera(); this.localParticipant.disableCamera();
this.updateLocalParticipant(); this.updateLocalParticipant();
} }
/**
* @internal
*/
activeMyScreenShare(screenPublisher: Publisher) { activeMyScreenShare(screenPublisher: Publisher) {
this.log.d('Enabling screen publisher'); this.log.d('Enabling screen publisher');
@ -96,16 +130,24 @@ export class ParticipantService {
} }
/** /**
* @internal
*/
disableScreenStream() { disableScreenStream() {
this.localParticipant.disableScreen(); this.localParticipant.disableScreen();
this.updateLocalParticipant(); this.updateLocalParticipant();
} }
/**
* @internal
*/
setMyNickname(nickname: string) { setMyNickname(nickname: string) {
this.localParticipant.setNickname(nickname); this.localParticipant.setNickname(nickname);
this.updateLocalParticipant(); this.updateLocalParticipant();
} }
/**
* @internal
*/
getMyNickname(): string { getMyNickname(): string {
return this.localParticipant.nickname; return this.localParticipant.nickname;
} }
@ -113,13 +155,13 @@ export class ParticipantService {
/** /**
* @internal * @internal
*/ */
toggleMyVideoEnlarged(connectionId: string) { toggleMyVideoEnlarged(connectionId: string) {
this.localParticipant.toggleVideoEnlarged(connectionId); this.localParticipant.toggleVideoEnlarged(connectionId);
} }
/**
* @internal
*/
resetMyStreamsToNormalSize() { resetMyStreamsToNormalSize() {
if (this.localParticipant.someHasVideoEnlarged()) { if (this.localParticipant.someHasVideoEnlarged()) {
this.localParticipant.setAllVideoEnlarged(false); this.localParticipant.setAllVideoEnlarged(false);
@ -127,6 +169,9 @@ export class ParticipantService {
} }
} }
/**
* @internal
*/
clear() { clear() {
this.disableScreenStream(); this.disableScreenStream();
// this.localParticipant = this.newParticipant(); // this.localParticipant = this.newParticipant();
@ -137,34 +182,58 @@ export class ParticipantService {
this.updateLocalParticipant(); this.updateLocalParticipant();
} }
/**
* @internal
*/
isMyCameraActive(): boolean { isMyCameraActive(): boolean {
return this.localParticipant.isCameraActive(); return this.localParticipant.isCameraActive();
} }
/**
* @internal
*/
isMyScreenActive(): boolean { isMyScreenActive(): boolean {
return this.localParticipant.isScreenActive(); return this.localParticipant.isScreenActive();
} }
/**
* @internal
*/
isOnlyMyCameraActive(): boolean { isOnlyMyCameraActive(): boolean {
return this.isMyCameraActive() && !this.isMyScreenActive(); return this.isMyCameraActive() && !this.isMyScreenActive();
} }
/**
* @internal
*/
isOnlyMyScreenActive(): boolean { isOnlyMyScreenActive(): boolean {
return this.isMyScreenActive() && !this.isMyCameraActive(); return this.isMyScreenActive() && !this.isMyCameraActive();
} }
/**
* @internal
*/
haveICameraAndScreenActive(): boolean { haveICameraAndScreenActive(): boolean {
return this.isMyCameraActive() && this.isMyScreenActive(); return this.isMyCameraActive() && this.isMyScreenActive();
} }
/**
* @internal
*/
hasCameraVideoActive(): boolean { hasCameraVideoActive(): boolean {
return this.localParticipant.isCameraVideoActive(); return this.localParticipant.isCameraVideoActive();
} }
/**
* @internal
*/
hasCameraAudioActive(): boolean { hasCameraAudioActive(): boolean {
return this.localParticipant?.isCameraAudioActive(); return this.localParticipant?.isCameraAudioActive();
} }
/**
* @internal
*/
hasScreenAudioActive(): boolean { hasScreenAudioActive(): boolean {
return this.localParticipant.isScreenAudioActive(); return this.localParticipant.isScreenAudioActive();
} }
@ -177,14 +246,16 @@ export class ParticipantService {
* REMOTE USERS * REMOTE USERS
*/ */
addRemoteConnection(connectionId:string, data: string, subscriber: Subscriber) { /**
* @internal
*/
addRemoteConnection(connectionId: string, data: string, subscriber: Subscriber) {
const type: VideoType = this.getTypeConnectionData(data); const type: VideoType = this.getTypeConnectionData(data);
const streamModel: StreamModel = { const streamModel: StreamModel = {
type, type,
videoEnlarged: type === VideoType.SCREEN, videoEnlarged: type === VideoType.SCREEN,
streamManager: subscriber, streamManager: subscriber,
connected: true, connected: true,
connectionId connectionId
}; };
@ -195,12 +266,12 @@ export class ParticipantService {
const participantAdded = this.getRemoteParticipantById(participantId); const participantAdded = this.getRemoteParticipantById(participantId);
if (!!participantAdded) { if (!!participantAdded) {
this.log.d('Adding connection to existing participant: ', participantId); this.log.d('Adding connection to existing participant: ', participantId);
if(participantAdded.hasConnectionType(streamModel.type)) { if (participantAdded.hasConnectionType(streamModel.type)) {
this.log.d('Participant has publisher, updating it'); this.log.d('Participant has publisher, updating it');
participantAdded.setPublisher(streamModel.type, subscriber); participantAdded.setPublisher(streamModel.type, subscriber);
} else { } else {
this.log.d('Participant has not publisher, adding it'); this.log.d('Participant has not publisher, adding it');
if(streamModel.type === VideoType.SCREEN) { if (streamModel.type === VideoType.SCREEN) {
this.resetRemoteStreamsToNormalSize(); this.resetRemoteStreamsToNormalSize();
this.resetMyStreamsToNormalSize(); this.resetMyStreamsToNormalSize();
} }
@ -212,18 +283,28 @@ export class ParticipantService {
nickname: this.getNicknameFromConnectionData(data), nickname: this.getNicknameFromConnectionData(data),
local: false, local: false,
id: participantId id: participantId
} };
const remoteParticipant = this.newParticipant(props, streamModel); const remoteParticipant = this.newParticipant(props, streamModel);
this.remoteParticipants.push(remoteParticipant); this.remoteParticipants.push(remoteParticipant);
} }
this.updateRemoteParticipants(); this.updateRemoteParticipants();
} }
getRemoteParticipants(): ParticipantAbstractModel[] {
return this.remoteParticipants;
}
/**
* @internal
*/
resetRemoteStreamsToNormalSize() { resetRemoteStreamsToNormalSize() {
this.remoteParticipants.forEach(participant => participant.setAllVideoEnlarged(false)); this.remoteParticipants.forEach((participant) => participant.setAllVideoEnlarged(false));
this.updateRemoteParticipants(); this.updateRemoteParticipants();
} }
/**
* @internal
*/
removeConnectionByConnectionId(connectionId: string) { removeConnectionByConnectionId(connectionId: string) {
this.log.w('Deleting connection: ', connectionId); this.log.w('Deleting connection: ', connectionId);
let participant = null; let participant = null;
@ -240,13 +321,13 @@ export class ParticipantService {
// Remove participants without connections // Remove participants without connections
this.remoteParticipants = this.remoteParticipants.filter((p) => p !== participant); this.remoteParticipants = this.remoteParticipants.filter((p) => p !== participant);
} }
if(removeStream.type === VideoType.SCREEN){ if (removeStream.type === VideoType.SCREEN) {
const remoteScreens = this.remoteParticipants.filter(p => p.isScreenActive()); const remoteScreens = this.remoteParticipants.filter((p) => p.isScreenActive());
if(remoteScreens.length > 0){ if (remoteScreens.length > 0) {
// Enlarging the last screen connection active // Enlarging the last screen connection active
const lastScreenActive = remoteScreens[remoteScreens.length -1]; const lastScreenActive = remoteScreens[remoteScreens.length - 1];
lastScreenActive.setScreenEnlarged(true); lastScreenActive.setScreenEnlarged(true);
} else if(this.localParticipant.isScreenActive()) { } else if (this.localParticipant.isScreenActive()) {
// Enlarging my screen if thereare not any remote screen active // Enlarging my screen if thereare not any remote screen active
this.localParticipant.setScreenEnlarged(true); this.localParticipant.setScreenEnlarged(true);
} }
@ -255,6 +336,9 @@ export class ParticipantService {
this.updateRemoteParticipants(); this.updateRemoteParticipants();
} }
} }
/**
* @internal
*/
getRemoteParticipantByConnectionId(connectionId: string): ParticipantAbstractModel { getRemoteParticipantByConnectionId(connectionId: string): ParticipantAbstractModel {
return this.remoteParticipants.find((p) => p.hasConnectionId(connectionId)); return this.remoteParticipants.find((p) => p.hasConnectionId(connectionId));
} }
@ -262,15 +346,24 @@ export class ParticipantService {
protected getRemoteParticipantById(id: string): ParticipantAbstractModel { protected getRemoteParticipantById(id: string): ParticipantAbstractModel {
return this.remoteParticipants.find((p) => p.id === id); return this.remoteParticipants.find((p) => p.id === id);
} }
/**
* @internal
*/
someoneIsSharingScreen(): boolean { someoneIsSharingScreen(): boolean {
return this.remoteParticipants.some((p) => p.someHasVideoEnlarged()); return this.remoteParticipants.some((p) => p.someHasVideoEnlarged());
} }
/**
* @internal
*/
toggleRemoteVideoEnlarged(connectionId: string) { toggleRemoteVideoEnlarged(connectionId: string) {
const p = this.getRemoteParticipantByConnectionId(connectionId); const p = this.getRemoteParticipantByConnectionId(connectionId);
p.toggleVideoEnlarged(connectionId); p.toggleVideoEnlarged(connectionId);
} }
/**
* @internal
*/
getNicknameFromConnectionData(data: string): string { getNicknameFromConnectionData(data: string): string {
try { try {
return JSON.parse(data).clientData; return JSON.parse(data).clientData;
@ -279,6 +372,9 @@ export class ParticipantService {
} }
} }
/**
* @internal
*/
setRemoteNickname(connectionId: string, nickname: string) { setRemoteNickname(connectionId: string, nickname: string) {
const participant = this.getRemoteParticipantByConnectionId(connectionId); const participant = this.getRemoteParticipantByConnectionId(connectionId);
if (participant) { if (participant) {
@ -308,8 +404,7 @@ export class ParticipantService {
} }
protected newParticipant(props: ParticipantProperties, streamModel?: StreamModel) { protected newParticipant(props: ParticipantProperties, streamModel?: StreamModel) {
if (this.openviduAngularConfigSrv.hasParticipantFactory()) {
if(this.openviduAngularConfigSrv.hasParticipantFactory()){
return this.openviduAngularConfigSrv.getParticipantFactory().apply(this, [props, streamModel]); return this.openviduAngularConfigSrv.getParticipantFactory().apply(this, [props, streamModel]);
} }
return new ParticipantModel(props, streamModel); return new ParticipantModel(props, streamModel);

View File

@ -1,5 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -4,6 +4,9 @@ import { ILogger } from '../../models/logger.model';
import { MenuType } from '../../models/menu.model'; import { MenuType } from '../../models/menu.model';
import { LoggerService } from '../logger/logger.service'; import { LoggerService } from '../logger/logger.service';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -3,6 +3,9 @@ import { ILogger } from '../../models/logger.model';
import { LoggerService } from '../logger/logger.service'; import { LoggerService } from '../logger/logger.service';
import { Storage } from '../../models/storage.model'; import { Storage } from '../../models/storage.model';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -1,5 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
/**
* @internal
*/
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -0,0 +1,30 @@
<ov-videoconference
*ngIf="success"
[participantName]="_participantName"
[tokens]="_tokens"
[minimal]="_minimal"
[prejoin]="_prejoin"
[videoMuted]="_videoMuted"
[audioMuted]="_audioMuted"
[toolbarScreenshareButton]="_toolbarScreenshareButton"
[toolbarFullscreenButton]="_toolbarFullscreenButton"
[toolbarLeaveButton]="_toolbarLeaveButton"
[toolbarChatPanelButton]="_toolbarChatPanelButton"
[toolbarParticipantsPanelButton]="_toolbarParticipantsPanelButton"
[toolbarDisplayLogo]="_toolbarDisplayLogo"
[toolbarDisplaySessionName]="_toolbarDisplaySessionName"
[streamDisplayParticipantName]="_streamDisplayParticipantName"
[streamDisplayAudioDetection]="_streamDisplayAudioDetection"
[streamSettingsButton]="_streamSettingsButton"
[participantPanelItemMuteButton]="_participantPanelItemMuteButton"
(onJoinButtonClicked)="_onJoinButtonClicked()"
(onToolbarLeaveButtonClicked)="_onToolbarLeaveButtonClicked()"
(onToolbarCameraButtonClicked)="_onToolbarCameraButtonClicked()"
(onToolbarMicrophoneButtonClicked)="_onToolbarMicrophoneButtonClicked()"
(onToolbarScreenshareButtonClicked)="_onToolbarScreenshareButtonClicked()"
(onToolbarParticipantsPanelButtonClicked)="_onToolbarParticipantsPanelButtonClicked()"
(onToolbarChatPanelButtonClicked)="_onToolbarChatPanelButtonClicked()"
(onToolbarFullscreenButtonClicked)="_onToolbarFullscreenButtonClicked()"
(onSessionCreated)="_onSessionCreated($event)"
(onParticipantCreated)="_onParticipantCreated($event)"
></ov-videoconference>

View File

@ -8,57 +8,81 @@ export interface TokenModel {
screen: string; screen: string;
} }
/**
*
* **OpenviduWebComponentComponent** is a wrapped of the {@link VideoconferenceComponent} which allows to generate and export the OpenVidu Webcomponent
*/
@Component({ @Component({
template: ` templateUrl: './openvidu-webcomponent.component.html'
<ov-videoconference
*ngIf="success"
[participantName]="_participantName"
[tokens]="_tokens"
[minimal]="_minimal"
[prejoin]="_prejoin"
[videoMuted]="_videoMuted"
[audioMuted]="_audioMuted"
[toolbarScreenshareButton]="_toolbarScreenshareButton"
[toolbarFullscreenButton]="_toolbarFullscreenButton"
[toolbarLeaveButton]="_toolbarLeaveButton"
[toolbarChatPanelButton]="_toolbarChatPanelButton"
[toolbarParticipantsPanelButton]="_toolbarParticipantsPanelButton"
[toolbarDisplayLogo]="_toolbarDisplayLogo"
[toolbarDisplaySessionName]="_toolbarDisplaySessionName"
[streamDisplayParticipantName]="_streamDisplayParticipantName"
[streamDisplayAudioDetection]="_streamDisplayAudioDetection"
[streamSettingsButton]="_streamSettingsButton"
[participantPanelItemMuteButton]="_participantPanelItemMuteButton"
(onJoinButtonClicked)="_onJoinButtonClicked()"
(onToolbarLeaveButtonClicked)="_onToolbarLeaveButtonClicked()"
(onToolbarCameraButtonClicked)="_onToolbarCameraButtonClicked()"
(onToolbarMicrophoneButtonClicked)="_onToolbarMicrophoneButtonClicked()"
(onToolbarScreenshareButtonClicked)="_onToolbarScreenshareButtonClicked()"
(onToolbarParticipantsPanelButtonClicked)="_onToolbarParticipantsPanelButtonClicked()"
(onToolbarChatPanelButtonClicked)="_onToolbarChatPanelButtonClicked()"
(onToolbarFullscreenButtonClicked)="_onToolbarFullscreenButtonClicked()"
(onSessionCreated)="_onSessionCreated($event)"
(onParticipantCreated)="_onParticipantCreated($event)"
></ov-videoconference>
`
}) })
export class OpenviduWebComponentComponent implements OnInit { export class OpenviduWebComponentComponent implements OnInit {
/**
* @internal
*/
_tokens: TokenModel; _tokens: TokenModel;
/**
* @internal
*/
_minimal: boolean = false; _minimal: boolean = false;
/**
* @internal
*/
_participantName: string; _participantName: string;
/**
* @internal
*/
_prejoin: boolean = true; _prejoin: boolean = true;
/**
* @internal
*/
_videoMuted: boolean = false; _videoMuted: boolean = false;
/**
* @internal
*/
_audioMuted: boolean = false; _audioMuted: boolean = false;
/**
* @internal
*/
_toolbarScreenshareButton: boolean = true; _toolbarScreenshareButton: boolean = true;
/**
* @internal
*/
_toolbarFullscreenButton: boolean = true; _toolbarFullscreenButton: boolean = true;
/**
* @internal
*/
_toolbarLeaveButton: boolean = true; _toolbarLeaveButton: boolean = true;
/**
* @internal
*/
_toolbarChatPanelButton: boolean = true; _toolbarChatPanelButton: boolean = true;
/**
* @internal
*/
_toolbarParticipantsPanelButton: boolean = true; _toolbarParticipantsPanelButton: boolean = true;
/**
* @internal
*/
_toolbarDisplayLogo: boolean = true; _toolbarDisplayLogo: boolean = true;
/**
* @internal
*/
_toolbarDisplaySessionName: boolean = true; _toolbarDisplaySessionName: boolean = true;
/**
* @internal
*/
_streamDisplayParticipantName: boolean = true; _streamDisplayParticipantName: boolean = true;
/**
* @internal
*/
_streamDisplayAudioDetection: boolean = true; _streamDisplayAudioDetection: boolean = true;
/**
* @internal
*/
_streamSettingsButton: boolean = true; _streamSettingsButton: boolean = true;
/**
* @internal
*/
_participantPanelItemMuteButton: boolean = true; _participantPanelItemMuteButton: boolean = true;
@Input() set minimal(value: string | boolean) { @Input() set minimal(value: string | boolean) {
@ -122,9 +146,15 @@ export class OpenviduWebComponentComponent implements OnInit {
@Output() onSessionCreated = new EventEmitter<any>(); @Output() onSessionCreated = new EventEmitter<any>();
@Output() onParticipantCreated = new EventEmitter<any>(); @Output() onParticipantCreated = new EventEmitter<any>();
/**
* @internal
*/
success: boolean = false; success: boolean = false;
private log: ILogger; private log: ILogger;
/**
* @internal
*/
constructor(private loggerService: LoggerService, private host: ElementRef, private openviduService: OpenViduService) { constructor(private loggerService: LoggerService, private host: ElementRef, private openviduService: OpenViduService) {
this.log = this.loggerService.get('WebComponent'); this.log = this.loggerService.get('WebComponent');
this.host.nativeElement.leaveSession = this.leaveSession.bind(this); this.host.nativeElement.leaveSession = this.leaveSession.bind(this);
@ -147,37 +177,68 @@ export class OpenviduWebComponentComponent implements OnInit {
} }
} }
/**
* @internal
*/
_onJoinButtonClicked() { _onJoinButtonClicked() {
this.onJoinButtonClicked.emit(); this.onJoinButtonClicked.emit();
} }
/**
* @internal
*/
_onToolbarLeaveButtonClicked() { _onToolbarLeaveButtonClicked() {
this.success = false;
this.onToolbarLeaveButtonClicked.emit(); this.onToolbarLeaveButtonClicked.emit();
} }
/**
* @internal
*/
_onToolbarCameraButtonClicked() { _onToolbarCameraButtonClicked() {
this.onToolbarCameraButtonClicked.emit(); this.onToolbarCameraButtonClicked.emit();
} }
/**
* @internal
*/
_onToolbarMicrophoneButtonClicked() { _onToolbarMicrophoneButtonClicked() {
this.onToolbarMicrophoneButtonClicked.emit(); this.onToolbarMicrophoneButtonClicked.emit();
} }
/**
* @internal
*/
_onToolbarScreenshareButtonClicked() { _onToolbarScreenshareButtonClicked() {
this.onToolbarScreenshareButtonClicked.emit(); this.onToolbarScreenshareButtonClicked.emit();
} }
/**
* @internal
*/
_onToolbarParticipantsPanelButtonClicked() { _onToolbarParticipantsPanelButtonClicked() {
this.onToolbarParticipantsPanelButtonClicked.emit(); this.onToolbarParticipantsPanelButtonClicked.emit();
} }
/**
* @internal
*/
_onToolbarChatPanelButtonClicked() { _onToolbarChatPanelButtonClicked() {
this.onToolbarChatPanelButtonClicked.emit(); this.onToolbarChatPanelButtonClicked.emit();
} }
/**
* @internal
*/
_onToolbarFullscreenButtonClicked() { _onToolbarFullscreenButtonClicked() {
this.onToolbarFullscreenButtonClicked.emit(); this.onToolbarFullscreenButtonClicked.emit();
} }
/**
* @internal
*/
_onSessionCreated(event: Session) { _onSessionCreated(event: Session) {
this.onSessionCreated.emit(event); this.onSessionCreated.emit(event);
} }
/**
* @internal
*/
_onParticipantCreated(event: ParticipantAbstractModel) { _onParticipantCreated(event: ParticipantAbstractModel) {
this.onParticipantCreated.emit(event); this.onParticipantCreated.emit(event);
} }

View File

@ -0,0 +1,19 @@
{
"name": "OpenVidu Angular Documentation",
"output": "docs/openvidu-angular",
"hideGenerator": true,
"disableLifeCycleHooks": true,
"disableProtected": true,
"disableInternal": true,
"disablePrivate": true,
"disableCoverage": true,
"disableRoutesGraph": true,
"disableSourceCode": true,
"disableTemplateTab": true,
"disableDomTree": true,
"disableStyleTab": true,
"disableDependencies": true,
"theme": "gitbook",
"customFavicon": "src/favicon.ico",
"extTheme": "src/doc/"
}

View File

@ -0,0 +1,93 @@
@import "./reset.css";
@import "./bootstrap.min.css";
@import "./bootstrap-card.css";
@import "./prism.css";
@import "./ionicons.min.css";
@import "./compodoc.css";
@import "./tablesort.css";
.title {
font-weight: bold;
}
/* .getting-started {
display: none;
} */
.token.property, .token.tag, .token.constant, .token.symbol, .token.deleted {
color: rgb(0, 204, 255);
}
.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted {
color: #d3ff7c;
}
.token.atrule, .token.attr-value, .token.function, .token.class-name {
color: rgb(255, 170, 72);
}
.italic {
font-style: italic;
}
.component-link-text {
padding-top: 5px;
text-align: center;
font-size: 14px;
}
div>p {
font-size: 16px;
}
code {
background-color: rgb(233, 245, 255);
font-weight: bold;
padding: 2px;
}
.directive section, .module-graph-container, .card-module>.card-block>p,
nav a[href*="license.html"],
nav a[href*="properties.html"],
nav a[href*="overview.html"],
nav a[href*="index.html"] {
display: none !important;
}
.card-module {
min-height: 0;
}
.custom-table-container {
display: flex;
}
.custom-table-container table {
margin: auto;
}
.custom-table-container>div {
padding: 10px;
}
.warn-container, .info-container {
display: table;
border-radius: 5px;
width: 100%;
margin-top: 30px;
margin-bottom: 25px;
padding: 5px 0 5px 0;
}
.info-container > span, .warn-container >span {
font-weight: bold;
padding-left: 5px;
}
.warn-container {
border: 2px solid #ffb600;
background-color: #FFFBF1;
}
.info-container {
border: 2px solid #0077ff;
background-color: #f1feff;
}

View File

@ -0,0 +1,14 @@
{
"include": [
"../../projects/openvidu-angular/src/lib/components/**/*.ts",
"../../projects/openvidu-angular/src/lib/directives/**/*.ts",
"../../projects/openvidu-angular/src/lib/services/**/*.ts",
"../../projects/openvidu-angular/src/lib/models/**/*.ts",
"../app/openvidu-webcomponent/openvidu-webcomponent.component.ts"
],
"exclude": [
"src/test.ts",
"../../projects/openvidu-angular/src/lib/**/*.mock.ts",
"../../projects/openvidu-angular/src/lib/**/*.spec.ts"
],
}