diff --git a/docs/maps/api-ui.md b/docs/maps/api-ui.md index f15de5df..44248b53 100644 --- a/docs/maps/api-ui.md +++ b/docs/maps/api-ui.md @@ -65,3 +65,26 @@ WA.room.onLeaveZone('myZone', () => { helloWorldPopup.close(); }); ``` + +### register additional menu entries + +adds an additional Entry to the main menu , these exist until the map is unloaded + + +```typescript +WA.ui.registerMenuCommand(menuCommand: string, callback: (menuCommand: string) => void): void +``` +Example: + + +```javascript + +WA.ui.registerMenuCommand("test", () => { + WA.chat.sendChatMessage("test clicked", "menu cmd") +}) + +``` + +
+ +
\ No newline at end of file diff --git a/docs/maps/assets/menu-command.png b/docs/maps/assets/menu-command.png new file mode 100644 index 00000000..0caf75c9 Binary files /dev/null and b/docs/maps/assets/menu-command.png differ diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index c3e2f6c9..7325f811 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -15,7 +15,8 @@ import type { LayerEvent } from './LayerEvent'; import type { SetPropertyEvent } from "./setPropertyEvent"; import type { LoadSoundEvent } from "./LoadSoundEvent"; import type { PlaySoundEvent } from "./PlaySoundEvent"; -import type { MenuItemClickedEvent } from "./MenuItemClickedEvent"; +import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent"; +import type { MenuItemRegisterEvent } from './ui/MenuItemRegisterEvent'; import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent"; @@ -47,7 +48,7 @@ export type IframeEventMap = { playSound: PlaySoundEvent stopSound: null, getState: undefined, - registerMenuCommand: undefined + registerMenuCommand: MenuItemRegisterEvent } export interface IframeEvent { type: T; diff --git a/front/src/Api/Events/MenuItemRegisterEvent.ts b/front/src/Api/Events/MenuItemRegisterEvent.ts deleted file mode 100644 index a25e5cc3..00000000 --- a/front/src/Api/Events/MenuItemRegisterEvent.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as tg from "generic-type-guard"; - -export const isMenuItemRegisterEvent = - new tg.IsInterface().withProperties({ - menutItem: tg.isString - }).get(); -/** - * A message sent from the iFrame to the game to add a new menu item. - */ -export type MenuItemRegisterEvent = tg.GuardedType; diff --git a/front/src/Api/Events/MenuItemClickedEvent.ts b/front/src/Api/Events/ui/MenuItemClickedEvent.ts similarity index 99% rename from front/src/Api/Events/MenuItemClickedEvent.ts rename to front/src/Api/Events/ui/MenuItemClickedEvent.ts index 0735eda4..fad2944f 100644 --- a/front/src/Api/Events/MenuItemClickedEvent.ts +++ b/front/src/Api/Events/ui/MenuItemClickedEvent.ts @@ -8,3 +8,5 @@ export const isMenuItemClickedEvent = * A message sent from the game to the iFrame when a menu item is clicked. */ export type MenuItemClickedEvent = tg.GuardedType; + + diff --git a/front/src/Api/Events/ui/MenuItemRegisterEvent.ts b/front/src/Api/Events/ui/MenuItemRegisterEvent.ts new file mode 100644 index 00000000..4a56d8a0 --- /dev/null +++ b/front/src/Api/Events/ui/MenuItemRegisterEvent.ts @@ -0,0 +1,25 @@ +import * as tg from "generic-type-guard"; +import { Subject } from 'rxjs'; + +export const isMenuItemRegisterEvent = + new tg.IsInterface().withProperties({ + menutItem: tg.isString + }).get(); +/** + * A message sent from the iFrame to the game to add a new menu item. + */ +export type MenuItemRegisterEvent = tg.GuardedType; + +export const isMenuItemRegisterIframeEvent = + new tg.IsInterface().withProperties({ + type: tg.isSingletonString("registerMenuCommand"), + data: isMenuItemRegisterEvent + }).get(); + + +const _registerMenuCommandStream: Subject = new Subject(); +export const registerMenuCommandStream = _registerMenuCommandStream.asObservable(); + +export function handleMenuItemRegistrationEvent(event: MenuItemRegisterEvent) { + _registerMenuCommandStream.next(event.menutItem) +} \ No newline at end of file diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index a495d92b..c4aa29b1 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -24,12 +24,12 @@ import {isStopSoundEvent, StopSoundEvent} from "./Events/StopSoundEvent"; import {isLoadSoundEvent, LoadSoundEvent} from "./Events/LoadSoundEvent"; import {isSetPropertyEvent, SetPropertyEvent} from "./Events/setPropertyEvent"; import {isLayerEvent, LayerEvent} from "./Events/LayerEvent"; -import {isMenuItemRegisterEvent} from "./Events/MenuItemRegisterEvent"; +import {isMenuItemRegisterEvent,} from "./Events/ui/MenuItemRegisterEvent"; import type {DataLayerEvent} from "./Events/DataLayerEvent"; import type {GameStateEvent} from "./Events/GameStateEvent"; -import type {MenuItemClickedEvent} from "./Events/MenuItemClickedEvent"; import type {HasPlayerMovedEvent} from "./Events/HasPlayerMovedEvent"; import {isLoadPageEvent} from "./Events/LoadPageEvent"; +import {handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent} from "./Events/ui/MenuItemRegisterEvent"; /** * Listens to messages from iframes and turn those messages into easy to use observables. @@ -115,21 +115,16 @@ class IframeListener { // Note: maybe we could restrict on the domain too for additional security (in case the iframe goes to another domain). let foundSrc: string | undefined; - foundSrc = [...this.scripts.keys()].find(key => { - return this.scripts.get(key)?.contentWindow == message.source - }); - - if (foundSrc === undefined) { - let iframe: HTMLIFrameElement; + let iframe: HTMLIFrameElement; for (iframe of this.iframes) { if (iframe.contentWindow === message.source) { foundSrc = iframe.src; - break;} + break; } + } - if (foundSrc === undefined) { - return; - } + if (foundSrc === undefined) { + return; } const payload = message.data; @@ -188,13 +183,13 @@ class IframeListener { this.sendPlayerMove = true } else if (payload.type == "getDataLayer") { this._dataLayerChangeStream.next(); - } else if (payload.type == "registerMenuCommand" && isMenuItemRegisterEvent(payload.data)) { + } else if (isMenuItemRegisterIframeEvent(payload)) { const data = payload.data.menutItem; // @ts-ignore this.iframeCloseCallbacks.get(iframe).push(() => { this._unregisterMenuCommandStream.next(data); }) - this._registerMenuCommandStream.next(payload.data.menutItem) + handleMenuItemRegistrationEvent(payload.data) } } }, false); @@ -295,15 +290,6 @@ class IframeListener { this.scripts.delete(scriptUrl); } - sendMenuClickedEvent(menuItem: string) { - this.postMessage({ - 'type': 'menuItemClicked', - 'data': { - menuItem: menuItem, - } as MenuItemClickedEvent - }); - } - sendUserInputChat(message: string) { this.postMessage({ 'type': 'userInputChat', @@ -353,7 +339,7 @@ class IframeListener { /** * Sends the message... to all allowed iframes. */ - private postMessage(message: IframeResponseEvent) { + public postMessage(message: IframeResponseEvent) { for (const iframe of this.iframes) { iframe.contentWindow?.postMessage(message, '*'); } diff --git a/front/src/Api/iframe/Ui/MenuItem.ts b/front/src/Api/iframe/Ui/MenuItem.ts new file mode 100644 index 00000000..9782ea7a --- /dev/null +++ b/front/src/Api/iframe/Ui/MenuItem.ts @@ -0,0 +1,11 @@ +import type { MenuItemClickedEvent } from '../../Events/ui/MenuItemClickedEvent'; +import { iframeListener } from '../../IframeListener'; + +export function sendMenuClickedEvent(menuItem: string) { + iframeListener.postMessage({ + 'type': 'menuItemClicked', + 'data': { + menuItem: menuItem, + } as MenuItemClickedEvent + }); +} \ No newline at end of file diff --git a/front/src/Api/iframe/ui.ts b/front/src/Api/iframe/ui.ts index 629d3c36..8e9943b2 100644 --- a/front/src/Api/iframe/ui.ts +++ b/front/src/Api/iframe/ui.ts @@ -1,14 +1,17 @@ import { isButtonClickedEvent } from '../Events/ButtonClickedEvent'; -import type { ClosePopupEvent } from '../Events/ClosePopupEvent'; +import { isMenuItemClickedEvent } from '../Events/ui/MenuItemClickedEvent'; +import type { MenuItemRegisterEvent } from '../Events/ui/MenuItemRegisterEvent'; import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; import { apiCallback } from "./registeredCallbacks"; -import {Popup} from "./Ui/Popup"; -import type {ButtonClickedCallback, ButtonDescriptor} from "./Ui/ButtonDescriptor"; +import type { ButtonClickedCallback, ButtonDescriptor } from "./Ui/ButtonDescriptor"; +import { Popup } from "./Ui/Popup"; let popupId = 0; const popups: Map = new Map(); const popupCallbacks: Map> = new Map>(); +const menuCallbacks: Map void> = new Map() + interface ZonedPopupOptions { zone: string objectLayerName?: string, @@ -33,6 +36,16 @@ class WorkAdventureUiCommands extends IframeApiContribution { + const callback = menuCallbacks.get(event.menuItem); + if (callback) { + callback(event.menuItem) + } + } })]; @@ -71,6 +84,16 @@ class WorkAdventureUiCommands extends IframeApiContribution void) { + menuCallbacks.set(commandDescriptor, callback); + sendToWorkadventure({ + 'type': 'registerMenuCommand', + 'data': { + menutItem: commandDescriptor + } as MenuItemRegisterEvent + }); + } + displayBubble(): void { sendToWorkadventure({ 'type': 'displayBubble', data: null }); } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index d21a125b..cc0ce1d3 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,4 +1,10 @@ -import {gameManager, HasMovedEvent} from "./GameManager"; +import { Queue } from 'queue-typescript'; +import type { Subscription } from "rxjs"; +import { ConsoleGlobalMessageManager } from "../../Administration/ConsoleGlobalMessageManager"; +import { GlobalMessageManager } from "../../Administration/GlobalMessageManager"; +import { userMessageManager } from "../../Administration/UserMessageManager"; +import { iframeListener } from "../../Api/IframeListener"; +import { connectionManager } from "../../Connexion/ConnectionManager"; import type { GroupCreatedUpdatedMessageInterface, MessageUserJoined, @@ -9,94 +15,84 @@ import type { PositionInterface, RoomJoinedMessageInterface } from "../../Connexion/ConnexionModels"; -import {hasMovedEventName, Player, requestEmoteEventName} from "../Player/Player"; +import { localUserStore } from "../../Connexion/LocalUserStore"; +import { Room } from "../../Connexion/Room"; +import type { RoomConnection } from "../../Connexion/RoomConnection"; +import { worldFullMessageStream } from "../../Connexion/WorldFullMessageStream"; import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, - POSITION_DELAY, + POSITION_DELAY } from "../../Enum/EnvironmentVariable"; -import type { - ITiledMap, - ITiledMapLayer, - ITiledMapLayerProperty, - ITiledMapObject, - ITiledMapTileLayer, - ITiledTileSet -} from "../Map/ITiledMap"; -import type { AddPlayerInterface } from "./AddPlayerInterface"; -import { PlayerAnimationDirections } from "../Player/Animation"; -import { PlayerMovement } from "./PlayerMovement"; -import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator"; -import { RemotePlayer } from "../Entity/RemotePlayer"; -import { Queue } from 'queue-typescript'; -import { SimplePeer, UserSimplePeerInterface } from "../../WebRtc/SimplePeer"; -import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene"; -import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager"; +import { TextureError } from "../../Exception/TextureError"; +import type { UserMovedMessage } from "../../Messages/generated/messages_pb"; +import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils"; +import { peerStore } from "../../Stores/PeerStore"; +import { touchScreenManager } from "../../Touch/TouchScreenManager"; +import { urlManager } from "../../Url/UrlManager"; +import { audioManager } from "../../WebRtc/AudioManager"; +import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; +import { HtmlUtils } from "../../WebRtc/HtmlUtils"; +import { jitsiFactory } from "../../WebRtc/JitsiFactory"; import { - CenterListener, + AUDIO_LOOP_PROPERTY, AUDIO_VOLUME_PROPERTY, CenterListener, JITSI_MESSAGE_PROPERTIES, layoutManager, LayoutMode, ON_ACTION_TRIGGER_BUTTON, TRIGGER_JITSI_PROPERTIES, TRIGGER_WEBSITE_PROPERTIES, - WEBSITE_MESSAGE_PROPERTIES, - AUDIO_VOLUME_PROPERTY, - AUDIO_LOOP_PROPERTY + WEBSITE_MESSAGE_PROPERTIES } from "../../WebRtc/LayoutManager"; -import { GameMap } from "./GameMap"; -import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; import { mediaManager } from "../../WebRtc/MediaManager"; -import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface"; -import type { ActionableItem } from "../Items/ActionableItem"; -import { UserInputManager } from "../UserInput/UserInputManager"; -import {soundManager} from "./SoundManager"; -import type { UserMovedMessage } from "../../Messages/generated/messages_pb"; -import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils"; -import { connectionManager } from "../../Connexion/ConnectionManager"; -import type { RoomConnection } from "../../Connexion/RoomConnection"; -import { GlobalMessageManager } from "../../Administration/GlobalMessageManager"; -import { userMessageManager } from "../../Administration/UserMessageManager"; -import { ConsoleGlobalMessageManager } from "../../Administration/ConsoleGlobalMessageManager"; -import { ResizableScene } from "../Login/ResizableScene"; -import { Room } from "../../Connexion/Room"; -import { jitsiFactory } from "../../WebRtc/JitsiFactory"; -import { urlManager } from "../../Url/UrlManager"; -import { audioManager } from "../../WebRtc/AudioManager"; -import { PresentationModeIcon } from "../Components/PresentationModeIcon"; +import { SimplePeer, UserSimplePeerInterface } from "../../WebRtc/SimplePeer"; +import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; import { ChatModeIcon } from "../Components/ChatModeIcon"; -import { OpenChatIcon, openChatIconName } from "../Components/OpenChatIcon"; -import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene"; -import { TextureError } from "../../Exception/TextureError"; import { addLoader } from "../Components/Loader"; +import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick"; +import { OpenChatIcon, openChatIconName } from "../Components/OpenChatIcon"; +import { PresentationModeIcon } from "../Components/PresentationModeIcon"; +import { TextUtils } from "../Components/TextUtils"; +import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager"; +import { RemotePlayer } from "../Entity/RemotePlayer"; +import type { ActionableItem } from "../Items/ActionableItem"; +import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface"; +import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene"; +import type { + ITiledMap, + ITiledMapLayer, + ITiledMapLayerProperty, + ITiledMapObject, + ITiledMapTileLayer, + ITiledTileSet } from "../Map/ITiledMap"; +import { MenuScene, MenuSceneName } from '../Menu/MenuScene'; +import { PlayerAnimationDirections } from "../Player/Animation"; +import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player"; import { ErrorSceneName } from "../Reconnecting/ErrorScene"; -import { localUserStore } from "../../Connexion/LocalUserStore"; -import { iframeListener } from "../../Api/IframeListener"; -import { HtmlUtils } from "../../WebRtc/HtmlUtils"; +import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene"; +import { waScaleManager } from "../Services/WaScaleManager"; +import { PinchManager } from "../UserInput/PinchManager"; +import { UserInputManager } from "../UserInput/UserInputManager"; +import type { AddPlayerInterface } from "./AddPlayerInterface"; +import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes"; +import { DirtyScene } from "./DirtyScene"; +import { EmoteManager } from "./EmoteManager"; +import { gameManager } from "./GameManager"; +import { GameMap } from "./GameMap"; +import { PlayerMovement } from "./PlayerMovement"; +import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator"; +import { soundManager } from "./SoundManager"; import Texture = Phaser.Textures.Texture; import Sprite = Phaser.GameObjects.Sprite; import CanvasTexture = Phaser.Textures.CanvasTexture; import GameObject = Phaser.GameObjects.GameObject; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import DOMElement = Phaser.GameObjects.DOMElement; -import EVENT_TYPE =Phaser.Scenes.Events -import type {Subscription} from "rxjs"; -import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream"; -import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; +import EVENT_TYPE = Phaser.Scenes.Events import RenderTexture = Phaser.GameObjects.RenderTexture; import Tilemap = Phaser.Tilemaps.Tilemap; -import { DirtyScene } from "./DirtyScene"; -import { TextUtils } from "../Components/TextUtils"; -import { touchScreenManager } from "../../Touch/TouchScreenManager"; -import { PinchManager } from "../UserInput/PinchManager"; -import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick"; -import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes"; -import { waScaleManager } from "../Services/WaScaleManager"; -import { peerStore} from "../../Stores/PeerStore"; -import {EmoteManager } from "./EmoteManager"; import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent'; -import { MenuScene, MenuSceneName } from '../Menu/MenuScene'; import AnimatedTiles from "phaser-animated-tiles"; @@ -192,7 +188,7 @@ export class GameScene extends DirtyScene implements CenterListener { private characterLayers!: string[]; private companion!: string | null; private messageSubscription: Subscription | null = null; - private popUpElements : Map = new Map(); + private popUpElements: Map = new Map(); private originalMapUrl: string | undefined; private pinchManager: PinchManager | undefined; private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time. @@ -899,37 +895,34 @@ ${escapedMessage} this.userInputManager.disableControls(); })); - this.iframeSubscriptionList.push(iframeListener.playSoundStream.subscribe((playSoundEvent)=> - { - const url = new URL(playSoundEvent.url, this.MapUrlFile); - soundManager.playSound(this.load,this.sound,url.toString(),playSoundEvent.config); - })) - - this.iframeSubscriptionList.push(iframeListener.stopSoundStream.subscribe((stopSoundEvent)=> - { - const url = new URL(stopSoundEvent.url, this.MapUrlFile); - soundManager.stopSound(this.sound,url.toString()); + this.iframeSubscriptionList.push(iframeListener.playSoundStream.subscribe((playSoundEvent) => { + const url = new URL(playSoundEvent.url, this.MapUrlFile); + soundManager.playSound(this.load, this.sound, url.toString(), playSoundEvent.config); })) - this.iframeSubscriptionList.push(iframeListener.loadSoundStream.subscribe((loadSoundEvent)=> - { + this.iframeSubscriptionList.push(iframeListener.stopSoundStream.subscribe((stopSoundEvent) => { + const url = new URL(stopSoundEvent.url, this.MapUrlFile); + soundManager.stopSound(this.sound, url.toString()); + })) + + this.iframeSubscriptionList.push(iframeListener.loadSoundStream.subscribe((loadSoundEvent) => { const url = new URL(loadSoundEvent.url, this.MapUrlFile); - soundManager.loadSound(this.load,this.sound,url.toString()); + soundManager.loadSound(this.load, this.sound, url.toString()); })) this.iframeSubscriptionList.push(iframeListener.enablePlayerControlStream.subscribe(() => { this.userInputManager.restoreControls(); })); - this.iframeSubscriptionList.push(iframeListener.loadPageStream.subscribe((url:string)=>{ - this.loadNextGame(url).then(()=>{ - this.events.once(EVENT_TYPE.POST_UPDATE,()=>{ + this.iframeSubscriptionList.push(iframeListener.loadPageStream.subscribe((url: string) => { + this.loadNextGame(url).then(() => { + this.events.once(EVENT_TYPE.POST_UPDATE, () => { this.onMapExit(url); }) }) })); - let scriptedBubbleSprite : Sprite; - this.iframeSubscriptionList.push(iframeListener.displayBubbleStream.subscribe(()=>{ - scriptedBubbleSprite = new Sprite(this,this.CurrentPlayer.x + 25,this.CurrentPlayer.y,'circleSprite-white'); + let scriptedBubbleSprite: Sprite; + this.iframeSubscriptionList.push(iframeListener.displayBubbleStream.subscribe(() => { + scriptedBubbleSprite = new Sprite(this, this.CurrentPlayer.x + 25, this.CurrentPlayer.y, 'circleSprite-white'); scriptedBubbleSprite.setDisplayOrigin(48, 48); this.add.existing(scriptedBubbleSprite); })); @@ -1000,8 +993,8 @@ ${escapedMessage} private onMapExit(exitKey: string) { if (this.mapTransitioning) return; this.mapTransitioning = true; - const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); - if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey); + const { roomId, hash } = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); + if (!roomId) throw new Error('Could not find the room from its exit key: ' + exitKey); urlManager.pushStartLayerNameToUrl(hash); const menuScene: MenuScene = this.scene.get(MenuSceneName) as MenuScene menuScene.reset() @@ -1159,7 +1152,7 @@ ${escapedMessage} private loadNextGame(exitSceneIdentifier: string) : Promise{ const { roomId, hash } = Room.getIdFromIdentifier(exitSceneIdentifier, this.MapUrlFile, this.instance); const room = new Room(roomId); - return gameManager.loadMap(room, this.scene).catch(() => {}); + return gameManager.loadMap(room, this.scene).catch(() => { }); } private startUser(layer: ITiledMapTileLayer): PositionInterface { diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index 8a01c259..15df4e5c 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -14,6 +14,8 @@ 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"; export const MenuSceneName = 'MenuScene'; const gameMenuKey = 'gameMenu'; @@ -42,12 +44,12 @@ export class MenuScene extends Phaser.Scene { private warningContainerTimeout: NodeJS.Timeout | null = null; private subscriptions = new Subscription() constructor() { - super({key: MenuSceneName}); + super({ key: MenuSceneName }); this.gameQualityValue = localUserStore.getGameQualityValue(); this.videoQualityValue = localUserStore.getVideoQualityValue(); - this.subscriptions.add(iframeListener.registerMenuCommandStream.subscribe(menuCommand => { + this.subscriptions.add(registerMenuCommandStream.subscribe(menuCommand => { this.addMenuOption(menuCommand); })) @@ -56,7 +58,25 @@ export class MenuScene extends Phaser.Scene { })) } - preload () { + 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 = `` + 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'); @@ -65,13 +85,6 @@ export class MenuScene extends Phaser.Scene { this.load.html(warningContainerKey, warningContainerHtml); } - reset() { - const addedMenuItems=[...this.menuElement.node.querySelectorAll(".fromApi")]; - for(let index=addedMenuItems.length-1;index>=0;index--){ - addedMenuItems[index].remove() - } - } - create() { menuIconVisible.set(true); this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey); @@ -86,11 +99,11 @@ export class MenuScene extends Phaser.Scene { this.gameShareElement = this.add.dom(middleX, -400).createFromCache(gameShare); MenuScene.revealMenusAfterInit(this.gameShareElement, gameShare); this.gameShareElement.addListener('click'); - this.gameShareElement.on('click', (event:MouseEvent) => { + this.gameShareElement.on('click', (event: MouseEvent) => { event.preventDefault(); - if((event?.target as HTMLInputElement).id === 'gameShareFormSubmit') { + if ((event?.target as HTMLInputElement).id === 'gameShareFormSubmit') { this.copyLink(); - }else if((event?.target as HTMLInputElement).id === 'gameShareFormCancel') { + } else if ((event?.target as HTMLInputElement).id === 'gameShareFormCancel') { this.closeGameShare(); } }); @@ -146,8 +159,8 @@ export class MenuScene extends Phaser.Scene { } //TODO bind with future metadata of card //if (connectionManager.getConnexionType === GameConnexionTypes.anonymous){ - const adminSection = this.menuElement.getChildByID('socialLinks') as HTMLElement; - adminSection.hidden = false; + const adminSection = this.menuElement.getChildByID('socialLinks') as HTMLElement; + adminSection.hidden = false; //} this.tweens.add({ targets: this.menuElement, @@ -197,28 +210,28 @@ export class MenuScene extends Phaser.Scene { this.settingsMenuOpened = true; const gameQualitySelect = this.gameQualityMenuElement.getChildByID('select-game-quality') as HTMLInputElement; - gameQualitySelect.value = ''+this.gameQualityValue; + gameQualitySelect.value = '' + this.gameQualityValue; const videoQualitySelect = this.gameQualityMenuElement.getChildByID('select-video-quality') as HTMLInputElement; - videoQualitySelect.value = ''+this.videoQualityValue; + videoQualitySelect.value = '' + this.videoQualityValue; this.gameQualityMenuElement.addListener('click'); - this.gameQualityMenuElement.on('click', (event:MouseEvent) => { + 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') { + } else if ((event?.target as HTMLInputElement).id === 'gameQualityFormCancel') { this.closeGameQualityMenu(); } }); - let middleY = this.scale.height / 2 - 392/2; - if(middleY < 0){ + let middleY = this.scale.height / 2 - 392 / 2; + if (middleY < 0) { middleY = 0; } - let middleX = this.scale.width / 2 - 457/2; - if(middleX < 0){ + let middleX = this.scale.width / 2 - 457 / 2; + if (middleX < 0) { middleX = 0; } this.tweens.add({ @@ -244,7 +257,7 @@ export class MenuScene extends Phaser.Scene { } - private openGameShare(): void{ + private openGameShare(): void { if (this.gameShareOpened) { this.closeGameShare(); return; @@ -258,11 +271,11 @@ export class MenuScene extends Phaser.Scene { this.gameShareOpened = true; let middleY = this.scale.height / 2 - 85; - if(middleY < 0){ + if (middleY < 0) { middleY = 0; } let middleX = this.scale.width / 2 - 200; - if(middleX < 0){ + if (middleX < 0) { middleX = 0; } this.tweens.add({ @@ -274,7 +287,7 @@ export class MenuScene extends Phaser.Scene { }); } - private closeGameShare(): void{ + private closeGameShare(): void { const gameShareInfo = this.gameShareElement.getChildByID('gameShareInfo') as HTMLParagraphElement; gameShareInfo.innerText = ''; gameShareInfo.style.display = 'none'; @@ -287,17 +300,6 @@ export class MenuScene extends Phaser.Scene { }); } - public addMenuOption(menuText: string) { - const wrappingSection = document.createElement("section") - const excapedHtml = HtmlUtils.escapeHtml(menuText); - wrappingSection.innerHTML = `` - const menuItemContainer = this.menuElement.node.querySelector("#gameMenu main"); - if (menuItemContainer) { - menuItemContainer.querySelector(`#${excapedHtml}.fromApi`)?.remove() - menuItemContainer.insertBefore(wrappingSection, menuItemContainer.querySelector("#socialLinks")) - } - } - private onMenuClick(event: MouseEvent) { const htmlMenuItem = (event?.target as HTMLInputElement); if (htmlMenuItem.classList.contains('not-button')) { @@ -306,11 +308,11 @@ export class MenuScene extends Phaser.Scene { event.preventDefault(); if (htmlMenuItem.classList.contains("fromApi")) { - iframeListener.sendMenuClickedEvent(htmlMenuItem.id) + sendMenuClickedEvent(htmlMenuItem.id) return } - switch (htmlMenuItem.id) { + switch ((event?.target as HTMLInputElement).id) { case 'changeNameButton': this.closeSideMenu(); gameManager.leaveGame(this, LoginSceneName, new LoginScene()); @@ -351,7 +353,7 @@ export class MenuScene extends Phaser.Scene { gameShareInfo.style.display = 'block'; } - private saveSetting(valueGame: number, valueVideo: number){ + private saveSetting(valueGame: number, valueVideo: number) { if (valueGame !== this.gameQualityValue) { this.gameQualityValue = valueGame; localUserStore.setGameQualityValue(valueGame); @@ -372,7 +374,7 @@ export class MenuScene extends Phaser.Scene { window.open(sparkHost, '_blank'); } - private closeAll(){ + private closeAll() { this.closeGameQualityMenu(); this.closeGameShare(); this.gameReportElement.close(); diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index e36199ba..7b6b2db9 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -1,4 +1,4 @@ -import {registeredCallbacks} from "./Api/iframe/registeredCallbacks"; +import { registeredCallbacks } from "./Api/iframe/registeredCallbacks"; import { IframeResponseEvent, IframeResponseEventMap, @@ -6,19 +6,16 @@ import { TypedMessageEvent } from "./Api/Events/IframeEvent"; import chat from "./Api/iframe/chat"; -import type {IframeCallback} from './Api/iframe/IframeApiContribution'; +import type { IframeCallback } from './Api/iframe/IframeApiContribution'; import nav from "./Api/iframe/nav"; import controls from "./Api/iframe/controls"; import ui from "./Api/iframe/ui"; import sound from "./Api/iframe/sound"; import room from "./Api/iframe/room"; -import type {ButtonDescriptor} from "./Api/iframe/Ui/ButtonDescriptor"; -import type {Popup} from "./Api/iframe/Ui/Popup"; -import type {Sound} from "./Api/iframe/Sound/Sound"; -import type {MenuItemRegisterEvent} from "./Api/Events/MenuItemRegisterEvent"; +import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor"; +import type { Popup } from "./Api/iframe/Ui/Popup"; +import type { Sound } from "./Api/iframe/Sound/Sound"; - -const menuCallbacks: Map void> = new Map() const wa = { ui, nav, @@ -80,7 +77,7 @@ const wa = { /** * @deprecated Use WA.sound.loadSound instead */ - loadSound(url: string) : Sound { + loadSound(url: string): Sound { console.warn('Method WA.loadSound is deprecated. Please use WA.sound.loadSound instead'); return sound.loadSound(url); }, @@ -88,7 +85,7 @@ const wa = { /** * @deprecated Use WA.nav.goToPage instead */ - goToPage(url : string) : void { + goToPage(url: string): void { console.warn('Method WA.goToPage is deprecated. Please use WA.nav.goToPage instead'); nav.goToPage(url); }, @@ -124,16 +121,6 @@ const wa = { console.warn('Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead'); return ui.openPopup(targetObject, message, buttons); }, - - registerMenuCommand(commandDescriptor: string, callback: (commandDescriptor: string) => void): void { - menuCallbacks.set(commandDescriptor, callback); - window.parent.postMessage({ - 'type': 'registerMenuCommand', - 'data': { - menutItem: commandDescriptor - } as MenuItemRegisterEvent - }, '*'); - }, /** * @deprecated Use WA.chat.onChatMessage instead */ diff --git a/maps/tests/Metadata/customMenu.html b/maps/tests/Metadata/customMenu.html index 59f579ba..a80dca08 100644 --- a/maps/tests/Metadata/customMenu.html +++ b/maps/tests/Metadata/customMenu.html @@ -5,11 +5,9 @@ \ No newline at end of file