Merge branch 'develop' of github.com:thecodingmachine/workadventure into main
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import * as tg from "generic-type-guard";
|
||||
|
||||
export const isRectangle = new tg.IsInterface()
|
||||
.withProperties({
|
||||
x: tg.isNumber,
|
||||
y: tg.isNumber,
|
||||
width: tg.isNumber,
|
||||
height: tg.isNumber,
|
||||
})
|
||||
.get();
|
||||
|
||||
export const isEmbeddedWebsiteEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
name: tg.isString,
|
||||
})
|
||||
.withOptionalProperties({
|
||||
url: tg.isString,
|
||||
visible: tg.isBoolean,
|
||||
allowApi: tg.isBoolean,
|
||||
allow: tg.isString,
|
||||
x: tg.isNumber,
|
||||
y: tg.isNumber,
|
||||
width: tg.isNumber,
|
||||
height: tg.isNumber,
|
||||
})
|
||||
.get();
|
||||
|
||||
export const isCreateEmbeddedWebsiteEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
name: tg.isString,
|
||||
url: tg.isString,
|
||||
position: isRectangle,
|
||||
})
|
||||
.withOptionalProperties({
|
||||
visible: tg.isBoolean,
|
||||
allowApi: tg.isBoolean,
|
||||
allow: tg.isString,
|
||||
})
|
||||
.get();
|
||||
|
||||
/**
|
||||
* A message sent from the iFrame to the game to modify an embedded website
|
||||
*/
|
||||
export type ModifyEmbeddedWebsiteEvent = tg.GuardedType<typeof isEmbeddedWebsiteEvent>;
|
||||
|
||||
export type CreateEmbeddedWebsiteEvent = tg.GuardedType<typeof isCreateEmbeddedWebsiteEvent>;
|
||||
// TODO: make a variation that is all optional (except for the name)
|
||||
export type Rectangle = tg.GuardedType<typeof isRectangle>;
|
||||
@@ -9,6 +9,7 @@ import type { OpenCoWebSiteEvent } from "./OpenCoWebSiteEvent";
|
||||
import type { OpenPopupEvent } from "./OpenPopupEvent";
|
||||
import type { OpenTabEvent } from "./OpenTabEvent";
|
||||
import type { UserInputChatEvent } from "./UserInputChatEvent";
|
||||
import type { MapDataEvent } from "./MapDataEvent";
|
||||
import type { LayerEvent } from "./LayerEvent";
|
||||
import type { SetPropertyEvent } from "./setPropertyEvent";
|
||||
import type { LoadSoundEvent } from "./LoadSoundEvent";
|
||||
@@ -21,8 +22,17 @@ import type { SetVariableEvent } from "./SetVariableEvent";
|
||||
import { isGameStateEvent } from "./GameStateEvent";
|
||||
import { isMapDataEvent } from "./MapDataEvent";
|
||||
import { isSetVariableEvent } from "./SetVariableEvent";
|
||||
import type { EmbeddedWebsite } from "../iframe/Room/EmbeddedWebsite";
|
||||
import { isCreateEmbeddedWebsiteEvent } from "./EmbeddedWebsiteEvent";
|
||||
import type { LoadTilesetEvent } from "./LoadTilesetEvent";
|
||||
import { isLoadTilesetEvent } from "./LoadTilesetEvent";
|
||||
import type {
|
||||
MessageReferenceEvent,
|
||||
removeActionMessage,
|
||||
triggerActionMessage,
|
||||
TriggerActionMessageEvent,
|
||||
} from "./ui/TriggerActionMessageEvent";
|
||||
import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent";
|
||||
|
||||
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||
data: T;
|
||||
@@ -55,6 +65,7 @@ export type IframeEventMap = {
|
||||
loadTileset: LoadTilesetEvent;
|
||||
registerMenuCommand: MenuItemRegisterEvent;
|
||||
setTiles: SetTilesEvent;
|
||||
modifyEmbeddedWebsite: Partial<EmbeddedWebsite>; // Note: name should be compulsory in fact
|
||||
};
|
||||
export interface IframeEvent<T extends keyof IframeEventMap> {
|
||||
type: T;
|
||||
@@ -73,6 +84,7 @@ export interface IframeResponseEventMap {
|
||||
hasPlayerMoved: HasPlayerMovedEvent;
|
||||
menuItemClicked: MenuItemClickedEvent;
|
||||
setVariable: SetVariableEvent;
|
||||
messageTriggered: MessageReferenceEvent;
|
||||
}
|
||||
export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> {
|
||||
type: T;
|
||||
@@ -105,6 +117,26 @@ export const iframeQueryMapTypeGuards = {
|
||||
query: isLoadTilesetEvent,
|
||||
answer: tg.isNumber,
|
||||
},
|
||||
triggerActionMessage: {
|
||||
query: isTriggerActionMessageEvent,
|
||||
answer: tg.isUndefined,
|
||||
},
|
||||
removeActionMessage: {
|
||||
query: isMessageReferenceEvent,
|
||||
answer: tg.isUndefined,
|
||||
},
|
||||
getEmbeddedWebsite: {
|
||||
query: tg.isString,
|
||||
answer: isCreateEmbeddedWebsiteEvent,
|
||||
},
|
||||
deleteEmbeddedWebsite: {
|
||||
query: tg.isString,
|
||||
answer: tg.isUndefined,
|
||||
},
|
||||
createEmbeddedWebsite: {
|
||||
query: isCreateEmbeddedWebsiteEvent,
|
||||
answer: tg.isUndefined,
|
||||
},
|
||||
};
|
||||
|
||||
type GuardedType<T> = T extends (x: unknown) => x is infer T ? T : never;
|
||||
@@ -141,7 +173,12 @@ export const isIframeQuery = (event: any): event is IframeQuery<keyof IframeQuer
|
||||
if (!isIframeQueryKey(type)) {
|
||||
return false;
|
||||
}
|
||||
return iframeQueryMapTypeGuards[type].query(event.data);
|
||||
|
||||
const result = iframeQueryMapTypeGuards[type].query(event.data);
|
||||
if (!result) {
|
||||
console.warn('Received a query with type "' + type + '" but the payload is invalid.');
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import * as tg from "generic-type-guard";
|
||||
|
||||
export const triggerActionMessage = "triggerActionMessage";
|
||||
export const removeActionMessage = "removeActionMessage";
|
||||
|
||||
export const isActionMessageType = tg.isSingletonStringUnion("message", "warning");
|
||||
|
||||
export type ActionMessageType = tg.GuardedType<typeof isActionMessageType>;
|
||||
|
||||
export const isTriggerActionMessageEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
message: tg.isString,
|
||||
uuid: tg.isString,
|
||||
type: isActionMessageType,
|
||||
})
|
||||
.get();
|
||||
|
||||
export type TriggerActionMessageEvent = tg.GuardedType<typeof isTriggerActionMessageEvent>;
|
||||
|
||||
export const isMessageReferenceEvent = new tg.IsInterface()
|
||||
.withProperties({
|
||||
uuid: tg.isString,
|
||||
})
|
||||
.get();
|
||||
|
||||
export type MessageReferenceEvent = tg.GuardedType<typeof isMessageReferenceEvent>;
|
||||
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
isMessageReferenceEvent,
|
||||
isTriggerActionMessageEvent,
|
||||
removeActionMessage,
|
||||
triggerActionMessage,
|
||||
} from './TriggerActionMessageEvent';
|
||||
|
||||
import * as tg from 'generic-type-guard';
|
||||
|
||||
const isTriggerMessageEventObject = new tg.IsInterface()
|
||||
.withProperties({
|
||||
type: tg.isSingletonString(triggerActionMessage),
|
||||
data: isTriggerActionMessageEvent,
|
||||
})
|
||||
.get();
|
||||
|
||||
const isTriggerMessageRemoveEventObject = new tg.IsInterface()
|
||||
.withProperties({
|
||||
type: tg.isSingletonString(removeActionMessage),
|
||||
data: isMessageReferenceEvent,
|
||||
})
|
||||
.get();
|
||||
|
||||
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";
|
||||
@@ -31,6 +32,8 @@ import { isLoadPageEvent } from "./Events/LoadPageEvent";
|
||||
import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from "./Events/ui/MenuItemRegisterEvent";
|
||||
import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent";
|
||||
import type { SetVariableEvent } from "./Events/SetVariableEvent";
|
||||
import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent";
|
||||
import { EmbeddedWebsite } from "./iframe/Room/EmbeddedWebsite";
|
||||
|
||||
type AnswererCallback<T extends keyof IframeQueryMap> = (
|
||||
query: IframeQueryMap[T]["query"],
|
||||
@@ -111,6 +114,9 @@ class IframeListener {
|
||||
private readonly _setTilesStream: Subject<SetTilesEvent> = new Subject();
|
||||
public readonly setTilesStream = this._setTilesStream.asObservable();
|
||||
|
||||
private readonly _modifyEmbeddedWebsiteStream: Subject<ModifyEmbeddedWebsiteEvent> = new Subject();
|
||||
public readonly modifyEmbeddedWebsiteStream = this._modifyEmbeddedWebsiteStream.asObservable();
|
||||
|
||||
private readonly iframes = new Set<HTMLIFrameElement>();
|
||||
private readonly iframeCloseCallbacks = new Map<HTMLIFrameElement, (() => void)[]>();
|
||||
private readonly scripts = new Map<string, HTMLIFrameElement>();
|
||||
@@ -124,7 +130,7 @@ class IframeListener {
|
||||
init() {
|
||||
window.addEventListener(
|
||||
"message",
|
||||
(message: TypedMessageEvent<IframeEvent<keyof IframeEventMap>>) => {
|
||||
(message: MessageEvent<unknown>) => {
|
||||
// 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).
|
||||
@@ -266,6 +272,8 @@ class IframeListener {
|
||||
handleMenuItemRegistrationEvent(payload.data);
|
||||
} else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) {
|
||||
this._setTilesStream.next(payload.data);
|
||||
} else if (payload.type == "modifyEmbeddedWebsite" && isEmbeddedWebsiteEvent(payload.data)) {
|
||||
this._modifyEmbeddedWebsiteStream.next(payload.data);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -423,6 +431,15 @@ class IframeListener {
|
||||
});
|
||||
}
|
||||
|
||||
sendActionMessageTriggered(uuid: string): void {
|
||||
this.postMessage({
|
||||
type: "messageTriggered",
|
||||
data: {
|
||||
uuid,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the message... to all allowed iframes.
|
||||
*/
|
||||
|
||||
@@ -1,51 +1,66 @@
|
||||
import type * as tg from "generic-type-guard";
|
||||
import type {
|
||||
IframeEvent,
|
||||
IframeEventMap, IframeQuery,
|
||||
IframeEventMap,
|
||||
IframeQuery,
|
||||
IframeQueryMap,
|
||||
IframeResponseEventMap
|
||||
} from '../Events/IframeEvent';
|
||||
import type {IframeQueryWrapper} from "../Events/IframeEvent";
|
||||
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, "*");
|
||||
}
|
||||
|
||||
let queryNumber = 0;
|
||||
|
||||
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 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>, "*");
|
||||
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
|
||||
reject,
|
||||
});
|
||||
|
||||
queryNumber++;
|
||||
});
|
||||
}
|
||||
|
||||
type GuardedType<Guard extends tg.TypeGuard<unknown>> = Guard extends tg.TypeGuard<infer T> ? T : never
|
||||
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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,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"];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { sendToWorkadventure } from "../IframeApiContribution";
|
||||
import type {
|
||||
CreateEmbeddedWebsiteEvent,
|
||||
ModifyEmbeddedWebsiteEvent,
|
||||
Rectangle,
|
||||
} from "../../Events/EmbeddedWebsiteEvent";
|
||||
|
||||
export class EmbeddedWebsite {
|
||||
public readonly name: string;
|
||||
private _url: string;
|
||||
private _visible: boolean;
|
||||
private _allow: string;
|
||||
private _allowApi: boolean;
|
||||
private _position: Rectangle;
|
||||
|
||||
constructor(private config: CreateEmbeddedWebsiteEvent) {
|
||||
this.name = config.name;
|
||||
this._url = config.url;
|
||||
this._visible = config.visible ?? true;
|
||||
this._allow = config.allow ?? "";
|
||||
this._allowApi = config.allowApi ?? false;
|
||||
this._position = config.position;
|
||||
}
|
||||
|
||||
public set url(url: string) {
|
||||
this._url = url;
|
||||
sendToWorkadventure({
|
||||
type: "modifyEmbeddedWebsite",
|
||||
data: {
|
||||
name: this.name,
|
||||
url: this._url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public set visible(visible: boolean) {
|
||||
this._visible = visible;
|
||||
sendToWorkadventure({
|
||||
type: "modifyEmbeddedWebsite",
|
||||
data: {
|
||||
name: this.name,
|
||||
visible: this._visible,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public set x(x: number) {
|
||||
this._position.x = x;
|
||||
sendToWorkadventure({
|
||||
type: "modifyEmbeddedWebsite",
|
||||
data: {
|
||||
name: this.name,
|
||||
x: this._position.x,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public set y(y: number) {
|
||||
this._position.y = y;
|
||||
sendToWorkadventure({
|
||||
type: "modifyEmbeddedWebsite",
|
||||
data: {
|
||||
name: this.name,
|
||||
y: this._position.y,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public set width(width: number) {
|
||||
this._position.width = width;
|
||||
sendToWorkadventure({
|
||||
type: "modifyEmbeddedWebsite",
|
||||
data: {
|
||||
name: this.name,
|
||||
width: this._position.width,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public set height(height: number) {
|
||||
this._position.height = height;
|
||||
sendToWorkadventure({
|
||||
type: "modifyEmbeddedWebsite",
|
||||
data: {
|
||||
name: this.name,
|
||||
height: this._position.height,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
ActionMessageType,
|
||||
MessageReferenceEvent,
|
||||
removeActionMessage,
|
||||
triggerActionMessage,
|
||||
TriggerActionMessageEvent,
|
||||
} from "../../Events/ui/TriggerActionMessageEvent";
|
||||
import { queryWorkadventure } from "../IframeApiContribution";
|
||||
import type { ActionMessageOptions } from "../ui";
|
||||
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 v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
export class ActionMessage {
|
||||
public readonly uuid: string;
|
||||
private readonly type: ActionMessageType;
|
||||
private readonly message: string;
|
||||
private readonly callback: () => void;
|
||||
|
||||
constructor(actionMessageOptions: ActionMessageOptions, private onRemove: () => void) {
|
||||
this.uuid = uuidv4();
|
||||
this.message = actionMessageOptions.message;
|
||||
this.type = actionMessageOptions.type ?? "message";
|
||||
this.callback = actionMessageOptions.callback;
|
||||
this.create();
|
||||
}
|
||||
|
||||
private async create() {
|
||||
await queryWorkadventure({
|
||||
type: triggerActionMessage,
|
||||
data: {
|
||||
message: this.message,
|
||||
type: this.type,
|
||||
uuid: this.uuid,
|
||||
} as TriggerActionMessageEvent,
|
||||
});
|
||||
}
|
||||
|
||||
async remove() {
|
||||
await queryWorkadventure({
|
||||
type: removeActionMessage,
|
||||
data: {
|
||||
uuid: this.uuid,
|
||||
} as MessageReferenceEvent,
|
||||
});
|
||||
this.onRemove();
|
||||
}
|
||||
|
||||
triggerCallback() {
|
||||
this.callback();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import { IframeApiContribution, queryWorkadventure, sendToWorkadventure } from "
|
||||
import { apiCallback } from "./registeredCallbacks";
|
||||
|
||||
import type { ITiledMap } from "../../Phaser/Map/ITiledMap";
|
||||
import type { WorkadventureRoomWebsiteCommands } from "./website";
|
||||
import website from "./website";
|
||||
|
||||
const enterStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
|
||||
const leaveStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
|
||||
@@ -105,6 +107,7 @@ export class WorkadventureRoomCommands extends IframeApiContribution<Workadventu
|
||||
}
|
||||
return mapURL;
|
||||
}
|
||||
|
||||
async loadTileset(url: string): Promise<number> {
|
||||
return await queryWorkadventure({
|
||||
type: "loadTileset",
|
||||
@@ -113,6 +116,10 @@ export class WorkadventureRoomCommands extends IframeApiContribution<Workadventu
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
get website(): WorkadventureRoomWebsiteCommands {
|
||||
return website;
|
||||
}
|
||||
}
|
||||
|
||||
export default new WorkadventureRoomCommands();
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { isButtonClickedEvent } from "../Events/ButtonClickedEvent";
|
||||
import { isMenuItemClickedEvent } from "../Events/ui/MenuItemClickedEvent";
|
||||
import type { MenuItemRegisterEvent } from "../Events/ui/MenuItemRegisterEvent";
|
||||
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
|
||||
import { apiCallback } from "./registeredCallbacks";
|
||||
import type { ButtonClickedCallback, ButtonDescriptor } from "./Ui/ButtonDescriptor";
|
||||
import { Popup } from "./Ui/Popup";
|
||||
import { ActionMessage } from "./Ui/ActionMessage";
|
||||
import { isMessageReferenceEvent } from "../Events/ui/TriggerActionMessageEvent";
|
||||
|
||||
let popupId = 0;
|
||||
const popups: Map<number, Popup> = new Map<number, Popup>();
|
||||
@@ -14,6 +15,7 @@ const popupCallbacks: Map<number, Map<number, ButtonClickedCallback>> = new Map<
|
||||
>();
|
||||
|
||||
const menuCallbacks: Map<string, (command: string) => void> = new Map();
|
||||
const actionMessages = new Map<string, ActionMessage>();
|
||||
|
||||
interface ZonedPopupOptions {
|
||||
zone: string;
|
||||
@@ -23,6 +25,12 @@ interface ZonedPopupOptions {
|
||||
popupOptions: Array<ButtonDescriptor>;
|
||||
}
|
||||
|
||||
export interface ActionMessageOptions {
|
||||
message: string;
|
||||
type?: "message" | "warning";
|
||||
callback: () => void;
|
||||
}
|
||||
|
||||
export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventureUiCommands> {
|
||||
callbacks = [
|
||||
apiCallback({
|
||||
@@ -50,6 +58,16 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
|
||||
}
|
||||
},
|
||||
}),
|
||||
apiCallback({
|
||||
type: "messageTriggered",
|
||||
typeChecker: isMessageReferenceEvent,
|
||||
callback: (event) => {
|
||||
const actionMessage = actionMessages.get(event.uuid);
|
||||
if (actionMessage) {
|
||||
actionMessage.triggerCallback();
|
||||
}
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[], input: boolean = false): Popup {
|
||||
@@ -105,6 +123,14 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
|
||||
removeBubble(): void {
|
||||
sendToWorkadventure({ type: "removeBubble", data: null });
|
||||
}
|
||||
|
||||
displayActionMessage(actionMessageOptions: ActionMessageOptions): ActionMessage {
|
||||
const actionMessage = new ActionMessage(actionMessageOptions, () => {
|
||||
actionMessages.delete(actionMessage.uuid);
|
||||
});
|
||||
actionMessages.set(actionMessage.uuid, actionMessage);
|
||||
return actionMessage;
|
||||
}
|
||||
}
|
||||
|
||||
export default new WorkAdventureUiCommands();
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { LoadSoundEvent } from "../Events/LoadSoundEvent";
|
||||
import type { PlaySoundEvent } from "../Events/PlaySoundEvent";
|
||||
import type { StopSoundEvent } from "../Events/StopSoundEvent";
|
||||
import { IframeApiContribution, queryWorkadventure, sendToWorkadventure } from "./IframeApiContribution";
|
||||
import { Sound } from "./Sound/Sound";
|
||||
import { EmbeddedWebsite } from "./Room/EmbeddedWebsite";
|
||||
import type { CreateEmbeddedWebsiteEvent } from "../Events/EmbeddedWebsiteEvent";
|
||||
|
||||
export class WorkadventureRoomWebsiteCommands extends IframeApiContribution<WorkadventureRoomWebsiteCommands> {
|
||||
callbacks = [];
|
||||
|
||||
async get(objectName: string): Promise<EmbeddedWebsite> {
|
||||
const websiteEvent = await queryWorkadventure({
|
||||
type: "getEmbeddedWebsite",
|
||||
data: objectName,
|
||||
});
|
||||
return new EmbeddedWebsite(websiteEvent);
|
||||
}
|
||||
|
||||
create(createEmbeddedWebsiteEvent: CreateEmbeddedWebsiteEvent): EmbeddedWebsite {
|
||||
queryWorkadventure({
|
||||
type: "createEmbeddedWebsite",
|
||||
data: createEmbeddedWebsiteEvent,
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
return new EmbeddedWebsite(createEmbeddedWebsiteEvent);
|
||||
}
|
||||
|
||||
async delete(objectName: string): Promise<void> {
|
||||
return await queryWorkadventure({
|
||||
type: "deleteEmbeddedWebsite",
|
||||
data: objectName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new WorkadventureRoomWebsiteCommands();
|
||||
Reference in New Issue
Block a user