openvidu-components: Added prejoin drective

- Hide/show prejoin page
- Refactored prejoin component
- Moved the participant initialization to videoconference
- Set necessary delay for the correct layout initialization
pull/707/head
csantosm 2022-03-10 14:12:43 +01:00
parent ab74818579
commit 7eedba795b
10 changed files with 160 additions and 98 deletions

View File

@ -50,10 +50,10 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
}
ngAfterViewInit() {
let timeout: number = null;
if (this.libService.isWebcomponent()) {
timeout = 0;
}
let timeout: number = 0;
// if (this.libService.isWebcomponent()) {
// timeout = 0;
// }
this.layoutService.initialize(timeout);
this.layoutService.update(timeout);

View File

@ -26,7 +26,6 @@ export class PreJoinComponent implements OnInit, OnDestroy {
microphoneSelected: CustomDevice;
isVideoMuted: boolean;
isAudioMuted: boolean;
screenShareEnabled: boolean;
localParticipant: ParticipantAbstractModel;
windowSize: number;
hasVideoDevices: boolean;
@ -45,7 +44,6 @@ export class PreJoinComponent implements OnInit, OnDestroy {
constructor(
private layoutService: LayoutService,
private actionService: ActionService,
private deviceSrv: DeviceService,
private loggerSrv: LoggerService,
private openviduService: OpenViduService,
@ -55,22 +53,20 @@ export class PreJoinComponent implements OnInit, OnDestroy {
this.log = this.loggerSrv.get('PreJoinComponent');
}
async ngOnInit() {
await this.deviceSrv.initializeDevices();
this.nickname = this.storageSrv.getNickname() || this.generateRandomNickname();
const props: ParticipantProperties = {
local: true,
nickname: this.nickname
};
this.participantService.initLocalParticipant(props);
ngOnInit() {
this.subscribeToLocalParticipantEvents();
this.openviduService.initialize();
this.windowSize = window.innerWidth;
this.setDevicesInfo();
if (this.hasAudioDevices || this.hasVideoDevices) {
await this.initwebcamPublisher();
}
this.hasVideoDevices = this.deviceSrv.hasVideoDeviceAvailable();
this.hasAudioDevices = this.deviceSrv.hasAudioDeviceAvailable();
this.microphones = this.deviceSrv.getMicrophones();
this.cameras = this.deviceSrv.getCameras();
this.cameraSelected = this.deviceSrv.getCameraSelected();
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected();
this.isVideoMuted = this.deviceSrv.isVideoMuted();
this.isAudioMuted = this.deviceSrv.isAudioMuted();
this.isLoading = false;
}
@ -201,7 +197,7 @@ export class PreJoinComponent implements OnInit, OnDestroy {
}
updateNickname() {
this.nickname = this.nickname === '' ? this.generateRandomNickname() : this.nickname;
this.nickname = this.nickname === '' ? this.participantService.getMyNickname() : this.nickname;
this.participantService.setMyNickname(this.nickname);
this.storageSrv.setNickname(this.nickname);
}
@ -210,33 +206,12 @@ export class PreJoinComponent implements OnInit, OnDestroy {
this.onJoinButtonClicked.emit();
}
private setDevicesInfo() {
this.hasVideoDevices = this.deviceSrv.hasVideoDeviceAvailable();
this.hasAudioDevices = this.deviceSrv.hasAudioDeviceAvailable();
this.microphones = this.deviceSrv.getMicrophones();
this.cameras = this.deviceSrv.getCameras();
this.cameraSelected = this.deviceSrv.getCameraSelected();
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected();
this.isVideoMuted = this.deviceSrv.isVideoMuted();
this.isAudioMuted = this.deviceSrv.isAudioMuted();
}
private subscribeToLocalParticipantEvents() {
this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p) => {
this.localParticipant = p;
this.screenShareEnabled = p.isScreenActive();
});
}
private async initwebcamPublisher() {
const publisher = await this.openviduService.initDefaultPublisher(undefined);
if (publisher) {
// this.handlePublisherSuccess(publisher);
this.handlePublisherError(publisher);
}
}
//? After test in Chrome and Firefox, the devices always have labels.
//? It's not longer needed
// private handlePublisherSuccess(publisher: Publisher) {
@ -256,33 +231,4 @@ export class PreJoinComponent implements OnInit, OnDestroy {
// }
// });
// }
private handlePublisherError(publisher: Publisher) {
publisher.once('accessDenied', (e: any) => {
let message: string;
if (e.name === OpenViduErrorName.DEVICE_ALREADY_IN_USE) {
this.log.w('Video device already in use. Disabling video device...');
// Allow access to the room with only mic if camera device is already in use
this.hasVideoDevices = false;
this.deviceSrv.disableVideoDevices();
return this.initwebcamPublisher();
}
if (e.name === OpenViduErrorName.DEVICE_ACCESS_DENIED) {
message = 'Access to media devices was not allowed.';
this.hasVideoDevices = false;
this.hasAudioDevices = false;
this.deviceSrv.disableVideoDevices();
this.deviceSrv.disableAudioDevices();
return this.initwebcamPublisher();
} else if (e.name === OpenViduErrorName.NO_INPUT_SOURCE_SET) {
message = 'No video or audio devices have been found. Please, connect at least one.';
}
this.actionService.openDialog(e.name.replace(/_/g, ' '), message, true);
this.log.e(e.message);
});
}
private generateRandomNickname(): string {
return 'OpenVidu_User' + Math.floor(Math.random() * 100);
}
}

View File

@ -86,10 +86,6 @@ export class SessionComponent implements OnInit {
async ngOnInit() {
if (this.openviduService.getWebcamSession() === null) {
this.openviduService.initialize();
await this.openviduService.initDefaultPublisher(undefined);
}
this.session = this.openviduService.getWebcamSession();
this.sessionScreen = this.openviduService.getScreenSession();
this.subscribeToConnectionCreatedAndDestroyed();

View File

@ -331,10 +331,12 @@ export class ToolbarComponent implements OnInit, OnDestroy {
}
protected subscribeToUserMediaProperties() {
this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p) => {
this.isWebcamVideoActive = p.isCameraVideoActive();
this.isWebcamAudioActive = p.isCameraAudioActive();
this.isScreenShareActive = p.isScreenActive();
this.cd.markForCheck();
if(p) {
this.isWebcamVideoActive = p.isCameraVideoActive();
this.isWebcamAudioActive = p.isCameraAudioActive();
this.isScreenShareActive = p.isScreenActive();
this.cd.markForCheck();
}
});
}

View File

@ -1,20 +1,20 @@
<div id="call-container">
<div id="pre-join-container" *ngIf="!joinSessionClicked">
<div id="pre-join-container" *ngIf="showPrejoin && participantReady && !joinSessionClicked">
<ov-pre-join (onJoinButtonClicked)="_onJoinButtonClicked()"></ov-pre-join>
<!-- <ov-user-settings (onJoinClicked)="_onJoinClicked()" (onCloseClicked)="onLeaveSessionClicked()"></ov-user-settings> -->
</div>
<div id="spinner" *ngIf="joinSessionClicked && !isSessionAlive && !error">
<div id="spinner" *ngIf="(joinSessionClicked || !showPrejoin) && !participantReady && !error">
<mat-spinner [diameter]="50"></mat-spinner>
<span>Joining the room ...</span>
</div>
<div id="spinner" *ngIf="joinSessionClicked && !isSessionAlive && error">
<div id="spinner" *ngIf="joinSessionClicked && !participantReady && error">
<mat-icon class="error-icon">error</mat-icon>
<span>{{ errorMessage }}</span>
</div>
<div id="session-container" *ngIf="joinSessionClicked && isSessionAlive && !error">
<div id="session-container" *ngIf="(joinSessionClicked || !showPrejoin) && participantReady && !error">
<ov-session [tokens]="_tokens">
<ng-template #toolbar>
<ng-container *ngIf="openviduAngularToolbarTemplate">

View File

@ -1,4 +1,17 @@
import { AfterViewInit, Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core';
import {
AfterViewInit,
Component,
ContentChild,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
TemplateRef,
ViewChild
} from '@angular/core';
import { OpenViduErrorName } from 'openvidu-browser';
import { Subscription } from 'rxjs';
import {
ChatPanelDirective,
LayoutDirective,
@ -11,14 +24,21 @@ import {
ToolbarDirective
} from '../../directives/template/openvidu-angular.directive';
import { ILogger } from '../../models/logger.model';
import { ParticipantProperties } from '../../models/participant.model';
import { ActionService } from '../../services/action/action.service';
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
import { DeviceService } from '../../services/device/device.service';
import { LoggerService } from '../../services/logger/logger.service';
import { OpenViduService } from '../../services/openvidu/openvidu.service';
import { ParticipantService } from '../../services/participant/participant.service';
import { StorageService } from '../../services/storage/storage.service';
@Component({
selector: 'ov-videoconference',
templateUrl: './videoconference.component.html',
styleUrls: ['./videoconference.component.css']
})
export class VideoconferenceComponent implements OnInit, AfterViewInit {
export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewInit {
// *** Toolbar ***
@ContentChild(ToolbarDirective) externalToolbar: ToolbarDirective;
@ContentChild(ToolbarAdditionalButtonsDirective) externalToolbarAdditionalButtons: ToolbarAdditionalButtonsDirective;
@ -30,7 +50,6 @@ export class VideoconferenceComponent implements OnInit, AfterViewInit {
@ContentChild(ParticipantPanelItemDirective) externalParticipantPanelItem: ParticipantPanelItemDirective;
@ContentChild(ParticipantPanelItemElementsDirective) externalParticipantPanelItemElements: ParticipantPanelItemElementsDirective;
// *** Layout ***
@ContentChild(LayoutDirective) externalLayout: LayoutDirective;
@ContentChild(StreamDirective) externalStream: StreamDirective;
@ -69,7 +88,6 @@ export class VideoconferenceComponent implements OnInit, AfterViewInit {
webcam: tokens.webcam,
screen: tokens.screen
};
this.isSessionAlive = true;
}
}
}
@ -97,17 +115,63 @@ export class VideoconferenceComponent implements OnInit, AfterViewInit {
@Output() onSessionCreated = new EventEmitter<any>();
joinSessionClicked: boolean = false;
isSessionAlive: boolean = false;
participantReady: boolean = false;
_tokens: { webcam: string; screen: string };
error: boolean = false;
errorMessage: string = '';
showPrejoin: boolean = true;
private prejoinSub: Subscription;
private log: ILogger;
constructor(private loggerSrv: LoggerService) {
constructor(
private loggerSrv: LoggerService,
private storageSrv: StorageService,
private participantService: ParticipantService,
private deviceSrv: DeviceService,
private openviduService: OpenViduService,
private actionService: ActionService,
private libService: OpenViduAngularConfigService
) {
this.log = this.loggerSrv.get('VideoconferenceComponent');
}
async ngOnInit() {
this.subscribeToVideconferenceDirectives();
await this.deviceSrv.initializeDevices();
const nickname = this.storageSrv.getNickname() || 'OpenVidu_User' + Math.floor(Math.random() * 100);
const props: ParticipantProperties = {
local: true,
nickname
};
this.participantService.initLocalParticipant(props);
this.openviduService.initialize();
if (this.deviceSrv.hasVideoDeviceAvailable() || this.deviceSrv.hasAudioDeviceAvailable()) {
await this.initwebcamPublisher();
}
}
private async initwebcamPublisher() {
try {
const publisher = await this.openviduService.initDefaultPublisher(undefined);
if (publisher) {
publisher.once('accessDenied', (e: any) => {
this.handlePublisherError(e);
});
publisher.once('accessAllowed', () => {
this.participantReady = true;
});
}
} catch (error) {
this.actionService.openDialog(error.name.replace(/_/g, ' '), error.message, true);
this.log.e(error);
}
}
ngOnDestroy(): void {
if (this.prejoinSub) this.prejoinSub.unsubscribe();
}
ngAfterViewInit() {
if (this.externalToolbar) {
this.openviduAngularToolbarTemplate = this.externalToolbar.template;
@ -173,15 +237,13 @@ export class VideoconferenceComponent implements OnInit, AfterViewInit {
}
}
ngOnInit() {}
_onJoinButtonClicked() {
this.joinSessionClicked = true;
this.onJoinButtonClicked.emit();
}
onLeaveButtonClicked() {
this.joinSessionClicked = false;
this.isSessionAlive = false;
this.participantReady = false;
this.onToolbarLeaveButtonClicked.emit();
}
onCameraButtonClicked() {
@ -203,4 +265,31 @@ export class VideoconferenceComponent implements OnInit, AfterViewInit {
onChatPanelButtonClicked() {
this.onToolbarChatPanelButtonClicked.emit();
}
private handlePublisherError(e: any) {
let message: string;
if (e.name === OpenViduErrorName.DEVICE_ALREADY_IN_USE) {
this.log.w('Video device already in use. Disabling video device...');
// Allow access to the room with only mic if camera device is already in use
this.deviceSrv.disableVideoDevices();
return this.initwebcamPublisher();
}
if (e.name === OpenViduErrorName.DEVICE_ACCESS_DENIED) {
message = 'Access to media devices was not allowed.';
this.deviceSrv.disableVideoDevices();
this.deviceSrv.disableAudioDevices();
return this.initwebcamPublisher();
} else if (e.name === OpenViduErrorName.NO_INPUT_SOURCE_SET) {
message = 'No video or audio devices have been found. Please, connect at least one.';
}
this.actionService.openDialog(e.name.replace(/_/g, ' '), message, true);
this.log.e(e.message);
}
private subscribeToVideconferenceDirectives() {
this.prejoinSub = this.libService.prejoin.subscribe((value: boolean) => {
this.showPrejoin = value;
// this.cd.markForCheck();
});
}
}

View File

@ -15,11 +15,12 @@ import {
ToolbarDisplayLogoDirective,
LogoDirective
} from './toolbar.directive';
import { AudioMutedDirective, MinimalDirective, VideoMutedDirective } from './videoconference.directive';
import { AudioMutedDirective, MinimalDirective, PrejoinDirective, VideoMutedDirective } from './videoconference.directive';
@NgModule({
declarations: [
MinimalDirective,
PrejoinDirective,
VideoMutedDirective,
AudioMutedDirective,
ToolbarScreenshareButtonDirective,
@ -37,6 +38,7 @@ import { AudioMutedDirective, MinimalDirective, VideoMutedDirective } from './vi
],
exports: [
MinimalDirective,
PrejoinDirective,
VideoMutedDirective,
AudioMutedDirective,
ToolbarScreenshareButtonDirective,

View File

@ -23,6 +23,29 @@ export class MinimalDirective implements OnDestroy {
}
}
@Directive({
selector: 'ov-videoconference[prejoin]'
})
export class PrejoinDirective implements OnDestroy {
@Input() set prejoin(value: boolean) {
this.update(value);
}
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngOnDestroy(): void {
this.clear();
}
clear() {
this.update(true);
}
update(value: boolean) {
if (this.libService.prejoin.getValue() !== value) {
this.libService.prejoin.next(value);
}
}
}
@Directive({
selector: 'ov-videoconference[videoMuted]'
})

View File

@ -51,7 +51,7 @@ export abstract class ParticipantAbstractModel {
public isCameraAudioActive(): boolean {
const cameraConnection = this.getCameraConnection();
if(cameraConnection) {
return cameraConnection.connected && cameraConnection.streamManager.stream.audioActive;
return cameraConnection.connected && cameraConnection.streamManager?.stream?.audioActive;
}
return this.isScreenAudioActive();;
}

View File

@ -11,6 +11,9 @@ export class OpenViduAngularConfigService {
minimal = <BehaviorSubject<boolean>>new BehaviorSubject(false);
minimalObs: Observable<boolean>;
prejoin = <BehaviorSubject<boolean>>new BehaviorSubject(true);
prejoinObs: Observable<boolean>;
videoMuted = <BehaviorSubject<boolean>>new BehaviorSubject(false);
videoMutedObs: Observable<boolean>;
audioMuted = <BehaviorSubject<boolean>>new BehaviorSubject(false);
@ -49,6 +52,7 @@ export class OpenViduAngularConfigService {
console.log(this.configuration);
if(this.isProduction()) console.log('OpenVidu Angular Production Mode');
this.minimalObs = this.minimal.asObservable();
this.prejoinObs = this.prejoin.asObservable();
this.videoMutedObs = this.videoMuted.asObservable();
this.audioMutedObs = this.audioMuted.asObservable();
//Toolbar observables
@ -74,9 +78,9 @@ export class OpenViduAngularConfigService {
return this.configuration?.production;
}
isWebcomponent(): boolean {
return this.configuration?.webcomponent;
}
// isWebcomponent(): boolean {
// return this.configuration?.webcomponent;
// }
hasParticipantFactory(): boolean {
return typeof this.getConfig().participantFactory === "function";