Skip to content

Commit

Permalink
Add enhancements
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
johnman committed Oct 27, 2024
1 parent 585084a commit 845dfd7
Show file tree
Hide file tree
Showing 16 changed files with 127 additions and 70 deletions.
3 changes: 3 additions & 0 deletions how-to/workspace-platform-starter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
20 changes: 3 additions & 17 deletions how-to/workspace-platform-starter/client/src/framework/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -30,7 +28,6 @@ let modules: ModuleEntry<Actions>[] | 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.
Expand All @@ -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<CustomActionsMap | undefined> {
if (isInitialized) {
logger.error("The actions can only be used once when configuring the platform");
Expand All @@ -73,10 +68,6 @@ export async function init(
updateToolbarButtons
});
}
if (!isEmpty(windowPositioning)) {
logger.info("Initializing with window positioning options", windowPositioning);
windowPositioningOptions = windowPositioning;
}

await buildActions();

Expand Down Expand Up @@ -256,12 +247,7 @@ async function getPlatformActions(): Promise<CustomActionsMap> {
*/
async function launchAppAction(app: PlatformApp, clientIdentity: OpenFin.Identity): Promise<void> {
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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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);
}
}
Expand Down
67 changes: 55 additions & 12 deletions how-to/workspace-platform-starter/client/src/framework/launch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<PlatformAppIdentifier[] | undefined> {
try {
logger.info("Application launch requested", platformApp);
Expand Down Expand Up @@ -66,15 +72,15 @@ 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);
}
break;
}
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);
}
Expand Down Expand Up @@ -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}.`
Expand Down Expand Up @@ -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<PlatformAppIdentifier | undefined> {
if (isEmpty(windowApp)) {
logger.warn("No app was passed to launchWindow");
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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<PlatformAppIdentifier | undefined> {
if (isEmpty(viewApp)) {
logger.warn("No app was passed to launchView");
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -1147,3 +1171,22 @@ function updateInstanceIds<T>(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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@ export function getDefaultHelpers(): ModuleHelpers {
getDialogClient,
launchApp: async (
appId: string,
launchPreference?: UpdatableLaunchPreference
launchPreference?: UpdatableLaunchPreference,
callerIdentity?: OpenFin.Identity
): Promise<PlatformAppIdentifier[] | undefined> => {
logger.info(`launchApp: Looking up appId: ${appId}`);
const app = await getApp(appId);
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<PlatformAppIdentifier[] | undefined>;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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" };
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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" ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ export class AppProvider implements IntegrationModule<AppSettings> {

if (data?.app?.appId) {
handled = true;
await this._integrationHelpers.launchApp(data.app.appId);
await this._integrationHelpers.launchApp(data.app.appId, undefined, result.dispatcherIdentity);
}
}
}
Expand Down
Loading

0 comments on commit 845dfd7

Please sign in to comment.