Skip to content
This repository has been archived by the owner on Sep 7, 2018. It is now read-only.

Feature/api definitions #171

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,6 @@ build/

# Visual Studio Code
.vscode


.idea/
33 changes: 33 additions & 0 deletions packages/api-specification/api/application.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export interface ApplicationAPI {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to clarify the meaning of Application for OpenFin, as currently OpenFin windows that are not child windows are created using fin.desktop.Application(). Would this API be used for each OpenFin application, or ContainerJS application? If we want this per ContainerJS application, then we might need to rethink the setBadgeCount() or the window creation implementation as each ContainerJS application could have multiple top level windows, which could have different badge counts.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess a wider question is, should the API support multiple applications?

With OpenFin you can create a new Application instance through the JavaScript API.

Personally I think this is a bit of an edge case. I can't see when you'd want to do this in practice? I'd be happy with ContainerJS treating the Application as a singleton.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know that.
We should decide if we want to have access to other applications and be able to start some of them (however I'm not sure how other apps will be discovered in Electron case)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can keep it as is for the time being, and have this as a single ContainerJS application. We can always change it in the future if we hit any problems

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in general need to think about which aspects are mandatory and optional

/**
* Returns an array containing the command line arguments passed when application was launched
*/
argv: string[];

/**
* Application configuration object (as defined in app.json file)
*/
config: ApplicationConfig;

/**
* The current value displayed in the counter badge.
*/
getBadgeCount(): number;

/**
* Sets the counter badge for current app. Setting the count to 0 will hide the badge.
*/
setBadgeCount(count: number);

/**
* Shutdown the application
*/
shutdown: () => void;
}

export interface ApplicationConfig {
name: string;
url: string;
uuid: string;
autoShow: boolean;
}
20 changes: 20 additions & 0 deletions packages/api-specification/api/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// contains common types
export interface Bounds {
top: number;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Electron, top and left are x and y. If we are trying to stick to the Electron API as much as possible then maybe this should be changed?

left: number;
width: number;
height: number;
}

export interface Size{
width: number;
height: number;
}

/**
* Function returned from event subscription functions (e.g. onWindowBoundsChanged).
* Executing it will remove the event handler.
*/
export interface UnsubscribeFunction {
(): void;
}
111 changes: 111 additions & 0 deletions packages/api-specification/api/confluence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
## [Activate API](https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/Activate+API)

> This api allows a window to be move to the front and given focus. This API is needed since JS web apps have no capability to perform such a function.
> An example use case might be: the main window might be minimized (or otherwise hidden) and the notification shows that the user then clicks on. The web app needs to ability to bring the window to the foreground and give focus.

The API proposal introduces window management section that enables the user to create, discover and manage windows.
It also defines a window object that has a set of properties and methods.

One of these methods is activate that will bring the window to front and focus it:
```javascript
// activate my window
var myWindow = ssf.windows.current;
myWindow.activate();

// activate some other window
var someWindow = ssf.windows.getById('4');
someWindow.activate();
```

## [RegisterBoundsChange API](https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/RegisterBoundsChange+API)
> This API allows JS web app to register a callback that will be notified whenever any child window's size or position changes. The primary motivation is to support a feature called "saved layout". This feature allows the symphony web app to save the size and location of child windows and restore them when the app is restarted (or refreshed).

Using the windows API the application can subscribe for
```javascript
ssf.windows.onWindowBoundsChanged(function(window, bounds){

})
```

or subscribe for each window individually
```javascript
someWindow.on('boundsChanged', function(bounds){

});
```

or iterate the windows when the layout should be saved
```javascript
ssf.windows.all.forEach(function(window){
var boundsToSave = window.getBounds();
});
```
## [Activity API](https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/Activity+API)
> The application needs to be informed if activity (keyboard or mouse input) has occurred.

Another section in the proposal is system API that allows the application to access system specific stuff.

The application can subscribe for user activity events:
```javascript
ssf.system.onUserActivity(function(){

}, 1000);
```

## [getMediaSources API](https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/getMediaSources+API) and [ScreenSnippet API](https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/ScreenSnippet+API)
> In order to support screen sharing the client api needs enumerate screens and windows.
> This api provided a list of screens (each monitor) and list of windows available; providing title, id and thumbnail. This api is essentially equivalent of electron api: https://electron.atom.io/docs/api/desktop-capturer/#desktopcapturergetsourcesoptions-callback

> The ScreenSnippet API is used to capture a snippet of their desktop (unconstrained by the host application window) and highlight portions of this snippet so it can then be consumed by the host application. This functionality is similar to the Windows Screen Snippet tool when used in rectangle capture mode. This lets a user captures portions of the Windows Desktop, highlight aspects of the image and then save this image for sharing.
Using the system API the user can enumerate the displays and capture any of them
```javascript
// capture all screens
ssf.system.capture().then(function(image){

});

// capture each screen as separate image
ssf.system.captureAllDisplays().then(function(images) {

});

// capture specific display only
ssf.system.displays[0].capture();

// Capture all windows as separate images with custom size
ssf.system.captureAllWindows({imageSize: {width: 100, height: 100}})
.then(function(images){

});

// Capture specific window (this goes throught the window API)
var window = ssf.windows.current;
window.capture();
```

## [BadgeCount API](https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/BadgeCount+API)
> Display a number on the application icon in the desktop tray... usually to indicate number of unread messages for given application.

There is application section that provides information about application config and allows
interactions with app specific functionalities like badgeCounts

```javascript
ssf.application.setBadgeCount(11);
```
## [Notification API](https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/Notification+API)
>The Notifications API is used to configure and display desktop notifications to the user. It is exposed as an extension to the HTML5 Notification API, providing additional functionality that is useful for financial desktop applications.
>
>The exact visual style and the extent of the OS-level integration is container dependent and hence out-of-scope of this specification.

The notification API that we propose is following the HTML5 notifications standard (https://developer.mozilla.org/en-US/docs/Web/API/notification)

If we need any extensions these can be added on top of it.

## [Version API](https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/Version+API)
> This API allows the JS web app to interrogate the container to find version support information. One possible use case is to get ssf API version supported so that in future it is possible to deprecate older containers that do not support latest version of API. Another possible use case is for logging purposes to help support track down issues.

There is an info section in the API that provides the api version and also container information (version, capabilities, etc)

```javascript
var apiVersion = ssf.info.apiVersion;
```
22 changes: 22 additions & 0 deletions packages/api-specification/api/info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export interface SSFInfo{
/** Version of the API **/
apiVersion: string;

/** Container information **/
container: ContainerInfo;
}

export interface ContainerInfo {
/** Name of the container */
name: string;

/** Version of the container */
version: string;

/**
* Capabilities supported by the container.
* Expressed as object where each property is a capability and the value
* is the level of support in the container
*/
capabilities: {[key:string]: string};
}
10 changes: 10 additions & 0 deletions packages/api-specification/api/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Currently this is based on OpenFin InterApplicationBus - http://cdn.openfin.co/jsdocs/stable/fin.desktop.InterApplicationBus.html
*
* TBD - Request/response and streaming
*/
export interface MessagesAPI{
send(windowId: string, topic :string, message: string|object): void;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is windowId here?

subscribe(windowId: string, topic :string, listener: Function): void;
unsubscribe(windowId: string, topic :string, listener: Function): void;
}
70 changes: 70 additions & 0 deletions packages/api-specification/api/notifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Currently following HTML5 notification specs
*
* Imported from typescript definition files
* https://github.com/Microsoft/TSJS-lib-generator/blob/cd60588b72a9188e89346b3c440a76508b4c0e76/baselines/dom.generated.d.ts#L8360-L8381
*/
export interface NotificationsAPI {
permission: NotificationPermission;
maxActions: number;

requestPermission(): Promise<NotificationPermission>;
}

export type NotificationPermission = "default" | "denied" | "granted";

export type NotificationDirection = "auto" | "ltr" | "rtl";

export interface NotificationPermissionCallback {
(permission: NotificationPermission): void;
}

export interface NotificationOptions {
dir?: NotificationDirection;
lang?: string;
body?: string;
tag?: string;
image?: string;
icon?: string;
badge?: string;
sound?: string;
vibrate?: number | number[],
timestamp?: number,
renotify?: boolean;
silent?: boolean;
requireInteraction?: boolean;
data?: any;
actions?: NotificationAction[]
}

export interface NotificationAction {
action: string;
title: string;
icon?: string;
}

declare class Notification extends EventTarget {
constructor(title: string, options?: NotificationOptions);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like missing requestPermission method.

onclick: EventListenerOrEventListenerObject;
onerror: EventListenerOrEventListenerObject;

close(): void;

readonly title: string;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also need likely need a sticky option

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a standard field?

readonly dir: NotificationDirection;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to think how we might handle non-std fields. e.g., blink and blink color.

readonly lang: string;
readonly body: string;
readonly tag: string;
readonly image: string;
readonly icon: string;
readonly badge: string;
readonly sound: string;
readonly vibrate: number[];
readonly timestamp: number;
readonly renotify: boolean;
readonly silent: boolean;
readonly requireInteraction: boolean;
readonly data: any;
readonly actions: NotificationAction[]
}
9 changes: 9 additions & 0 deletions packages/api-specification/api/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
This is an attempt to define an API that tries to unify what we currently have in ContainerJS, [the working group APIs](https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/Proposed+Standard+API+Specifications)
and things that we at Tick42 think are useful based on our experience.

This is an initial version that aims to start a discussion - some parts of it are not fully defined (e.g. window events) and some
upcoming parts are just mentioned in comments (e.g. interop).

This also contains a [document](./confluence.md) describing how to map the [confluence API](https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/Proposed+Standard+API+Specifications) to the current API.

The starting point when reading the code should be [ssf.ts](./ssf.ts) file that describes the ssf root object as exposed to clients applications.
46 changes: 46 additions & 0 deletions packages/api-specification/api/ssf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {SystemAPI} from "./system";
import {WindowsAPI} from "./windows";
import {MessagesAPI} from "./messages";
import {NotificationsAPI} from "./notifications";
import {ApplicationAPI} from "./application";
import {SSFInfo} from "./info";

/**
* Describes the ssf object as exposed to clients applications
*
* SSF object groups different APIs, where specific API methods can be accessed using the corresponding sub-object, e.g.
* to access window management API :
*
* ssf.windows.open({name:'search', url: 'http://google.com'});
*/
export interface SSF {
/**
* Window management API - create, discover and manage windows
*/
windows: WindowsAPI;

/**
* Messaging API - communicate with other windows
*/
messages: MessagesAPI;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is this diff than window.sendMessage (postMesage) below?


/**
* Notifications API - publish notifications to the user
*/
notifications: NotificationsAPI;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the interesting thing here is that both within the browser and Electron container users will see two notification APIs:

new Notification(...)
new ssf.Notification(...)

This is probably unavoidable, but something that users need to be warned about.


/**
* Application API - access application specific information
*/
application: ApplicationAPI;

/**
* System API - access system specific stuff,e.g. display information, user activity, os information...
*/
system : SystemAPI;

/**
* Info API - general info about the API and the host container
*/
info: SSFInfo;
}