Recipes - Platform

Standard recipes for Platform API Use Cases. These examples assume a basic understanding of OpenFin Platforms, so please see the Platforms Getting Started page first. Many of these recipes can be seen in action in the Platform seed.

🚧

Requirements

  • OpenFin CLI installed
  • OpenFin Runtime 16.83.53.26+
  • Windows Operating System | Mac not fully supported and may result in unexpected behavior

Fixed or "Locked" Layouts

Use Case

Within a Layout, lock a View in place so an end user cannot rearrange the Views.

Description
Locking Views in place can be achieved by setting the reorderEnabled property in the LayoutSettings object to false. Additionally, the tabs or "header" section of the Layout can be removed by setting the hasHeaders parameter to false.

You can toggle between these states by using the code below. Please note that the headerHeight is set to 0 when hasHeaders is false.

createLockedWindow.js tab is an example using the Platform API to create a window with a "locked" Layout without headers.

toggleLockedLayout.js tab is an example javascript code to toggle between a locked and unlocked layout. This should be run in the window context.

lockedEventing.js tab is an example of eventing based on "locked" Layout state. This should be run in the window context.

For more information about manifest files, see Manifest settings.

Example

let platform = await fin.Platform.getCurrent();
let myWinIdentity = await platform.createWindow({
    contextMenu: true,
    layout: {
        settings: {
           hasHeaders: false,
           reorderEnabled: false
        },
        content: [{
            type: 'stack',
            content:[{
                type: 'component',
                componentName: 'view',
                componentState: {
                    name: 'my-new-test-view',
                    url: 'http://www.example.com' // The URL of the View
                }
            }]
        }]
    }
});
async toggleLockedLayout () {
    const oldLayout = await fin.Platform.Layout.getCurrentSync().getConfig();
    const { settings, dimensions } = oldLayout;
    if(settings.hasHeaders && settings.reorderEnabled) {
        fin.Platform.Layout.getCurrentSync().replace({
            ...oldLayout,
            settings: {
                ...settings,
                hasHeaders: false,
                reorderEnabled: false
            }
        });
    } else {
        fin.Platform.Layout.getCurrentSync().replace({
            ...oldLayout,
            settings: {
                ...settings,
                hasHeaders: true,
                reorderEnabled: true
            },
            dimensions: {
                ...dimensions,
                headerHeight: 25 // please insert your default headerHeight here
            }
        });
    }
};
fin.me.on('layout-ready', async () => {
    // Whenever a new layout is ready on this window (on init, replace, or applyPreset)
    const { settings } = await fin.Platform.Layout.getCurrentSync().getConfig();
    // determine whether it is locked and update an icon
    const lockIcon = document.getElementById('lock-button');
    if(settings.hasHeaders && settings.reorderEnabled) {
        lockIcon.classList.remove('layout-locked');
    } else {
        lockIcon.classList.add('layout-locked');
    }
});

Layout Stack Buttons

Use Case

Provide Layout Stack buttons to your Platform Windows.

Description
Adding Layout Stack Buttons can be achieved by setting the corresponding properties in your LayoutSettings.

Note that the red rectangles show the Layout Stack buttons.

642

createWindowWithStackButtons.js tab is an example that uses the Platform API to create a Window with the various stack buttons enabled.

You'll see the popoutWholeStack option listed as well.

Example

let platform = await fin.Platform.getCurrent();
let myWinIdentity = await platform.createWindow({
    contextMenu: true,
    layout: { // The `layout` object has two fields: `settings` and `content`
        settings: { // Here are the options you can toggle
            showPopoutIcon: true,
            showCloseIcon: true,
            showMaximiseIcon: true,
            popoutWholeStack: false
        },
        content: [{
            type: 'stack',
            content:[{
                type: 'component',
                componentName: 'view',
                componentState: {
                    name: 'my-new-test-view',
                    url: 'http://www.example.com' // The URL of the View
                }
            }]
        }]
    }
});

Context Sharing

Use Case

Share context data between all Views in a Window.

