Merge branch 'gamestate-api-read' of github.com:jonnytest1/workadventure into metadataScriptingApi

This commit is contained in:
GRL 2021-05-18 15:18:35 +02:00
commit 201fcf6afa
16 changed files with 9165 additions and 319 deletions

View file

@ -1,15 +1,15 @@
import {DivImportance, layoutManager} from "./LayoutManager";
import {HtmlUtils} from "./HtmlUtils";
import {discussionManager, SendMessageCallback} from "./DiscussionManager";
import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {localUserStore} from "../Connexion/LocalUserStore";
import type {UserSimplePeerInterface} from "./SimplePeer";
import {SoundMeter} from "../Phaser/Components/SoundMeter";
import {DISABLE_NOTIFICATIONS} from "../Enum/EnvironmentVariable";
import { DivImportance, layoutManager } from "./LayoutManager";
import { HtmlUtils } from "./HtmlUtils";
import { discussionManager, SendMessageCallback } from "./DiscussionManager";
import type { UserInputManager } from "../Phaser/UserInput/UserInputManager";
import { localUserStore } from "../Connexion/LocalUserStore";
import type { UserSimplePeerInterface } from "./SimplePeer";
import { SoundMeter } from "../Phaser/Components/SoundMeter";
import { DISABLE_NOTIFICATIONS } from "../Enum/EnvironmentVariable";
declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
declare const navigator: any; // eslint-disable-line @typescript-eslint/no-explicit-any
let videoConstraint: boolean|MediaTrackConstraints = {
let videoConstraint: boolean | MediaTrackConstraints = {
width: { min: 640, ideal: 1280, max: 1920 },
height: { min: 400, ideal: 720 },
frameRate: { ideal: localUserStore.getVideoQualityValue() },
@ -17,24 +17,24 @@ let videoConstraint: boolean|MediaTrackConstraints = {
resizeMode: 'crop-and-scale',
aspectRatio: 1.777777778
};
const audioConstraint: boolean|MediaTrackConstraints = {
const audioConstraint: boolean | MediaTrackConstraints = {
//TODO: make these values configurable in the game settings menu and store them in localstorage
autoGainControl: false,
echoCancellation: true,
noiseSuppression: true
};
export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void;
export type UpdatedLocalStreamCallback = (media: MediaStream | null) => void;
export type StartScreenSharingCallback = (media: MediaStream) => void;
export type StopScreenSharingCallback = (media: MediaStream) => void;
export type ReportCallback = (message: string) => void;
export type ShowReportCallBack = (userId: string, userName: string|undefined) => void;
export type ShowReportCallBack = (userId: string, userName: string | undefined) => void;
export type HelpCameraSettingsCallBack = () => void;
// TODO: Split MediaManager in 2 classes: MediaManagerUI (in charge of HTML) and MediaManager (singleton in charge of the camera only)
export class MediaManager {
localStream: MediaStream|null = null;
localScreenCapture: MediaStream|null = null;
localStream: MediaStream | null = null;
localScreenCapture: MediaStream | null = null;
private remoteVideo: Map<string, HTMLVideoElement> = new Map<string, HTMLVideoElement>();
myCamVideo: HTMLVideoElement;
cinemaClose: HTMLImageElement;
@ -47,26 +47,26 @@ export class MediaManager {
//FIX ME SOUNDMETER: check stalability of sound meter calculation
//mySoundMeterElement: HTMLDivElement;
private webrtcOutAudio: HTMLAudioElement;
constraintsMedia : MediaStreamConstraints = {
constraintsMedia: MediaStreamConstraints = {
audio: audioConstraint,
video: videoConstraint
};
updatedLocalStreamCallBacks : Set<UpdatedLocalStreamCallback> = new Set<UpdatedLocalStreamCallback>();
startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
showReportModalCallBacks : Set<ShowReportCallBack> = new Set<ShowReportCallBack>();
helpCameraSettingsCallBacks : Set<HelpCameraSettingsCallBack> = new Set<HelpCameraSettingsCallBack>();
updatedLocalStreamCallBacks: Set<UpdatedLocalStreamCallback> = new Set<UpdatedLocalStreamCallback>();
startScreenSharingCallBacks: Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
stopScreenSharingCallBacks: Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
showReportModalCallBacks: Set<ShowReportCallBack> = new Set<ShowReportCallBack>();
helpCameraSettingsCallBacks: Set<HelpCameraSettingsCallBack> = new Set<HelpCameraSettingsCallBack>();
private microphoneBtn: HTMLDivElement;
private cinemaBtn: HTMLDivElement;
private monitorBtn: HTMLDivElement;
private previousConstraint : MediaStreamConstraints;
private focused : boolean = true;
private previousConstraint: MediaStreamConstraints;
private focused: boolean = true;
private hasCamera = true;
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
private triggerCloseJistiFrame: Map<String, Function> = new Map<String, Function>();
private userInputManager?: UserInputManager;
@ -148,7 +148,7 @@ export class MediaManager {
}
public blurCamera() {
if(!this.focused){
if (!this.focused) {
return;
}
this.focused = false;
@ -164,7 +164,7 @@ export class MediaManager {
}
public focusCamera() {
if(this.focused){
if (this.focused) {
return;
}
this.focused = true;
@ -187,7 +187,7 @@ export class MediaManager {
this.updatedLocalStreamCallBacks.delete(callback);
}
private triggerUpdatedLocalStreamCallbacks(stream: MediaStream|null): void {
private triggerUpdatedLocalStreamCallbacks(stream: MediaStream | null): void {
for (const callback of this.updatedLocalStreamCallBacks) {
callback(stream);
}
@ -235,7 +235,7 @@ export class MediaManager {
public updateCameraQuality(value: number) {
this.enableCameraStyle();
const newVideoConstraint = JSON.parse(JSON.stringify(videoConstraint));
newVideoConstraint.frameRate = {exact: value, ideal: value};
newVideoConstraint.frameRate = { exact: value, ideal: value };
videoConstraint = newVideoConstraint;
this.constraintsMedia.video = videoConstraint;
this.getCamera().then((stream: MediaStream) => {
@ -250,7 +250,7 @@ export class MediaManager {
const stream = await this.getCamera()
//TODO show error message tooltip upper of camera button
//TODO message : please check camera permission of your navigator
if(stream.getVideoTracks().length === 0) {
if (stream.getVideoTracks().length === 0) {
throw new Error('Video track is empty, please check camera permission of your navigator')
}
this.enableCameraStyle();
@ -315,14 +315,14 @@ export class MediaManager {
private applyPreviousConfig() {
this.constraintsMedia = this.previousConstraint;
if(!this.constraintsMedia.video){
if (!this.constraintsMedia.video) {
this.disableCameraStyle();
}else{
} else {
this.enableCameraStyle();
}
if(!this.constraintsMedia.audio){
if (!this.constraintsMedia.audio) {
this.disableMicrophoneStyle()
}else{
} else {
this.enableMicrophoneStyle()
}
@ -331,13 +331,13 @@ export class MediaManager {
});
}
private enableCameraStyle(){
private enableCameraStyle() {
this.cinemaClose.style.display = "none";
this.cinemaBtn.classList.remove("disabled");
this.cinema.style.display = "block";
}
private disableCameraStyle(){
private disableCameraStyle() {
this.cinemaClose.style.display = "block";
this.cinema.style.display = "none";
this.cinemaBtn.classList.add("disabled");
@ -345,13 +345,13 @@ export class MediaManager {
this.myCamVideo.srcObject = null;
}
private enableMicrophoneStyle(){
private enableMicrophoneStyle() {
this.microphoneClose.style.display = "none";
this.microphone.style.display = "block";
this.microphoneBtn.classList.remove("disabled");
}
private disableMicrophoneStyle(){
private disableMicrophoneStyle() {
this.microphoneClose.style.display = "block";
this.microphone.style.display = "none";
this.microphoneBtn.classList.add("disabled");
@ -399,7 +399,7 @@ export class MediaManager {
}
//get screen
getScreenMedia() : Promise<MediaStream>{
getScreenMedia(): Promise<MediaStream> {
try {
return this._startScreenCapture()
.then((stream: MediaStream) => {
@ -421,7 +421,7 @@ export class MediaManager {
console.error("Error => getScreenMedia => ", err);
throw err;
});
}catch (err) {
} catch (err) {
return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars
reject(err);
});
@ -430,9 +430,9 @@ export class MediaManager {
private _startScreenCapture() {
if (navigator.getDisplayMedia) {
return navigator.getDisplayMedia({video: true});
return navigator.getDisplayMedia({ video: true });
} else if (navigator.mediaDevices.getDisplayMedia) {
return navigator.mediaDevices.getDisplayMedia({video: true});
return navigator.mediaDevices.getDisplayMedia({ video: true });
} else {
return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars
reject("error sharing screen");
@ -455,7 +455,7 @@ export class MediaManager {
this.disableCameraStyle();
this.stopCamera();
return this.getLocalStream().then((stream : MediaStream) => {
return this.getLocalStream().then((stream: MediaStream) => {
this.hasCamera = false;
return stream;
}).catch((err) => {
@ -472,8 +472,8 @@ export class MediaManager {
console.info(`${width}x${height}`); // 6*/
}
private getLocalStream() : Promise<MediaStream> {
return navigator.mediaDevices.getUserMedia(this.constraintsMedia).then((stream : MediaStream) => {
private getLocalStream(): Promise<MediaStream> {
return navigator.mediaDevices.getUserMedia(this.constraintsMedia).then((stream: MediaStream) => {
this.localStream = stream;
this.myCamVideo.srcObject = this.localStream;
@ -514,7 +514,7 @@ export class MediaManager {
setCamera(id: string): Promise<MediaStream> {
let video = this.constraintsMedia.video;
if (typeof(video) === 'boolean' || video === undefined) {
if (typeof (video) === 'boolean' || video === undefined) {
video = {}
}
video.deviceId = {
@ -526,7 +526,7 @@ export class MediaManager {
setMicrophone(id: string): Promise<MediaStream> {
let audio = this.constraintsMedia.audio;
if (typeof(audio) === 'boolean' || audio === undefined) {
if (typeof (audio) === 'boolean' || audio === undefined) {
audio = {}
}
audio.deviceId = {
@ -536,14 +536,14 @@ export class MediaManager {
return this.getCamera();
}
addActiveVideo(user: UserSimplePeerInterface, userName: string = ""){
addActiveVideo(user: UserSimplePeerInterface, userName: string = "") {
this.webrtcInAudio.play();
const userId = ''+user.userId
const userId = '' + user.userId
userName = userName.toUpperCase();
const color = this.getColorByString(userName);
const html = `
const html = `
<div id="div-${userId}" class="video-container">
<div class="connecting-spinner"></div>
<div class="rtc-error" style="display: none"></div>
@ -571,7 +571,7 @@ export class MediaManager {
//permit to create participant in discussion part
const showReportUser = () => {
for(const callBack of this.showReportModalCallBacks){
for (const callBack of this.showReportModalCallBacks) {
callBack(userId, userName);
}
};
@ -603,17 +603,17 @@ export class MediaManager {
return `screen-sharing-${userId}`;
}
disabledMicrophoneByUserId(userId: number){
disabledMicrophoneByUserId(userId: number) {
const element = document.getElementById(`microphone-${userId}`);
if(!element){
if (!element) {
return;
}
element.classList.add('active') //todo: why does a method 'disable' add a class 'active'?
}
enabledMicrophoneByUserId(userId: number){
enabledMicrophoneByUserId(userId: number) {
const element = document.getElementById(`microphone-${userId}`);
if(!element){
if (!element) {
return;
}
element.classList.remove('active') //todo: why does a method 'enable' remove a class 'active'?
@ -630,22 +630,22 @@ export class MediaManager {
}
}
enabledVideoByUserId(userId: number){
enabledVideoByUserId(userId: number) {
let element = document.getElementById(`${userId}`);
if(element){
if (element) {
element.style.opacity = "1";
}
element = document.getElementById(`name-${userId}`);
if(element){
if (element) {
element.style.display = "none";
}
}
toggleBlockLogo(userId: number, show: boolean): void {
const blockLogoElement = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('blocking-'+userId);
const blockLogoElement = HtmlUtils.getElementByIdOrFail<HTMLImageElement>('blocking-' + userId);
show ? blockLogoElement.classList.add('active') : blockLogoElement.classList.remove('active');
}
addStreamRemoteVideo(userId: string, stream : MediaStream): void {
addStreamRemoteVideo(userId: string, stream: MediaStream): void {
const remoteVideo = this.remoteVideo.get(userId);
if (remoteVideo === undefined) {
throw `Unable to find video for ${userId}`;
@ -659,7 +659,7 @@ export class MediaManager {
this.soundMeters.set(userId, soundMeter);
this.soundMeterElements.set(userId, HtmlUtils.getElementByIdOrFail<HTMLImageElement>('soundMeter-'+userId));*/
}
addStreamRemoteScreenSharing(userId: string, stream : MediaStream){
addStreamRemoteScreenSharing(userId: string, stream: MediaStream) {
// In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet
const remoteVideo = this.remoteVideo.get(this.getScreenSharingId(userId));
if (remoteVideo === undefined) {
@ -669,7 +669,7 @@ export class MediaManager {
this.addStreamRemoteVideo(this.getScreenSharingId(userId), stream);
}
removeActiveVideo(userId: string){
removeActiveVideo(userId: string) {
layoutManager.remove(userId);
this.remoteVideo.delete(userId);
@ -708,10 +708,10 @@ export class MediaManager {
isError(userId: string): void {
console.info("isError", `div-${userId}`);
const element = document.getElementById(`div-${userId}`);
if(!element){
if (!element) {
return;
}
const errorDiv = element.getElementsByClassName('rtc-error').item(0) as HTMLDivElement|null;
const errorDiv = element.getElementsByClassName('rtc-error').item(0) as HTMLDivElement | null;
if (errorDiv === null) {
return;
}
@ -722,16 +722,16 @@ export class MediaManager {
}
private getSpinner(userId: string): HTMLDivElement|null {
private getSpinner(userId: string): HTMLDivElement | null {
const element = document.getElementById(`div-${userId}`);
if(!element){
if (!element) {
return null;
}
const connnectingSpinnerDiv = element.getElementsByClassName('connecting-spinner').item(0) as HTMLDivElement|null;
const connnectingSpinnerDiv = element.getElementsByClassName('connecting-spinner').item(0) as HTMLDivElement | null;
return connnectingSpinnerDiv;
}
private getColorByString(str: String) : String|null {
private getColorByString(str: String): String | null {
let hash = 0;
if (str.length === 0) return null;
for (let i = 0; i < str.length; i++) {
@ -746,18 +746,18 @@ export class MediaManager {
return color;
}
public addNewParticipant(userId: number|string, name: string|undefined, img?: string, showReportUserCallBack?: ShowReportCallBack){
public addNewParticipant(userId: number | string, name: string | undefined, img?: string, showReportUserCallBack?: ShowReportCallBack) {
discussionManager.addParticipant(userId, name, img, false, showReportUserCallBack);
}
public removeParticipant(userId: number|string){
public removeParticipant(userId: number | string) {
discussionManager.removeParticipant(userId);
}
public addTriggerCloseJitsiFrameButton(id: String, Function: Function){
public addTriggerCloseJitsiFrameButton(id: String, Function: Function) {
this.triggerCloseJistiFrame.set(id, Function);
}
public removeTriggerCloseJitsiFrameButton(id: String){
public removeTriggerCloseJitsiFrameButton(id: String) {
this.triggerCloseJistiFrame.delete(id);
}
@ -770,45 +770,45 @@ export class MediaManager {
* For some reasons, the microphone muted icon or the stream is not always up to date.
* Here, every 30 seconds, we are "reseting" the streams and sending again the constraints to the other peers via the data channel again (see SimplePeer::pushVideoToRemoteUser)
**/
private pingCameraStatus(){
private pingCameraStatus() {
/*setInterval(() => {
console.log('ping camera status');
this.triggerUpdatedLocalStreamCallbacks(this.localStream);
}, 30000);*/
}
public addNewMessage(name: string, message: string, isMe: boolean = false){
public addNewMessage(name: string, message: string, isMe: boolean = false) {
discussionManager.addMessage(name, message, isMe);
//when there are new message, show discussion
if(!discussionManager.activatedDiscussion) {
if (!discussionManager.activatedDiscussion) {
discussionManager.showDiscussionPart();
}
}
public addSendMessageCallback(userId: string|number, callback: SendMessageCallback){
public addSendMessageCallback(userId: string | number, callback: SendMessageCallback) {
discussionManager.onSendMessageCallback(userId, callback);
}
get activatedDiscussion(){
get activatedDiscussion() {
return discussionManager.activatedDiscussion;
}
public setUserInputManager(userInputManager : UserInputManager){
public setUserInputManager(userInputManager: UserInputManager) {
this.userInputManager = userInputManager;
discussionManager.setUserInputManager(userInputManager);
}
public setShowReportModalCallBacks(callback: ShowReportCallBack){
public setShowReportModalCallBacks(callback: ShowReportCallBack) {
this.showReportModalCallBacks.add(callback);
}
public setHelpCameraSettingsCallBack(callback: HelpCameraSettingsCallBack){
public setHelpCameraSettingsCallBack(callback: HelpCameraSettingsCallBack) {
this.helpCameraSettingsCallBacks.add(callback);
}
private showHelpCameraSettingsCallBack(){
for(const callBack of this.helpCameraSettingsCallBacks){
private showHelpCameraSettingsCallBack() {
for (const callBack of this.helpCameraSettingsCallBacks) {
callBack();
}
}
@ -822,33 +822,33 @@ export class MediaManager {
for(const indexUserId of this.soundMeters.keys()){
const soundMeter = this.soundMeters.get(indexUserId);
const soundMeterElement = this.soundMeterElements.get(indexUserId);
if(!soundMeter || !soundMeterElement){
if (!soundMeter || !soundMeterElement) {
return;
}
const volumeByUser = parseInt((soundMeter.getVolume() / 10).toFixed(0));
this.setVolumeSoundMeter(volumeByUser, soundMeterElement);
}
}catch(err){
} catch (err) {
//console.error(err);
}
}*/
private setVolumeSoundMeter(volume: number, element: HTMLDivElement){
if(volume <= 0 && !element.classList.contains('active')){
private setVolumeSoundMeter(volume: number, element: HTMLDivElement) {
if (volume <= 0 && !element.classList.contains('active')) {
return;
}
element.classList.remove('active');
if(volume <= 0){
if (volume <= 0) {
return;
}
element.classList.add('active');
element.childNodes.forEach((value: ChildNode, index) => {
const elementChildre = element.children.item(index);
if(!elementChildre){
if (!elementChildre) {
return;
}
elementChildre.classList.remove('active');
if((index +1) > volume){
if ((index + 1) > volume) {
return;
}
elementChildre.classList.add('active');