From 58aef6dd55711c681ae1c457dae2b18d284c2727 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 13 Feb 2024 15:37:21 +0000 Subject: [PATCH] Add support for duplicating a page Update Save Page As and Duplicate Page logic using the copyPage platform override introduced in 16.1. When a page is duplicated we generated a copy of the page but generate the instance id associated with a view's name while retaining the app Id (previously you would get a new randomly generated name for the app). If your app is a singleton then the name remains unique. So you will have two pages referencing the same instance. When you switch between them it will be displaying the same instance. However, please note if you move a page to a new window then the single instance view will move with it and leave the other page's layout (a single instance can only exist on one window if you need multiple instances then instanceMode should not be set to single). Duplicate view (view context menu) currently generates a new unlinked id for a view so if you do not want this behavior you can remove Duplicate View from the View Context Menu. Feedback on this behavior is of interest. --- .../workspace-platform-starter/CHANGELOG.md | 1 + .../framework/platform/platform-override.ts | 51 ++++++++++++++++++- .../manager-portal-fixed.snapshot.fin.json | 3 -- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/how-to/workspace-platform-starter/CHANGELOG.md b/how-to/workspace-platform-starter/CHANGELOG.md index a5d126dbc5..6c6c137a02 100644 --- a/how-to/workspace-platform-starter/CHANGELOG.md +++ b/how-to/workspace-platform-starter/CHANGELOG.md @@ -10,6 +10,7 @@ - Fixed an issue where the order of Workspace component entries within dock may change when toggling between light and dark theme. - FDC3 2.0 Open improvement - You can now specify instance id if you wish to open an existing instance of an app and optionally pass it context. - Add option to allow specific modules to get unrestricted notificationClients if they want to implement custom platform logic without importing notifications directly via the npm package. The notification client options in the notification provider now has a 'restricted' setting which can be set to false. +- Update Save Page As and Duplicate Page logic using the copyPage platform override introduced in 16.1. When a page is duplicated we generated a copy of the page but generate the instance id associated with a view's name while retaining the app Id (previously you would get a new randomly generated name for the app). If your app is a singleton then the name remains unique. So you will have two pages referencing the same instance. When you switch between them it will be displaying the same instance. However, please note if you move a page to a new window then the single instance view will move with it and leave the other page's layout (a single instance can only exist on one window if you need multiple instances then instanceMode should not be set to single). Duplicate view (view context menu) currently generates a new unlinked id for a view so if you do not want this behavior you can remove Duplicate View from the View Context Menu. Feedback on this behavior is of interest. ## v16.1.0 diff --git a/how-to/workspace-platform-starter/client/src/framework/platform/platform-override.ts b/how-to/workspace-platform-starter/client/src/framework/platform/platform-override.ts index 8b78e081f7..536d74e270 100644 --- a/how-to/workspace-platform-starter/client/src/framework/platform/platform-override.ts +++ b/how-to/workspace-platform-starter/client/src/framework/platform/platform-override.ts @@ -20,7 +20,8 @@ import { type Workspace, type WorkspacePlatformProvider, type HandleSaveModalOnPageClosePayload, - type SaveModalOnPageCloseResult + type SaveModalOnPageCloseResult, + type CopyPagePayload } from "@openfin/workspace-platform"; import type { DockProviderConfigWithIdentity } from "@openfin/workspace-platform/client-api/src"; import type { PopupMenuStyles } from "workspace-platform-starter/shapes/menu-shapes"; @@ -61,7 +62,7 @@ import type { VersionInfo } from "../shapes/version-shapes"; import * as snapProvider from "../snap"; import { applyClientSnapshot, decorateSnapshot } from "../snapshot-source"; import { setCurrentColorSchemeMode } from "../themes"; -import { deepMerge, isEmpty } from "../utils"; +import { deepMerge, isEmpty, isStringValue, randomUUID } from "../utils"; import { loadConfig, saveConfig } from "../workspace/dock"; import { getPageBounds } from "./browser"; import { closedown as closedownPlatform } from "./platform"; @@ -890,6 +891,27 @@ export function overrideCallback( logger.warn("Unsaved page prompt strategy is not valid. Using default."); return super.handleSaveModalOnPageClose(payload); } + + /** + * Copies a page, respecting conventions for page and panel names. + * @param payload The payload for the copy page request. + * @returns The copied page. + */ + public async copyPage(payload: CopyPagePayload): Promise { + const panels = isEmpty(payload?.page?.panels) + ? payload?.page?.panels + : duplicateLayout(payload.page.panels); + + const layout = duplicateLayout(payload.page.layout); + + return { + ...payload.page, + panels, + layout, + pageId: randomUUID(), + isReadOnly: false + }; + } } return new Override(); } @@ -920,3 +942,28 @@ async function buildDefaultOptions(browserProvider?: BrowserProviderOptions): Pr view: deepMerge({}, manifest.platform?.defaultViewOptions, browserProvider?.defaultViewOptions) }; } + +/** + * Takes a layout and walks through all the nodes and applies logic to nodes that have + * a url and a name that matches a pattern. Updates the name to make it unique (if applicable) + * while retaining information related to an application's identity if present. + * @param layout The layout to duplicate + * @returns The duplicated layout. + */ +function duplicateLayout(layout: T): T { + return JSON.parse( + JSON.stringify(layout, (_, nestedValue) => { + // check to ensure that we have a name field and that we also have a url field in this object (in case name was added to a random part of the layout) + if (isStringValue(nestedValue?.name) && !isEmpty(nestedValue.url)) { + if (/\/[\d,a-z-]{36}$/.test(nestedValue.name)) { + nestedValue.name = nestedValue.name.replace(/([\d,a-z-]{36}$)/, randomUUID()); + } + // case: internal-generated-view- + if (/-[\d,a-z-]{36}$/.test(nestedValue.name)) { + nestedValue.name = nestedValue.name.replace(/(-[\d,a-z-]{36}$)/, randomUUID()); + } + } + return nestedValue as unknown; + }) + ); +} diff --git a/how-to/workspace-platform-starter/public/common/views/manager-portal/manager-portal-fixed.snapshot.fin.json b/how-to/workspace-platform-starter/public/common/views/manager-portal/manager-portal-fixed.snapshot.fin.json index 3cd7daab87..b3b821e7df 100644 --- a/how-to/workspace-platform-starter/public/common/views/manager-portal/manager-portal-fixed.snapshot.fin.json +++ b/how-to/workspace-platform-starter/public/common/views/manager-portal/manager-portal-fixed.snapshot.fin.json @@ -186,7 +186,6 @@ "position": "Top", "height": "40px", "viewOptions": { - "name": "manager-portal-header-panel", "url": "http://localhost:8080/common/panels/header-panel.html", "preloadScripts": [ { @@ -200,7 +199,6 @@ "width": "40px", "extendToTop": true, "viewOptions": { - "name": "manager-portal-nav-panel", "url": "http://localhost:8080/common/panels/nav-panel.html", "preloadScripts": [ { @@ -213,7 +211,6 @@ "position": "Bottom", "height": "40px", "viewOptions": { - "name": "manager-portal-footer-panel", "url": "http://localhost:8080/common/panels/footer-panel.html", "preloadScripts": [ {