Description
setWindowContext can be used to set a Window's customContext. This context will be accessible from the Window itself and from any child Views via getWindowContext. It will be saved in any Platform snapShots and available upon application of those snapShots. The context data must be serializable. This can only be called from a window or view that has been launched into a Platform. This method can be called from the Window itself, or from any child View.

setWindowContext.js is an example using the Platform Context API to set the context of a Window or View.

listenToHostContextChanges.js is an example using the Platform Context API to listen for changes in a window's context from a view via the host-context-changed event. This event fires when a host window's context is updated or when the view is reparented to a new Window.

Example

const platform = fin.Platform.getCurrentSync();
const contextData = {
    security: 'STOCK',
    currentView: 'detailed'
}

const windowOrViewIdentity = { uuid: fin.me.uuid, name: 'nameOfWindowOrView' };
await platform.setWindowContext(contextData, windowOrViewIdentity);
// Context of the target window or view is now set to `contextData`
// From a view
const contextChangeHandler = ({ context }) => {
    console.log('Host window\'s context has changed. New context data:', context);
    // react to new context data here
}
await fin.me.on('host-context-changed', contextChangeHandler);

const platform = await fin.Platform.getCurrentSync();
const contextData = {
    security: 'STOCK',
    currentView: 'detailed'
}
platform.setWindowContext(contextData) // contextChangeHandler will log
// From a window
const contextChangeHandler = ({ context }) => {
    console.log('This window\'s context has changed. New context data:', context);
    // react to new context data here
    // e.g. to make other windows aware of the context change
    // one could fin.InterApplicationBus.publish 
    // or ChannelClient.dispatch here
}
await fin.me.on('context-changed', contextChangeHandler);

const platform = await fin.Platform.getCurrentSync();
const contextData = {
    security: 'STOCK',
    currentView: 'detailed'
}
platform.setWindowContext(contextData) // contextChangeHandler will log

Provider Customization

Use Case

Functionality that needs to be persisted / applied across the entire Platform via the Platform Provider, this includes but is not limited to Provider overrides

