2022-02-24 10:20:32 +01:00
|
|
|
import { ChangeDetectionStrategy, Component, ContentChild, EventEmitter, HostListener, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core';
|
2022-01-19 17:24:11 +01:00
|
|
|
import { Subscriber, Session, StreamEvent, StreamPropertyChangedEvent, SessionDisconnectedEvent, ConnectionEvent } from 'openvidu-browser';
|
|
|
|
|
|
|
|
import { VideoType } from '../../models/video-type.model';
|
|
|
|
import { ILogger } from '../../models/logger.model';
|
|
|
|
|
|
|
|
import { ChatService } from '../../services/chat/chat.service';
|
|
|
|
import { LoggerService } from '../../services/logger/logger.service';
|
2022-02-15 15:52:59 +01:00
|
|
|
import { OpenViduService } from '../../services/openvidu/openvidu.service';
|
2022-01-19 17:24:11 +01:00
|
|
|
import { TokenService } from '../../services/token/token.service';
|
|
|
|
import { ActionService } from '../../services/action/action.service';
|
|
|
|
import { Signal } from '../../models/signal.model';
|
|
|
|
import { ParticipantService } from '../../services/participant/participant.service';
|
2022-01-26 16:01:12 +01:00
|
|
|
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';
|
2022-01-19 17:24:11 +01:00
|
|
|
|
|
|
|
@Component({
|
2022-01-26 11:30:30 +01:00
|
|
|
selector: 'ov-session',
|
|
|
|
templateUrl: './session.component.html',
|
2022-02-24 10:20:32 +01:00
|
|
|
styleUrls: ['./session.component.css'],
|
|
|
|
changeDetection: ChangeDetectionStrategy.OnPush
|
2022-01-19 17:24:11 +01:00
|
|
|
})
|
2022-02-24 10:20:32 +01:00
|
|
|
export class SessionComponent implements OnInit {
|
2022-02-17 17:26:30 +01:00
|
|
|
@ContentChild('toolbar', { read: TemplateRef }) toolbarTemplate: TemplateRef<any>;
|
|
|
|
@ContentChild('panel', { read: TemplateRef }) panelTemplate: TemplateRef<any>;
|
2022-02-03 17:08:23 +01:00
|
|
|
@ContentChild('layout', { read: TemplateRef }) layoutTemplate: TemplateRef<any>;
|
2022-01-26 16:01:12 +01:00
|
|
|
|
2022-01-19 17:24:11 +01:00
|
|
|
@Input() tokens: { webcam: string; screen: string };
|
|
|
|
@Output() _session = new EventEmitter<any>();
|
|
|
|
@Output() _publisher = new EventEmitter<any>();
|
|
|
|
@Output() _error = new EventEmitter<any>();
|
|
|
|
|
|
|
|
session: Session;
|
|
|
|
sessionScreen: Session;
|
2022-01-26 16:01:12 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2022-01-19 17:24:11 +01:00
|
|
|
protected log: ILogger;
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
protected actionService: ActionService,
|
2022-02-15 15:52:59 +01:00
|
|
|
protected openviduService: OpenViduService,
|
2022-01-19 17:24:11 +01:00
|
|
|
protected participantService: ParticipantService,
|
|
|
|
protected loggerSrv: LoggerService,
|
|
|
|
protected chatService: ChatService,
|
|
|
|
protected tokenService: TokenService,
|
2022-01-26 16:01:12 +01:00
|
|
|
protected layoutService: LayoutService,
|
|
|
|
protected menuService: SidenavMenuService,
|
2022-01-19 17:24:11 +01:00
|
|
|
) {
|
2022-01-26 11:30:30 +01:00
|
|
|
this.log = this.loggerSrv.get('SessionComponent');
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@HostListener('window:beforeunload')
|
|
|
|
beforeunloadHandler() {
|
|
|
|
this.leaveSession();
|
|
|
|
}
|
|
|
|
|
2022-01-26 16:01:12 +01:00
|
|
|
@HostListener('window:resize')
|
|
|
|
sizeChange() {
|
|
|
|
this.layoutService.update();
|
|
|
|
}
|
|
|
|
|
|
|
|
@ViewChild('sidenav')
|
|
|
|
set sidenavMenu(menu: MatSidenav) {
|
|
|
|
setTimeout(() => {
|
|
|
|
if (menu) {
|
|
|
|
this.sideMenu = menu;
|
|
|
|
this.subscribeToTogglingMenu();
|
|
|
|
}
|
|
|
|
}, 0);
|
|
|
|
}
|
|
|
|
|
2022-01-19 17:24:11 +01:00
|
|
|
async ngOnInit() {
|
2022-01-26 16:01:12 +01:00
|
|
|
|
2022-02-15 15:52:59 +01:00
|
|
|
if (this.openviduService.getWebcamSession() === null) {
|
|
|
|
this.openviduService.initialize();
|
|
|
|
await this.openviduService.initDefaultPublisher(undefined);
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
2022-02-15 15:52:59 +01:00
|
|
|
this.session = this.openviduService.getWebcamSession();
|
|
|
|
this.sessionScreen = this.openviduService.getScreenSession();
|
2022-01-19 17:24:11 +01:00
|
|
|
this.subscribeToConnectionCreatedAndDestroyed();
|
|
|
|
this.subscribeToStreamCreated();
|
|
|
|
this.subscribeToStreamDestroyed();
|
|
|
|
this.subscribeToStreamPropertyChange();
|
|
|
|
this.subscribeToNicknameChanged();
|
|
|
|
this.chatService.subscribeToChat();
|
|
|
|
this.subscribeToReconnection();
|
|
|
|
|
|
|
|
this.tokenService.setWebcamToken(this.tokens.webcam);
|
|
|
|
this.tokenService.setScreenToken(this.tokens.screen);
|
|
|
|
|
|
|
|
await this.connectToSession();
|
|
|
|
// Workaround, firefox does not have audio when publisher join with muted camera
|
|
|
|
// if (this.platformService.isFirefox() && !this.localUserService.hasCameraVideoActive()) {
|
2022-02-15 15:52:59 +01:00
|
|
|
// this.openviduService.publishVideo(this.localUserService.getMyCameraPublisher(), true);
|
|
|
|
// this.openviduService.publishVideo(this.localUserService.getMyCameraPublisher(), false);
|
2022-01-19 17:24:11 +01:00
|
|
|
// }
|
|
|
|
|
|
|
|
this._session.emit(this.session);
|
|
|
|
}
|
|
|
|
|
|
|
|
ngOnDestroy() {
|
|
|
|
// Reconnecting session is received in Firefox
|
|
|
|
// To avoid 'Connection lost' message uses session.off()
|
|
|
|
this.session?.off('reconnecting');
|
|
|
|
this.participantService.clear();
|
|
|
|
this.session = null;
|
|
|
|
this.sessionScreen = null;
|
2022-01-26 16:01:12 +01:00
|
|
|
this.isChatPanelOpened = false;
|
|
|
|
this.isParticipantsPanelOpened = false;
|
|
|
|
if (this.menuSubscription) this.menuSubscription.unsubscribe();
|
|
|
|
if (this.layoutWidthSubscription) this.layoutWidthSubscription.unsubscribe();
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
leaveSession() {
|
|
|
|
this.log.d('Leaving session...');
|
2022-02-15 15:52:59 +01:00
|
|
|
this.openviduService.disconnect();
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
|
2022-01-26 16:01:12 +01:00
|
|
|
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;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-01-19 17:24:11 +01:00
|
|
|
private async connectToSession(): Promise<void> {
|
|
|
|
try {
|
2022-02-25 11:19:21 +01:00
|
|
|
if (this.participantService.haveICameraAndScreenActive()) {
|
2022-02-15 15:52:59 +01:00
|
|
|
await this.openviduService.connectSession(this.openviduService.getWebcamSession(), this.tokenService.getWebcamToken());
|
|
|
|
await this.openviduService.connectSession(this.openviduService.getScreenSession(), this.tokenService.getScreenToken());
|
|
|
|
await this.openviduService.publish(this.participantService.getMyCameraPublisher());
|
|
|
|
await this.openviduService.publish(this.participantService.getMyScreenPublisher());
|
2022-02-25 11:19:21 +01:00
|
|
|
} else if (this.participantService.isOnlyMyScreenActive()) {
|
2022-02-15 15:52:59 +01:00
|
|
|
await this.openviduService.connectSession(this.openviduService.getScreenSession(), this.tokenService.getScreenToken());
|
|
|
|
await this.openviduService.publish(this.participantService.getMyScreenPublisher());
|
2022-01-19 17:24:11 +01:00
|
|
|
} else {
|
2022-02-15 15:52:59 +01:00
|
|
|
await this.openviduService.connectSession(this.openviduService.getWebcamSession(), this.tokenService.getWebcamToken());
|
|
|
|
await this.openviduService.publish(this.participantService.getMyCameraPublisher());
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private subscribeToConnectionCreatedAndDestroyed() {
|
|
|
|
this.session.on('connectionCreated', (event: ConnectionEvent) => {
|
|
|
|
const connectionId = event.connection?.connectionId;
|
|
|
|
const nickname: string = this.participantService.getNicknameFromConnectionData(event.connection.data);
|
2022-02-15 15:52:59 +01:00
|
|
|
const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(connectionId);
|
2022-01-19 17:24:11 +01:00
|
|
|
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);
|
|
|
|
|
|
|
|
//Sending nicnkanme signal to new participants
|
2022-02-15 15:52:59 +01:00
|
|
|
if (this.openviduService.needSendNicknameSignal()) {
|
2022-01-19 17:24:11 +01:00
|
|
|
const data = { clientData: this.participantService.getWebcamNickname() };
|
2022-02-15 15:52:59 +01:00
|
|
|
this.openviduService.sendSignal(Signal.NICKNAME_CHANGED, [event.connection], data);
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
this.session.on('connectionDestroyed', (event: ConnectionEvent) => {
|
|
|
|
const nickname: string = this.participantService.getNicknameFromConnectionData(event.connection.data);
|
2022-02-15 15:52:59 +01:00
|
|
|
const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(event.connection.connectionId);
|
2022-01-19 17:24:11 +01:00
|
|
|
const isCameraConnection: boolean = !nickname?.includes(`_${VideoType.SCREEN}`);
|
|
|
|
// Deleting participant when connection is destroyed
|
|
|
|
if (isRemoteConnection && isCameraConnection) {
|
|
|
|
this.participantService.removeConnectionByConnectionId(event.connection.connectionId);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private subscribeToStreamCreated() {
|
|
|
|
this.session.on('streamCreated', (event: StreamEvent) => {
|
|
|
|
const connectionId = event.stream?.connection?.connectionId;
|
|
|
|
const data = event.stream?.connection?.data;
|
|
|
|
|
2022-02-15 15:52:59 +01:00
|
|
|
const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(connectionId);
|
2022-01-19 17:24:11 +01:00
|
|
|
if (isRemoteConnection) {
|
|
|
|
const subscriber: Subscriber = this.session.subscribe(event.stream, undefined);
|
|
|
|
this.participantService.addRemoteConnection(connectionId, data, subscriber);
|
|
|
|
// this.oVSessionService.sendNicknameSignal(event.stream.connection);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private subscribeToStreamDestroyed() {
|
|
|
|
this.session.on('streamDestroyed', (event: StreamEvent) => {
|
|
|
|
const connectionId = event.stream.connection.connectionId;
|
|
|
|
this.participantService.removeConnectionByConnectionId(connectionId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private subscribeToStreamPropertyChange() {
|
|
|
|
// this.session.on('streamPropertyChanged', (event: StreamPropertyChangedEvent) => {
|
|
|
|
// const connectionId = event.stream.connection.connectionId;
|
2022-02-15 15:52:59 +01:00
|
|
|
// const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(connectionId);
|
2022-01-19 17:24:11 +01:00
|
|
|
// if (isRemoteConnection) {
|
|
|
|
// if (event.changedProperty === 'videoActive') {
|
|
|
|
// // this.participantService.updateUsers();
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// });
|
|
|
|
}
|
|
|
|
|
|
|
|
private subscribeToNicknameChanged() {
|
|
|
|
this.session.on(`signal:${Signal.NICKNAME_CHANGED}`, (event: any) => {
|
|
|
|
const connectionId = event.from.connectionId;
|
2022-02-15 15:52:59 +01:00
|
|
|
const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(connectionId);
|
2022-01-19 17:24:11 +01:00
|
|
|
|
|
|
|
if (isRemoteConnection) {
|
|
|
|
const nickname = this.participantService.getNicknameFromConnectionData(event.data);
|
|
|
|
this.participantService.setNickname(connectionId, nickname);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private subscribeToReconnection() {
|
|
|
|
this.session.on('reconnecting', () => {
|
|
|
|
this.log.w('Connection lost: Reconnecting');
|
|
|
|
this.actionService.openDialog('Connection Problem', 'Oops! Trying to reconnect to the session ...', false);
|
|
|
|
});
|
|
|
|
this.session.on('reconnected', () => {
|
|
|
|
this.log.w('Connection lost: Reconnected');
|
|
|
|
this.actionService.closeDialog();
|
|
|
|
});
|
|
|
|
this.session.on('sessionDisconnected', (event: SessionDisconnectedEvent) => {
|
|
|
|
if (event.reason === 'networkDisconnect') {
|
|
|
|
this.actionService.closeDialog();
|
|
|
|
this.leaveSession();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|