From 199ed1266a1ce419d398e8c527acb5b65a4483fa Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Sat, 24 Oct 2020 14:13:23 +0200 Subject: [PATCH 01/40] Switch off camera when user is not focused on WorkAdventure windows Feature to switch off camera when user is not focused on WorkAdventure windows after 10 seconds --- front/src/WebRtc/MediaManager.ts | 84 ++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 15 deletions(-) diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 557f12fb..070f27f9 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -38,6 +38,9 @@ export class MediaManager { private cinemaBtn: HTMLDivElement; private monitorBtn: HTMLDivElement; + private previousConstraint : MediaStreamConstraints; + private timeoutBlurWindows?: NodeJS.Timeout; + constructor() { this.myCamVideo = this.getElementByIdOrFail('myCamVideo'); @@ -89,6 +92,23 @@ export class MediaManager { this.disableScreenSharing(); //update tracking }); + + this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia)); + window.addEventListener('blur', () => { + if(this.timeoutBlurWindows){ + clearTimeout(this.timeoutBlurWindows); + } + this.timeoutBlurWindows = setTimeout(() => { + this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia)); + this.disableCamera(); + }, 10000); + }); + window.addEventListener('focus', () => { + if(this.timeoutBlurWindows){ + clearTimeout(this.timeoutBlurWindows); + } + this.applyPreviousConfig(); + }); } public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void { @@ -136,9 +156,7 @@ export class MediaManager { } private enableCamera() { - this.cinemaClose.style.display = "none"; - this.cinemaBtn.classList.remove("disabled"); - this.cinema.style.display = "block"; + this.enableCameraStyle(); this.constraintsMedia.video = videoConstraint; this.getCamera().then((stream: MediaStream) => { this.triggerUpdatedLocalStreamCallbacks(stream); @@ -146,11 +164,7 @@ export class MediaManager { } private async disableCamera() { - this.cinemaClose.style.display = "block"; - this.cinema.style.display = "none"; - this.cinemaBtn.classList.add("disabled"); - this.constraintsMedia.video = false; - this.myCamVideo.srcObject = null; + this.disableCameraStyle(); this.stopCamera(); if (this.constraintsMedia.audio !== false) { @@ -162,9 +176,7 @@ export class MediaManager { } private enableMicrophone() { - this.microphoneClose.style.display = "none"; - this.microphone.style.display = "block"; - this.microphoneBtn.classList.remove("disabled"); + this.enableMicrophoneStyle(); this.constraintsMedia.audio = true; this.getCamera().then((stream) => { @@ -173,10 +185,7 @@ export class MediaManager { } private async disableMicrophone() { - this.microphoneClose.style.display = "block"; - this.microphone.style.display = "none"; - this.microphoneBtn.classList.add("disabled"); - this.constraintsMedia.audio = false; + this.disableMicrophoneStyle(); this.stopMicrophone(); if (this.constraintsMedia.video !== false) { @@ -187,6 +196,51 @@ export class MediaManager { } } + private applyPreviousConfig() { + this.constraintsMedia = this.previousConstraint; + if(!this.constraintsMedia.video){ + this.disableCameraStyle(); + }else{ + this.enableCameraStyle(); + } + if(!this.constraintsMedia.audio){ + this.disableMicrophoneStyle() + }else{ + this.enableMicrophoneStyle() + } + + this.getCamera().then((stream: MediaStream) => { + this.triggerUpdatedLocalStreamCallbacks(stream); + }); + } + + private enableCameraStyle(){ + this.cinemaClose.style.display = "none"; + this.cinemaBtn.classList.remove("disabled"); + this.cinema.style.display = "block"; + } + + private disableCameraStyle(){ + this.cinemaClose.style.display = "block"; + this.cinema.style.display = "none"; + this.cinemaBtn.classList.add("disabled"); + this.constraintsMedia.video = false; + this.myCamVideo.srcObject = null; + } + + private enableMicrophoneStyle(){ + this.microphoneClose.style.display = "none"; + this.microphone.style.display = "block"; + this.microphoneBtn.classList.remove("disabled"); + } + + private disableMicrophoneStyle(){ + this.microphoneClose.style.display = "block"; + this.microphone.style.display = "none"; + this.microphoneBtn.classList.add("disabled"); + this.constraintsMedia.audio = false; + } + private enableScreenSharing() { this.monitorClose.style.display = "none"; this.monitor.style.display = "block"; From 69f3e511aba0a6c3d89c71ef93f20193bde9dc4c Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Sat, 24 Oct 2020 14:40:51 +0200 Subject: [PATCH 02/40] Ping status camera and microphone --- front/src/WebRtc/MediaManager.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 070f27f9..28771f63 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -109,6 +109,7 @@ export class MediaManager { } this.applyPreviousConfig(); }); + this.pingCameraStatus(); } public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void { @@ -619,6 +620,17 @@ export class MediaManager { mainContainer.appendChild(divReport); } + //ping always 30 seconds data on stream + private pingCameraStatus(){ + setTimeout(() => { + console.log('ping camera status'); + this.getCamera().then((stream: MediaStream) => { + this.triggerUpdatedLocalStreamCallbacks(stream); + this.pingCameraStatus(); + }); + }, 30000); + } + } From b6fe9e72e1aaf3622471e094839271a51a72710b Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Sun, 25 Oct 2020 19:38:00 +0100 Subject: [PATCH 03/40] Fix style and refactor --- front/dist/resources/style/style.css | 7 ++-- front/src/Phaser/Login/EnableCameraScene.ts | 24 ++++-------- front/src/WebRtc/MediaManager.ts | 43 ++++++++------------- 3 files changed, 29 insertions(+), 45 deletions(-) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 5f5d37f2..d0f0e68b 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -72,8 +72,9 @@ body .message-info.warning{ #div-myCamVideo { position: absolute; - right: 0; - bottom: 0; + right: 15px; + bottom: 15px; + border-radius: 15px; } video#myCamVideo{ @@ -419,7 +420,7 @@ body { max-height: 80%; top: -80%; left: 10%; - background: #000000a6; + background: #333333; z-index: 200; transition: all 0.1s ease-out; } diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index 32037858..56502704 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -7,6 +7,7 @@ import {mediaManager} from "../../WebRtc/MediaManager"; import {RESOLUTION} from "../../Enum/EnvironmentVariable"; import {SoundMeter} from "../Components/SoundMeter"; import {SoundMeterSprite} from "../Components/SoundMeterSprite"; +import {HtmlUtils} from "../../WebRtc/HtmlUtils"; export const EnableCameraSceneName = "EnableCameraScene"; enum LoginTextures { @@ -93,7 +94,7 @@ export class EnableCameraScene extends Phaser.Scene { this.login(); }); - this.getElementByIdOrFail('webRtcSetup').classList.add('active'); + HtmlUtils.getElementByIdOrFail('webRtcSetup').classList.add('active'); const mediaPromise = mediaManager.getCamera(); mediaPromise.then(this.getDevices.bind(this)); @@ -150,10 +151,10 @@ export class EnableCameraScene extends Phaser.Scene { * Function called each time a camera is changed */ private setupStream(stream: MediaStream): void { - const img = this.getElementByIdOrFail('webRtcSetupNoVideo'); + const img = HtmlUtils.getElementByIdOrFail('webRtcSetupNoVideo'); img.style.display = 'none'; - const div = this.getElementByIdOrFail('myCamVideoSetup'); + const div = HtmlUtils.getElementByIdOrFail('myCamVideoSetup'); div.srcObject = stream; this.soundMeter.connectToSource(stream, new window.AudioContext()); @@ -164,7 +165,7 @@ export class EnableCameraScene extends Phaser.Scene { private updateWebCamName(): void { if (this.camerasList.length > 1) { - const div = this.getElementByIdOrFail('myCamVideoSetup'); + const div = HtmlUtils.getElementByIdOrFail('myCamVideoSetup'); let label = this.camerasList[this.cameraSelected].label; // remove text in parenthesis @@ -211,10 +212,10 @@ export class EnableCameraScene extends Phaser.Scene { } private reposition(): void { - let div = this.getElementByIdOrFail('myCamVideoSetup'); + let div = HtmlUtils.getElementByIdOrFail('myCamVideoSetup'); let bounds = div.getBoundingClientRect(); if (!div.srcObject) { - div = this.getElementByIdOrFail('webRtcSetup'); + div = HtmlUtils.getElementByIdOrFail('webRtcSetup'); bounds = div.getBoundingClientRect(); } @@ -255,7 +256,7 @@ export class EnableCameraScene extends Phaser.Scene { } private login(): void { - this.getElementByIdOrFail('webRtcSetup').style.display = 'none'; + HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none'; this.soundMeter.stop(); window.removeEventListener('resize', this.repositionCallback); @@ -276,13 +277,4 @@ export class EnableCameraScene extends Phaser.Scene { } this.updateWebCamName(); } - - private getElementByIdOrFail(id: string): T { - const elem = document.getElementById(id); - if (elem === null) { - throw new Error("Cannot find HTML element with id '"+id+"'"); - } - // FIXME: does not check the type of the returned type - return elem as T; - } } diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 557f12fb..9c3563ad 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -40,49 +40,49 @@ export class MediaManager { constructor() { - this.myCamVideo = this.getElementByIdOrFail('myCamVideo'); - this.webrtcInAudio = this.getElementByIdOrFail('audio-webrtc-in'); + this.myCamVideo = HtmlUtils.getElementByIdOrFail('myCamVideo'); + this.webrtcInAudio = HtmlUtils.getElementByIdOrFail('audio-webrtc-in'); this.webrtcInAudio.volume = 0.2; - this.microphoneBtn = this.getElementByIdOrFail('btn-micro'); - this.microphoneClose = this.getElementByIdOrFail('microphone-close'); + this.microphoneBtn = HtmlUtils.getElementByIdOrFail('btn-micro'); + this.microphoneClose = HtmlUtils.getElementByIdOrFail('microphone-close'); this.microphoneClose.style.display = "none"; this.microphoneClose.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); this.enableMicrophone(); //update tracking }); - this.microphone = this.getElementByIdOrFail('microphone'); + this.microphone = HtmlUtils.getElementByIdOrFail('microphone'); this.microphone.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); this.disableMicrophone(); //update tracking }); - this.cinemaBtn = this.getElementByIdOrFail('btn-video'); - this.cinemaClose = this.getElementByIdOrFail('cinema-close'); + this.cinemaBtn = HtmlUtils.getElementByIdOrFail('btn-video'); + this.cinemaClose = HtmlUtils.getElementByIdOrFail('cinema-close'); this.cinemaClose.style.display = "none"; this.cinemaClose.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); this.enableCamera(); //update tracking }); - this.cinema = this.getElementByIdOrFail('cinema'); + this.cinema = HtmlUtils.getElementByIdOrFail('cinema'); this.cinema.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); this.disableCamera(); //update tracking }); - this.monitorBtn = this.getElementByIdOrFail('btn-monitor'); - this.monitorClose = this.getElementByIdOrFail('monitor-close'); + this.monitorBtn = HtmlUtils.getElementByIdOrFail('btn-monitor'); + this.monitorClose = HtmlUtils.getElementByIdOrFail('monitor-close'); this.monitorClose.style.display = "block"; this.monitorClose.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); this.enableScreenSharing(); //update tracking }); - this.monitor = this.getElementByIdOrFail('monitor'); + this.monitor = HtmlUtils.getElementByIdOrFail('monitor'); this.monitor.style.display = "none"; this.monitor.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); @@ -126,12 +126,12 @@ export class MediaManager { } public showGameOverlay(){ - const gameOverlay = this.getElementByIdOrFail('game-overlay'); + const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay'); gameOverlay.classList.add('active'); } public hideGameOverlay(){ - const gameOverlay = this.getElementByIdOrFail('game-overlay'); + const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay'); gameOverlay.classList.remove('active'); } @@ -355,14 +355,14 @@ export class MediaManager { layoutManager.add(DivImportance.Normal, userId, html); if (reportCallBack) { - const reportBtn = this.getElementByIdOrFail(`report-${userId}`); + const reportBtn = HtmlUtils.getElementByIdOrFail(`report-${userId}`); reportBtn.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); this.showReportModal(userId, userName, reportCallBack); }); } - this.remoteVideo.set(userId, this.getElementByIdOrFail(userId)); + this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail(userId)); } addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){ @@ -376,7 +376,7 @@ export class MediaManager { layoutManager.add(divImportance, userId, html); - this.remoteVideo.set(userId, this.getElementByIdOrFail(userId)); + this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail(userId)); } disabledMicrophoneByUserId(userId: number){ @@ -499,18 +499,9 @@ export class MediaManager { return color; } - private getElementByIdOrFail(id: string): T { - const elem = document.getElementById(id); - if (elem === null) { - throw new Error("Cannot find HTML element with id '"+id+"'"); - } - // FIXME: does not check the type of the returned type - return elem as T; - } - private showReportModal(userId: string, userName: string, reportCallBack: ReportCallback){ //create report text area - const mainContainer = this.getElementByIdOrFail('main-container'); + const mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); const divReport = document.createElement('div'); divReport.classList.add('modal-report-user'); From f344adc48bba3e612d7636ae711d2a75efbf5d7e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Sun, 25 Oct 2020 19:39:15 +0100 Subject: [PATCH 04/40] Create discussion - Add new discussion class - Feature to discuss and report user --- front/dist/resources/logos/boy.svg | 77 +++++++++++++ front/dist/resources/logos/discussion.svg | 1 + front/dist/resources/style/style.css | 125 ++++++++++++++++++++++ front/src/Phaser/Login/LoginScene.ts | 8 ++ front/src/WebRtc/Discussion.ts | 124 +++++++++++++++++++++ 5 files changed, 335 insertions(+) create mode 100644 front/dist/resources/logos/boy.svg create mode 100644 front/dist/resources/logos/discussion.svg create mode 100644 front/src/WebRtc/Discussion.ts diff --git a/front/dist/resources/logos/boy.svg b/front/dist/resources/logos/boy.svg new file mode 100644 index 00000000..d6d9582e --- /dev/null +++ b/front/dist/resources/logos/boy.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front/dist/resources/logos/discussion.svg b/front/dist/resources/logos/discussion.svg new file mode 100644 index 00000000..1b4572b3 --- /dev/null +++ b/front/dist/resources/logos/discussion.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index d0f0e68b..d1f5e7f7 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -686,3 +686,128 @@ div.modal-report-user{ max-width: calc(800px - 60px); /* size of modal - padding*/ } +/*MESSAGE*/ +.discussion{ + position: fixed; + right: -300px; + width: 220px; + height: 100%; + background-color: #000000; + padding: 20px; +} +.discussion.active{ + right: 0; +} +.discussion .toggle-btn{ + display: none; + cursor: pointer; + height: 50px; + width: 50px; + background-color: #2d2d2dba; + position: absolute; + top: calc(50% - 25px); + margin-left: -125px; + border-radius: 50%; + border: none; + transition: all 0.5s ease; +} +.discussion .toggle-btn.active{ + display: block; +} +.discussion .toggle-btn:hover { + transform: scale(1.1) rotateY(3.142rad); +} +.discussion .toggle-btn img{ + width: 26px; + height: 26px; + margin: 13px 5px; +} +.discussion p{ + color: white; + font-size: 22px; + padding-left: 10px; + margin: 0; +} + +.discussion .participants{ + height: 200px; + margin: 10px 0; +} + +.discussion .participants .participant{ + display: flex; + margin: 5px 10px; + background-color: #ffffff69; + padding: 5px; + border-radius: 15px; + cursor: pointer; +} + +.discussion .participants .participant:hover{ + background-color: #ffffff; +} +.discussion .participants .participant:hover p{ + color: black; +} + +.discussion .participants .participant:before { + content: ''; + height: 10px; + width: 10px; + background-color: #1e7e34; + position: absolute; + margin-left: 18px; + border-radius: 50%; + margin-top: 18px; +} + +.discussion .participants .participant img{ + width: 26px; + height: 26px; +} + +.discussion .participants .participant p{ + font-size: 16px; + margin-left: 10px; + margin-top: 8px; +} + +.discussion .participants .participant button.report-btn{ + cursor: pointer; + position: absolute; + background-color: #2d2d2dba; + right: 34px; + margin: 0px; + padding: 6px 0px; + border-radius: 15px; + border: none; + color: white; + width: 0px; + overflow: hidden; + transition: all .5s ease; +} + +.discussion .participants .participant:hover button.report-btn{ + width: 70px; +} + +.discussion .messages{ + position: absolute; + height: calc(100% - 300px); +} + +.discussion .messages .message{ + margin: 5px; + float: right; + text-align: right; +} + +.discussion .messages .message.me{ + float: left; + text-align: left; +} + +.discussion .messages .message p{ + font-size: 12px; +} + diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index 2c5c1882..55321124 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -9,6 +9,7 @@ import {cypressAsserter} from "../../Cypress/CypressAsserter"; import {SelectCharacterSceneName} from "./SelectCharacterScene"; import {ResizableScene} from "./ResizableScene"; import {localUserStore} from "../../Connexion/LocalUserStore"; +import {Discussion} from "../../WebRtc/Discussion"; //todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; @@ -74,6 +75,13 @@ export class LoginScene extends ResizableScene { }); cypressAsserter.initFinished(); + + const discussion = new Discussion('test'); + discussion.addParticipant('GRP'); + discussion.addParticipant('LOL'); + + discussion.addMessage('GRP', 'ceci est un test d\'envoi de message', true); + discussion.addMessage('LOL', 'ceci est un test d\'envoi de message'); } update(time: number, delta: number): void { diff --git a/front/src/WebRtc/Discussion.ts b/front/src/WebRtc/Discussion.ts new file mode 100644 index 00000000..b7307745 --- /dev/null +++ b/front/src/WebRtc/Discussion.ts @@ -0,0 +1,124 @@ +import {HtmlUtils} from "./HtmlUtils"; + +export class Discussion { + + private mainContainer: HTMLDivElement; + + private divDiscuss?: HTMLDivElement; + private divParticipants?: HTMLDivElement; + private nbpParticipants?: HTMLParagraphElement; + private divMessages?: HTMLParagraphElement; + + private participants: Map = new Map(); + + private activeDiscussion: boolean = false; + + constructor(name: string) { + this.mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); + this.createDiscussPart(name); + } + + private createDiscussPart(name: string) { + this.divDiscuss = document.createElement('div'); + this.divDiscuss.classList.add('discussion'); + + const buttonToggleDiscussion = document.createElement('button'); + buttonToggleDiscussion.classList.add('toggle-btn'); + buttonToggleDiscussion.classList.add('active'); + buttonToggleDiscussion.innerHTML = ``; + buttonToggleDiscussion.addEventListener('click', () => { + if(this.activeDiscussion){ + this.activeDiscussion = false; + this.divDiscuss?.classList.remove('active'); + buttonToggleDiscussion.classList.add('active'); + }else{ + this.activeDiscussion = true; + this.divDiscuss?.classList.add('active'); + buttonToggleDiscussion.classList.remove('active'); + } + }); + this.divDiscuss.appendChild(buttonToggleDiscussion); + + const myName = document.createElement('p'); + myName.innerText = name.toUpperCase(); + this.nbpParticipants = document.createElement('p'); + this.nbpParticipants.innerText = 'PARTICIPANTS (1)'; + + this.divParticipants = document.createElement('div'); + this.divParticipants.classList.add('participants'); + + this.divMessages = document.createElement('div'); + this.divMessages.classList.add('messages'); + + this.divDiscuss.appendChild(myName); + this.divDiscuss.appendChild(this.nbpParticipants); + this.divDiscuss.appendChild(this.divParticipants); + this.divDiscuss.appendChild(this.divMessages); + + //append in main container + this.mainContainer.appendChild(this.divDiscuss); + } + + public addParticipant(name: string, img?: string) { + const divParticipant = document.createElement('div'); + divParticipant.classList.add('participant'); + + const divImgParticipant = document.createElement('img'); + divImgParticipant.src = 'resources/logos/boy.svg'; + if (img) { + divImgParticipant.src = img; + } + const divPParticipant = document.createElement('p'); + divPParticipant.innerText = name; + + const reportBanUserAction = document.createElement('button'); + reportBanUserAction.classList.add('report-btn') + reportBanUserAction.innerText = 'Report'; + reportBanUserAction.addEventListener('click', () => { + //TODO report user + console.log('report'); + }); + + divParticipant.appendChild(divImgParticipant); + divParticipant.appendChild(divPParticipant); + divParticipant.appendChild(reportBanUserAction); + + this.divParticipants?.appendChild(divParticipant); + this.participants.set(name, divParticipant); + + this.updateParticipant(this.participants.size); + } + + public updateParticipant(nb: number) { + if (!this.nbpParticipants) { + return; + } + this.nbpParticipants.innerText = `PARTICIPANTS (${nb})`; + } + + public addMessage(name: string, message: string, isMe: boolean = false) { + const divMessage = document.createElement('div'); + divMessage.classList.add('message'); + if(isMe){ + divMessage.classList.add('me'); + } + + const pMessage = document.createElement('p'); + const date = new Date(); + if(isMe){ + name = 'Moi'; + } + pMessage.innerHTML = `${name} + + ${date.getHours()}:${date.getMinutes()} + `; + divMessage.appendChild(pMessage); + + const userMessage = document.createElement('p'); + userMessage.innerText = message; + divMessage.appendChild(userMessage); + + this.divMessages?.appendChild(divMessage); + } + +} \ No newline at end of file From 1945c24704e96baab1be4673581ef65e2ecc13a6 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Sun, 25 Oct 2020 21:59:14 +0100 Subject: [PATCH 05/40] Create send data discussion --- front/dist/resources/style/style.css | 77 ++++++++++-- front/src/Phaser/Login/LoginScene.ts | 8 -- front/src/WebRtc/Discussion.ts | 124 ------------------- front/src/WebRtc/DiscussionManager.ts | 169 ++++++++++++++++++++++++++ front/src/WebRtc/MediaManager.ts | 25 ++++ front/src/WebRtc/ScreenSharingPeer.ts | 3 +- front/src/WebRtc/SimplePeer.ts | 10 +- front/src/WebRtc/VideoPeer.ts | 34 ++++-- 8 files changed, 294 insertions(+), 156 deletions(-) delete mode 100644 front/src/WebRtc/Discussion.ts create mode 100644 front/src/WebRtc/DiscussionManager.ts diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index d1f5e7f7..dec0630f 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -689,16 +689,17 @@ div.modal-report-user{ /*MESSAGE*/ .discussion{ position: fixed; - right: -300px; + left: -300px; width: 220px; height: 100%; - background-color: #000000; + background-color: #333333; padding: 20px; + transition: all 0.5s ease; } .discussion.active{ - right: 0; + left: 0; } -.discussion .toggle-btn{ +.discussion .active-btn{ display: none; cursor: pointer; height: 50px; @@ -706,22 +707,36 @@ div.modal-report-user{ background-color: #2d2d2dba; position: absolute; top: calc(50% - 25px); - margin-left: -125px; + margin-left: 315px; border-radius: 50%; border: none; transition: all 0.5s ease; } -.discussion .toggle-btn.active{ +.discussion .active-btn.active{ display: block; } -.discussion .toggle-btn:hover { +.discussion .active-btn:hover { transform: scale(1.1) rotateY(3.142rad); } -.discussion .toggle-btn img{ +.discussion .active-btn img{ width: 26px; height: 26px; margin: 13px 5px; } + +.discussion .close-btn{ + position: absolute; + top: 0; + right: 10px; + background: none; + border: none; + cursor: pointer; +} +.discussion .close-btn img{ + height: 15px; + right: 15px; +} + .discussion p{ color: white; font-size: 22px; @@ -793,13 +808,22 @@ div.modal-report-user{ .discussion .messages{ position: absolute; - height: calc(100% - 300px); + height: calc(100% - 360px); + overflow-x: hidden; + overflow-y: scroll; + max-width: calc(100% - 40px); + width: calc(100% - 40px); +} + +.discussion .messages h2{ + color: white; } .discussion .messages .message{ margin: 5px; float: right; text-align: right; + width: 100%; } .discussion .messages .message.me{ @@ -811,3 +835,38 @@ div.modal-report-user{ font-size: 12px; } +.discussion .messages .message p.body{ + font-size: 16px; + overflow: hidden; + white-space: pre-wrap; + word-wrap: break-word; +} + +.discussion .send-message{ + position: absolute; + bottom: 45px; + width: 220px; + height: 26px; +} + +.discussion .send-message input{ + position: absolute; + width: calc(100% - 10px); + height: 20px; + background-color: #171717; + color: white; + border-radius: 15px; + border: none; +} + +.discussion .send-message img{ + position: absolute; + margin-right: 10px; + width: 20px; + height: 20px; + background-color: #ffffff69; +} +.discussion .send-message img:hover{ + background-color: #ffffff; +} + diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index 55321124..2c5c1882 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -9,7 +9,6 @@ import {cypressAsserter} from "../../Cypress/CypressAsserter"; import {SelectCharacterSceneName} from "./SelectCharacterScene"; import {ResizableScene} from "./ResizableScene"; import {localUserStore} from "../../Connexion/LocalUserStore"; -import {Discussion} from "../../WebRtc/Discussion"; //todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; @@ -75,13 +74,6 @@ export class LoginScene extends ResizableScene { }); cypressAsserter.initFinished(); - - const discussion = new Discussion('test'); - discussion.addParticipant('GRP'); - discussion.addParticipant('LOL'); - - discussion.addMessage('GRP', 'ceci est un test d\'envoi de message', true); - discussion.addMessage('LOL', 'ceci est un test d\'envoi de message'); } update(time: number, delta: number): void { diff --git a/front/src/WebRtc/Discussion.ts b/front/src/WebRtc/Discussion.ts deleted file mode 100644 index b7307745..00000000 --- a/front/src/WebRtc/Discussion.ts +++ /dev/null @@ -1,124 +0,0 @@ -import {HtmlUtils} from "./HtmlUtils"; - -export class Discussion { - - private mainContainer: HTMLDivElement; - - private divDiscuss?: HTMLDivElement; - private divParticipants?: HTMLDivElement; - private nbpParticipants?: HTMLParagraphElement; - private divMessages?: HTMLParagraphElement; - - private participants: Map = new Map(); - - private activeDiscussion: boolean = false; - - constructor(name: string) { - this.mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); - this.createDiscussPart(name); - } - - private createDiscussPart(name: string) { - this.divDiscuss = document.createElement('div'); - this.divDiscuss.classList.add('discussion'); - - const buttonToggleDiscussion = document.createElement('button'); - buttonToggleDiscussion.classList.add('toggle-btn'); - buttonToggleDiscussion.classList.add('active'); - buttonToggleDiscussion.innerHTML = ``; - buttonToggleDiscussion.addEventListener('click', () => { - if(this.activeDiscussion){ - this.activeDiscussion = false; - this.divDiscuss?.classList.remove('active'); - buttonToggleDiscussion.classList.add('active'); - }else{ - this.activeDiscussion = true; - this.divDiscuss?.classList.add('active'); - buttonToggleDiscussion.classList.remove('active'); - } - }); - this.divDiscuss.appendChild(buttonToggleDiscussion); - - const myName = document.createElement('p'); - myName.innerText = name.toUpperCase(); - this.nbpParticipants = document.createElement('p'); - this.nbpParticipants.innerText = 'PARTICIPANTS (1)'; - - this.divParticipants = document.createElement('div'); - this.divParticipants.classList.add('participants'); - - this.divMessages = document.createElement('div'); - this.divMessages.classList.add('messages'); - - this.divDiscuss.appendChild(myName); - this.divDiscuss.appendChild(this.nbpParticipants); - this.divDiscuss.appendChild(this.divParticipants); - this.divDiscuss.appendChild(this.divMessages); - - //append in main container - this.mainContainer.appendChild(this.divDiscuss); - } - - public addParticipant(name: string, img?: string) { - const divParticipant = document.createElement('div'); - divParticipant.classList.add('participant'); - - const divImgParticipant = document.createElement('img'); - divImgParticipant.src = 'resources/logos/boy.svg'; - if (img) { - divImgParticipant.src = img; - } - const divPParticipant = document.createElement('p'); - divPParticipant.innerText = name; - - const reportBanUserAction = document.createElement('button'); - reportBanUserAction.classList.add('report-btn') - reportBanUserAction.innerText = 'Report'; - reportBanUserAction.addEventListener('click', () => { - //TODO report user - console.log('report'); - }); - - divParticipant.appendChild(divImgParticipant); - divParticipant.appendChild(divPParticipant); - divParticipant.appendChild(reportBanUserAction); - - this.divParticipants?.appendChild(divParticipant); - this.participants.set(name, divParticipant); - - this.updateParticipant(this.participants.size); - } - - public updateParticipant(nb: number) { - if (!this.nbpParticipants) { - return; - } - this.nbpParticipants.innerText = `PARTICIPANTS (${nb})`; - } - - public addMessage(name: string, message: string, isMe: boolean = false) { - const divMessage = document.createElement('div'); - divMessage.classList.add('message'); - if(isMe){ - divMessage.classList.add('me'); - } - - const pMessage = document.createElement('p'); - const date = new Date(); - if(isMe){ - name = 'Moi'; - } - pMessage.innerHTML = `${name} - - ${date.getHours()}:${date.getMinutes()} - `; - divMessage.appendChild(pMessage); - - const userMessage = document.createElement('p'); - userMessage.innerText = message; - divMessage.appendChild(userMessage); - - this.divMessages?.appendChild(divMessage); - } - -} \ No newline at end of file diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts new file mode 100644 index 00000000..4663adcf --- /dev/null +++ b/front/src/WebRtc/DiscussionManager.ts @@ -0,0 +1,169 @@ +import {HtmlUtils} from "./HtmlUtils"; +import {UpdatedLocalStreamCallback} from "./MediaManager"; +export type SendMessageCallback = (message:string) => void; + +export class DiscussionManager { + + private mainContainer: HTMLDivElement; + + private divDiscuss?: HTMLDivElement; + private divParticipants?: HTMLDivElement; + private nbpParticipants?: HTMLParagraphElement; + private divMessages?: HTMLParagraphElement; + + private participants: Map = new Map(); + + private activeDiscussion: boolean = false; + + private sendMessageCallBack : Set = new Set(); + + constructor(name: string) { + this.mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); + this.createDiscussPart(name); + } + + private createDiscussPart(name: string) { + this.divDiscuss = document.createElement('div'); + this.divDiscuss.classList.add('discussion'); + + const buttonCloseDiscussion: HTMLButtonElement = document.createElement('button'); + const buttonActiveDiscussion: HTMLButtonElement = document.createElement('button'); + buttonCloseDiscussion.classList.add('close-btn'); + buttonCloseDiscussion.innerHTML = ``; + buttonCloseDiscussion.addEventListener('click', () => { + this.activeDiscussion = false; + this.divDiscuss?.classList.remove('active'); + buttonActiveDiscussion.classList.add('active'); + }); + buttonActiveDiscussion.classList.add('active-btn'); + buttonActiveDiscussion.classList.add('active'); + buttonActiveDiscussion.innerHTML = ``; + buttonActiveDiscussion.addEventListener('click', () => { + this.activeDiscussion = true; + this.divDiscuss?.classList.add('active'); + buttonActiveDiscussion.classList.remove('active'); + }); + this.divDiscuss.appendChild(buttonCloseDiscussion); + this.divDiscuss.appendChild(buttonActiveDiscussion); + + const myName: HTMLParagraphElement = document.createElement('p'); + myName.innerText = name.toUpperCase(); + this.nbpParticipants = document.createElement('p'); + this.nbpParticipants.innerText = 'PARTICIPANTS (1)'; + + this.divParticipants = document.createElement('div'); + this.divParticipants.classList.add('participants'); + + this.divMessages = document.createElement('div'); + this.divMessages.classList.add('messages'); + this.divMessages.innerHTML = "

Local messages

" + + this.divDiscuss.appendChild(myName); + this.divDiscuss.appendChild(this.nbpParticipants); + this.divDiscuss.appendChild(this.divParticipants); + this.divDiscuss.appendChild(this.divMessages); + + const sendDivMessage: HTMLDivElement = document.createElement('div'); + sendDivMessage.classList.add('send-message'); + const inputMessage: HTMLInputElement = document.createElement('input'); + inputMessage.type = "text"; + inputMessage.addEventListener('keyup', (event: KeyboardEvent) => { + if (event.key === 'Enter') { + event.preventDefault(); + this.addMessage(name, inputMessage.value, true); + for(const callback of this.sendMessageCallBack) { + callback(inputMessage.value); + } + inputMessage.value = ""; + } + }); + sendDivMessage.appendChild(inputMessage); + this.divDiscuss.appendChild(sendDivMessage); + + //append in main container + this.mainContainer.appendChild(this.divDiscuss); + + this.addParticipant('me', 'Moi', undefined, true); + } + + public addParticipant(userId: number|string, name: string|undefined, img?: string|undefined, isMe: boolean = false) { + const divParticipant: HTMLDivElement = document.createElement('div'); + divParticipant.classList.add('participant'); + divParticipant.id = `participant-${userId}`; + + const divImgParticipant: HTMLImageElement = document.createElement('img'); + divImgParticipant.src = 'resources/logos/boy.svg'; + if (img !== undefined) { + divImgParticipant.src = img; + } + const divPParticipant: HTMLParagraphElement = document.createElement('p'); + if(!name){ + name = 'Anonymous'; + } + divPParticipant.innerText = name; + + divParticipant.appendChild(divImgParticipant); + divParticipant.appendChild(divPParticipant); + + if(!isMe) { + const reportBanUserAction: HTMLButtonElement = document.createElement('button'); + reportBanUserAction.classList.add('report-btn') + reportBanUserAction.innerText = 'Report'; + reportBanUserAction.addEventListener('click', () => { + //TODO report user + console.log('report'); + }); + divParticipant.appendChild(reportBanUserAction); + } + + this.divParticipants?.appendChild(divParticipant); + this.participants.set(userId, divParticipant); + + this.updateParticipant(this.participants.size); + } + + public updateParticipant(nb: number) { + if (!this.nbpParticipants) { + return; + } + this.nbpParticipants.innerText = `PARTICIPANTS (${nb})`; + } + + public addMessage(name: string, message: string, isMe: boolean = false) { + const divMessage: HTMLDivElement = document.createElement('div'); + divMessage.classList.add('message'); + if(isMe){ + divMessage.classList.add('me'); + } + + const pMessage: HTMLParagraphElement = document.createElement('p'); + const date = new Date(); + if(isMe){ + name = 'Moi'; + } + pMessage.innerHTML = `${name} + + ${date.getHours()}:${date.getMinutes()} + `; + divMessage.appendChild(pMessage); + + const userMessage: HTMLParagraphElement = document.createElement('p'); + userMessage.innerText = message; + userMessage.classList.add('body'); + divMessage.appendChild(userMessage); + + this.divMessages?.appendChild(divMessage); + } + + public removeParticipant(userId: number|string){ + const element = this.participants.get(userId); + if(element){ + element.remove(); + } + } + + public onSendMessageCallback(callback: SendMessageCallback): void { + this.sendMessageCallBack.add(callback); + } + +} \ No newline at end of file diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 9c3563ad..660aa599 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -1,5 +1,6 @@ import {DivImportance, layoutManager} from "./LayoutManager"; import {HtmlUtils} from "./HtmlUtils"; +import {DiscussionManager, SendMessageCallback} from "./DiscussionManager"; declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any const videoConstraint: boolean|MediaTrackConstraints = { @@ -38,6 +39,8 @@ export class MediaManager { private cinemaBtn: HTMLDivElement; private monitorBtn: HTMLDivElement; + private discussionManager: DiscussionManager; + constructor() { this.myCamVideo = HtmlUtils.getElementByIdOrFail('myCamVideo'); @@ -89,6 +92,8 @@ export class MediaManager { this.disableScreenSharing(); //update tracking }); + + this.discussionManager = new DiscussionManager('test'); } public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void { @@ -363,6 +368,9 @@ export class MediaManager { } this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail(userId)); + + //permit to create participant in discussion part + this.addNewParticipant(userId, userName); } addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){ @@ -437,6 +445,9 @@ export class MediaManager { removeActiveVideo(userId: string){ layoutManager.remove(userId); this.remoteVideo.delete(userId); + + //permit to remove user in discussion part + this.removeParticipant(userId); } removeActiveScreenSharingVideo(userId: string) { this.removeActiveVideo(`screen-sharing-${userId}`) @@ -556,7 +567,21 @@ export class MediaManager { mainContainer.appendChild(divReport); } + public addNewParticipant(userId: number|string, name: string|undefined, img?: string){ + this.discussionManager.addParticipant(userId, name, img); + } + public removeParticipant(userId: number|string){ + this.discussionManager.removeParticipant(userId) + } + + public addNewMessage(name: string, message: string, isMe: boolean = false){ + this.discussionManager.addMessage(name, message, isMe) + } + + public addSendMessageCallback(callback: SendMessageCallback){ + this.discussionManager.onSendMessageCallback(callback) + } } export const mediaManager = new MediaManager(); diff --git a/front/src/WebRtc/ScreenSharingPeer.ts b/front/src/WebRtc/ScreenSharingPeer.ts index 3efee1c3..a6cf679b 100644 --- a/front/src/WebRtc/ScreenSharingPeer.ts +++ b/front/src/WebRtc/ScreenSharingPeer.ts @@ -2,6 +2,7 @@ import * as SimplePeerNamespace from "simple-peer"; import {mediaManager} from "./MediaManager"; import {TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable"; import {RoomConnection} from "../Connexion/RoomConnection"; +import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer"; const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); @@ -148,6 +149,6 @@ export class ScreenSharingPeer extends Peer { public stopPushingScreenSharingToRemoteUser(stream: MediaStream) { this.removeStream(stream); - this.write(new Buffer(JSON.stringify({streamEnded: true}))); + this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, streamEnded: true}))); } } diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index eb2ee42b..a9c24f17 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -10,7 +10,7 @@ import { UpdatedLocalStreamCallback } from "./MediaManager"; import {ScreenSharingPeer} from "./ScreenSharingPeer"; -import {VideoPeer} from "./VideoPeer"; +import {MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer"; import {RoomConnection} from "../Connexion/RoomConnection"; export interface UserSimplePeerInterface{ @@ -145,6 +145,12 @@ export class SimplePeer { mediaManager.addActiveVideo("" + user.userId, reportCallback, name); const peer = new VideoPeer(user.userId, user.initiator ? user.initiator : false, this.Connection); + + //permit to send message + mediaManager.addSendMessageCallback((message: string) => { + peer.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_MESSAGE, name: name?.toUpperCase(), message: message}))); + }); + peer.toClose = false; // When a connection is established to a video stream, and if a screen sharing is taking place, // the user sharing screen should also initiate a connection to the remote user! @@ -318,7 +324,7 @@ export class SimplePeer { throw new Error('While adding media, cannot find user with ID ' + userId); } const localStream: MediaStream | null = mediaManager.localStream; - PeerConnection.write(new Buffer(JSON.stringify(mediaManager.constraintsMedia))); + PeerConnection.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...mediaManager.constraintsMedia}))); if(!localStream){ return; diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index fb34f29e..f8bfa3f9 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -5,6 +5,8 @@ import {RoomConnection} from "../Connexion/RoomConnection"; const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); +export const MESSAGE_TYPE_CONSTRAINT = 'constraint'; +export const MESSAGE_TYPE_MESSAGE = 'message'; /** * A peer connection used to transmit video / audio signals between 2 peers. */ @@ -78,19 +80,27 @@ export class VideoPeer extends Peer { }); this.on('data', (chunk: Buffer) => { - const constraint = JSON.parse(chunk.toString('utf8')); - console.log("data", constraint); - if (constraint.audio) { - mediaManager.enabledMicrophoneByUserId(this.userId); - } else { - mediaManager.disabledMicrophoneByUserId(this.userId); + const message = JSON.parse(chunk.toString('utf8')); + console.log("data", message); + + if(message.type === MESSAGE_TYPE_CONSTRAINT) { + const constraint = message; + if (constraint.audio) { + mediaManager.enabledMicrophoneByUserId(this.userId); + } else { + mediaManager.disabledMicrophoneByUserId(this.userId); + } + + if (constraint.video || constraint.screen) { + mediaManager.enabledVideoByUserId(this.userId); + } else { + this.stream(undefined); + mediaManager.disabledVideoByUserId(this.userId); + } } - if (constraint.video || constraint.screen) { - mediaManager.enabledVideoByUserId(this.userId); - } else { - this.stream(undefined); - mediaManager.disabledVideoByUserId(this.userId); + if(message.type === 'message') { + mediaManager.addNewMessage(message.name, message.message); } }); @@ -163,7 +173,7 @@ export class VideoPeer extends Peer { private pushVideoToRemoteUser() { try { const localStream: MediaStream | null = mediaManager.localStream; - this.write(new Buffer(JSON.stringify(mediaManager.constraintsMedia))); + this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...mediaManager.constraintsMedia}))); if(!localStream){ return; From bdb504722ff75c4add12a3e25cc4f4c74c14b516 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Sun, 25 Oct 2020 22:41:31 +0100 Subject: [PATCH 06/40] Fix style -webkit-focus --- front/dist/resources/style/style.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index dec0630f..234425aa 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -1,6 +1,11 @@ body{ overflow: hidden; } +body button:focus, +body img:focus, +body input:focus { + outline: -webkit-focus-ring-color auto 0; +} body .message-info{ width: 20%; height: auto; From 1e096af43b3026a5e8ced762eb35da853d750d27 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 26 Oct 2020 00:03:30 +0100 Subject: [PATCH 07/40] Fix font family --- front/dist/resources/style/style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 234425aa..762b44cd 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -1,3 +1,6 @@ +*{ + font-family: 'Open Sans', sans-serif; +} body{ overflow: hidden; } From 850be63284419a83a287ff146a320a90c1838cba Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 26 Oct 2020 00:26:10 +0100 Subject: [PATCH 08/40] Fix style --- front/dist/resources/style/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 762b44cd..4b06d926 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -792,7 +792,7 @@ div.modal-report-user{ .discussion .participants .participant p{ font-size: 16px; margin-left: 10px; - margin-top: 8px; + margin-top: 2px; } .discussion .participants .participant button.report-btn{ From f95f38b09251a6a796e3a7b16bf27d365056ea9f Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 26 Oct 2020 14:13:51 +0100 Subject: [PATCH 09/40] Permit to remove callback --- front/src/WebRtc/DiscussionManager.ts | 9 +++++---- front/src/WebRtc/MediaManager.ts | 4 ++-- front/src/WebRtc/SimplePeer.ts | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts index 4663adcf..d2a52632 100644 --- a/front/src/WebRtc/DiscussionManager.ts +++ b/front/src/WebRtc/DiscussionManager.ts @@ -15,7 +15,7 @@ export class DiscussionManager { private activeDiscussion: boolean = false; - private sendMessageCallBack : Set = new Set(); + private sendMessageCallBack : Map = new Map(); constructor(name: string) { this.mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); @@ -71,7 +71,7 @@ export class DiscussionManager { if (event.key === 'Enter') { event.preventDefault(); this.addMessage(name, inputMessage.value, true); - for(const callback of this.sendMessageCallBack) { + for(const callback of this.sendMessageCallBack.values()) { callback(inputMessage.value); } inputMessage.value = ""; @@ -160,10 +160,11 @@ export class DiscussionManager { if(element){ element.remove(); } + this.sendMessageCallBack.delete(userId); } - public onSendMessageCallback(callback: SendMessageCallback): void { - this.sendMessageCallBack.add(callback); + public onSendMessageCallback(userId: string|number, callback: SendMessageCallback): void { + this.sendMessageCallBack.set(userId, callback); } } \ No newline at end of file diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 660aa599..6c33bc32 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -579,8 +579,8 @@ export class MediaManager { this.discussionManager.addMessage(name, message, isMe) } - public addSendMessageCallback(callback: SendMessageCallback){ - this.discussionManager.onSendMessageCallback(callback) + public addSendMessageCallback(userId: string|number, callback: SendMessageCallback){ + this.discussionManager.onSendMessageCallback(userId, callback) } } diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index a9c24f17..72c9d890 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -147,7 +147,7 @@ export class SimplePeer { const peer = new VideoPeer(user.userId, user.initiator ? user.initiator : false, this.Connection); //permit to send message - mediaManager.addSendMessageCallback((message: string) => { + mediaManager.addSendMessageCallback(user.userId,(message: string) => { peer.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_MESSAGE, name: name?.toUpperCase(), message: message}))); }); From 997acd17ad8216ba2d4e0f641fa36823a2fdb0dc Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 26 Oct 2020 22:39:52 +0100 Subject: [PATCH 10/40] Update to use update function scene --- front/src/Phaser/Game/GameScene.ts | 7 ++-- front/src/WebRtc/MediaManager.ts | 60 +++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index bfc6402e..46efc5e5 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -324,7 +324,7 @@ export class GameScene extends ResizableScene implements CenterListener { // Let's alter browser history let path = this.room.id; if (this.room.hash) { - path += '#'+this.room.hash; + path += '#' + this.room.hash; } window.history.pushState({}, 'WorkAdventure', path); @@ -486,7 +486,7 @@ export class GameScene extends ResizableScene implements CenterListener { this.stopJitsi(); } else { if (JITSI_PRIVATE_MODE) { - const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined; + const adminTag = allProps.get("jitsiRoomAdminTag") as string | undefined; this.connection.emitQueryJitsiJwtMessage(this.instance.replace('/', '-') + "-" + newValue, adminTag); } else { @@ -634,7 +634,6 @@ export class GameScene extends ResizableScene implements CenterListener { this.gameMap.setPosition(event.x, event.y); }) - this.scene.wake(); this.scene.sleep(ReconnectingSceneName); @@ -973,6 +972,8 @@ export class GameScene extends ResizableScene implements CenterListener { this.scene.remove(this.scene.key); this.scene.start(nextSceneKey.key); } + + mediaManager.setLastUpdateScene(); } private checkToExit(): {key: string, hash: string} | null { diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 28771f63..1cb1bdbb 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -39,7 +39,10 @@ export class MediaManager { private monitorBtn: HTMLDivElement; private previousConstraint : MediaStreamConstraints; - private timeoutBlurWindows?: NodeJS.Timeout; + private focused : boolean = true; + + private lastUpdateScene : Date = new Date(); + private setTimeOutlastUpdateScene? : NodeJS.Timeout; constructor() { @@ -94,22 +97,30 @@ export class MediaManager { }); this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia)); - window.addEventListener('blur', () => { - if(this.timeoutBlurWindows){ - clearTimeout(this.timeoutBlurWindows); - } - this.timeoutBlurWindows = setTimeout(() => { - this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia)); - this.disableCamera(); - }, 10000); - }); - window.addEventListener('focus', () => { - if(this.timeoutBlurWindows){ - clearTimeout(this.timeoutBlurWindows); - } - this.applyPreviousConfig(); - }); this.pingCameraStatus(); + + this.checkActiveUser(); + } + + public setLastUpdateScene(){ + this.lastUpdateScene = new Date(); + } + + public blurCamera() { + if(!this.focused){ + return; + } + this.focused = false; + this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia)); + this.disableCamera(); + } + + public focusCamera() { + if(this.focused){ + return; + } + this.focused = true; + this.applyPreviousConfig(); } public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void { @@ -631,7 +642,22 @@ export class MediaManager { }, 30000); } - + //check if user is active + private checkActiveUser(){ + if(this.setTimeOutlastUpdateScene){ + clearTimeout(this.setTimeOutlastUpdateScene); + } + this.setTimeOutlastUpdateScene = setTimeout(() => { + const now = new Date(); + //if last update is more of 10 sec + if( (now.getTime() - this.lastUpdateScene.getTime()) > 10000) { + this.blurCamera(); + }else{ + this.focusCamera(); + } + this.checkActiveUser(); + }, this.focused ? 10000 : 1000); + } } export const mediaManager = new MediaManager(); From 55c1bcee66ab8db8c86af4649d34a74a44c6b339 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 27 Oct 2020 20:18:25 +0100 Subject: [PATCH 11/40] Add callback report in discussion part --- front/dist/resources/style/style.css | 8 +++++--- front/src/WebRtc/DiscussionManager.ts | 18 ++++++++++++------ front/src/WebRtc/MediaManager.ts | 10 +++++----- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 4b06d926..6eb6f2b5 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -82,13 +82,14 @@ body .message-info.warning{ position: absolute; right: 15px; bottom: 15px; - border-radius: 15px; + border-radius: 15px 15px 15px 15px; } video#myCamVideo{ width: 15vw; -webkit-transform: scaleX(-1); transform: scaleX(-1); + border-radius: 15px 15px 15px 15px; /*width: 200px;*/ /*height: 113px;*/ } @@ -365,6 +366,7 @@ body { margin: 2%; transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s; cursor: pointer; + border-radius: 15px 15px 15px 15px; } .sidebar > div:hover { @@ -525,7 +527,7 @@ body { border: 1px solid black; background-color: #00000000; color: #ffda01; - border-radius: 10px; + border-radius: 15px; padding: 10px 30px; transition: all .2s ease; } @@ -662,7 +664,7 @@ div.modal-report-user{ border: 1px solid black; background-color: #00000000; color: #ffda01; - border-radius: 10px; + border-radius: 15px; padding: 10px 30px; transition: all .2s ease; } diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts index d2a52632..a65ede25 100644 --- a/front/src/WebRtc/DiscussionManager.ts +++ b/front/src/WebRtc/DiscussionManager.ts @@ -1,9 +1,8 @@ import {HtmlUtils} from "./HtmlUtils"; -import {UpdatedLocalStreamCallback} from "./MediaManager"; +import {MediaManager, ReportCallback, UpdatedLocalStreamCallback} from "./MediaManager"; export type SendMessageCallback = (message:string) => void; export class DiscussionManager { - private mainContainer: HTMLDivElement; private divDiscuss?: HTMLDivElement; @@ -17,7 +16,7 @@ export class DiscussionManager { private sendMessageCallBack : Map = new Map(); - constructor(name: string) { + constructor(private mediaManager: MediaManager, name: string) { this.mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); this.createDiscussPart(name); } @@ -86,7 +85,13 @@ export class DiscussionManager { this.addParticipant('me', 'Moi', undefined, true); } - public addParticipant(userId: number|string, name: string|undefined, img?: string|undefined, isMe: boolean = false) { + public addParticipant( + userId: number|string, + name: string|undefined, + img?: string|undefined, + isMe: boolean = false, + reportCallback?: ReportCallback + ) { const divParticipant: HTMLDivElement = document.createElement('div'); divParticipant.classList.add('participant'); divParticipant.id = `participant-${userId}`; @@ -110,8 +115,9 @@ export class DiscussionManager { reportBanUserAction.classList.add('report-btn') reportBanUserAction.innerText = 'Report'; reportBanUserAction.addEventListener('click', () => { - //TODO report user - console.log('report'); + if(reportCallback) { + this.mediaManager.showReportModal(`${userId}`, name ?? '', reportCallback); + } }); divParticipant.appendChild(reportBanUserAction); } diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 6c33bc32..9ad2438d 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -93,7 +93,7 @@ export class MediaManager { //update tracking }); - this.discussionManager = new DiscussionManager('test'); + this.discussionManager = new DiscussionManager(this,'test'); } public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void { @@ -370,7 +370,7 @@ export class MediaManager { this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail(userId)); //permit to create participant in discussion part - this.addNewParticipant(userId, userName); + this.addNewParticipant(userId, userName, undefined, reportCallBack); } addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){ @@ -510,7 +510,7 @@ export class MediaManager { return color; } - private showReportModal(userId: string, userName: string, reportCallBack: ReportCallback){ + public showReportModal(userId: string, userName: string, reportCallBack: ReportCallback){ //create report text area const mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); @@ -567,8 +567,8 @@ export class MediaManager { mainContainer.appendChild(divReport); } - public addNewParticipant(userId: number|string, name: string|undefined, img?: string){ - this.discussionManager.addParticipant(userId, name, img); + public addNewParticipant(userId: number|string, name: string|undefined, img?: string, reportCallBack?: ReportCallback){ + this.discussionManager.addParticipant(userId, name, img, false, reportCallBack); } public removeParticipant(userId: number|string){ From 80355e6b85c331c1197546c94b33d101d58d1384 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 27 Oct 2020 20:46:53 +0100 Subject: [PATCH 12/40] fix layout mode click when discussion is activated --- front/dist/resources/style/style.css | 3 +++ front/src/Phaser/Game/GameScene.ts | 4 ++++ front/src/WebRtc/DiscussionManager.ts | 3 +++ front/src/WebRtc/MediaManager.ts | 4 ++++ 4 files changed, 14 insertions(+) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 6eb6f2b5..a01b1723 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -379,6 +379,7 @@ body { justify-content: center; flex-direction: column; overflow: hidden; + border-radius: 15px; } .chat-mode { @@ -622,6 +623,7 @@ div.modal-report-user{ left: calc(50% - 400px); top: 100px; background-color: #000000ad; + border-radius: 15px; } .modal-report-user textarea{ @@ -633,6 +635,7 @@ div.modal-report-user{ color: white; width: calc(100% - 60px); margin: 30px; + border-radius: 15px; } .modal-report-user img{ diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index bfc6402e..39b40c74 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -643,6 +643,10 @@ export class GameScene extends ResizableScene implements CenterListener { } private switchLayoutMode(): void { + //if discussion is activated, this layout cannot be activated + if(mediaManager.activatedDiscussion){ + return; + } const mode = layoutManager.getLayoutMode(); if (mode === LayoutMode.Presentation) { layoutManager.switchLayoutMode(LayoutMode.VideoChat); diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts index a65ede25..cd063996 100644 --- a/front/src/WebRtc/DiscussionManager.ts +++ b/front/src/WebRtc/DiscussionManager.ts @@ -173,4 +173,7 @@ export class DiscussionManager { this.sendMessageCallBack.set(userId, callback); } + get activatedDiscussion(){ + return this.activeDiscussion; + } } \ No newline at end of file diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 9ad2438d..9ce449c0 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -582,6 +582,10 @@ export class MediaManager { public addSendMessageCallback(userId: string|number, callback: SendMessageCallback){ this.discussionManager.onSendMessageCallback(userId, callback) } + + get activatedDiscussion(){ + return this.discussionManager.activatedDiscussion; + } } export const mediaManager = new MediaManager(); From df49ea856a9d446c9f87742443eb763bac2e7045 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 27 Oct 2020 20:51:11 +0100 Subject: [PATCH 13/40] Fix conflict error --- front/src/WebRtc/MediaManager.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 1cb1bdbb..672b09ae 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -167,7 +167,7 @@ export class MediaManager { gameOverlay.classList.remove('active'); } - private enableCamera() { + public enableCamera() { this.enableCameraStyle(); this.constraintsMedia.video = videoConstraint; this.getCamera().then((stream: MediaStream) => { @@ -175,7 +175,7 @@ export class MediaManager { }); } - private async disableCamera() { + public async disableCamera() { this.disableCameraStyle(); this.stopCamera(); @@ -187,7 +187,7 @@ export class MediaManager { } } - private enableMicrophone() { + public enableMicrophone() { this.enableMicrophoneStyle(); this.constraintsMedia.audio = true; @@ -196,7 +196,7 @@ export class MediaManager { }); } - private async disableMicrophone() { + public async disableMicrophone() { this.disableMicrophoneStyle(); this.stopMicrophone(); @@ -574,7 +574,7 @@ export class MediaManager { return elem as T; } - private showReportModal(userId: string, userName: string, reportCallBack: ReportCallback){ + public showReportModal(userId: string, userName: string, reportCallBack: ReportCallback){ //create report text area const mainContainer = this.getElementByIdOrFail('main-container'); From a2dd3bd6ab1b530542741a6a6ebabbc9e6198752 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Fri, 30 Oct 2020 20:35:45 +0100 Subject: [PATCH 14/40] Fix style --- front/dist/resources/style/style.css | 1 + 1 file changed, 1 insertion(+) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 9e14340e..e26256b8 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -719,6 +719,7 @@ div.modal-report-user{ .discussion{ position: fixed; left: -300px; + top: 0px; width: 220px; height: 100%; background-color: #333333; From 74de2746c2088f19c9397662c2535166da35c92e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Sat, 31 Oct 2020 14:04:55 +0100 Subject: [PATCH 15/40] Create action button --- front/dist/resources/style/style.css | 27 +++++++++++ front/src/Phaser/Game/GameScene.ts | 27 +++++++---- .../src/Phaser/UserInput/UserInputManager.ts | 7 +++ front/src/WebRtc/JitsiFactory.ts | 3 ++ front/src/WebRtc/LayoutManager.ts | 46 +++++++++++++++++++ 5 files changed, 100 insertions(+), 10 deletions(-) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index edd3ddf0..9aa9bc32 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -701,3 +701,30 @@ div.modal-report-user{ max-width: calc(800px - 60px); /* size of modal - padding*/ } +/** Action button **/ +div.action{ + position: absolute; + width: 100%; + height: auto; + text-align: center; + bottom: 40px; + transition: all .5s ease; + animation: mymove .5s; + animation-iteration-count: infinite; + animation-timing-function: ease-in-out; +} +div.action p.action-body{ + padding: 10px; + background-color: #2d2d2dba; + color: #fff; + font-size: 12px; + text-align: center; + max-width: 150px; + margin-left: calc(50% - 75px); + border-radius: 15px; +} +@keyframes mymove { + 0% {bottom: 40px;} + 50% {bottom: 30px;} + 100% {bottom: 40px;} +} \ No newline at end of file diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 96648255..9aaf8ee8 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -469,24 +469,31 @@ export class GameScene extends ResizableScene implements CenterListener { this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => { if (newValue === undefined) { + layoutManager.removeActionButton('openWebsite', this.userInputManager); coWebsiteManager.closeCoWebsite(); - } else { - coWebsiteManager.loadCoWebsite(newValue as string); + }else{ + layoutManager.addActionButton('openWebsite', 'Clik on SPACE to open web site', () => { + coWebsiteManager.loadCoWebsite(newValue as string); + }, this.userInputManager); } }); + this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => { if (newValue === undefined) { + layoutManager.removeActionButton('jitsiRoom', this.userInputManager); this.stopJitsi(); - } else { - if (JITSI_PRIVATE_MODE) { - const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined; + }else{ + layoutManager.addActionButton('jitsiRoom', 'Clik on SPACE to enter in jitsi meet room', () => { + if (JITSI_PRIVATE_MODE) { + const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined; - this.connection.emitQueryJitsiJwtMessage(this.instance.replace('/', '-') + "-" + newValue, adminTag); - } else { - this.startJitsi(newValue as string); - } + this.connection.emitQueryJitsiJwtMessage(this.instance.replace('/', '-') + "-" + newValue, adminTag); + } else { + this.startJitsi(newValue as string); + } + }, this.userInputManager); } - }) + }); this.gameMap.onPropertyChange('silent', (newValue, oldValue) => { if (newValue === undefined || newValue === false || newValue === '') { diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index fa7080b5..9f4a6071 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -77,4 +77,11 @@ export class UserInputManager { return event; }); } + + addSpaceEventListner(callback : Function){ + this.Scene.input.keyboard.addListener('keyup-SPACE', callback); + } + removeSpaceEventListner(callback : Function){ + this.Scene.input.keyboard.removeListener('keyup-SPACE', callback); + } } diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 45b9b3cf..6ad69e2a 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -62,6 +62,9 @@ class JitsiFactory { } public async stop(): Promise { + if(!this.jitsiApi){ + return; + } await coWebsiteManager.closeCoWebsite(); this.jitsiApi.removeListener('audioMuteStatusChanged', this.audioCallback); this.jitsiApi.removeListener('videoMuteStatusChanged', this.videoCallback); diff --git a/front/src/WebRtc/LayoutManager.ts b/front/src/WebRtc/LayoutManager.ts index dc013563..4013f1d8 100644 --- a/front/src/WebRtc/LayoutManager.ts +++ b/front/src/WebRtc/LayoutManager.ts @@ -1,3 +1,4 @@ +import { UserInputManager } from "../Phaser/UserInput/UserInputManager"; import {HtmlUtils} from "./HtmlUtils"; export enum LayoutMode { @@ -33,6 +34,9 @@ class LayoutManager { private normalDivs: Map = new Map(); private listener: CenterListener|null = null; + private actionButtonTrigger: Map = new Map(); + private actionButtonInformation: Map = new Map(); + public setListener(centerListener: CenterListener|null) { this.listener = centerListener; } @@ -305,6 +309,48 @@ class LayoutManager { } } } + + public addActionButton(id: string, text: string, callBack: Function, userInputManager: UserInputManager){ + //delete previous element + this.removeActionButton(id, userInputManager); + + //create div and text html component + const p = document.createElement('p'); + p.classList.add('action-body'); + p.innerText = text; + + const div = document.createElement('div'); + div.classList.add('action'); + div.id = id; + div.appendChild(p); + + this.actionButtonInformation.set(id, div); + + const mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); + mainContainer.appendChild(div); + + const callBackFunctionTrigger = (() => { + console.log('user click on space => ', id); + callBack(); + }); + + //add trigger action + this.actionButtonTrigger.set(id, callBackFunctionTrigger); + userInputManager.addSpaceEventListner(callBackFunctionTrigger); + } + + public removeActionButton(id: string, userInputManager: UserInputManager){ + //delete previous element + const previousDiv = this.actionButtonInformation.get(id); + if(previousDiv){ + previousDiv.remove(); + this.actionButtonInformation.delete(id); + } + const previousEventCallback = this.actionButtonTrigger.get(id); + if(previousEventCallback){ + userInputManager.removeSpaceEventListner(previousEventCallback); + } + } } const layoutManager = new LayoutManager(); From fcb7f364b6c6f6247c9128cde47fe8a02880626b Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Sun, 1 Nov 2020 11:49:09 +0100 Subject: [PATCH 16/40] Fix button action when user have activated --- front/src/Phaser/Game/GameScene.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9aaf8ee8..f470c994 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -474,6 +474,7 @@ export class GameScene extends ResizableScene implements CenterListener { }else{ layoutManager.addActionButton('openWebsite', 'Clik on SPACE to open web site', () => { coWebsiteManager.loadCoWebsite(newValue as string); + layoutManager.removeActionButton('openWebsite', this.userInputManager); }, this.userInputManager); } }); From 6051e5f46a01fddc0ae21ddbdcdce1f97f817e36 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Sun, 1 Nov 2020 11:53:24 +0100 Subject: [PATCH 17/40] Fix when user have activated action button --- front/src/Phaser/Game/GameScene.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index f470c994..27d7d3d2 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -492,6 +492,7 @@ export class GameScene extends ResizableScene implements CenterListener { } else { this.startJitsi(newValue as string); } + layoutManager.removeActionButton('jitsiRoom', this.userInputManager); }, this.userInputManager); } }); From 47a049ecafd689ece1cb2e7fc1b4edc2a1ec6414 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 4 Nov 2020 12:42:33 +0100 Subject: [PATCH 18/40] Feedback comment @moufmouf --- front/src/WebRtc/MediaManager.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 672b09ae..37a23578 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -631,7 +631,10 @@ export class MediaManager { mainContainer.appendChild(divReport); } - //ping always 30 seconds data on stream + /** + * 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(){ setTimeout(() => { console.log('ping camera status'); From 6ef2148a34aeacc8de7caedea3810d3df63d391b Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 4 Nov 2020 13:07:38 +0100 Subject: [PATCH 19/40] Fix feedback @moufmouf --- front/src/WebRtc/DiscussionManager.ts | 36 +++++++++++++++++++++------ 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts index cd063996..37767ffa 100644 --- a/front/src/WebRtc/DiscussionManager.ts +++ b/front/src/WebRtc/DiscussionManager.ts @@ -9,6 +9,7 @@ export class DiscussionManager { private divParticipants?: HTMLDivElement; private nbpParticipants?: HTMLParagraphElement; private divMessages?: HTMLParagraphElement; + private buttonActiveDiscussion?: HTMLButtonElement; private participants: Map = new Map(); @@ -26,24 +27,23 @@ export class DiscussionManager { this.divDiscuss.classList.add('discussion'); const buttonCloseDiscussion: HTMLButtonElement = document.createElement('button'); - const buttonActiveDiscussion: HTMLButtonElement = document.createElement('button'); + this.buttonActiveDiscussion = document.createElement('button'); buttonCloseDiscussion.classList.add('close-btn'); buttonCloseDiscussion.innerHTML = ``; buttonCloseDiscussion.addEventListener('click', () => { this.activeDiscussion = false; this.divDiscuss?.classList.remove('active'); - buttonActiveDiscussion.classList.add('active'); + this.showButtonDiscussionBtn(); }); - buttonActiveDiscussion.classList.add('active-btn'); - buttonActiveDiscussion.classList.add('active'); - buttonActiveDiscussion.innerHTML = ``; - buttonActiveDiscussion.addEventListener('click', () => { + this.buttonActiveDiscussion.classList.add('active-btn'); + this.buttonActiveDiscussion.innerHTML = ``; + this.buttonActiveDiscussion.addEventListener('click', () => { this.activeDiscussion = true; this.divDiscuss?.classList.add('active'); - buttonActiveDiscussion.classList.remove('active'); + this.hideButtonDiscussionBtn(); }); this.divDiscuss.appendChild(buttonCloseDiscussion); - this.divDiscuss.appendChild(buttonActiveDiscussion); + this.divDiscuss.appendChild(this.buttonActiveDiscussion); const myName: HTMLParagraphElement = document.createElement('p'); myName.innerText = name.toUpperCase(); @@ -123,7 +123,9 @@ export class DiscussionManager { } this.divParticipants?.appendChild(divParticipant); + this.participants.set(userId, divParticipant); + this.showButtonDiscussionBtn(); this.updateParticipant(this.participants.size); } @@ -165,7 +167,13 @@ export class DiscussionManager { const element = this.participants.get(userId); if(element){ element.remove(); + this.participants.delete(userId); } + //if all participant leave, hide discussion button + if(this.participants.size === 1){ + this.hideButtonDiscussionBtn(); + } + this.sendMessageCallBack.delete(userId); } @@ -176,4 +184,16 @@ export class DiscussionManager { get activatedDiscussion(){ return this.activeDiscussion; } + + private showButtonDiscussionBtn(){ + //if it's first participant, show discussion button + if(this.activatedDiscussion || this.participants.size === 1) { + return; + } + this.buttonActiveDiscussion?.classList.add('active'); + } + + private hideButtonDiscussionBtn(){ + this.buttonActiveDiscussion?.classList.remove('active'); + } } \ No newline at end of file From 968787555fe2c91ef978f404b6380ccadaa8b5c2 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 4 Nov 2020 13:33:58 +0100 Subject: [PATCH 20/40] Update feedback @moufmouf --- front/src/Phaser/Game/GameScene.ts | 32 +++++++++++++++++++++++------- maps/Floor0/floor0.json | 17 +++++++++++++++- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 27d7d3d2..882681ed 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -467,24 +467,33 @@ export class GameScene extends ResizableScene implements CenterListener { // From now, this game scene will be notified of reposition events layoutManager.setListener(this); - this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => { + this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue, allProps) => { if (newValue === undefined) { layoutManager.removeActionButton('openWebsite', this.userInputManager); coWebsiteManager.closeCoWebsite(); }else{ - layoutManager.addActionButton('openWebsite', 'Clik on SPACE to open web site', () => { + const openWebsiteFunction = () => { coWebsiteManager.loadCoWebsite(newValue as string); layoutManager.removeActionButton('openWebsite', this.userInputManager); - }, this.userInputManager); + }; + + const openWebsiteTriggerValue = allProps.get('openWebsiteTrigger'); + if(openWebsiteTriggerValue && openWebsiteTriggerValue === 'onaction') { + layoutManager.addActionButton('openWebsite', 'Clik on SPACE to open web site', () => { + openWebsiteFunction(); + }, this.userInputManager); + }else{ + openWebsiteFunction(); + } } }); - + this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => { if (newValue === undefined) { layoutManager.removeActionButton('jitsiRoom', this.userInputManager); this.stopJitsi(); }else{ - layoutManager.addActionButton('jitsiRoom', 'Clik on SPACE to enter in jitsi meet room', () => { + const openJitsiRoomFunction = () => { if (JITSI_PRIVATE_MODE) { const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined; @@ -493,7 +502,16 @@ export class GameScene extends ResizableScene implements CenterListener { this.startJitsi(newValue as string); } layoutManager.removeActionButton('jitsiRoom', this.userInputManager); - }, this.userInputManager); + } + + const jitsiTriggerValue = allProps.get('jitsiTrigger'); + if(jitsiTriggerValue && jitsiTriggerValue === 'onaction') { + layoutManager.addActionButton('jitsiRoom', 'Clik on SPACE to enter in jitsi meet room', () => { + openJitsiRoomFunction(); + }, this.userInputManager); + }else{ + openJitsiRoomFunction(); + } } }); @@ -678,7 +696,7 @@ export class GameScene extends ResizableScene implements CenterListener { if (!properties) { return undefined; } - const obj = properties.find((property: ITiledMapLayerProperty) => property.name === name); + const obj = properties.find((property: ITiledMapLayerProperty) => property.name.toLowerCase() === name.toLowerCase()); if (obj === undefined) { return undefined; } diff --git a/maps/Floor0/floor0.json b/maps/Floor0/floor0.json index 7e6f179b..f03778e6 100644 --- a/maps/Floor0/floor0.json +++ b/maps/Floor0/floor0.json @@ -44,6 +44,11 @@ "name":"jitsiRoom", "type":"string", "value":"tcm-chillzone-2" + }, + { + "name":"jitsiTrigger", + "type":"string", + "value":"auto" }], "type":"tilelayer", "visible":true, @@ -61,7 +66,12 @@ { "name":"jitsiRoom", "type":"string", - "value":"tcm-chillzone-1" + "value":"tcm-chillzone-11" + }, + { + "name":"jitsiTrigger", + "type":"string", + "value":"onaction" }], "type":"tilelayer", "visible":true, @@ -128,6 +138,11 @@ "name":"openWebsite", "type":"string", "value":"https:\/\/app.swile.co\/" + }, + { + "name":"openWebsiteTrigger", + "type":"string", + "value":"auto" }], "type":"tilelayer", "visible":true, From a1d3b016198e5c8b3862e97591d3b0882839da94 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 4 Nov 2020 16:30:01 +0100 Subject: [PATCH 21/40] Fix typo --- front/src/Phaser/Game/GameScene.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 882681ed..3e9361ff 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -479,7 +479,7 @@ export class GameScene extends ResizableScene implements CenterListener { const openWebsiteTriggerValue = allProps.get('openWebsiteTrigger'); if(openWebsiteTriggerValue && openWebsiteTriggerValue === 'onaction') { - layoutManager.addActionButton('openWebsite', 'Clik on SPACE to open web site', () => { + layoutManager.addActionButton('openWebsite', 'Click on SPACE to open web site', () => { openWebsiteFunction(); }, this.userInputManager); }else{ @@ -506,7 +506,7 @@ export class GameScene extends ResizableScene implements CenterListener { const jitsiTriggerValue = allProps.get('jitsiTrigger'); if(jitsiTriggerValue && jitsiTriggerValue === 'onaction') { - layoutManager.addActionButton('jitsiRoom', 'Clik on SPACE to enter in jitsi meet room', () => { + layoutManager.addActionButton('jitsiRoom', 'Click on SPACE to enter in jitsi meet room', () => { openJitsiRoomFunction(); }, this.userInputManager); }else{ From 3333b3cee3beae37606c7f0cae7f7c5afd0dfe5e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 10 Nov 2020 12:38:32 +0100 Subject: [PATCH 22/40] Fix feedback moufmouf --- front/dist/resources/style/style.css | 3 ++- front/src/Phaser/Game/GameScene.ts | 1 + front/src/WebRtc/DiscussionManager.ts | 36 ++++++++++++++++++++++++--- front/src/WebRtc/MediaManager.ts | 15 ++++++++--- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index e26256b8..fc055941 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -840,7 +840,7 @@ div.modal-report-user{ position: absolute; height: calc(100% - 360px); overflow-x: hidden; - overflow-y: scroll; + overflow-y: auto; max-width: calc(100% - 40px); width: calc(100% - 40px); } @@ -887,6 +887,7 @@ div.modal-report-user{ color: white; border-radius: 15px; border: none; + padding: 6px; } .discussion .send-message img{ diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index dd2694f8..6aa678a4 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -387,6 +387,7 @@ export class GameScene extends ResizableScene implements CenterListener { //create input to move this.userInputManager = new UserInputManager(this); + mediaManager.setUserInputManager(this.userInputManager); //notify game manager can to create currentUser in map this.createCurrentPlayer(); diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts index 37767ffa..1671f661 100644 --- a/front/src/WebRtc/DiscussionManager.ts +++ b/front/src/WebRtc/DiscussionManager.ts @@ -1,5 +1,6 @@ import {HtmlUtils} from "./HtmlUtils"; import {MediaManager, ReportCallback, UpdatedLocalStreamCallback} from "./MediaManager"; +import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; export type SendMessageCallback = (message:string) => void; export class DiscussionManager { @@ -17,6 +18,8 @@ export class DiscussionManager { private sendMessageCallBack : Map = new Map(); + private userInputManager?: UserInputManager; + constructor(private mediaManager: MediaManager, name: string) { this.mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); this.createDiscussPart(name); @@ -31,15 +34,13 @@ export class DiscussionManager { buttonCloseDiscussion.classList.add('close-btn'); buttonCloseDiscussion.innerHTML = ``; buttonCloseDiscussion.addEventListener('click', () => { - this.activeDiscussion = false; - this.divDiscuss?.classList.remove('active'); + this.hideDiscussion(); this.showButtonDiscussionBtn(); }); this.buttonActiveDiscussion.classList.add('active-btn'); this.buttonActiveDiscussion.innerHTML = ``; this.buttonActiveDiscussion.addEventListener('click', () => { - this.activeDiscussion = true; - this.divDiscuss?.classList.add('active'); + this.showDiscussion(); this.hideButtonDiscussionBtn(); }); this.divDiscuss.appendChild(buttonCloseDiscussion); @@ -69,6 +70,11 @@ export class DiscussionManager { inputMessage.addEventListener('keyup', (event: KeyboardEvent) => { if (event.key === 'Enter') { event.preventDefault(); + if(inputMessage.value === null + || inputMessage.value === '' + || inputMessage.value === undefined) { + return; + } this.addMessage(name, inputMessage.value, true); for(const callback of this.sendMessageCallBack.values()) { callback(inputMessage.value); @@ -117,6 +123,8 @@ export class DiscussionManager { reportBanUserAction.addEventListener('click', () => { if(reportCallback) { this.mediaManager.showReportModal(`${userId}`, name ?? '', reportCallback); + }else{ + console.info('report feature is not activated!'); } }); divParticipant.appendChild(reportBanUserAction); @@ -193,7 +201,27 @@ export class DiscussionManager { this.buttonActiveDiscussion?.classList.add('active'); } + private showDiscussion(){ + this.activeDiscussion = true; + if(this.userInputManager) { + this.userInputManager.clearAllInputKeyboard(); + } + this.divDiscuss?.classList.add('active'); + } + + private hideDiscussion(){ + this.activeDiscussion = false; + if(this.userInputManager) { + this.userInputManager.initKeyBoardEvent(); + } + this.divDiscuss?.classList.remove('active'); + } + private hideButtonDiscussionBtn(){ this.buttonActiveDiscussion?.classList.remove('active'); } + + public setUserInputManager(userInputManager : UserInputManager){ + this.userInputManager = userInputManager; + } } \ No newline at end of file diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 6dc35008..907b2c0b 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -1,6 +1,7 @@ import {DivImportance, layoutManager} from "./LayoutManager"; import {HtmlUtils} from "./HtmlUtils"; import {DiscussionManager, SendMessageCallback} from "./DiscussionManager"; +import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any const videoConstraint: boolean|MediaTrackConstraints = { @@ -41,6 +42,8 @@ export class MediaManager { private discussionManager: DiscussionManager; + private userInputManager?: UserInputManager; + constructor() { this.myCamVideo = HtmlUtils.getElementByIdOrFail('myCamVideo'); @@ -93,7 +96,7 @@ export class MediaManager { //update tracking }); - this.discussionManager = new DiscussionManager(this,'test'); + this.discussionManager = new DiscussionManager(this,''); } public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void { @@ -572,20 +575,24 @@ export class MediaManager { } public removeParticipant(userId: number|string){ - this.discussionManager.removeParticipant(userId) + this.discussionManager.removeParticipant(userId); } public addNewMessage(name: string, message: string, isMe: boolean = false){ - this.discussionManager.addMessage(name, message, isMe) + this.discussionManager.addMessage(name, message, isMe); } public addSendMessageCallback(userId: string|number, callback: SendMessageCallback){ - this.discussionManager.onSendMessageCallback(userId, callback) + this.discussionManager.onSendMessageCallback(userId, callback); } get activatedDiscussion(){ return this.discussionManager.activatedDiscussion; } + + public setUserInputManager(userInputManager : UserInputManager){ + this.discussionManager.setUserInputManager(userInputManager); + } } export const mediaManager = new MediaManager(); From 0acbe20bbf9d2db310b25b1410c9c3a1a17911f1 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 10 Nov 2020 13:00:14 +0100 Subject: [PATCH 23/40] Fix name send in message --- front/src/Phaser/Game/GameScene.ts | 2 +- front/src/WebRtc/SimplePeer.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 6aa678a4..d34541dd 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -603,7 +603,7 @@ export class GameScene extends ResizableScene implements CenterListener { }); // When connection is performed, let's connect SimplePeer - this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic); + this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic, this.GameManager.getPlayerName()); this.GlobalMessageManager = new GlobalMessageManager(this.connection); this.UserMessageManager = new UserMessageManager(this.connection); diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index 72c9d890..195b57b3 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -38,7 +38,7 @@ export class SimplePeer { private readonly stopLocalScreenSharingStreamCallback: StopScreenSharingCallback; private readonly peerConnectionListeners: Array = new Array(); - constructor(private Connection: RoomConnection, private enableReporting: boolean) { + constructor(private Connection: RoomConnection, private enableReporting: boolean, private myName: string) { // We need to go through this weird bound function pointer in order to be able to "free" this reference later. this.sendLocalVideoStreamCallback = this.sendLocalVideoStream.bind(this); this.sendLocalScreenSharingStreamCallback = this.sendLocalScreenSharingStream.bind(this); @@ -148,7 +148,7 @@ export class SimplePeer { //permit to send message mediaManager.addSendMessageCallback(user.userId,(message: string) => { - peer.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_MESSAGE, name: name?.toUpperCase(), message: message}))); + peer.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_MESSAGE, name: this.myName.toUpperCase(), message: message}))); }); peer.toClose = false; From 73fa0ecbfc9eabb5fb6a51386fe64632dad99d25 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 10 Nov 2020 17:00:23 +0100 Subject: [PATCH 24/40] Fix init position and trigger layers properties --- front/src/Phaser/Game/GameScene.ts | 68 +++++++++++++++++------------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 96648255..d632045f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -466,35 +466,7 @@ export class GameScene extends ResizableScene implements CenterListener { // From now, this game scene will be notified of reposition events layoutManager.setListener(this); - - this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => { - if (newValue === undefined) { - coWebsiteManager.closeCoWebsite(); - } else { - coWebsiteManager.loadCoWebsite(newValue as string); - } - }); - this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => { - if (newValue === undefined) { - this.stopJitsi(); - } else { - if (JITSI_PRIVATE_MODE) { - const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined; - - this.connection.emitQueryJitsiJwtMessage(this.instance.replace('/', '-') + "-" + newValue, adminTag); - } else { - this.startJitsi(newValue as string); - } - } - }) - - this.gameMap.onPropertyChange('silent', (newValue, oldValue) => { - if (newValue === undefined || newValue === false || newValue === '') { - this.connection.setSilent(false); - } else { - this.connection.setSilent(true); - } - }); + this.triggerOnMapLayerPropertyChange(); const camera = this.cameras.main; @@ -627,14 +599,49 @@ export class GameScene extends ResizableScene implements CenterListener { this.gameMap.setPosition(event.x, event.y); }) - this.scene.wake(); this.scene.sleep(ReconnectingSceneName); + //init connection in silent mode + this.connection.setSilent(true); + + //init user position and play trigger to check layers properties + this.gameMap.setPosition(this.CurrentPlayer.x, this.CurrentPlayer.y); + return connection; }); } + private triggerOnMapLayerPropertyChange(){ + this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => { + if (newValue === undefined) { + coWebsiteManager.closeCoWebsite(); + } else { + coWebsiteManager.loadCoWebsite(newValue as string); + } + }); + this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => { + if (newValue === undefined) { + this.stopJitsi(); + } else { + if (JITSI_PRIVATE_MODE) { + const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined; + + this.connection.emitQueryJitsiJwtMessage(this.instance.replace('/', '-') + "-" + newValue, adminTag); + } else { + this.startJitsi(newValue as string); + } + } + }) + this.gameMap.onPropertyChange('silent', (newValue, oldValue) => { + if (newValue === undefined || newValue === false || newValue === '') { + this.connection.setSilent(false); + } else { + this.connection.setSilent(true); + } + }); + } + private switchLayoutMode(): void { const mode = layoutManager.getLayoutMode(); if (mode === LayoutMode.Presentation) { @@ -713,6 +720,7 @@ export class GameScene extends ResizableScene implements CenterListener { */ //todo: push that into the gameManager private loadNextGame(layer: ITiledMapLayer, mapWidth: number, roomId: string){ + const room = new Room(roomId); gameManager.loadMap(room, this.scene); const exitSceneKey = roomId; From 2d0fc1072ff984fa96a91c2cece2222eea35d54f Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 16 Nov 2020 15:05:51 +0100 Subject: [PATCH 25/40] Fix feedback --- front/src/Phaser/Game/GameScene.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index d632045f..7c467862 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -602,9 +602,6 @@ export class GameScene extends ResizableScene implements CenterListener { this.scene.wake(); this.scene.sleep(ReconnectingSceneName); - //init connection in silent mode - this.connection.setSilent(true); - //init user position and play trigger to check layers properties this.gameMap.setPosition(this.CurrentPlayer.x, this.CurrentPlayer.y); From b8bafe9c7effb600a077b8f1025cc1e1e0e3b2e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Nov 2020 14:26:58 +0000 Subject: [PATCH 26/40] Bump dot-prop from 4.2.0 to 4.2.1 in /website Bumps [dot-prop](https://github.com/sindresorhus/dot-prop) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/sindresorhus/dot-prop/releases) - [Commits](https://github.com/sindresorhus/dot-prop/compare/v4.2.0...v4.2.1) Signed-off-by: dependabot[bot] --- website/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/package-lock.json b/website/package-lock.json index 286ca3f0..947eb4ff 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -2251,9 +2251,9 @@ } }, "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", "dev": true, "requires": { "is-obj": "^1.0.0" From 64d00bda562bf58dac42d3ffc606a9375bdc842e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 17 Nov 2020 16:46:46 +0100 Subject: [PATCH 27/40] Add function to show when message received --- front/src/WebRtc/DiscussionManager.ts | 8 ++++++-- front/src/WebRtc/MediaManager.ts | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts index 1671f661..097bf3a3 100644 --- a/front/src/WebRtc/DiscussionManager.ts +++ b/front/src/WebRtc/DiscussionManager.ts @@ -40,8 +40,7 @@ export class DiscussionManager { this.buttonActiveDiscussion.classList.add('active-btn'); this.buttonActiveDiscussion.innerHTML = ``; this.buttonActiveDiscussion.addEventListener('click', () => { - this.showDiscussion(); - this.hideButtonDiscussionBtn(); + this.showDiscussionPart(); }); this.divDiscuss.appendChild(buttonCloseDiscussion); this.divDiscuss.appendChild(this.buttonActiveDiscussion); @@ -224,4 +223,9 @@ export class DiscussionManager { public setUserInputManager(userInputManager : UserInputManager){ this.userInputManager = userInputManager; } + + public showDiscussionPart(){ + this.showDiscussion(); + this.hideButtonDiscussionBtn(); + } } \ No newline at end of file diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 1c60dda8..ce1878fb 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -597,6 +597,11 @@ export class MediaManager { public addNewMessage(name: string, message: string, isMe: boolean = false){ this.discussionManager.addMessage(name, message, isMe); + + //when there are new message, show discussion + if(!this.discussionManager.activatedDiscussion) { + this.discussionManager.showDiscussionPart(); + } } public addSendMessageCallback(userId: string|number, callback: SendMessageCallback){ From 0a7063a47822255c7152bdc6e00db375d1b571a6 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 17 Nov 2020 18:03:44 +0100 Subject: [PATCH 28/40] Add close button --- front/dist/index.html | 6 +++++- front/dist/resources/style/style.css | 17 +++++++++++++++++ front/src/Phaser/Game/GameScene.ts | 7 +++++++ front/src/WebRtc/CoWebsiteManager.ts | 8 ++++++-- front/src/WebRtc/MediaManager.ts | 27 +++++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 3 deletions(-) diff --git a/front/dist/index.html b/front/dist/index.html index 0e696622..5806bef7 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -72,7 +72,11 @@ - +
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index edd3ddf0..4a74fdae 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -274,6 +274,23 @@ body { #cowebsite.hidden { transform: translateX(100%); } + + #cowebsite .close-btn{ + position: absolute; + top: 10px; + right: -100px; + background: none; + border: none; + cursor: pointer; + animation: right .2s ease; + } + #cowebsite .close-btn img{ + height: 15px; + right: 15px; + } + #cowebsite:hover .close-btn{ + right: 10px; + } } @media (max-aspect-ratio: 1/1) { .game-overlay { diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index ae689775..2d6f1eec 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1196,12 +1196,19 @@ export class GameScene extends ResizableScene implements CenterListener { jitsiFactory.start(roomName, gameManager.getPlayerName(), jwt); this.connection.setSilent(true); mediaManager.hideGameOverlay(); + + //permit to stop jitsi when user close iframe + mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => { + this.stopJitsi(); + }); } public stopJitsi(): void { this.connection.setSilent(false); jitsiFactory.stop(); mediaManager.showGameOverlay(); + + mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi'); } private loadSpritesheet(name: string, url: string): Promise { diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index b625bf6e..e6d5b748 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -44,7 +44,9 @@ class CoWebsiteManager { public loadCoWebsite(url: string): void { this.load(); - this.cowebsiteDiv.innerHTML = ''; + this.cowebsiteDiv.innerHTML = ``; const iframe = document.createElement('iframe'); iframe.id = 'cowebsite-iframe'; @@ -83,7 +85,9 @@ class CoWebsiteManager { this.close(); this.fire(); setTimeout(() => { - this.cowebsiteDiv.innerHTML = ''; + this.cowebsiteDiv.innerHTML = ``; resolve(); }, animationTime) })); diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index fa703fbe..1f0988de 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -40,6 +40,8 @@ export class MediaManager { private hasCamera = true; + private triggerCloseJistiFrame : Map = new Map(); + constructor() { this.myCamVideo = this.getElementByIdOrFail('myCamVideo'); @@ -130,11 +132,23 @@ export class MediaManager { public showGameOverlay(){ const gameOverlay = this.getElementByIdOrFail('game-overlay'); gameOverlay.classList.add('active'); + + const buttonCloseFrame = HtmlUtils.getElementByIdOrFail('cowebsite-close'); + const functionTrigger = () => { + this.triggerCloseJitsiFrameButton(); + } + buttonCloseFrame.removeEventListener('click', functionTrigger); } public hideGameOverlay(){ const gameOverlay = this.getElementByIdOrFail('game-overlay'); gameOverlay.classList.remove('active'); + + const buttonCloseFrame = HtmlUtils.getElementByIdOrFail('cowebsite-close'); + const functionTrigger = () => { + this.triggerCloseJitsiFrameButton(); + } + buttonCloseFrame.addEventListener('click', functionTrigger); } public enableCamera() { @@ -582,6 +596,19 @@ export class MediaManager { mainContainer.appendChild(divReport); } + public addTriggerCloseJitsiFrameButton(id: String, Function: Function){ + this.triggerCloseJistiFrame.set(id, Function); + } + + public removeTriggerCloseJitsiFrameButton(id: String){ + this.triggerCloseJistiFrame.delete(id); + } + + private triggerCloseJitsiFrameButton(): void { + for (const callback of this.triggerCloseJistiFrame.values()) { + callback(); + } + } } From 25f6fa7d2acae06e3833b92011185240c89788d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Tue, 17 Nov 2020 18:27:33 +0100 Subject: [PATCH 29/40] Migrating to rlespinasse/github-slug-action@3.1.0 --- .github/workflows/build-and-deploy.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 390f2556..4cf2a8fb 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -20,7 +20,7 @@ jobs: # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@1.1.1 + - uses: rlespinasse/github-slug-action@3.1.0 - name: "Build and push front image" uses: docker/build-push-action@v1 @@ -43,7 +43,7 @@ jobs: uses: actions/checkout@v2 # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@1.1.1 + - uses: rlespinasse/github-slug-action@3.1.0 - name: "Build and push back image" uses: docker/build-push-action@v1 @@ -66,7 +66,7 @@ jobs: uses: actions/checkout@v2 # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@1.1.1 + - uses: rlespinasse/github-slug-action@3.1.0 - name: "Build and push back image" uses: docker/build-push-action@v1 @@ -90,7 +90,7 @@ jobs: # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@1.1.1 + - uses: rlespinasse/github-slug-action@3.1.0 - name: "Build and push front image" uses: docker/build-push-action@v1 @@ -114,7 +114,7 @@ jobs: uses: actions/checkout@v2 # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@1.1.0 + - uses: rlespinasse/github-slug-action@3.1.0 - name: Deploy uses: thecodingmachine/deeployer@master From 62a1732e53c5f9a15ba7ae673a8068db7f3295e6 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 18 Nov 2020 08:40:42 +0100 Subject: [PATCH 30/40] wording changes --- website/dist/index.html | 96 +++++++++++++++++---- website/dist/static/images/facebook_bw.png | Bin 0 -> 1409 bytes website/dist/static/images/linkedin_bw.png | Bin 0 -> 1712 bytes website/dist/static/images/twitter_bw.png | Bin 0 -> 1455 bytes website/src/sass/styles.scss | 45 +++++----- 5 files changed, 100 insertions(+), 41 deletions(-) create mode 100644 website/dist/static/images/facebook_bw.png create mode 100644 website/dist/static/images/linkedin_bw.png create mode 100644 website/dist/static/images/twitter_bw.png diff --git a/website/dist/index.html b/website/dist/index.html index 1e06204a..167cea49 100644 --- a/website/dist/index.html +++ b/website/dist/index.html @@ -10,6 +10,9 @@ gtag('js', new Date()); gtag('config', 'UA-10196481-11'); + if (window.location.host.endsWith("localhost")){ + window['ga-disable-UA-10196481-11'] = true; + } @@ -82,22 +85,32 @@
-

Your workplace
but better

-

You are impatient to discover this new world? Click on "Work online" and meet new people or share this adventure with your colleagues and friends by clicking on "Work in private"

+

Meet your teammates

+

+ WorkAdventure preserves your social interaction while COVID is still out there. +

+

+ Stay connected with your teamworkers, by creating your own online workspace to work remotely. +

+

+ Stay connected with your clients by providing a dedicated digital place to organize meetings, workshops. +

+

+ Stay connected with your future collaborators by organizing online event. +

@@ -107,7 +120,7 @@
@@ -158,7 +171,7 @@

Click the button below to come and say hi!

- START IN PUBLIC MODE + TRY IT NOW !

@@ -166,10 +179,11 @@

You can also create a private room with your friends or your team !

To try, press button work in private

-

- - START WORKING IN PRIVATE -

+

+ + + CHOOSE YOU OWN MAP +

Don’t forget to activate your mic and camera, let’s play

@@ -179,7 +193,6 @@ @@ -82,22 +85,32 @@
-

Your workplace
but better

-

You are impatient to discover this new world? Click on "Work online" and meet new people or share this adventure with your colleagues and friends by clicking on "Work in private"

+

Meet your teammates

+

+ WorkAdventure preserves your social interaction while COVID is still out there. +

+

+ Stay connected with your teamworkers, by creating your own online workspace to work remotely. +

+

+ Stay connected with your clients by providing a dedicated digital place to organize meetings, workshops. +

+

+ Stay connected with your future collaborators by organizing online event. +

@@ -107,7 +120,7 @@
@@ -158,7 +171,7 @@

Click the button below to come and say hi!

- START IN PUBLIC MODE + TRY IT NOW !

@@ -166,10 +179,11 @@

You can also create a private room with your friends or your team !

To try, press button work in private

-

- - START WORKING IN PRIVATE -

+

+ + + CHOOSE YOU OWN MAP +

Don’t forget to activate your mic and camera, let’s play

@@ -179,7 +193,6 @@