Skip to content

Commit

Permalink
Documentation and tidy up
Browse files Browse the repository at this point in the history
  • Loading branch information
johnman committed Apr 23, 2024
1 parent 7171f1b commit 7aec63c
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 45 deletions.
160 changes: 159 additions & 1 deletion how-to/web-interop-basic/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,161 @@
![OpenFin Web Interop Basic Example](../../assets/openfin-web-starter.png)

---
> **_:information_source: OpenFin:_** [OpenFin](https://www.openfin.co/) libraries are a commercial product and this repo is for evaluation purposes. Use of the OpenFin npm packages is only granted pursuant to a license from OpenFin. Please [**contact us**](https://www.openfin.co/contact/) if you would like to request a developer evaluation key or to discuss a production license.
# OpenFin Web Interop Basic

This is a very basic example that has a simple provider web page that acts as the main/index page. This page wires up the interop broker using the [@openfin/web-interop](https://www.npmjs.com/package/@openfin/web-interop) library.

This page has a very simple layout which is made up of two iframes:

- An FDC3 View - This uses the FDC3 API to add a context listener and to broadcast a hardcoded context object.
- An Interop View - This uses the OpenFin Interop API to add a context listener and to set context using a hardcoded context object.

![OpenFin Web Interop Basic Example](./docs/web-interop-basic.png)

## Getting Started

1. Install dependencies and do the initial build. Note that these examples assume you are in the sub-directory for the example.

```shell
npm install
```

2. Build the example.

```shell
npm run build
```

3. Start the test server in a new window.

```shell
npm run start
```

4. Launch the sample in your default desktop browser (or copy <http://localhost:6060/platform/provider.html> into your Desktop Browser).

```shell
npm run client
```

## Setup Notes

There are a few things to note before trying to use @openfin/web-interop:

- This current release requires Buffer support and this is added through the [buffer](https://www.npmjs.com/package/buffer) npm package. We have added this to the npm package and we have made it available through a [buffer util TypeScript file](./client/src/util/buffer.ts). _This is a requirement that will be removed in the future_.
- If your [tsconfig](./client/tsconfig.json) file is using **node** for moduleResolution it will need to use **Node16** instead as export/imports are defined in the package.json of the @openfin/web-interop npm package. This is required for when you try to import @openfin/web-interop/iframe-broker.
- You will need to copy the shared-worker.js file from the [@openfin/web-interop](https://www.npmjs.com/package/@openfin/web-interop) npm package to your public folder. We have created a [copy-shared-worker.js](./scripts/copy-shared-worker.js) script to do this and it is referenced in the build-client npm command.

## How things are structured

### Host

The host is the entry point and it is the page that gets loaded into the Chrome/Edge/Safari/Firefox tab.

It has a responsibility to create a connection providing a broker url and then initializing the broker providing an id (**this id will be needed by your content when it wishes to connect**).

In the sample we use a [settings](./client/src/platform/settings.ts) file but this has been removed from the snippet to simplify the code snippet.

```javascript
import { connect } from "@openfin/web-interop";
import "./util/buffer";

/**
* Initializes the OpenFin Web Broker connection.
*/
async function init(): Promise<void> {
const settings = await getSettings();
// Connect to the OpenFin Web Broker.
const fin = await connect({ options: { brokerUrl: "http://localhost:6060/platform/iframe-broker.html" } });

// You may now use the `fin` object. This step is important as it initializes the interop broker.
await fin.Interop.init("web-interop-basic");
}
```

The host html page [provider.html](./public/platform/provider.html) then:

- imports this code and initializes it.
- brings in required content through an iframe.

### IFrame Broker

This is the iframe that is referenced by the Host and Content Providers and it is how they communicate with each other. The iframe broker html page and the shared-webworker.js file have to reside on the same domain as the **host**.

The [iframe broker html page](./public/platform/iframe-broker.html) uses the shared-webworker.js file that comes as part of the [@openfin/web-interop](https://www.npmjs.com/package/@openfin/web-interop) npm package.

The iframe broker needs some initialization logic as well.

```javascript
import { init as initBrokerConnection } from "@openfin/web-interop/iframe-broker";

/**
* Initializes the OpenFin Web Broker connection.
* @returns A promise that resolves when the connection is established.
*/
async function init(): Promise<void> {
// The shared worker is copied and renamed to the public/js directory from the @openfin/web-interop package
// using the scripts/copy-shared-worker.js file that is called when npm run build is called.
return initBrokerConnection({
sharedWorkerUrl: "http://localhost:6060/js/shared-worker.bundle.js"
});
}
```

### Content

Content refers to content that is framed within an iframe on the **host** html page. It establishes a connection to the **host** through the **iframe broker** via some initialization code.

Some things to note about the content provider setup:

- Content imports an init function from the [api.ts](./client/src/platform/api.ts) that creates the connection and assigns the window.fin and window.fdc3 APIs if they do not exist.
- You do not need to assign fdc3 or fin to the window object but we have done so for consistency with our workspace and container starter examples.
- The snippet below is the init function from the [api.ts](./client/src/platform/api.ts) file (although the settings function has been replaced with hard coded values for simplicity) that is imported and called.
- Content initializes the API and then runs code normally like it would inside of a workspace platform or container platform.
- the **finReady** event shown below is an example and doesn't exist in the OpenFin container as the API is injected into the document. We added **finReady** to have similar behavior to the **fdc3Ready** event that we also raise.

```javascript
import { connect } from "@openfin/web-interop";
import "../util/buffer";

/**
* Initializes the OpenFin Web Broker connection.
*/
export async function init(): Promise<void> {
// Set window.fin to the `fin` object if needed.
if (window.fin === undefined) {
const settings = await getSettings();
// Specify an interopConfig with a specific provider ID and a context group to initialize the `fin.me.interop` client on connection.
window.fin = await connect({
options: {
brokerUrl: "http://localhost:6060/platform/iframe-broker.html",
interopConfig: {
providerId: "web-interop-basic",
currentContextGroup: "green"
}
}
});
console.log("Finished initializing the fin API.");
// Create and dispatch the finReady event
const event = new CustomEvent("finReady");
window.dispatchEvent(event);
}

if (window.fdc3 === undefined && window?.fin?.me.interop?.getFDC3Sync !== undefined) {
window.fdc3 = fin.me.interop.getFDC3Sync("2.0");
console.log("Finished initializing the fdc3 API.");
// Create and dispatch the FDC3Ready event
const event = new CustomEvent("fdc3Ready");
window.dispatchEvent(event);
}
}
```
## A visual representation
We've covered the key pieces. We have a host, one or more pieces of content and a common iframe broker html page that is used to tie them altogether.
This diagram is here to provide a rough visual guide to support the content above and the example:
![OpenFin Web Interop Basic Rough Visual Guide](./docs/web-interop-basic-visualization.png)
37 changes: 20 additions & 17 deletions how-to/web-interop-basic/client/src/content/fdc3-view.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Context } from "@finos/fdc3";
import "../platform/api";
import { init } from "../platform/api";

window.addEventListener("DOMContentLoaded", async () => {
await init();
await initializeDOM();
});

Expand Down Expand Up @@ -30,22 +31,6 @@ async function broadcastContext(): Promise<void> {
}
}

/**
* Updates the DOM elements with the provided context.
* @param context The context to update the DOM elements with.
*/
function updateDOMElements(context: Context): void {
const contextTypeSpan = document.querySelector<HTMLSpanElement>("#contextType");
const contextNameSpan = document.querySelector<HTMLSpanElement>("#contextName");
const contextBodyPre = document.querySelector<HTMLPreElement>("#contextBody");

if (contextTypeSpan !== null && contextNameSpan !== null && contextBodyPre !== null) {
contextTypeSpan.textContent = context.type;
contextNameSpan.textContent = context.name ?? "No name provided.";
contextBodyPre.textContent = JSON.stringify(context, null, 2);
}
}

/**
* Adds an FDC3 context listener to the window.
*/
Expand All @@ -62,6 +47,23 @@ async function addFDC3Listener(): Promise<void> {
});
}
}

/**
* Updates the DOM elements with the provided context.
* @param context The context to update the DOM elements with.
*/
function updateDOMElements(context: Context): void {
const contextTypeSpan = document.querySelector<HTMLSpanElement>("#contextType");
const contextNameSpan = document.querySelector<HTMLSpanElement>("#contextName");
const contextBodyPre = document.querySelector<HTMLPreElement>("#contextBody");

if (contextTypeSpan !== null && contextNameSpan !== null && contextBodyPre !== null) {
contextTypeSpan.textContent = context.type;
contextNameSpan.textContent = context.name ?? "No name provided.";
contextBodyPre.textContent = JSON.stringify(context, null, 2);
}
}

/**
* Initialize the DOM elements.
*/
Expand All @@ -73,5 +75,6 @@ async function initializeDOM(): Promise<void> {
await broadcastContext();
});
}

