Feature/api definitions #171
base: master
Are you sure you want to change the base?
Feature/api definitions #171
Conversation
This reverts commit ca5ae28. # Conflicts: # packages/api-specification/api/windows.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a great start!
I have left a few comments, mostly high level, but i'm not sure how much leeway we have in changing some of these APIs. I will do another code review once we have finalized on the design :-).
capture(options?: CaptureOptions): Promise<Base64ImageData>; | ||
|
||
/** Get the bounds of the window */ | ||
getBounds(): Bounds; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all window methods should be asynchronous, as OpenFin methods are all async.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are, although I do wonder whether we should wrap them to be synchronous? All the equivalent Electron methods are synchronous.
I wonder whether this is an OpenFin implementation detail that has 'leaked' into the public API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually these are sync operations , not sure why openfin did it that way.
Also I think usage is better
//move my window with 10px
window.setBounds({left: window.getBounds().left + 20});
//vs
window.getBounds().then((bounds)=>{
window.setBounds({left: bounds.left + 20});
});
export interface Window extends EventEmitter { | ||
|
||
/** Get or set the max width of the window. Might be undefined if no restrictions applied**/ | ||
maxWidth?: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these will need to be getters/setters as they can change after the window is open. OpenFin's methods are also async, see below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes these can be implemented as properties with getter and setters. (I prefer this over get* set* methods),
window.maxWidth = 10;
console.log(window.maxWidth);
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure it would work, unless we have them as read only and use them instead of get methods. If we had an open window and did window.maxWidth = 10;
, then we need a way of calling the underlying method to set the value. I think the only way to do this would be to watch for variable changes? Unless you can think of another way? :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of using properties, something like (in typescript)
class Window{
private _maxWidth: number;
get maxWidth():boolean {
return this._maxWidth;
}
set maxWidth(width:number) {
// do some container specific magic
this._maxWidth= width;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah I forgot we were using TypeScript 😄. Sounds like a good idea to me.
@@ -0,0 +1,20 @@ | |||
// contains common types | |||
export interface Bounds { | |||
top: number; |
There was a problem hiding this comment.
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?
* Send a message to the window. | ||
* @param {string|object} message - The message to send to the window. Can be any serializable object. | ||
*/ | ||
sendMessage(message: string | object): Promise<void>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HTML5 API is postMessage()
so we should maybe use that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok will update
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
getById(windowId: string): Window; | ||
|
||
/** Opens a new window */ | ||
open(options: WindowOptions): Promise<Window>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer using the constructor pattern that is used in some of the newer browser APIs (like notifications). Might be better to keep it like this though if window.open
is used in the symphony application.
export interface WindowsAPI { | ||
|
||
/** Returns the current window */ | ||
current: Window; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have getById()
, then we might want to have these as getCurrent()
and getFocusedWindow()
etc. this would match both Electron + OpenFinas well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes these should be get* methods . Will update
@@ -0,0 +1,33 @@ | |||
export interface ApplicationAPI { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
/** | ||
* Notifications API - publish notifications to the user | ||
*/ | ||
notifications: NotificationsAPI; |
There was a problem hiding this comment.
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.
* If additional activity occurs within the period given by the 'throttle', then the callback will be | ||
* invoked at time: X + throttle (where X is the time of last activity report send). | ||
*/ | ||
onUserActivity(callback: ()=> void, throtle: number); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throttle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks will change
capture(options?: CaptureOptions): Promise<Base64ImageData>; | ||
|
||
/** Get the bounds of the window */ | ||
getBounds(): Bounds; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are, although I do wonder whether we should wrap them to be synchronous? All the equivalent Electron methods are synchronous.
I wonder whether this is an OpenFin implementation detail that has 'leaked' into the public API?
* Receive notifications when a new window is opened | ||
* @returns Function Execute the function to unsubscribe | ||
*/ | ||
onWindowOpened(callback: (window: Window) => void): UnsubscribeFunction; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason why Window
is an event emitter, whereas the WindowsAPI
has explicit methods for each event type?
I'd prefer to stick to the event emitter pattern!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok will change
focusedWindow: Window; | ||
|
||
/** | ||
getById(windowId: string): Window; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing closing comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks - will update
…itions # Conflicts: # .gitignore
|
||
declare class Notification extends EventTarget { | ||
constructor(title: string, options?: NotificationOptions); | ||
|
There was a problem hiding this comment.
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.
|
||
close(): void; | ||
|
||
readonly title: string; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
close(): void; | ||
|
||
readonly title: string; | ||
readonly dir: NotificationDirection; |
There was a problem hiding this comment.
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.
* It implements EventEmitter which means users can subscribe for events using on, once , addEventListener etc. | ||
* The list of events is in WindowsEvents structure; | ||
*/ | ||
export interface Window extends EventEmitter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one issue i have here is that is kind of re-inventing the wheel. javascript already has a Window object and window.open nicely returns one these. so for javascript already using window.open this might require significant refactoring and might not be able to achieve same functionality. for example, window.opener. here the equivalent looks like window. getParentWindow() but it still don't think will give same functionality. what about postMesage and 'closed'
for example, one use case we have is we want child window to able to share large object (redux state) with parent. with window.opener this is possible. not sure here.
is it possible to augment existing window object (or inherit from it) and add missing/optional things we need here???
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not about reinventing but for defining a common contract for container window. Note that this is not just about the current window, it's a way to address any window in your application from the current one (even if they don't have a parent/child relationship).
Any container I've worked with (Electron, OpenFin, GlueDesktop (which is Tick42 CEF based container)) has similar contract for windows, because the standard browser windowing API is not good enough for desktop applications.
Augmenting the existing window is actually a proposal for implementation of this contract.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for example, one use case we have is we want child window to able to share large object (redux state) with parent. with window.opener this is possible. not sure here.
One problem you'll encounter here is that the process model differs between various containers / browser.
Within a browser, a window opened via window.open is in the same process, therefore window.opener from the child process uses the same JavaScript context, and as a result, sharing objects, such as redux state becomes possible.
This is also true within OpenFin.
However, in Electron each renderer window has its own process, so you cannot just use window.opener to access the parent context and state.
This is why projects such as https://github.com/samiskin/redux-electron-store exist.
Within ContainerJS we are looking to create a consistent process model across all containers, as well as a consistent API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using window.open (with sandbox on) in electron like this works just fine, the child can use the window.opener. we have this working currently in electron. when window.open url has the same domain, then it runs in the same process and the child can access the parent.
but yes if in diff process then window.opener won't work without some extra method to proxy between process across ipc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we recently discovered this behaviour, and @BenLambertNcl is exploring it further via #195
* Capture | ||
* @param options | ||
*/ | ||
capture: (options?: CaptureOptions) => Promise<Base64ImageData>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how is this capture working? would like to see something similar to ScreenSnippet here https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/ScreenSnippet+API
|
||
export interface SystemAPI { | ||
/** | ||
* Info about all displays available |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
other interface needed is for screen sharing... https://symphonyoss.atlassian.net/wiki/display/WGDWAPI/getMediaSources+API which is essentially equal to https://electron.atom.io/docs/api/desktop-capturer/#desktopcapturer
* TBD - Request/response and streaming | ||
*/ | ||
export interface MessagesAPI{ | ||
send(windowId: string, topic :string, message: string|object): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is windowId here?
* Send a message to the window. | ||
* @param {string|object} message - The message to send to the window. Can be any serializable object. | ||
*/ | ||
sendMessage(message: string | object): Promise<void>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how to receive a message? ??? win.addEventListener('message', callback);
/** | ||
* Messaging API - communicate with other windows | ||
*/ | ||
messages: MessagesAPI; |
There was a problem hiding this comment.
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?
This is an attempt to define an API to unify what we currently have in ContainerJS, the working group APIs
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 describing how to map the [the working group APIs in confluence (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 file that describes the ssf root object as exposed to clients applications.