From 34dc5a0bc6e041d083f6c98e55e31e3732090f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 18 Jun 2021 17:22:56 +0200 Subject: [PATCH] Refactoring WorkAdventureAPI. Simplifying a lot what was done (even if the new code is a bit less automated, it allows to list exactly the methods we want to see deprecated and to add a notification message) --- front/src/Api/iframe/Bubble.ts | 20 -- front/src/Api/iframe/CoWebsite.ts | 29 --- front/src/Api/iframe/IframeApiContribution.ts | 19 +- .../Api/iframe/{chatmessage.ts => chat.ts} | 8 +- .../src/Api/iframe/{Player.ts => controls.ts} | 10 +- .../src/Api/iframe/{Navigation.ts => nav.ts} | 21 +- front/src/Api/iframe/registeredCallbacks.ts | 0 .../Api/iframe/{zone-events.ts => room.ts} | 8 +- front/src/Api/iframe/{Sound.ts => sound.ts} | 0 front/src/Api/iframe/{popup.ts => ui.ts} | 16 +- front/src/iframe_api.d.ts | 25 --- front/src/iframe_api.ts | 179 ++++++++++++++---- front/src/registered_callbacks.ts | 4 + 13 files changed, 186 insertions(+), 153 deletions(-) delete mode 100644 front/src/Api/iframe/Bubble.ts delete mode 100644 front/src/Api/iframe/CoWebsite.ts rename front/src/Api/iframe/{chatmessage.ts => chat.ts} (81%) rename front/src/Api/iframe/{Player.ts => controls.ts} (59%) rename front/src/Api/iframe/{Navigation.ts => nav.ts} (69%) create mode 100644 front/src/Api/iframe/registeredCallbacks.ts rename front/src/Api/iframe/{zone-events.ts => room.ts} (87%) rename front/src/Api/iframe/{Sound.ts => sound.ts} (100%) rename front/src/Api/iframe/{popup.ts => ui.ts} (88%) delete mode 100644 front/src/iframe_api.d.ts create mode 100644 front/src/registered_callbacks.ts diff --git a/front/src/Api/iframe/Bubble.ts b/front/src/Api/iframe/Bubble.ts deleted file mode 100644 index b0f070cb..00000000 --- a/front/src/Api/iframe/Bubble.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { IframeApiContribution } from './IframeApiContribution'; - -class WorkadventureNavigationCommands extends IframeApiContribution { - - readonly subObjectIdentifier = "bubble" - - readonly addMethodsAtRoot = true - callbacks = [] - displayBubble(): void { - window.parent.postMessage({ 'type': 'displayBubble' }, '*'); - } - - removeBubble(): void { - window.parent.postMessage({ 'type': 'removeBubble' }, '*'); - } - -} - - -export default new WorkadventureNavigationCommands(); \ No newline at end of file diff --git a/front/src/Api/iframe/CoWebsite.ts b/front/src/Api/iframe/CoWebsite.ts deleted file mode 100644 index 346c1589..00000000 --- a/front/src/Api/iframe/CoWebsite.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { OpenCoWebSiteEvent } from '../Events/OpenCoWebSiteEvent'; -import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; - -class WorkadventureCoWebsiteCommands extends IframeApiContribution { - - readonly subObjectIdentifier = "cowebsite" - - readonly addMethodsAtRoot = true - callbacks = [] - - openCoWebSite(url: string): void { - sendToWorkadventure({ - "type": 'openCoWebSite', - "data": { - url - } as OpenCoWebSiteEvent - }); - } - - closeCoWebSite(): void { - sendToWorkadventure({ - "type": 'closeCoWebSite', - data: null - }); - } -} - - -export default new WorkadventureCoWebsiteCommands(); \ No newline at end of file diff --git a/front/src/Api/iframe/IframeApiContribution.ts b/front/src/Api/iframe/IframeApiContribution.ts index 77739f7e..9b5aba33 100644 --- a/front/src/Api/iframe/IframeApiContribution.ts +++ b/front/src/Api/iframe/IframeApiContribution.ts @@ -1,6 +1,6 @@ import type * as tg from "generic-type-guard"; -import { registeredCallbacks } from '../../iframe_api'; import type { IframeEvent, IframeEventMap, IframeResponseEventMap } from '../Events/IframeEvent'; +import {registeredCallbacks} from "../../registered_callbacks"; @@ -31,26 +31,15 @@ export interface IframeCallbackContribution>, - readonly subObjectIdentifier: PossibleSubobjects, - readonly addMethodsAtRoot: boolean | undefined }> { abstract callbacks: T["callbacks"] - - /** - * @deprecated this is only there for backwards compatibility on new apis this should be set to false or ignored - */ - addMethodsAtRoot = false - - abstract readonly subObjectIdentifier: T["subObjectIdentifier"] - -} \ No newline at end of file +} diff --git a/front/src/Api/iframe/chatmessage.ts b/front/src/Api/iframe/chat.ts similarity index 81% rename from front/src/Api/iframe/chatmessage.ts rename to front/src/Api/iframe/chat.ts index 80b549dd..c6fec2ad 100644 --- a/front/src/Api/iframe/chatmessage.ts +++ b/front/src/Api/iframe/chat.ts @@ -3,11 +3,7 @@ import { isUserInputChatEvent, UserInputChatEvent } from '../Events/UserInputCha import { apiCallback, IframeApiContribution, sendToWorkadventure } from './IframeApiContribution' -class WorkadvntureChatCommands extends IframeApiContribution { - readonly subObjectIdentifier = 'chat' - - readonly addMethodsAtRoot = true - +class WorkadventureChatCommands extends IframeApiContribution { chatMessageCallback?: (event: string) => void callbacks = [apiCallback({ @@ -37,4 +33,4 @@ class WorkadvntureChatCommands extends IframeApiContribution { - - readonly subObjectIdentifier = "player" - - readonly addMethodsAtRoot = true +class WorkadventureControlsCommands extends IframeApiContribution { callbacks = [] disablePlayerControls(): void { @@ -14,9 +10,7 @@ class WorkadventureNavigationCommands extends IframeApiContribution { - - readonly subObjectIdentifier = "nav" - - readonly addMethodsAtRoot = true callbacks = [] @@ -37,6 +34,22 @@ class WorkadventureNavigationCommands extends IframeApiContribution> = new Map>(); const leaveStreams: Map> = new Map>(); -class WorkadventureZoneCommands extends IframeApiContribution { - - readonly subObjectIdentifier = "zone" - - readonly addMethodsAtRoot = true +class WorkadventureRoomCommands extends IframeApiContribution { callbacks = [ apiCallback({ callback: (payloadData: EnterLeaveEvent) => { @@ -51,4 +47,4 @@ class WorkadventureZoneCommands extends IframeApiContribution void; -interface ButtonDescriptor { +export interface ButtonDescriptor { /** * The label of the button */ @@ -46,7 +46,7 @@ interface ZonedPopupOptions { } -class PopupApiContribution extends IframeApiContribution { +class WorkAdventureUiCommands extends IframeApiContribution { readonly subObjectIdentifier = "ui" @@ -101,6 +101,14 @@ class PopupApiContribution extends IframeApiContribution { popups.set(popupId, popup) return popup; } + + displayBubble(): void { + window.parent.postMessage({ 'type': 'displayBubble' }, '*'); + } + + removeBubble(): void { + window.parent.postMessage({ 'type': 'removeBubble' }, '*'); + } } -export default new PopupApiContribution() \ No newline at end of file +export default new WorkAdventureUiCommands() diff --git a/front/src/iframe_api.d.ts b/front/src/iframe_api.d.ts deleted file mode 100644 index 944fdd6b..00000000 --- a/front/src/iframe_api.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { WorkadventureImport } from './iframe_api'; - -type PromiseReturnType

