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 5fb43e07..10e16cfa 100644
--- a/front/dist/resources/style/style.css
+++ b/front/dist/resources/style/style.css
@@ -284,6 +284,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 6e85e6cf..a07b4c3a 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -317,7 +317,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);
@@ -932,6 +932,7 @@ export class GameScene extends ResizableScene implements CenterListener {
* @param delta The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
*/
update(time: number, delta: number) : void {
+ mediaManager.setLastUpdateScene();
this.currentTick = time;
this.CurrentPlayer.moveUser(delta);
@@ -1227,12 +1228,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 ce1878fb..943e13c2 100644
--- a/front/src/WebRtc/MediaManager.ts
+++ b/front/src/WebRtc/MediaManager.ts
@@ -40,12 +40,20 @@ export class MediaManager {
private cinemaBtn: HTMLDivElement;
private monitorBtn: HTMLDivElement;
+ private previousConstraint : MediaStreamConstraints;
+ private focused : boolean = true;
+
+ private lastUpdateScene : Date = new Date();
+ private setTimeOutlastUpdateScene? : NodeJS.Timeout;
+
private discussionManager: DiscussionManager;
private userInputManager?: UserInputManager;
private hasCamera = true;
+ private triggerCloseJistiFrame : Map = new Map();
+
constructor() {
this.myCamVideo = HtmlUtils.getElementByIdOrFail('myCamVideo');
@@ -98,9 +106,35 @@ export class MediaManager {
//update tracking
});
+ this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia));
+ this.pingCameraStatus();
+
+ this.checkActiveUser();
+
this.discussionManager = new DiscussionManager(this,'');
}
+ 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 {
this.updatedLocalStreamCallBacks.add(callback);
}
@@ -138,20 +172,27 @@ export class MediaManager {
public showGameOverlay(){
const gameOverlay = HtmlUtils.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 = HtmlUtils.getElementByIdOrFail('game-overlay');
gameOverlay.classList.remove('active');
+
+ const buttonCloseFrame = HtmlUtils.getElementByIdOrFail('cowebsite-close');
+ const functionTrigger = () => {
+ this.triggerCloseJitsiFrameButton();
+ }
+ buttonCloseFrame.addEventListener('click', functionTrigger);
}
public enableCamera() {
- if(!this.hasCamera){
- return;
- }
- 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);
@@ -159,7 +200,8 @@ export class MediaManager {
}
public async disableCamera() {
- this.disabledCameraView();
+ this.disableCameraStyle();
+
if (this.constraintsMedia.audio !== false) {
const stream = await this.getCamera();
this.triggerUpdatedLocalStreamCallbacks(stream);
@@ -168,19 +210,8 @@ export class MediaManager {
}
}
- private disabledCameraView(){
- this.cinemaClose.style.display = "block";
- this.cinema.style.display = "none";
- this.cinemaBtn.classList.add("disabled");
- this.constraintsMedia.video = false;
- this.myCamVideo.srcObject = null;
- this.stopCamera();
- }
-
public 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) => {
@@ -189,10 +220,7 @@ export class MediaManager {
}
public 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) {
@@ -203,6 +231,52 @@ 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;
+ this.stopCamera();
+ }
+
+ 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";
@@ -285,7 +359,7 @@ export class MediaManager {
return this.getLocalStream().catch(() => {
console.info('Error get camera, trying with video option at null');
- this.disabledCameraView();
+ this.disableCameraStyle();
return this.getLocalStream().then((stream : MediaStream) => {
this.hasCamera = false;
return stream;
@@ -594,6 +668,30 @@ export class MediaManager {
public removeParticipant(userId: number|string){
this.discussionManager.removeParticipant(userId);
}
+ 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();
+ }
+ }
+ /**
+ * 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');
+ this.triggerUpdatedLocalStreamCallbacks(this.localStream);
+ this.pingCameraStatus();
+ }, 30000);
+ }
public addNewMessage(name: string, message: string, isMe: boolean = false){
this.discussionManager.addMessage(name, message, isMe);
@@ -615,6 +713,22 @@ export class MediaManager {
public setUserInputManager(userInputManager : UserInputManager){
this.discussionManager.setUserInputManager(userInputManager);
}
+ //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();
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"