From 9a5f4f5518bb21fe4700b7ffc17c7db6e5d928da Mon Sep 17 00:00:00 2001 From: John Date: Tue, 22 Oct 2024 15:47:07 +0100 Subject: [PATCH 1/3] Update excel example to break logic out and show calling it from a page as well --- .../integrate-with-excel/client/src/apps.ts | 45 +++- .../client/src/excel-integration.ts | 189 ++------------ .../client/src/excel-window.ts | 60 +++++ .../integrate-with-excel/client/src/excel.ts | 242 ++++++++++++++++++ .../client/src/provider.ts | 4 +- .../integrate-with-excel/client/src/shapes.ts | 11 +- .../client/webpack.config.js | 27 ++ .../public/manifest.fin.json | 102 ++++---- .../public/windows/excel.html | 20 ++ .../public/windows/excel.window.fin.json | 11 + 10 files changed, 477 insertions(+), 234 deletions(-) create mode 100644 how-to/integrate-with-excel/client/src/excel-window.ts create mode 100644 how-to/integrate-with-excel/client/src/excel.ts create mode 100644 how-to/integrate-with-excel/public/windows/excel.html create mode 100644 how-to/integrate-with-excel/public/windows/excel.window.fin.json diff --git a/how-to/integrate-with-excel/client/src/apps.ts b/how-to/integrate-with-excel/client/src/apps.ts index b8846d69cc..20ac414ce3 100644 --- a/how-to/integrate-with-excel/client/src/apps.ts +++ b/how-to/integrate-with-excel/client/src/apps.ts @@ -7,7 +7,7 @@ import { AppManifestType, getCurrentSync } from "@openfin/workspace-platform"; * @returns List of app definitions. */ export function getApps(): App[] { - return [OPENFIN_INFORMATION_APP, FDC3_BROADCAST, INTEROP_BROADCAST]; + return [OPENFIN_INFORMATION_APP, FDC3_BROADCAST, INTEROP_BROADCAST, EXCEL_WINDOW]; } /** @@ -92,6 +92,29 @@ const INTEROP_BROADCAST: App = { tags: ["view", "interop", "tools"] }; +/** + * App definition for the FDC3 Interop Broadcast View. + */ +const EXCEL_WINDOW: App = { + appId: "excel-window", + title: "Excel Window", + description: + "A window that launches Excel and listens for changes. This is an alternative to the Excel Home integration.", + manifest: "http://localhost:8080/windows/excel.window.fin.json", + manifestType: "window", + icons: [ + { + src: "http://localhost:8080/common/images/icon-blue.png" + } + ], + contactEmail: "contact@example.com", + supportEmail: "support@example.com", + publisher: "OpenFin", + intents: [], + images: [], + tags: ["window", "excel", "tools"] +}; + /** * Launch the passed app using its manifest type to determine how to launch it. * @param app The app to launch. @@ -99,13 +122,21 @@ const INTEROP_BROADCAST: App = { */ export async function launchApp( app: App -): Promise { +): Promise< + OpenFin.Platform | OpenFin.Identity | OpenFin.View | OpenFin.Application | OpenFin.Window | undefined +> { if (!app.manifest) { console.error(`No manifest was provided for type ${app.manifestType}`); return; } - let ret: OpenFin.Platform | OpenFin.Identity | OpenFin.View | OpenFin.Application | undefined; + let ret: + | OpenFin.Platform + | OpenFin.Identity + | OpenFin.View + | OpenFin.Application + | OpenFin.Window + | undefined; console.log("Application launch requested:", app); @@ -127,6 +158,14 @@ export async function launchApp( break; } + case "window": { + const manifest: OpenFin.WindowCreationOptions = await fetch(app.manifest).then(async (response) => + response.json() + ); + ret = await fin.Window.create(manifest); + break; + } + default: { ret = await fin.Application.startFromManifest(app.manifest); break; diff --git a/how-to/integrate-with-excel/client/src/excel-integration.ts b/how-to/integrate-with-excel/client/src/excel-integration.ts index 66424df56a..c94cca0d7e 100644 --- a/how-to/integrate-with-excel/client/src/excel-integration.ts +++ b/how-to/integrate-with-excel/client/src/excel-integration.ts @@ -1,12 +1,11 @@ -import type OpenFin from "@openfin/core"; -import { enableLogging, getExcelApplication, type Cell, type ExcelApplication } from "@openfin/excel"; import { CLITemplate, type HomeDispatchedSearchResult, type HomeSearchResponse, type HomeSearchResult } from "@openfin/workspace"; -import type { ExcelAssetSettings, ExcelSettings, ExcelWorksheetSettings } from "./shapes"; +import { init as initExcel, closeDown as closeDownExcel, launchExcel } from "./excel"; +import type { ExcelAssetSettings, ExcelSettings } from "./shapes"; /** * Implement the integration for Excel. @@ -24,18 +23,6 @@ export class ExcelIntegration { */ private _settings: ExcelSettings | undefined; - /** - * The Excel application interop. - * @internal - */ - private _excel: ExcelApplication | undefined; - - /** - * The interop clients for the different contexts. - * @internal - */ - private _interopClients?: { [id: string]: OpenFin.InteropClient }; - /** * Initialize the module. * @param settings The settings for the integration. @@ -44,19 +31,7 @@ export class ExcelIntegration { public async initialize(settings: ExcelSettings): Promise { this._settings = settings; - const brokerClient = fin.Interop.connectSync(fin.me.identity.uuid, {}); - const contextGroups = await brokerClient.getContextGroups(); - this._interopClients = {}; - for (const contextGroup of contextGroups) { - const contextClient = fin.Interop.connectSync(fin.me.identity.uuid, {}); - await contextClient.joinContextGroup(contextGroup.id); - await contextClient.addContextHandler(async (ctx) => { - await this.handleContext(contextGroup.id, ctx); - }); - this._interopClients[contextGroup.id] = contextClient; - } - - enableLogging(); + await initExcel(settings); } /** @@ -64,10 +39,7 @@ export class ExcelIntegration { * @returns Nothing. */ public async closedown(): Promise { - for (const client in this._interopClients) { - await this._interopClients[client].removeFromContextGroup(); - } - this._interopClients = {}; + await closeDownExcel(); } /** @@ -76,19 +48,19 @@ export class ExcelIntegration { * @returns The list of results and new filters. */ public async getSearchResults(query: string): Promise { - let filteredAssets: ExcelAssetSettings[] = []; - if (this._settings?.assets) { - if (query.length >= 3) { - filteredAssets = this._settings.assets.filter((a) => - a.title.toLowerCase().includes(query.toLowerCase()) - ); - } else { - filteredAssets = this._settings.assets; - } + if (this._settings?.asset && query.length < 3) { + return { results: [this.createResult(this._settings.asset)] }; + } + if ( + this._settings?.asset && + query.length >= 3 && + this._settings.asset.title.toLowerCase().includes(query.toLowerCase()) + ) { + return { results: [this.createResult(this._settings.asset)] }; } return { - results: filteredAssets.map((a) => this.createResult(a)) + results: [] }; } @@ -103,45 +75,7 @@ export class ExcelIntegration { result.action.trigger === "user-action" && result.data.workbook ) { - const excelAsset = result.data as ExcelAssetSettings; - - await fin.System.launchExternalProcess({ - alias: excelAsset.workbook - }); - - // The workbook is not always available immediately, so start a background process - // to wait for the workbook being ready - - let tryCount = 0; - const intervalId = window.setInterval(async () => { - const excel = await this.getExcel(); - if (excel) { - const workbooks = await excel.getWorkbooks(); - - if (workbooks.length === 0) { - if (tryCount === 10) { - window.clearInterval(intervalId); - } else { - tryCount++; - } - } else { - window.clearInterval(intervalId); - for (const workbook of workbooks) { - const name = await workbook.getName(); - if (name === excelAsset.workbook) { - for (const worksheetSettings of excelAsset.worksheets) { - const worksheet = await workbook.getWorksheetByName(worksheetSettings.name); - if (worksheet) { - await worksheet.addEventListener("change", async (cells) => { - await this.handleCellChanges(excelAsset, worksheetSettings, cells); - }); - } - } - } - } - } - } - }, 1000); + await launchExcel(result.data as ExcelAssetSettings); return true; } @@ -173,97 +107,4 @@ export class ExcelIntegration { templateContent: excelAsset.description }; } - - /** - * Get the excel application. - * @returns The application. - * @internal - */ - private async getExcel(): Promise { - try { - this._excel = await getExcelApplication(); - return this._excel; - } catch (err) { - console.error("Error getting Excel application", err); - } - } - - /** - * Handle the cell changes. - * @param excelAsset The asset to use for processing the cell changes. - * @param worksheet The asset to use for processing the cell changes. - * @param cells The cells that have changed. - */ - private async handleCellChanges( - excelAsset: ExcelAssetSettings, - worksheet: ExcelWorksheetSettings, - cells: Cell[] - ): Promise { - if (this._interopClients && worksheet.cellHandlers) { - for (const cell of cells) { - const cellHandler = worksheet.cellHandlers.find((c) => c.cell === cell.address); - - if (cellHandler) { - const client = this._interopClients[cellHandler.contextGroup]; - if ( - client && - (cellHandler.types.includes("instrument") || cellHandler.types.includes("fdc3.instrument")) - ) { - await client.setContext({ - type: "fdc3.instrument", - id: { - ticker: cell.value, - _source: `excel.${excelAsset.workbook}.${worksheet.name}` - } - }); - } - } - } - } - } - - /** - * Handle a context. - * @param contextGroup The group receiving the context. - * @param context The context being received. - */ - private async handleContext(contextGroup: string, context: OpenFin.Context): Promise { - if (this._settings?.assets) { - const excel = await this.getExcel(); - if (excel) { - const workbooks = await excel.getWorkbooks(); - for (const workbook of workbooks) { - const workbookName = await workbook.getName(); - - const connectedWorkbook = this._settings?.assets.find((a) => a.workbook === workbookName); - if (connectedWorkbook?.worksheets) { - for (const worksheetSettings of connectedWorkbook.worksheets) { - if (worksheetSettings.cellHandlers) { - const incomingSource = `excel.${workbookName}.${worksheetSettings.name}`; - - if (incomingSource !== context?.id?._source) { - const cellHandlers = worksheetSettings.cellHandlers?.filter( - (ch) => ch.contextGroup === contextGroup && ch.types.includes(context.type) - ); - for (const cellHandler of cellHandlers) { - const worksheet = await workbook.getWorksheetByName(worksheetSettings.name); - - if (worksheet) { - let cellValue: string | undefined; - if (context.type === "fdc3.instrument" || context.type === "instrument") { - cellValue = context.id?.ticker; - } - if (cellValue !== undefined) { - await worksheet.setCellValues(cellHandler.cell, [[cellValue]]); - } - } - } - } - } - } - } - } - } - } - } } diff --git a/how-to/integrate-with-excel/client/src/excel-window.ts b/how-to/integrate-with-excel/client/src/excel-window.ts new file mode 100644 index 0000000000..d786cddd2c --- /dev/null +++ b/how-to/integrate-with-excel/client/src/excel-window.ts @@ -0,0 +1,60 @@ +import { init as initExcel, launchExcel } from "./excel"; +import type { ExcelSettings } from "./shapes"; + +window.addEventListener("DOMContentLoaded", async () => { + const url = new URL(window.location.href); + const root = url.host; + const excelSettings: ExcelSettings = { + appAsset: { + alias: "excel-interop-example.xlsx", + version: "0.0.5", + src: `${root}/assets/excel-interop-example.zip`, + target: "excel-interop-example.xlsx" + }, + icon: `${root}/assets/excel.svg`, + asset: { + title: "Excel Interop Example", + description: "Demonstrate interop with Excel workbook", + workbook: "excel-interop-example.xlsx", + worksheets: [ + { + name: "Sheet1", + cellHandlers: [ + { + cell: "$B$3", + types: ["instrument", "fdc3.instrument"], + contextGroup: "green" + }, + { + cell: "$B$4", + types: ["instrument", "fdc3.instrument"], + contextGroup: "purple" + }, + { + cell: "$B$5", + types: ["instrument", "fdc3.instrument"], + contextGroup: "orange" + }, + { + cell: "$B$6", + types: ["instrument", "fdc3.instrument"], + contextGroup: "red" + }, + { + cell: "$B$7", + types: ["instrument", "fdc3.instrument"], + contextGroup: "pink" + }, + { + cell: "$B$8", + types: ["instrument", "fdc3.instrument"], + contextGroup: "yellow" + } + ] + } + ] + } + }; + await initExcel(excelSettings); + await launchExcel(excelSettings.asset); +}); diff --git a/how-to/integrate-with-excel/client/src/excel.ts b/how-to/integrate-with-excel/client/src/excel.ts new file mode 100644 index 0000000000..9c0c18e29a --- /dev/null +++ b/how-to/integrate-with-excel/client/src/excel.ts @@ -0,0 +1,242 @@ +import type OpenFin from "@openfin/core"; +import { enableLogging, getExcelApplication, type Cell, type ExcelApplication } from "@openfin/excel"; +import type { ExcelAssetSettings, ExcelSettings, ExcelWorksheetSettings } from "./shapes"; + +let excel: ExcelApplication | undefined; +/** + * The interop clients for the different contexts. + */ +let interopClients: { [id: string]: OpenFin.InteropClient } | undefined; + +let settings: ExcelSettings | undefined; + +let workbookFound: boolean; + +/** + * Initialize the Excel interop. + * @param excelSettings The settings to use for initializing the Excel interop. + */ +export async function init(excelSettings: ExcelSettings): Promise { + if (!settings) { + settings = excelSettings; + const brokerClient = fin.Interop.connectSync(fin.me.identity.uuid, {}); + const contextGroups = await brokerClient.getContextGroups(); + interopClients = {}; + for (const contextGroup of contextGroups) { + const contextClient = fin.Interop.connectSync(fin.me.identity.uuid, {}); + await contextClient.joinContextGroup(contextGroup.id); + await contextClient.addContextHandler(async (ctx) => { + await handleContext(contextGroup.id, ctx); + }); + interopClients[contextGroup.id] = contextClient; + } + + enableLogging(); + } +} + +/** + * Get the Excel application. + * @param assetSettings The settings to use for getting the Excel application. + * @returns The Excel application. + */ +export async function launchExcel(assetSettings: ExcelAssetSettings): Promise { + const assetAvailable = await getAppAsset(); + if (!assetAvailable) { + return false; + } + await fin.System.launchExternalProcess({ + alias: settings?.appAsset.alias + }); + // The workbook is not always available immediately, so start a background process + // to wait for the workbook being ready + + await listenToExcel(assetSettings); + return true; +} + +/** + * Listen to the Excel application. + * @param assetSettings The settings to use for listening to the Excel application. + * @param tryCount The number of times the function has been called. + * @returns A promise that resolves when the Excel application is available. + */ +async function listenToExcel(assetSettings: ExcelAssetSettings, tryCount = 0): Promise { + if (tryCount === 10) { + console.error("Excel workbook not available after 10 attempts"); + return; + } + if (workbookFound) { + console.info("Workbook already found"); + return; + } + setTimeout(async () => { + const excelInstance = await getExcel(); + if (excelInstance) { + const workbooks = await excelInstance.getWorkbooks(); + if (workbooks.length === 0) { + tryCount++; + await listenToExcel(assetSettings, tryCount); + } else { + workbookFound = false; + for (const workbook of workbooks) { + const name = await workbook.getName(); + if (name === assetSettings.workbook) { + for (const worksheetSettings of assetSettings.worksheets) { + const worksheet = await workbook.getWorksheetByName(worksheetSettings.name); + if (worksheet) { + workbookFound = true; + await worksheet.addEventListener("change", async (cells) => { + await handleCellChanges(assetSettings, worksheetSettings, cells); + }); + } + } + } + } + if (!workbookFound) { + tryCount++; + await listenToExcel(assetSettings, tryCount); + } + } + } + }, 1000); +} + +/** + * Do any cleanup that is required. + */ +export async function closeDown(): Promise { + for (const client in interopClients) { + await interopClients[client].removeFromContextGroup(); + } + interopClients = {}; +} + +/** + * Gets the configured app asset and ensures it is available. + * @returns A boolean indicating if the app asset is available. + */ +async function getAppAsset(): Promise { + let availableAppAsset: OpenFin.AppAssetInfo | undefined; + try { + if (settings?.appAsset !== undefined) { + availableAppAsset = await fin.System.getAppAssetInfo({ alias: settings.appAsset.alias }); + } + } catch (appAssetError) { + console.debug( + `App asset info for alias: ${settings?.appAsset.alias} is not available. Response from getAppAssetInfo`, + appAssetError + ); + } + if ( + (availableAppAsset === undefined || settings?.appAsset.version !== availableAppAsset.version) && + settings?.appAsset !== undefined + ) { + console.info(`App asset with alias: ${settings?.appAsset.alias} does not exist in memory. Fetching it.`); + try { + await fin.System.downloadAsset(settings.appAsset, (progress) => { + const downloadedPercent = Math.floor((progress.downloadedBytes / progress.totalBytes) * 100); + console.info( + `Downloaded ${downloadedPercent}% of app asset with alias of ${settings?.appAsset.alias}` + ); + }); + } catch (error) { + console.error(`Error trying to download app asset with alias: ${settings?.appAsset.alias}`, error); + return false; + } + } + return true; +} + +/** + * Get the excel application. + * @returns The application. + * @internal + */ +async function getExcel(): Promise { + try { + excel = await getExcelApplication(); + return excel; + } catch (err) { + console.error("Error getting Excel application", err); + } +} + +/** + * Handle the cell changes. + * @param excelAsset The asset to use for processing the cell changes. + * @param worksheet The asset to use for processing the cell changes. + * @param cells The cells that have changed. + */ +async function handleCellChanges( + excelAsset: ExcelAssetSettings, + worksheet: ExcelWorksheetSettings, + cells: Cell[] +): Promise { + if (interopClients && worksheet.cellHandlers) { + for (const cell of cells) { + const cellHandler = worksheet.cellHandlers.find((c) => c.cell === cell.address); + + if (cellHandler) { + const client = interopClients[cellHandler.contextGroup]; + if ( + client && + (cellHandler.types.includes("instrument") || cellHandler.types.includes("fdc3.instrument")) + ) { + await client.setContext({ + type: "fdc3.instrument", + id: { + ticker: cell.value, + _source: `excel.${excelAsset.workbook}.${worksheet.name}` + } + }); + } + } + } + } +} + +/** + * Handle a context. + * @param contextGroup The group receiving the context. + * @param context The context being received. + */ +async function handleContext(contextGroup: string, context: OpenFin.Context): Promise { + if (settings?.asset) { + const excelInstance = await getExcel(); + if (excelInstance) { + const workbooks = await excelInstance.getWorkbooks(); + for (const workbook of workbooks) { + const workbookName = await workbook.getName(); + + const connectedWorkbook = settings?.asset; + if (connectedWorkbook?.worksheets) { + for (const worksheetSettings of connectedWorkbook.worksheets) { + if (worksheetSettings.cellHandlers) { + const incomingSource = `excel.${workbookName}.${worksheetSettings.name}`; + + if (incomingSource !== context?.id?._source) { + const cellHandlers = worksheetSettings.cellHandlers?.filter( + (ch) => ch.contextGroup === contextGroup && ch.types.includes(context.type) + ); + for (const cellHandler of cellHandlers) { + const worksheet = await workbook.getWorksheetByName(worksheetSettings.name); + + if (worksheet) { + let cellValue: string | undefined; + if (context.type === "fdc3.instrument" || context.type === "instrument") { + cellValue = context.id?.ticker; + } + if (cellValue !== undefined) { + await worksheet.setCellValues(cellHandler.cell, [[cellValue]]); + } + } + } + } + } + } + } + } + } + } +} diff --git a/how-to/integrate-with-excel/client/src/provider.ts b/how-to/integrate-with-excel/client/src/provider.ts index 27af9aee23..8d5fb451cd 100644 --- a/how-to/integrate-with-excel/client/src/provider.ts +++ b/how-to/integrate-with-excel/client/src/provider.ts @@ -99,8 +99,8 @@ async function initializeWorkspaceComponents(): Promise { title: app.title, icon: app.icons[0]?.src, data: app, - label: "View", - actions: [{ name: "Launch View", hotkey: "enter" }], + label: "App", + actions: [{ name: "Launch App", hotkey: "enter" }], description: app.description, shortDescription: app.description, template: CLITemplate.SimpleText, diff --git a/how-to/integrate-with-excel/client/src/shapes.ts b/how-to/integrate-with-excel/client/src/shapes.ts index 8104210e01..1d19b40367 100644 --- a/how-to/integrate-with-excel/client/src/shapes.ts +++ b/how-to/integrate-with-excel/client/src/shapes.ts @@ -1,3 +1,5 @@ +import type { OpenFin } from "@openfin/core"; + /** * Custom settings for the application. */ @@ -18,9 +20,14 @@ export interface ExcelSettings { icon: string; /** - * The list of assets for the home integration. + * The asset for the excel integration. + */ + asset: ExcelAssetSettings; + + /** + * The app asset information. */ - assets: ExcelAssetSettings[]; + appAsset: OpenFin.AppAssetInfo; } /** diff --git a/how-to/integrate-with-excel/client/webpack.config.js b/how-to/integrate-with-excel/client/webpack.config.js index 93e9dbff13..e757032de3 100644 --- a/how-to/integrate-with-excel/client/webpack.config.js +++ b/how-to/integrate-with-excel/client/webpack.config.js @@ -20,5 +20,32 @@ module.exports = [ filename: 'provider.bundle.js', path: path.resolve(__dirname, '..', 'public', 'js') } + }, + { + entry: './client/src/excel-window.ts', + devtool: 'source-map', + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/ + } + ] + }, + resolve: { + extensions: ['.tsx', '.ts', '.js'] + }, + externals: { fin: 'fin' }, + output: { + filename: 'excel.bundle.js', + library: { + type: 'module' + }, + path: path.resolve(__dirname, '..', 'public', 'js') + }, + experiments: { + outputModule: true + } } ]; diff --git a/how-to/integrate-with-excel/public/manifest.fin.json b/how-to/integrate-with-excel/public/manifest.fin.json index ad4d6f6f28..a8b0449f42 100644 --- a/how-to/integrate-with-excel/public/manifest.fin.json +++ b/how-to/integrate-with-excel/public/manifest.fin.json @@ -39,15 +39,7 @@ "name": "Integrate With Excel - v19.1.0", "target": ["desktop", "start-menu"] }, - "appAssets": [ - { - "alias": "excel-interop-example.xlsx", - "version": "0.0.5", - "src": "http://localhost:8080/assets/excel-interop-example.zip", - "target": "excel-interop-example.xlsx", - "forceDownload": true - } - ], + "appAssets": [], "supportInformation": { "company": "OpenFin", "product": "Workspace Starter - Integrate With Excel - Client", @@ -56,51 +48,55 @@ }, "customSettings": { "excel": { + "appAsset": { + "alias": "excel-interop-example.xlsx", + "version": "0.0.6", + "src": "http://localhost:8080/assets/excel-interop-example.zip", + "target": "excel-interop-example.xlsx" + }, "icon": "http://localhost:8080/assets/excel.svg", - "assets": [ - { - "title": "Excel Interop Example", - "description": "Demonstrate interop with Excel workbook", - "workbook": "excel-interop-example.xlsx", - "worksheets": [ - { - "name": "Sheet1", - "cellHandlers": [ - { - "cell": "$B$3", - "types": ["instrument", "fdc3.instrument"], - "contextGroup": "green" - }, - { - "cell": "$B$4", - "types": ["instrument", "fdc3.instrument"], - "contextGroup": "purple" - }, - { - "cell": "$B$5", - "types": ["instrument", "fdc3.instrument"], - "contextGroup": "orange" - }, - { - "cell": "$B$6", - "types": ["instrument", "fdc3.instrument"], - "contextGroup": "red" - }, - { - "cell": "$B$7", - "types": ["instrument", "fdc3.instrument"], - "contextGroup": "pink" - }, - { - "cell": "$B$8", - "types": ["instrument", "fdc3.instrument"], - "contextGroup": "yellow" - } - ] - } - ] - } - ] + "asset": { + "title": "Excel Interop Example", + "description": "Demonstrate interop with Excel workbook", + "workbook": "excel-interop-example.xlsx", + "worksheets": [ + { + "name": "Sheet1", + "cellHandlers": [ + { + "cell": "$B$3", + "types": ["instrument", "fdc3.instrument"], + "contextGroup": "green" + }, + { + "cell": "$B$4", + "types": ["instrument", "fdc3.instrument"], + "contextGroup": "purple" + }, + { + "cell": "$B$5", + "types": ["instrument", "fdc3.instrument"], + "contextGroup": "orange" + }, + { + "cell": "$B$6", + "types": ["instrument", "fdc3.instrument"], + "contextGroup": "red" + }, + { + "cell": "$B$7", + "types": ["instrument", "fdc3.instrument"], + "contextGroup": "pink" + }, + { + "cell": "$B$8", + "types": ["instrument", "fdc3.instrument"], + "contextGroup": "yellow" + } + ] + } + ] + } } } } diff --git a/how-to/integrate-with-excel/public/windows/excel.html b/how-to/integrate-with-excel/public/windows/excel.html new file mode 100644 index 0000000000..f74d4f5d66 --- /dev/null +++ b/how-to/integrate-with-excel/public/windows/excel.html @@ -0,0 +1,20 @@ + + + + + + Alternative Excel Example + + + + + + + +
Example Excel Launcher and Listener.
+
+ If Excel is launched through this and the Excel entry in Home there will be multiple context messages + published as both entries are listening. +
+ + diff --git a/how-to/integrate-with-excel/public/windows/excel.window.fin.json b/how-to/integrate-with-excel/public/windows/excel.window.fin.json new file mode 100644 index 0000000000..ead7fa044d --- /dev/null +++ b/how-to/integrate-with-excel/public/windows/excel.window.fin.json @@ -0,0 +1,11 @@ +{ + "url": "http://localhost:8080/windows/excel.html", + "name": "excel", + "permissions": { + "System": { + "launchExternalProcess": true, + "downloadAsset": true + } + }, + "autoShow": false +} From 585084a1b39e5351899ddcebaf707e0df22586fa Mon Sep 17 00:00:00 2001 From: John Date: Tue, 22 Oct 2024 16:38:09 +0100 Subject: [PATCH 2/3] Change to localhost which is replaced by build process. --- how-to/integrate-with-excel/client/src/excel-window.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/how-to/integrate-with-excel/client/src/excel-window.ts b/how-to/integrate-with-excel/client/src/excel-window.ts index d786cddd2c..5ef61a4ca4 100644 --- a/how-to/integrate-with-excel/client/src/excel-window.ts +++ b/how-to/integrate-with-excel/client/src/excel-window.ts @@ -2,8 +2,7 @@ import { init as initExcel, launchExcel } from "./excel"; import type { ExcelSettings } from "./shapes"; window.addEventListener("DOMContentLoaded", async () => { - const url = new URL(window.location.href); - const root = url.host; + const root = "http://localhost:8080"; const excelSettings: ExcelSettings = { appAsset: { alias: "excel-interop-example.xlsx", From ee19b943109a89c5e62cddbf82188a6b207c5496 Mon Sep 17 00:00:00 2001 From: John Date: Mon, 28 Oct 2024 06:53:06 +0000 Subject: [PATCH 3/3] Add enhancements (#749) --- .../workspace-platform-starter/CHANGELOG.md | 3 + .../client/src/framework/actions.ts | 20 +----- .../client/src/framework/integrations.ts | 9 +++ .../client/src/framework/launch.ts | 67 +++++++++++++++---- .../client/src/framework/modules.ts | 5 +- .../client/src/framework/platform/platform.ts | 10 +-- .../src/framework/shapes/module-shapes.ts | 4 +- .../src/framework/workspace/identity.ts | 27 ++++++++ .../client/src/framework/workspace/store.ts | 4 +- .../view-position/content-creation.ts | 2 +- .../content-creation/view-position/shapes.ts | 5 ++ .../modules/integrations/apps/integration.ts | 2 +- .../broker/wps-interop-override.ts | 19 +----- .../platform/platform-mapper.ts | 5 +- .../types/module/shapes/module-shapes.d.ts | 4 +- .../public/manifest.fin.json | 11 +-- 16 files changed, 127 insertions(+), 70 deletions(-) create mode 100644 how-to/workspace-platform-starter/client/src/framework/workspace/identity.ts diff --git a/how-to/workspace-platform-starter/CHANGELOG.md b/how-to/workspace-platform-starter/CHANGELOG.md index aa39ac5d9a..f425a61e55 100644 --- a/how-to/workspace-platform-starter/CHANGELOG.md +++ b/how-to/workspace-platform-starter/CHANGELOG.md @@ -34,6 +34,9 @@ - Enhancement - Previously if you were generating a log module you needed to avoid using the injected createLogger function as you would end up calling yourself and ending up in a loop. The module logic has been updated to pass an alternative createLogger function to log modules that only logs to the console (so that it can still be used for debugging) but doesn't call itself or other log sinks (log modules). - Enhancement - You may want to enable snap but not have auto window registration. This may be because you only want snap for your native windows or you might want to have your own rules (via a custom platform provider module) on which windows should be tracked (outside of the default rules snap has such as includeInSnapshots false meaning don't track). The snapProvider config now lets you specify "enableAutoWindowRegistration": false to disable auto tracking. - Example Added - Added an additional [Platform Override Example](./client/src/modules/platform-override/snap-window-selection-override/README.md) that shows how you can enable Snap but disable auto window tracking so that you can plug in your own logic to determine which windows should be tracked by the snap server. +- Enhancement - Apps now launch on the monitor that Stock, Dock and Home are on in a multi monitor setup. Added support for specifying a calling identity when launching an app. This is now used by the apps integration module for launching apps, called by the launch app action used by the dock, called by the launch app function triggered by Store. +- Updated the example content creation rule to support specifying a default view placement position when window.open is called from inside a view. Defaulted it to "stack-right" so that it has the same behavior as Chrome/Edge when window.open is called without a target (opens a new tab to the right hand side of the tab). It is now turned on by default in the main manifest (search for view-position-content-creation)Turned it on by default (the example app content Content Creation Examples will now work). See [How to Customize Content Creation Rules](./docs/how-to-customize-content-creation-rules.md). +- Change - Updated the platform mapper (configurable option to make snapshots smaller when sending to custom endpoints) so that it no longer strips out snapshotDetails as we received feedback that it resulted in monitor placement not working as it should. ## v19.0.0 diff --git a/how-to/workspace-platform-starter/client/src/framework/actions.ts b/how-to/workspace-platform-starter/client/src/framework/actions.ts index a1ebe3c9ae..bc7fb9e04e 100644 --- a/how-to/workspace-platform-starter/client/src/framework/actions.ts +++ b/how-to/workspace-platform-starter/client/src/framework/actions.ts @@ -14,13 +14,11 @@ import { createLogger } from "./logger-provider"; import * as menuProvider from "./menu"; import { closedownModules, initializeModules, loadModules } from "./modules"; import type { ActionHelpers, Actions, ActionsProviderOptions } from "./shapes/actions-shapes"; -import type { LaunchPreference, PlatformApp } from "./shapes/app-shapes"; -import type { WindowPositioningOptions } from "./shapes/browser-shapes"; +import type { PlatformApp } from "./shapes/app-shapes"; import type { ModuleEntry, ModuleHelpers } from "./shapes/module-shapes"; import * as shareProvider from "./share"; import * as themeProvider from "./themes"; import { isEmpty, isStringValue } from "./utils"; -import { getWindowPositionUsingStrategy } from "./utils-position"; import * as homeComponent from "./workspace/home"; import * as notificationsComponent from "./workspace/notifications"; @@ -30,7 +28,6 @@ let modules: ModuleEntry[] | undefined; const customActionMap: CustomActionsMap = {}; let platformActionMap: CustomActionsMap | undefined; let isInitialized: boolean = false; -let windowPositioningOptions: WindowPositioningOptions | undefined; /** * These Ids are for actions built in to the platform. @@ -48,13 +45,11 @@ export const PLATFORM_ACTION_IDS = { * Initialize the actions provider. * @param options Options for the actions provider. * @param helpers Module helpers to pass to any loaded modules. - * @param windowPositioning Options for positioning windows. * @returns The platform action map. */ export async function init( options: ActionsProviderOptions | undefined, - helpers: ModuleHelpers, - windowPositioning: WindowPositioningOptions + helpers: ModuleHelpers ): Promise { if (isInitialized) { logger.error("The actions can only be used once when configuring the platform"); @@ -73,10 +68,6 @@ export async function init( updateToolbarButtons }); } - if (!isEmpty(windowPositioning)) { - logger.info("Initializing with window positioning options", windowPositioning); - windowPositioningOptions = windowPositioning; - } await buildActions(); @@ -256,12 +247,7 @@ async function getPlatformActions(): Promise { */ async function launchAppAction(app: PlatformApp, clientIdentity: OpenFin.Identity): Promise { if (!isEmpty(app)) { - let launchPreference: LaunchPreference | undefined; - const bounds = await getWindowPositionUsingStrategy(windowPositioningOptions, clientIdentity); - if (!isEmpty(bounds)) { - launchPreference = { bounds }; - } - await launch(app, launchPreference); + await launch(app, undefined, clientIdentity); } else { logger.error("Unable to do launch app action as an app object was not passed"); } diff --git a/how-to/workspace-platform-starter/client/src/framework/integrations.ts b/how-to/workspace-platform-starter/client/src/framework/integrations.ts index fc4e21b1ae..1188d84941 100644 --- a/how-to/workspace-platform-starter/client/src/framework/integrations.ts +++ b/how-to/workspace-platform-starter/client/src/framework/integrations.ts @@ -31,6 +31,7 @@ import * as templateHelpers from "./templates"; import { createButton, createContainer, createHelp, createImage, createText, createTitle } from "./templates"; import { isEmpty, isStringValue } from "./utils"; import { getVersionInfo } from "./version"; +import { getHomeIdentity } from "./workspace/identity"; const logger = createLogger("Integrations"); @@ -340,6 +341,14 @@ export async function itemSelection( const foundIntegration = integrationModules.find((hi) => hi.definition.id === result.data?.providerId); if (foundIntegration?.implementation?.itemSelection) { + if (result.dispatcherIdentity.uuid === result.dispatcherIdentity.name) { + // If the dispatcher is the same as the name, then it would be the provider + // and the provider is routing the message from Home + // we provide the home name to the dispatcherIdentity.name + // to allow the integration to determine the monitor Home is on. + const homeIdentity = getHomeIdentity(); + result.dispatcherIdentity.name = homeIdentity.name; + } return foundIntegration.implementation.itemSelection(result, lastResponse); } } diff --git a/how-to/workspace-platform-starter/client/src/framework/launch.ts b/how-to/workspace-platform-starter/client/src/framework/launch.ts index 023d7d8ed8..4dd1c4d04c 100644 --- a/how-to/workspace-platform-starter/client/src/framework/launch.ts +++ b/how-to/workspace-platform-starter/client/src/framework/launch.ts @@ -12,6 +12,7 @@ import * as endpointProvider from "./endpoint"; import { createLogger } from "./logger-provider"; import { MANIFEST_TYPES } from "./manifest-types"; import { bringViewToFront, bringWindowToFront, doesViewExist, doesWindowExist } from "./platform/browser"; +import { getSettings } from "./settings"; import type { NativeLaunchOptions, PlatformApp, @@ -22,20 +23,25 @@ import type { PreferenceConstraintUrl, HostLaunchOptions } from "./shapes/app-shapes"; +import type { WindowPositioningOptions } from "./shapes/browser-shapes"; import * as snapProvider from "./snap"; import { formatError, getCommandLineArgs, isEmpty, isStringValue, objectClone, randomUUID } from "./utils"; +import { getWindowPositionOptions, getWindowPositionUsingStrategy } from "./utils-position"; const logger = createLogger("Launch"); +let windowPositioningOptions: WindowPositioningOptions | undefined; /** * Launch an application in the way specified by its manifest type. * @param platformApp The application to launch. * @param launchPreference launchPreferences if updatable for your the application. + * @param callerIdentity optionally pass information related to the caller to consider when launching. * @returns Identifiers specific to the type of application launched. */ export async function launch( platformApp: PlatformApp, - launchPreference?: UpdatableLaunchPreference + launchPreference?: UpdatableLaunchPreference, + callerIdentity?: OpenFin.Identity ): Promise { try { logger.info("Application launch requested", platformApp); @@ -66,7 +72,7 @@ export async function launch( } case MANIFEST_TYPES.InlineView.id: case MANIFEST_TYPES.View.id: { - const platformIdentity = await launchView(app, launchPreference); + const platformIdentity = await launchView(app, launchPreference, callerIdentity); if (platformIdentity) { platformAppIdentities.push(platformIdentity); } @@ -74,7 +80,7 @@ export async function launch( } case MANIFEST_TYPES.Window.id: case MANIFEST_TYPES.InlineWindow.id: { - const platformIdentity = await launchWindow(app, launchPreference); + const platformIdentity = await launchWindow(app, launchPreference, callerIdentity); if (platformIdentity) { platformAppIdentities.push(platformIdentity); } @@ -106,10 +112,14 @@ export async function launch( } case MANIFEST_TYPES.Endpoint.id: { if (endpointProvider.hasEndpoint(app.manifest)) { - const identity = await endpointProvider.requestResponse<{ payload: PlatformApp }, OpenFin.Identity>( - app.manifest, - { payload: app } - ); + const identity = await endpointProvider.requestResponse< + { + payload: PlatformApp; + launchPreference?: UpdatableLaunchPreference; + callerIdentity?: OpenFin.Identity; + }, + OpenFin.Identity + >(app.manifest, { payload: app, launchPreference, callerIdentity }); if (isEmpty(identity)) { logger.warn( `App with id: ${app.appId} encountered when launched using endpoint: ${app.manifest}.` @@ -316,11 +326,13 @@ function getManifestEndpointId(appId?: string): string { * Launch a window for the platform app. * @param windowApp The app to launch the window for. * @param launchPreference Optional custom launch preferences + * @param callerIdentity optionally pass information related to the caller to consider when launching. * @returns The identity of the window launched. */ async function launchWindow( windowApp: PlatformApp, - launchPreference?: UpdatableLaunchPreference + launchPreference?: UpdatableLaunchPreference, + callerIdentity?: OpenFin.Identity ): Promise { if (isEmpty(windowApp)) { logger.warn("No app was passed to launchWindow"); @@ -391,7 +403,12 @@ async function launchWindow( const canUpdateUrl = isAppPreferenceUpdatable(windowApp, "url"); if (!isEmpty(canUpdateBounds) && !isEmpty(launchPreference?.bounds)) { - appLaunchPreference.bounds = { ...appLaunchPreference.bounds, ...launchPreference?.bounds }; + const callerIdentityBasedLocation = await getCallerIdentityBasedPosition(callerIdentity); + appLaunchPreference.bounds = { + ...appLaunchPreference.bounds, + ...callerIdentityBasedLocation, + ...launchPreference?.bounds + }; } if (!isEmpty(canUpdateCentered) && !isEmpty(launchPreference?.defaultCentered)) { @@ -481,11 +498,13 @@ async function launchWindow( * Launch a view for the platform app. * @param viewApp The app to launch the view for. * @param launchPreference The preferences (if supported) that you would like to apply + * @param callerIdentity optionally pass information related to the caller to consider when launching. * @returns The identity of the view launched. */ async function launchView( viewApp: PlatformApp, - launchPreference?: UpdatableLaunchPreference + launchPreference?: UpdatableLaunchPreference, + callerIdentity?: OpenFin.Identity ): Promise { if (isEmpty(viewApp)) { logger.warn("No app was passed to launchView"); @@ -560,8 +579,13 @@ async function launchView( const canUpdateInterop = isAppPreferenceUpdatable(viewApp, "interop"); const canUpdateUrl = isAppPreferenceUpdatable(viewApp, "url"); - if (!isEmpty(canUpdateBounds) && !isEmpty(launchPreference?.bounds)) { - appLaunchPreference.bounds = { ...appLaunchPreference.bounds, ...launchPreference?.bounds }; + if (!isEmpty(canUpdateBounds) && (!isEmpty(launchPreference?.bounds) || !isEmpty(callerIdentity))) { + const callerIdentityBasedLocation = await getCallerIdentityBasedPosition(callerIdentity); + appLaunchPreference.bounds = { + ...appLaunchPreference.bounds, + ...callerIdentityBasedLocation, + ...launchPreference?.bounds + }; } if (!isEmpty(canUpdateCentered) && !isEmpty(launchPreference?.defaultCentered)) { @@ -1147,3 +1171,22 @@ function updateInstanceIds(layout: T): T { }) ); } + +/** + * Returns undefined or left/top positioning based on the position of the next window given the monitor of the caller. + * @param callerIdentity The identity of the caller to consider when determining x/y of new window + * @returns Left/Top co-ordinates to consider when launching a new window + */ +async function getCallerIdentityBasedPosition( + callerIdentity?: OpenFin.Identity +): Promise<{ left: number; top: number } | undefined> { + if (isEmpty(callerIdentity)) { + return; + } + if (isEmpty(windowPositioningOptions)) { + const settings = await getSettings(); + windowPositioningOptions = await getWindowPositionOptions(settings?.browserProvider); + } + const bounds = await getWindowPositionUsingStrategy(windowPositioningOptions, callerIdentity); + return bounds; +} diff --git a/how-to/workspace-platform-starter/client/src/framework/modules.ts b/how-to/workspace-platform-starter/client/src/framework/modules.ts index cdb084b714..df5872e72b 100644 --- a/how-to/workspace-platform-starter/client/src/framework/modules.ts +++ b/how-to/workspace-platform-starter/client/src/framework/modules.ts @@ -304,7 +304,8 @@ export function getDefaultHelpers(): ModuleHelpers { getDialogClient, launchApp: async ( appId: string, - launchPreference?: UpdatableLaunchPreference + launchPreference?: UpdatableLaunchPreference, + callerIdentity?: OpenFin.Identity ): Promise => { logger.info(`launchApp: Looking up appId: ${appId}`); const app = await getApp(appId); @@ -313,7 +314,7 @@ export function getDefaultHelpers(): ModuleHelpers { logger.warn(`launchApp: The specified appId: ${appId} is not listed in this platform.`); } else { logger.info(`launchApp: Launching app with appId: ${appId}`); - result = await launch(app, launchPreference); + result = await launch(app, launchPreference, callerIdentity); logger.info(`launchApp: App with appId: ${appId} launched.`); } return result; diff --git a/how-to/workspace-platform-starter/client/src/framework/platform/platform.ts b/how-to/workspace-platform-starter/client/src/framework/platform/platform.ts index 5037215b53..18d89ddc81 100644 --- a/how-to/workspace-platform-starter/client/src/framework/platform/platform.ts +++ b/how-to/workspace-platform-starter/client/src/framework/platform/platform.ts @@ -169,13 +169,7 @@ async function setupPlatform(manifestSettings: CustomSettings | undefined): Prom browser.defaultViewOptions = customSettings?.browserProvider?.defaultViewOptions; } - logger.info("Specifying following browser options", browser); - const windowPositioningOptions = await getWindowPositionOptions(customSettings?.browserProvider); - const customActions = await actionsProvider.init( - customSettings?.actionsProvider, - helpers, - windowPositioningOptions - ); + const customActions = await actionsProvider.init(customSettings?.actionsProvider, helpers); const theme = await getThemes(); await lowCodeIntegrationProvider.init(customSettings?.lowCodeIntegrationProvider); @@ -193,7 +187,7 @@ async function setupPlatform(manifestSettings: CustomSettings | undefined): Prom browser.defaultWindowOptions = browser.defaultWindowOptions ?? {}; await contentCreationProvider.populateRules(browser.defaultWindowOptions); } - + const windowPositioningOptions = await getWindowPositionOptions(customSettings?.browserProvider); await interopProvider.init(customSettings?.platformProvider, windowPositioningOptions, helpers); const platform = getCurrentSync(); diff --git a/how-to/workspace-platform-starter/client/src/framework/shapes/module-shapes.ts b/how-to/workspace-platform-starter/client/src/framework/shapes/module-shapes.ts index 2aa588f207..c38277259d 100644 --- a/how-to/workspace-platform-starter/client/src/framework/shapes/module-shapes.ts +++ b/how-to/workspace-platform-starter/client/src/framework/shapes/module-shapes.ts @@ -193,11 +193,13 @@ export interface ModuleHelpers { * the current user. * @param appId The id of the application that is registered against the currently running platform * @param launchPreference If the app supports launch preferences then these can be passed. + * @param callerIdentity The identity of the caller to consider when determining x/y of new window. * @returns An array of the platform identities that related from the launch or nothing if nothing was launched. */ launchApp?( appId: string, - launchPreference?: UpdatableLaunchPreference + launchPreference?: UpdatableLaunchPreference, + callerIdentity?: OpenFin.Identity ): Promise; /** diff --git a/how-to/workspace-platform-starter/client/src/framework/workspace/identity.ts b/how-to/workspace-platform-starter/client/src/framework/workspace/identity.ts new file mode 100644 index 0000000000..d2c5aee05b --- /dev/null +++ b/how-to/workspace-platform-starter/client/src/framework/workspace/identity.ts @@ -0,0 +1,27 @@ +import type { OpenFin } from "@openfin/core"; + +const workspaceUUID: string = "openfin-workspace"; + +/** + * Returns the identity of Workspace Store. + * @returns The identity of Workspace Store. + */ +export function getStoreIdentity(): OpenFin.Identity { + return { uuid: workspaceUUID, name: "openfin-storefront" }; +} + +/** + * Returns the identity of Workspace Home. + * @returns The identity of Workspace Home. + */ +export function getHomeIdentity(): OpenFin.Identity { + return { uuid: workspaceUUID, name: "openfin-home" }; +} + +/** + * Returns the identity of Workspace Dock. + * @returns The identity of Workspace Dock. + */ +export function getDockIdentity(): OpenFin.Identity { + return { uuid: workspaceUUID, name: "openfin-dock" }; +} diff --git a/how-to/workspace-platform-starter/client/src/framework/workspace/store.ts b/how-to/workspace-platform-starter/client/src/framework/workspace/store.ts index e054aa4f54..48a1596b21 100644 --- a/how-to/workspace-platform-starter/client/src/framework/workspace/store.ts +++ b/how-to/workspace-platform-starter/client/src/framework/workspace/store.ts @@ -25,6 +25,7 @@ import type { StorefrontSettingsNavigationItem } from "../shapes/store-shapes"; import { isEmpty, isStringValue, randomUUID } from "../utils"; +import { getStoreIdentity } from "./identity"; const TOP_ROW_LIMIT = 4; const MIDDLE_ROW_LIMIT = 6; @@ -59,7 +60,8 @@ export async function register( getFooter: async () => getFooter(), getApps: async () => addButtons(await getApps({ private: false })), launchApp: async (app) => { - await launch(app as PlatformApp); + // this request has come from the store. + await launch(app as PlatformApp, undefined, getStoreIdentity()); } }); diff --git a/how-to/workspace-platform-starter/client/src/modules/content-creation/view-position/content-creation.ts b/how-to/workspace-platform-starter/client/src/modules/content-creation/view-position/content-creation.ts index ed6d178b47..b345504fbd 100644 --- a/how-to/workspace-platform-starter/client/src/modules/content-creation/view-position/content-creation.ts +++ b/how-to/workspace-platform-starter/client/src/modules/content-creation/view-position/content-creation.ts @@ -85,7 +85,7 @@ export class ViewPositionContentCreationProvider // You can of course locate the view elsewhere as shown using the view-position // feature flag which could be passed to the window.open call - const viewPosition = event.parsedFeatures["view-position"]; + const viewPosition = event.parsedFeatures["view-position"] ?? this._settings?.defaultViewPosition; if (isStringValue(viewPosition) && !isEmpty(event.viewIdentity)) { if ( viewPosition === "right" || diff --git a/how-to/workspace-platform-starter/client/src/modules/content-creation/view-position/shapes.ts b/how-to/workspace-platform-starter/client/src/modules/content-creation/view-position/shapes.ts index 517c76382b..5b0e978efa 100644 --- a/how-to/workspace-platform-starter/client/src/modules/content-creation/view-position/shapes.ts +++ b/how-to/workspace-platform-starter/client/src/modules/content-creation/view-position/shapes.ts @@ -8,4 +8,9 @@ export interface ViewPositionContentCreationSettings { * Rules for the content creation. */ rules?: OpenFin.ContentCreationRule[]; + + /** + * Presents the option of having a default behavior if the window.open features doesn't specify one and a view is targeted. + */ + defaultViewPosition?: "right" | "left" | "top" | "bottom" | "stack-left" | "stack-right"; } diff --git a/how-to/workspace-platform-starter/client/src/modules/integrations/apps/integration.ts b/how-to/workspace-platform-starter/client/src/modules/integrations/apps/integration.ts index 17e1e93125..ea1dbff7eb 100644 --- a/how-to/workspace-platform-starter/client/src/modules/integrations/apps/integration.ts +++ b/how-to/workspace-platform-starter/client/src/modules/integrations/apps/integration.ts @@ -249,7 +249,7 @@ export class AppProvider implements IntegrationModule { if (data?.app?.appId) { handled = true; - await this._integrationHelpers.launchApp(data.app.appId); + await this._integrationHelpers.launchApp(data.app.appId, undefined, result.dispatcherIdentity); } } } diff --git a/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/wps-interop-override.ts b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/wps-interop-override.ts index 3436bd79cb..76578d9277 100644 --- a/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/wps-interop-override.ts +++ b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/wps-interop-override.ts @@ -17,7 +17,6 @@ import { MANIFEST_TYPES } from "workspace-platform-starter/manifest-types"; import type { ConnectionClient, EndpointClient } from "workspace-platform-starter/shapes"; import type { AppsForIntent, - LaunchPreference, PlatformApp, PlatformAppIdentifier } from "workspace-platform-starter/shapes/app-shapes"; @@ -44,7 +43,6 @@ import { randomUUID, sanitizeString } from "workspace-platform-starter/utils"; -import { getWindowPositionUsingStrategy } from "workspace-platform-starter/utils-position"; import { AppIdHelper } from "./app-id-helper"; import { AppIntentHelper } from "./app-intent-helper"; import { getAppsMetaData } from "./app-meta-data-helper"; @@ -665,15 +663,7 @@ export async function getConstructorOverride( } } else { if (isEmpty(platformIdentities)) { - let launchPreference: LaunchPreference | undefined; - const bounds = await getWindowPositionUsingStrategy( - options.windowPositionOptions, - clientIdentity - ); - if (!isEmpty(bounds)) { - launchPreference = { bounds }; - } - platformIdentities = await launch(requestedApp?.appId, launchPreference); + platformIdentities = await launch(requestedApp?.appId, undefined, clientIdentity); } else { focusApp = true; } @@ -954,12 +944,7 @@ export async function getConstructorOverride( } if (platformIdentities.length === 0) { - let launchPreference: LaunchPreference | undefined; - const bounds = await getWindowPositionUsingStrategy(options.windowPositionOptions, clientIdentity); - if (!isEmpty(bounds)) { - launchPreference = { bounds }; - } - platformIdentities = await launch(app.appId, launchPreference); + platformIdentities = await launch(app.appId, undefined, clientIdentity); if (!platformIdentities?.length) { throw new Error(ResolveError.IntentDeliveryFailed); } diff --git a/how-to/workspace-platform-starter/client/src/modules/platform-override/wps-platform-override/platform/platform-mapper.ts b/how-to/workspace-platform-starter/client/src/modules/platform-override/wps-platform-override/platform/platform-mapper.ts index a81780a572..b102a74cdf 100644 --- a/how-to/workspace-platform-starter/client/src/modules/platform-override/wps-platform-override/platform/platform-mapper.ts +++ b/how-to/workspace-platform-starter/client/src/modules/platform-override/wps-platform-override/platform/platform-mapper.ts @@ -185,7 +185,7 @@ export function mapPlatformWorkspaceToStorage( } } // Skip to make platform dependent and remove below - // { propName: "snapshotDetails", defaultValue: "" }, + // { propName: "snapshotDetails", defaultValue: "" } ]; for (const workspaceSnapshotDefault of workspaceSnapshotDefaults) { @@ -196,9 +196,6 @@ export function mapPlatformWorkspaceToStorage( ); } - // remove platform dependent props - removeProp(clone.snapshot, "snapshotDetails"); - return clone; } diff --git a/how-to/workspace-platform-starter/client/types/module/shapes/module-shapes.d.ts b/how-to/workspace-platform-starter/client/types/module/shapes/module-shapes.d.ts index c4f89ec21f..b3688382d1 100644 --- a/how-to/workspace-platform-starter/client/types/module/shapes/module-shapes.d.ts +++ b/how-to/workspace-platform-starter/client/types/module/shapes/module-shapes.d.ts @@ -166,11 +166,13 @@ export interface ModuleHelpers { * the current user. * @param appId The id of the application that is registered against the currently running platform * @param launchPreference If the app supports launch preferences then these can be passed. + * @param callerIdentity The identity of the caller to consider when determining x/y of new window. * @returns An array of the platform identities that related from the launch or nothing if nothing was launched. */ launchApp?( appId: string, - launchPreference?: UpdatableLaunchPreference + launchPreference?: UpdatableLaunchPreference, + callerIdentity?: OpenFin.Identity ): Promise; /** * Launch a page in the workspace. diff --git a/how-to/workspace-platform-starter/public/manifest.fin.json b/how-to/workspace-platform-starter/public/manifest.fin.json index c7c04e2131..2a61e2a818 100644 --- a/how-to/workspace-platform-starter/public/manifest.fin.json +++ b/how-to/workspace-platform-starter/public/manifest.fin.json @@ -1826,9 +1826,10 @@ "id": "view-position-content-creation", "icon": "http://localhost:8080/favicon.ico", "title": "View Position Content Creation", - "enabled": false, + "enabled": true, "url": "http://localhost:8080/js/modules/content-creation/view-position.bundle.js", "data": { + "defaultViewPosition": "stack-right", "rules": [ { "behavior": "window", @@ -1838,10 +1839,6 @@ "defaultHeight": 600 } }, - { - "behavior": "view", - "match": ["https://www.scientificamerican.com/*"] - }, { "behavior": "browser", "match": ["https://www.livescience.com/*"] @@ -1849,6 +1846,10 @@ { "behavior": "block", "match": ["https://www.sciencenews.org/*"] + }, + { + "behavior": "view", + "match": ["https://*", "http://*"] } ] }