openvidu-componentS: Decoupled panels from openvidu layout

pull/690/head
csantosm 2022-01-26 16:01:12 +01:00
parent 9f9f09c8c5
commit e16816422c
6 changed files with 176 additions and 183 deletions

View File

@ -2,34 +2,6 @@
height: 100%;
}
.sidenav-container {
position: relative;
width: 100%;
height: 100%;
min-height: -webkit-fill-available;
overflow: hidden;
}
.sidenav-menu {
display: flex;
align-items: center;
justify-content: center;
width: 360px;
background-color: var(--ov-primary-color);
border-left: none;
position: absolute;
}
.mat-drawer-container {
background-color: var(--ov-primary-color);
}
.sidenav-main {
height: 100%;
overflow: hidden;
min-height: -webkit-fill-available;
min-height: -moz-available;
}
.bounds {
position: absolute;

View File

@ -1,29 +1,4 @@
<mat-sidenav-container class="sidenav-container">
<mat-sidenav
#sidenav
mode="{{ sidenavMode }}"
position="end"
class="sidenav-menu"
fixedInViewport="true"
fixedTopGap="0"
fixedBottomGap="0"
>
<!-- Custom menu content -->
<ng-container *ngIf="customMenuContentTemplate; else defaultMenuContent">
<ng-container *ngTemplateOutlet="customMenuContentTemplate"></ng-container>
</ng-container>
<!-- Default menu content if custom menu content is not injected -->
<ng-template #defaultMenuContent>
<ov-chat-panel *ngIf="isChatOpened"></ov-chat-panel>
<ov-participants-panel *ngIf="isParticipantsOpened"></ov-participants-panel>
</ng-template>
</mat-sidenav>
<mat-sidenav-content class="sidenav-main">
<div id="layout" class="bounds">
<!-- Custom local participant -->
<ng-container *ngIf="customLocalParticipantTemplate; else defaultLocalParticipant">
<ng-container *ngTemplateOutlet="customLocalParticipantTemplate"></ng-container>
@ -41,11 +16,6 @@
</div>
</ng-template>
<!-- Custom layout elements -->
<ng-container *ngIf="customLayoutElementTemplate">
<ng-container *ngTemplateOutlet="customLayoutElementTemplate" [@inOutAnimation]></ng-container>
</ng-container>
<!-- Custom remote participant -->
<ng-container *ngIf="customRemoteParticipantsTemplate; else defaultRemoteParticipants">
<ng-container *ngTemplateOutlet="customRemoteParticipantsTemplate"></ng-container>
@ -63,5 +33,3 @@
</div>
</ng-template>
</div>
</mat-sidenav-content>
</mat-sidenav-container>

View File

@ -1,12 +1,8 @@
import { AfterViewInit, Component, ContentChild, HostListener, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { MatSidenav } from '@angular/material/sidenav';
import { skip, Subscription } from 'rxjs';
import { SidenavMode } from '../../models/layout.model';
import { LayoutService } from '../../services/layout/layout.service';
import { AfterViewInit, Component, ContentChild, OnDestroy, OnInit, TemplateRef } from '@angular/core';
import { Subscription } from 'rxjs';
import { ParticipantService } from '../../services/participant/participant.service';
import { SidenavMenuService } from '../../services/sidenav-menu/sidenav-menu.service';
import { ParticipantAbstractModel } from '../../models/participant.model';
import { MenuType } from '../../models/menu.model';
import { LayoutService } from '../../services/layout/layout.service';
@Component({
selector: 'ov-layout',
@ -16,96 +12,28 @@ import { MenuType } from '../../models/menu.model';
export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
@ContentChild('customLocalParticipant', { read: TemplateRef }) customLocalParticipantTemplate: TemplateRef<any>;
@ContentChild('customRemoteParticipants', { read: TemplateRef }) customRemoteParticipantsTemplate: TemplateRef<any>;
@ContentChild('customMenuContent', { read: TemplateRef }) customMenuContentTemplate: TemplateRef<any>;
@ContentChild('customLayoutElement', { read: TemplateRef }) customLayoutElementTemplate: TemplateRef<any>;
showCustomParticipant = true;
sideMenu: MatSidenav;
localParticipant: ParticipantAbstractModel;
remoteParticipants: ParticipantAbstractModel[] = [];
sidenavMode: SidenavMode = SidenavMode.SIDE;
isParticipantsOpened: boolean;
isChatOpened: boolean;
protected readonly SIDENAV_WIDTH_LIMIT_MODE = 790;
protected menuSubscription: Subscription;
protected layoutWidthSubscription: Subscription;
protected localParticipantSubs: Subscription;
protected remoteParticipantsSubs: Subscription;
protected updateLayoutInterval: NodeJS.Timer;
@HostListener('window:resize')
sizeChange() {
this.layoutService.update();
}
constructor(
protected participantService: ParticipantService,
protected layoutService: LayoutService,
protected menuService: SidenavMenuService
) {}
@ViewChild('sidenav')
set sidenavMenu(menu: MatSidenav) {
setTimeout(() => {
if (menu) {
this.sideMenu = menu;
this.subscribeToTogglingMenu();
}
}, 0);
}
constructor(protected layoutService: LayoutService, protected participantService: ParticipantService) {}
ngOnInit(): void {
this.layoutService.initialize();
this.subscribeToLayoutWidth();
this.subscribeToUsers();
}
ngAfterViewInit() {}
ngOnDestroy() {
this.layoutService.clear();
this.localParticipant = null;
this.remoteParticipants = [];
this.isChatOpened = false;
this.isParticipantsOpened = false;
if (this.menuSubscription) this.menuSubscription.unsubscribe();
if (this.layoutWidthSubscription) this.layoutWidthSubscription.unsubscribe();
if (this.localParticipantSubs) this.localParticipantSubs.unsubscribe();
if (this.remoteParticipantsSubs) this.remoteParticipantsSubs.unsubscribe();
}
protected subscribeToTogglingMenu() {
this.sideMenu.openedChange.subscribe(() => {
if(this.updateLayoutInterval) {
clearInterval(this.updateLayoutInterval);
}
this.layoutService.update();
});
this.sideMenu.openedStart.subscribe(() => {
this.updateLayoutInterval = setInterval(() => this.layoutService.update(), 50);
});
this.sideMenu.closedStart.subscribe(() => {
this.updateLayoutInterval = setInterval(() => this.layoutService.update(), 50);
});
this.menuSubscription = this.menuService.menuOpenedObs.pipe(skip(1)).subscribe((ev: { opened: boolean; type?: MenuType }) => {
if (this.sideMenu) {
this.isChatOpened = ev.opened && ev.type === MenuType.CHAT;
this.isParticipantsOpened = ev.opened && ev.type === MenuType.PARTICIPANTS;
ev.opened ? this.sideMenu.open() : this.sideMenu.close();
}
});
}
protected subscribeToLayoutWidth() {
this.layoutWidthSubscription = this.layoutService.layoutWidthObs.subscribe((width) => {
this.sidenavMode = width <= this.SIDENAV_WIDTH_LIMIT_MODE ? SidenavMode.OVER : SidenavMode.SIDE;
});
}
protected subscribeToUsers() {
this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p) => {
this.localParticipant = p;

View File

@ -4,6 +4,36 @@
height: 100%;
}
.sidenav-container {
position: relative;
height: calc(100% - 80px);
min-height: calc(100% - 80px);
padding-top: 10px;
width: 100%;
overflow: hidden;
}
.sidenav-menu {
display: flex;
align-items: center;
justify-content: center;
width: 360px;
background-color: var(--ov-primary-color);
border-left: none;
position: absolute;
}
.sidenav-main {
height: 100%;
overflow: hidden;
min-height: -webkit-fill-available;
min-height: -moz-available;
}
.mat-drawer-container {
background-color: var(--ov-primary-color);
}
#toolbar-container, #footer-container {
background-color: var(--ov-primary-color);
min-width: 400px !important;
@ -24,11 +54,6 @@
}
#layout-container {
height: calc(100% - 80px);
padding-top: 10px;
}
.reconnecting-container{
width: 100%;

View File

@ -1,4 +1,34 @@
<div id="session-container">
<mat-sidenav-container class="sidenav-container">
<!-- OPENVIDU PANELS -->
<mat-sidenav
#sidenav
mode="{{ sidenavMode }}"
position="end"
class="sidenav-menu"
fixedInViewport="true"
fixedTopGap="0"
fixedBottomGap="0"
>
<!-- Custom menu content -->
<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>
<ov-chat-panel *ngIf="isChatPanelOpened"></ov-chat-panel>
<ov-participants-panel *ngIf="isParticipantsPanelOpened"></ov-participants-panel>
</ng-template>
</mat-sidenav>
<!-- OPENVIDU LAYOUT -->
<mat-sidenav-content class="sidenav-main">
<div id="layout-container">
<ng-content select="[layout]"></ng-content>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
<!-- Custom toolbar -->
<!-- <ng-container *ngIf="toolbarTemplate">
@ -7,15 +37,9 @@
</div>
</ng-container> -->
<div id="layout-container">
<ng-content select="[layout]"></ng-content>
</div>
<ng-container *ngIf="toolbarTemplate">
<div id="footer-container">
<ng-container *ngTemplateOutlet="toolbarTemplate"></ng-container>
</div>
</ng-container>
</div>

View File

@ -1,4 +1,4 @@
import { Component, ContentChild, EventEmitter, HostListener, Input, OnInit, Output, TemplateRef } from '@angular/core';
import { Component, ContentChild, EventEmitter, HostListener, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core';
import { Subscriber, Session, StreamEvent, StreamPropertyChangedEvent, SessionDisconnectedEvent, ConnectionEvent } from 'openvidu-browser';
import { VideoType } from '../../models/video-type.model';
@ -12,6 +12,12 @@ 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';
import { MatSidenav } from '@angular/material/sidenav';
import { SidenavMode } from '../../models/layout.model';
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';
@Component({
selector: 'ov-session',
@ -20,6 +26,9 @@ import { ParticipantService } from '../../services/participant/participant.servi
})
export class SessionComponent implements OnInit {
@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>();
@ -27,6 +36,19 @@ export class SessionComponent implements OnInit {
session: Session;
sessionScreen: Session;
sideMenu: MatSidenav;
sidenavMode: SidenavMode = SidenavMode.SIDE;
isParticipantsPanelOpened: boolean;
isChatPanelOpened: boolean;
protected readonly SIDENAV_WIDTH_LIMIT_MODE = 790;
protected menuSubscription: Subscription;
protected layoutWidthSubscription: Subscription;
protected updateLayoutInterval: NodeJS.Timer;
protected log: ILogger;
constructor(
@ -36,6 +58,9 @@ export class SessionComponent implements OnInit {
protected loggerSrv: LoggerService,
protected chatService: ChatService,
protected tokenService: TokenService,
protected layoutService: LayoutService,
protected menuService: SidenavMenuService,
protected platformService: PlatformService
) {
this.log = this.loggerSrv.get('SessionComponent');
@ -46,7 +71,24 @@ export class SessionComponent implements OnInit {
this.leaveSession();
}
@HostListener('window:resize')
sizeChange() {
this.layoutService.update();
}
@ViewChild('sidenav')
set sidenavMenu(menu: MatSidenav) {
setTimeout(() => {
if (menu) {
this.sideMenu = menu;
this.subscribeToTogglingMenu();
}
}, 0);
}
async ngOnInit() {
this.layoutService.initialize();
if (this.webrtcService.getWebcamSession() === null) {
this.webrtcService.initialize();
await this.webrtcService.initDefaultPublisher(undefined);
@ -81,6 +123,11 @@ export class SessionComponent implements OnInit {
this.participantService.clear();
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();
}
leaveSession() {
@ -88,6 +135,37 @@ export class SessionComponent implements OnInit {
this.webrtcService.disconnect();
}
protected subscribeToTogglingMenu() {
this.sideMenu.openedChange.subscribe(() => {
if (this.updateLayoutInterval) {
clearInterval(this.updateLayoutInterval);
}
this.layoutService.update();
});
this.sideMenu.openedStart.subscribe(() => {
this.updateLayoutInterval = setInterval(() => this.layoutService.update(), 50);
});
this.sideMenu.closedStart.subscribe(() => {
this.updateLayoutInterval = setInterval(() => this.layoutService.update(), 50);
});
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();
}
});
}
protected subscribeToLayoutWidth() {
this.layoutWidthSubscription = this.layoutService.layoutWidthObs.subscribe((width) => {
this.sidenavMode = width <= this.SIDENAV_WIDTH_LIMIT_MODE ? SidenavMode.OVER : SidenavMode.SIDE;
});
}
private async connectToSession(): Promise<void> {
try {
if (this.participantService.areBothEnabled()) {
@ -117,7 +195,6 @@ export class SessionComponent implements OnInit {
const isCameraConnection: boolean = !nickname?.includes(`_${VideoType.SCREEN}`);
const data = event.connection?.data;
if (isRemoteConnection && isCameraConnection) {
// Adding participant when connection is created and it's not screen
this.participantService.addRemoteConnection(connectionId, data, null);
@ -167,7 +244,6 @@ export class SessionComponent implements OnInit {
// this.session.on('streamPropertyChanged', (event: StreamPropertyChangedEvent) => {
// const connectionId = event.stream.connection.connectionId;
// const isRemoteConnection: boolean = !this.webrtcService.isMyOwnConnection(connectionId);
// if (isRemoteConnection) {
// if (event.changedProperty === 'videoActive') {
// // this.participantService.updateUsers();