diff --git a/front/dist/index.html b/front/dist/index.html index 8e957965..0e696622 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -42,30 +42,14 @@
-
-
+
+
-
- -
@@ -88,7 +72,7 @@
-
+
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 23a4bba6..1acf6d2a 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -242,15 +242,10 @@ body { .main-container { height: 100vh; width: 100vw; - display: flex; - align-items: stretch; + position: absolute; } @media (min-aspect-ratio: 1/1) { - .main-container { - flex-direction: row; - } - .game-overlay { flex-direction: row; } @@ -266,12 +261,21 @@ body { .sidebar > div:hover { max-height: 25%; } + + #cowebsite { + right: 0; + top: 0; + width: 50%; + height: 100vh; + } + #cowebsite.loading { + transform: translateX(90%); + } + #cowebsite.hidden { + transform: translateX(100%); + } } @media (max-aspect-ratio: 1/1) { - .main-container { - flex-direction: column; - } - .game-overlay { flex-direction: column; } @@ -288,24 +292,36 @@ body { .sidebar > div:hover { max-width: 25%; } + + #cowebsite { + left: 0; + bottom: 0; + width: 100%; + height: 50%; + } + #cowebsite.loading { + transform: translateY(90%); + } + #cowebsite.hidden { + transform: translateY(100%); + } } -.game { - flex-basis: 100%; +#game { + width: 100%; position: relative; /* Position relative is needed for the game-overlay. */ } /* A potentially shared website could appear in an iframe in the cowebsite space. */ -.cowebsite { - flex-basis: 100%; - transition: flex-basis 0.5s; +#cowebsite { + position: fixed; + transition: transform 0.5s; +} +#cowebsite.loading { + background-color: gray; } -/*.cowebsite:hover { - flex-basis: 100%; -}*/ - -.cowebsite > iframe { +#cowebsite > iframe { width: 100%; height: 100%; } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index bfc6402e..96648255 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -38,7 +38,7 @@ import CanvasTexture = Phaser.Textures.CanvasTexture; import GameObject = Phaser.GameObjects.GameObject; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import {GameMap} from "./GameMap"; -import {CoWebsiteManager} from "../../WebRtc/CoWebsiteManager"; +import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; import {mediaManager} from "../../WebRtc/MediaManager"; import {FourOFourSceneName} from "../Reconnecting/FourOFourScene"; import {ItemFactoryInterface} from "../Items/ItemFactoryInterface"; @@ -292,13 +292,6 @@ export class GameScene extends ResizableScene implements CenterListener { // }); // }); } - - // TEST: let's load a module dynamically! - /*let foo = "http://maps.workadventure.localhost/computer.js"; - import(/* webpackIgnore: true * / foo).then(result => { - console.log(result); - - });*/ } //hook initialisation @@ -476,9 +469,9 @@ export class GameScene extends ResizableScene implements CenterListener { this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => { if (newValue === undefined) { - CoWebsiteManager.closeCoWebsite(); + coWebsiteManager.closeCoWebsite(); } else { - CoWebsiteManager.loadCoWebsite(newValue as string); + coWebsiteManager.loadCoWebsite(newValue as string); } }); this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => { diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 1793335b..ab63e60a 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -2,47 +2,90 @@ import {HtmlUtils} from "./HtmlUtils"; export type CoWebsiteStateChangedCallback = () => void; -export class CoWebsiteManager { +enum iframeStates { + closed = 1, + loading, // loading an iframe can be slow, so we show some placeholder until it is ready + opened, +} - private static observers = new Array(); +const cowebsiteDivId = "cowebsite"; // the id of the parent div of the iframe. +const animationTime = 500; //time used by the css transitions, in ms. - public static loadCoWebsite(url: string): void { - const cowebsiteDiv = HtmlUtils.getElementByIdOrFail("cowebsite"); +class CoWebsiteManager { + + private opened: iframeStates = iframeStates.closed; + + private observers = new Array(); + + private close(): HTMLDivElement { + const cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); + cowebsiteDiv.classList.remove('loaded'); //edit the css class to trigger the transition + cowebsiteDiv.classList.add('hidden'); + this.opened = iframeStates.closed; + return cowebsiteDiv; + } + private load(): HTMLDivElement { + const cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); + cowebsiteDiv.classList.remove('hidden'); //edit the css class to trigger the transition + cowebsiteDiv.classList.add('loading'); + this.opened = iframeStates.loading; + return cowebsiteDiv; + } + private open(): HTMLDivElement { + const cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); + cowebsiteDiv.classList.remove('loading', 'hidden'); //edit the css class to trigger the transition + this.opened = iframeStates.opened; + return cowebsiteDiv; + } + + public loadCoWebsite(url: string): void { + const cowebsiteDiv = this.load(); cowebsiteDiv.innerHTML = ''; const iframe = document.createElement('iframe'); iframe.id = 'cowebsite-iframe'; iframe.src = url; + const onloadPromise = new Promise((resolve) => { + iframe.onload = () => resolve(); + }); cowebsiteDiv.appendChild(iframe); - //iframe.onload = () => { - // onload can be long to trigger. Maybe we should display the website, whatever happens, after 1 second? - CoWebsiteManager.fire(); - //} + const onTimeoutPromise = new Promise((resolve) => { + setTimeout(() => resolve(), 2000); + }); + Promise.race([onloadPromise, onTimeoutPromise]).then(() => { + this.open(); + setTimeout(() => { + this.fire(); + }, animationTime) + }); } /** * Just like loadCoWebsite but the div can be filled by the user. */ - public static insertCoWebsite(callback: (cowebsite: HTMLDivElement) => void): void { - const cowebsiteDiv = HtmlUtils.getElementByIdOrFail("cowebsite"); - cowebsiteDiv.innerHTML = ''; - - callback(cowebsiteDiv); - //iframe.onload = () => { - // onload can be long to trigger. Maybe we should display the website, whatever happens, after 1 second? - CoWebsiteManager.fire(); - //} + public insertCoWebsite(callback: (cowebsite: HTMLDivElement) => Promise): void { + const cowebsiteDiv = this.load(); + callback(cowebsiteDiv).then(() => { + this.open(); + setTimeout(() => { + this.fire(); + }, animationTime) + }); } - public static closeCoWebsite(): void { - const cowebsiteDiv = HtmlUtils.getElementByIdOrFail("cowebsite"); - cowebsiteDiv.innerHTML = ''; - CoWebsiteManager.fire(); + public closeCoWebsite(): Promise { + return new Promise((resolve, reject) => { + const cowebsiteDiv = this.close(); + this.fire(); + setTimeout(() => { + resolve(); + setTimeout(() => cowebsiteDiv.innerHTML = '', 500) + }, animationTime) + }); } - public static getGameSize(): {width: number, height: number} { - const hasChildren = HtmlUtils.getElementByIdOrFail("cowebsite").children.length > 0; - if (hasChildren === false) { + public getGameSize(): {width: number, height: number} { + if (this.opened !== iframeStates.opened) { return { width: window.innerWidth, height: window.innerHeight @@ -61,13 +104,15 @@ export class CoWebsiteManager { } } - public static onStateChange(observer: CoWebsiteStateChangedCallback) { - CoWebsiteManager.observers.push(observer); + public onStateChange(observer: CoWebsiteStateChangedCallback) { + this.observers.push(observer); } - private static fire(): void { - for (const callback of CoWebsiteManager.observers) { + private fire(): void { + for (const callback of this.observers) { callback(); } } } + +export const coWebsiteManager = new CoWebsiteManager(); \ No newline at end of file diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 191642fb..45b9b3cf 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -1,6 +1,6 @@ -import {CoWebsiteManager} from "./CoWebsiteManager"; import {JITSI_URL} from "../Enum/EnvironmentVariable"; import {mediaManager} from "./MediaManager"; +import {coWebsiteManager} from "./CoWebsiteManager"; declare const window:any; // eslint-disable-line @typescript-eslint/no-explicit-any const interfaceConfig = { @@ -31,9 +31,9 @@ class JitsiFactory { private videoCallback = this.onVideoChange.bind(this); public start(roomName: string, playerName:string, jwt?: string): void { - CoWebsiteManager.insertCoWebsite((cowebsiteDiv => { + coWebsiteManager.insertCoWebsite((cowebsiteDiv => { const domain = JITSI_URL; - const options = { + const options: any = { // eslint-disable-line @typescript-eslint/no-explicit-any roomName: roomName, jwt: jwt, width: "100%", @@ -49,19 +49,23 @@ class JitsiFactory { if (!options.jwt) { delete options.jwt; } - this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options); - this.jitsiApi.executeCommand('displayName', playerName); + + return new Promise((resolve) => { + options.onload = () => resolve(); //we want for the iframe to be loaded before triggering animations. + this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options); + this.jitsiApi.executeCommand('displayName', playerName); - this.jitsiApi.addListener('audioMuteStatusChanged', this.audioCallback); - this.jitsiApi.addListener('videoMuteStatusChanged', this.videoCallback); + this.jitsiApi.addListener('audioMuteStatusChanged', this.audioCallback); + this.jitsiApi.addListener('videoMuteStatusChanged', this.videoCallback); + }); })); } - public stop(): void { + public async stop(): Promise { + await coWebsiteManager.closeCoWebsite(); this.jitsiApi.removeListener('audioMuteStatusChanged', this.audioCallback); this.jitsiApi.removeListener('videoMuteStatusChanged', this.videoCallback); this.jitsiApi?.dispose(); - CoWebsiteManager.closeCoWebsite(); } private onAudioChange({muted}: {muted: boolean}): void { diff --git a/front/src/index.ts b/front/src/index.ts index e12d8707..fe7ceb34 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -10,12 +10,9 @@ import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene"; import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; import {OutlinePipeline} from "./Phaser/Shaders/OutlinePipeline"; import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; -import {CoWebsiteManager} from "./WebRtc/CoWebsiteManager"; -import {gameManager} from "./Phaser/Game/GameManager"; import {ResizableScene} from "./Phaser/Login/ResizableScene"; import {EntryScene} from "./Phaser/Login/EntryScene"; - -//CoWebsiteManager.loadCoWebsite('https://thecodingmachine.com'); +import {coWebsiteManager} from "./WebRtc/CoWebsiteManager"; // Load Jitsi if the environment variable is set. if (JITSI_URL) { @@ -24,7 +21,7 @@ if (JITSI_URL) { document.head.appendChild(jitsiScript); } -const {width, height} = CoWebsiteManager.getGameSize(); +const {width, height} = coWebsiteManager.getGameSize(); const config: GameConfig = { title: "WorkAdventure", @@ -53,8 +50,7 @@ cypressAsserter.gameStarted(); const game = new Phaser.Game(config); window.addEventListener('resize', function (event) { - const {width, height} = CoWebsiteManager.getGameSize(); - + const {width, height} = coWebsiteManager.getGameSize(); game.scale.resize(width / RESOLUTION, height / RESOLUTION); // Let's trigger the onResize method of any active scene that is a ResizableScene @@ -64,8 +60,7 @@ window.addEventListener('resize', function (event) { } } }); -CoWebsiteManager.onStateChange(() => { - const {width, height} = CoWebsiteManager.getGameSize(); - +coWebsiteManager.onStateChange(() => { + const {width, height} = coWebsiteManager.getGameSize(); game.scale.resize(width / RESOLUTION, height / RESOLUTION); });