Skip to content

Commit

Permalink
Add iframe submenu by scripting API
Browse files Browse the repository at this point in the history
Delete menu by scripting API
  • Loading branch information
GRL78 committed Aug 24, 2021
1 parent 49eb28d commit f3c4d34
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 328 deletions.
4 changes: 3 additions & 1 deletion front/src/Api/Events/IframeEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import type { SetPropertyEvent } from "./setPropertyEvent";
import type { LoadSoundEvent } from "./LoadSoundEvent";
import type { PlaySoundEvent } from "./PlaySoundEvent";
import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent";
import type { MenuItemRegisterEvent } from "./ui/MenuItemRegisterEvent";
import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent";
import type { SetTilesEvent } from "./SetTilesEvent";
import type { SetVariableEvent } from "./SetVariableEvent";
Expand All @@ -33,6 +32,7 @@ import type {
TriggerActionMessageEvent,
} from "./ui/TriggerActionMessageEvent";
import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent";
import type { MenuIframeRegisterEvent, MenuItemRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent";

export interface TypedMessageEvent<T> extends MessageEvent {
data: T;
Expand Down Expand Up @@ -64,6 +64,8 @@ export type IframeEventMap = {
getState: undefined;
loadTileset: LoadTilesetEvent;
registerMenuCommand: MenuItemRegisterEvent;
registerMenuIframe: MenuIframeRegisterEvent;
unregisterMenu: UnregisterMenuEvent;
setTiles: SetTilesEvent;
modifyEmbeddedWebsite: Partial<EmbeddedWebsite>; // Note: name should be compulsory in fact
};
Expand Down
2 changes: 1 addition & 1 deletion front/src/Api/Events/SetVariableEvent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as tg from "generic-type-guard";
import { isMenuItemRegisterEvent } from "./ui/MenuItemRegisterEvent";
import { isMenuItemRegisterEvent } from "./ui/MenuRegisterEvent";

export const isSetVariableEvent = new tg.IsInterface()
.withProperties({
Expand Down
27 changes: 0 additions & 27 deletions front/src/Api/Events/ui/MenuItemRegisterEvent.ts

This file was deleted.

42 changes: 42 additions & 0 deletions front/src/Api/Events/ui/MenuRegisterEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as tg from "generic-type-guard";

/**
* A message sent from a script to the game to add a new button in the menu.
*/
export const isMenuItemRegisterEvent = new tg.IsInterface()
.withProperties({
menuItem: tg.isString,
})
.get();

export type MenuItemRegisterEvent = tg.GuardedType<typeof isMenuItemRegisterEvent>;

export const isMenuItemRegisterIframeEvent = new tg.IsInterface()
.withProperties({
type: tg.isSingletonString("registerMenuCommand"),
data: isMenuItemRegisterEvent,
})
.get();

/**
* A message sent from a script to the game to add an iframe submenu in the menu.
*/
export const isMenuIframeEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
url: tg.isString,
})
.get();

export type MenuIframeRegisterEvent = tg.GuardedType<typeof isMenuIframeEvent>;

/**
* A message sent from a script to the game to remove a custom menu from the menu
*/
export const isUnregisterMenuEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
})
.get();

export type UnregisterMenuEvent = tg.GuardedType<typeof isUnregisterMenuEvent>;
19 changes: 14 additions & 5 deletions front/src/Api/IframeListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ import { isSetPropertyEvent, SetPropertyEvent } from "./Events/setPropertyEvent"
import { isLayerEvent, LayerEvent } from "./Events/LayerEvent";
import type { HasPlayerMovedEvent } from "./Events/HasPlayerMovedEvent";
import { isLoadPageEvent } from "./Events/LoadPageEvent";
import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from "./Events/ui/MenuItemRegisterEvent";
import { isMenuItemRegisterIframeEvent, isMenuIframeEvent, isUnregisterMenuEvent } from "./Events/ui/MenuRegisterEvent";
import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent";
import type { SetVariableEvent } from "./Events/SetVariableEvent";
import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent";
import { EmbeddedWebsite } from "./iframe/Room/EmbeddedWebsite";
import { subMenusStore } from "../Stores/MenuStore";
import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore";

type AnswererCallback<T extends keyof IframeQueryMap> = (
query: IframeQueryMap[T]["query"],
Expand Down Expand Up @@ -256,16 +256,25 @@ class IframeListener {
} else if (payload.type == "onPlayerMove") {
this.sendPlayerMove = true;
} else if (isMenuItemRegisterIframeEvent(payload)) {
const data = payload.data.menutItem;
const data = payload.data.menuItem;
// @ts-ignore
this.iframeCloseCallbacks.get(iframe).push(() => {
subMenusStore.removeMenu(data);
handleMenuUnregisterEvent(data);
});
handleMenuItemRegistrationEvent(payload.data);
handleMenuRegistrationEvent(payload.data.menuItem);
} else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) {
this._setTilesStream.next(payload.data);
} else if (payload.type == "modifyEmbeddedWebsite" && isEmbeddedWebsiteEvent(payload.data)) {
this._modifyEmbeddedWebsiteStream.next(payload.data);
} else if (payload.type == "registerMenuIframe" && isMenuIframeEvent(payload.data)) {
const data = payload.data.name;
// @ts-ignore
this.iframeCloseCallbacks.get(iframe).push(() => {
handleMenuUnregisterEvent(data);
});
handleMenuRegistrationEvent(payload.data.name, payload.data.url, foundSrc);
} else if (payload.type == "unregisterMenu" && isUnregisterMenuEvent(payload.data)) {
handleMenuUnregisterEvent(payload.data.name);
}
}
},
Expand Down
24 changes: 23 additions & 1 deletion front/src/Api/iframe/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,33 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
sendToWorkadventure({
type: "registerMenuCommand",
data: {
menutItem: commandDescriptor,
menuItem: commandDescriptor,
},
});
}

