diff --git a/front/dist/resources/logos/monitor-yellow.svg b/front/dist/resources/logos/monitor-yellow.svg
new file mode 100644
index 00000000..f26d30c1
--- /dev/null
+++ b/front/dist/resources/logos/monitor-yellow.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 10e16cfa..0fa951bc 100644
--- a/front/dist/resources/style/style.css
+++ b/front/dist/resources/style/style.css
@@ -74,6 +74,10 @@ body .message-info.warning{
height: 100%;
}
+.video-container video:focus{
+ outline: none;
+}
+
.video-container#div-myCamVideo{
border: none;
}
@@ -455,6 +459,8 @@ body {
flex-grow: 5;
}
+/*CONSOLE*/
+
.message-container,
.main-console{
position: absolute;
@@ -467,6 +473,7 @@ body {
background: #333333;
z-index: 200;
transition: all 0.1s ease-out;
+ border-radius: 0 0 15px 15px;
}
.message-container{
@@ -499,6 +506,16 @@ body {
text-align: center;
}
+.main-console div.message,
+.main-console div.setting{
+ display: none;
+}
+
+.main-console div.message.active,
+.main-console div.setting.active{
+ display: block;
+}
+
.message-container div.clear{
width: 100px;
left: calc(50% - 50px);
@@ -648,6 +665,28 @@ body {
margin-left: 10px;
}
+/* VIDEO QUALITY */
+.main-console div.setting h1{
+ color: white;
+}
+.main-console div.setting select{
+ background: black;
+ color: white;
+ min-width: 280px;
+ text-align: center;
+ min-height: 40px;
+ padding: 10px;
+ border-radius: 15px;
+}
+.main-console div.setting select:focus{
+ border: solid 1px white;
+ outline: none;
+}
+.main-console div.setting.active section{
+ display: block;
+}
+
+
/*REPORT input*/
div.modal-report-user{
position: absolute;
diff --git a/front/src/Administration/ConsoleGlobalMessageManager.ts b/front/src/Administration/ConsoleGlobalMessageManager.ts
index a79ecf75..03929a46 100644
--- a/front/src/Administration/ConsoleGlobalMessageManager.ts
+++ b/front/src/Administration/ConsoleGlobalMessageManager.ts
@@ -3,11 +3,15 @@ import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {RoomConnection} from "../Connexion/RoomConnection";
import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
import {ADMIN_URL} from "../Enum/EnvironmentVariable";
+import {mediaManager} from "../WebRtc/MediaManager";
export const CLASS_CONSOLE_MESSAGE = 'main-console';
export const INPUT_CONSOLE_MESSAGE = 'input-send-text';
export const UPLOAD_CONSOLE_MESSAGE = 'input-upload-music';
export const INPUT_TYPE_CONSOLE = 'input-type';
+export const GAME_QUALITY_SELECT = 'select-game-quality';
+export const VIDEO_QUALITY_SELECT = 'select-video-quality';
+export const VIDEO_QUALITY_CONSOLE = 'video-quality';
export const AUDIO_TYPE = 'audio';
export const MESSAGE_TYPE = 'message';
@@ -19,19 +23,30 @@ interface EventTargetFiles extends EventTarget {
export class ConsoleGlobalMessageManager {
private readonly divMainConsole: HTMLDivElement;
+ private readonly divMessageConsole: HTMLDivElement;
+ private readonly divSettingConsole: HTMLDivElement;
private readonly buttonMainConsole: HTMLDivElement;
private readonly buttonSendMainConsole: HTMLImageElement;
+ private readonly buttonAdminMainConsole: HTMLImageElement;
private readonly buttonSettingsMainConsole: HTMLImageElement;
private activeConsole: boolean = false;
+ private activeMessage: boolean = false;
+ private activeSetting: boolean = false;
private userInputManager!: UserInputManager;
private static cssLoaded: boolean = false;
- constructor(private Connection: RoomConnection, userInputManager : UserInputManager) {
+ constructor(private Connection: RoomConnection, userInputManager : UserInputManager, private isAdmin: Boolean) {
this.buttonMainConsole = document.createElement('div');
this.buttonMainConsole.classList.add('console');
this.divMainConsole = document.createElement('div');
+ this.divMainConsole.className = CLASS_CONSOLE_MESSAGE;
+ this.divMessageConsole = document.createElement('div');
+ this.divMessageConsole.className = 'message';
+ this.divSettingConsole = document.createElement('div');
+ this.divSettingConsole.className = 'setting';
this.buttonSendMainConsole = document.createElement('img');
this.buttonSettingsMainConsole = document.createElement('img');
+ this.buttonAdminMainConsole = document.createElement('img');
this.userInputManager = userInputManager;
this.initialise();
}
@@ -78,35 +93,48 @@ export class ConsoleGlobalMessageManager {
});
menu.appendChild(textMessage);
menu.appendChild(textAudio);
- this.divMainConsole.appendChild(menu);
+ this.divMessageConsole.appendChild(menu);
this.buttonSendMainConsole.src = 'resources/logos/send-yellow.svg';
this.buttonSendMainConsole.addEventListener('click', () => {
- if(this.activeConsole){
- this.disabled();
+ if(this.activeMessage){
+ this.disabledMessageConsole();
}else{
- this.buttonSendMainConsole.classList.add('active');
- this.active();
+ this.activeMessageConsole();
}
});
- this.buttonMainConsole.appendChild(this.buttonSendMainConsole);
- this.buttonSettingsMainConsole.src = 'resources/logos/setting-yellow.svg';
- this.buttonSettingsMainConsole.addEventListener('click', () => {
+ this.buttonAdminMainConsole.src = 'resources/logos/setting-yellow.svg';
+ this.buttonAdminMainConsole.addEventListener('click', () => {
window.open(ADMIN_URL, '_blank');
});
- this.buttonMainConsole.appendChild(this.buttonSettingsMainConsole);
+
+ this.buttonSettingsMainConsole.src = 'resources/logos/monitor-yellow.svg';
+ this.buttonSettingsMainConsole.addEventListener('click', () => {
+ if(this.activeSetting){
+ this.disabledSettingConsole();
+ }else{
+ this.activeSettingConsole();
+ }
+ });
/*const buttonText = document.createElement('p');
buttonText.innerText = 'Console';
this.buttonMainConsole.appendChild(buttonText);*/
+ this.divMessageConsole.appendChild(typeConsole);
+
+ if(this.isAdmin) {
+ this.buttonMainConsole.appendChild(this.buttonSendMainConsole);
+ this.buttonMainConsole.appendChild(this.buttonAdminMainConsole);
+ this.createTextMessagePart();
+ this.createUploadAudioPart();
+ }
+ this.buttonMainConsole.appendChild(this.buttonSettingsMainConsole);
+ this.createSettings();
- this.divMainConsole.className = CLASS_CONSOLE_MESSAGE;
this.divMainConsole.appendChild(this.buttonMainConsole);
- this.divMainConsole.appendChild(typeConsole);
-
- this.createTextMessagePart();
- this.createUploadAudioPart();
+ this.divMainConsole.appendChild(this.divMessageConsole);
+ this.divMainConsole.appendChild(this.divSettingConsole);
const mainSectionDiv = HtmlUtils.getElementByIdOrFail('main-container');
mainSectionDiv.appendChild(this.divMainConsole);
@@ -120,7 +148,7 @@ export class ConsoleGlobalMessageManager {
buttonSend.classList.add('btn');
buttonSend.addEventListener('click', (event: MouseEvent) => {
this.sendMessage();
- this.disabled();
+ this.disabledMessageConsole();
});
const buttonDiv = document.createElement('div');
buttonDiv.classList.add('btn-action');
@@ -131,7 +159,7 @@ export class ConsoleGlobalMessageManager {
section.classList.add('active');
section.appendChild(div);
section.appendChild(buttonDiv);
- this.divMainConsole.appendChild(section);
+ this.divMessageConsole.appendChild(section);
(async () => {
// Start loading CSS
@@ -173,27 +201,6 @@ export class ConsoleGlobalMessageManager {
})();
}
- private static loadCss(): Promise {
- return new Promise((resolve, reject) => {
- if (ConsoleGlobalMessageManager.cssLoaded) {
- resolve();
- return;
- }
- const fileref = document.createElement("link")
- fileref.setAttribute("rel", "stylesheet")
- fileref.setAttribute("type", "text/css")
- fileref.setAttribute("href", "https://cdn.quilljs.com/1.3.7/quill.snow.css");
- document.getElementsByTagName("head")[0].appendChild(fileref);
- ConsoleGlobalMessageManager.cssLoaded = true;
- fileref.onload = () => {
- resolve();
- }
- fileref.onerror = () => {
- reject();
- }
- });
- }
-
createUploadAudioPart(){
const div = document.createElement('div');
div.classList.add('upload');
@@ -243,7 +250,7 @@ export class ConsoleGlobalMessageManager {
buttonSend.classList.add('btn');
buttonSend.addEventListener('click', (event: MouseEvent) => {
this.sendMessage();
- this.disabled();
+ this.disabledMessageConsole();
});
const buttonDiv = document.createElement('div');
buttonDiv.classList.add('btn-action');
@@ -253,7 +260,114 @@ export class ConsoleGlobalMessageManager {
section.id = this.getSectionId(UPLOAD_CONSOLE_MESSAGE);
section.appendChild(div);
section.appendChild(buttonDiv);
- this.divMainConsole.appendChild(section);
+ this.divMessageConsole.appendChild(section);
+ }
+
+ createSettings(){
+ const labelGame = document.createElement('h1');
+ labelGame.innerText = "Game quality";
+
+ const selectGame = document.createElement('select');
+ selectGame.id = VIDEO_QUALITY_SELECT;
+
+ const option1 : HTMLOptionElement = document.createElement('option');
+ option1.value = '120';
+ option1.innerText = 'High video quality (120 fps)';
+ selectGame.appendChild(option1);
+
+ const option2 : HTMLOptionElement = document.createElement('option');
+ option2.value = '60';
+ option2.innerText = 'Medium video quality (60 fps, recommended)';
+ selectGame.appendChild(option2);
+
+ const option3 : HTMLOptionElement = document.createElement('option');
+ option3.value = '40';
+ option3.innerText = 'Minimum video quality (40 fps)';
+ selectGame.appendChild(option3);
+
+ const option4 : HTMLOptionElement = document.createElement('option');
+ option4.value = '20';
+ option4.innerText = 'Small video quality (20 fps)';
+ selectGame.appendChild(option4);
+
+ const labelVideo = document.createElement('h1');
+ labelVideo.innerText = "Video quality";
+
+ const selectVideo = document.createElement('select');
+ selectVideo.id = GAME_QUALITY_SELECT;
+
+ const optionVideo1 : HTMLOptionElement = document.createElement('option');
+ optionVideo1.value = '30';
+ optionVideo1.innerText = 'High video quality (30 fps)';
+ selectVideo.appendChild(optionVideo1);
+
+ const optionVideo2 : HTMLOptionElement = document.createElement('option');
+ optionVideo2.value = '20';
+ optionVideo2.innerText = 'Medium video quality (20 fps, recommended)';
+ selectVideo.appendChild(optionVideo2);
+
+ const optionVideo3 : HTMLOptionElement = document.createElement('option');
+ optionVideo3.value = '10';
+ optionVideo3.innerText = 'Minimum video quality (10 fps)';
+ selectVideo.appendChild(optionVideo3);
+
+ const optionVideo4 : HTMLOptionElement = document.createElement('option');
+ optionVideo4.value = '5';
+ optionVideo4.innerText = 'Small video quality (5 fps)';
+ selectVideo.appendChild(optionVideo4);
+
+ selectGame.value = '60';
+ const localQualityGame = localStorage.getItem(GAME_QUALITY_SELECT);
+ if(localQualityGame){
+ selectGame.value = localQualityGame;
+ }
+
+ selectVideo.value = '30';
+ const localQualityCam = localStorage.getItem(VIDEO_QUALITY_SELECT);
+ if(localQualityCam){
+ selectVideo.value = localQualityCam;
+ }
+
+ const divButtonAction = document.createElement('div');
+ divButtonAction.className = 'btn-action';
+ const buttonSave = document.createElement('button');
+ buttonSave.innerText = 'Save';
+ buttonSave.classList.add('btn');
+ buttonSave.addEventListener('click', () => {
+ this.saveSetting(selectGame.value, selectVideo.value);
+ this.disabledSettingConsole();
+ });
+ divButtonAction.appendChild(buttonSave);
+
+ const section = document.createElement('section');
+ section.id = this.getSectionId(VIDEO_QUALITY_CONSOLE);
+ section.appendChild(labelGame);
+ section.appendChild(selectGame);
+ section.appendChild(labelVideo);
+ section.appendChild(selectVideo);
+ section.appendChild(divButtonAction);
+ this.divSettingConsole.appendChild(section);
+ }
+
+ private static loadCss(): Promise {
+ return new Promise((resolve, reject) => {
+ if (ConsoleGlobalMessageManager.cssLoaded) {
+ resolve();
+ return;
+ }
+ const fileref = document.createElement("link")
+ fileref.setAttribute("rel", "stylesheet")
+ fileref.setAttribute("type", "text/css")
+ fileref.setAttribute("href", "https://cdn.quilljs.com/1.3.7/quill.snow.css");
+ document.getElementsByTagName("head")[0].appendChild(fileref);
+ ConsoleGlobalMessageManager.cssLoaded = true;
+ fileref.onload = () => {
+ resolve();
+ }
+ fileref.onerror = () => {
+ reject();
+ }
+ });
}
sendMessage(){
@@ -307,20 +421,65 @@ export class ConsoleGlobalMessageManager {
this.Connection.emitGlobalMessage(GlobalMessage);
}
+ private saveSetting(valueGame: string, valueVideo: string){
+ const previousGameValue = localStorage.getItem(GAME_QUALITY_SELECT);
+ if(!previousGameValue || previousGameValue !== valueGame) {
+ localStorage.setItem(GAME_QUALITY_SELECT, valueGame);
+ window.location.reload();
+ }
+ const previousVideoValue = localStorage.getItem(VIDEO_QUALITY_SELECT);
+ if(!previousVideoValue || previousVideoValue !== valueVideo) {
+ localStorage.setItem(VIDEO_QUALITY_SELECT, valueVideo);
+ mediaManager.updateCameraQuality(parseInt(valueVideo));
+ }
+ }
+
active(){
this.userInputManager.clearAllInputKeyboard();
- this.activeConsole = true;
this.divMainConsole.style.top = '0';
- this.buttonSendMainConsole.classList.add('active');
+ this.activeConsole = true;
}
disabled(){
this.userInputManager.initKeyBoardEvent();
this.activeConsole = false;
this.divMainConsole.style.top = '-80%';
+ }
+
+ activeMessageConsole(){
+ this.activeMessage = true;
+ if(this.activeSetting){
+ this.disabledSettingConsole();
+ }
+ this.active();
+ this.divMessageConsole.classList.add('active');
+ this.buttonSendMainConsole.classList.add('active');
+ }
+
+ disabledMessageConsole(){
+ this.activeMessage = false;
+ this.disabled();
+ this.divMessageConsole.classList.remove('active');
this.buttonSendMainConsole.classList.remove('active');
}
+ activeSettingConsole(){
+ this.activeSetting = true;
+ if(this.activeMessage){
+ this.disabledMessageConsole();
+ }
+ this.active();
+ this.divSettingConsole.classList.add('active');
+ this.buttonSettingsMainConsole.classList.add('active');
+ }
+
+ disabledSettingConsole(){
+ this.activeSetting = false;
+ this.disabled();
+ this.divSettingConsole.classList.remove('active');
+ this.buttonSettingsMainConsole.classList.remove('active');
+ }
+
private getSectionId(id: string) : string {
return `section-${id}`;
}
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index 7241ae14..6abeb4f2 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -454,9 +454,7 @@ export class GameScene extends ResizableScene implements CenterListener {
this.initUsersPosition(roomJoinedMessage.users);
this.connectionAnswerPromiseResolve(roomJoinedMessage);
// Analyze tags to find if we are admin. If yes, show console.
- if (this.connection.hasTag('admin')) {
- this.ConsoleGlobalMessageManager = new ConsoleGlobalMessageManager(this.connection, this.userInputManager);
- }
+ this.ConsoleGlobalMessageManager = new ConsoleGlobalMessageManager(this.connection, this.userInputManager, this.connection.hasTag('admin'));
});
connection.onUserJoins((message: MessageUserJoined) => {
diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts
index 33be1c6a..6c3b6898 100644
--- a/front/src/WebRtc/MediaManager.ts
+++ b/front/src/WebRtc/MediaManager.ts
@@ -2,12 +2,21 @@ import {DivImportance, layoutManager} from "./LayoutManager";
import {HtmlUtils} from "./HtmlUtils";
import {DiscussionManager, SendMessageCallback} from "./DiscussionManager";
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
+import {VIDEO_QUALITY_SELECT} from "../Administration/ConsoleGlobalMessageManager";
declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
-const videoConstraint: boolean|MediaTrackConstraints = {
- width: { ideal: 1280 },
- height: { ideal: 720 },
- facingMode: "user"
+const localValueVideo = localStorage.getItem(VIDEO_QUALITY_SELECT);
+let valueVideo = 20;
+if(localValueVideo){
+ valueVideo = parseInt(localValueVideo);
+}
+let videoConstraint: boolean|MediaTrackConstraints = {
+ width: { min: 640, ideal: 1280, max: 1920 },
+ height: { min: 400, ideal: 720 },
+ frameRate: {exact: valueVideo, ideal: valueVideo},
+ facingMode: "user",
+ resizeMode: 'crop-and-scale',
+ aspectRatio: 1.777777778
};
export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void;
@@ -191,6 +200,17 @@ export class MediaManager {
buttonCloseFrame.addEventListener('click', functionTrigger);
}
+ public updateCameraQuality(value: number) {
+ this.enableCameraStyle();
+ const newVideoConstraint = JSON.parse(JSON.stringify(videoConstraint));
+ newVideoConstraint.frameRate = {exact: value, ideal: value};
+ videoConstraint = newVideoConstraint;
+ this.constraintsMedia.video = videoConstraint;
+ this.getCamera().then((stream: MediaStream) => {
+ this.triggerUpdatedLocalStreamCallbacks(stream);
+ });
+ }
+
public enableCamera() {
this.enableCameraStyle();
this.constraintsMedia.video = videoConstraint;
@@ -564,7 +584,7 @@ export class MediaManager {
}
isError(userId: string): void {
- console.log("isError", `div-${userId}`);
+ console.info("isError", `div-${userId}`);
const element = document.getElementById(`div-${userId}`);
if(!element){
return;
diff --git a/front/src/index.ts b/front/src/index.ts
index fe7ceb34..640b04f9 100644
--- a/front/src/index.ts
+++ b/front/src/index.ts
@@ -13,6 +13,7 @@ import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
import {ResizableScene} from "./Phaser/Login/ResizableScene";
import {EntryScene} from "./Phaser/Login/EntryScene";
import {coWebsiteManager} from "./WebRtc/CoWebsiteManager";
+import {GAME_QUALITY_SELECT} from "./Administration/ConsoleGlobalMessageManager";
// Load Jitsi if the environment variable is set.
if (JITSI_URL) {
@@ -23,17 +24,55 @@ if (JITSI_URL) {
const {width, height} = coWebsiteManager.getGameSize();
+let valueGameQuality : number = 60
+const localGameQuality = localStorage.getItem(GAME_QUALITY_SELECT);
+if(localGameQuality){
+ try {
+ valueGameQuality = parseInt(localGameQuality);
+ }catch (err){
+ console.error(err);
+ }
+}
+const fps : Phaser.Types.Core.FPSConfig = {
+ /**
+ * The minimum acceptable rendering rate, in frames per second.
+ */
+ min: valueGameQuality,
+ /**
+ * The optimum rendering rate, in frames per second.
+ */
+ target: valueGameQuality,
+ /**
+ * Use setTimeout instead of requestAnimationFrame to run the game loop.
+ */
+ forceSetTimeOut: true,
+ /**
+ * Calculate the average frame delta from this many consecutive frame intervals.
+ */
+ deltaHistory: 120,
+ /**
+ * The amount of frames the time step counts before we trust the delta values again.
+ */
+ panicMax: 20,
+ /**
+ * Apply delta smoothing during the game update to help avoid spikes?
+ */
+ smoothStep: false
+}
+
const config: GameConfig = {
+ type: Phaser.AUTO,
title: "WorkAdventure",
width: width / RESOLUTION,
height: height / RESOLUTION,
parent: "game",
scene: [EntryScene, LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, FourOFourScene, CustomizeScene],
zoom: RESOLUTION,
+ fps: fps,
physics: {
default: "arcade",
arcade: {
- debug: DEBUG_MODE
+ debug: DEBUG_MODE,
}
},
callbacks: {
@@ -60,6 +99,7 @@ window.addEventListener('resize', function (event) {
}
}
});
+
coWebsiteManager.onStateChange(() => {
const {width, height} = coWebsiteManager.getGameSize();
game.scale.resize(width / RESOLUTION, height / RESOLUTION);