diff --git a/front/dist/resources/objects/layout_modes.png b/front/dist/resources/objects/layout_modes.png
deleted file mode 100644
index abd9adaf..00000000
Binary files a/front/dist/resources/objects/layout_modes.png and /dev/null differ
diff --git a/front/package.json b/front/package.json
index 61205855..ee031668 100644
--- a/front/package.json
+++ b/front/package.json
@@ -64,8 +64,8 @@
"lint": "node_modules/.bin/eslint src/ . --ext .ts",
"fix": "node_modules/.bin/eslint --fix src/ . --ext .ts",
"precommit": "lint-staged",
- "svelte-check-watch": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore\" --watch",
- "svelte-check": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore\"",
+ "svelte-check-watch": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore,a11y-media-has-caption:ignore\" --watch",
+ "svelte-check": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore,a11y-media-has-caption:ignore\"",
"pretty": "yarn prettier --write 'src/**/*.{ts,tsx}'",
"pretty-check": "yarn prettier --check 'src/**/*.{ts,tsx}'"
},
diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte
index 2e159d2d..8ade9398 100644
--- a/front/src/Components/App.svelte
+++ b/front/src/Components/App.svelte
@@ -1,5 +1,5 @@
@@ -68,6 +71,7 @@
-->
{#if $gameOverlayVisibilityStore}
+
diff --git a/front/src/Components/CameraControls.svelte b/front/src/Components/CameraControls.svelte
index 5c17a9fe..d6b31af4 100644
--- a/front/src/Components/CameraControls.svelte
+++ b/front/src/Components/CameraControls.svelte
@@ -7,6 +7,11 @@
import cinemaCloseImg from "./images/cinema-close.svg";
import microphoneImg from "./images/microphone.svg";
import microphoneCloseImg from "./images/microphone-close.svg";
+ import layoutPresentationImg from "./images/layout-presentation.svg";
+ import layoutChatImg from "./images/layout-chat.svg";
+ import {layoutModeStore} from "../Stores/StreamableCollectionStore";
+ import {LayoutMode} from "../WebRtc/LayoutManager";
+ import {peerStore} from "../Stores/PeerStore";
function screenSharingClick(): void {
if ($requestedScreenSharingState === true) {
@@ -32,10 +37,24 @@
}
}
+ function switchLayoutMode() {
+ if ($layoutModeStore === LayoutMode.Presentation) {
+ $layoutModeStore = LayoutMode.VideoChat;
+ } else {
+ $layoutModeStore = LayoutMode.Presentation;
+ }
+ }
+
+ {#if $layoutModeStore === LayoutMode.Presentation }
+
+ {:else}
+
+ {/if}
+
{#if $requestedScreenSharingState}
diff --git a/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte b/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte
index a22da2fa..79ad1810 100644
--- a/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte
+++ b/front/src/Components/EnableCamera/HorizontalSoundMeterWidget.svelte
@@ -58,7 +58,7 @@
- {#each [...Array(NB_BARS).keys()] as i}
+ {#each [...Array(NB_BARS).keys()] as i (i)}
{/each}
diff --git a/front/src/Components/SoundMeterWidget.svelte b/front/src/Components/SoundMeterWidget.svelte
index 30650e3f..74d3f7b3 100644
--- a/front/src/Components/SoundMeterWidget.svelte
+++ b/front/src/Components/SoundMeterWidget.svelte
@@ -6,8 +6,6 @@
export let stream: MediaStream|null;
let volume = 0;
- const NB_BARS = 5;
-
let timeout: ReturnType
;
const soundMeter = new SoundMeter();
let display = false;
@@ -23,7 +21,7 @@
timeout = setInterval(() => {
try{
- volume = parseInt((soundMeter.getVolume() / 100 * NB_BARS).toFixed(0));
+ volume = soundMeter.getVolume();
//console.log(volume);
}catch(err){
@@ -45,9 +43,9 @@
- 1}>
- 2}>
- 3}>
- 4}>
5}>
+ 10}>
+ 15}>
+ 40}>
+ 70}>
diff --git a/front/src/Components/Video/ChatLayout.svelte b/front/src/Components/Video/ChatLayout.svelte
new file mode 100644
index 00000000..ac91217e
--- /dev/null
+++ b/front/src/Components/Video/ChatLayout.svelte
@@ -0,0 +1,35 @@
+
+
+
+ {#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
+
+ {/each}
+
diff --git a/front/src/Components/Video/LocalStreamMediaBox.svelte b/front/src/Components/Video/LocalStreamMediaBox.svelte
new file mode 100644
index 00000000..55c7f22c
--- /dev/null
+++ b/front/src/Components/Video/LocalStreamMediaBox.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+ {#if stream}
+
+ {/if}
+
diff --git a/front/src/Components/Video/MediaBox.svelte b/front/src/Components/Video/MediaBox.svelte
new file mode 100644
index 00000000..9160a7a9
--- /dev/null
+++ b/front/src/Components/Video/MediaBox.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {#if streamable instanceof VideoPeer}
+
+ {:else if streamable instanceof ScreenSharingPeer}
+
+ {:else}
+
+ {/if}
+
diff --git a/front/src/Components/Video/PresentationLayout.svelte b/front/src/Components/Video/PresentationLayout.svelte
new file mode 100644
index 00000000..f68dd2f1
--- /dev/null
+++ b/front/src/Components/Video/PresentationLayout.svelte
@@ -0,0 +1,24 @@
+
+
+
+ {#if $videoFocusStore }
+
+ {/if}
+
+
diff --git a/front/src/Components/Video/ScreenSharingMediaBox.svelte b/front/src/Components/Video/ScreenSharingMediaBox.svelte
new file mode 100644
index 00000000..c6e1564f
--- /dev/null
+++ b/front/src/Components/Video/ScreenSharingMediaBox.svelte
@@ -0,0 +1,33 @@
+
+
+
+ {#if $statusStore === 'connecting'}
+
+ {/if}
+ {#if $statusStore === 'error'}
+
+ {/if}
+ {#if $streamStore === null}
+
{name}
+ {:else}
+
+ {/if}
+
+
+
diff --git a/front/src/Components/Video/VideoMediaBox.svelte b/front/src/Components/Video/VideoMediaBox.svelte
new file mode 100644
index 00000000..1a581914
--- /dev/null
+++ b/front/src/Components/Video/VideoMediaBox.svelte
@@ -0,0 +1,48 @@
+
+
+
+ {#if $statusStore === 'connecting'}
+
+ {/if}
+ {#if $statusStore === 'error'}
+
+ {/if}
+ {#if !$constraintStore || $constraintStore.video === false}
+
{name}
+ {/if}
+ {#if $constraintStore && $constraintStore.audio === false}
+
+ {/if}
+
+ {#if $streamStore }
+
+ {/if}
+
+ {#if $constraintStore && $constraintStore.audio !== false}
+
+ {/if}
+
+
diff --git a/front/src/Components/Video/VideoOverlay.svelte b/front/src/Components/Video/VideoOverlay.svelte
new file mode 100644
index 00000000..feb13743
--- /dev/null
+++ b/front/src/Components/Video/VideoOverlay.svelte
@@ -0,0 +1,23 @@
+
+
+
+ {#if $layoutModeStore === LayoutMode.Presentation }
+
+ {:else }
+
+ {/if}
+
+
+
diff --git a/front/src/Components/Video/images/blockSign.svg b/front/src/Components/Video/images/blockSign.svg
new file mode 100644
index 00000000..c64ba294
--- /dev/null
+++ b/front/src/Components/Video/images/blockSign.svg
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/front/src/Components/Video/images/report.svg b/front/src/Components/Video/images/report.svg
new file mode 100644
index 00000000..14753256
--- /dev/null
+++ b/front/src/Components/Video/images/report.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/front/src/Components/Video/utils.ts b/front/src/Components/Video/utils.ts
new file mode 100644
index 00000000..ab7a63be
--- /dev/null
+++ b/front/src/Components/Video/utils.ts
@@ -0,0 +1,27 @@
+export function getColorByString(str: string) : string|null {
+ let hash = 0;
+ if (str.length === 0) {
+ return null;
+ }
+ for (let i = 0; i < str.length; i++) {
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
+ hash = hash & hash;
+ }
+ let color = '#';
+ for (let i = 0; i < 3; i++) {
+ const value = (hash >> (i * 8)) & 255;
+ color += ('00' + value.toString(16)).substr(-2);
+ }
+ return color;
+}
+
+export function srcObject(node: HTMLVideoElement, stream: MediaStream) {
+ node.srcObject = stream;
+ return {
+ update(newStream: MediaStream) {
+ if (node.srcObject != newStream) {
+ node.srcObject = newStream
+ }
+ }
+ }
+}
diff --git a/front/src/Components/images/layout-chat.svg b/front/src/Components/images/layout-chat.svg
new file mode 100644
index 00000000..af071d49
--- /dev/null
+++ b/front/src/Components/images/layout-chat.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/front/src/Components/images/layout-presentation.svg b/front/src/Components/images/layout-presentation.svg
new file mode 100644
index 00000000..bf65d002
--- /dev/null
+++ b/front/src/Components/images/layout-presentation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index 7c07f187..e5507c2f 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -35,10 +35,10 @@ import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import { jitsiFactory } from "../../WebRtc/JitsiFactory";
import {
- AUDIO_LOOP_PROPERTY, AUDIO_VOLUME_PROPERTY, CenterListener,
+ AUDIO_LOOP_PROPERTY, AUDIO_VOLUME_PROPERTY,
+ Box,
JITSI_MESSAGE_PROPERTIES,
layoutManager,
- LayoutMode,
ON_ACTION_TRIGGER_BUTTON,
TRIGGER_JITSI_PROPERTIES,
TRIGGER_WEBSITE_PROPERTIES,
@@ -94,6 +94,9 @@ import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent';
import AnimatedTiles from "phaser-animated-tiles";
import {soundManager} from "./SoundManager";
+import {screenSharingPeerStore} from "../../Stores/PeerStore";
+import {videoFocusStore} from "../../Stores/VideoFocusStore";
+import {biggestAvailableAreaStore} from "../../Stores/BiggestAvailableAreaStore";
export interface GameSceneInitInterface {
initPosition: PointInterface | null,
@@ -132,7 +135,7 @@ interface DeleteGroupEventInterface {
const defaultStartLayerName = 'start';
-export class GameScene extends DirtyScene implements CenterListener {
+export class GameScene extends DirtyScene {
Terrains: Array;
CurrentPlayer!: Player;
MapPlayers!: Phaser.Physics.Arcade.Group;
@@ -172,8 +175,6 @@ export class GameScene extends DirtyScene implements CenterListener {
y: -1000
}
- private presentationModeSprite!: Sprite;
- private chatModeSprite!: Sprite;
private gameMap!: GameMap;
private actionableItems: Map = new Map();
// The item that can be selected by pressing the space key.
@@ -277,7 +278,6 @@ export class GameScene extends DirtyScene implements CenterListener {
this.onMapLoad(data);
}
- this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', { frameWidth: 32, frameHeight: 32 });
this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
//eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.load as any).rexWebFont({
@@ -497,10 +497,6 @@ export class GameScene extends DirtyScene implements CenterListener {
this.outlinedItem?.activate();
});
- this.presentationModeSprite = new PresentationModeIcon(this, 36, this.game.renderer.height - 2);
- this.presentationModeSprite.on('pointerup', this.switchLayoutMode.bind(this));
- this.chatModeSprite = new ChatModeIcon(this, 70, this.game.renderer.height - 2);
- this.chatModeSprite.on('pointerup', this.switchLayoutMode.bind(this));
this.openChatIcon = new OpenChatIcon(this, 2, this.game.renderer.height - 2)
// FIXME: change this to use the UserInputManager class for input
@@ -512,7 +508,8 @@ export class GameScene extends DirtyScene implements CenterListener {
this.reposition();
// From now, this game scene will be notified of reposition events
- layoutManager.setListener(this);
+ biggestAvailableAreaStore.subscribe((box) => this.updateCameraOffset(box));
+
this.triggerOnMapLayerPropertyChange();
this.listenToIframeEvents();
@@ -643,21 +640,19 @@ export class GameScene extends DirtyScene implements CenterListener {
// When connection is performed, let's connect SimplePeer
this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic, this.playerName);
peerStore.connectToSimplePeer(this.simplePeer);
+ screenSharingPeerStore.connectToSimplePeer(this.simplePeer);
+ videoFocusStore.connectToSimplePeer(this.simplePeer);
this.GlobalMessageManager = new GlobalMessageManager(this.connection);
userMessageManager.setReceiveBanListener(this.bannedUser.bind(this));
const self = this;
this.simplePeer.registerPeerConnectionListener({
- onConnect(user: UserSimplePeerInterface) {
- self.presentationModeSprite.setVisible(true);
- self.chatModeSprite.setVisible(true);
+ onConnect(peer) {
self.openChatIcon.setVisible(true);
audioManager.decreaseVolume();
},
onDisconnect(userId: number) {
if (self.simplePeer.getNbConnections() === 0) {
- self.presentationModeSprite.setVisible(false);
- self.chatModeSprite.setVisible(false);
self.openChatIcon.setVisible(false);
audioManager.restoreVolume();
}
@@ -1058,23 +1053,6 @@ ${escapedMessage}
this.MapPlayersByKey = new Map();
}
- private switchLayoutMode(): void {
- //if discussion is activated, this layout cannot be activated
- if (mediaManager.activatedDiscussion) {
- return;
- }
- const mode = layoutManager.getLayoutMode();
- if (mode === LayoutMode.Presentation) {
- layoutManager.switchLayoutMode(LayoutMode.VideoChat);
- this.presentationModeSprite.setFrame(1);
- this.chatModeSprite.setFrame(2);
- } else {
- layoutManager.switchLayoutMode(LayoutMode.Presentation);
- this.presentationModeSprite.setFrame(0);
- this.chatModeSprite.setFrame(3);
- }
- }
-
private initStartXAndStartY() {
// If there is an init position passed
if (this.initPosition !== null) {
@@ -1187,7 +1165,7 @@ ${escapedMessage}
initCamera() {
this.cameras.main.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels);
this.cameras.main.startFollow(this.CurrentPlayer, true);
- this.updateCameraOffset();
+ biggestAvailableAreaStore.recompute();
}
createCollisionWithPlayer() {
@@ -1334,7 +1312,7 @@ ${escapedMessage}
* @param delta The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
*/
update(time: number, delta: number): void {
- mediaManager.updateScene();
+ this.dirty = false;
this.currentTick = time;
this.CurrentPlayer.moveUser(delta);
@@ -1564,20 +1542,17 @@ ${escapedMessage}
}
private reposition(): void {
- this.presentationModeSprite.setY(this.game.renderer.height - 2);
- this.chatModeSprite.setY(this.game.renderer.height - 2);
this.openChatIcon.setY(this.game.renderer.height - 2);
// Recompute camera offset if needed
- this.updateCameraOffset();
+ biggestAvailableAreaStore.recompute();
}
/**
* Updates the offset of the character compared to the center of the screen according to the layout manager
* (tries to put the character in the center of the remaining space if there is a discussion going on.
*/
- private updateCameraOffset(): void {
- const array = layoutManager.findBiggestAvailableArray();
+ private updateCameraOffset(array: Box): void {
const xCenter = (array.xEnd - array.xStart) / 2 + array.xStart;
const yCenter = (array.yEnd - array.yStart) / 2 + array.yStart;
@@ -1587,10 +1562,6 @@ ${escapedMessage}
this.cameras.main.setFollowOffset((xCenter - game.offsetWidth / 2) * window.devicePixelRatio / this.scale.zoom, (yCenter - game.offsetHeight / 2) * window.devicePixelRatio / this.scale.zoom);
}
- public onCenterChange(): void {
- this.updateCameraOffset();
- }
-
public startJitsi(roomName: string, jwt?: string): void {
const allProps = this.gameMap.getCurrentProperties();
const jitsiConfig = this.safeParseJSONstring(allProps.get("jitsiConfig") as string | undefined, 'jitsiConfig');
@@ -1650,6 +1621,6 @@ ${escapedMessage}
zoomByFactor(zoomFactor: number) {
waScaleManager.zoomModifier *= zoomFactor;
- this.updateCameraOffset();
+ biggestAvailableAreaStore.recompute();
}
}
diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts
index bf9c9a08..4fc58f5d 100644
--- a/front/src/Phaser/Menu/MenuScene.ts
+++ b/front/src/Phaser/Menu/MenuScene.ts
@@ -3,17 +3,17 @@ import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCha
import {SelectCompanionScene, SelectCompanionSceneName} from "../Login/SelectCompanionScene";
import {gameManager} from "../Game/GameManager";
import {localUserStore} from "../../Connexion/LocalUserStore";
-import {mediaManager} from "../../WebRtc/MediaManager";
import {gameReportKey, gameReportRessource, ReportMenu} from "./ReportMenu";
import {connectionManager} from "../../Connexion/ConnectionManager";
import {GameConnexionTypes} from "../../Url/UrlManager";
import {WarningContainer, warningContainerHtml, warningContainerKey} from "../Components/WarningContainer";
import {worldFullWarningStream} from "../../Connexion/WorldFullWarningStream";
import {menuIconVisible} from "../../Stores/MenuStore";
+import {videoConstraintStore} from "../../Stores/MediaStore";
+import {showReportScreenStore} from "../../Stores/ShowReportScreenStore";
import { HtmlUtils } from '../../WebRtc/HtmlUtils';
import { iframeListener } from '../../Api/IframeListener';
import { Subscription } from 'rxjs';
-import { videoConstraintStore } from "../../Stores/MediaStore";
import {registerMenuCommandStream} from "../../Api/Events/ui/MenuItemRegisterEvent";
import {sendMenuClickedEvent} from "../../Api/iframe/Ui/MenuItem";
import {consoleGlobalMessageManagerVisibleStore} from "../../Stores/ConsoleGlobalMessageManagerStore";
@@ -111,9 +111,11 @@ export class MenuScene extends Phaser.Scene {
});
this.gameReportElement = new ReportMenu(this, connectionManager.getConnexionType === GameConnexionTypes.anonymous);
- mediaManager.setShowReportModalCallBacks((userId, userName) => {
+ showReportScreenStore.subscribe((user) => {
this.closeAll();
- this.gameReportElement.open(parseInt(userId), userName);
+ if (user !== null) {
+ this.gameReportElement.open(user.userId, user.userName);
+ }
});
this.input.keyboard.on('keyup-TAB', () => {
diff --git a/front/src/Stores/BiggestAvailableAreaStore.ts b/front/src/Stores/BiggestAvailableAreaStore.ts
new file mode 100644
index 00000000..716f37fc
--- /dev/null
+++ b/front/src/Stores/BiggestAvailableAreaStore.ts
@@ -0,0 +1,127 @@
+import {get, writable} from "svelte/store";
+import type {Box} from "../WebRtc/LayoutManager";
+import {HtmlUtils} from "../WebRtc/HtmlUtils";
+import {LayoutMode} from "../WebRtc/LayoutManager";
+import {layoutModeStore} from "./StreamableCollectionStore";
+
+/**
+ * Tries to find the biggest available box of remaining space (this is a space where we can center the character)
+ */
+function findBiggestAvailableArea(): Box {
+ const game = HtmlUtils.querySelectorOrFail('#game canvas');
+ if (get(layoutModeStore) === LayoutMode.VideoChat) {
+ const children = document.querySelectorAll('div.chat-mode > div');
+ const htmlChildren = Array.from(children.values());
+
+ // No chat? Let's go full center
+ if (htmlChildren.length === 0) {
+ return {
+ xStart: 0,
+ yStart: 0,
+ xEnd: game.offsetWidth,
+ yEnd: game.offsetHeight
+ }
+ }
+
+ const lastDiv = htmlChildren[htmlChildren.length - 1];
+ // Compute area between top right of the last div and bottom right of window
+ const area1 = (game.offsetWidth - (lastDiv.offsetLeft + lastDiv.offsetWidth))
+ * (game.offsetHeight - lastDiv.offsetTop);
+
+ // Compute area between bottom of last div and bottom of the screen on whole width
+ const area2 = game.offsetWidth
+ * (game.offsetHeight - (lastDiv.offsetTop + lastDiv.offsetHeight));
+
+ if (area1 < 0 && area2 < 0) {
+ // If screen is full, let's not attempt something foolish and simply center character in the middle.
+ return {
+ xStart: 0,
+ yStart: 0,
+ xEnd: game.offsetWidth,
+ yEnd: game.offsetHeight
+ }
+ }
+ if (area1 <= area2) {
+ return {
+ xStart: 0,
+ yStart: lastDiv.offsetTop + lastDiv.offsetHeight,
+ xEnd: game.offsetWidth,
+ yEnd: game.offsetHeight
+ }
+ } else {
+ return {
+ xStart: lastDiv.offsetLeft + lastDiv.offsetWidth,
+ yStart: lastDiv.offsetTop,
+ xEnd: game.offsetWidth,
+ yEnd: game.offsetHeight
+ }
+ }
+ } else {
+ // Possible destinations: at the center bottom or at the right bottom.
+ const mainSectionChildren = Array.from(document.querySelectorAll('div.main-section > div').values());
+ const sidebarChildren = Array.from(document.querySelectorAll('aside.sidebar > div').values());
+
+ // No presentation? Let's center on the screen
+ if (mainSectionChildren.length === 0) {
+ return {
+ xStart: 0,
+ yStart: 0,
+ xEnd: game.offsetWidth,
+ yEnd: game.offsetHeight
+ }
+ }
+
+ // At this point, we know we have at least one element in the main section.
+ const lastPresentationDiv = mainSectionChildren[mainSectionChildren.length-1];
+
+ const presentationArea = (game.offsetHeight - (lastPresentationDiv.offsetTop + lastPresentationDiv.offsetHeight))
+ * (lastPresentationDiv.offsetLeft + lastPresentationDiv.offsetWidth);
+
+ let leftSideBar: number;
+ let bottomSideBar: number;
+ if (sidebarChildren.length === 0) {
+ leftSideBar = HtmlUtils.getElementByIdOrFail('sidebar').offsetLeft;
+ bottomSideBar = 0;
+ } else {
+ const lastSideBarChildren = sidebarChildren[sidebarChildren.length - 1];
+ leftSideBar = lastSideBarChildren.offsetLeft;
+ bottomSideBar = lastSideBarChildren.offsetTop + lastSideBarChildren.offsetHeight;
+ }
+ const sideBarArea = (game.offsetWidth - leftSideBar)
+ * (game.offsetHeight - bottomSideBar);
+
+ if (presentationArea <= sideBarArea) {
+ return {
+ xStart: leftSideBar,
+ yStart: bottomSideBar,
+ xEnd: game.offsetWidth,
+ yEnd: game.offsetHeight
+ }
+ } else {
+ return {
+ xStart: 0,
+ yStart: lastPresentationDiv.offsetTop + lastPresentationDiv.offsetHeight,
+ xEnd: /*lastPresentationDiv.offsetLeft + lastPresentationDiv.offsetWidth*/ game.offsetWidth , // To avoid flickering when a chat start, we center on the center of the screen, not the center of the main content area
+ yEnd: game.offsetHeight
+ }
+ }
+ }
+}
+
+
+/**
+ * A store that contains the list of (video) peers we are connected to.
+ */
+function createBiggestAvailableAreaStore() {
+
+ const { subscribe, set } = writable({xStart:0, yStart: 0, xEnd: 1, yEnd: 1});
+
+ return {
+ subscribe,
+ recompute: () => {
+ set(findBiggestAvailableArea());
+ }
+ };
+}
+
+export const biggestAvailableAreaStore = createBiggestAvailableAreaStore();
diff --git a/front/src/Stores/GameOverlayStoreVisibility.ts b/front/src/Stores/GameOverlayStoreVisibility.ts
new file mode 100644
index 00000000..c58c929d
--- /dev/null
+++ b/front/src/Stores/GameOverlayStoreVisibility.ts
@@ -0,0 +1,17 @@
+import {writable} from "svelte/store";
+
+/**
+ * A store that contains whether the game overlay is shown or not.
+ * Typically, the overlay is hidden when entering Jitsi meet.
+ */
+function createGameOverlayVisibilityStore() {
+ const { subscribe, set, update } = writable(false);
+
+ return {
+ subscribe,
+ showGameOverlay: () => set(true),
+ hideGameOverlay: () => set(false),
+ };
+}
+
+export const gameOverlayVisibilityStore = createGameOverlayVisibilityStore();
diff --git a/front/src/Stores/MediaStore.ts b/front/src/Stores/MediaStore.ts
index d622511e..b2cd9f42 100644
--- a/front/src/Stores/MediaStore.ts
+++ b/front/src/Stores/MediaStore.ts
@@ -1,13 +1,13 @@
import {derived, get, Readable, readable, writable, Writable} from "svelte/store";
import {peerStore} from "./PeerStore";
import {localUserStore} from "../Connexion/LocalUserStore";
-import {ITiledMapGroupLayer, ITiledMapObjectLayer, ITiledMapTileLayer} from "../Phaser/Map/ITiledMap";
import {userMovingStore} from "./GameStore";
import {HtmlUtils} from "../WebRtc/HtmlUtils";
import {BrowserTooOldError} from "./Errors/BrowserTooOldError";
import {errorStore} from "./ErrorStore";
import {isIOS} from "../WebRtc/DeviceUtils";
import {WebviewOnOldIOS} from "./Errors/WebviewOnOldIOS";
+import {gameOverlayVisibilityStore} from "./GameOverlayStoreVisibility";
/**
* A store that contains the camera state requested by the user (on or off).
@@ -50,20 +50,6 @@ export const visibilityStore = readable(document.visibilityState === 'visible',
};
});
-/**
- * A store that contains whether the game overlay is shown or not.
- * Typically, the overlay is hidden when entering Jitsi meet.
- */
-function createGameOverlayVisibilityStore() {
- const { subscribe, set, update } = writable(false);
-
- return {
- subscribe,
- showGameOverlay: () => set(true),
- hideGameOverlay: () => set(false),
- };
-}
-
/**
* A store that contains whether the EnableCameraScene is shown or not.
*/
@@ -79,7 +65,6 @@ function createEnableCameraSceneVisibilityStore() {
export const requestedCameraState = createRequestedCameraState();
export const requestedMicrophoneState = createRequestedMicrophoneState();
-export const gameOverlayVisibilityStore = createGameOverlayVisibilityStore();
export const enableCameraSceneVisibilityStore = createEnableCameraSceneVisibilityStore();
/**
diff --git a/front/src/Stores/PeerStore.ts b/front/src/Stores/PeerStore.ts
index a582e692..b3690595 100644
--- a/front/src/Stores/PeerStore.ts
+++ b/front/src/Stores/PeerStore.ts
@@ -1,26 +1,62 @@
-import { derived, writable, Writable } from "svelte/store";
-import type {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
-import type {SimplePeer} from "../WebRtc/SimplePeer";
+import {derived, get, readable, writable} from "svelte/store";
+import type {RemotePeer, SimplePeer} from "../WebRtc/SimplePeer";
+import {VideoPeer} from "../WebRtc/VideoPeer";
+import {ScreenSharingPeer} from "../WebRtc/ScreenSharingPeer";
/**
- * A store that contains the camera state requested by the user (on or off).
+ * A store that contains the list of (video) peers we are connected to.
*/
function createPeerStore() {
- let users = new Map();
+ let peers = new Map();
- const { subscribe, set, update } = writable(users);
+ const { subscribe, set, update } = writable(peers);
return {
subscribe,
connectToSimplePeer: (simplePeer: SimplePeer) => {
- users = new Map();
- set(users);
+ peers = new Map();
+ set(peers);
simplePeer.registerPeerConnectionListener({
- onConnect(user: UserSimplePeerInterface) {
+ onConnect(peer: RemotePeer) {
+ if (peer instanceof VideoPeer) {
+ update(users => {
+ users.set(peer.userId, peer);
+ return users;
+ });
+ }
+ },
+ onDisconnect(userId: number) {
update(users => {
- users.set(user.userId, user);
+ users.delete(userId);
return users;
});
+ }
+ })
+ }
+ };
+}
+
+/**
+ * A store that contains the list of screen sharing peers we are connected to.
+ */
+function createScreenSharingPeerStore() {
+ let peers = new Map();
+
+ const { subscribe, set, update } = writable(peers);
+
+ return {
+ subscribe,
+ connectToSimplePeer: (simplePeer: SimplePeer) => {
+ peers = new Map();
+ set(peers);
+ simplePeer.registerPeerConnectionListener({
+ onConnect(peer: RemotePeer) {
+ if (peer instanceof ScreenSharingPeer) {
+ update(users => {
+ users.set(peer.userId, peer);
+ return users;
+ });
+ }
},
onDisconnect(userId: number) {
update(users => {
@@ -34,3 +70,56 @@ function createPeerStore() {
}
export const peerStore = createPeerStore();
+export const screenSharingPeerStore = createScreenSharingPeerStore();
+
+/**
+ * A store that contains ScreenSharingPeer, ONLY if those ScreenSharingPeer are emitting a stream towards us!
+ */
+function createScreenSharingStreamStore() {
+ let peers = new Map();
+
+ return readable