Adding LayoutManager to position videos as cleverly as possible
This commit is contained in:
parent
4b940a571c
commit
7232bbaef9
10
front/src/WebRtc/HtmlUtils.ts
Normal file
10
front/src/WebRtc/HtmlUtils.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
94
front/src/WebRtc/LayoutManager.ts
Normal file
94
front/src/WebRtc/LayoutManager.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene";
|
|||||||
import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
|
import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
|
||||||
|
|
||||||
const config: GameConfig = {
|
const config: GameConfig = {
|
||||||
title: "Office game",
|
title: "WorkAdventure",
|
||||||
width: window.innerWidth / RESOLUTION,
|
width: window.innerWidth / RESOLUTION,
|
||||||
height: window.innerHeight / RESOLUTION,
|
height: window.innerHeight / RESOLUTION,
|
||||||
parent: "game",
|
parent: "game",
|
||||||
|
Loading…
Reference in New Issue
Block a user