= P extends Promise ? T : P; -type WorkadventureCommandClasses = PromiseReturnType[number]; -type KeysOfUnion = T extends T ? keyof T : never; -type ObjectWithKeyOfUnion = O extends O ? (Key extends keyof O ? O[Key] : never) : never; -type ApiKeys = KeysOfUnion; -type ObjectOfKey = O extends O ? (Key extends keyof O ? O : never) : never; -type ShouldAddAttribute = ObjectWithKeyOfUnion; -type WorkadventureFunctions = { - [K in ApiKeys]: ObjectWithKeyOfUnion extends Function ? K : never; -}[ApiKeys]; -type WorkadventureFunctionsFilteredByRoot = { - [K in WorkadventureFunctions]: ObjectOfKey["addMethodsAtRoot"] extends true ? K : never; -}[WorkadventureFunctions]; -type JustMethodKeys = ({ - [P in keyof T]: T[P] extends Function ? P : never; -})[keyof T]; -type JustMethods = Pick>; -type SubObjectTypes = { - [importCl in WorkadventureCommandClasses as importCl["subObjectIdentifier"]]: JustMethods; -}; -export type WorkAdventureApi = { - [Key in WorkadventureFunctionsFilteredByRoot]: ShouldAddAttribute; -} & SubObjectTypes; diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index 59f5d23a..b3279766 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -1,24 +1,148 @@ -import { IframeResponseEvent, IframeResponseEventMap, isIframeResponseEventWrapper, TypedMessageEvent } from "./Api/Events/IframeEvent"; -import Bubble from "./Api/iframe/Bubble"; -import chatmessage from "./Api/iframe/chatmessage"; -import CoWebsite from "./Api/iframe/CoWebsite"; -import type { IframeCallback } from './Api/iframe/IframeApiContribution'; -import Navigation from "./Api/iframe/Navigation"; -import Player from "./Api/iframe/Player"; -import popupApi from "./Api/iframe/popup"; -import Sound from "./Api/iframe/Sound"; -import zoneRvents from "./Api/iframe/zone-events"; -import type { WorkAdventureApi } from './iframe_api.d'; -import type { LoadPageEvent } from './Api/Events/LoadPageEvent'; +import {registeredCallbacks} from "./registered_callbacks"; +import { + IframeResponseEvent, + IframeResponseEventMap, + isIframeResponseEventWrapper, + TypedMessageEvent +} from "./Api/Events/IframeEvent"; +import chat from "./Api/iframe/chat"; +import type {IframeCallback} from './Api/iframe/IframeApiContribution'; +import nav from "./Api/iframe/nav"; +import controls from "./Api/iframe/controls"; +import ui, {ButtonDescriptor, Popup} from "./Api/iframe/ui"; +import sound, {Sound} from "./Api/iframe/sound"; +import room from "./Api/iframe/room"; -export const registeredCallbacks: { [K in keyof IframeResponseEventMap]?: IframeCallback } = {} -const apis = [ - popupApi, Navigation, Player, Bubble, - chatmessage, Sound, zoneRvents, CoWebsite -] +const wa = { + ui, + nav, + controls, + chat, + sound, + room, -export type WorkadventureImport = typeof apis + // All methods below are deprecated and should not be used anymore. + // They are kept here for backward compatibility. + + /** + * @deprecated Use WA.chat.sendChatMessage instead + */ + sendChatMessage(message: string, author: string): void { + console.log('Method WA.sendChatMessage is deprecated. Please use WA.chat.sendChatMessage instead'); + chat.sendChatMessage(message, author); + }, + /** + * @deprecated Use WA.chat.disablePlayerControls instead + */ + disablePlayerControls(): void { + console.log('Method WA.disablePlayerControls is deprecated. Please use WA.controls.disablePlayerControls instead'); + controls.disablePlayerControls(); + }, + + /** + * @deprecated Use WA.controls.restorePlayerControls instead + */ + restorePlayerControls(): void { + console.log('Method WA.restorePlayerControls is deprecated. Please use WA.controls.restorePlayerControls instead'); + controls.restorePlayerControls(); + }, + + /** + * @deprecated Use WA.ui.displayBubble instead + */ + displayBubble(): void { + console.log('Method WA.displayBubble is deprecated. Please use WA.ui.displayBubble instead'); + ui.displayBubble(); + }, + + /** + * @deprecated Use WA.ui.removeBubble instead + */ + removeBubble(): void { + console.log('Method WA.removeBubble is deprecated. Please use WA.ui.removeBubble instead'); + ui.removeBubble(); + }, + + /** + * @deprecated Use WA.nav.openTab instead + */ + openTab(url: string): void { + console.log('Method WA.openTab is deprecated. Please use WA.nav.openTab instead'); + nav.openTab(url); + }, + + /** + * @deprecated Use WA.sound.loadSound instead + */ + loadSound(url: string) : Sound { + console.log('Method WA.loadSound is deprecated. Please use WA.sound.loadSound instead'); + return sound.loadSound(url); + }, + + /** + * @deprecated Use WA.nav.goToPage instead + */ + goToPage(url : string) : void { + console.log('Method WA.goToPage is deprecated. Please use WA.nav.goToPage instead'); + nav.goToPage(url); + }, + + /** + * @deprecated Use WA.nav.goToRoom instead + */ + goToRoom(url: string): void { + console.log('Method WA.goToRoom is deprecated. Please use WA.nav.goToRoom instead'); + nav.goToRoom(url); + }, + + /** + * @deprecated Use WA.nav.openCoWebSite instead + */ + openCoWebSite(url : string) : void{ + console.log('Method WA.openCoWebSite is deprecated. Please use WA.nav.openCoWebSite instead'); + nav.openCoWebSite(url); + }, + + /** + * @deprecated Use WA.nav.closeCoWebSite instead + */ + closeCoWebSite(): void { + console.log('Method WA.closeCoWebSite is deprecated. Please use WA.nav.closeCoWebSite instead'); + nav.closeCoWebSite(); + }, + + /** + * @deprecated Use WA.controls.restorePlayerControls instead + */ + openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup { + console.log('Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead'); + return ui.openPopup(targetObject, message, buttons); + }, + /** + * @deprecated Use WA.chat.onChatMessage instead + */ + onChatMessage(callback: (message: string) => void): void { + console.log('Method WA.onChatMessage is deprecated. Please use WA.chat.onChatMessage instead'); + chat.onChatMessage(callback); + }, + /** + * @deprecated Use WA.room.onEnterZone instead + */ + onEnterZone(name: string, callback: () => void): void { + console.log('Method WA.onEnterZone is deprecated. Please use WA.room.onEnterZone instead'); + room.onEnterZone(name, callback); + }, + /** + * @deprecated Use WA.room.onLeaveZone instead + */ + onLeaveZone(name: string, callback: () => void): void { + console.log('Method WA.onLeaveZone is deprecated. Please use WA.room.onLeaveZone instead'); + room.onLeaveZone(name, callback); + }, +}; + +export type WorkAdventureApi = typeof wa; declare global { @@ -28,24 +152,7 @@ declare global { let WA: WorkAdventureApi } -const wa: Partial = {} -for (const apiImport of apis) { - const commandPrototype = Object.getPrototypeOf(apiImport); - const commandClassPropertyNames = Object.getOwnPropertyNames(commandPrototype).filter(name => name !== "constructor"); - const importObject: Partial = {} - for (const prop of commandClassPropertyNames) { - const apiImportKey = prop as keyof typeof apiImport; - if (typeof apiImport[apiImportKey] === "function") { - importObject[apiImportKey as keyof WorkAdventureApi] = commandPrototype[apiImportKey] as never - } - } - wa[apiImport.subObjectIdentifier] = importObject as never - if (apiImport.addMethodsAtRoot) { - Object.assign(wa, importObject) - } -} - -window.WA = Object.assign({}, wa) as WorkAdventureApi +window.WA = wa; window.addEventListener('message', (message: TypedMessageEvent>) => { if (message.source !== window.parent) { diff --git a/front/src/registered_callbacks.ts b/front/src/registered_callbacks.ts new file mode 100644 index 00000000..cf941a13 --- /dev/null +++ b/front/src/registered_callbacks.ts @@ -0,0 +1,4 @@ +import type {IframeResponseEventMap} from "./Api/Events/IframeEvent"; +import type {IframeCallback} from "./Api/iframe/IframeApiContribution"; + +export const registeredCallbacks: { [K in keyof IframeResponseEventMap]?: IframeCallback } = {}