Skip to content

Commit

Permalink
Merge branch 'main' into dev/john/snap-1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
johnman committed Oct 28, 2024
2 parents 9ee4408 + ee19b94 commit 7ada8d9
Show file tree
Hide file tree
Showing 38 changed files with 995 additions and 323 deletions.
45 changes: 42 additions & 3 deletions how-to/integrate-with-excel/client/src/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}

/**
Expand Down Expand Up @@ -92,20 +92,51 @@ 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: "[email protected]",
supportEmail: "[email protected]",
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.
* @returns The value returned by the launch.
*/
export async function launchApp(
app: App
): Promise<OpenFin.Platform | OpenFin.Identity | OpenFin.View | OpenFin.Application | undefined> {
): 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);

Expand All @@ -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;
Expand Down
189 changes: 15 additions & 174 deletions how-to/integrate-with-excel/client/src/excel-integration.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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.
Expand All @@ -44,30 +31,15 @@ export class ExcelIntegration {
public async initialize(settings: ExcelSettings): Promise<void> {
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);
}

/**
* The module is being deregistered.
* @returns Nothing.
*/
public async closedown(): Promise<void> {
for (const client in this._interopClients) {
await this._interopClients[client].removeFromContextGroup();
}
this._interopClients = {};
await closeDownExcel();
}

/**
Expand All @@ -76,19 +48,19 @@ export class ExcelIntegration {
* @returns The list of results and new filters.
*/
public async getSearchResults(query: string): Promise<HomeSearchResponse> {
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: []
};
}

Expand All @@ -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;
}

Expand Down Expand Up @@ -173,97 +107,4 @@ export class ExcelIntegration {
templateContent: excelAsset.description
};
}

/**
* Get the excel application.
* @returns The application.
* @internal
*/
private async getExcel(): Promise<ExcelApplication | undefined> {
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<void> {
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<void> {
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]]);
}
}
}
}
}
}
}
}
}
}
}
}
59 changes: 59 additions & 0 deletions how-to/integrate-with-excel/client/src/excel-window.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { init as initExcel, launchExcel } from "./excel";
import type { ExcelSettings } from "./shapes";

window.addEventListener("DOMContentLoaded", async () => {
const root = "http://localhost:8080";
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);
});
Loading

0 comments on commit 7ada8d9

Please sign in to comment.