Migrate layoutManager in Svelte

This commit is contained in:
GRL 2021-08-03 11:13:08 +02:00
parent 3ab069d650
commit 1436b15328
6 changed files with 178 additions and 121 deletions

View file

@ -29,6 +29,8 @@
import ConsoleGlobalMessageManager from "./ConsoleGlobalMessageManager/ConsoleGlobalMessageManager.svelte";
import {warningContainerStore} from "../Stores/MenuStore";
import WarningContainer from "./WarningContainer/WarningContainer.svelte";
import {layoutManagerVisibilityStore} from "../Stores/LayoutManagerStore";
import LayoutManager from "./LayoutManager/LayoutManager.svelte";
export let game: Game;
@ -65,6 +67,11 @@
<AudioPlaying url={$soundPlayingStore} />
</div>
{/if}
{#if $layoutManagerVisibilityStore}
<div>
<LayoutManager></LayoutManager>
</div>
{/if}
{#if $gameOverlayVisibilityStore}
<div>
<VideoOverlay></VideoOverlay>

View file

@ -0,0 +1,78 @@
<script lang="ts">
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
import { onDestroy, onMount } from "svelte";
import { get } from "svelte/store";
onMount(() => {
for (const action of get(layoutManagerActionStore)) {
action.userInputManager?.addSpaceEventListner(action.callback);
if ( action.type === 'warning') {
//remove it after 10 sec
setTimeout(() => {
layoutManagerActionStore.removeAction(action);
}, 10000)
}
}
})
onDestroy(() => {
for (const action of get(layoutManagerActionStore)) {
action.userInputManager?.removeSpaceEventListner(action.callback);
}
layoutManagerActionStore.clearActions();
})
function onClick(callback: () => void) {
callback();
}
</script>
<div class="layout-manager-list">
{#each $layoutManagerActionStore as action}
<div class="nes-container is-rounded {action.type}" on:click={() => onClick(action.callback)}>
<p>{action.message}</p>
</div>
{/each}
</div>
<style lang="scss">
div.layout-manager-list {
pointer-events: auto;
position: absolute;
left: 0;
right: 0;
bottom: 40px;
margin: 0 auto;
padding: 0;
width: clamp(200px, 20vw, 20vw);
display: flex;
flex-direction: column;
animation: moveMessage .5s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
div.nes-container.is-rounded {
padding: 8px 4px;
text-align: center;
font-family: Lato;
color: whitesmoke;
background-color: rgb(0,0,0,0.5);
&.warning {
background-color: #ff9800eb;
color: #000;
}
}
@keyframes moveMessage {
0% {bottom: 40px;}
50% {bottom: 30px;}
100% {bottom: 40px;}
}
</style>

View file

@ -21,7 +21,6 @@ import {
AUDIO_VOLUME_PROPERTY,
Box,
JITSI_MESSAGE_PROPERTIES,
layoutManager,
ON_ACTION_TRIGGER_BUTTON,
TRIGGER_JITSI_PROPERTIES,
TRIGGER_WEBSITE_PROPERTIES,
@ -89,6 +88,8 @@ import { SharedVariablesManager } from "./SharedVariablesManager";
import { playersStore } from "../../Stores/PlayersStore";
import { chatVisibilityStore } from "../../Stores/ChatStore";
import { userIsAdminStore } from "../../Stores/GameStore";
import { layoutManagerActionStore, layoutManagerVisibilityStore } from "../../Stores/LayoutManagerStore";
import { get } from "svelte/store";
export interface GameSceneInitInterface {
initPosition: PointInterface | null;
@ -793,7 +794,7 @@ export class GameScene extends DirtyScene {
});
this.gameMap.onPropertyChange("openWebsite", (newValue, oldValue, allProps) => {
if (newValue === undefined) {
layoutManager.removeActionButton("openWebsite", this.userInputManager);
layoutManagerVisibilityStore.set(false);
coWebsiteManager.closeCoWebsite();
} else {
const openWebsiteFunction = () => {
@ -803,7 +804,7 @@ export class GameScene extends DirtyScene {
allProps.get("openWebsiteAllowApi") as boolean | undefined,
allProps.get("openWebsitePolicy") as string | undefined
);
layoutManager.removeActionButton("openWebsite", this.userInputManager);
layoutManagerVisibilityStore.set(false);
};
const openWebsiteTriggerValue = allProps.get(TRIGGER_WEBSITE_PROPERTIES);
@ -812,14 +813,13 @@ export class GameScene extends DirtyScene {
if (message === undefined) {
message = "Press SPACE or touch here to open web site";
}
layoutManager.addActionButton(
"openWebsite",
message.toString(),
() => {
openWebsiteFunction();
},
this.userInputManager
);
layoutManagerActionStore.addAction({
type: "openWebsite",
message: message,
callback: () => openWebsiteFunction(),
userInputManager: this.userInputManager,
});
layoutManagerVisibilityStore.set(true);
} else {
openWebsiteFunction();
}
@ -827,7 +827,7 @@ export class GameScene extends DirtyScene {
});
this.gameMap.onPropertyChange("jitsiRoom", (newValue, oldValue, allProps) => {
if (newValue === undefined) {
layoutManager.removeActionButton("jitsiRoom", this.userInputManager);
layoutManagerVisibilityStore.set(false);
this.stopJitsi();
} else {
const openJitsiRoomFunction = () => {
@ -840,7 +840,7 @@ export class GameScene extends DirtyScene {
} else {
this.startJitsi(roomName, undefined);
}
layoutManager.removeActionButton("jitsiRoom", this.userInputManager);
layoutManagerVisibilityStore.set(false);
};
const jitsiTriggerValue = allProps.get(TRIGGER_JITSI_PROPERTIES);
@ -849,14 +849,13 @@ export class GameScene extends DirtyScene {
if (message === undefined) {
message = "Press SPACE or touch here to enter Jitsi Meet room";
}
layoutManager.addActionButton(
"jitsiRoom",
message.toString(),
() => {
openJitsiRoomFunction();
},
this.userInputManager
);
layoutManagerActionStore.addAction({
type: "jitsiRoom",
message: message,
callback: () => openJitsiRoomFunction(),
userInputManager: this.userInputManager,
});
layoutManagerVisibilityStore.set(true);
} else {
openJitsiRoomFunction();
}
@ -1153,7 +1152,7 @@ ${escapedMessage}
let targetRoom: Room;
try {
targetRoom = await Room.createRoom(roomUrl);
} catch (e: unknown) {
} catch (e) {
console.error('Error while fetching new room "' + roomUrl.toString() + '"', e);
this.mapTransitioning = false;
return;
@ -1279,7 +1278,7 @@ ${escapedMessage}
try {
const room = await Room.createRoom(exitRoomPath);
return gameManager.loadMap(room, this.scene);
} catch (e: unknown) {
} catch (e) {
console.warn('Error while pre-loading exit room "' + exitRoomPath.toString() + '"', e);
}
}

View file

@ -0,0 +1,53 @@
import { writable } from "svelte/store";
import type { UserInputManager } from "../Phaser/UserInput/UserInputManager";
export interface LayoutManagerAction {
type: string;
message: string | number | boolean | undefined;
callback: () => void;
userInputManager: UserInputManager | undefined;
}
export const layoutManagerVisibilityStore = writable(false);
function createLayoutManagerAction() {
const { subscribe, set, update } = writable<LayoutManagerAction[]>([]);
return {
subscribe,
addAction: (newAction: LayoutManagerAction): void => {
update((list: LayoutManagerAction[]) => {
let found = false;
for (const actions of list) {
if (actions.type === newAction.type && actions.message === newAction.message) {
found = true;
}
}
if (!found) {
list.push(newAction);
}
return list;
});
},
removeAction: (oldAction: LayoutManagerAction): void => {
update((list: LayoutManagerAction[]) => {
const index = list.findIndex(
(actions) => actions.type === oldAction.type && actions.message === oldAction.message
);
if (index !== -1) {
list.splice(index, 1);
}
return list;
});
},
clearActions: (): void => {
set([]);
},
};
}
export const layoutManagerActionStore = createLayoutManagerAction();

View file

@ -1,6 +1,3 @@
import type { UserInputManager } from "../Phaser/UserInput/UserInputManager";
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",
@ -27,85 +24,3 @@ export const AUDIO_VOLUME_PROPERTY = "audioVolume";
export const AUDIO_LOOP_PROPERTY = "audioLoop";
export type Box = { xStart: number; yStart: number; xEnd: number; yEnd: number };
class LayoutManager {
private actionButtonTrigger: Map<string, Function> = new Map<string, Function>();
private actionButtonInformation: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>();
public addActionButton(id: string, text: string, callBack: Function, userInputManager: UserInputManager) {
//delete previous element
this.removeActionButton(id, userInputManager);
//create div and text html component
const p = document.createElement("p");
p.classList.add("action-body");
p.innerText = text;
const div = document.createElement("div");
div.classList.add("action");
div.id = id;
div.appendChild(p);
this.actionButtonInformation.set(id, div);
const mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("main-container");
mainContainer.appendChild(div);
//add trigger action
div.onpointerdown = () => callBack();
this.actionButtonTrigger.set(id, callBack);
userInputManager.addSpaceEventListner(callBack);
}
public removeActionButton(id: string, userInputManager?: UserInputManager) {
//delete previous element
const previousDiv = this.actionButtonInformation.get(id);
if (previousDiv) {
previousDiv.remove();
this.actionButtonInformation.delete(id);
}
const previousEventCallback = this.actionButtonTrigger.get(id);
if (previousEventCallback && userInputManager) {
userInputManager.removeSpaceEventListner(previousEventCallback);
}
}
public addInformation(id: string, text: string, callBack?: Function, userInputManager?: UserInputManager) {
//delete previous element
for (const [key, value] of this.actionButtonInformation) {
this.removeActionButton(key, userInputManager);
}
//create div and text html component
const p = document.createElement("p");
p.classList.add("action-body");
p.innerText = text;
const div = document.createElement("div");
div.classList.add("action");
div.classList.add(id);
div.id = id;
div.appendChild(p);
this.actionButtonInformation.set(id, div);
const mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("main-container");
mainContainer.appendChild(div);
//add trigger action
if (callBack) {
div.onpointerdown = () => {
callBack();
this.removeActionButton(id, userInputManager);
};
}
//remove it after 10 sec
setTimeout(() => {
this.removeActionButton(id, userInputManager);
}, 10000);
}
}
const layoutManager = new LayoutManager();
export { layoutManager };

View file

@ -1,4 +1,3 @@
import { layoutManager } from "./LayoutManager";
import { HtmlUtils } from "./HtmlUtils";
import type { UserInputManager } from "../Phaser/UserInput/UserInputManager";
import { localStreamStore } from "../Stores/MediaStore";
@ -10,6 +9,8 @@ export type StopScreenSharingCallback = (media: MediaStream) => void;
import { cowebsiteCloseButtonId } from "./CoWebsiteManager";
import { gameOverlayVisibilityStore } from "../Stores/GameOverlayStoreVisibility";
import { layoutManagerActionStore, layoutManagerVisibilityStore } from "../Stores/LayoutManagerStore";
import { get } from "svelte/store";
export class MediaManager {
startScreenSharingCallBacks: Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
@ -23,14 +24,16 @@ export class MediaManager {
localStreamStore.subscribe((result) => {
if (result.type === "error") {
console.error(result.error);
layoutManager.addInformation(
"warning",
"Camera access denied. Click here and check your browser permissions.",
() => {
layoutManagerActionStore.addAction({
type: "warning",
message: "Camera access denied. Click here and check your browser permissions.",
callback: () => {
helpCameraSettingsVisibleStore.set(true);
layoutManagerVisibilityStore.set(false);
},
this.userInputManager
);
userInputManager: this.userInputManager,
});
layoutManagerVisibilityStore.set(true);
return;
}
});
@ -38,14 +41,16 @@ export class MediaManager {
screenSharingLocalStreamStore.subscribe((result) => {
if (result.type === "error") {
console.error(result.error);
layoutManager.addInformation(
"warning",
"Screen sharing denied. Click here and check your browser permissions.",
() => {
layoutManagerActionStore.addAction({
type: "warning",
message: "Screen sharing denied. Click here and check your browser permissions.",
callback: () => {
helpCameraSettingsVisibleStore.set(true);
layoutManagerVisibilityStore.set(false);
},
this.userInputManager
);
userInputManager: this.userInputManager,
});
layoutManagerVisibilityStore.set(true);
return;
}
});