From b6fe9e72e1aaf3622471e094839271a51a72710b Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Sun, 25 Oct 2020 19:38:00 +0100 Subject: [PATCH 01/15] 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 02/15] 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 03/15] 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 04/15] 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 05/15] 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 06/15] 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 07/15] 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 55c1bcee66ab8db8c86af4649d34a74a44c6b339 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 27 Oct 2020 20:18:25 +0100 Subject: [PATCH 08/15] 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 09/15] 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 a2dd3bd6ab1b530542741a6a6ebabbc9e6198752 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Fri, 30 Oct 2020 20:35:45 +0100 Subject: [PATCH 10/15] 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 6ef2148a34aeacc8de7caedea3810d3df63d391b Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 4 Nov 2020 13:07:38 +0100 Subject: [PATCH 11/15] 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 3333b3cee3beae37606c7f0cae7f7c5afd0dfe5e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 10 Nov 2020 12:38:32 +0100 Subject: [PATCH 12/15] 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 13/15] 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 64d00bda562bf58dac42d3ffc606a9375bdc842e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 17 Nov 2020 16:46:46 +0100 Subject: [PATCH 14/15] 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 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 15/15] 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