registerMenuIframe(menuName: string, iframeUrl: string) {
sendToWorkadventure({
type: "registerMenuIframe",
data: {
name: menuName,
url: iframeUrl,
},
});
}

unregisterMenu(menuName: string) {
sendToWorkadventure({
type: "unregisterMenu",
data: {
name: menuName,
},
});
if (menuCallbacks.get(menuName)) {
menuCallbacks.delete(menuName);
}
}

displayBubble(): void {
sendToWorkadventure({ type: "displayBubble", data: null });
}
Expand Down
16 changes: 16 additions & 0 deletions front/src/Components/Menu/CustomSubMenu.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script lang="ts">
export let iframe: string;
</script>


<iframe title="customSubMenu" src="{iframe}"></iframe>

<style lang="scss">
iframe {
border: none;
height: calc(100% - 56px);
width: 100%;
margin: 0;
}
</style>
18 changes: 13 additions & 5 deletions front/src/Components/Menu/Menu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
import AboutRoomSubMenu from "./AboutRoomSubMenu.svelte";
import GlobalMessageSubMenu from "./GlobalMessagesSubMenu.svelte";
import ContactSubMenu from "./ContactSubMenu.svelte";
import {menuVisiblilityStore, SubMenusInterface, subMenusStore} from "../../Stores/MenuStore";
import CustomSubMenu from "./CustomSubMenu.svelte"
import {customMenuIframe, menuVisiblilityStore, SubMenusInterface, subMenusStore} from "../../Stores/MenuStore";
import {userIsAdminStore} from "../../Stores/GameStore";
import {onMount} from "svelte";
import {get} from "svelte/store";
import {sendMenuClickedEvent} from "../../Api/iframe/Ui/MenuItem";
import {CONTACT_URL} from "../../Enum/EnvironmentVariable";
let activeSubMenu: string = SubMenusInterface.settings;
let activeComponent: typeof SettingsSubMenu = SettingsSubMenu;
let activeComponent: typeof SettingsSubMenu | typeof CustomSubMenu= SettingsSubMenu;
let props: {iframe: string};
onMount(() => {
if(!get(userIsAdminStore)) {
Expand Down Expand Up @@ -51,8 +53,14 @@
activeComponent = ContactSubMenu;
break;
default:
sendMenuClickedEvent(menu);
menuVisiblilityStore.set(false);
const customMenu = customMenuIframe.get(menu);
if (customMenu !== undefined) {
props = {iframe: customMenu};
activeComponent = CustomSubMenu;
} else {
sendMenuClickedEvent(menu);
menuVisiblilityStore.set(false);
}
break;
}
} else throw ("There is no menu called " + menu);
Expand Down Expand Up @@ -84,7 +92,7 @@
</div>
<div class="menu-submenu-container nes-container is-rounded" transition:fly="{{ y: -1000, duration: 500 }}">
<h2>{activeSubMenu}</h2>
<svelte:component this={activeComponent}/>
<svelte:component this={activeComponent} {...props}/>
</div>
</div>

Expand Down
34 changes: 33 additions & 1 deletion front/src/Stores/MenuStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { writable } from "svelte/store";
import { get, writable } from "svelte/store";
import Timeout = NodeJS.Timeout;

export const menuIconVisiblilityStore = writable(false);
Expand Down Expand Up @@ -66,3 +66,35 @@ function createSubMenusStore() {
}

export const subMenusStore = createSubMenusStore();

export const customMenuIframe = new Map<string, string>();

export function handleMenuRegistrationEvent(
menuName: string,
iframeUrl: string | undefined = undefined,
source: string | undefined = undefined
) {
if (get(subMenusStore).includes(menuName)) {
console.warn("The menu " + menuName + " already exist.");
return;
}

subMenusStore.addMenu(menuName);

if (iframeUrl !== undefined) {
//Add a subMenu from an iframe to the menu
const url = new URL(iframeUrl, source);
customMenuIframe.set(menuName, url.toString());
}
}

export function handleMenuUnregisterEvent(menuName: string) {
const subMenuGeneral: string[] = Object.values(SubMenusInterface);
if (subMenuGeneral.includes(menuName)) {
console.warn("The menu " + menuName + " is a mandatory menu. It can't be remove");
return;
}

subMenusStore.removeMenu(menuName);
customMenuIframe.delete(menuName);
}
32 changes: 15 additions & 17 deletions maps/tests/Metadata/customMenu.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<script>
var script = document.createElement('script');
// Don't do this at home kids! The "document.referrer" part is actually inserting a XSS security.
// We are OK in this precise case because the HTML page is hosted on the "maps" domain that contains only static files.
script.setAttribute('src', document.referrer + 'iframe_api.js');
document.head.appendChild(script);
window.addEventListener('load', () => {
WA.ui.registerMenuCommand("test", () => {
WA.chat.sendChatMessage("test clicked", "menu cmd")
})
})
</script>
</head>
<body>
<p>Add a custom menu</p>
</body>
<head>
<script>
var script = document.createElement('script');
// Don't do this at home kids! The "document.referrer" part is actually inserting a XSS security.
// We are OK in this precise case because the HTML page is hosted on the "maps" domain that contains only static files.
script.setAttribute('src', document.referrer + 'iframe_api.js');
document.head.appendChild(script);
window.addEventListener('load', () => {
WA.ui.registerMenuIframe('test', 'customIframeMenu.html');
})
</script>
</head>
<body>
<p>Add a custom menu</p>
</body>
</html>
Loading

0 comments on commit f3c4d34

Please sign in to comment.