diff --git a/front/src/WebRtc/HtmlUtils.ts b/front/src/WebRtc/HtmlUtils.ts
new file mode 100644
index 00000000..c2e6ff6d
--- /dev/null
+++ b/front/src/WebRtc/HtmlUtils.ts
@@ -0,0 +1,10 @@
+export class HtmlUtils {
+ public static 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/LayoutManager.ts b/front/src/WebRtc/LayoutManager.ts
new file mode 100644
index 00000000..cf986d0c
--- /dev/null
+++ b/front/src/WebRtc/LayoutManager.ts
@@ -0,0 +1,94 @@
+import {HtmlUtils} from "./HtmlUtils";
+
+export enum LayoutMode {
+ // All videos are displayed on the right side of the screen. If there is a screen sharing, it is displayed in the middle.
+ Presentation = "Presentation",
+ // Videos take the whole page.
+ VideoChat = "VideoChat",
+}
+
+export enum DivImportance {
+ // For screen sharing
+ Important = "Important",
+ // For normal video
+ Normal = "Normal",
+}
+
+/**
+ * This class is in charge of the video-conference layout.
+ * It receives positioning requests for videos and does its best to place them on the screen depending on the active layout mode.
+ */
+export class LayoutManager {
+ private mode: LayoutMode = LayoutMode.Presentation;
+
+ private importantDivs: Map = new Map();
+ private normalDivs: Map = new Map();
+
+ public add(importance: DivImportance, userId: string, html: string): void {
+ const div = document.createElement('div');
+ div.append(html);
+ div.id = "user-"+userId;
+
+ if (importance === DivImportance.Important) {
+ this.importantDivs.set(userId, div);
+
+ // If this is the first video with high importance, let's switch mode automatically.
+ if (this.importantDivs.size === 1 && this.mode === LayoutMode.VideoChat) {
+ this.switchLayoutMode(LayoutMode.Presentation);
+ }
+ } else if (importance === DivImportance.Normal) {
+ this.normalDivs.set(userId, div);
+ } else {
+ throw new Error('Unexpected importance');
+ }
+
+ this.positionDiv(div, importance);
+ }
+
+ private positionDiv(elem: HTMLDivElement, importance: DivImportance): void {
+ if (this.mode === LayoutMode.VideoChat) {
+ const chatModeDiv = HtmlUtils.getElementByIdOrFail('chat-mode');
+ chatModeDiv.appendChild(elem);
+ } else {
+ if (importance === DivImportance.Important) {
+ const mainSectionDiv = HtmlUtils.getElementByIdOrFail('main-section');
+ mainSectionDiv.appendChild(elem);
+ } else if (importance === DivImportance.Normal) {
+ const sideBarDiv = HtmlUtils.getElementByIdOrFail('sidebar');
+ sideBarDiv.appendChild(elem);
+ }
+ }
+ }
+
+ /**
+ * Removes the DIV matching userId.
+ */
+ public remove(userId: string): void {
+ let div = this.importantDivs.get(userId);
+ if (div !== undefined) {
+ div.remove();
+ this.importantDivs.delete(userId);
+ return;
+ }
+
+ div = this.normalDivs.get(userId);
+ if (div !== undefined) {
+ div.remove();
+ this.normalDivs.delete(userId);
+ return;
+ }
+
+ throw new Error('Could not find user ID "'+userId+'"');
+ }
+
+ private switchLayoutMode(layoutMode: LayoutMode) {
+ this.mode = layoutMode;
+
+ for (let div of this.importantDivs.values()) {
+ this.positionDiv(div, DivImportance.Important);
+ }
+ for (let div of this.normalDivs.values()) {
+ this.positionDiv(div, DivImportance.Normal);
+ }
+ }
+}
diff --git a/front/src/index.ts b/front/src/index.ts
index 7634351f..d64a8f2e 100644
--- a/front/src/index.ts
+++ b/front/src/index.ts
@@ -11,7 +11,7 @@ import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene";
import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
const config: GameConfig = {
- title: "Office game",
+ title: "WorkAdventure",
width: window.innerWidth / RESOLUTION,
height: window.innerHeight / RESOLUTION,
parent: "game",