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 {HtmlUtils} from "../WebRtc/HtmlUtils";
import {EnterLeaveEvent} from "./Events/EnterLeaveEvent";
import {isOpenPopupEvent, OpenPopupEvent} from "./Events/OpenPopupEvent";
@ -16,6 +17,9 @@ class IframeListener {
private readonly _chatStream: Subject<ChatEvent> = new Subject();
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 scripts = new Map<string, HTMLIFrameElement>();
@ -36,9 +40,14 @@ class IframeListener {
}
const payload = message.data;
console.log('FOO');
if (isIframeEventWrapper(payload)) {
console.log('FOOBAR', payload);
if (payload.type === 'chat' && isChatEvent(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 DOMElement = Phaser.GameObjects.DOMElement;
import Tween = Phaser.Tweens.Tween;
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
export interface GameSceneInitInterface {
initPosition: PointInterface|null,
@ -165,8 +166,8 @@ export class GameScene extends ResizableScene implements CenterListener {
private openChatIcon!: OpenChatIcon;
private playerName!: 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) {
super({
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
layoutManager.setListener(this);
this.triggerOnMapLayerPropertyChange();
this.listenToIframeEvents();
const camera = this.cameras.main;
@ -655,33 +657,6 @@ export class GameScene extends ResizableScene implements CenterListener {
this.gameMap.onPropertyChange('exitSceneUrl', (newValue, oldValue) => {
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) => {
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) {
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);

View file

@ -24,7 +24,7 @@ export class HtmlUtils {
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 p = document.createElement('p');
p.appendChild(text);

View file

@ -3,12 +3,14 @@ import {isIframeEventWrapper} from "./Api/Events/IframeEvent";
import {isUserInputChatEvent, UserInputChatEvent} from "./Api/Events/UserInputChatEvent";
import {Subject} from "rxjs";
import {EnterLeaveEvent, isEnterLeaveEvent} from "./Api/Events/EnterLeaveEvent";
import {OpenPopupEvent} from "./Api/Events/OpenPopupEvent";
interface WorkAdventureApi {
sendChatMessage(message: string, author: string): void;
onChatMessage(callback: (message: string) => void): void;
onEnterZone(name: string, callback: () => void): void;
onLeaveZone(name: string, callback: () => void): void;
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): number;
}
declare global {
@ -21,6 +23,25 @@ type ChatMessageCallback = (message: string) => void;
const userInputChatStream: Subject<UserInputChatEvent> = new Subject();
const enterStreams: 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 = {
@ -37,6 +58,25 @@ window.WA = {
} 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.
*/

View file

@ -18,3 +18,26 @@ WA.onLeaveZone('myTrigger', () => {
WA.onEnterZone('notExist', () => {
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

@ -20,7 +20,7 @@
"width":10,
"x":0,
"y":0
},
},
{
"data":[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, 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,
@ -38,7 +38,25 @@
"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, 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],
"height":10,
@ -50,20 +68,31 @@
"width":10,
"x":0,
"y":0
},
},
{
"draworder":"topdown",
"id":3,
"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,
"type":"objectgroup",
"visible":true,
"x":0,
"y":0
}],
"nextlayerid":7,
"nextobjectid":1,
"nextlayerid":8,
"nextobjectid":2,
"orientation":"orthogonal",
"properties":[
{
@ -92,4 +121,4 @@
"type":"map",
"version":1.2,
"width":10
}
}