Migration of registerCustomMenu for Menu in Svelte

Refactor subMenuStore
Suppression of old MenuScene and ReportMenu
This commit is contained in:
GRL 2021-08-11 14:07:34 +02:00
parent 9c1926f636
commit 8105e966ff
18 changed files with 137 additions and 617 deletions

View file

@ -1,5 +1,6 @@
import * as tg from "generic-type-guard"; import * as tg from "generic-type-guard";
import { Subject } from "rxjs"; import { Subject } from "rxjs";
import { subMenusStore } from "../../../Stores/MenuStore";
export const isMenuItemRegisterEvent = new tg.IsInterface() export const isMenuItemRegisterEvent = new tg.IsInterface()
.withProperties({ .withProperties({
@ -22,5 +23,5 @@ const _registerMenuCommandStream: Subject<string> = new Subject();
export const registerMenuCommandStream = _registerMenuCommandStream.asObservable(); export const registerMenuCommandStream = _registerMenuCommandStream.asObservable();
export function handleMenuItemRegistrationEvent(event: MenuItemRegisterEvent) { export function handleMenuItemRegistrationEvent(event: MenuItemRegisterEvent) {
_registerMenuCommandStream.next(event.menutItem); subMenusStore.addMenu(event.menutItem);
} }

View file

@ -34,6 +34,7 @@ import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent";
import type { SetVariableEvent } from "./Events/SetVariableEvent"; import type { SetVariableEvent } from "./Events/SetVariableEvent";
import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent"; import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent";
import { EmbeddedWebsite } from "./iframe/Room/EmbeddedWebsite"; import { EmbeddedWebsite } from "./iframe/Room/EmbeddedWebsite";
import { subMenusStore } from "../Stores/MenuStore";
type AnswererCallback<T extends keyof IframeQueryMap> = ( type AnswererCallback<T extends keyof IframeQueryMap> = (
query: IframeQueryMap[T]["query"], query: IframeQueryMap[T]["query"],
@ -93,12 +94,6 @@ class IframeListener {
private readonly _setPropertyStream: Subject<SetPropertyEvent> = new Subject(); private readonly _setPropertyStream: Subject<SetPropertyEvent> = new Subject();
public readonly setPropertyStream = this._setPropertyStream.asObservable(); public readonly setPropertyStream = this._setPropertyStream.asObservable();
private readonly _registerMenuCommandStream: Subject<string> = new Subject();
public readonly registerMenuCommandStream = this._registerMenuCommandStream.asObservable();
private readonly _unregisterMenuCommandStream: Subject<string> = new Subject();
public readonly unregisterMenuCommandStream = this._unregisterMenuCommandStream.asObservable();
private readonly _playSoundStream: Subject<PlaySoundEvent> = new Subject(); private readonly _playSoundStream: Subject<PlaySoundEvent> = new Subject();
public readonly playSoundStream = this._playSoundStream.asObservable(); public readonly playSoundStream = this._playSoundStream.asObservable();
@ -264,7 +259,7 @@ class IframeListener {
const data = payload.data.menutItem; const data = payload.data.menutItem;
// @ts-ignore // @ts-ignore
this.iframeCloseCallbacks.get(iframe).push(() => { this.iframeCloseCallbacks.get(iframe).push(() => {
this._unregisterMenuCommandStream.next(data); subMenusStore.removeMenu(data);
}); });
handleMenuItemRegistrationEvent(payload.data); handleMenuItemRegistrationEvent(payload.data);
} else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) { } else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) {

View file

@ -2,6 +2,7 @@ import type { MenuItemClickedEvent } from "../../Events/ui/MenuItemClickedEvent"
import { iframeListener } from "../../IframeListener"; import { iframeListener } from "../../IframeListener";
export function sendMenuClickedEvent(menuItem: string) { export function sendMenuClickedEvent(menuItem: string) {
console.log("Menu " + menuItem);
iframeListener.postMessage({ iframeListener.postMessage({
type: "menuItemClicked", type: "menuItemClicked",
data: { data: {

View file

@ -51,6 +51,7 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
type: "menuItemClicked", type: "menuItemClicked",
typeChecker: isMenuItemClickedEvent, typeChecker: isMenuItemClickedEvent,
callback: (event) => { callback: (event) => {
console.log("BOUM : " + event.menuItem);
const callback = menuCallbacks.get(event.menuItem); const callback = menuCallbacks.get(event.menuItem);
if (callback) { if (callback) {
callback(event.menuItem); callback(event.menuItem);

View file

@ -52,7 +52,7 @@
<button type="button" class="nes-btn is-primary" on:click={copyLink}>Copy</button> <button type="button" class="nes-btn is-primary" on:click={copyLink}>Copy</button>
</section> </section>
<section class="presentation-map"> <section class="presentation-map">
<p>This room use this map : </p> <p>This room use the following map : </p>
<h3>{mapName}</h3> <h3>{mapName}</h3>
<p class="string-HTML">{mapDescription}</p> <p class="string-HTML">{mapDescription}</p>
</section> </section>

View file

@ -22,37 +22,37 @@
The WorkAdventure team has its own offices in ... WorkAdventure! The WorkAdventure team has its own offices in ... WorkAdventure!
Do not hesitate to come see and talk to us. Do not hesitate to come see and talk to us.
</p> </p>
<a href="https://play.staging.workadventu.re/@tcm/workadventure/wa-village" target="_blank">Visit us</a> <a class="nes-pointer" href="https://play.staging.workadventu.re/@tcm/workadventure/wa-village" target="_blank">Visit us</a>
</section> </section>
<section> <section>
<h3>Our Mail</h3> <h3>Our Mail</h3>
<p>Although we offer a solution to reduce their use, we have an email address that allows us to receive all your requests.</p> <p>Although we offer a solution to reduce their use, we have an email address that allows us to receive all your requests.</p>
<a href="mailto:hello@workadventu.re" target="_blank">hello@workadventu.re</a> <a class="nes-pointer" href="mailto:hello@workadventu.re" target="_blank">hello@workadventu.re</a>
</section> </section>
<section> <section>
<h3>Our web site</h3> <h3>Our web site</h3>
<p>If you want to know more about us, follow the link to our web site.</p> <p>If you want to know more about us, follow the link to our web site.</p>
<a href="https://workadventu.re/about-us" target="_blank">About us</a> <a class="nes-pointer" href="https://workadventu.re/about-us" target="_blank">About us</a>
</section> </section>
<section> <section>
<h3>Our social media</h3> <h3>Our social media</h3>
<a href="https://discord.gg/YGtngdh9gt" target="_blank"> <a href="https://discord.gg/YGtngdh9gt" target="_blank">
<img src="{discordImg}" alt="{'Discord'}"> <img class="nes-pointer" src="{discordImg}" alt="{'Discord'}">
</a> </a>
<a href="https://www.facebook.com/workadventure.WA" target="_blank"> <a href="https://www.facebook.com/workadventure.WA" target="_blank">
<img src="{facebookImg}" alt="{'Facebook'}"> <img class="nes-pointer" src="{facebookImg}" alt="{'Facebook'}">
</a> </a>
<a href="https://www.instagram.com/workadventure_/" target="_blank"> <a href="https://www.instagram.com/workadventure_/" target="_blank">
<img src="{instagramImg}" alt="{'Instagram'}"> <img class="nes-pointer" src="{instagramImg}" alt="{'Instagram'}">
</a> </a>
<a href="https://www.linkedin.com/company/workadventure-by-tcm/" target="_blank"> <a href="https://www.linkedin.com/company/workadventure-by-tcm/" target="_blank">
<img src="{linkedinImg}" alt="{'LinkedIn'}"> <img class="nes-pointer" src="{linkedinImg}" alt="{'LinkedIn'}">
</a> </a>
<a href="https://twitter.com/Workadventure_" target="_blank"> <a href="https://twitter.com/Workadventure_" target="_blank">
<img src="{twitterImg}" alt="{'Twitter'}"> <img class="nes-pointer" src="{twitterImg}" alt="{'Twitter'}">
</a> </a>
<a href="https://www.youtube.com/channel/UCXJ9igV-kb9gw1ftR33y5tA" target="_blank"> <a href="https://www.youtube.com/channel/UCXJ9igV-kb9gw1ftR33y5tA" target="_blank">
<img src="{youtubeImg}" alt="{'Youtube'}"> <img class="nes-pointer" src="{youtubeImg}" alt="{'Youtube'}">
</a> </a>
</section> </section>
</div> </div>

View file

@ -0,0 +1,13 @@
<script lang="ts">
import {sendMenuClickedEvent} from "../../Api/iframe/Ui/MenuItem";
export let menuCommand: string;
function menuItemClicked() {
sendMenuClickedEvent(menuCommand);
}
</script>
<div>
<button type="button" class="nes-btn is-primary" on:click|preventDefault={menuItemClicked}>{menuCommand}</button>
</div>

View file

@ -1,32 +1,57 @@
<script lang="typescript"> <script lang="typescript">
import {fly} from "svelte/transition"; import {fly} from "svelte/transition";
import type { SvelteComponent } from "svelte";
import SettingsSubMenu from "./SettingsSubMenu.svelte"; import SettingsSubMenu from "./SettingsSubMenu.svelte";
import CreateMapSubMenu from "./CreateMapSubMenu.svelte";
import ProfileSubMenu from "./ProfileSubMenu.svelte"; import ProfileSubMenu from "./ProfileSubMenu.svelte";
import ContactSubMenu from "./ContactSubMenu.svelte"; import CreateMapSubMenu from "./CreateMapSubMenu.svelte";
import AboutRoomSubMenu from "./AboutRoomSubMenu.svelte"; import AboutRoomSubMenu from "./AboutRoomSubMenu.svelte";
import GlobalMessagesSubMenu from "./GlobalMessagesSubMenu.svelte"; import GlobalMessageSubMenu from "./GlobalMessagesSubMenu.svelte";
import {menuVisiblilityStore} from "../../Stores/MenuStore"; import ContactSubMenu from "./ContactSubMenu.svelte";
import CustomSubMenu from "./CustomSubMenu.svelte";
import {menuVisiblilityStore, SubMenusInterface, subMenusStore} from "../../Stores/MenuStore";
import {userIsAdminStore} from "../../Stores/GameStore";
import {onMount} from "svelte";
import {get} from "svelte/store";
//TODO: When we register a new custom menu we need to add it here let activeSubMenu: string = SubMenusInterface.settings;
const SubMenus = new Map<string, typeof SvelteComponent>([ let props: { menuCommand: string } = { menuCommand: SubMenusInterface.settings};
['Settings', SettingsSubMenu], let activeComponent: typeof SettingsSubMenu | typeof CustomSubMenu = SettingsSubMenu;
['Profile', ProfileSubMenu],
['Create a Map', CreateMapSubMenu],
['About the room', AboutRoomSubMenu],
['Global Messages', GlobalMessagesSubMenu], //Remove if player has not the admin tag
['Contact', ContactSubMenu] //Always last (except custom)
]);
let activeSubMenu = 'Settings'; onMount(() => {
let activeComponent = SubMenus.get(activeSubMenu); if(!get(userIsAdminStore)) {
subMenusStore.removeMenu(SubMenusInterface.globalMessages);
}
switchMenu(SubMenusInterface.settings);
})
function switchMenu(menu: string) { function switchMenu(menu: string) {
if (SubMenus.has(menu)) { if (get(subMenusStore).find((subMenu) => subMenu === menu)) {
activeSubMenu = menu activeSubMenu = menu;
activeComponent = SubMenus.get(activeSubMenu); props = {menuCommand: menu};
} switch (menu) {
case SubMenusInterface.settings:
activeComponent = SettingsSubMenu;
break;
case SubMenusInterface.profile:
activeComponent = ProfileSubMenu;
break;
case SubMenusInterface.createMap:
activeComponent = CreateMapSubMenu;
break;
case SubMenusInterface.aboutRoom:
activeComponent = AboutRoomSubMenu;
break;
case SubMenusInterface.globalMessages:
activeComponent = GlobalMessageSubMenu;
break;
case SubMenusInterface.contact:
activeComponent = ContactSubMenu;
break;
default:
activeComponent = CustomSubMenu;
break;
}
} else throw ("There is no menu called " + menu);
} }
function closeMenu() { function closeMenu() {
@ -46,16 +71,16 @@
<div class="menu-nav-sidebar nes-container is-rounded" transition:fly="{{ x: -1000, duration: 500 }}"> <div class="menu-nav-sidebar nes-container is-rounded" transition:fly="{{ x: -1000, duration: 500 }}">
<h2>Menu</h2> <h2>Menu</h2>
<nav> <nav>
{#each [...SubMenus] as [submenuKey, submenuComponent]} {#each $subMenusStore as submenu}
<button type="button" class="nes-btn {activeComponent === submenuComponent ? 'is-disabled' : ''}" on:click|preventDefault={() => switchMenu(submenuKey)}>{submenuKey}</button> <button type="button" class="nes-btn {activeSubMenu === submenu ? 'is-disabled' : ''}" on:click|preventDefault={() => switchMenu(submenu)}>
{submenu}
</button>
{/each} {/each}
</nav> </nav>
</div> </div>
<div class="menu-submenu-container nes-container is-rounded" transition:fly="{{ y: -1000, duration: 500 }}"> <div class="menu-submenu-container nes-container is-rounded" transition:fly="{{ y: -1000, duration: 500 }}">
<h2>{activeSubMenu}</h2> <h2>{activeSubMenu}</h2>
{#if activeComponent} <svelte:component this={activeComponent} {...props}/>
<svelte:component this="{activeComponent}"/>
{/if}
</div> </div>
</div> </div>
@ -65,7 +90,7 @@
} }
div.menu-container-main { div.menu-container-main {
--size-first-columns-grid: 15%; //TODO: clamp value --size-first-columns-grid: 15%;
font-family: "Press Start 2P"; font-family: "Press Start 2P";
pointer-events: auto; pointer-events: auto;

View file

@ -2,13 +2,21 @@
import {menuVisiblilityStore} from "../../Stores/MenuStore"; import {menuVisiblilityStore} from "../../Stores/MenuStore";
import {get} from "svelte/store"; import {get} from "svelte/store";
function openMenu(){ function showMenu(){
menuVisiblilityStore.set(!get(menuVisiblilityStore)) menuVisiblilityStore.set(!get(menuVisiblilityStore))
} }
function onKeyDown(e: KeyboardEvent) {
if (e.key === "Tab") {
showMenu();
}
}
</script> </script>
<svelte:window on:keydown={onKeyDown}/>
<main class="menuIcon"> <main class="menuIcon">
<img src="/static/images/logo-WA-min.png" alt="open menu" on:click|preventDefault={openMenu}> <img src="/static/images/logo-WA-min.png" alt="open menu" class="nes-pointer" on:click|preventDefault={showMenu}>
</main> </main>
<style lang="scss"> <style lang="scss">
@ -18,7 +26,6 @@
img { img {
width: 60px; width: 60px;
padding-top: 0; padding-top: 0;
//cursor: url('/resources/logos/cursor_pointer.png'), pointer;
} }
} }
@media only screen and (max-height: 700px) { @media only screen and (max-height: 700px) {

View file

@ -54,7 +54,7 @@ function changeNotification() {
<div class="settings-main" on:submit|preventDefault={saveSetting}> <div class="settings-main" on:submit|preventDefault={saveSetting}>
<section> <section>
<h3>Game quality</h3> <h3>Game quality</h3>
<select bind:value={valueGame}> <select class="nes-pointer" bind:value={valueGame}>
<option value="{120}">High video quality (120 fps)</option> <option value="{120}">High video quality (120 fps)</option>
<option value="{60}">Medium video quality (60 fps, recommended)</option> <option value="{60}">Medium video quality (60 fps, recommended)</option>
<option value="{40}">Minimum video quality (40 fps)</option> <option value="{40}">Minimum video quality (40 fps)</option>
@ -63,7 +63,7 @@ function changeNotification() {
</section> </section>
<section> <section>
<h3>Video quality</h3> <h3>Video quality</h3>
<select bind:value={valueVideo}> <select class="nes-pointer" bind:value={valueVideo}>
<option value="{30}">High video quality (30 fps)</option> <option value="{30}">High video quality (30 fps)</option>
<option value="{20}">Medium video quality (20 fps, recommended)</option> <option value="{20}">Medium video quality (20 fps, recommended)</option>
<option value="{10}">Minimum video quality (10 fps)</option> <option value="{10}">Minimum video quality (10 fps)</option>

View file

@ -1,7 +1,6 @@
import { GameScene } from "./GameScene"; import { GameScene } from "./GameScene";
import { connectionManager } from "../../Connexion/ConnectionManager"; import { connectionManager } from "../../Connexion/ConnectionManager";
import type { Room } from "../../Connexion/Room"; import type { Room } from "../../Connexion/Room";
import { MenuScene, MenuSceneName } from "../Menu/MenuScene";
import { LoginSceneName } from "../Login/LoginScene"; import { LoginSceneName } from "../Login/LoginScene";
import { SelectCharacterSceneName } from "../Login/SelectCharacterScene"; import { SelectCharacterSceneName } from "../Login/SelectCharacterScene";
import { EnableCameraSceneName } from "../Login/EnableCameraScene"; import { EnableCameraSceneName } from "../Login/EnableCameraScene";
@ -85,7 +84,6 @@ export class GameManager {
public goToStartingMap(): void { public goToStartingMap(): void {
console.log("starting " + (this.currentGameSceneName || this.startRoom.key)); console.log("starting " + (this.currentGameSceneName || this.startRoom.key));
this.scenePlugin.start(this.currentGameSceneName || this.startRoom.key); this.scenePlugin.start(this.currentGameSceneName || this.startRoom.key);
this.scenePlugin.launch(MenuSceneName);
if ( if (
!localUserStore.getHelpCameraSettingsShown() && !localUserStore.getHelpCameraSettingsShown() &&
@ -98,8 +96,6 @@ export class GameManager {
public gameSceneIsCreated(scene: GameScene) { public gameSceneIsCreated(scene: GameScene) {
this.currentGameSceneName = scene.scene.key; this.currentGameSceneName = scene.scene.key;
const menuScene: MenuScene = scene.scene.get(MenuSceneName) as MenuScene;
menuScene.revealMenuIcon();
menuIconVisiblilityStore.set(true); menuIconVisiblilityStore.set(true);
} }
@ -112,7 +108,7 @@ export class GameManager {
const gameScene: GameScene = this.scenePlugin.get(this.currentGameSceneName) as GameScene; const gameScene: GameScene = this.scenePlugin.get(this.currentGameSceneName) as GameScene;
gameScene.cleanupClosingScene(); gameScene.cleanupClosingScene();
this.scenePlugin.stop(this.currentGameSceneName); this.scenePlugin.stop(this.currentGameSceneName);
this.scenePlugin.sleep(MenuSceneName); menuIconVisiblilityStore.set(false);
if (!this.scenePlugin.get(targetSceneName)) { if (!this.scenePlugin.get(targetSceneName)) {
this.scenePlugin.add(targetSceneName, sceneClass, false); this.scenePlugin.add(targetSceneName, sceneClass, false);
} }
@ -125,7 +121,7 @@ export class GameManager {
tryResumingGame(fallbackSceneName: string) { tryResumingGame(fallbackSceneName: string) {
if (this.currentGameSceneName) { if (this.currentGameSceneName) {
this.scenePlugin.start(this.currentGameSceneName); this.scenePlugin.start(this.currentGameSceneName);
this.scenePlugin.wake(MenuSceneName); menuIconVisiblilityStore.set(true);
} else { } else {
this.scenePlugin.run(fallbackSceneName); this.scenePlugin.run(fallbackSceneName);
} }

View file

@ -45,7 +45,6 @@ import type { ActionableItem } from "../Items/ActionableItem";
import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface"; import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface";
import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene"; import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene";
import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapObject, ITiledTileSet } from "../Map/ITiledMap"; import type { ITiledMap, ITiledMapLayer, ITiledMapProperty, ITiledMapObject, ITiledTileSet } from "../Map/ITiledMap";
import { MenuScene, MenuSceneName } from "../Menu/MenuScene";
import { PlayerAnimationDirections } from "../Player/Animation"; import { PlayerAnimationDirections } from "../Player/Animation";
import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player"; import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player";
import { ErrorSceneName } from "../Reconnecting/ErrorScene"; import { ErrorSceneName } from "../Reconnecting/ErrorScene";
@ -1298,9 +1297,6 @@ ${escapedMessage}
urlManager.pushStartLayerNameToUrl(roomUrl.hash); urlManager.pushStartLayerNameToUrl(roomUrl.hash);
} }
const menuScene: MenuScene = this.scene.get(MenuSceneName) as MenuScene;
menuScene.reset();
if (!targetRoom.isEqual(this.room)) { if (!targetRoom.isEqual(this.room)) {
if (this.scene.get(targetRoom.key) === null) { if (this.scene.get(targetRoom.key) === null) {
console.error("next room not loaded", targetRoom.key); console.error("next room not loaded", targetRoom.key);

View file

@ -1,13 +1,4 @@
import { gameManager } from "../Game/GameManager"; import { gameManager } from "../Game/GameManager";
import { TextField } from "../Components/TextField";
import Image = Phaser.GameObjects.Image;
import { mediaManager } from "../../WebRtc/MediaManager";
import { SoundMeter } from "../Components/SoundMeter";
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import { touchScreenManager } from "../../Touch/TouchScreenManager";
import { PinchManager } from "../UserInput/PinchManager";
import Zone = Phaser.GameObjects.Zone;
import { MenuScene } from "../Menu/MenuScene";
import { ResizableScene } from "./ResizableScene"; import { ResizableScene } from "./ResizableScene";
import { enableCameraSceneVisibilityStore } from "../../Stores/MediaStore"; import { enableCameraSceneVisibilityStore } from "../../Stores/MediaStore";

View file

@ -9,7 +9,6 @@ import type { CompanionResourceDescriptionInterface } from "../Companion/Compani
import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager"; import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager";
import { touchScreenManager } from "../../Touch/TouchScreenManager"; import { touchScreenManager } from "../../Touch/TouchScreenManager";
import { PinchManager } from "../UserInput/PinchManager"; import { PinchManager } from "../UserInput/PinchManager";
import { MenuScene } from "../Menu/MenuScene";
import { selectCompanionSceneVisibleStore } from "../../Stores/SelectCompanionStore"; import { selectCompanionSceneVisibleStore } from "../../Stores/SelectCompanionStore";
import { waScaleManager } from "../Services/WaScaleManager"; import { waScaleManager } from "../Services/WaScaleManager";
import { isMobile } from "../../Enum/EnvironmentVariable"; import { isMobile } from "../../Enum/EnvironmentVariable";

View file

@ -1,429 +0,0 @@
import { LoginScene, LoginSceneName } from "../Login/LoginScene";
import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene";
import { SelectCompanionScene, SelectCompanionSceneName } from "../Login/SelectCompanionScene";
import { gameManager } from "../Game/GameManager";
import { localUserStore } from "../../Connexion/LocalUserStore";
import { gameReportKey, gameReportRessource, ReportMenu } from "./ReportMenu";
import { connectionManager } from "../../Connexion/ConnectionManager";
import { GameConnexionTypes } from "../../Url/UrlManager";
import { menuIconVisiblilityStore, menuVisiblilityStore } 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 { registerMenuCommandStream } from "../../Api/Events/ui/MenuItemRegisterEvent";
import { sendMenuClickedEvent } from "../../Api/iframe/Ui/MenuItem";
import { consoleGlobalMessageManagerVisibleStore } from "../../Stores/ConsoleGlobalMessageManagerStore";
import { get } from "svelte/store";
import { playersStore } from "../../Stores/PlayersStore";
import { mediaManager } from "../../WebRtc/MediaManager";
import { chatVisibilityStore } from "../../Stores/ChatStore";
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
export const MenuSceneName = "MenuScene";
const gameMenuKey = "gameMenu";
const gameMenuIconKey = "gameMenuIcon";
const gameSettingsMenuKey = "gameSettingsMenu";
const gameShare = "gameShare";
const closedSideMenuX = -1000;
const openedSideMenuX = 0;
/**
* The scene that manages the game menu, rendered using a DOM element.
*/
export class MenuScene extends Phaser.Scene {
private menuElement!: Phaser.GameObjects.DOMElement;
private gameQualityMenuElement!: Phaser.GameObjects.DOMElement;
private gameShareElement!: Phaser.GameObjects.DOMElement;
private gameReportElement!: ReportMenu;
private sideMenuOpened = false;
private settingsMenuOpened = false;
private gameShareOpened = false;
private gameQualityValue: number;
private videoQualityValue: number;
private menuButton!: Phaser.GameObjects.DOMElement;
private subscriptions = new Subscription();
constructor() {
super({ key: MenuSceneName });
this.gameQualityValue = localUserStore.getGameQualityValue();
this.videoQualityValue = localUserStore.getVideoQualityValue();
this.subscriptions.add(
registerMenuCommandStream.subscribe((menuCommand) => {
this.addMenuOption(menuCommand);
})
);
this.subscriptions.add(
iframeListener.unregisterMenuCommandStream.subscribe((menuCommand) => {
this.destroyMenu(menuCommand);
})
);
}
reset() {
const addedMenuItems = [...this.menuElement.node.querySelectorAll(".fromApi")];
for (let index = addedMenuItems.length - 1; index >= 0; index--) {
addedMenuItems[index].remove();
}
}
public addMenuOption(menuText: string) {
const wrappingSection = document.createElement("section");
const escapedHtml = HtmlUtils.escapeHtml(menuText);
wrappingSection.innerHTML = `<button class="fromApi" id="${escapedHtml}">${escapedHtml}</button>`;
const menuItemContainer = this.menuElement.node.querySelector("#gameMenu main");
if (menuItemContainer) {
menuItemContainer.querySelector(`#${escapedHtml}.fromApi`)?.remove();
menuItemContainer.insertBefore(wrappingSection, menuItemContainer.querySelector("#socialLinks"));
}
}
preload() {
this.load.html(gameMenuKey, "resources/html/gameMenu.html");
this.load.html(gameMenuIconKey, "resources/html/gameMenuIcon.html");
this.load.html(gameSettingsMenuKey, "resources/html/gameQualityMenu.html");
this.load.html(gameShare, "resources/html/gameShare.html");
this.load.html(gameReportKey, gameReportRessource);
}
create() {
this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey);
this.menuElement.setOrigin(0);
MenuScene.revealMenusAfterInit(this.menuElement, "gameMenu");
if (mediaManager.hasNotification()) {
HtmlUtils.getElementByIdOrFail("enableNotification").hidden = true;
}
const middleX = window.innerWidth / 3 - 298;
this.gameQualityMenuElement = this.add.dom(middleX, -400).createFromCache(gameSettingsMenuKey);
MenuScene.revealMenusAfterInit(this.gameQualityMenuElement, "gameQuality");
this.gameShareElement = this.add.dom(middleX, -400).createFromCache(gameShare);
MenuScene.revealMenusAfterInit(this.gameShareElement, gameShare);
this.gameShareElement.addListener("click");
this.gameShareElement.on("click", (event: MouseEvent) => {
event.preventDefault();
if ((event?.target as HTMLInputElement).id === "gameShareFormSubmit") {
this.copyLink();
} else if ((event?.target as HTMLInputElement).id === "gameShareFormCancel") {
this.closeGameShare();
}
});
this.gameReportElement = new ReportMenu(
this,
connectionManager.getConnexionType === GameConnexionTypes.anonymous
);
showReportScreenStore.subscribe((user) => {
if (user !== null) {
this.closeAll();
//this.gameReportElement.open(user., user.userName);
}
});
this.input.keyboard.on("keyup-TAB", () => {
this.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu();
});
this.menuButton = this.add.dom(0, 0).createFromCache(gameMenuIconKey);
this.menuButton.addListener("click");
this.menuButton.on("click", () => {
this.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu();
});
this.menuElement.addListener("click");
this.menuElement.on("click", this.onMenuClick.bind(this));
chatVisibilityStore.subscribe((v) => {
this.menuButton.setVisible(!v);
});
}
//todo put this method in a parent menuElement class
static revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
//Dom elements will appear inside the viewer screen when creating before being moved out of it, which create a flicker effect.
//To prevent this, we put a 'hidden' attribute on the root element, we remove it only after the init is done.
setTimeout(() => {
(menuElement.getChildByID(rootDomId) as HTMLElement).hidden = false;
}, 250);
}
public revealMenuIcon(): void {
return;
//TODO fix me: add try catch because at the same time, 'this.menuButton' variable doesn't exist and there is error on 'getChildByID' function
try {
(this.menuButton.getChildByID("menuIcon") as HTMLElement).hidden = false;
} catch (err) {
console.error(err);
}
}
openSideMenu() {
menuVisiblilityStore.set(true);
if (this.sideMenuOpened) return;
/*this.closeAll();
this.sideMenuOpened = true;
this.menuButton.getChildByID('openMenuButton').innerHTML = 'X';
const connection = gameManager.getCurrentGameScene(this).connection;
if (connection && connection.isAdmin()) {
const adminSection = this.menuElement.getChildByID('adminConsoleSection') as HTMLElement;
adminSection.hidden = false;
}
//TODO bind with future metadata of card
//if (connectionManager.getConnexionType === GameConnexionTypes.anonymous){
const adminSection = this.menuElement.getChildByID('socialLinks') as HTMLElement;
adminSection.hidden = false;
//}
this.tweens.add({
targets: this.menuElement,
x: openedSideMenuX,
duration: 500,
ease: 'Power3'
});*/
}
private closeSideMenu(): void {
menuVisiblilityStore.set(false);
/* if (!this.sideMenuOpened) return;
this.sideMenuOpened = false;
this.closeAll();
this.menuButton.getChildByID('openMenuButton').innerHTML = `<img src="/static/images/menu.svg">`;
consoleGlobalMessageManagerVisibleStore.set(false);
this.tweens.add({
targets: this.menuElement,
x: closedSideMenuX,
duration: 500,
ease: 'Power3'
});*/
}
private openGameSettingsMenu(): void {
if (this.settingsMenuOpened) {
this.closeGameQualityMenu();
return;
}
//close all
this.closeAll();
this.settingsMenuOpened = true;
const gameQualitySelect = this.gameQualityMenuElement.getChildByID("select-game-quality") as HTMLInputElement;
gameQualitySelect.value = "" + this.gameQualityValue;
const videoQualitySelect = this.gameQualityMenuElement.getChildByID("select-video-quality") as HTMLInputElement;
videoQualitySelect.value = "" + this.videoQualityValue;
this.gameQualityMenuElement.addListener("click");
this.gameQualityMenuElement.on("click", (event: MouseEvent) => {
event.preventDefault();
if ((event?.target as HTMLInputElement).id === "gameQualityFormSubmit") {
const gameQualitySelect = this.gameQualityMenuElement.getChildByID(
"select-game-quality"
) as HTMLInputElement;
const videoQualitySelect = this.gameQualityMenuElement.getChildByID(
"select-video-quality"
) as HTMLInputElement;
this.saveSetting(parseInt(gameQualitySelect.value), parseInt(videoQualitySelect.value));
} else if ((event?.target as HTMLInputElement).id === "gameQualityFormCancel") {
this.closeGameQualityMenu();
}
});
let middleY = this.scale.height / 2 - 392 / 2;
if (middleY < 0) {
middleY = 0;
}
let middleX = this.scale.width / 2 - 457 / 2;
if (middleX < 0) {
middleX = 0;
}
this.tweens.add({
targets: this.gameQualityMenuElement,
y: middleY,
x: middleX,
duration: 1000,
ease: "Power3",
});
}
private closeGameQualityMenu(): void {
if (!this.settingsMenuOpened) return;
this.settingsMenuOpened = false;
this.gameQualityMenuElement.removeListener("click");
this.tweens.add({
targets: this.gameQualityMenuElement,
y: -400,
duration: 1000,
ease: "Power3",
});
}
private openGameShare(): void {
if (this.gameShareOpened) {
this.closeGameShare();
return;
}
//close all
this.closeAll();
const gameShareLink = this.gameShareElement.getChildByID("gameShareLink") as HTMLInputElement;
gameShareLink.value = location.toString();
this.gameShareOpened = true;
let middleY = this.scale.height / 2 - 85;
if (middleY < 0) {
middleY = 0;
}
let middleX = this.scale.width / 2 - 200;
if (middleX < 0) {
middleX = 0;
}
this.tweens.add({
targets: this.gameShareElement,
y: middleY,
x: middleX,
duration: 1000,
ease: "Power3",
});
}
private closeGameShare(): void {
const gameShareInfo = this.gameShareElement.getChildByID("gameShareInfo") as HTMLParagraphElement;
gameShareInfo.innerText = "";
gameShareInfo.style.display = "none";
this.gameShareOpened = false;
this.tweens.add({
targets: this.gameShareElement,
y: -400,
duration: 1000,
ease: "Power3",
});
}
private onMenuClick(event: MouseEvent) {
const htmlMenuItem = event?.target as HTMLInputElement;
if (htmlMenuItem.classList.contains("not-button")) {
return;
}
event.preventDefault();
if (htmlMenuItem.classList.contains("fromApi")) {
sendMenuClickedEvent(htmlMenuItem.id);
return;
}
switch ((event?.target as HTMLInputElement).id) {
case "changeNameButton":
this.closeSideMenu();
gameManager.leaveGame(LoginSceneName, new LoginScene());
break;
case "sparkButton":
this.gotToCreateMapPage();
break;
case "changeSkinButton":
this.closeSideMenu();
gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene());
break;
case "changeCompanionButton":
this.closeSideMenu();
gameManager.leaveGame(SelectCompanionSceneName, new SelectCompanionScene());
break;
case "closeButton":
this.closeSideMenu();
break;
case "shareButton":
this.openGameShare();
break;
case "editGameSettingsButton":
this.openGameSettingsMenu();
break;
case "oidcLogin":
connectionManager.loadOpenIDScreen();
break;
case "toggleFullscreen":
this.toggleFullscreen();
break;
case "enableNotification":
this.enableNotification();
break;
case "adminConsoleButton":
if (get(consoleGlobalMessageManagerVisibleStore)) {
consoleGlobalMessageManagerVisibleStore.set(false);
} else {
consoleGlobalMessageManagerVisibleStore.set(true);
}
break;
}
}
private async copyLink() {
await navigator.clipboard.writeText(location.toString());
const gameShareInfo = this.gameShareElement.getChildByID("gameShareInfo") as HTMLParagraphElement;
gameShareInfo.innerText = "Link copied, you can share it now!";
gameShareInfo.style.display = "block";
}
private saveSetting(valueGame: number, valueVideo: number) {
if (valueGame !== this.gameQualityValue) {
this.gameQualityValue = valueGame;
localUserStore.setGameQualityValue(valueGame);
window.location.reload();
}
if (valueVideo !== this.videoQualityValue) {
this.videoQualityValue = valueVideo;
localUserStore.setVideoQualityValue(valueVideo);
videoConstraintStore.setFrameRate(valueVideo);
}
this.closeGameQualityMenu();
}
private gotToCreateMapPage() {
//const sparkHost = 'https://'+window.location.host.replace('play.', '')+'/choose-map.html';
//TODO fix me: this button can to send us on WorkAdventure BO.
//const sparkHost = ADMIN_URL + "/getting-started";
//The redirection must be only on workadventu.re domain
//To day the domain staging cannot be use by customer
const sparkHost = "https://workadventu.re/getting-started";
window.open(sparkHost, "_blank");
}
private closeAll() {
this.closeGameQualityMenu();
this.closeGameShare();
this.gameReportElement.close();
}
private toggleFullscreen() {
const body = document.querySelector("body");
if (body) {
if (document.fullscreenElement ?? document.fullscreen) {
document.exitFullscreen();
} else {
body.requestFullscreen();
}
}
}
public destroyMenu(menu: string) {
this.menuElement.getChildByID(menu).remove();
}
public isDirty(): boolean {
return false;
}
private enableNotification() {
mediaManager.requestNotification().then(() => {
if (mediaManager.hasNotification()) {
HtmlUtils.getElementByIdOrFail("enableNotification").hidden = true;
}
});
}
}

View file

@ -1,117 +0,0 @@
import { MenuScene } from "./MenuScene";
import { gameManager } from "../Game/GameManager";
import { blackListManager } from "../../WebRtc/BlackListManager";
export const gameReportKey = "gameReport";
export const gameReportRessource = "resources/html/gameReport.html";
export class ReportMenu extends Phaser.GameObjects.DOMElement {
private opened: boolean = false;
private userUuid!: string;
private userName!: string | undefined;
private anonymous: boolean;
constructor(scene: Phaser.Scene, anonymous: boolean) {
super(scene, -2000, -2000);
this.anonymous = anonymous;
this.createFromCache(gameReportKey);
if (this.anonymous) {
const divToHide = this.getChildByID("reportSection") as HTMLElement;
divToHide.hidden = true;
const textToHide = this.getChildByID("askActionP") as HTMLElement;
textToHide.hidden = true;
}
scene.add.existing(this);
MenuScene.revealMenusAfterInit(this, gameReportKey);
this.addListener("click");
this.on("click", (event: MouseEvent) => {
event.preventDefault();
if ((event?.target as HTMLInputElement).id === "gameReportFormSubmit") {
this.submitReport();
} else if ((event?.target as HTMLInputElement).id === "gameReportFormCancel") {
this.close();
} else if ((event?.target as HTMLInputElement).id === "toggleBlockButton") {
this.toggleBlock();
}
});
}
public open(userUuid: string, userName: string | undefined): void {
if (this.opened) {
this.close();
return;
}
this.userUuid = userUuid;
this.userName = userName;
const mainEl = this.getChildByID("gameReport") as HTMLElement;
this.x = this.getCenteredX(mainEl);
this.y = this.getHiddenY(mainEl);
const gameTitleReport = this.getChildByID("nameReported") as HTMLElement;
gameTitleReport.innerText = userName || "";
const blockButton = this.getChildByID("toggleBlockButton") as HTMLElement;
blockButton.innerText = blackListManager.isBlackListed(this.userUuid) ? "Unblock this user" : "Block this user";
this.opened = true;
//gameManager.getCurrentGameScene().userInputManager.disableControls();
this.scene.tweens.add({
targets: this,
y: this.getCenteredY(mainEl),
duration: 1000,
ease: "Power3",
});
}
public close(): void {
//gameManager.getCurrentGameScene().userInputManager.restoreControls();
this.opened = false;
const mainEl = this.getChildByID("gameReport") as HTMLElement;
this.scene.tweens.add({
targets: this,
y: this.getHiddenY(mainEl),
duration: 1000,
ease: "Power3",
});
}
//todo: into a parent class?
private getCenteredX(mainEl: HTMLElement): number {
return window.innerWidth / 4 - mainEl.clientWidth / 2;
}
private getHiddenY(mainEl: HTMLElement): number {
return -mainEl.clientHeight - 50;
}
private getCenteredY(mainEl: HTMLElement): number {
return window.innerHeight / 4 - mainEl.clientHeight / 2;
}
private toggleBlock(): void {
!blackListManager.isBlackListed(this.userUuid)
? blackListManager.blackList(this.userUuid)
: blackListManager.cancelBlackList(this.userUuid);
this.close();
}
private submitReport(): void {
const gamePError = this.getChildByID("gameReportErr") as HTMLParagraphElement;
gamePError.innerText = "";
gamePError.style.display = "none";
const gameTextArea = this.getChildByID("gameReportInput") as HTMLInputElement;
if (!gameTextArea || !gameTextArea.value) {
gamePError.innerText = "Report message cannot to be empty.";
gamePError.style.display = "block";
return;
}
//gameManager.getCurrentGameScene().connection?.emitReportPlayerMessage(this.userId, gameTextArea.value);
this.close();
}
}

View file

@ -23,3 +23,46 @@ function createWarningContainerStore() {
} }
export const warningContainerStore = createWarningContainerStore(); export const warningContainerStore = createWarningContainerStore();
export enum SubMenusInterface {
settings = "Settings",
profile = "Profile",
createMap = "Create a Map",
aboutRoom = "About the Room",
globalMessages = "Global Messages",
contact = "Contact",
}
function createSubMenusStore() {
const { subscribe, update } = writable<string[]>([
SubMenusInterface.settings,
SubMenusInterface.profile,
SubMenusInterface.createMap,
SubMenusInterface.aboutRoom,
SubMenusInterface.globalMessages,
SubMenusInterface.contact,
]);
return {
subscribe,
addMenu(menuCommand: string) {
update((menuList: string[]) => {
if (!menuList.find((menu) => menu === menuCommand)) {
menuList.push(menuCommand);
}
return menuList;
});
},
removeMenu(menuCommand: string) {
update((menuList: string[]) => {
const index = menuList.findIndex((menu) => menu === menuCommand);
if (index !== -1) {
menuList.splice(index, 1);
}
return menuList;
});
},
};
}
export const subMenusStore = createSubMenusStore();

View file

@ -13,7 +13,6 @@ import WebFontLoaderPlugin from "phaser3-rex-plugins/plugins/webfontloader-plugi
import OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js"; import OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js";
import { EntryScene } from "./Phaser/Login/EntryScene"; import { EntryScene } from "./Phaser/Login/EntryScene";
import { coWebsiteManager } from "./WebRtc/CoWebsiteManager"; import { coWebsiteManager } from "./WebRtc/CoWebsiteManager";
import { MenuScene } from "./Phaser/Menu/MenuScene";
import { localUserStore } from "./Connexion/LocalUserStore"; import { localUserStore } from "./Connexion/LocalUserStore";
import { ErrorScene } from "./Phaser/Reconnecting/ErrorScene"; import { ErrorScene } from "./Phaser/Reconnecting/ErrorScene";
import { iframeListener } from "./Api/IframeListener"; import { iframeListener } from "./Api/IframeListener";
@ -97,7 +96,6 @@ const config: GameConfig = {
ReconnectingScene, ReconnectingScene,
ErrorScene, ErrorScene,
CustomizeScene, CustomizeScene,
MenuScene,
], ],
//resolution: window.devicePixelRatio / 2, //resolution: window.devicePixelRatio / 2,
fps: fps, fps: fps,