Implementation of openPopup script method (WIP)

This commit is contained in:
David Négrier 2021-03-09 16:21:14 +01:00
parent ed2ce68f37
commit 1e002f93ed
7 changed files with 184 additions and 36 deletions

View File

@ -0,0 +1,22 @@
import * as tg from "generic-type-guard";
const isButtonDescriptor =
new tg.IsInterface().withProperties({
label: tg.isString,
className: tg.isOptional(tg.isString),
closeOnClick: tg.isOptional(tg.isBoolean)
}).get();
type ButtonDescriptor = tg.GuardedType<typeof isButtonDescriptor>;
export const isOpenPopupEvent =
new tg.IsInterface().withProperties({
popupId: tg.isNumber,
targetObject: tg.isString,
message: tg.isString,
buttons: tg.isAny //tg.isArray<ButtonDescriptor>,
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type OpenPopupEvent = tg.GuardedType<typeof isOpenPopupEvent>;

View File

@ -5,6 +5,7 @@ import {UserInputChatEvent} from "./Events/UserInputChatEvent";
import * as crypto from "crypto"; import * as crypto from "crypto";
import {HtmlUtils} from "../WebRtc/HtmlUtils"; import {HtmlUtils} from "../WebRtc/HtmlUtils";
import {EnterLeaveEvent} from "./Events/EnterLeaveEvent"; import {EnterLeaveEvent} from "./Events/EnterLeaveEvent";
import {isOpenPopupEvent, OpenPopupEvent} from "./Events/OpenPopupEvent";
@ -16,6 +17,9 @@ class IframeListener {
private readonly _chatStream: Subject<ChatEvent> = new Subject(); private readonly _chatStream: Subject<ChatEvent> = new Subject();
public readonly chatStream = this._chatStream.asObservable(); public readonly chatStream = this._chatStream.asObservable();
private readonly _openPopupStream: Subject<OpenPopupEvent> = new Subject();
public readonly openPopupStream = this._openPopupStream.asObservable();
private readonly iframes = new Set<HTMLIFrameElement>(); private readonly iframes = new Set<HTMLIFrameElement>();
private readonly scripts = new Map<string, HTMLIFrameElement>(); private readonly scripts = new Map<string, HTMLIFrameElement>();
@ -36,9 +40,14 @@ class IframeListener {
} }
const payload = message.data; const payload = message.data;
console.log('FOO');
if (isIframeEventWrapper(payload)) { if (isIframeEventWrapper(payload)) {
console.log('FOOBAR', payload);
if (payload.type === 'chat' && isChatEvent(payload.data)) { if (payload.type === 'chat' && isChatEvent(payload.data)) {
this._chatStream.next(payload.data); this._chatStream.next(payload.data);
} else if (payload.type === 'openPopup' && isOpenPopupEvent(payload.data)) {
console.log('OPENPOPUP called');
this._openPopupStream.next(payload.data);
} }
} }

View File

@ -75,6 +75,7 @@ import {localUserStore} from "../../Connexion/LocalUserStore";
import {iframeListener} from "../../Api/IframeListener"; import {iframeListener} from "../../Api/IframeListener";
import DOMElement = Phaser.GameObjects.DOMElement; import DOMElement = Phaser.GameObjects.DOMElement;
import Tween = Phaser.Tweens.Tween; import Tween = Phaser.Tweens.Tween;
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
export interface GameSceneInitInterface { export interface GameSceneInitInterface {
initPosition: PointInterface|null, initPosition: PointInterface|null,
@ -165,8 +166,8 @@ export class GameScene extends ResizableScene implements CenterListener {
private openChatIcon!: OpenChatIcon; private openChatIcon!: OpenChatIcon;
private playerName!: string; private playerName!: string;
private characterLayers!: string[]; private characterLayers!: string[];
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
private popUpElement : DOMElement| undefined;
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) { constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
super({ super({
key: customKey ?? room.id key: customKey ?? room.id
@ -442,6 +443,7 @@ export class GameScene extends ResizableScene implements CenterListener {
// From now, this game scene will be notified of reposition events // From now, this game scene will be notified of reposition events
layoutManager.setListener(this); layoutManager.setListener(this);
this.triggerOnMapLayerPropertyChange(); this.triggerOnMapLayerPropertyChange();
this.listenToIframeEvents();
const camera = this.cameras.main; const camera = this.cameras.main;
@ -655,33 +657,6 @@ export class GameScene extends ResizableScene implements CenterListener {
this.gameMap.onPropertyChange('exitSceneUrl', (newValue, oldValue) => { this.gameMap.onPropertyChange('exitSceneUrl', (newValue, oldValue) => {
if (newValue) this.onMapExit(newValue as string); if (newValue) this.onMapExit(newValue as string);
}); });
this.gameMap.onPropertyChange('inGameConsoleMessage', (newValue, oldValue, allProps) => {
if (newValue !== undefined) {
this.popUpElement?.destroy();
this.popUpElement = this.add.dom(2100, 150).createFromHTML(newValue as string);
this.popUpElement.scale = 0;
this.tweens.add({
targets : this.popUpElement ,
scale : 1,
ease : "EaseOut",
duration : 400,
});
this.popUpElement.setClassName("popUpElement");
} else {
this.tweens.add({
targets : this.popUpElement ,
scale : 0,
ease : "EaseOut",
duration : 400,
onComplete : () => {
this.popUpElement?.destroy();
this.popUpElement = undefined;
},
});
}
});
this.gameMap.onPropertyChange('exitUrl', (newValue, oldValue) => { this.gameMap.onPropertyChange('exitUrl', (newValue, oldValue) => {
if (newValue) this.onMapExit(newValue as string); if (newValue) this.onMapExit(newValue as string);
}); });
@ -765,6 +740,56 @@ export class GameScene extends ResizableScene implements CenterListener {
} }
private listenToIframeEvents(): void {
iframeListener.openPopupStream.subscribe((openPopupEvent) => {
const escapedMessage = HtmlUtils.escapeHtml(openPopupEvent.message);
let html = `<div class="nes-container with-title is-centered">
${escapedMessage}
</div>`;
const domElement = this.add.dom(150, 150).createFromHTML(html);
domElement.scale = 0;
domElement.setClassName('popUpElement');
this.tweens.add({
targets : domElement ,
scale : 1,
ease : "EaseOut",
duration : 400,
});
this.popUpElements.set(openPopupEvent.popupId, domElement);
});
/*this.gameMap.onPropertyChange('inGameConsoleMessage', (newValue, oldValue, allProps) => {
if (newValue !== undefined) {
this.popUpElement?.destroy();
this.popUpElement = this.add.dom(2100, 150).createFromHTML(newValue as string);
this.popUpElement.scale = 0;
this.tweens.add({
targets : this.popUpElement ,
scale : 1,
ease : "EaseOut",
duration : 400,
});
this.popUpElement.setClassName("popUpElement");
} else {
this.tweens.add({
targets : this.popUpElement ,
scale : 0,
ease : "EaseOut",
duration : 400,
onComplete : () => {
this.popUpElement?.destroy();
this.popUpElement = undefined;
},
});
}
});*/
}
private onMapExit(exitKey: string) { private onMapExit(exitKey: string) {
const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance);
if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey); if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey);

View File

@ -24,7 +24,7 @@ export class HtmlUtils {
throw new Error("Cannot find HTML element with id '"+id+"'"); throw new Error("Cannot find HTML element with id '"+id+"'");
} }
private static escapeHtml(html: string): string { public static escapeHtml(html: string): string {
const text = document.createTextNode(html); const text = document.createTextNode(html);
const p = document.createElement('p'); const p = document.createElement('p');
p.appendChild(text); p.appendChild(text);

View File

@ -3,12 +3,14 @@ import {isIframeEventWrapper} from "./Api/Events/IframeEvent";
import {isUserInputChatEvent, UserInputChatEvent} from "./Api/Events/UserInputChatEvent"; import {isUserInputChatEvent, UserInputChatEvent} from "./Api/Events/UserInputChatEvent";
import {Subject} from "rxjs"; import {Subject} from "rxjs";
import {EnterLeaveEvent, isEnterLeaveEvent} from "./Api/Events/EnterLeaveEvent"; import {EnterLeaveEvent, isEnterLeaveEvent} from "./Api/Events/EnterLeaveEvent";
import {OpenPopupEvent} from "./Api/Events/OpenPopupEvent";
interface WorkAdventureApi { interface WorkAdventureApi {
sendChatMessage(message: string, author: string): void; sendChatMessage(message: string, author: string): void;
onChatMessage(callback: (message: string) => void): void; onChatMessage(callback: (message: string) => void): void;
onEnterZone(name: string, callback: () => void): void; onEnterZone(name: string, callback: () => void): void;
onLeaveZone(name: string, callback: () => void): void; onLeaveZone(name: string, callback: () => void): void;
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): number;
} }
declare global { declare global {
@ -21,6 +23,25 @@ type ChatMessageCallback = (message: string) => void;
const userInputChatStream: Subject<UserInputChatEvent> = new Subject(); const userInputChatStream: Subject<UserInputChatEvent> = new Subject();
const enterStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>(); const enterStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
const leaveStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>(); const leaveStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subject<EnterLeaveEvent>>();
let popupId = 0;
interface ButtonDescriptor {
/**
* The label of the button
*/
label: string,
/**
* The type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled"
*/
className?: "normal"|"primary"|"success"|"warning"|"error"|"disabled",
/**
* Callback called if the button is pressed
*/
callback?: () => void,
/**
* If set to true, the popup is closed when the button is clicked
*/
closeOnClick?: boolean
}
window.WA = { window.WA = {
@ -37,6 +58,25 @@ window.WA = {
} as ChatEvent } as ChatEvent
}, '*'); }, '*');
}, },
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): number {
popupId++;
window.parent.postMessage({
'type': 'openPopup',
'data': {
popupId,
targetObject,
message,
buttons: buttons.map((button) => {
return {
label: button.label,
className: button.className,
closeOnClick: button.closeOnClick
};
})
} as OpenPopupEvent
}, '*');
return popupId;
},
/** /**
* Listen to messages sent by the local user, in the chat. * Listen to messages sent by the local user, in the chat.
*/ */

View File

@ -18,3 +18,26 @@ WA.onLeaveZone('myTrigger', () => {
WA.onEnterZone('notExist', () => { WA.onEnterZone('notExist', () => {
WA.sendChatMessage("YOU SHOULD NEVER SEE THIS", 'Poly Parrot'); WA.sendChatMessage("YOU SHOULD NEVER SEE THIS", 'Poly Parrot');
}) })
let popupId;
WA.onEnterZone('popupZone', () => {
popupId = WA.openPopup('foobar', 'This is a test message. Hi!', [
{
label: "Close",
className: "normal",
closeOnClick: true
},
{
label: "Next",
className: "success",
callback: () => {
console.log('BUTTON CLICKED')
}
}
])
})
/*WA.onLeaveZone('popupZone', () => {
WA.sendChatMessage("Thanks!", 'Poly Parrot');
})*/

View File

@ -39,6 +39,24 @@
"x":0, "x":0,
"y":0 "y":0
}, },
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":10,
"id":7,
"name":"popupZone",
"opacity":1,
"properties":[
{
"name":"zone",
"type":"string",
"value":"popupZone"
}],
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{ {
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":10, "height":10,
@ -55,15 +73,26 @@
"draworder":"topdown", "draworder":"topdown",
"id":3, "id":3,
"name":"floorLayer", "name":"floorLayer",
"objects":[], "objects":[
{
"height":120.377012261239,
"id":1,
"name":"myPopup",
"rotation":0,
"type":"",
"visible":true,
"width":162.815914588373,
"x":77.4042872633247,
"y":61.1226958044874
}],
"opacity":1, "opacity":1,
"type":"objectgroup", "type":"objectgroup",
"visible":true, "visible":true,
"x":0, "x":0,
"y":0 "y":0
}], }],
"nextlayerid":7, "nextlayerid":8,
"nextobjectid":1, "nextobjectid":2,
"orientation":"orthogonal", "orientation":"orthogonal",
"properties":[ "properties":[
{ {