Adding LayoutManager to position videos as cleverly as possible

This commit is contained in:
David Négrier 2020-08-11 22:32:55 +02:00
parent 4b940a571c
commit 7232bbaef9
3 changed files with 105 additions and 1 deletions

View File

@ -0,0 +1,10 @@
export class HtmlUtils {
public static getElementByIdOrFail<T extends HTMLElement>(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;
}
}

View File

@ -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<string, HTMLDivElement> = new Map<string, HTMLDivElement>();
private normalDivs: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>();
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<HTMLDivElement>('chat-mode');
chatModeDiv.appendChild(elem);
} else {
if (importance === DivImportance.Important) {
const mainSectionDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-section');
mainSectionDiv.appendChild(elem);
} else if (importance === DivImportance.Normal) {
const sideBarDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('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);
}
}
}

View File

@ -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",