await addFDC3Listener();
}
37 changes: 20 additions & 17 deletions how-to/web-interop-basic/client/src/content/interop-view.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Context } from "@finos/fdc3";
import "../platform/api";
import { init } from "../platform/api";

window.addEventListener("DOMContentLoaded", async () => {
await init();
await initializeDOM();
});

Expand Down Expand Up @@ -30,22 +31,6 @@ async function setContext(): Promise<void> {
}
}

/**
* Updates the DOM elements with the provided context.
* @param context The context to update the DOM elements with.
*/
function updateDOMElements(context: Context): void {
const contextTypeSpan = document.querySelector<HTMLSpanElement>("#contextType");
const contextNameSpan = document.querySelector<HTMLSpanElement>("#contextName");
const contextBodyPre = document.querySelector<HTMLPreElement>("#contextBody");

if (contextTypeSpan !== null && contextNameSpan !== null && contextBodyPre !== null) {
contextTypeSpan.textContent = context.type;
contextNameSpan.textContent = context.name ?? "No name provided.";
contextBodyPre.textContent = JSON.stringify(context, null, 2);
}
}

/**
* Adds an interop context listener to the window.
*/
Expand All @@ -62,6 +47,23 @@ async function addContextListener(): Promise<void> {
});
}
}

/**
* Updates the DOM elements with the provided context.
* @param context The context to update the DOM elements with.
*/
function updateDOMElements(context: Context): void {
const contextTypeSpan = document.querySelector<HTMLSpanElement>("#contextType");
const contextNameSpan = document.querySelector<HTMLSpanElement>("#contextName");
const contextBodyPre = document.querySelector<HTMLPreElement>("#contextBody");

if (contextTypeSpan !== null && contextNameSpan !== null && contextBodyPre !== null) {
contextTypeSpan.textContent = context.type;
contextNameSpan.textContent = context.name ?? "No name provided.";
contextBodyPre.textContent = JSON.stringify(context, null, 2);
}
}

