Merge remote-tracking branch 'remotes/upstream/develop' into trigger-message-refv3
This commit is contained in:
commit
369d453455
202 changed files with 15971 additions and 4927 deletions
|
@ -1,13 +1,12 @@
|
|||
import * as tg from "generic-type-guard";
|
||||
|
||||
|
||||
|
||||
export const isDataLayerEvent =
|
||||
new tg.IsInterface().withProperties({
|
||||
data: tg.isObject
|
||||
}).get();
|
||||
export const isDataLayerEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
data: tg.isObject,
|
||||
})
|
||||
.get();
|
||||
|
||||
/**
|
||||
* A message sent from the game to the iFrame when the data of the layers change after the iFrame send a message to the game that it want to listen to the data of the layers
|
||||
*/
|
||||
export type DataLayerEvent = tg.GuardedType<typeof isDataLayerEvent>;
|
||||
export type DataLayerEvent = tg.GuardedType<typeof isDataLayerEvent>;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import * as tg from "generic-type-guard";
|
||||
|
||||
export const isGameStateEvent =
|
||||
new tg.IsInterface().withProperties({
|
||||
roomId: tg.isString,
|
||||
mapUrl: tg.isString,
|
||||
nickname: tg.isUnion(tg.isString, tg.isNull),
|
||||
uuid: tg.isUnion(tg.isString, tg.isUndefined),
|
||||
startLayerName: tg.isUnion(tg.isString, tg.isNull),
|
||||
tags : tg.isArray(tg.isString),
|
||||
}).get();
|
||||
export const isGameStateEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
roomId: tg.isString,
|
||||
mapUrl: tg.isString,
|
||||
nickname: tg.isUnion(tg.isString, tg.isNull),
|
||||
uuid: tg.isUnion(tg.isString, tg.isUndefined),
|
||||
startLayerName: tg.isUnion(tg.isString, tg.isNull),
|
||||
tags: tg.isArray(tg.isString),
|
||||
})
|
||||
.get();
|
||||
/**
|
||||
* A message sent from the game to the iFrame when the gameState is received by the script
|
||||
*/
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import * as tg from "generic-type-guard";
|
||||
|
||||
|
||||
|
||||
export const isHasPlayerMovedEvent =
|
||||
new tg.IsInterface().withProperties({
|
||||
direction: tg.isElementOf('right', 'left', 'up', 'down'),
|
||||
export const isHasPlayerMovedEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
direction: tg.isElementOf("right", "left", "up", "down"),
|
||||
moving: tg.isBoolean,
|
||||
x: tg.isNumber,
|
||||
y: tg.isNumber
|
||||
}).get();
|
||||
y: tg.isNumber,
|
||||
})
|
||||
.get();
|
||||
|
||||
/**
|
||||
* A message sent from the game to the iFrame to notify a movement from the current player.
|
||||
*/
|
||||
export type HasPlayerMovedEvent = tg.GuardedType<typeof isHasPlayerMovedEvent>;
|
||||
|
||||
|
||||
export type HasPlayerMovedEventCallback = (event: HasPlayerMovedEvent) => void
|
||||
export type HasPlayerMovedEventCallback = (event: HasPlayerMovedEvent) => void;
|
||||
|
|
|
@ -1,78 +1,83 @@
|
|||
|
||||
import type { GameStateEvent } from './GameStateEvent';
|
||||
import type { ButtonClickedEvent } from './ButtonClickedEvent';
|
||||
import type { ChatEvent } from './ChatEvent';
|
||||
import type { ClosePopupEvent } from './ClosePopupEvent';
|
||||
import type { EnterLeaveEvent } from './EnterLeaveEvent';
|
||||
import type { GoToPageEvent } from './GoToPageEvent';
|
||||
import type { LoadPageEvent } from './LoadPageEvent';
|
||||
import type { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
|
||||
import type { OpenPopupEvent } from './OpenPopupEvent';
|
||||
import type { OpenTabEvent } from './OpenTabEvent';
|
||||
import type { UserInputChatEvent } from './UserInputChatEvent';
|
||||
import type { GameStateEvent } from "./GameStateEvent";
|
||||
import type { ButtonClickedEvent } from "./ButtonClickedEvent";
|
||||
import type { ChatEvent } from "./ChatEvent";
|
||||
import type { ClosePopupEvent } from "./ClosePopupEvent";
|
||||
import type { EnterLeaveEvent } from "./EnterLeaveEvent";
|
||||
import type { GoToPageEvent } from "./GoToPageEvent";
|
||||
import type { LoadPageEvent } from "./LoadPageEvent";
|
||||
import type { OpenCoWebSiteEvent } from "./OpenCoWebSiteEvent";
|
||||
import type { OpenPopupEvent } from "./OpenPopupEvent";
|
||||
import type { OpenTabEvent } from "./OpenTabEvent";
|
||||
import type { UserInputChatEvent } from "./UserInputChatEvent";
|
||||
import type { DataLayerEvent } from "./DataLayerEvent";
|
||||
import type { LayerEvent } from './LayerEvent';
|
||||
import type { LayerEvent } from "./LayerEvent";
|
||||
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 { MenuItemRegisterEvent } from "./ui/MenuItemRegisterEvent";
|
||||
import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent";
|
||||
import type { MessageReferenceEvent, TriggerMessageEvent } from '../iframe/TriggerMessageEvent';
|
||||
|
||||
import type { SetTilesEvent } from "./SetTilesEvent";
|
||||
import type {
|
||||
MessageReferenceEvent,
|
||||
removeTriggerMessage,
|
||||
triggerMessage,
|
||||
TriggerMessageEvent,
|
||||
} from "./ui/TriggerMessageEvent";
|
||||
|
||||
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||
data: T
|
||||
data: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* List event types sent from an iFrame to WorkAdventure
|
||||
*/
|
||||
export type IframeEventMap = {
|
||||
//getState: GameStateEvent,
|
||||
// updateTile: UpdateTileEvent
|
||||
loadPage: LoadPageEvent
|
||||
chat: ChatEvent,
|
||||
openPopup: OpenPopupEvent
|
||||
closePopup: ClosePopupEvent
|
||||
openTab: OpenTabEvent
|
||||
goToPage: GoToPageEvent
|
||||
openCoWebSite: OpenCoWebSiteEvent
|
||||
closeCoWebSite: null
|
||||
disablePlayerControls: null
|
||||
restorePlayerControls: null
|
||||
displayBubble: null
|
||||
removeBubble: null
|
||||
onPlayerMove: undefined
|
||||
showLayer: LayerEvent
|
||||
hideLayer: LayerEvent
|
||||
setProperty: SetPropertyEvent
|
||||
getDataLayer: undefined
|
||||
loadSound: LoadSoundEvent
|
||||
playSound: PlaySoundEvent
|
||||
stopSound: null,
|
||||
getState: undefined,
|
||||
registerMenuCommand: MenuItemRegisterEvent
|
||||
loadPage: LoadPageEvent;
|
||||
chat: ChatEvent;
|
||||
openPopup: OpenPopupEvent;
|
||||
closePopup: ClosePopupEvent;
|
||||
openTab: OpenTabEvent;
|
||||
goToPage: GoToPageEvent;
|
||||
openCoWebSite: OpenCoWebSiteEvent;
|
||||
closeCoWebSite: null;
|
||||
disablePlayerControls: null;
|
||||
restorePlayerControls: null;
|
||||
displayBubble: null;
|
||||
removeBubble: null;
|
||||
onPlayerMove: undefined;
|
||||
showLayer: LayerEvent;
|
||||
hideLayer: LayerEvent;
|
||||
setProperty: SetPropertyEvent;
|
||||
getDataLayer: undefined;
|
||||
loadSound: LoadSoundEvent;
|
||||
playSound: PlaySoundEvent;
|
||||
stopSound: null;
|
||||
getState: undefined;
|
||||
registerMenuCommand: MenuItemRegisterEvent;
|
||||
setTiles: SetTilesEvent;
|
||||
|
||||
triggerMessage: TriggerMessageEvent
|
||||
removeTriggerMessage: MessageReferenceEvent
|
||||
}
|
||||
triggerMessage: TriggerMessageEvent;
|
||||
removeTriggerMessage: MessageReferenceEvent;
|
||||
};
|
||||
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 const isIframeEventWrapper = (event: any): event is IframeEvent<keyof IframeEventMap> =>
|
||||
typeof event.type === "string";
|
||||
|
||||
export interface IframeResponseEventMap {
|
||||
userInputChat: UserInputChatEvent
|
||||
enterEvent: EnterLeaveEvent
|
||||
leaveEvent: EnterLeaveEvent
|
||||
buttonClickedEvent: ButtonClickedEvent
|
||||
gameState: GameStateEvent
|
||||
hasPlayerMoved: HasPlayerMovedEvent
|
||||
dataLayer: DataLayerEvent
|
||||
menuItemClicked: MenuItemClickedEvent
|
||||
messageTriggered: MessageReferenceEvent
|
||||
userInputChat: UserInputChatEvent;
|
||||
enterEvent: EnterLeaveEvent;
|
||||
leaveEvent: EnterLeaveEvent;
|
||||
buttonClickedEvent: ButtonClickedEvent;
|
||||
hasPlayerMoved: HasPlayerMovedEvent;
|
||||
dataLayer: DataLayerEvent;
|
||||
menuItemClicked: MenuItemClickedEvent;
|
||||
messageTriggered: MessageReferenceEvent;
|
||||
}
|
||||
export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> {
|
||||
type: T;
|
||||
|
@ -80,4 +85,67 @@ export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> {
|
|||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const isIframeResponseEventWrapper = (event: { type?: string }): event is IframeResponseEvent<keyof IframeResponseEventMap> => typeof event.type === 'string';
|
||||
export const isIframeResponseEventWrapper = (event: {
|
||||
type?: string;
|
||||
}): event is IframeResponseEvent<keyof IframeResponseEventMap> => typeof event.type === "string";
|
||||
|
||||
/**
|
||||
* List event types sent from an iFrame to WorkAdventure that expect a unique answer from WorkAdventure along the type for the answer from WorkAdventure to the iFrame
|
||||
*/
|
||||
export type IframeQueryMap = {
|
||||
getState: {
|
||||
query: undefined;
|
||||
answer: GameStateEvent;
|
||||
};
|
||||
|
||||
[triggerMessage]: {
|
||||
query: TriggerMessageEvent;
|
||||
answer: void;
|
||||
};
|
||||
|
||||
[removeTriggerMessage]: {
|
||||
query: MessageReferenceEvent;
|
||||
answer: void;
|
||||
};
|
||||
};
|
||||
|
||||
export interface IframeQuery<T extends keyof IframeQueryMap> {
|
||||
type: T;
|
||||
data: IframeQueryMap[T]["query"];
|
||||
}
|
||||
|
||||
export interface IframeQueryWrapper<T extends keyof IframeQueryMap> {
|
||||
id: number;
|
||||
query: IframeQuery<T>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const isIframeQuery = (event: any): event is IframeQuery<keyof IframeQueryMap> => typeof event.type === "string";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const isIframeQueryWrapper = (event: any): event is IframeQueryWrapper<keyof IframeQueryMap> =>
|
||||
typeof event.id === "number" && isIframeQuery(event.query);
|
||||
|
||||
export interface IframeAnswerEvent<T extends keyof IframeQueryMap> {
|
||||
id: number;
|
||||
type: T;
|
||||
data: IframeQueryMap[T]["answer"];
|
||||
}
|
||||
|
||||
export const isIframeAnswerEvent = (event: {
|
||||
type?: string;
|
||||
id?: number;
|
||||
}): event is IframeAnswerEvent<keyof IframeQueryMap> => typeof event.type === "string" && typeof event.id === "number";
|
||||
|
||||
export interface IframeErrorAnswerEvent {
|
||||
id: number;
|
||||
type: keyof IframeQueryMap;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export const isIframeErrorAnswerEvent = (event: {
|
||||
type?: string;
|
||||
id?: number;
|
||||
error?: string;
|
||||
}): event is IframeErrorAnswerEvent =>
|
||||
typeof event.type === "string" && typeof event.id === "number" && typeof event.error === "string";
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import * as tg from "generic-type-guard";
|
||||
|
||||
export const isLayerEvent =
|
||||
new tg.IsInterface().withProperties({
|
||||
export const isLayerEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
name: tg.isString,
|
||||
}).get();
|
||||
})
|
||||
.get();
|
||||
/**
|
||||
* A message sent from the iFrame to the game to show/hide a layer.
|
||||
*/
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import * as tg from "generic-type-guard";
|
||||
|
||||
|
||||
|
||||
export const isLoadPageEvent =
|
||||
new tg.IsInterface().withProperties({
|
||||
export const isLoadPageEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
url: tg.isString,
|
||||
}).get();
|
||||
})
|
||||
.get();
|
||||
|
||||
/**
|
||||
* A message sent from the iFrame to the game to add a message in the chat.
|
||||
*/
|
||||
export type LoadPageEvent = tg.GuardedType<typeof isLoadPageEvent>;
|
||||
export type LoadPageEvent = tg.GuardedType<typeof isLoadPageEvent>;
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import * as tg from "generic-type-guard";
|
||||
|
||||
|
||||
|
||||
export const isOpenCoWebsite =
|
||||
new tg.IsInterface().withProperties({
|
||||
export const isOpenCoWebsite = new tg.IsInterface()
|
||||
.withProperties({
|
||||
url: tg.isString,
|
||||
}).get();
|
||||
allowApi: tg.isBoolean,
|
||||
allowPolicy: tg.isString,
|
||||
})
|
||||
.get();
|
||||
|
||||
/**
|
||||
* A message sent from the iFrame to the game to add a message in the chat.
|
||||
|
|
16
front/src/Api/Events/SetTilesEvent.ts
Normal file
16
front/src/Api/Events/SetTilesEvent.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import * as tg from "generic-type-guard";
|
||||
|
||||
export const isSetTilesEvent = tg.isArray(
|
||||
new tg.IsInterface()
|
||||
.withProperties({
|
||||
x: tg.isNumber,
|
||||
y: tg.isNumber,
|
||||
tile: tg.isUnion(tg.isNumber, tg.isString),
|
||||
layer: tg.isString,
|
||||
})
|
||||
.get()
|
||||
);
|
||||
/**
|
||||
* A message sent from the iFrame to the game to set one or many tiles.
|
||||
*/
|
||||
export type SetTilesEvent = tg.GuardedType<typeof isSetTilesEvent>;
|
|
@ -1,12 +1,13 @@
|
|||
import * as tg from "generic-type-guard";
|
||||
|
||||
export const isSetPropertyEvent =
|
||||
new tg.IsInterface().withProperties({
|
||||
export const isSetPropertyEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
layerName: tg.isString,
|
||||
propertyName: tg.isString,
|
||||
propertyValue: tg.isUnion(tg.isString, tg.isUnion(tg.isNumber, tg.isUnion(tg.isBoolean, tg.isUndefined)))
|
||||
}).get();
|
||||
propertyValue: tg.isUnion(tg.isString, tg.isUnion(tg.isNumber, tg.isUnion(tg.isBoolean, tg.isUndefined))),
|
||||
})
|
||||
.get();
|
||||
/**
|
||||
* A message sent from the iFrame to the game to change the value of the property of the layer
|
||||
*/
|
||||
export type SetPropertyEvent = tg.GuardedType<typeof isSetPropertyEvent>;
|
||||
export type SetPropertyEvent = tg.GuardedType<typeof isSetPropertyEvent>;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import * as tg from "generic-type-guard";
|
||||
|
||||
export const isMenuItemClickedEvent =
|
||||
new tg.IsInterface().withProperties({
|
||||
menuItem: tg.isString
|
||||
}).get();
|
||||
export const isMenuItemClickedEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
menuItem: tg.isString,
|
||||
})
|
||||
.get();
|
||||
/**
|
||||
* A message sent from the game to the iFrame when a menu item is clicked.
|
||||
*/
|
||||
export type MenuItemClickedEvent = tg.GuardedType<typeof isMenuItemClickedEvent>;
|
||||
|
||||
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
import * as tg from "generic-type-guard";
|
||||
import { Subject } from 'rxjs';
|
||||
import { Subject } from "rxjs";
|
||||
|
||||
export const isMenuItemRegisterEvent =
|
||||
new tg.IsInterface().withProperties({
|
||||
menutItem: tg.isString
|
||||
}).get();
|
||||
export const isMenuItemRegisterEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
menutItem: tg.isString,
|
||||
})
|
||||
.get();
|
||||
/**
|
||||
* A message sent from the iFrame to the game to add a new menu item.
|
||||
*/
|
||||
export type MenuItemRegisterEvent = tg.GuardedType<typeof isMenuItemRegisterEvent>;
|
||||
|
||||
export const isMenuItemRegisterIframeEvent =
|
||||
new tg.IsInterface().withProperties({
|
||||
export const isMenuItemRegisterIframeEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
type: tg.isSingletonString("registerMenuCommand"),
|
||||
data: isMenuItemRegisterEvent
|
||||
}).get();
|
||||
|
||||
data: isMenuItemRegisterEvent,
|
||||
})
|
||||
.get();
|
||||
|
||||
const _registerMenuCommandStream: Subject<string> = new Subject();
|
||||
export const registerMenuCommandStream = _registerMenuCommandStream.asObservable();
|
||||
|
||||
export function handleMenuItemRegistrationEvent(event: MenuItemRegisterEvent) {
|
||||
_registerMenuCommandStream.next(event.menutItem)
|
||||
}
|
||||
_registerMenuCommandStream.next(event.menutItem);
|
||||
}
|
||||
|
|
|
@ -1,42 +1,35 @@
|
|||
import { Subject } from 'rxjs';
|
||||
import { iframeListener } from '../../IframeListener';
|
||||
import { isMessageReferenceEvent, isTriggerMessageEvent, MessageReferenceEvent, removeTriggerMessage, triggerMessage, TriggerMessageEvent } from './TriggerMessageEvent';
|
||||
import { Subject } from "rxjs";
|
||||
import { iframeListener } from "../../IframeListener";
|
||||
import {
|
||||
isMessageReferenceEvent,
|
||||
isTriggerMessageEvent,
|
||||
MessageReferenceEvent,
|
||||
removeTriggerMessage,
|
||||
triggerMessage,
|
||||
TriggerMessageEvent,
|
||||
} from "./TriggerMessageEvent";
|
||||
import * as tg from "generic-type-guard";
|
||||
export function sendMessageTriggeredEvent(uuid: string) {
|
||||
iframeListener.postMessage({
|
||||
'type': 'messageTriggered',
|
||||
'data': {
|
||||
type: "messageTriggered",
|
||||
data: {
|
||||
uuid,
|
||||
} as MessageReferenceEvent
|
||||
} as MessageReferenceEvent,
|
||||
});
|
||||
}
|
||||
|
||||
const _triggerMessageEvent: Subject<TriggerMessageEvent> = new Subject();
|
||||
const _removeTriggerMessageEvent: Subject<MessageReferenceEvent> = new Subject();
|
||||
const isTriggerMessageEventObject = new tg.IsInterface()
|
||||
.withProperties({
|
||||
type: tg.isSingletonString(triggerMessage),
|
||||
data: isTriggerMessageEvent,
|
||||
})
|
||||
.get();
|
||||
|
||||
export const triggerMessageEvent = _triggerMessageEvent.asObservable();
|
||||
const isTriggerMessageRemoveEventObject = new tg.IsInterface()
|
||||
.withProperties({
|
||||
type: tg.isSingletonString(removeTriggerMessage),
|
||||
data: isMessageReferenceEvent,
|
||||
})
|
||||
.get();
|
||||
|
||||
export const removeTriggerMessageEvent = _removeTriggerMessageEvent.asObservable();
|
||||
|
||||
const isTriggerMessageEventObject = new tg.IsInterface().withProperties({
|
||||
type: tg.isSingletonString(triggerMessage),
|
||||
data: isTriggerMessageEvent
|
||||
}).get()
|
||||
const isTriggerMessageRemoveEventObject = new tg.IsInterface().withProperties({
|
||||
type: tg.isSingletonString(removeTriggerMessage),
|
||||
data: isMessageReferenceEvent
|
||||
}).get()
|
||||
|
||||
|
||||
export const isTriggerMessageHandlerEvent = tg.isUnion(isTriggerMessageEventObject, isTriggerMessageRemoveEventObject)
|
||||
|
||||
|
||||
|
||||
|
||||
export function triggerMessageEventHandler(event: tg.GuardedType<typeof isTriggerMessageHandlerEvent>) {
|
||||
if (isTriggerMessageEventObject(event)) {
|
||||
_triggerMessageEvent.next(event.data)
|
||||
} else if (isTriggerMessageRemoveEventObject(event)) {
|
||||
_removeTriggerMessageEvent.next(event.data)
|
||||
}
|
||||
}
|
||||
export const isTriggerMessageHandlerEvent = tg.isUnion(isTriggerMessageEventObject, isTriggerMessageRemoveEventObject);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Subject } from "rxjs";
|
||||
import type * as tg from "generic-type-guard";
|
||||
import { ChatEvent, isChatEvent } from "./Events/ChatEvent";
|
||||
import { HtmlUtils } from "../WebRtc/HtmlUtils";
|
||||
import type { EnterLeaveEvent } from "./Events/EnterLeaveEvent";
|
||||
|
@ -10,34 +11,40 @@ import { scriptUtils } from "./ScriptUtils";
|
|||
import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent";
|
||||
import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent";
|
||||
import {
|
||||
IframeErrorAnswerEvent,
|
||||
IframeEvent,
|
||||
IframeEventMap,
|
||||
IframeQuery,
|
||||
IframeQueryMap,
|
||||
IframeResponseEvent,
|
||||
IframeResponseEventMap,
|
||||
isIframeEventWrapper,
|
||||
TypedMessageEvent
|
||||
isIframeQueryWrapper,
|
||||
TypedMessageEvent,
|
||||
} from "./Events/IframeEvent";
|
||||
import type { UserInputChatEvent } from "./Events/UserInputChatEvent";
|
||||
//import { isLoadPageEvent } from './Events/LoadPageEvent';
|
||||
import { isPlaySoundEvent, PlaySoundEvent } from "./Events/PlaySoundEvent";
|
||||
import { isStopSoundEvent, StopSoundEvent } from "./Events/StopSoundEvent";
|
||||
import { isLoadSoundEvent, LoadSoundEvent } from "./Events/LoadSoundEvent";
|
||||
import { isSetPropertyEvent, SetPropertyEvent } from "./Events/setPropertyEvent";
|
||||
import { isLayerEvent, LayerEvent } from "./Events/LayerEvent";
|
||||
import { isMenuItemRegisterEvent, } from "./Events/ui/MenuItemRegisterEvent";
|
||||
import { isMenuItemRegisterEvent } from "./Events/ui/MenuItemRegisterEvent";
|
||||
import type { DataLayerEvent } from "./Events/DataLayerEvent";
|
||||
import type { GameStateEvent } from "./Events/GameStateEvent";
|
||||
import type { HasPlayerMovedEvent } from "./Events/HasPlayerMovedEvent";
|
||||
import { isLoadPageEvent } from "./Events/LoadPageEvent";
|
||||
import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from "./Events/ui/MenuItemRegisterEvent";
|
||||
import { isTriggerMessageHandlerEvent, triggerMessageEventHandler } from './Events/ui/TriggerMessageEventHandler';
|
||||
import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent";
|
||||
|
||||
type AnswererCallback<T extends keyof IframeQueryMap> = (
|
||||
query: IframeQueryMap[T]["query"]
|
||||
) => IframeQueryMap[T]["answer"] | Promise<IframeQueryMap[T]["answer"]>;
|
||||
|
||||
/**
|
||||
* Listens to messages from iframes and turn those messages into easy to use observables.
|
||||
* Also allows to send messages to those iframes.
|
||||
*/
|
||||
class IframeListener {
|
||||
|
||||
private readonly _chatStream: Subject<ChatEvent> = new Subject();
|
||||
public readonly chatStream = this._chatStream.asObservable();
|
||||
|
||||
|
@ -83,9 +90,6 @@ class IframeListener {
|
|||
private readonly _setPropertyStream: Subject<SetPropertyEvent> = new Subject();
|
||||
public readonly setPropertyStream = this._setPropertyStream.asObservable();
|
||||
|
||||
private readonly _gameStateStream: Subject<void> = new Subject();
|
||||
public readonly gameStateStream = this._gameStateStream.asObservable();
|
||||
|
||||
private readonly _dataLayerChangeStream: Subject<void> = new Subject();
|
||||
public readonly dataLayerChangeStream = this._dataLayerChangeStream.asObservable();
|
||||
|
||||
|
@ -104,113 +108,170 @@ class IframeListener {
|
|||
private readonly _loadSoundStream: Subject<LoadSoundEvent> = new Subject();
|
||||
public readonly loadSoundStream = this._loadSoundStream.asObservable();
|
||||
|
||||
private readonly _setTilesStream: Subject<SetTilesEvent> = new Subject();
|
||||
public readonly setTilesStream = this._setTilesStream.asObservable();
|
||||
|
||||
private readonly iframes = new Set<HTMLIFrameElement>();
|
||||
private readonly iframeCloseCallbacks = new Map<HTMLIFrameElement, (() => void)[]>();
|
||||
private readonly scripts = new Map<string, HTMLIFrameElement>();
|
||||
private sendPlayerMove: boolean = false;
|
||||
|
||||
private answerers: {
|
||||
[key in keyof IframeQueryMap]?: AnswererCallback<key>;
|
||||
} = {};
|
||||
|
||||
init() {
|
||||
window.addEventListener("message", (message: TypedMessageEvent<IframeEvent<keyof IframeEventMap>>) => {
|
||||
// Do we trust the sender of this message?
|
||||
// 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).
|
||||
let foundSrc: string | undefined;
|
||||
window.addEventListener(
|
||||
"message",
|
||||
<T extends keyof IframeEventMap, U extends keyof IframeQueryMap>(
|
||||
message: TypedMessageEvent<IframeEvent<T | U>>
|
||||
) => {
|
||||
// Do we trust the sender of this message?
|
||||
// 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).
|
||||
let foundSrc: string | undefined;
|
||||
|
||||
let iframe: HTMLIFrameElement;
|
||||
for (iframe of this.iframes) {
|
||||
if (iframe.contentWindow === message.source) {
|
||||
foundSrc = iframe.src;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundSrc === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = message.data;
|
||||
if (isIframeEventWrapper(payload)) {
|
||||
if (payload.type === 'showLayer' && isLayerEvent(payload.data)) {
|
||||
this._showLayerStream.next(payload.data);
|
||||
} else if (payload.type === 'hideLayer' && isLayerEvent(payload.data)) {
|
||||
this._hideLayerStream.next(payload.data);
|
||||
} else if (payload.type === 'setProperty' && isSetPropertyEvent(payload.data)) {
|
||||
this._setPropertyStream.next(payload.data);
|
||||
} else if (payload.type === 'chat' && isChatEvent(payload.data)) {
|
||||
this._chatStream.next(payload.data);
|
||||
} else if (payload.type === 'openPopup' && isOpenPopupEvent(payload.data)) {
|
||||
this._openPopupStream.next(payload.data);
|
||||
} else if (payload.type === 'closePopup' && isClosePopupEvent(payload.data)) {
|
||||
this._closePopupStream.next(payload.data);
|
||||
}
|
||||
else if (payload.type === 'openTab' && isOpenTabEvent(payload.data)) {
|
||||
scriptUtils.openTab(payload.data.url);
|
||||
}
|
||||
else if (payload.type === 'goToPage' && isGoToPageEvent(payload.data)) {
|
||||
scriptUtils.goToPage(payload.data.url);
|
||||
}
|
||||
else if (payload.type === 'loadPage' && isLoadPageEvent(payload.data)) {
|
||||
this._loadPageStream.next(payload.data.url);
|
||||
}
|
||||
else if (payload.type === 'playSound' && isPlaySoundEvent(payload.data)) {
|
||||
this._playSoundStream.next(payload.data);
|
||||
}
|
||||
else if (payload.type === 'stopSound' && isStopSoundEvent(payload.data)) {
|
||||
this._stopSoundStream.next(payload.data);
|
||||
}
|
||||
else if (payload.type === 'loadSound' && isLoadSoundEvent(payload.data)) {
|
||||
this._loadSoundStream.next(payload.data);
|
||||
}
|
||||
else if (payload.type === 'openCoWebSite' && isOpenCoWebsite(payload.data)) {
|
||||
scriptUtils.openCoWebsite(payload.data.url, foundSrc);
|
||||
let iframe: HTMLIFrameElement | undefined;
|
||||
for (iframe of this.iframes) {
|
||||
if (iframe.contentWindow === message.source) {
|
||||
foundSrc = iframe.src;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
else if (payload.type === 'closeCoWebSite') {
|
||||
scriptUtils.closeCoWebSite();
|
||||
const payload = message.data;
|
||||
|
||||
if (foundSrc === undefined || iframe === undefined) {
|
||||
if (isIframeEventWrapper(payload)) {
|
||||
console.warn(
|
||||
"It seems an iFrame is trying to communicate with WorkAdventure but was not explicitly granted the permission to do so. " +
|
||||
"If you are looking to use the WorkAdventure Scripting API inside an iFrame, you should allow the " +
|
||||
'iFrame to communicate with WorkAdventure by using the "openWebsiteAllowApi" property in your map (or passing "true" as a second' +
|
||||
"parameter to WA.nav.openCoWebSite())"
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
else if (payload.type === 'disablePlayerControls') {
|
||||
this._disablePlayerControlStream.next();
|
||||
}
|
||||
else if (payload.type === 'restorePlayerControls') {
|
||||
this._enablePlayerControlStream.next();
|
||||
} else if (payload.type === 'displayBubble') {
|
||||
this._displayBubbleStream.next();
|
||||
} else if (payload.type === 'removeBubble') {
|
||||
this._removeBubbleStream.next();
|
||||
} else if (payload.type == "getState") {
|
||||
this._gameStateStream.next();
|
||||
} else if (payload.type == "onPlayerMove") {
|
||||
this.sendPlayerMove = true
|
||||
} else if (payload.type == "getDataLayer") {
|
||||
this._dataLayerChangeStream.next();
|
||||
} else if (isMenuItemRegisterIframeEvent(payload)) {
|
||||
const data = payload.data.menutItem;
|
||||
// @ts-ignore
|
||||
this.iframeCloseCallbacks.get(iframe).push(() => {
|
||||
this._unregisterMenuCommandStream.next(data);
|
||||
})
|
||||
handleMenuItemRegistrationEvent(payload.data)
|
||||
} else if (isTriggerMessageHandlerEvent(payload)) {
|
||||
triggerMessageEventHandler(payload)
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
foundSrc = this.getBaseUrl(foundSrc, message.source);
|
||||
|
||||
if (isIframeQueryWrapper(payload)) {
|
||||
const queryId = payload.id;
|
||||
const query = payload.query as IframeQuery<U>;
|
||||
|
||||
const answerer = this.answerers[query.type] as AnswererCallback<U> | undefined;
|
||||
if (answerer === undefined) {
|
||||
const errorMsg =
|
||||
'The iFrame sent a message of type "' +
|
||||
query.type +
|
||||
'" but there is no service configured to answer these messages.';
|
||||
console.error(errorMsg);
|
||||
iframe.contentWindow?.postMessage(
|
||||
{
|
||||
id: queryId,
|
||||
type: query.type,
|
||||
error: errorMsg,
|
||||
} as IframeErrorAnswerEvent,
|
||||
"*"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Promise.resolve(answerer(query.data))
|
||||
.then((value) => {
|
||||
iframe?.contentWindow?.postMessage(
|
||||
{
|
||||
id: queryId,
|
||||
type: query.type,
|
||||
data: value,
|
||||
},
|
||||
"*"
|
||||
);
|
||||
})
|
||||
.catch((reason) => {
|
||||
console.error("An error occurred while responding to an iFrame query.", reason);
|
||||
let reasonMsg: string;
|
||||
if (reason instanceof Error) {
|
||||
reasonMsg = reason.message;
|
||||
} else {
|
||||
reasonMsg = reason.toString();
|
||||
}
|
||||
|
||||
iframe?.contentWindow?.postMessage(
|
||||
{
|
||||
id: queryId,
|
||||
type: query.type,
|
||||
error: reasonMsg,
|
||||
} as IframeErrorAnswerEvent,
|
||||
"*"
|
||||
);
|
||||
});
|
||||
} else if (isIframeEventWrapper(payload)) {
|
||||
if (payload.type === "showLayer" && isLayerEvent(payload.data)) {
|
||||
this._showLayerStream.next(payload.data);
|
||||
} else if (payload.type === "hideLayer" && isLayerEvent(payload.data)) {
|
||||
this._hideLayerStream.next(payload.data);
|
||||
} else if (payload.type === "setProperty" && isSetPropertyEvent(payload.data)) {
|
||||
this._setPropertyStream.next(payload.data);
|
||||
} else if (payload.type === "chat" && isChatEvent(payload.data)) {
|
||||
this._chatStream.next(payload.data);
|
||||
} else if (payload.type === "openPopup" && isOpenPopupEvent(payload.data)) {
|
||||
this._openPopupStream.next(payload.data);
|
||||
} else if (payload.type === "closePopup" && isClosePopupEvent(payload.data)) {
|
||||
this._closePopupStream.next(payload.data);
|
||||
} else if (payload.type === "openTab" && isOpenTabEvent(payload.data)) {
|
||||
scriptUtils.openTab(payload.data.url);
|
||||
} else if (payload.type === "goToPage" && isGoToPageEvent(payload.data)) {
|
||||
scriptUtils.goToPage(payload.data.url);
|
||||
} else if (payload.type === "loadPage" && isLoadPageEvent(payload.data)) {
|
||||
this._loadPageStream.next(payload.data.url);
|
||||
} else if (payload.type === "playSound" && isPlaySoundEvent(payload.data)) {
|
||||
this._playSoundStream.next(payload.data);
|
||||
} else if (payload.type === "stopSound" && isStopSoundEvent(payload.data)) {
|
||||
this._stopSoundStream.next(payload.data);
|
||||
} else if (payload.type === "loadSound" && isLoadSoundEvent(payload.data)) {
|
||||
this._loadSoundStream.next(payload.data);
|
||||
} else if (payload.type === "openCoWebSite" && isOpenCoWebsite(payload.data)) {
|
||||
scriptUtils.openCoWebsite(
|
||||
payload.data.url,
|
||||
foundSrc,
|
||||
payload.data.allowApi,
|
||||
payload.data.allowPolicy
|
||||
);
|
||||
} else if (payload.type === "closeCoWebSite") {
|
||||
scriptUtils.closeCoWebSite();
|
||||
} else if (payload.type === "disablePlayerControls") {
|
||||
this._disablePlayerControlStream.next();
|
||||
} else if (payload.type === "restorePlayerControls") {
|
||||
this._enablePlayerControlStream.next();
|
||||
} else if (payload.type === "displayBubble") {
|
||||
this._displayBubbleStream.next();
|
||||
} else if (payload.type === "removeBubble") {
|
||||
this._removeBubbleStream.next();
|
||||
} else if (payload.type == "onPlayerMove") {
|
||||
this.sendPlayerMove = true;
|
||||
} else if (payload.type == "getDataLayer") {
|
||||
this._dataLayerChangeStream.next();
|
||||
} else if (isMenuItemRegisterIframeEvent(payload)) {
|
||||
const data = payload.data.menutItem;
|
||||
// @ts-ignore
|
||||
this.iframeCloseCallbacks.get(iframe).push(() => {
|
||||
this._unregisterMenuCommandStream.next(data);
|
||||
});
|
||||
handleMenuItemRegistrationEvent(payload.data);
|
||||
} else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) {
|
||||
this._setTilesStream.next(payload.data);
|
||||
}
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
sendDataLayerEvent(dataLayerEvent: DataLayerEvent) {
|
||||
this.postMessage({
|
||||
'type': 'dataLayer',
|
||||
'data': dataLayerEvent
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
sendGameStateEvent(gameStateEvent: GameStateEvent) {
|
||||
this.postMessage({
|
||||
'type': 'gameState',
|
||||
'data': gameStateEvent
|
||||
type: "dataLayer",
|
||||
data: dataLayerEvent,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -223,25 +284,25 @@ class IframeListener {
|
|||
}
|
||||
|
||||
unregisterIframe(iframe: HTMLIFrameElement): void {
|
||||
this.iframeCloseCallbacks.get(iframe)?.forEach(callback => {
|
||||
this.iframeCloseCallbacks.get(iframe)?.forEach((callback) => {
|
||||
callback();
|
||||
});
|
||||
this.iframes.delete(iframe);
|
||||
}
|
||||
|
||||
registerScript(scriptUrl: string): void {
|
||||
console.log('Loading map related script at ', scriptUrl)
|
||||
console.log("Loading map related script at ", scriptUrl);
|
||||
|
||||
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
|
||||
if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
|
||||
// Using external iframe mode (
|
||||
const iframe = document.createElement('iframe');
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.id = IframeListener.getIFrameId(scriptUrl);
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = '/iframe.html?script=' + encodeURIComponent(scriptUrl);
|
||||
iframe.style.display = "none";
|
||||
iframe.src = "/iframe.html?script=" + encodeURIComponent(scriptUrl);
|
||||
|
||||
// We are putting a sandbox on this script because it will run in the same domain as the main website.
|
||||
iframe.sandbox.add('allow-scripts');
|
||||
iframe.sandbox.add('allow-top-navigation-by-user-activation');
|
||||
iframe.sandbox.add("allow-scripts");
|
||||
iframe.sandbox.add("allow-top-navigation-by-user-activation");
|
||||
|
||||
document.body.prepend(iframe);
|
||||
|
||||
|
@ -249,36 +310,50 @@ class IframeListener {
|
|||
this.registerIframe(iframe);
|
||||
} else {
|
||||
// production code
|
||||
const iframe = document.createElement('iframe');
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.id = IframeListener.getIFrameId(scriptUrl);
|
||||
iframe.style.display = 'none';
|
||||
iframe.style.display = "none";
|
||||
|
||||
// We are putting a sandbox on this script because it will run in the same domain as the main website.
|
||||
iframe.sandbox.add('allow-scripts');
|
||||
iframe.sandbox.add('allow-top-navigation-by-user-activation');
|
||||
iframe.sandbox.add("allow-scripts");
|
||||
iframe.sandbox.add("allow-top-navigation-by-user-activation");
|
||||
|
||||
//iframe.src = "data:text/html;charset=utf-8," + escape(html);
|
||||
iframe.srcdoc = '<!doctype html>\n' +
|
||||
'\n' +
|
||||
iframe.srcdoc =
|
||||
"<!doctype html>\n" +
|
||||
"\n" +
|
||||
'<html lang="en">\n' +
|
||||
'<head>\n' +
|
||||
'<script src="' + window.location.protocol + '//' + window.location.host + '/iframe_api.js" ></script>\n' +
|
||||
'<script src="' + scriptUrl + '" ></script>\n' +
|
||||
'<title></title>\n' +
|
||||
'</head>\n' +
|
||||
'</html>\n';
|
||||
"<head>\n" +
|
||||
'<script src="' +
|
||||
window.location.protocol +
|
||||
"//" +
|
||||
window.location.host +
|
||||
'/iframe_api.js" ></script>\n' +
|
||||
'<script src="' +
|
||||
scriptUrl +
|
||||
'" ></script>\n' +
|
||||
"<title></title>\n" +
|
||||
"</head>\n" +
|
||||
"</html>\n";
|
||||
|
||||
document.body.prepend(iframe);
|
||||
|
||||
this.scripts.set(scriptUrl, iframe);
|
||||
this.registerIframe(iframe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private getBaseUrl(src: string, source: MessageEventSource | null): string {
|
||||
for (const script of this.scripts) {
|
||||
if (script[1].contentWindow === source) {
|
||||
return script[0];
|
||||
}
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
private static getIFrameId(scriptUrl: string): string {
|
||||
return 'script' + btoa(scriptUrl);
|
||||
return "script" + btoa(scriptUrl);
|
||||
}
|
||||
|
||||
unregisterScript(scriptUrl: string): void {
|
||||
|
@ -295,47 +370,47 @@ class IframeListener {
|
|||
|
||||
sendUserInputChat(message: string) {
|
||||
this.postMessage({
|
||||
'type': 'userInputChat',
|
||||
'data': {
|
||||
'message': message,
|
||||
} as UserInputChatEvent
|
||||
type: "userInputChat",
|
||||
data: {
|
||||
message: message,
|
||||
} as UserInputChatEvent,
|
||||
});
|
||||
}
|
||||
|
||||
sendEnterEvent(name: string) {
|
||||
this.postMessage({
|
||||
'type': 'enterEvent',
|
||||
'data': {
|
||||
"name": name
|
||||
} as EnterLeaveEvent
|
||||
type: "enterEvent",
|
||||
data: {
|
||||
name: name,
|
||||
} as EnterLeaveEvent,
|
||||
});
|
||||
}
|
||||
|
||||
sendLeaveEvent(name: string) {
|
||||
this.postMessage({
|
||||
'type': 'leaveEvent',
|
||||
'data': {
|
||||
"name": name
|
||||
} as EnterLeaveEvent
|
||||
type: "leaveEvent",
|
||||
data: {
|
||||
name: name,
|
||||
} as EnterLeaveEvent,
|
||||
});
|
||||
}
|
||||
|
||||
hasPlayerMoved(event: HasPlayerMovedEvent) {
|
||||
if (this.sendPlayerMove) {
|
||||
this.postMessage({
|
||||
'type': 'hasPlayerMoved',
|
||||
'data': event
|
||||
type: "hasPlayerMoved",
|
||||
data: event,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sendButtonClickedEvent(popupId: number, buttonId: number): void {
|
||||
this.postMessage({
|
||||
'type': 'buttonClickedEvent',
|
||||
'data': {
|
||||
type: "buttonClickedEvent",
|
||||
data: {
|
||||
popupId,
|
||||
buttonId
|
||||
} as ButtonClickedEvent
|
||||
buttonId,
|
||||
} as ButtonClickedEvent,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -344,10 +419,30 @@ class IframeListener {
|
|||
*/
|
||||
public postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>) {
|
||||
for (const iframe of this.iframes) {
|
||||
iframe.contentWindow?.postMessage(message, '*');
|
||||
iframe.contentWindow?.postMessage(message, "*");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback that can be used to respond to some query (as defined in the IframeQueryMap type).
|
||||
*
|
||||
* Important! There can be only one "answerer" so registering a new one will unregister the old one.
|
||||
*
|
||||
* @param key The "type" of the query we are answering
|
||||
* @param callback
|
||||
*/
|
||||
public registerAnswerer<T extends keyof IframeQueryMap, Guard extends tg.TypeGuard<IframeQueryMap[T]["query"]>>(
|
||||
key: T,
|
||||
callback: AnswererCallback<T>,
|
||||
typeChecker?: Guard
|
||||
): void {
|
||||
//@ts-ignore
|
||||
this.answerers[key] = callback;
|
||||
}
|
||||
|
||||
public unregisterAnswerer(key: keyof IframeQueryMap): void {
|
||||
delete this.answerers[key];
|
||||
}
|
||||
}
|
||||
|
||||
export const iframeListener = new IframeListener();
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
import {coWebsiteManager} from "../WebRtc/CoWebsiteManager";
|
||||
import { coWebsiteManager } from "../WebRtc/CoWebsiteManager";
|
||||
|
||||
class ScriptUtils {
|
||||
|
||||
public openTab(url : string){
|
||||
public openTab(url: string) {
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
public goToPage(url : string){
|
||||
window.location.href = url;
|
||||
|
||||
public goToPage(url: string) {
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
public openCoWebsite(url: string, base: string) {
|
||||
coWebsiteManager.loadCoWebsite(url, base);
|
||||
public openCoWebsite(url: string, base: string, api: boolean, policy: string) {
|
||||
coWebsiteManager.loadCoWebsite(url, base, api, policy);
|
||||
}
|
||||
|
||||
public closeCoWebSite(){
|
||||
public closeCoWebSite() {
|
||||
coWebsiteManager.closeCoWebsite();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,66 @@
|
|||
import type * as tg from "generic-type-guard";
|
||||
import type { IframeEvent, IframeEventMap, IframeResponseEventMap } from '../Events/IframeEvent';
|
||||
import type {
|
||||
IframeEvent,
|
||||
IframeEventMap,
|
||||
IframeQuery,
|
||||
IframeQueryMap,
|
||||
IframeResponseEventMap,
|
||||
} from "../Events/IframeEvent";
|
||||
import type { IframeQueryWrapper } from "../Events/IframeEvent";
|
||||
|
||||
export function sendToWorkadventure(content: IframeEvent<keyof IframeEventMap>) {
|
||||
window.parent.postMessage(content, "*")
|
||||
window.parent.postMessage(content, "*");
|
||||
}
|
||||
type GuardedType<Guard extends tg.TypeGuard<unknown>> = Guard extends tg.TypeGuard<infer T> ? T : never
|
||||
|
||||
export interface IframeCallback<Key extends keyof IframeResponseEventMap, T = IframeResponseEventMap[Key], Guard = tg.TypeGuard<T>> {
|
||||
let queryNumber = 0;
|
||||
|
||||
typeChecker: Guard,
|
||||
callback: (payloadData: T) => void
|
||||
export const answerPromises = new Map<
|
||||
number,
|
||||
{
|
||||
resolve: (
|
||||
value:
|
||||
| IframeQueryMap[keyof IframeQueryMap]["answer"]
|
||||
| PromiseLike<IframeQueryMap[keyof IframeQueryMap]["answer"]>
|
||||
) => void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
reject: (reason?: any) => void;
|
||||
}
|
||||
>();
|
||||
|
||||
export function queryWorkadventure<T extends keyof IframeQueryMap>(
|
||||
content: IframeQuery<T>
|
||||
): Promise<IframeQueryMap[T]["answer"]> {
|
||||
return new Promise<IframeQueryMap[T]["answer"]>((resolve, reject) => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
id: queryNumber,
|
||||
query: content,
|
||||
} as IframeQueryWrapper<T>,
|
||||
"*"
|
||||
);
|
||||
|
||||
answerPromises.set(queryNumber, {
|
||||
resolve,
|
||||
reject,
|
||||
});
|
||||
|
||||
queryNumber++;
|
||||
});
|
||||
}
|
||||
|
||||
type GuardedType<Guard extends tg.TypeGuard<unknown>> = Guard extends tg.TypeGuard<infer T> ? T : never;
|
||||
|
||||
export interface IframeCallback<
|
||||
Key extends keyof IframeResponseEventMap,
|
||||
T = IframeResponseEventMap[Key],
|
||||
Guard = tg.TypeGuard<T>
|
||||
> {
|
||||
typeChecker: Guard;
|
||||
callback: (payloadData: T) => void;
|
||||
}
|
||||
|
||||
export interface IframeCallbackContribution<Key extends keyof IframeResponseEventMap> extends IframeCallback<Key> {
|
||||
|
||||
type: Key
|
||||
type: Key;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,9 +69,10 @@ export interface IframeCallbackContribution<Key extends keyof IframeResponseEven
|
|||
*
|
||||
*/
|
||||
|
||||
export abstract class IframeApiContribution<T extends {
|
||||
callbacks: Array<IframeCallbackContribution<keyof IframeResponseEventMap>>,
|
||||
}> {
|
||||
|
||||
abstract callbacks: T["callbacks"]
|
||||
export abstract class IframeApiContribution<
|
||||
T extends {
|
||||
callbacks: Array<IframeCallbackContribution<keyof IframeResponseEventMap>>;
|
||||
}
|
||||
> {
|
||||
abstract callbacks: T["callbacks"];
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import type { MenuItemClickedEvent } from '../../Events/ui/MenuItemClickedEvent';
|
||||
import { iframeListener } from '../../IframeListener';
|
||||
import type { MenuItemClickedEvent } from "../../Events/ui/MenuItemClickedEvent";
|
||||
import { iframeListener } from "../../IframeListener";
|
||||
|
||||
export function sendMenuClickedEvent(menuItem: string) {
|
||||
iframeListener.postMessage({
|
||||
'type': 'menuItemClicked',
|
||||
'data': {
|
||||
type: "menuItemClicked",
|
||||
data: {
|
||||
menuItem: menuItem,
|
||||
} as MenuItemClickedEvent
|
||||
} as MenuItemClickedEvent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
|
||||
import { removeTriggerMessage, triggerMessage, TriggerMessageEvent } from '../../Events/ui/TriggerMessageEvent';
|
||||
import { sendToWorkadventure } from '../IframeApiContribution';
|
||||
import {
|
||||
MessageReferenceEvent,
|
||||
removeTriggerMessage,
|
||||
triggerMessage,
|
||||
TriggerMessageEvent,
|
||||
} from "../../Events/ui/TriggerMessageEvent";
|
||||
import { queryWorkadventure } from "../IframeApiContribution";
|
||||
function uuidv4() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0,
|
||||
v = c === "x" ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
export let triggerMessageInstance: TriggerMessage | undefined = undefined
|
||||
|
||||
|
||||
export let triggerMessageInstance: TriggerMessage | undefined = undefined;
|
||||
|
||||
export class TriggerMessage {
|
||||
uuid: string
|
||||
uuid: string;
|
||||
|
||||
constructor(private message: string, private callback: () => void) {
|
||||
this.uuid = uuidv4()
|
||||
this.uuid = uuidv4();
|
||||
if (triggerMessageInstance) {
|
||||
triggerMessageInstance.remove();
|
||||
}
|
||||
|
@ -24,28 +27,24 @@ export class TriggerMessage {
|
|||
this.create();
|
||||
}
|
||||
|
||||
create(): this {
|
||||
sendToWorkadventure({
|
||||
async create() {
|
||||
await queryWorkadventure({
|
||||
type: triggerMessage,
|
||||
data: {
|
||||
message: this.message,
|
||||
uuid: this.uuid
|
||||
} as TriggerMessageEvent
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
remove() {
|
||||
sendToWorkadventure({
|
||||
type: removeTriggerMessage,
|
||||
data: {
|
||||
uuid: this.uuid
|
||||
} as TriggerMessageEvent
|
||||
})
|
||||
triggerMessageInstance = undefined
|
||||
}
|
||||
|
||||
trigger() {
|
||||
uuid: this.uuid,
|
||||
} as TriggerMessageEvent,
|
||||
});
|
||||
this.callback();
|
||||
}
|
||||
}
|
||||
|
||||
async remove() {
|
||||
await queryWorkadventure({
|
||||
type: removeTriggerMessage,
|
||||
data: {
|
||||
uuid: this.uuid,
|
||||
} as MessageReferenceEvent,
|
||||
});
|
||||
triggerMessageInstance = undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
import type { ChatEvent } from '../Events/ChatEvent'
|
||||
import { isUserInputChatEvent, UserInputChatEvent } from '../Events/UserInputChatEvent'
|
||||
import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'
|
||||
import type { ChatEvent } from "../Events/ChatEvent";
|
||||
import { isUserInputChatEvent, UserInputChatEvent } from "../Events/UserInputChatEvent";
|
||||
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
|
||||
import { apiCallback } from "./registeredCallbacks";
|
||||
import {Subject} from "rxjs";
|
||||
import { Subject } from "rxjs";
|
||||
|
||||
const chatStream = new Subject<string>();
|
||||
|
||||
class WorkadventureChatCommands extends IframeApiContribution<WorkadventureChatCommands> {
|
||||
|
||||
callbacks = [apiCallback({
|
||||
callback: (event: UserInputChatEvent) => {
|
||||
chatStream.next(event.message);
|
||||
},
|
||||
type: "userInputChat",
|
||||
typeChecker: isUserInputChatEvent
|
||||
})]
|
||||
|
||||
export class WorkadventureChatCommands extends IframeApiContribution<WorkadventureChatCommands> {
|
||||
callbacks = [
|
||||
apiCallback({
|
||||
callback: (event: UserInputChatEvent) => {
|
||||
chatStream.next(event.message);
|
||||
},
|
||||
type: "userInputChat",
|
||||
typeChecker: isUserInputChatEvent,
|
||||
}),
|
||||
];
|
||||
|
||||
sendChatMessage(message: string, author: string) {
|
||||
sendToWorkadventure({
|
||||
type: 'chat',
|
||||
type: "chat",
|
||||
data: {
|
||||
'message': message,
|
||||
'author': author
|
||||
}
|
||||
})
|
||||
message: message,
|
||||
author: author,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,4 +35,4 @@ class WorkadventureChatCommands extends IframeApiContribution<WorkadventureChatC
|
|||
}
|
||||
}
|
||||
|
||||
export default new WorkadventureChatCommands()
|
||||
export default new WorkadventureChatCommands();
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution';
|
||||
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
|
||||
|
||||
class WorkadventureControlsCommands extends IframeApiContribution<WorkadventureControlsCommands> {
|
||||
callbacks = []
|
||||
export class WorkadventureControlsCommands extends IframeApiContribution<WorkadventureControlsCommands> {
|
||||
callbacks = [];
|
||||
|
||||
disablePlayerControls(): void {
|
||||
sendToWorkadventure({ 'type': 'disablePlayerControls', data: null });
|
||||
sendToWorkadventure({ type: "disablePlayerControls", data: null });
|
||||
}
|
||||
|
||||
restorePlayerControls(): void {
|
||||
sendToWorkadventure({ 'type': 'restorePlayerControls', data: null });
|
||||
sendToWorkadventure({ type: "restorePlayerControls", data: null });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default new WorkadventureControlsCommands();
|
||||
|
|
|
@ -1,57 +1,56 @@
|
|||
import type { GoToPageEvent } from '../Events/GoToPageEvent';
|
||||
import type { OpenTabEvent } from '../Events/OpenTabEvent';
|
||||
import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution';
|
||||
import type {OpenCoWebSiteEvent} from "../Events/OpenCoWebSiteEvent";
|
||||
import type {LoadPageEvent} from "../Events/LoadPageEvent";
|
||||
|
||||
|
||||
class WorkadventureNavigationCommands extends IframeApiContribution<WorkadventureNavigationCommands> {
|
||||
callbacks = []
|
||||
import type { GoToPageEvent } from "../Events/GoToPageEvent";
|
||||
import type { OpenTabEvent } from "../Events/OpenTabEvent";
|
||||
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
|
||||
import type { OpenCoWebSiteEvent } from "../Events/OpenCoWebSiteEvent";
|
||||
import type { LoadPageEvent } from "../Events/LoadPageEvent";
|
||||
|
||||
export class WorkadventureNavigationCommands extends IframeApiContribution<WorkadventureNavigationCommands> {
|
||||
callbacks = [];
|
||||
|
||||
openTab(url: string): void {
|
||||
sendToWorkadventure({
|
||||
"type": 'openTab',
|
||||
"data": {
|
||||
url
|
||||
}
|
||||
type: "openTab",
|
||||
data: {
|
||||
url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
goToPage(url: string): void {
|
||||
sendToWorkadventure({
|
||||
"type": 'goToPage',
|
||||
"data": {
|
||||
url
|
||||
}
|
||||
type: "goToPage",
|
||||
data: {
|
||||
url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
goToRoom(url: string): void {
|
||||
sendToWorkadventure({
|
||||
"type": 'loadPage',
|
||||
"data": {
|
||||
url
|
||||
}
|
||||
type: "loadPage",
|
||||
data: {
|
||||
url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
openCoWebSite(url: string): void {
|
||||
openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ""): void {
|
||||
sendToWorkadventure({
|
||||
"type": 'openCoWebSite',
|
||||
"data": {
|
||||
url
|
||||
}
|
||||
type: "openCoWebSite",
|
||||
data: {
|
||||
url,
|
||||
allowApi,
|
||||
allowPolicy,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
closeCoWebSite(): void {
|
||||
sendToWorkadventure({
|
||||
"type": 'closeCoWebSite',
|
||||
data: null
|
||||
type: "closeCoWebSite",
|
||||
data: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default new WorkadventureNavigationCommands();
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
import {IframeApiContribution, sendToWorkadventure} from "./IframeApiContribution";
|
||||
import type {HasPlayerMovedEvent, HasPlayerMovedEventCallback} from "../Events/HasPlayerMovedEvent";
|
||||
import {Subject} from "rxjs";
|
||||
import {apiCallback} from "./registeredCallbacks";
|
||||
import {isHasPlayerMovedEvent} from "../Events/HasPlayerMovedEvent";
|
||||
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
|
||||
import type { HasPlayerMovedEvent, HasPlayerMovedEventCallback } from "../Events/HasPlayerMovedEvent";
|
||||
import { Subject } from "rxjs";
|
||||
import { apiCallback } from "./registeredCallbacks";
|
||||
import { isHasPlayerMovedEvent } from "../Events/HasPlayerMovedEvent";
|
||||
|
||||
const moveStream = new Subject<HasPlayerMovedEvent>();
|
||||
|
||||
class WorkadventurePlayerCommands extends IframeApiContribution<WorkadventurePlayerCommands> {
|
||||
export class WorkadventurePlayerCommands extends IframeApiContribution<WorkadventurePlayerCommands> {
|
||||
callbacks = [
|
||||
apiCallback({
|
||||
type: 'hasPlayerMoved',
|
||||
type: "hasPlayerMoved",
|
||||
typeChecker: isHasPlayerMovedEvent,
|
||||
callback: (payloadData) => {
|
||||
moveStream.next(payloadData);
|
||||
}
|
||||
},
|
||||
}),
|
||||
]
|
||||
];
|
||||
|
||||
onPlayerMove(callback: HasPlayerMovedEventCallback): void {
|
||||
moveStream.subscribe(callback);
|
||||
sendToWorkadventure({
|
||||
type: 'onPlayerMove',
|
||||
data: null
|
||||
})
|
||||
type: "onPlayerMove",
|
||||
data: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new WorkadventurePlayerCommands();
|
||||
export default new WorkadventurePlayerCommands();
|
||||
|
|
|
@ -1,87 +1,81 @@
|
|||
import { Subject } from "rxjs";
|
||||
import { EnterLeaveEvent, isEnterLeaveEvent } from '../Events/EnterLeaveEvent';
|
||||
import {IframeApiContribution, sendToWorkadventure} from './IframeApiContribution';
|
||||
|
||||
import { isDataLayerEvent } from "../Events/DataLayerEvent";
|
||||
import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent";
|
||||
import { isGameStateEvent } from "../Events/GameStateEvent";
|
||||
|
||||
import { IframeApiContribution, queryWorkadventure, sendToWorkadventure } from "./IframeApiContribution";
|
||||
import { apiCallback } from "./registeredCallbacks";
|
||||
import type {LayerEvent} from "../Events/LayerEvent";
|
||||
import type {SetPropertyEvent} from "../Events/setPropertyEvent";
|
||||
import type {GameStateEvent} from "../Events/GameStateEvent";
|
||||
import type {ITiledMap} from "../../Phaser/Map/ITiledMap";
|
||||
import type {DataLayerEvent} from "../Events/DataLayerEvent";
|
||||
import {isGameStateEvent} from "../Events/GameStateEvent";
|
||||
import {isDataLayerEvent} from "../Events/DataLayerEvent";
|
||||
|
||||
import type { ITiledMap } from "../../Phaser/Map/ITiledMap";
|
||||
import type { DataLayerEvent } from "../Events/DataLayerEvent";
|
||||
import type { GameStateEvent } from "../Events/GameStateEvent";
|
||||
|
||||
const enterStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
|
||||
const leaveStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
|
||||
const dataLayerResolver = new Subject<DataLayerEvent>();
|
||||
const stateResolvers = new Subject<GameStateEvent>();
|
||||
|
||||
let immutableData: GameStateEvent;
|
||||
let immutableDataPromise: Promise<GameStateEvent> | undefined = undefined;
|
||||
|
||||
interface Room {
|
||||
id: string,
|
||||
mapUrl: string,
|
||||
map: ITiledMap,
|
||||
startLayer: string | null
|
||||
id: string;
|
||||
mapUrl: string;
|
||||
map: ITiledMap;
|
||||
startLayer: string | null;
|
||||
}
|
||||
|
||||
interface User {
|
||||
id: string | undefined,
|
||||
nickName: string | null,
|
||||
tags: string[]
|
||||
id: string | undefined;
|
||||
nickName: string | null;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface TileDescriptor {
|
||||
x: number;
|
||||
y: number;
|
||||
tile: number | string;
|
||||
layer: string;
|
||||
}
|
||||
|
||||
function getGameState(): Promise<GameStateEvent> {
|
||||
if (immutableData) {
|
||||
return Promise.resolve(immutableData);
|
||||
}
|
||||
else {
|
||||
return new Promise<GameStateEvent>((resolver, thrower) => {
|
||||
stateResolvers.subscribe(resolver);
|
||||
sendToWorkadventure({type: "getState", data: null});
|
||||
})
|
||||
if (immutableDataPromise === undefined) {
|
||||
immutableDataPromise = queryWorkadventure({ type: "getState", data: undefined });
|
||||
}
|
||||
return immutableDataPromise;
|
||||
}
|
||||
|
||||
function getDataLayer(): Promise<DataLayerEvent> {
|
||||
return new Promise<DataLayerEvent>((resolver, thrower) => {
|
||||
dataLayerResolver.subscribe(resolver);
|
||||
sendToWorkadventure({type: "getDataLayer", data: null})
|
||||
})
|
||||
sendToWorkadventure({ type: "getDataLayer", data: null });
|
||||
});
|
||||
}
|
||||
|
||||
class WorkadventureRoomCommands extends IframeApiContribution<WorkadventureRoomCommands> {
|
||||
export class WorkadventureRoomCommands extends IframeApiContribution<WorkadventureRoomCommands> {
|
||||
callbacks = [
|
||||
apiCallback({
|
||||
callback: (payloadData: EnterLeaveEvent) => {
|
||||
enterStreams.get(payloadData.name)?.next();
|
||||
},
|
||||
type: "enterEvent",
|
||||
typeChecker: isEnterLeaveEvent
|
||||
typeChecker: isEnterLeaveEvent,
|
||||
}),
|
||||
apiCallback({
|
||||
type: "leaveEvent",
|
||||
typeChecker: isEnterLeaveEvent,
|
||||
callback: (payloadData) => {
|
||||
leaveStreams.get(payloadData.name)?.next();
|
||||
}
|
||||
}),
|
||||
apiCallback({
|
||||
type: "gameState",
|
||||
typeChecker: isGameStateEvent,
|
||||
callback: (payloadData) => {
|
||||
stateResolvers.next(payloadData);
|
||||
}
|
||||
},
|
||||
}),
|
||||
apiCallback({
|
||||
type: "dataLayer",
|
||||
typeChecker: isDataLayerEvent,
|
||||
callback: (payloadData) => {
|
||||
dataLayerResolver.next(payloadData);
|
||||
}
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
onEnterZone(name: string, callback: () => void): void {
|
||||
let subject = enterStreams.get(name);
|
||||
|
@ -90,7 +84,6 @@ class WorkadventureRoomCommands extends IframeApiContribution<WorkadventureRoomC
|
|||
enterStreams.set(name, subject);
|
||||
}
|
||||
subject.subscribe(callback);
|
||||
|
||||
}
|
||||
onLeaveZone(name: string, callback: () => void): void {
|
||||
let subject = leaveStreams.get(name);
|
||||
|
@ -101,35 +94,44 @@ class WorkadventureRoomCommands extends IframeApiContribution<WorkadventureRoomC
|
|||
subject.subscribe(callback);
|
||||
}
|
||||
showLayer(layerName: string): void {
|
||||
sendToWorkadventure({type: 'showLayer', data: {'name': layerName}});
|
||||
sendToWorkadventure({ type: "showLayer", data: { name: layerName } });
|
||||
}
|
||||
hideLayer(layerName: string): void {
|
||||
sendToWorkadventure({type: 'hideLayer', data: {'name': layerName}});
|
||||
sendToWorkadventure({ type: "hideLayer", data: { name: layerName } });
|
||||
}
|
||||
setProperty(layerName: string, propertyName: string, propertyValue: string | number | boolean | undefined): void {
|
||||
sendToWorkadventure({
|
||||
type: 'setProperty',
|
||||
type: "setProperty",
|
||||
data: {
|
||||
'layerName': layerName,
|
||||
'propertyName': propertyName,
|
||||
'propertyValue': propertyValue,
|
||||
}
|
||||
})
|
||||
layerName: layerName,
|
||||
propertyName: propertyName,
|
||||
propertyValue: propertyValue,
|
||||
},
|
||||
});
|
||||
}
|
||||
getCurrentRoom(): Promise<Room> {
|
||||
return getGameState().then((gameState) => {
|
||||
return getDataLayer().then((mapJson) => {
|
||||
return {id: gameState.roomId, map: mapJson.data as ITiledMap, mapUrl: gameState.mapUrl, startLayer: gameState.startLayerName};
|
||||
})
|
||||
})
|
||||
return getDataLayer().then((mapJson) => {
|
||||
return {
|
||||
id: gameState.roomId,
|
||||
map: mapJson.data as ITiledMap,
|
||||
mapUrl: gameState.mapUrl,
|
||||
startLayer: gameState.startLayerName,
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
getCurrentUser(): Promise<User> {
|
||||
return getGameState().then((gameState) => {
|
||||
return {id: gameState.uuid, nickName: gameState.nickname, tags: gameState.tags};
|
||||
})
|
||||
return { id: gameState.uuid, nickName: gameState.nickname, tags: gameState.tags };
|
||||
});
|
||||
}
|
||||
setTiles(tiles: TileDescriptor[]) {
|
||||
sendToWorkadventure({
|
||||
type: "setTiles",
|
||||
data: tiles,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default new WorkadventureRoomCommands();
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
import type { LoadSoundEvent } from '../Events/LoadSoundEvent';
|
||||
import type { PlaySoundEvent } from '../Events/PlaySoundEvent';
|
||||
import type { StopSoundEvent } from '../Events/StopSoundEvent';
|
||||
import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution';
|
||||
import {Sound} from "./Sound/Sound";
|
||||
import type { LoadSoundEvent } from "../Events/LoadSoundEvent";
|
||||
import type { PlaySoundEvent } from "../Events/PlaySoundEvent";
|
||||
import type { StopSoundEvent } from "../Events/StopSoundEvent";
|
||||
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
|
||||
import { Sound } from "./Sound/Sound";
|
||||
|
||||
class WorkadventureSoundCommands extends IframeApiContribution<WorkadventureSoundCommands> {
|
||||
callbacks = []
|
||||
export class WorkadventureSoundCommands extends IframeApiContribution<WorkadventureSoundCommands> {
|
||||
callbacks = [];
|
||||
|
||||
loadSound(url: string): Sound {
|
||||
return new Sound(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default new WorkadventureSoundCommands();
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
import { isButtonClickedEvent } from '../Events/ButtonClickedEvent';
|
||||
import { isMenuItemClickedEvent } from '../Events/ui/MenuItemClickedEvent';
|
||||
import { isMessageReferenceEvent } from '../Events/ui/TriggerMessageEvent';
|
||||
import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution';
|
||||
import { isButtonClickedEvent } from "../Events/ButtonClickedEvent";
|
||||
import { isMenuItemClickedEvent } from "../Events/ui/MenuItemClickedEvent";
|
||||
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
|
||||
import { apiCallback } from "./registeredCallbacks";
|
||||
import type { ButtonClickedCallback, ButtonDescriptor } from "./Ui/ButtonDescriptor";
|
||||
import { Popup } from "./Ui/Popup";
|
||||
import { TriggerMessage, triggerMessageInstance } from './Ui/TriggerMessage';
|
||||
import { TriggerMessage } from "./Ui/TriggerMessage";
|
||||
|
||||
let popupId = 0;
|
||||
const popups: Map<number, Popup> = new Map<number, Popup>();
|
||||
const popupCallbacks: Map<number, Map<number, ButtonClickedCallback>> = new Map<number, Map<number, ButtonClickedCallback>>();
|
||||
const popupCallbacks: Map<number, Map<number, ButtonClickedCallback>> = new Map<
|
||||
number,
|
||||
Map<number, ButtonClickedCallback>
|
||||
>();
|
||||
|
||||
const menuCallbacks: Map<string, (command: string) => void> = new Map()
|
||||
const menuCallbacks: Map<string, (command: string) => void> = new Map();
|
||||
|
||||
class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventureUiCommands> {
|
||||
interface ZonedPopupOptions {
|
||||
zone: string;
|
||||
objectLayerName?: string;
|
||||
popupText: string;
|
||||
delay?: number;
|
||||
popupOptions: Array<ButtonDescriptor>;
|
||||
}
|
||||
|
||||
export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventureUiCommands> {
|
||||
callbacks = [
|
||||
apiCallback({
|
||||
type: "buttonClickedEvent",
|
||||
|
@ -28,28 +37,20 @@ class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventureUiComma
|
|||
if (callback) {
|
||||
callback(popup);
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
apiCallback({
|
||||
type: "menuItemClicked",
|
||||
typeChecker: isMenuItemClickedEvent,
|
||||
callback: event => {
|
||||
callback: (event) => {
|
||||
const callback = menuCallbacks.get(event.menuItem);
|
||||
if (callback) {
|
||||
callback(event.menuItem)
|
||||
callback(event.menuItem);
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
apiCallback({
|
||||
type: "messageTriggered",
|
||||
typeChecker: isMessageReferenceEvent,
|
||||
callback: event => {
|
||||
triggerMessageInstance?.trigger();
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
|
||||
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup {
|
||||
popupId++;
|
||||
const popup = new Popup(popupId);
|
||||
|
@ -67,41 +68,42 @@ class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventureUiComma
|
|||
}
|
||||
|
||||
sendToWorkadventure({
|
||||
'type': 'openPopup',
|
||||
'data': {
|
||||
type: "openPopup",
|
||||
data: {
|
||||
popupId,
|
||||
targetObject,
|
||||
message,
|
||||
buttons: buttons.map((button) => {
|
||||
return {
|
||||
label: button.label,
|
||||
className: button.className
|
||||
className: button.className,
|
||||
};
|
||||
})
|
||||
}
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
popups.set(popupId, popup)
|
||||
popups.set(popupId, popup);
|
||||
return popup;
|
||||
}
|
||||
|
||||
registerMenuCommand(commandDescriptor: string, callback: (commandDescriptor: string) => void) {
|
||||
menuCallbacks.set(commandDescriptor, callback);
|
||||
sendToWorkadventure({
|
||||
'type': 'registerMenuCommand',
|
||||
'data': {
|
||||
menutItem: commandDescriptor
|
||||
}
|
||||
type: "registerMenuCommand",
|
||||
data: {
|
||||
menutItem: commandDescriptor,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
displayBubble(): void {
|
||||
sendToWorkadventure({ 'type': 'displayBubble', data: null });
|
||||
sendToWorkadventure({ type: "displayBubble", data: null });
|
||||
}
|
||||
|
||||
removeBubble(): void {
|
||||
sendToWorkadventure({ 'type': 'removeBubble', data: null });
|
||||
sendToWorkadventure({ type: "removeBubble", data: null });
|
||||
}
|
||||
|
||||
triggerMessage(message: string, callback: () => void): TriggerMessage {
|
||||
return new TriggerMessage(message, callback);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue