SDA-4770 Expose openfin (#2264)

This commit is contained in:
Salah Benmoussati 2025-01-16 08:43:01 +01:00 committed by GitHub
parent 8a6704b449
commit b27faedc28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 730 additions and 5 deletions

View File

@ -47,5 +47,11 @@
"userDataPath": "",
"chromeFlags": "",
"betaAutoUpdateChannelEnabled": true,
"latestAutoUpdateChannelEnabled": true
"latestAutoUpdateChannelEnabled": true,
"openfin": {
"uuid": "",
"licenseKey": "",
"runtimeVersion": "",
"autoConnect": false
}
}

View File

@ -123,6 +123,10 @@ if [ "$EUID" -ne 0 ]; then
defaults write "$plistFilePath" betaAutoUpdateChannelEnabled -bool true
defaults write "$plistFilePath" latestAutoUpdateChannelEnabled -bool true
defaults write "$plistFilePath" installVariant -string "$uuid"
defaults write "$plistFilePath" uuid -string ""
defaults write "$plistFilePath" licenseKey -string ""
defaults write "$plistFilePath" runtimeVersion -string ""
defaults write "$plistFilePath" autoConnect -bool false
else
sudo -u "$userName" defaults write "$plistFilePath" url -string "$pod_url"
sudo -u "$userName" defaults write "$plistFilePath" autoUpdateUrl -string ""
@ -168,6 +172,10 @@ else
sudo -u "$userName" defaults write "$plistFilePath" betaAutoUpdateChannelEnabled -bool true
sudo -u "$userName" defaults write "$plistFilePath" latestAutoUpdateChannelEnabled -bool true
sudo -u "$userName" defaults write "$plistFilePath" installVariant -string "$uuid"
sudo -u "$userName" defaults write "$plistFilePath" uuid -string ""
sudo -u "$userName" defaults write "$plistFilePath" licenseKey -string ""
sudo -u "$userName" defaults write "$plistFilePath" runtimeVersion -string ""
sudo -u "$userName" defaults write "$plistFilePath" autoConnect -bool false
fi
## Remove the temp settings & permissions file created ##

56
package-lock.json generated
View File

@ -10,6 +10,7 @@
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@openfin/node-adapter": "^40.101.1",
"@types/lazy-brush": "^1.0.0",
"adm-zip": "^0.5.10",
"bplist-parser": "^0.3.2",
@ -2636,6 +2637,59 @@
"node": ">= 10.0.0"
}
},
"node_modules/@openfin/core": {
"version": "40.101.1",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@openfin/core/-/core-40.101.1.tgz",
"integrity": "sha512-jXAsWz0R+g26k+6+Jps9BaAsAR3VURnjX1ZVFZ4toyS7q+NHysF1oUxE4nBKSKTgOBAqX+TqAvO7ZaUhPTrlNw==",
"license": "SEE LICENSE IN LICENSE.md",
"dependencies": {
"@types/node": "^20.14.2",
"lodash": "^4.17.21",
"ws": "^7.3.0"
}
},
"node_modules/@openfin/core/node_modules/@types/node": {
"version": "20.17.13",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@types/node/-/node-20.17.13.tgz",
"integrity": "sha512-RNf+4dEeV69PIvyp++4IKM2vnLXtmp/JovfeQm5P5+qpKb6wHoH7INywLdZ7z+gVX46kgBP/fwJJvZYaHxtdyw==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/@openfin/core/node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"license": "MIT"
},
"node_modules/@openfin/node-adapter": {
"version": "40.101.1",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@openfin/node-adapter/-/node-adapter-40.101.1.tgz",
"integrity": "sha512-b3uKfE8OulvV2xp3aAB7QMvY013LI8GBPjypEYgZx7qW0b5g6SNHu2eVzOJf5Nl3SJVUoBu6TawtpKZo8Qtewg==",
"license": "SEE LICENSE IN LICENSE.md",
"dependencies": {
"@openfin/core": "40.101.1",
"@types/node": "^20.14.2",
"lodash": "^4.17.21",
"ws": "^7.3.0"
}
},
"node_modules/@openfin/node-adapter/node_modules/@types/node": {
"version": "20.17.6",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@types/node/-/node-20.17.6.tgz",
"integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/@openfin/node-adapter/node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"license": "MIT"
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -12038,7 +12092,6 @@
},
"node_modules/lodash": {
"version": "4.17.21",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.debounce": {
@ -17820,7 +17873,6 @@
},
"node_modules/ws": {
"version": "7.4.4",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.3.0"

View File

@ -221,6 +221,7 @@
"typescript": "^4.9.5"
},
"dependencies": {
"@openfin/node-adapter": "^40.101.1",
"@types/lazy-brush": "^1.0.0",
"adm-zip": "^0.5.10",
"bplist-parser": "^0.3.2",

View File

@ -98,6 +98,7 @@ describe('config', () => {
'latestAutoUpdateChannelEnabled',
'betaAutoUpdateChannelEnabled',
'browserLoginRetryTimeout',
'openfin',
];
const globalConfig: object = { url: 'test' };
const userConfig: object = { configVersion: '4.0.1' };

View File

@ -86,6 +86,10 @@ jest.mock('../src/renderer/notification', () => {
};
});
jest.mock('@openfin/node-adapter', () => ({
connect: jest.fn(),
}));
jest.mock('electron-log');
describe('dialog handler', () => {

View File

@ -2,6 +2,7 @@ import { activityDetection } from '../src/app/activity-detection';
import * as c9PipeHandler from '../src/app/c9-pipe-handler';
import { downloadHandler } from '../src/app/download-handler';
import '../src/app/main-api-handler';
import { openfinHandler } from '../src/app/openfin-handler';
import { protocolHandler } from '../src/app/protocol-handler';
import { screenSnippet } from '../src/app/screen-snippet-handler';
import * as windowActions from '../src/app/window-actions';
@ -10,9 +11,56 @@ import * as utils from '../src/app/window-utils';
import { apiCmds, apiName } from '../src/common/api-interface';
import { logger } from '../src/common/logger';
import { BrowserWindow, ipcMain } from './__mocks__/electron';
import { connect } from '@openfin/node-adapter';
jest.mock('electron-log');
jest.mock('../src/app/openfin-handler', () => {
return {
openfinHandler: {
connect: jest.fn(),
fireIntent: jest.fn(),
joinContextGroup: jest.fn(),
getContextGroups: jest.fn(),
getConnectionStatus: jest.fn(),
getInfo: jest.fn(),
getAllClientsInContextGroup: jest.fn(),
registerIntentHandler: jest.fn(),
unregisterIntentHandler: jest.fn(),
},
};
});
jest.mock('@openfin/node-adapter', () => ({
connect: jest.fn(),
}));
(connect as jest.Mock).mockResolvedValue({
Interop: {
connectSync: jest.fn().mockReturnValue({
onDisconnection: jest.fn(),
fireIntent: jest.fn(),
registerIntentHandler: jest.fn(),
}),
},
});
jest.mock('../src/app/config-handler', () => {
return {
config: {
getConfigFields: jest.fn(() => {
return {
openfin: {
uuid: 'some-uuid',
licenseKey: 'some-license-key',
runtimeVersion: 'some-runtime-version',
},
};
}),
},
};
});
jest.mock('../src/app/protocol-handler', () => {
return {
protocolHandler: {
@ -553,4 +601,114 @@ describe('main api handler', () => {
expect(spy).toBeCalledWith(...expectedValue);
});
});
describe('openfin api events', () => {
beforeEach(() => {
(connect as jest.Mock).mockResolvedValue({
Interop: {
connectSync: jest.fn().mockReturnValue({
onDisconnection: jest.fn(),
}),
},
});
(windowHandler.getMainWebContents as jest.Mock).mockReturnValue({
send: jest.fn(),
});
});
afterEach(() => {
jest.clearAllMocks();
});
it('should call `connect` correctly', () => {
const spy = jest.spyOn(openfinHandler, 'connect');
const value = {
cmd: apiCmds.openfinConnect,
};
ipcMain.send(apiName.symphonyApi, value);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should call `fireIntent`', () => {
const spy = jest.spyOn(openfinHandler, 'fireIntent');
const value = {
cmd: apiCmds.openfinFireIntent,
intent: {
name: 'ViewContact',
context: {
type: 'fdc3.contact',
name: 'Andy Young',
id: {
email: 'andy.young@example.com',
},
},
},
};
ipcMain.send(apiName.symphonyApi, value);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should call `registerIntentHandler`', () => {
const spy = jest.spyOn(openfinHandler, 'registerIntentHandler');
const value = {
cmd: apiCmds.openfinRegisterIntentHandler,
intentName: 'ViewContact',
};
ipcMain.send(apiName.symphonyApi, value);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should call `unregisterIntentHandler`', () => {
const spy = jest.spyOn(openfinHandler, 'unregisterIntentHandler');
const value = {
cmd: apiCmds.openfinUnregisterIntentHandler,
intentName: 'ViewContact',
};
ipcMain.send(apiName.symphonyApi, value);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should call `joinContextGroup`', () => {
const spy = jest.spyOn(openfinHandler, 'joinContextGroup');
const value = {
cmd: apiCmds.openfinJoinContextGroup,
contextGroupId: 'group-id',
};
ipcMain.send(apiName.symphonyApi, value);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should call `getContextGroups`', () => {
const spy = jest.spyOn(openfinHandler, 'getContextGroups');
const value = {
cmd: apiCmds.openfinGetContextGroups,
};
ipcMain.send(apiName.symphonyApi, value);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should call `getAllClientsInContextGroup`', () => {
const spy = jest.spyOn(openfinHandler, 'getAllClientsInContextGroup');
const value = {
cmd: apiCmds.openfinGetAllClientsInContextGroup,
contextGroupId: 'group-id',
};
ipcMain.send(apiName.symphonyApi, value);
expect(spy).toHaveBeenCalledTimes(1);
});
});
});

130
spec/openfinHandler.spec.ts Normal file
View File

@ -0,0 +1,130 @@
import { openfinHandler } from '../src/app/openfin-handler';
import { connect } from '@openfin/node-adapter';
jest.mock('@openfin/node-adapter', () => ({
connect: jest.fn(),
}));
(connect as jest.Mock).mockResolvedValue({
Interop: {
connectSync: jest.fn().mockReturnValue({
onDisconnection: jest.fn(),
fireIntent: jest.fn(),
registerIntentHandler: jest.fn(),
getAllClientsInContextGroup: jest.fn(),
joinContextGroup: jest.fn(),
getContextGroups: jest.fn(),
}),
},
});
jest.mock('../src/app/config-handler', () => ({
config: {
getConfigFields: jest.fn(() => ({
openfin: {
uuid: 'mock-uuid',
licenseKey: 'mock-license',
runtimeVersion: 'mock-version',
},
})),
},
}));
jest.mock('../src/app/window-handler', () => {
return {
windowHandler: {
getMainWebContents: jest.fn(),
},
};
});
describe('Openfin', () => {
let connectMock;
beforeAll(async () => {
connectMock = await connect({} as any);
});
it('should not be connected', () => {
const info = openfinHandler.getInfo();
const isConnected = openfinHandler.getConnectionStatus();
expect(info.isConnected).toBeFalsy();
expect(isConnected).toBeFalsy();
});
it('should connect', async () => {
const connectSyncSpy = jest.spyOn(connectMock.Interop, 'connectSync');
await openfinHandler.connect();
const info = openfinHandler.getInfo();
const isConnected = openfinHandler.getConnectionStatus();
expect(connect).toHaveBeenCalled();
expect(connectSyncSpy).toHaveBeenCalledTimes(1);
expect(info.isConnected).toBeTruthy();
expect(isConnected).toBeTruthy();
});
it('should fire an intent', async () => {
const connectSyncMock = await connectMock.Interop.connectSync();
const fireIntentSpy = jest.spyOn(connectSyncMock, 'fireIntent');
await openfinHandler.connect();
const customIntent = {
type: 'fdc3.contact',
name: 'Andy Young',
id: {
email: 'andy.young@example.com',
},
};
await openfinHandler.fireIntent(customIntent);
expect(fireIntentSpy).toHaveBeenCalledTimes(1);
});
it('should register an intent handler', async () => {
const connectSyncMock = await connectMock.Interop.connectSync();
const intentHandlerRegistrationSpy = jest.spyOn(
connectSyncMock,
'registerIntentHandler',
);
await openfinHandler.connect();
await openfinHandler.registerIntentHandler('my-intent');
expect(intentHandlerRegistrationSpy).toHaveBeenCalledTimes(1);
});
it('should join a context group', async () => {
const connectSyncMock = await connectMock.Interop.connectSync();
const joinContextGroupSpy = jest.spyOn(connectSyncMock, 'joinContextGroup');
await openfinHandler.connect();
await openfinHandler.joinContextGroup('contextGroupId');
expect(joinContextGroupSpy).toHaveBeenCalledTimes(1);
});
it('should return all context groups', async () => {
const connectSyncMock = await connectMock.Interop.connectSync();
const getContextGroupsSpy = jest.spyOn(connectSyncMock, 'getContextGroups');
await openfinHandler.connect();
await openfinHandler.getContextGroups();
expect(getContextGroupsSpy).toHaveBeenCalledTimes(1);
});
it('should return all clients in a given context group', async () => {
const connectSyncMock = await connectMock.Interop.connectSync();
const getAllClientsInContextGroupSpy = jest.spyOn(
connectSyncMock,
'getAllClientsInContextGroup',
);
await openfinHandler.connect();
await openfinHandler.getAllClientsInContextGroup('contextGroup1');
expect(getAllClientsInContextGroupSpy).toHaveBeenCalledTimes(1);
});
});

View File

@ -91,6 +91,12 @@ describe('Plist Handler', () => {
whitelistUrl: undefined,
chromeFlags: undefined,
latestAutoUpdateChannelEnabled: undefined,
openfin: {
autoConnect: undefined,
licenseKey: undefined,
runtimeVersion: undefined,
uuid: undefined,
},
});
});
});

View File

@ -34,6 +34,7 @@ describe('Shell Script Field Validation', () => {
'notificationSettings',
'customFlags',
'permissions',
'openfin',
]);
// Read fields from post install script file
@ -54,12 +55,12 @@ describe('Shell Script Field Validation', () => {
'notificationSettings',
'customFlags',
'permissions',
'openfin',
]);
// Read fields from post install script file
const scriptFields = extractSystemDefaults(scriptFilePath);
scriptFields.splice(scriptFields.indexOf('ApplicationName'), 1);
expect(isArraySubset(scriptFields, filteredFields)).toBe(true);
});
});

View File

@ -83,6 +83,7 @@ export interface IConfig {
isPodUrlEditable?: boolean;
sdaInstallerMsiUrlEnabledVisible?: boolean;
sdaInstallerMsiUrlBetaEnabledVisible?: boolean;
openfin?: IOpenfin;
}
export interface IGlobalConfig {
@ -163,6 +164,13 @@ export interface ICustomRectangle extends Partial<Electron.Rectangle> {
isFullScreen?: boolean;
}
export interface IOpenfin {
uuid: string;
licenseKey: string;
runtimeVersion: string;
autoConnect: boolean;
}
class Config {
public userConfig: IConfig | {};
public globalConfig: IConfig | {};

View File

@ -59,6 +59,7 @@ import { getCommandLineArgs } from '../common/utils';
import callNotificationHelper from '../renderer/call-notification-helper';
import { autoUpdate, AutoUpdateTrigger } from './auto-update-handler';
import { SDAUserSessionActionTypes } from './bi/interface';
import { openfinHandler } from './openfin-handler';
import { presenceStatus } from './presence-status-handler';
import { appStats } from './stats';
import { presenceStatusStore, sdaMenuStore } from './stores/index';
@ -559,6 +560,21 @@ ipcMain.on(
helpMenu.setValue(helpCenter);
break;
case apiCmds.openfinConnect:
openfinHandler.connect();
break;
case apiCmds.openfinFireIntent:
openfinHandler.fireIntent(arg.intent);
break;
case apiCmds.openfinJoinContextGroup:
openfinHandler.joinContextGroup(arg.contextGroupId, arg.target);
break;
case apiCmds.openfinRegisterIntentHandler:
openfinHandler.registerIntentHandler(arg.intentName);
break;
case apiCmds.openfinUnregisterIntentHandler:
openfinHandler.unregisterIntentHandler(arg.intentName);
break;
default:
break;
}
@ -627,6 +643,14 @@ ipcMain.handle(
return getContentWindowHandle(windowHandle);
}
break;
case apiCmds.openfinGetConnectionStatus:
return openfinHandler.getConnectionStatus();
case apiCmds.openfinGetInfo:
return openfinHandler.getInfo();
case apiCmds.openfinGetContextGroups:
return openfinHandler.getContextGroups();
case apiCmds.openfinGetAllClientsInContextGroup:
return openfinHandler.getAllClientsInContextGroup(arg.contextGroupId);
default:
break;
}

140
src/app/openfin-handler.ts Normal file
View File

@ -0,0 +1,140 @@
import { connect } from '@openfin/node-adapter';
import { logger } from '../common/openfin-logger';
import { config, IConfig } from './config-handler';
import { windowHandler } from './window-handler';
export class OpenfinHandler {
private interopClient;
private intentHandlerSubscriptions = new Map();
private isConnected: boolean = false;
/**
* Connection to interop brocker
*/
public async connect() {
logger.info('openfin-handler: connecting');
const { openfin }: IConfig = config.getConfigFields(['openfin']);
if (openfin) {
const fin = await connect({
uuid: openfin.uuid,
licenseKey: openfin.licenseKey,
runtime: {
version: openfin.runtimeVersion,
},
});
logger.info('openfin-handler: connected');
logger.info('openfin-handler: connecting to interop broker');
this.interopClient = fin.Interop.connectSync(
'workspace-platform-starter',
);
this.isConnected = true;
this.interopClient.onDisconnection((event) => {
const { brokerName } = event;
logger.warn(
`openfin-handler: Disconnected from Interop Broker ${brokerName} `,
);
this.clearSubscriptions();
});
return;
}
logger.error('openfin-handler: missing openfin params to connect.');
}
/**
* Sends an intent to the Interop Broker
*/
public fireIntent(intent) {
this.interopClient.fireIntent(intent);
}
/**
* Adds an intent handler for incoming intents
*/
public async registerIntentHandler(intentName: string) {
const unsubscriptionCallback =
await this.interopClient.registerIntentHandler(
this.intentHandler,
intentName,
);
this.intentHandlerSubscriptions.set(intentName, unsubscriptionCallback);
}
/**
* Removes an intent handler for a given intent
*/
public unregisterIntentHandler(intentName) {
const unsubscriptionCallback =
this.intentHandlerSubscriptions.get(intentName);
unsubscriptionCallback.unsubscribe();
this.intentHandlerSubscriptions.delete(intentName);
}
/**
* Join all Interop Clients at the given identity to context group contextGroupId. If no target is specified, it adds the sender to the context group.
*/
public async joinContextGroup(contextGroupId: string, target?: any) {
await this.interopClient.joinContextGroup(contextGroupId, target);
}
/**
* Returns the Interop-Broker-defined context groups available for an entity to join.
*/
public async getContextGroups() {
return this.interopClient.getContextGroups();
}
/**
* Gets all clients for a context group.
*/
public getAllClientsInContextGroup(contextGroupId: string) {
return this.interopClient.getAllClientsInContextGroup(contextGroupId);
}
/**
* Clears all openfin subscriptions
*/
public clearSubscriptions() {
this.isConnected = false;
this.interopClient = undefined;
this.intentHandlerSubscriptions.forEach(
(unsubscriptionCallback, intent) => {
try {
unsubscriptionCallback.unsubscribe();
} catch (e) {
logger.error(
`openfin-handler: Error unsubscribing from intent ${intent}:`,
e,
);
}
},
);
this.intentHandlerSubscriptions.clear();
}
/**
* Returns openfin connection status
*/
public getConnectionStatus(): boolean {
return this.isConnected;
}
/**
* Returns connection status and provider name
*/
public getInfo() {
return {
provider: 'Openfin',
isConnected: this.getConnectionStatus(),
};
}
private intentHandler = (intent: any) => {
logger.info('openfin-handler: intent received - ', intent);
const mainWebContents = windowHandler.getMainWebContents();
mainWebContents?.send('intent-received', intent.name);
};
}
const openfinHandler = new OpenfinHandler();
export { openfinHandler };

View File

@ -65,6 +65,13 @@ const PERMISSIONS = {
openExternal: 'boolean',
};
const OPENFIN = {
uuid: 'string',
licenseKey: 'string',
runtimeVersion: 'string',
autoConnect: 'boolean',
};
export const getAllUserDefaults = (): IConfig => {
const settings: any = {};
@ -113,6 +120,14 @@ export const getAllUserDefaults = (): IConfig => {
PERMISSIONS[key],
);
});
Object.keys(OPENFIN).map((key) => {
if (!settings.openfin) {
settings.openfin = {};
}
settings.openfin[key] = systemPreferences.getUserDefault(key, OPENFIN[key]);
});
logger.info('plist-handler: getting all user defaults', settings);
return settings;
};
@ -161,6 +176,18 @@ export const setPlistFromPreviousSettings = (
}
systemPreferences.setUserDefault(key, PERMISSIONS[key], value);
});
Object.keys(OPENFIN).map((key) => {
let value = settings?.openfin?.[key];
if (value === undefined) {
if (appGlobalConfig?.openfin?.[key] === undefined) {
return;
}
value = appGlobalConfig.openfin[key];
}
systemPreferences.setUserDefault(key, OPENFIN[key], value);
});
systemPreferences.setUserDefault('installVariant', 'string', getGuid());
};

View File

@ -55,6 +55,7 @@ import { notification } from '../renderer/notification';
import { autoLaunchInstance } from './auto-launch-controller';
import { autoUpdate, AutoUpdateTrigger } from './auto-update-handler';
import { mainEvents } from './main-event-handler';
import { openfinHandler } from './openfin-handler';
import { presenceStatus } from './presence-status-handler';
import { presenceStatusStore } from './stores';
interface IStyles {
@ -472,7 +473,8 @@ export const sanitize = (windowName: string): void => {
if (mainWindow && windowName === mainWindow.winName) {
// reset the badge count whenever an user refreshes the electron client
showBadgeCount(0);
// Clear all openfin subscriptions
openfinHandler.clearSubscriptions();
// Terminates the screen snippet process and screen share indicator frame on reload
if (!isMac || !isLinux) {
logger.info(

View File

@ -1,3 +1,4 @@
import { UUID } from 'crypto';
import { NativeImage, Size, Tray } from 'electron';
import { AutoUpdateTrigger } from '../app/auto-update-handler';
@ -82,6 +83,16 @@ export enum apiCmds {
registerPhoneNumberServices = 'register-phone-numbers-services',
unregisterPhoneNumberServices = 'unregister-phone-numbers-services',
getHelpInfo = 'get-help-info',
// Openfin API commands
openfinConnect = 'openfin-connect',
openfinFireIntent = 'openfin-fire-intent',
openfinRegisterIntentHandler = 'openfin-register-intent-handler',
openfinUnregisterIntentHandler = 'openfin-unregister-intent-handler',
openfinGetConnectionStatus = 'openfin-get-connection-status',
openfinGetInfo = 'openfin-get-info',
openfinJoinContextGroup = 'openfin-join-context-group',
openfinGetContextGroups = 'openfin-get-context-groups',
openfinGetAllClientsInContextGroup = 'openfin-get-all-clients-in-context-group',
}
export enum apiName {
@ -149,6 +160,18 @@ export interface IApiArgs {
status: IPresenceStatus;
protocols: PhoneNumberProtocol[];
menu?: any;
handler: any;
uuid: UUID;
intent: any;
intentHandler: any;
intentName: any;
infoForIntentOptions: any;
context: any;
sessionContextGroupId: any;
contextForIntent: any;
contextType: any;
contextGroupId: string;
target: any;
}
export type Themes = 'light' | 'dark';

View File

@ -8,4 +8,10 @@ export const ConfigFieldsDefaultValues: Partial<IConfig> = {
latestAutoUpdateChannelEnabled: true,
betaAutoUpdateChannelEnabled: true,
browserLoginRetryTimeout: '5',
openfin: {
uuid: '',
licenseKey: '',
runtimeVersion: '',
autoConnect: false,
},
};

View File

@ -0,0 +1,5 @@
import { Logger } from './loggerBase';
const logger = new Logger('openfin');
export { logger };

View File

@ -105,6 +105,19 @@ if (ssfWindow.ssf) {
registerPhoneNumberServices: ssfWindow.ssf.registerPhoneNumberServices,
unregisterPhoneNumberServices: ssfWindow.ssf.unregisterPhoneNumberServices,
});
contextBridge.exposeInMainWorld('openfin', {
init: ssfWindow.ssf.openfinInit,
getInfo: ssfWindow.ssf.openfinGetInfo,
getConnectionStatus: ssfWindow.ssf.openfinGetConnectionStatus,
fireIntent: ssfWindow.ssf.openfinFireIntent,
registerIntentHandler: ssfWindow.ssf.openfinRegisterIntentHandler,
unregisterIntentHandler: ssfWindow.ssf.openfinUnregisterIntentHandler,
getContextGroups: ssfWindow.ssf.openfinGetContextGroups,
joinContextGroup: ssfWindow.ssf.openfinJoinContextGroup,
getAllClientsInContextGroup:
ssfWindow.ssf.openfinGetAllClientsInContextGroup,
});
}
/**

View File

@ -66,12 +66,14 @@ export interface ILocalObject {
c9MessageCallback?: (status: IShellStatus) => void;
updateMyPresenceCallback?: (presence: EPresenceStatusCategory) => void;
phoneNumberCallback?: (arg: string) => void;
intentsCallbacks: {};
writeImageToClipboard?: (blob: string) => void;
getHelpInfo?: () => Promise<IPodSettingsClientSpecificSupportLink>;
}
const local: ILocalObject = {
ipcRenderer,
intentsCallbacks: {},
};
const notificationActionCallbacks = new Map<
@ -952,6 +954,107 @@ export class SSFApi {
});
}
/**
* Openfin Interop client initialization
*/
public openfinInit(): void {
local.ipcRenderer.send(apiName.symphonyApi, {
cmd: apiCmds.openfinConnect,
});
}
/**
* Returns provider and connection status
*/
public async openfinGetInfo() {
const info = await local.ipcRenderer.invoke(apiName.symphonyApi, {
cmd: apiCmds.openfinGetInfo,
});
return info;
}
/**
* Fires an intent
*/
public openfinFireIntent(intent: any): void {
local.ipcRenderer.send(apiName.symphonyApi, {
cmd: apiCmds.openfinFireIntent,
intent,
});
}
/**
*
* Returns Openfin connection status
*/
public async openfinGetConnectionStatus() {
const connectionStatus = await local.ipcRenderer.invoke(
apiName.symphonyApi,
{
cmd: apiCmds.openfinGetConnectionStatus,
},
);
return connectionStatus;
}
/**
* Registers a handler for a given intent
*/
public openfinRegisterIntentHandler(
intentHandler: any,
intentName: any,
): void {
local.intentsCallbacks[intentName] = intentHandler;
local.ipcRenderer.send(apiName.symphonyApi, {
cmd: apiCmds.openfinRegisterIntentHandler,
intentName,
});
}
/**
* Unregisters a handler based on a given intent name
* @param intentName
*/
public openfinUnregisterIntentHandler(intentName: string): void {
local.ipcRenderer.send(apiName.symphonyApi, {
cmd: apiCmds.openfinUnregisterIntentHandler,
intentName,
});
}
/**
* Returns openfin context groups
*/
public async openfinGetContextGroups() {
const contextGroups = await local.ipcRenderer.invoke(apiName.symphonyApi, {
cmd: apiCmds.openfinGetContextGroups,
});
return contextGroups;
}
/**
* Allows to join an Openfin context group
* @param contextGroupId
* @param target
*/
public openfinJoinContextGroup(contextGroupId: string, target?: any) {
local.ipcRenderer.send(apiName.symphonyApi, {
cmd: apiCmds.openfinJoinContextGroup,
contextGroupId,
target,
});
}
/**
* Returns registered clients in a given context group
*/
public openfinGetAllClientsInContextGroup(contextGroupId: string) {
return local.ipcRenderer.invoke(apiName.symphonyApi, {
cmd: apiCmds.openfinGetAllClientsInContextGroup,
contextGroupId,
});
}
/**
* Allows JS to register SDA for phone numbers clicks
* @param {Function} phoneNumberCallback callback function invoked when receiving a phone number for calls/sms
@ -1290,6 +1393,12 @@ local.ipcRenderer.on(
},
);
local.ipcRenderer.on('intent-received', (_event: Event, intentName: string) => {
if (typeof intentName === 'string' && local.intentsCallbacks[intentName]) {
local.intentsCallbacks[intentName]();
}
});
// Invoked whenever the app is reloaded/navigated
const sanitize = (): void => {
if (window.name === apiName.mainWindowName) {
@ -1298,6 +1407,7 @@ const sanitize = (): void => {
windowName: window.name,
});
}
local.intentsCallbacks = {};
};
// listens for the online/offline events and updates the main process