mirror of https://github.com/OpenVidu/openvidu.git
openvidu-componentS: Decoupled panels from openvidu layout
parent
9f9f09c8c5
commit
e16816422c
|
@ -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;
|
||||
|
|
|
@ -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">
|
||||
|
||||
<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>
|
||||
|
@ -62,6 +32,4 @@
|
|||
<ov-stream [participant]="connection" [videoEnlarged]="connection.videoEnlarged"></ov-stream>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</mat-sidenav-content>
|
||||
</mat-sidenav-container>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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%;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue