diff --git a/front/src/Api/iframe/chatmessage.ts b/front/src/Api/iframe/chatmessage.ts new file mode 100644 index 00000000..934fb1b1 --- /dev/null +++ b/front/src/Api/iframe/chatmessage.ts @@ -0,0 +1,35 @@ +import { ChatEvent } from '../Events/ChatEvent' +import { isUserInputChatEvent, UserInputChatEvent } from '../Events/UserInputChatEvent' +import { registerWorkadventureCommand, registerWorkadvntureCallback, sendToWorkadventure } from "./iframe-registration" + +let chatMessageCallback: (event: string) => void | undefined + +class WorkadvntureChatCommands { + + sendChatMessage(message: string, author: string) { + sendToWorkadventure({ + type: 'chat', + data: { + 'message': message, + 'author': author + } as ChatEvent + }) + } + + /** + * Listen to messages sent by the local user, in the chat. + */ + onChatMessage(callback: (message: string) => void) { + chatMessageCallback = callback + } +} + +export const commands = registerWorkadventureCommand(new WorkadvntureChatCommands()) +export const callbacks = registerWorkadvntureCallback([{ + callback: (event: UserInputChatEvent) => { + chatMessageCallback?.(event.message) + }, + type: "userInputChat", + typeChecker: isUserInputChatEvent +}]) + diff --git a/front/src/Api/iframe/iframe-registration.ts b/front/src/Api/iframe/iframe-registration.ts new file mode 100644 index 00000000..ceb6daf4 --- /dev/null +++ b/front/src/Api/iframe/iframe-registration.ts @@ -0,0 +1,30 @@ +import { IframeEvent, IframeEventMap, IframeResponseEventMap } from '../Events/IframeEvent'; +import { registeredCallbacks, WorkAdventureApi } from "../../iframe_api" +export function registerWorkadventureCommand(commnds: T): T { + const commandPrototype = Object.getPrototypeOf(commnds); + const commandClassPropertyNames = Object.getOwnPropertyNames(commandPrototype).filter(name => name !== "constructor"); + for (const key of commandClassPropertyNames) { + window.WA[key as keyof WorkAdventureApi] = commandPrototype[key] as never + } + return commnds +} + + +export function registerWorkadvntureCallback(callbacks: Array<{ + type: keyof IframeResponseEventMap, + typeChecker: Function, + callback: T +}>) { + for (const callback of callbacks) { + registeredCallbacks[callback.type] = { + typeChecker: callback.typeChecker, + callback: callback.callback + } + } + return callbacks +} + + +export function sendToWorkadventure(content: IframeEvent) { + window.parent.postMessage(content, "*") +} \ No newline at end of file diff --git a/front/src/Api/iframe/zone-events.ts b/front/src/Api/iframe/zone-events.ts new file mode 100644 index 00000000..11df18d9 --- /dev/null +++ b/front/src/Api/iframe/zone-events.ts @@ -0,0 +1,27 @@ +import { EnterLeaveEvent, isEnterLeaveEvent } from '../Events/EnterLeaveEvent' +import { registerWorkadventureCommand, registerWorkadvntureCallback, sendToWorkadventure } from "./iframe-registration" + +class WorkadventureZoneCommands { + + onEnterZone(name: string, callback: () => void): void { + + + } + onLeaveZone(name: string, callback: () => void): void { + + } + +} + + + + +export const commands = registerWorkadventureCommand(new WorkadventureZoneCommands()) +export const callbacks = registerWorkadvntureCallback([{ + callback: (enterEvent: EnterLeaveEvent) => { + + }, + type: "enterEvent", + typeChecker: isEnterLeaveEvent +},]) + diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index df37e53d..fbf3ec4f 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -1,5 +1,5 @@ import type { ChatEvent } from "./Api/Events/ChatEvent"; -import { isIframeResponseEventWrapper } from "./Api/Events/IframeEvent"; +import { IframeEvent, IframeEventMap, IframeResponseEventMap, isIframeResponseEventWrapper } from "./Api/Events/IframeEvent"; import { isUserInputChatEvent, UserInputChatEvent } from "./Api/Events/UserInputChatEvent"; import { Subject } from "rxjs"; import { EnterLeaveEvent, isEnterLeaveEvent } from "./Api/Events/EnterLeaveEvent"; @@ -10,9 +10,19 @@ import type { OpenTabEvent } from "./Api/Events/OpenTabEvent"; import type { GoToPageEvent } from "./Api/Events/GoToPageEvent"; import type { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent"; -interface WorkAdventureApi { - sendChatMessage(message: string, author: string): void; - onChatMessage(callback: (message: string) => void): void; +const importType = Promise.all([ + import("./Api/iframe/chatmessage"), + import("./Api/iframe/zone-events") +]) +type UnPromise

= P extends Promise ? T : P + +type WorkadventureCommandClasses = UnPromise[number]["commands"]; +type KeysOfUnion = T extends T ? keyof T : never +type ObjectWithKeyOfUnion = O extends O ? (Key extends keyof O ? O[Key] : never) : never + +type WorkAdventureApiFiles = { [Key in KeysOfUnion]: ObjectWithKeyOfUnion }; + +export interface WorkAdventureApi extends WorkAdventureApiFiles { onEnterZone(name: string, callback: () => void): void; onLeaveZone(name: string, callback: () => void): void; openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup; @@ -26,10 +36,15 @@ interface WorkAdventureApi { removeBubble(): void; } -declare global { - // eslint-disable-next-line no-var - var WA: WorkAdventureApi + + +declare global { + + interface Window { + WA: WorkAdventureApi + } + let WA: WorkAdventureApi } type ChatMessageCallback = (message: string) => void; @@ -172,14 +187,7 @@ window.WA = { popups.set(popupId, popup) return popup; }, - /** - * Listen to messages sent by the local user, in the chat. - */ - onChatMessage(callback: ChatMessageCallback): void { - userInputChatStream.subscribe((userInputChatEvent) => { - callback(userInputChatEvent.message); - }); - }, + ...({} as WorkAdventureApiFiles), onEnterZone(name: string, callback: () => void): void { let subject = enterStreams.get(name); if (subject === undefined) { @@ -196,6 +204,7 @@ window.WA = { } subject.subscribe(callback); }, + } window.addEventListener('message', message => { @@ -209,6 +218,12 @@ window.addEventListener('message', message => { if (isIframeResponseEventWrapper(payload)) { const payloadData = payload.data; + + if (registeredCallbacks[payload.type] && registeredCallbacks[payload.type]?.typeChecker(payloadData)) { + registeredCallbacks[payload.type]?.callback(payloadData) + return + } + if (payload.type === 'userInputChat' && isUserInputChatEvent(payloadData)) { userInputChatStream.next(payloadData); } else if (payload.type === 'enterEvent' && isEnterLeaveEvent(payloadData)) {