typed iframe api events

# Conflicts:
#	front/src/Api/IframeListener.ts
#	front/src/iframe_api.ts
This commit is contained in:
jonny 2021-05-09 21:30:29 +02:00
parent 5605e63e5f
commit 8da5bf9f8e
3 changed files with 106 additions and 45 deletions

View File

@ -1,7 +1,59 @@
export interface IframeEvent {
type: string;
data: unknown; //import { GameStateEvent } from './ApiGameStateEvent';
//import { UpdateTileEvent } from './ApiUpdateTileEvent';
import { ButtonClickedEvent } from './ButtonClickedEvent';
import { ChatEvent } from './ChatEvent';
import { ClosePopupEvent } from './ClosePopupEvent';
import { EnterLeaveEvent } from './EnterLeaveEvent';
import { GoToPageEvent } from './GoToPageEvent';
import { LoadPageEvent } from './LoadPageEvent';
import { MenuItemClickedEvent } from './MenuItemClickedEvent';
import { MenuItemRegisterEvent } from './MenuItemRegisterEvent';
import { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
import { OpenPopupEvent } from './OpenPopupEvent';
import { OpenTabEvent } from './OpenTabEvent';
import { UserInputChatEvent } from './UserInputChatEvent';
export type IframeEventMap = {
//getState: GameStateEvent,
// updateTile: UpdateTileEvent
chat: ChatEvent,
openPopup: OpenPopupEvent
closePopup: ClosePopupEvent
openTab: OpenTabEvent
goToPage: GoToPageEvent
openCoWebSite: OpenCoWebSiteEvent
closeCoWebSite: null
disablePlayerControl: null
restorePlayerControl: null
displayBubble: null
removeBubble: null
registerMenuCommand: MenuItemRegisterEvent
loadPage: LoadPageEvent
}
export interface IframeEvent<T extends keyof IframeEventMap> {
type: T;
data: IframeEventMap[T];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isIframeEventWrapper = (event: any): event is IframeEvent<keyof IframeEventMap> => typeof event.type === 'string';
export interface IframeResponseEventMap {
menuItemClicked: MenuItemClickedEvent
userInputChat: UserInputChatEvent
enterEvent: EnterLeaveEvent
leaveEvent: EnterLeaveEvent
buttonClickedEvent: ButtonClickedEvent
// gameState: GameStateEvent
}
export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> {
type: T;
data: IframeResponseEventMap[T];
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isIframeEventWrapper = (event: any): event is IframeEvent => typeof event.type === 'string'; export const isIframeResponseEventWrapper = (event: { type?: string }): event is IframeResponseEvent<keyof IframeResponseEventMap> => typeof event.type === 'string';

View File

@ -12,6 +12,15 @@ import {ClosePopupEvent, isClosePopupEvent} from "./Events/ClosePopupEvent";
import {scriptUtils} from "./ScriptUtils"; import {scriptUtils} from "./ScriptUtils";
import {GoToPageEvent, isGoToPageEvent} from "./Events/GoToPageEvent"; import {GoToPageEvent, isGoToPageEvent} from "./Events/GoToPageEvent";
import {isOpenCoWebsite, OpenCoWebSiteEvent} from "./Events/OpenCoWebSiteEvent"; import {isOpenCoWebsite, OpenCoWebSiteEvent} from "./Events/OpenCoWebSiteEvent";
import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMap, isIframeEventWrapper } from "./Events/IframeEvent";
import { isLoadPageEvent } from './Events/LoadPageEvent';
import { MenuItemClickedEvent } from './Events/MenuItemClickedEvent';
import { isMenuItemRegisterEvent } from './Events/MenuItemRegisterEvent';
import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent";
import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent";
import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent";
import { UserInputChatEvent } from "./Events/UserInputChatEvent";
import { scriptUtils } from "./ScriptUtils";
/** /**
@ -56,7 +65,7 @@ class IframeListener {
private readonly scripts = new Map<string, HTMLIFrameElement>(); private readonly scripts = new Map<string, HTMLIFrameElement>();
init() { init() {
window.addEventListener("message", (message) => { window.addEventListener("message", (message: MessageEvent<IframeEvent<keyof IframeEventMap>>) => {
// Do we trust the sender of this message? // Do we trust the sender of this message?
// Let's only accept messages from the iframe that are allowed. // Let's only accept messages from the iframe that are allowed.
// Note: maybe we could restrict on the domain too for additional security (in case the iframe goes to another domain). // Note: maybe we could restrict on the domain too for additional security (in case the iframe goes to another domain).
@ -231,7 +240,7 @@ class IframeListener {
/** /**
* Sends the message... to all allowed iframes. * Sends the message... to all allowed iframes.
*/ */
private postMessage(message: IframeEvent) { private postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>) {
for (const iframe of this.iframes) { for (const iframe of this.iframes) {
iframe.contentWindow?.postMessage(message, '*'); iframe.contentWindow?.postMessage(message, '*');
} }

View File

@ -1,14 +1,14 @@
import {ChatEvent, isChatEvent} from "./Api/Events/ChatEvent"; import { ChatEvent } from "./Api/Events/ChatEvent";
import {isIframeEventWrapper} from "./Api/Events/IframeEvent"; import { isIframeResponseEventWrapper } from "./Api/Events/IframeEvent";
import {isUserInputChatEvent, UserInputChatEvent} from "./Api/Events/UserInputChatEvent"; import { isUserInputChatEvent, UserInputChatEvent } from "./Api/Events/UserInputChatEvent";
import {Subject} from "rxjs"; import { Subject } from "rxjs";
import {EnterLeaveEvent, isEnterLeaveEvent} from "./Api/Events/EnterLeaveEvent"; import { EnterLeaveEvent, isEnterLeaveEvent } from "./Api/Events/EnterLeaveEvent";
import {OpenPopupEvent} from "./Api/Events/OpenPopupEvent"; import { OpenPopupEvent } from "./Api/Events/OpenPopupEvent";
import {isButtonClickedEvent} from "./Api/Events/ButtonClickedEvent"; import { isButtonClickedEvent } from "./Api/Events/ButtonClickedEvent";
import {ClosePopupEvent} from "./Api/Events/ClosePopupEvent"; import { ClosePopupEvent } from "./Api/Events/ClosePopupEvent";
import {OpenTabEvent} from "./Api/Events/OpenTabEvent"; import { OpenTabEvent } from "./Api/Events/OpenTabEvent";
import {GoToPageEvent} from "./Api/Events/GoToPageEvent"; import { GoToPageEvent } from "./Api/Events/GoToPageEvent";
import {OpenCoWebSiteEvent} from "./Api/Events/OpenCoWebSiteEvent"; import { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent";
interface WorkAdventureApi { interface WorkAdventureApi {
sendChatMessage(message: string, author: string): void; sendChatMessage(message: string, author: string): void;
@ -20,10 +20,10 @@ interface WorkAdventureApi {
goToPage(url : string): void; goToPage(url : string): void;
openCoWebSite(url : string): void; openCoWebSite(url : string): void;
closeCoWebSite(): void; closeCoWebSite(): void;
disablePlayerControl() : void; disablePlayerControl(): void;
restorePlayerControl() : void; restorePlayerControl(): void;
displayBubble() : void; displayBubble(): void;
removeBubble() : void; removeBubble(): void;
} }
declare global { declare global {
@ -50,7 +50,7 @@ interface ButtonDescriptor {
/** /**
* The type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled" * The type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled"
*/ */
className?: "normal"|"primary"|"success"|"warning"|"error"|"disabled", className?: "normal" | "primary" | "success" | "warning" | "error" | "disabled",
/** /**
* Callback called if the button is pressed * Callback called if the button is pressed
*/ */
@ -88,38 +88,38 @@ window.WA = {
} as ChatEvent } as ChatEvent
}, '*'); }, '*');
}, },
disablePlayerControl() : void { disablePlayerControl(): void {
window.parent.postMessage({'type' : 'disablePlayerControl'},'*'); window.parent.postMessage({ 'type': 'disablePlayerControl' }, '*');
}, },
restorePlayerControl() : void { restorePlayerControl(): void {
window.parent.postMessage({'type' : 'restorePlayerControl'},'*'); window.parent.postMessage({ 'type': 'restorePlayerControl' }, '*');
}, },
displayBubble() : void { displayBubble(): void {
window.parent.postMessage({'type' : 'displayBubble'},'*'); window.parent.postMessage({ 'type': 'displayBubble' }, '*');
}, },
removeBubble() : void { removeBubble(): void {
window.parent.postMessage({'type' : 'removeBubble'},'*'); window.parent.postMessage({ 'type': 'removeBubble' }, '*');
}, },
openTab(url : string) : void{ openTab(url: string): void {
window.parent.postMessage({ window.parent.postMessage({
"type" : 'openTab', "type": 'openTab',
"data" : { "data": {
url url
} as OpenTabEvent } as OpenTabEvent
},'*'); }, '*');
}, },
goToPage(url : string) : void{ goToPage(url: string): void {
window.parent.postMessage({ window.parent.postMessage({
"type" : 'goToPage', "type": 'goToPage',
"data" : { "data": {
url url
} as GoToPageEvent } as GoToPageEvent
},'*'); }, '*');
}, },
openCoWebSite(url : string) : void{ openCoWebSite(url : string) : void{
@ -128,13 +128,13 @@ window.WA = {
"data" : { "data" : {
url url
} as OpenCoWebSiteEvent } as OpenCoWebSiteEvent
},'*'); }, '*');
}, },
closeCoWebSite() : void{ closeCoWebSite(): void {
window.parent.postMessage({ window.parent.postMessage({
"type" : 'closeCoWebSite' "type": 'closeCoWebSite'
},'*'); }, '*');
}, },
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup { openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup {
@ -205,9 +205,9 @@ window.addEventListener('message', message => {
const payload = message.data; const payload = message.data;
console.log(payload); console.debug(payload);
if (isIframeEventWrapper(payload)) { if (isIframeResponseEventWrapper(payload)) {
const payloadData = payload.data; const payloadData = payload.data;
if (payload.type === 'userInputChat' && isUserInputChatEvent(payloadData)) { if (payload.type === 'userInputChat' && isUserInputChatEvent(payloadData)) {
userInputChatStream.next(payloadData); userInputChatStream.next(payloadData);
@ -219,7 +219,7 @@ window.addEventListener('message', message => {
const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId); const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId);
const popup = popups.get(payloadData.popupId); const popup = popups.get(payloadData.popupId);
if (popup === undefined) { if (popup === undefined) {
throw new Error('Could not find popup with ID "'+payloadData.popupId+'"'); throw new Error('Could not find popup with ID "' + payloadData.popupId + '"');
} }
if (callback) { if (callback) {
callback(popup); callback(popup);