diff --git a/front/src/Api/Events/ChatEvent.ts b/front/src/Api/Events/ChatEvent.ts index 6242b871..5729a120 100644 --- a/front/src/Api/Events/ChatEvent.ts +++ b/front/src/Api/Events/ChatEvent.ts @@ -5,4 +5,7 @@ export const isChatEvent = message: tg.isString, author: tg.isString, }).get(); +/** + * A message sent from the iFrame to the game to add a message in the chat. + */ export type ChatEvent = tg.GuardedType; diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index fb63e7cf..65d2b443 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -1,4 +1,4 @@ -interface IframeEvent { +export interface IframeEvent { type: string; data: unknown; } diff --git a/front/src/Api/Events/UserInputChatEvent.ts b/front/src/Api/Events/UserInputChatEvent.ts new file mode 100644 index 00000000..de21ff6e --- /dev/null +++ b/front/src/Api/Events/UserInputChatEvent.ts @@ -0,0 +1,10 @@ +import * as tg from "generic-type-guard"; + +export const isUserInputChatEvent = + new tg.IsInterface().withProperties({ + message: tg.isString, + }).get(); +/** + * A message sent from the game to the iFrame when a user types a message in the chat. + */ +export type UserInputChatEvent = tg.GuardedType; diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 0e67433c..a94d294c 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -1,6 +1,7 @@ import {Subject} from "rxjs"; import {ChatEvent, isChatEvent} from "./Events/ChatEvent"; -import {isIframeEventWrapper} from "./Events/IframeEvent"; +import {IframeEvent, isIframeEventWrapper} from "./Events/IframeEvent"; +import {UserInputChatEvent} from "./Events/UserInputChatEvent"; @@ -12,15 +13,15 @@ class IframeListener { public readonly chatStream = this._chatStream.asObservable(); init() { - window.addEventListener("message", (event) => { + window.addEventListener("message", (message) => { // Do we trust the sender of this message? - //if (event.origin !== "http://example.com:8080") + //if (message.origin !== "http://example.com:8080") // return; - // event.source is window.opener - // event.data is the data sent by the iframe + // message.source is window.opener + // message.data is the data sent by the iframe - const payload = event.data; + const payload = message.data; if (isIframeEventWrapper(payload)) { if (payload.type === 'chat' && isChatEvent(payload.data)) { this._chatStream.next(payload.data); @@ -29,6 +30,27 @@ class IframeListener { }, false); + + + } + + sendUserInputChat(message: string) { + this.postMessage({ + 'type': 'userInputChat', + 'data': { + 'message': message, + } as UserInputChatEvent + }); + } + + /** + * Sends the message... to absolutely all the iFrames that can be found in the current document. + */ + private postMessage(message: IframeEvent) { + // TODO: not the most effecient implementation if there are many events sent! + for (const iframe of document.querySelectorAll('iframe')) { + iframe.contentWindow?.postMessage(message, '*'); + } } } diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts index 824bdfdc..e27cecf7 100644 --- a/front/src/WebRtc/DiscussionManager.ts +++ b/front/src/WebRtc/DiscussionManager.ts @@ -31,6 +31,9 @@ export class DiscussionManager { this.addMessage(chatEvent.author, chatEvent.message, false); this.showDiscussion(); }); + this.onSendMessageCallback('iframe_listener', (message) => { + iframeListener.sendUserInputChat(message); + }) } private createDiscussPart(name: string) { diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index 9f981d3a..1a442661 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -1,4 +1,7 @@ -import {ChatEvent} from "./Api/Events/ChatEvent"; +import {ChatEvent, isChatEvent} from "./Api/Events/ChatEvent"; +import {isIframeEventWrapper} from "./Api/Events/IframeEvent"; +import {isUserInputChatEvent, UserInputChatEvent} from "./Api/Events/UserInputChatEvent"; +import {Subject} from "rxjs"; interface WorkAdventureApi { sendChatMessage(message: string, author: string): void; @@ -10,6 +13,11 @@ declare global { var WA: WorkAdventureApi } +type ChatMessageCallback = (message: string) => void; + +const userInputChatStream: Subject = new Subject(); + + window.WA = { /** * Send a message in the chat. @@ -27,7 +35,25 @@ window.WA = { /** * Listen to messages sent by the local user, in the chat. */ - onChatMessage(callback: (message: string) => void): void { - + onChatMessage(callback: ChatMessageCallback): void { + userInputChatStream.subscribe((userInputChatEvent) => { + callback(userInputChatEvent.message); + }); } } + +window.addEventListener('message', message => { + if (message.source !== window.parent) { + console.log('MESSAGE SKIPPED!!!') + return; // Skip message in this event listener + } + + const payload = message.data; + if (isIframeEventWrapper(payload)) { + if (payload.type === 'userInputChat' && isUserInputChatEvent(payload.data)) { + userInputChatStream.next(payload.data); + } + } + + // ... +}); diff --git a/maps/tests/iframe.html b/maps/tests/iframe.html index fd430e18..23bfb479 100644 --- a/maps/tests/iframe.html +++ b/maps/tests/iframe.html @@ -13,5 +13,13 @@ WA.sendChatMessage('Hello world!', 'Mr Robot'); } +
+