Adding an API for inter-iframe communication
Adds a first version of an API to communicate between an iFrame opened by WorkAdventure and WorkAdventure itself. The first API method is a method allowing to add messages in the chat, from the iFrame. Comes with a test file.
This commit is contained in:
parent
bf8e8bf777
commit
eb93a04341
1
front/dist/.gitignore
vendored
1
front/dist/.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
index.html
|
index.html
|
||||||
|
/js/
|
||||||
|
34
front/src/Api/IframeListener.ts
Normal file
34
front/src/Api/IframeListener.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import {Subject} from "rxjs";
|
||||||
|
|
||||||
|
interface ChatEvent {
|
||||||
|
message: string,
|
||||||
|
author: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens to messages from iframes and turn those messages into easy to use observables.
|
||||||
|
*/
|
||||||
|
class IframeListener {
|
||||||
|
private readonly _chatStream: Subject<ChatEvent> = new Subject();
|
||||||
|
public readonly chatStream = this._chatStream.asObservable();
|
||||||
|
|
||||||
|
init() {
|
||||||
|
window.addEventListener("message", (event) => {
|
||||||
|
// Do we trust the sender of this message?
|
||||||
|
//if (event.origin !== "http://example.com:8080")
|
||||||
|
// return;
|
||||||
|
|
||||||
|
// event.source is window.opener
|
||||||
|
// event.data is the data sent by the iframe
|
||||||
|
|
||||||
|
// FIXME: this is WAAAAAAAY too sloppy as "any" let's us put anything in the message.
|
||||||
|
|
||||||
|
if (event.data.type === 'chat') {
|
||||||
|
this._chatStream.next(event.data.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const iframeListener = new IframeListener();
|
@ -3,6 +3,7 @@ import {mediaManager, ReportCallback, ShowReportCallBack} from "./MediaManager";
|
|||||||
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
|
||||||
import {connectionManager} from "../Connexion/ConnectionManager";
|
import {connectionManager} from "../Connexion/ConnectionManager";
|
||||||
import {GameConnexionTypes} from "../Url/UrlManager";
|
import {GameConnexionTypes} from "../Url/UrlManager";
|
||||||
|
import {iframeListener} from "../Api/IframeListener";
|
||||||
|
|
||||||
export type SendMessageCallback = (message:string) => void;
|
export type SendMessageCallback = (message:string) => void;
|
||||||
|
|
||||||
@ -25,6 +26,11 @@ export class DiscussionManager {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-container');
|
this.mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-container');
|
||||||
this.createDiscussPart(''); //todo: why do we always use empty string?
|
this.createDiscussPart(''); //todo: why do we always use empty string?
|
||||||
|
|
||||||
|
iframeListener.chatStream.subscribe((chatEvent) => {
|
||||||
|
this.addMessage(chatEvent.author, chatEvent.message, false);
|
||||||
|
this.showDiscussion();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private createDiscussPart(name: string) {
|
private createDiscussPart(name: string) {
|
||||||
|
21
front/src/iframe_api.ts
Normal file
21
front/src/iframe_api.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
interface WorkAdventureApi {
|
||||||
|
sendChatMessage(message: string, author: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var WA: WorkAdventureApi;
|
||||||
|
|
||||||
|
window.WA = {
|
||||||
|
/**
|
||||||
|
* Sends a message in the chat.
|
||||||
|
* Only the local user will receive this message.
|
||||||
|
*/
|
||||||
|
sendChatMessage(message: string, author: string) {
|
||||||
|
window.parent.postMessage({
|
||||||
|
'type': 'chat',
|
||||||
|
'data': {
|
||||||
|
'message': message,
|
||||||
|
'author': author
|
||||||
|
}
|
||||||
|
}, '*');
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,8 @@ import {coWebsiteManager} from "./WebRtc/CoWebsiteManager";
|
|||||||
import {MenuScene} from "./Phaser/Menu/MenuScene";
|
import {MenuScene} from "./Phaser/Menu/MenuScene";
|
||||||
import {localUserStore} from "./Connexion/LocalUserStore";
|
import {localUserStore} from "./Connexion/LocalUserStore";
|
||||||
import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene";
|
import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene";
|
||||||
|
import {iframeListener} from "./Api/IframeListener";
|
||||||
|
import {discussionManager} from "./WebRtc/DiscussionManager";
|
||||||
|
|
||||||
// Load Jitsi if the environment variable is set.
|
// Load Jitsi if the environment variable is set.
|
||||||
if (JITSI_URL) {
|
if (JITSI_URL) {
|
||||||
@ -124,3 +126,5 @@ coWebsiteManager.onStateChange(() => {
|
|||||||
const {width, height} = coWebsiteManager.getGameSize();
|
const {width, height} = coWebsiteManager.getGameSize();
|
||||||
game.scale.resize(width / RESOLUTION, height / RESOLUTION);
|
game.scale.resize(width / RESOLUTION, height / RESOLUTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
iframeListener.init();
|
||||||
|
@ -3,7 +3,10 @@ const webpack = require('webpack');
|
|||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: './src/index.ts',
|
entry: {
|
||||||
|
'main': './src/index.ts',
|
||||||
|
'iframe_api': './src/iframe_api.ts'
|
||||||
|
},
|
||||||
devtool: 'inline-source-map',
|
devtool: 'inline-source-map',
|
||||||
devServer: {
|
devServer: {
|
||||||
contentBase: './dist',
|
contentBase: './dist',
|
||||||
@ -29,7 +32,11 @@ module.exports = {
|
|||||||
extensions: [ '.tsx', '.ts', '.js' ],
|
extensions: [ '.tsx', '.ts', '.js' ],
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
filename: '[name].[contenthash].js',
|
filename: (pathData) => {
|
||||||
|
// Add a content hash only for the main bundle.
|
||||||
|
// We want the iframe_api.js file to keep its name as it will be referenced from outside iframes.
|
||||||
|
return pathData.chunk.name === 'main' ? 'js/[name].[contenthash].js': '[name].js';
|
||||||
|
},
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
publicPath: '/'
|
publicPath: '/'
|
||||||
},
|
},
|
||||||
@ -48,7 +55,8 @@ module.exports = {
|
|||||||
removeScriptTypeAttributes: true,
|
removeScriptTypeAttributes: true,
|
||||||
removeStyleLinkTypeAttributes: true,
|
removeStyleLinkTypeAttributes: true,
|
||||||
useShortDoctype: true
|
useShortDoctype: true
|
||||||
}
|
},
|
||||||
|
chunks: ['main']
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
new webpack.ProvidePlugin({
|
new webpack.ProvidePlugin({
|
||||||
|
17
maps/tests/iframe.html
Normal file
17
maps/tests/iframe.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<script src="http://play.workadventure.localhost/iframe_api.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<button id="sendchat">Send chat message</button>
|
||||||
|
<script>
|
||||||
|
document.getElementById('sendchat').onclick = () => {
|
||||||
|
WA.sendChatMessage('Hello world!', 'Mr Robot');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
89
maps/tests/iframe_api.json
Normal file
89
maps/tests/iframe_api.json
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
{ "compressionlevel":-1,
|
||||||
|
"editorsettings":
|
||||||
|
{
|
||||||
|
"export":
|
||||||
|
{
|
||||||
|
"target":"."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"height":10,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
"height":10,
|
||||||
|
"id":1,
|
||||||
|
"name":"floor",
|
||||||
|
"opacity":1,
|
||||||
|
"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,
|
||||||
|
"id":2,
|
||||||
|
"name":"start",
|
||||||
|
"opacity":1,
|
||||||
|
"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, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 0, 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":5,
|
||||||
|
"name":"iframe_api",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"openWebsite",
|
||||||
|
"type":"string",
|
||||||
|
"value":"iframe.html"
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":10,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":3,
|
||||||
|
"name":"floorLayer",
|
||||||
|
"objects":[],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":6,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.3.3",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":11,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"tileset1.png",
|
||||||
|
"imageheight":352,
|
||||||
|
"imagewidth":352,
|
||||||
|
"margin":0,
|
||||||
|
"name":"tileset1",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":121,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":1.2,
|
||||||
|
"width":10
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user