openvidu-component: Loaded components dynamically

- For loading components with inputs/outputs directives, has been necessary to use the 'ng-dynamic-component' library because of unsupported on the official Angular issue  https://github.com/angular/angular/issues/15360
- Allowed the dynamic components load
- Grouped panels into panel component
pull/690/head
csantosm 2022-01-28 15:16:09 +01:00
parent e56ac82749
commit 5f0ffb4b3a
32 changed files with 462 additions and 144 deletions

View File

@ -20,6 +20,7 @@
"@angular/router": "13.0.0",
"autolinker": "3.14.3",
"buffer": "^6.0.3",
"ng-dynamic-component": "^10.1.0",
"openvidu-browser": "2.21.0-beta1",
"rxjs": "7.4.0",
"tslib": "2.3.1",
@ -9065,6 +9066,19 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
"node_modules/ng-dynamic-component": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ng-dynamic-component/-/ng-dynamic-component-10.1.0.tgz",
"integrity": "sha512-P3ejLAuezi/a7DfLk6SaqGUcDIhM8tfeNbJ3fAu2sPXgOOpHLPkEKw9nJc8C365B+fsZVX0B5GYuDX4pXa3GMQ==",
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/common": "^12.0.0 || ^13.0.0",
"@angular/core": "^12.0.0 || ^13.0.0",
"rxjs": "^6.0.0 || ^7.0.0"
}
},
"node_modules/ng-packagr": {
"version": "13.0.3",
"resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-13.0.3.tgz",
@ -21901,6 +21915,14 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
"ng-dynamic-component": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ng-dynamic-component/-/ng-dynamic-component-10.1.0.tgz",
"integrity": "sha512-P3ejLAuezi/a7DfLk6SaqGUcDIhM8tfeNbJ3fAu2sPXgOOpHLPkEKw9nJc8C365B+fsZVX0B5GYuDX4pXa3GMQ==",
"requires": {
"tslib": "^2.0.0"
}
},
"ng-packagr": {
"version": "13.0.3",
"resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-13.0.3.tgz",

View File

@ -27,6 +27,7 @@
"@angular/router": "13.0.0",
"autolinker": "3.14.3",
"buffer": "^6.0.3",
"ng-dynamic-component": "10.1.0",
"openvidu-browser": "2.21.0-beta1",
"rxjs": "7.4.0",
"tslib": "2.3.1",

View File

@ -10,7 +10,8 @@
"@angular/flex-layout": "^13.0.0-beta.36",
"autolinker": "^3.14.3",
"buffer": "^6.0.3",
"openvidu-browser": "^2.20.0"
"openvidu-browser": "^2.20.0",
"ng-dynamic-component": "^10.1.0"
},
"dependencies": {
"tslib": "^2.3.0"

View File

@ -1,35 +1,22 @@
<div id="layout" class="bounds">
<!-- Custom local participant -->
<ng-container *ngIf="customLocalParticipantTemplate; else defaultLocalParticipant">
<ng-container *ngTemplateOutlet="customLocalParticipantTemplate"></ng-container>
</ng-container>
<div
class="OT_root OT_publisher"
id="localUser"
*ngFor="let connection of localParticipant | connections"
[ngClass]="{ OV_small: !connection.streamManager?.stream?.videoActive }"
>
<ng-template #localStream [ngComponentOutlet]="_localStreamComponent" [ndcDynamicInputs]="{ participant: connection }">
</ng-template>
</div>
<!-- Default local participant if custom participant is not injected -->
<ng-template #defaultLocalParticipant>
<div
class="OT_root OT_publisher"
id="localUser"
*ngFor="let connection of localParticipant | connections"
[ngClass]="{ OV_small: !connection.streamManager?.stream?.videoActive }"
>
<ov-stream [participant]="connection" [videoEnlarged]="connection.videoEnlarged"></ov-stream>
</div>
</ng-template>
<!-- Custom remote participant -->
<ng-container *ngIf="customRemoteParticipantsTemplate; else defaultRemoteParticipants">
<ng-container *ngTemplateOutlet="customRemoteParticipantsTemplate"></ng-container>
</ng-container>
<!-- Default remote participants if custom participants is not injected -->
<ng-template #defaultRemoteParticipants>
<div
*ngFor="let connection of remoteParticipants | connections"
class="OT_root OT_publisher"
id="remote-participant"
[ngClass]="{ OV_small: !connection.streamManager?.stream?.videoActive }"
>
<ov-stream [participant]="connection" [videoEnlarged]="connection.videoEnlarged"></ov-stream>
</div>
</ng-template>
<div
*ngFor="let connection of remoteParticipants | connections"
class="OT_root OT_publisher"
id="remote-participant"
[ngClass]="{ OV_small: !connection.streamManager?.stream?.videoActive }"
>
<!-- Dynamic ov-stream component injected -->
<ng-template #remoteStream [ngComponentOutlet]="_remoteStreamComponent" [ndcDynamicInputs]="{ participant: connection }">
</ng-template>
</div>
</div>

View File

@ -1,8 +1,11 @@
import { AfterViewInit, Component, ContentChild, OnDestroy, OnInit, TemplateRef } from '@angular/core';
import { AfterViewInit, Component, OnDestroy, OnInit, Type, ViewChild, ViewContainerRef } from '@angular/core';
import { Subscription } from 'rxjs';
import { ParticipantService } from '../../services/participant/participant.service';
import { ParticipantAbstractModel } from '../../models/participant.model';
import { LayoutService } from '../../services/layout/layout.service';
import { LibraryComponents } from '../../config/lib.config';
import { LibraryConfigService } from '../../services/library-config/library-config.service';
import { StreamComponent } from '../stream/stream.component';
@Component({
selector: 'ov-layout',
@ -10,8 +13,10 @@ import { LayoutService } from '../../services/layout/layout.service';
styleUrls: ['./layout.component.css']
})
export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
@ContentChild('customLocalParticipant', { read: TemplateRef }) customLocalParticipantTemplate: TemplateRef<any>;
@ContentChild('customRemoteParticipants', { read: TemplateRef }) customRemoteParticipantsTemplate: TemplateRef<any>;
_localStream: ViewContainerRef;
_localStreamComponent: Type<any>;
_remoteStream: ViewContainerRef;
_remoteStreamComponent: Type<any>;
localParticipant: ParticipantAbstractModel;
remoteParticipants: ParticipantAbstractModel[] = [];
@ -19,10 +24,48 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
protected remoteParticipantsSubs: Subscription;
protected updateLayoutInterval: NodeJS.Timer;
constructor(protected layoutService: LayoutService, protected participantService: ParticipantService) {}
constructor(
protected libraryConfigSrv: LibraryConfigService,
protected layoutService: LayoutService,
protected participantService: ParticipantService
) {}
@ViewChild('localStream', { static: false, read: ViewContainerRef })
set stream(reference: ViewContainerRef) {
setTimeout(() => {
this._localStream = reference;
if (this._localStream) {
let component = StreamComponent;
if (this.libraryConfigSrv.isCustomComponentDefined(LibraryComponents.STREAM)) {
component = this.libraryConfigSrv.getCustomComponent(LibraryComponents.STREAM);
}
// this._stream?.clear();
this._localStreamComponent = component;
// this._stream.createComponent(component);
}
}, 0);
}
@ViewChild('remoteStream', { static: false, read: ViewContainerRef })
set remoteStream(reference: ViewContainerRef) {
setTimeout(() => {
this._remoteStream = reference;
if (this._remoteStream) {
let component = StreamComponent;
if (this.libraryConfigSrv.isCustomComponentDefined(LibraryComponents.STREAM)) {
component = this.libraryConfigSrv.getCustomComponent(LibraryComponents.STREAM);
}
// this.remoteStream?.clear();
this._remoteStreamComponent = component;
// this._stream.createComponent(component);
}
}, 0);
}
ngOnInit(): void {
this.subscribeToUsers();
this.subscribeToParticipants();
}
ngAfterViewInit() {}
@ -34,7 +77,7 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
if (this.remoteParticipantsSubs) this.remoteParticipantsSubs.unsubscribe();
}
protected subscribeToUsers() {
protected subscribeToParticipants() {
this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p) => {
this.localParticipant = p;
this.layoutService.update();

View File

@ -1,6 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChatService } from '../../services/chat/chat.service';
import { ChatServiceMock } from '../../services/chat/chat.service.mock';
import { ChatService } from '../../../services/chat/chat.service';
import { ChatServiceMock } from '../../../services/chat/chat.service.mock';
import { ChatPanelComponent } from './chat-panel.component';

View File

@ -1,9 +1,9 @@
import { AfterViewInit, Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core';
import { Subscription } from 'rxjs';
import { ChatMessage } from '../../models/chat.model';
import { MenuType } from '../../models/menu.model';
import { ChatService } from '../../services/chat/chat.service';
import { SidenavMenuService } from '../../services/sidenav-menu/sidenav-menu.service';
import { ChatMessage } from '../../../models/chat.model';
import { MenuType } from '../../../models/menu.model';
import { ChatService } from '../../../services/chat/chat.service';
import { SidenavMenuService } from '../../../services/sidenav-menu/sidenav-menu.service';
@Component({
selector: 'ov-chat-panel',

View File

@ -0,0 +1,5 @@
<ng-container *ngIf="isChatPanelOpened" #chat></ng-container>
<ng-container *ngIf="isParticipantsPanelOpened" #participants></ng-container>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PanelComponent } from './panel.component';
describe('PanelComponent', () => {
let component: PanelComponent;
let fixture: ComponentFixture<PanelComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PanelComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PanelComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,71 @@
import { Component, OnDestroy, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
import { skip, Subscription } from 'rxjs';
import { LibraryComponents } from '../../config/lib.config';
import { MenuType } from '../../models/menu.model';
import { LibraryConfigService } from '../../services/library-config/library-config.service';
import { SidenavMenuService } from '../../services/sidenav-menu/sidenav-menu.service';
import { ChatPanelComponent } from './chat-panel/chat-panel.component';
import { ParticipantsPanelComponent } from './participants-panel/participants-panel/participants-panel.component';
@Component({
selector: 'ov-panel',
templateUrl: './panel.component.html',
styleUrls: ['./panel.component.css']
})
export class PanelComponent implements OnInit, OnDestroy {
isParticipantsPanelOpened: boolean;
isChatPanelOpened: boolean;
_chat: ViewContainerRef;
_participants: ViewContainerRef;
menuSubscription: Subscription;
@ViewChild('chat', { static: false, read: ViewContainerRef })
set chat(reference: ViewContainerRef) {
setTimeout(() => {
this._chat = reference;
if (this._chat) {
let component = ChatPanelComponent;
if (this.libraryConfigSrv.isCustomComponentDefined(LibraryComponents.CHAT_PANEL)) {
component = this.libraryConfigSrv.getCustomComponent(LibraryComponents.CHAT_PANEL);
}
this._chat?.clear();
this._chat.createComponent(component);
}
}, 0);
}
@ViewChild('participants', { static: false, read: ViewContainerRef })
set participants(reference: ViewContainerRef) {
setTimeout(() => {
this._participants = reference;
if (this._participants) {
let component = ParticipantsPanelComponent;
if (this.libraryConfigSrv.isCustomComponentDefined(LibraryComponents.PARTICIPANTS_PANEL)) {
component = this.libraryConfigSrv.getCustomComponent(LibraryComponents.PARTICIPANTS_PANEL);
}
this._participants?.clear();
this._participants.createComponent(component);
}
}, 0);
}
constructor(protected libraryConfigSrv: LibraryConfigService, protected menuService: SidenavMenuService) {}
ngOnInit(): void {
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;
});
}
ngOnDestroy() {
this.isChatPanelOpened = false;
this.isParticipantsPanelOpened = false;
if (this.menuSubscription) this.menuSubscription.unsubscribe();
}
}

View File

@ -1,7 +1,7 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ParticipantAbstractModel, ParticipantModel } from '../../../models/participant.model';
import { ParticipantService } from '../../../services/participant/participant.service';
import { SidenavMenuService } from '../../../services/sidenav-menu/sidenav-menu.service';
import { ParticipantAbstractModel, ParticipantModel } from '../../../../models/participant.model';
import { ParticipantService } from '../../../../services/participant/participant.service';
import { SidenavMenuService } from '../../../../services/sidenav-menu/sidenav-menu.service';
@Component({
selector: 'ov-participants-panel',

View File

@ -11,22 +11,30 @@
fixedBottomGap="0"
>
<!-- Custom menu content -->
<ng-container *ngIf="customPanelContentTemplate; else defaultPanelContent">
<ng-container *ngTemplateOutlet="customPanelContentTemplate"></ng-container>
</ng-container>
<!-- <ng-container *ngIf="customPanelContentTemplate; else defaultPanelContent"> -->
<!-- <ng-container *ngTemplateOutlet="customPanelContentTemplate"></ng-container> -->
<!-- </ng-container> -->
<!-- Default menu content if custom menu content is not injected -->
<ng-template #defaultPanelContent>
<!-- <ng-template #defaultPanelContent>
<ov-chat-panel *ngIf="isChatPanelOpened"></ov-chat-panel>
<ov-participants-panel *ngIf="isParticipantsPanelOpened"></ov-participants-panel>
</ng-template>
</ng-template> -->
<ng-template #panel></ng-template>
</mat-sidenav>
<!-- OPENVIDU LAYOUT -->
<mat-sidenav-content class="sidenav-main">
<div id="layout-container">
<!-- <div id="layout-container">
<ng-content select="[layout]"></ng-content>
</div>
</div> -->
<ng-template #layout></ng-template>
</mat-sidenav-content>
</mat-sidenav-container>
@ -37,9 +45,11 @@
</div>
</ng-container> -->
<ng-container *ngIf="toolbarTemplate">
<!-- <ng-container *ngIf="toolbarTemplate">
<div id="footer-container">
<ng-container *ngTemplateOutlet="toolbarTemplate"></ng-container>
</div>
</ng-container>
</ng-container> -->
<ng-template #toolbar></ng-template>
</div>

View File

@ -1,4 +1,15 @@
import { Component, ContentChild, EventEmitter, HostListener, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core';
import {
Component,
ContentChild,
EventEmitter,
HostListener,
Input,
OnInit,
Output,
TemplateRef,
ViewChild,
ViewContainerRef
} from '@angular/core';
import { Subscriber, Session, StreamEvent, StreamPropertyChangedEvent, SessionDisconnectedEvent, ConnectionEvent } from 'openvidu-browser';
import { VideoType } from '../../models/video-type.model';
@ -8,7 +19,6 @@ import { ChatService } from '../../services/chat/chat.service';
import { LoggerService } from '../../services/logger/logger.service';
import { WebrtcService } from '../../services/webrtc/webrtc.service';
import { TokenService } from '../../services/token/token.service';
import { PlatformService } from '../../services/platform/platform.service';
import { ActionService } from '../../services/action/action.service';
import { Signal } from '../../models/signal.model';
import { ParticipantService } from '../../services/participant/participant.service';
@ -18,6 +28,11 @@ import { LayoutService } from '../../services/layout/layout.service';
import { Subscription, skip } from 'rxjs';
import { MenuType } from '../../models/menu.model';
import { SidenavMenuService } from '../../services/sidenav-menu/sidenav-menu.service';
import { ToolbarComponent } from '../toolbar/toolbar.component';
import { LibraryConfigService } from '../../services/library-config/library-config.service';
import { LayoutComponent } from '../layout/layout.component';
import { PanelComponent } from '../panel/panel.component';
import { LibraryComponents } from '../../config/lib.config';
@Component({
selector: 'ov-session',
@ -25,14 +40,14 @@ import { SidenavMenuService } from '../../services/sidenav-menu/sidenav-menu.ser
styleUrls: ['./session.component.css']
})
export class SessionComponent implements OnInit {
@ContentChild('toolbar', { read: TemplateRef }) toolbarTemplate: TemplateRef<any>;
// @ContentChild('toolbar', { read: TemplateRef }) toolbarTemplate: TemplateRef<any>;
@ContentChild('customPanelContent', { read: TemplateRef }) customPanelContentTemplate: TemplateRef<any>;
@ContentChild('customLayoutElement', { read: TemplateRef }) customLayoutElementTemplate: TemplateRef<any>;
@Input() tokens: { webcam: string; screen: string };
@Output() _session = new EventEmitter<any>();
@Output() _publisher = new EventEmitter<any>();
@Output() _error = new EventEmitter<any>();
// @Output() _session = new EventEmitter<any>();
// @Output() _publisher = new EventEmitter<any>();
// @Output() _error = new EventEmitter<any>();
session: Session;
sessionScreen: Session;
@ -40,8 +55,7 @@ export class SessionComponent implements OnInit {
sideMenu: MatSidenav;
sidenavMode: SidenavMode = SidenavMode.SIDE;
isParticipantsPanelOpened: boolean;
isChatPanelOpened: boolean;
protected readonly SIDENAV_WIDTH_LIMIT_MODE = 790;
protected menuSubscription: Subscription;
@ -51,6 +65,10 @@ export class SessionComponent implements OnInit {
protected log: ILogger;
_toolbar: ViewContainerRef;
_layout: ViewContainerRef;
_panel: ViewContainerRef;
constructor(
protected actionService: ActionService,
protected webrtcService: WebrtcService,
@ -60,12 +78,63 @@ export class SessionComponent implements OnInit {
protected tokenService: TokenService,
protected layoutService: LayoutService,
protected menuService: SidenavMenuService,
protected platformService: PlatformService
protected libraryConfigSrv: LibraryConfigService
) {
this.log = this.loggerSrv.get('SessionComponent');
}
@ViewChild('toolbar', { static: false, read: ViewContainerRef })
set toolbar(reference: ViewContainerRef) {
setTimeout(() => {
this._toolbar = reference;
if (this._toolbar) {
let component = ToolbarComponent;
// Inject the custom component if exists
if (this.libraryConfigSrv.isCustomComponentDefined(LibraryComponents.TOOLBAR)) {
component = this.libraryConfigSrv.getCustomComponent(LibraryComponents.TOOLBAR);
}
this._toolbar?.clear();
this._toolbar.createComponent(component);
}
}, 0);
}
@ViewChild('layout', { static: false, read: ViewContainerRef })
set layout(reference: ViewContainerRef) {
setTimeout(() => {
this._layout = reference;
if (this._layout) {
let component = LayoutComponent;
// Inject the custom component if exists
if (this.libraryConfigSrv.isCustomComponentDefined(LibraryComponents.LAYOUT)) {
component = this.libraryConfigSrv.getCustomComponent(LibraryComponents.LAYOUT);
}
this._layout?.clear();
this._layout.createComponent(component);
this.layoutService.initialize();
}
}, 0);
}
@ViewChild('panel', { static: false, read: ViewContainerRef })
set panel(reference: ViewContainerRef) {
setTimeout(() => {
this._panel = reference;
if (this._panel) {
let component = PanelComponent;
// Inject the custom component if exists
// if (this.libraryConfigSrv.isCustomComponentDefined(LibraryComponents.PANEL)) {
// component = this.libraryConfigSrv.getCustomComponent(LibraryComponents.PANEL);
// }
this._panel?.clear();
this._panel.createComponent(component);
}
}, 0);
}
@HostListener('window:beforeunload')
beforeunloadHandler() {
this.leaveSession();
@ -87,8 +156,6 @@ export class SessionComponent implements OnInit {
}
async ngOnInit() {
this.layoutService.initialize();
if (this.webrtcService.getWebcamSession() === null) {
this.webrtcService.initialize();
await this.webrtcService.initDefaultPublisher(undefined);
@ -113,7 +180,7 @@ export class SessionComponent implements OnInit {
// this.webrtcService.publishVideo(this.localUserService.getMyCameraPublisher(), false);
// }
this._session.emit(this.session);
// this._session.emit(this.session);
}
ngOnDestroy() {
@ -124,8 +191,6 @@ export class SessionComponent implements OnInit {
this.session = null;
this.sessionScreen = null;
this.layoutService.clear();
this.isChatPanelOpened = false;
this.isParticipantsPanelOpened = false;
if (this.menuSubscription) this.menuSubscription.unsubscribe();
if (this.layoutWidthSubscription) this.layoutWidthSubscription.unsubscribe();
}
@ -152,11 +217,7 @@ export class SessionComponent implements OnInit {
});
this.menuSubscription = this.menuService.menuOpenedObs.pipe(skip(1)).subscribe((ev: { opened: boolean; type?: MenuType }) => {
if (this.sideMenu) {
this.isChatPanelOpened = ev.opened && ev.type === MenuType.CHAT;
this.isParticipantsPanelOpened = ev.opened && ev.type === MenuType.PARTICIPANTS;
ev.opened ? this.sideMenu.open() : this.sideMenu.close();
}
this.sideMenu && ev.opened ? this.sideMenu.open() : this.sideMenu.close();
});
}
@ -181,7 +242,7 @@ export class SessionComponent implements OnInit {
await this.webrtcService.publish(this.participantService.getMyCameraPublisher());
}
} catch (error) {
this._error.emit({ error: error.error, messgae: error.message, code: error.code, status: error.status });
// this._error.emit({ error: error.error, messgae: error.message, code: error.code, status: error.status });
this.log.e('There was an error connecting to the session:', error.code, error.message);
this.actionService.openDialog('There was an error connecting to the session:', error?.error || error?.message);
}

View File

@ -52,16 +52,10 @@ export class StreamComponent implements OnInit {
// this.isFullscreenEnabled = !this.isFullscreenEnabled;
// }
// Has been mandatory fullscreen Input because of Input user did not fire changing
// the fullscreen user property in publisherStartSpeaking event in SessionComponent
@Input()
set videoEnlarged(enlarged: boolean) {
this.checkVideoSizeBigIcon(enlarged);
}
@Input()
set participant(participant: StreamModel) {
this._participant = participant;
this.checkVideoSizeBigIcon(this._participant.videoEnlarged);
this.nicknameFormControl = new FormControl(this._participant.nickname, [Validators.maxLength(25), Validators.required]);
}

View File

@ -1,5 +1,5 @@
#toolbar {
height: 100%;
/* height: 100%; */
background-color: transparent;
color: var(--ov-light-color);
}

View File

@ -1,4 +1,15 @@
import { Component, ContentChild, EventEmitter, HostListener, OnDestroy, OnInit, Output, TemplateRef } from '@angular/core';
import {
AfterViewInit,
Component,
ContentChild,
EventEmitter,
HostListener,
OnDestroy,
OnInit,
Output,
TemplateRef,
ViewChild
} from '@angular/core';
import { skip, Subscription } from 'rxjs';
import { TokenService } from '../../services/token/token.service';
import { ChatService } from '../../services/chat/chat.service';
@ -103,10 +114,8 @@ export class ToolbarComponent implements OnInit, OnDestroy {
this.subscribeToUserMediaProperties();
this.subscribeToReconnection();
if(!this.libraryConfigSrv.isUsingProLibrary()){
this.subscribeToMenuToggling();
this.subscribeToChatMessages();
}
this.subscribeToMenuToggling();
this.subscribeToChatMessages();
}
toggleMicrophone() {
@ -268,7 +277,7 @@ export class ToolbarComponent implements OnInit, OnDestroy {
});
}
protected subscribeToMenuToggling() {
this.menuTogglingSubscription = this.menuService.menuOpenedObs.subscribe((ev: {opened: boolean, type?: MenuType}) => {
this.menuTogglingSubscription = this.menuService.menuOpenedObs.subscribe((ev: { opened: boolean; type?: MenuType }) => {
this.isChatOpened = ev.opened && ev.type === MenuType.CHAT;
this.isParticipantsOpened = ev.opened && ev.type === MenuType.PARTICIPANTS;
if (this.isChatOpened) {

View File

@ -1,6 +1,6 @@
<div id="call-container">
<div id="user-settings-container" *ngIf="!joinSessionClicked && !closeClicked">
<ov-user-settings (onJoinClicked)="onJoinClicked()" (onCloseClicked)="onLeaveSessionClicked()"></ov-user-settings>
<ov-user-settings (onJoinClicked)="_onJoinClicked()" (onCloseClicked)="onLeaveSessionClicked()"></ov-user-settings>
</div>
<div id="spinner" *ngIf="joinSessionClicked && !isSessionAlive && !error">
@ -10,21 +10,23 @@
<div id="spinner" *ngIf="joinSessionClicked && !isSessionAlive && error">
<mat-icon class="error-icon">error</mat-icon>
<span>{{errorMessage}}</span>
<span>{{ errorMessage }}</span>
</div>
<div id="session-container" *ngIf="joinSessionClicked && isSessionAlive && !error">
<ov-session [tokens]="_tokens">
<ng-template #toolbar>
<ov-toolbar
<!-- Default toolbar -->
<!--<ng-template #toolbar toolbar>
<ov-toolbar
(onCamClicked)="onCamClicked()"
(onMicClicked)="onMicClicked()"
(onScreenShareClicked)="onScreenShareClicked()"
(onSpeakerLayoutClicked)="onSpeakerLayoutClicked()"
(onLeaveSessionClicked)="onLeaveSessionClicked()"
></ov-toolbar>
</ng-template>
<ov-layout layout></ov-layout>
</ng-template>-->
<!-- <ov-layout layout></ov-layout> -->
</ov-session>
</div>
</div>

View File

@ -1,17 +1,31 @@
import { Component, Input, OnInit } from '@angular/core';
import { RestService } from '../../services/rest/rest.service';
import {
Component,
Input,
OnInit,
Output,
EventEmitter,
ViewChild,
ChangeDetectorRef,
AfterViewInit,
ViewContainerRef
} from '@angular/core';
import { LibraryConfigService } from '../../services/library-config/library-config.service';
import { ToolbarComponent } from '../toolbar/toolbar.component';
@Component({
selector: 'ov-videoconference',
templateUrl: './videoconference.component.html',
styleUrls: ['./videoconference.component.css']
})
export class VideoconferenceComponent implements OnInit {
export class VideoconferenceComponent implements OnInit, AfterViewInit {
@Input() sessionName: string;
@Input() userName: string;
@Input() openviduServerUrl: string;
@Input() openviduSecret: string;
@Input() tokens: { webcam: string; screen: string };
// @Input() tokens: { webcam: string; screen: string };
@Output() onJoinClicked = new EventEmitter<any>();
@Output() onCloseClicked = new EventEmitter<any>();
joinSessionClicked: boolean = false;
closeClicked: boolean = false;
@ -20,42 +34,91 @@ export class VideoconferenceComponent implements OnInit {
error: boolean = false;
errorMessage: string = '';
constructor(private restService: RestService) {}
_toolbar: ViewContainerRef;
ngOnInit() {}
constructor(protected libraryConfigSrv: LibraryConfigService, private cd: ChangeDetectorRef) {}
async onJoinClicked() {
if (!this.tokens || (!this.tokens?.webcam && !this.tokens?.screen)) {
//No tokens received
// @ViewChild('toolbar', { static: false, read: ViewContainerRef })
// set toolbar(reference: ViewContainerRef) {
// setTimeout(() => {
// console.log('setting ref', reference);
// this._toolbar = reference;
if (!!this.sessionName && !!this.openviduServerUrl && !!this.openviduSecret) {
// Generate tokens
this._tokens = {
webcam: await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret),
screen: await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret)
};
} else {
// No tokens received and can't generate them
this.error = true;
this.errorMessage = `Cannot access to OpenVidu Server with url '${this.openviduServerUrl}' to genere tokens for session '${this.sessionName}'`;
throw this.errorMessage;
}
} else if (!this.tokens?.webcam || !this.tokens?.screen) {
// if (this._toolbar) {
// let component = ToolbarComponent;
// if (this.libraryConfigSrv.isCustomComponentDefined('ov-toolbar')) {
// component = this.libraryConfigSrv.getToolbarComponent();
// }
// this._toolbar?.clear();
// this._toolbar.createComponent(component);
// }
// }, 100);
// }
ngAfterViewInit() {
// if(this.customToolbar && this.libraryConfigSrv.isCustomComponentDefined('ov-toolbar')){
// const viewContainerRef = this.customToolbar.viewContainerRef;
// viewContainerRef.clear();
// const componentRef = viewContainerRef.createComponent<any>(this.libraryConfigSrv.getToolbarComponent());
// } else {
// this.customToolbar.viewContainerRef.clear();
// const viewContainerRef = this.toolbar.viewContainerRef;
// viewContainerRef.clear();
// viewContainerRef.createComponent<any>(ToolbarComponent);
// }
}
ngOnInit() {
}
@Input('tokens')
set tokens(tokens: { webcam: string; screen: string }) {
if (!!tokens?.webcam || !!this.tokens?.screen) {
// 1 token received
const aditionalToken = await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret);
this._tokens = {
webcam: !!this.tokens.webcam ? this.tokens.webcam : aditionalToken,
screen: !!this.tokens.screen ? this.tokens.screen : aditionalToken
};
this.cd.detectChanges();
// this.cd.markForCheck();
this._tokens = tokens;
this.joinSessionClicked = true;
this.isSessionAlive = true;
} else {
// 2 tokens received.
this._tokens = {
webcam: this.tokens.webcam,
screen: this.tokens.screen
};
//No tokens received
throw new Error('No tokens received');
}
this.joinSessionClicked = true;
this.isSessionAlive = true;
}
async _onJoinClicked() {
this.onJoinClicked.emit();
// if (!this.tokens || (!this.tokens?.webcam && !this.tokens?.screen)) {
// //No tokens received
// if (!!this.sessionName && !!this.openviduServerUrl && !!this.openviduSecret) {
// // Generate tokens
// this._tokens = {
// webcam: await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret),
// screen: await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret)
// };
// } else {
// // No tokens received and can't generate them
// this.error = true;
// this.errorMessage = `Cannot access to OpenVidu Server with url '${this.openviduServerUrl}' to genere tokens for session '${this.sessionName}'`;
// throw this.errorMessage;
// }
// } else if (!this.tokens?.webcam || !this.tokens?.screen) {
// // 1 token received
// const aditionalToken = await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret);
// this._tokens = {
// webcam: !!this.tokens.webcam ? this.tokens.webcam : aditionalToken,
// screen: !!this.tokens.screen ? this.tokens.screen : aditionalToken
// };
// } else {
// // 2 tokens received.
// this._tokens = {
// webcam: this.tokens.webcam,
// screen: this.tokens.screen
// };
// }
// this.joinSessionClicked = true;
// this.isSessionAlive = true;
}
onLeaveSessionClicked() {
this.isSessionAlive = false;

View File

@ -1,6 +1,19 @@
import { Type } from '@angular/core';
export interface LibConfig {
environment: {
production: boolean;
useProdLibrary?: boolean
useProdLibrary?: boolean;
customComponents?: { LibraryComponents: Type<any> };
};
}
export enum LibraryComponents {
TOOLBAR = 'ov-toolbar',
LAYOUT = 'ov-layout',
PANEL = 'ov-panel',
CHAT_PANEL = 'ov-chat-panel',
PARTICIPANTS_PANEL = 'ov-participants-panel',
STREAM = "ov-stream"
}

View File

@ -29,7 +29,7 @@ import { HttpClientModule } from '@angular/common/http';
import { UserSettingsComponent } from './components/user-settings/user-settings.component';
import { ToolbarComponent } from './components/toolbar/toolbar.component';
import { VideoComponent } from './components/video/video.component';
import { ChatPanelComponent } from './components/chat-panel/chat-panel.component';
import { ChatPanelComponent } from './components/panel/chat-panel/chat-panel.component';
import { SessionComponent } from './components/session/session.component';
import { LayoutComponent } from './components/layout/layout.component';
import { StreamComponent } from './components/stream/stream.component';
@ -54,9 +54,14 @@ import { DocumentService } from './services/document/document.service';
import { LayoutService } from './services/layout/layout.service';
import { SidenavMenuService } from './services/sidenav-menu/sidenav-menu.service';
import { ParticipantService } from './services/participant/participant.service';
import { ParticipantItemComponent } from './components/participants-panel/participant-item/participant-item.component';
import { ParticipantsPanelComponent } from './components/participants-panel/participants-panel/participants-panel.component';
import { ParticipantItemComponent } from './components/panel/participants-panel/participant-item/participant-item.component';
import { ParticipantsPanelComponent } from './components/panel/participants-panel/participants-panel/participants-panel.component';
import { VideoconferenceComponent } from './components/videoconference/videoconference.component';
import { PanelComponent } from './components/panel/panel.component';
// Used for loading dynamic components because of Inputs and Outputs are not supported in the official Angular way
// https://github.com/angular/angular/issues/15360
import { DynamicIoModule } from 'ng-dynamic-component';
@NgModule({
declarations: [
@ -75,7 +80,8 @@ import { VideoconferenceComponent } from './components/videoconference/videoconf
NicknamePipe,
ParticipantItemComponent,
ParticipantsPanelComponent,
VideoconferenceComponent
VideoconferenceComponent,
PanelComponent,
],
imports: [
CommonModule,
@ -102,7 +108,8 @@ import { VideoconferenceComponent } from './components/videoconference/videoconf
FlexLayoutModule,
MatMenuModule,
MatDividerModule,
MatListModule
MatListModule,
DynamicIoModule
],
providers: [
ActionService,

View File

@ -5,12 +5,12 @@ import { LibConfig } from '../../config/lib.config';
@Injectable()
export class LibraryConfigService {
private configuration: LibConfig;
constructor(@Inject('LIB_CONFIG') config: LibConfig) {
this.configuration = config;
console.log(this.configuration);
this.isUsingProLibrary() ? console.log('Using PRO library') : console.log('Using CE library');
if(this.isProduction()) console.log('Production Mode');
// console.log(version)
}
@ -22,7 +22,11 @@ export class LibraryConfigService {
return this.configuration?.environment?.production;
}
isUsingProLibrary(): boolean {
return !!this.configuration?.environment?.useProdLibrary;
isCustomComponentDefined(component: string): boolean {
return !!this.configuration?.environment?.customComponents && !!this.configuration.environment.customComponents[component];
}
getCustomComponent(component: string){
return this.configuration.environment.customComponents[component];
}
}

View File

@ -24,8 +24,8 @@ export * from './lib/services/storage/storage.service';
export * from './lib/components/videoconference/videoconference.component';
export * from './lib/components/user-settings/user-settings.component';
export * from './lib/components/toolbar/toolbar.component';
export * from './lib/components/chat-panel/chat-panel.component';
export * from './lib/components/participants-panel/participants-panel/participants-panel.component';
export * from './lib/components/panel/chat-panel/chat-panel.component';
export * from './lib/components/panel/participants-panel/participants-panel/participants-panel.component';
export * from './lib/components/session/session.component';
export * from './lib/components/layout/layout.component';
export * from './lib/components/stream/stream.component';