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:
David Négrier 2021-03-04 19:00:00 +01:00
parent bf8e8bf777
commit eb93a04341
8 changed files with 184 additions and 4 deletions

View file

@ -1 +1,2 @@
index.html
index.html
/js/

View 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();

View file

@ -3,6 +3,7 @@ import {mediaManager, ReportCallback, ShowReportCallBack} from "./MediaManager";
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {connectionManager} from "../Connexion/ConnectionManager";
import {GameConnexionTypes} from "../Url/UrlManager";
import {iframeListener} from "../Api/IframeListener";
export type SendMessageCallback = (message:string) => void;
@ -25,6 +26,11 @@ export class DiscussionManager {
constructor() {
this.mainContainer = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-container');
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) {

21
front/src/iframe_api.ts Normal file
View 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
}
}, '*');
}
}

View file

@ -14,6 +14,8 @@ import {coWebsiteManager} from "./WebRtc/CoWebsiteManager";
import {MenuScene} from "./Phaser/Menu/MenuScene";
import {localUserStore} from "./Connexion/LocalUserStore";
import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene";
import {iframeListener} from "./Api/IframeListener";
import {discussionManager} from "./WebRtc/DiscussionManager";
// Load Jitsi if the environment variable is set.
if (JITSI_URL) {
@ -124,3 +126,5 @@ coWebsiteManager.onStateChange(() => {
const {width, height} = coWebsiteManager.getGameSize();
game.scale.resize(width / RESOLUTION, height / RESOLUTION);
});
iframeListener.init();

View file

@ -3,7 +3,10 @@ const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.ts',
entry: {
'main': './src/index.ts',
'iframe_api': './src/iframe_api.ts'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
@ -29,7 +32,11 @@ module.exports = {
extensions: [ '.tsx', '.ts', '.js' ],
},
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'),
publicPath: '/'
},
@ -48,7 +55,8 @@ module.exports = {
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}
},
chunks: ['main']
}
),
new webpack.ProvidePlugin({