Description
Because the Platform Provider is effectively the "main" application window (that's generally hidden in the background) it can be leveraged to add listeners that need to apply to the entire platform.

lastVisibleWindowClosed.js is an example using the Platform API that each time a window is closed checks if the window was the last visible Window and shuts down the Platform if it is.

Example

// from the Platform Provider
async function lastVisibleWindowClosed(e) {
  const platform = await fin.Platform.getCurrent();
  const childWindows = await platform.Application.getChildWindows();

  async function isShowing(win) {
    return await win.isShowing();
  }

  const asyncFilter = async (arr, predicate) => {
    const results = await Promise.all(arr.map(predicate));

    return arr.filter((_v, index) => results[index]);
  };

  const visibleWindows = await asyncFilter(childWindows, isShowing);

  if (visibleWindows.length === 1) return await platform.quit();
  return await fin.me.close(true);
}

await fin.me.addListener("close-requested", lastVisibleWindowClosed);

Provider overrides & snapshot logic

Use Case

Override OpenFin methods with your own to save and apply snapshots.

Description
Saving a snapshot on quit and restoring a snapshot from a stored location on initialization. Assumes this code has been loaded into the html file defined in the providerUrl of the platform attribute in your application’s manifest.
The Override class as presented below should be returned to Platform.init

setLastSnapshot gets a snapshot of the running platform instance upon invocation, and saves snapshot object as a serialized JSON string in localStorage.

getLastSnapshot retrieves the JSON string representation item from localStorage.

quit override method called anytime a platform entity with the given provider url invokes the quit method. In this recipe we call setLastSnapshot.

applySnapshot override method called anytime a platform entity with the given provider url invokes the applySnapshot method. In this recipe we call getLastSnapshot.

Example

class Override extends Provider {

  async setLastSnapShot = () => {
    const snapshot = await fin.Platform.getCurrentSync().getSnapshot();
    localStorage.setItem("last-saved-snapshot", JSON.stringify(snapshot))
  }

  getLastSnapShot = () => {
    const snapshot = localStorage.getItem("last-saved-snapshot");
    return JSON.parse(snapshot);
  }

  async quit(...args) {
    await this.setLastSnapshot();
    return super.quit(...args);
  } 
      
  async applySnapshot({ snapshot, options }, callerIdentity) {
    const lastSnapshot = this.getLastSnapshot();
    if(lastSnapshot) {
      return super.applySnapshot({snapshot: lastSnapshot, options}, callerIdentity)
    } else { 
      return super.applySnapshot({ snapshot, options }, callerIdentity)
    }
  }

};

Launching a Headless Platform

Use Case

Headless Platform for Launching Platform Windows and applying snapshots

Description
An alternative approach to Provider overrides and snapshot logic, for handling additional edges cases while reducing code complexity.

app.json Main application manifest

default-snapshot.json Snapshot to load if there’s not one in localStorage

platform-provider.js Js file to load the logic in the provider window

platform-window.js Executed in the context of the defaultWindowUrl content

platform-window.html Include the platform-window.js to be executed in the context of the defaultWindowUrl

platform-provider.html Include the platform-provider.js to be executed in the context of the providerUrl.

Example

{
    "runtime": {
      ...
    },
    "shortcut": {
      ...
    },
    "platform": {
        "uuid": "platform_customization_local",
        "applicationIcon": "https://openfin.github.io/golden-prototype/favicon.ico",
        "autoShow": false,
        "providerUrl": "http://localhost:5555/provider.html",
        "defaultWindowOptions": {
            "url": "http://localhost:5555/platform-window.html",
            "contextMenu": true,
            "defaultLeft": 0,
            "defaultTop": 0,
            "minHeight": 445
        },
        "defaultViewOptions": {
            ...
        },
    }
}
//NOTE: the manifest does not contain a default snapshot to apply on startup
{
    "snapshot": {
        "windows": [
            {
                "url": "http://localhost:5555/main-window.html",
                "layout": {
                    "content": [
                        {
                            "type": "stack",
                            "id": "no-drop-target",
                            "content": [
                                {
                                    "type": "component",
                                    "componentName": "view",
                                    "componentState": {
                                        "name": "component_A1",
                                        "processAffinity": "ps_1",
                                        "url": "https://cdn.openfin.co/embed-web/chart.html"
                                    }
                                }
                            ]
                        }
                    ]
                }
            }
        ]
    }
}

//NOTE: A default snapshot to provide the applySnapshot call if the requested snapshot does not exist
(async function() {
    await fin.Platform.init();
    const snapshotItem = 'snapShot';
    const defaultSnapshotUrl = 'http://localhost:5555/default-snapshot.json';
    const platform = fin.Platform.getCurrentSync();
    const storedSnapshotValue = window.localStorage.getItem(snapshotItem);
    const storedSnapshot = storedSnapshotValue && JSON.parse(storedSnapshotValue);
    if(storedSnapshot) {
        return platform.applySnapshot(storedSnapshot);
    } else {
        const defaultSnapshotReq = await window.fetch(defaultSnapshotUrl);
        const defaultSnapshot = await defaultSnapshotReq.json();
        return platform.applySnapshot(defaultSnapshot.snapshot);
    }
 }
)();
//Executed in the context of the defaultWindowUrl content
async function restoreWindowLayout() {
   const storedWinLayout = localStorage.getItem(fin.me.identity.name);
        if (storedWinLayout) {
            return fin.Platform.Layout.getCurrentSync()
                                      .replace(JSON.parse(storedWinLayout));
        } else {
            throw new Error("No snapshot found in localstorage");
        }
}
//Include the platform-window.js to be executed in the context of the defaultWindowUrl 
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title>OpenFin Template</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script type="module" src="platform-window.js"></script>
    </head>
   <!-- default window content -->
   .
   .
   .
</html>
//Include the platform-provider.js to be executed in the context of the providerUrl 
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title>OpenFin Template</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script type="module" src="platform-provider.js"></script>
    </head>
   <!-- default window content -->
   .
   .
   .
</html>