/**
* Initialize the DOM elements.
*/
Expand All @@ -73,5 +75,6 @@ async function initializeDOM(): Promise<void> {
await setContext();
});
}

await addContextListener();
}
12 changes: 4 additions & 8 deletions how-to/web-interop-basic/client/src/platform/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getSettings } from "./settings";
/**
* Initializes the OpenFin Web Broker connection.
*/
async function init(): Promise<void> {
export async function init(): Promise<void> {
// Set window.fin to the `fin` object.
if (window.fin === undefined) {
const settings = await getSettings();
Expand All @@ -19,21 +19,17 @@ async function init(): Promise<void> {
}
}
});
console.log("Finished initializing the fin API.");
// Create and dispatch the finReady event
const event = new CustomEvent("finReady");
window.dispatchEvent(event);
}

if (window.fdc3 === undefined && window?.fin?.me.interop?.getFDC3Sync !== undefined) {
window.fdc3 = fin.me.interop.getFDC3Sync("2.0");
console.log("Finished initializing the fdc3 API.");
// Create and dispatch the FDC3Ready event
const event = new CustomEvent("fdc3Ready");
window.dispatchEvent(event);
}
}

init()
.then(() => {
console.log("Connected to the OpenFin Web Broker.");
return true;
})
.catch((err) => console.error(err));
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions how-to/web-interop-basic/public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Web Interop Basic",
"short_name": "WebInteropBasic",
"name": "OpenFin Web Interop Basic",
"short_name": "OpenFinWebInteropBasic",
"start_url": "./platform/provider.html",
"display": "standalone",
"background_color": "#fff",
Expand Down
1 change: 1 addition & 0 deletions how-to/web-interop-basic/public/platform/provider.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../common/style/app.css" />
<script defer="defer" src="../js/provider.bundle.js"></script>
<link rel="manifest" href="../manifest.json">
</head>
<body class="col fill gap20">
<header class="row spread middle">
Expand Down

0 comments on commit 7aec63c

Please sign in to comment.