mirror of
https://github.com/wassname/talk.git
synced 2026-07-02 23:39:20 +08:00
Add PostMessage Service
This commit is contained in:
@@ -6,6 +6,7 @@ import { MediaQueryMatchers } from "react-responsive";
|
||||
import { Formatter } from "react-timeago";
|
||||
import { Environment } from "relay-runtime";
|
||||
|
||||
import { PostMessageService } from "talk-framework/lib/postMessage";
|
||||
import { UIContext } from "talk-ui/components";
|
||||
import { ClickFarAwayRegister } from "talk-ui/components/ClickOutside";
|
||||
|
||||
@@ -22,6 +23,9 @@ export interface TalkContext {
|
||||
/** media query values for testing purposes */
|
||||
mediaQueryValues?: MediaQueryMatchers;
|
||||
|
||||
/** postMessage service */
|
||||
postMessage: PostMessageService;
|
||||
|
||||
/**
|
||||
* A way to listen for clicks that are e.g. outside of the
|
||||
* current frame for `ClickOutside`
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ClickFarAwayRegister } from "talk-ui/components/ClickOutside";
|
||||
|
||||
import { generateMessages, LocalesData, negotiateLanguages } from "../i18n";
|
||||
import { fetchQuery } from "../network";
|
||||
import { PostMessageService } from "../postMessage";
|
||||
import { TalkContext } from "./TalkContext";
|
||||
|
||||
interface CreateContextArguments {
|
||||
@@ -99,6 +100,7 @@ export default async function createContext({
|
||||
pym,
|
||||
eventEmitter,
|
||||
registerClickFarAway,
|
||||
postMessage: new PostMessageService(),
|
||||
};
|
||||
|
||||
// Run custom initializations.
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { PostMessageService } from "./postMessage";
|
||||
|
||||
it("post and subscribe to a message", done => {
|
||||
const postMessage = new PostMessageService();
|
||||
const cancel = postMessage.on("test", value => {
|
||||
expect(value).toBe("value");
|
||||
done();
|
||||
cancel();
|
||||
});
|
||||
postMessage.send("test", "value", window);
|
||||
});
|
||||
|
||||
it("send to a different origin", done => {
|
||||
const postMessage = new PostMessageService();
|
||||
const cancelA = postMessage.on("testA", value => {
|
||||
throw new Error("Should not reach this");
|
||||
});
|
||||
const cancelB = postMessage.on("testB", value => {
|
||||
done();
|
||||
cancelA();
|
||||
cancelB();
|
||||
});
|
||||
postMessage.send("testA", "value", window, "http://i-do-not-exist.de");
|
||||
postMessage.send("testB", "value", window);
|
||||
});
|
||||
|
||||
it("should cancel", done => {
|
||||
const postMessage = new PostMessageService();
|
||||
const cancelA = postMessage.on("testA", value => {
|
||||
throw new Error("Should not reach this");
|
||||
});
|
||||
const cancelB = postMessage.on("testB", value => {
|
||||
done();
|
||||
cancelB();
|
||||
});
|
||||
cancelA();
|
||||
postMessage.send("testA", "value", window);
|
||||
postMessage.send("testB", "value", window);
|
||||
});
|
||||
|
||||
it("different scopes are isolated", done => {
|
||||
const postMessageA = new PostMessageService("scopeA");
|
||||
const postMessageB = new PostMessageService("scopeB");
|
||||
const cancelA = postMessageA.on("testA", value => {
|
||||
throw new Error("Should not reach this");
|
||||
});
|
||||
const cancelB = postMessageA.on("testB", value => {
|
||||
done();
|
||||
cancelA();
|
||||
cancelB();
|
||||
});
|
||||
postMessageB.send("testA", "value", window);
|
||||
postMessageA.send("testB", "value", window);
|
||||
});
|
||||
|
||||
it("different message names are isolated", done => {
|
||||
const postMessage = new PostMessageService();
|
||||
const cancelA = postMessage.on("testA", value => {
|
||||
expect(value).toBe("valueA");
|
||||
});
|
||||
const cancelB = postMessage.on("testB", value => {
|
||||
expect(value).toBe("valueB");
|
||||
done();
|
||||
cancelA();
|
||||
cancelB();
|
||||
});
|
||||
postMessage.send("testA", "valueA", window);
|
||||
postMessage.send("testB", "valueB", window);
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
type PostMessageHandler = (value: any, name: string) => void;
|
||||
|
||||
/**
|
||||
* Wrapper around the HTML postMessage API.
|
||||
*/
|
||||
export class PostMessageService {
|
||||
private origin: string;
|
||||
private scope: string;
|
||||
|
||||
constructor(
|
||||
scope = "talk",
|
||||
origin: string = `${location.protocol}//${location.host}`
|
||||
) {
|
||||
this.origin = origin;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message over the postMessage API
|
||||
* @param name string name of the message
|
||||
* @param value string value of the message
|
||||
* @param target Window target window, e.g. window.opener
|
||||
* @param targetOrigin string origin of target
|
||||
*/
|
||||
public send(name: string, value: any, target: Window, targetOrigin?: string) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Serialize the message to be sent via postMessage.
|
||||
const msg = { name, value, scope: this.scope };
|
||||
|
||||
// Send the message.
|
||||
target.postMessage(msg, targetOrigin || this.origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to messages
|
||||
* @param name string Name of the message
|
||||
* @param handler PostMessageHandler
|
||||
*/
|
||||
public on(name: string, handler: PostMessageHandler) {
|
||||
const listener = (event: MessageEvent) => {
|
||||
if (!event.origin) {
|
||||
if (process.env.NODE_ENV !== "test") {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.warn("empty origin received in postMessage", name);
|
||||
}
|
||||
} else if (event.origin !== this.origin) {
|
||||
return;
|
||||
}
|
||||
if (event.data.scope !== this.scope) {
|
||||
return;
|
||||
}
|
||||
if (event.data.name !== name) {
|
||||
return;
|
||||
}
|
||||
handler(event.data.value, event.data.name);
|
||||
};
|
||||
// Attach the listener to the target.
|
||||
window.addEventListener("message", listener);
|
||||
return () => {
|
||||
window.removeEventListener("message", listener);
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user