Communication bridge to storyteller-frontend (#74)

This PR establishes a rudimentary communication protocol between
Storyteller Studio and the parent application (at fakeyou.com /
storyteller.ai) via the `Window.postMessage` API. An example of how this
protocol can be used from the parent application is here:
https://github.com/storytold/storyteller-frontend/commit/d36e2a9e5c162233d7bd194fdbaa26199bbf6f7a
This commit is contained in:
Danny McGee
2024-03-05 18:04:41 -05:00
committed by GitHub
parent add1e79fea
commit 970f5b25bd
4 changed files with 90 additions and 1 deletions
+1
View File
@@ -1,4 +1,5 @@
export * from "./lib/canvas-provider.element";
export * from "./lib/iframe-messaging-bridge.element";
export * from "./lib/inspector/entity-inspector.element";
export * from "./lib/inspector/inspector.types";
export * from "./lib/loading.element";
@@ -0,0 +1,70 @@
import { on } from "@storyteller/framework";
import * as studio from "@storyteller/studio";
import { SceneState, type SceneStateEvent } from "@storyteller/studio";
import { LitElement, nothing } from "lit";
import { customElement } from "lit/decorators.js";
const PARENT_ORIGIN_WHITELIST: readonly string[] = [
"https://fakeyou.com",
"https://storyteller.ai",
"https://staging.fakeyou.com",
"https://staging.storyteller.ai",
"https://feature-mvp--fakeyou.netlify.app",
"https://feature-mvp--storyteller.netlify.app",
"http://dev.fakeyou.com:7000",
"http://dev.fakeyou.com:7001",
"http://dev.fakeyou.com:7002",
"http://dev.storyteller.com:7000",
"http://dev.storyteller.com:7001",
"http://dev.storyteller.com:7002",
"http://localhost:7000",
"http://localhost:7001",
"http://localhost:7002",
];
@customElement("sts-iframe-messaging-bridge")
export class IframeMessagingBridgeElement extends LitElement {
#parentWindow?: Window;
#parentOrigin?: string;
override connectedCallback(): void {
if (
window.parent
&& window.parent !== window
&& PARENT_ORIGIN_WHITELIST.includes(window.parent.origin)
) {
this.#parentWindow = window.parent;
this.#parentOrigin = window.parent.origin;
}
super.connectedCallback();
}
@on("window:message")
onParentMessage(event: MessageEvent<string>): void {
if (!this.#parentOrigin || event.origin !== this.#parentOrigin)
return;
if (event.data === "save-scene")
studio.saveScene();
}
@on("window:scene-uploaded")
onSceneUploaded(event: CustomEvent<string>): void {
if (!this.#parentWindow || !this.#parentOrigin)
return;
this.#parentWindow.postMessage(`scene-saved:${event.detail}`, this.#parentOrigin);
}
@on("window:scene-state")
onSceneStateChange({ detail: state }: SceneStateEvent): void {
if (!this.#parentWindow || !this.#parentOrigin)
return;
if (state === SceneState.Active)
this.#parentWindow.postMessage("studio-ready", this.#parentOrigin);
}
protected override render = () => nothing;
}