From c3c944cd3020fea243d132aeb02fafc36efd8b1d Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 17 Jun 2021 05:59:27 -0400 Subject: [PATCH] Send LensMainExtension.nagivate calls to renderer before navigating (#3082) Signed-off-by: Sebastian Malton --- src/common/cluster-frames.ts | 4 +- src/common/utils/iter.ts | 16 ++++++++ src/extensions/common-api/registrations.ts | 2 +- src/extensions/extension-loader.ts | 4 ++ src/extensions/lens-main-extension.ts | 12 +----- src/extensions/lens-renderer-extension.ts | 37 ++++++++----------- .../registries/page-menu-registry.ts | 19 ++++------ src/main/window-manager.ts | 24 ++++++++++-- src/renderer/bootstrap.tsx | 1 + src/renderer/initializers/index.ts | 1 + src/renderer/initializers/ipc.ts | 30 +++++++++++++++ src/renderer/initializers/registries.ts | 1 - 12 files changed, 100 insertions(+), 51 deletions(-) create mode 100644 src/renderer/initializers/ipc.ts diff --git a/src/common/cluster-frames.ts b/src/common/cluster-frames.ts index 794a09d827..88dc2d3163 100644 --- a/src/common/cluster-frames.ts +++ b/src/common/cluster-frames.ts @@ -21,9 +21,9 @@ import { observable } from "mobx"; -export type ClusterFrameInfo = { +export interface ClusterFrameInfo { frameId: number; processId: number -}; +} export const clusterFrameMap = observable.map(); diff --git a/src/common/utils/iter.ts b/src/common/utils/iter.ts index 277c58213e..59fce41829 100644 --- a/src/common/utils/iter.ts +++ b/src/common/utils/iter.ts @@ -140,3 +140,19 @@ export function* filterMapStrict(src: Iterable, fn: (from: T) => U | nu } } } + +/** + * Iterate through `src` until `match` returns a truthy value + * @param src A type that can be iterated over + * @param match A function that should return a truthy value for the item that you want to find + * @returns The first entry that `match` returns a truthy value for, or `undefined` + */ +export function find(src: Iterable, match: (i: T) => any): T | undefined { + for (const from of src) { + if (match(from)) { + return from; + } + } + + return void 0; +} diff --git a/src/extensions/common-api/registrations.ts b/src/extensions/common-api/registrations.ts index dadde70ceb..7b7688e85f 100644 --- a/src/extensions/common-api/registrations.ts +++ b/src/extensions/common-api/registrations.ts @@ -24,6 +24,6 @@ export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from ". export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../registries/kube-object-menu-registry"; export type { KubeObjectStatusRegistration } from "../registries/kube-object-status-registry"; export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../registries/page-registry"; -export type { PageMenuRegistration, ClusterPageMenuRegistration, PageMenuComponents } from "../registries/page-menu-registry"; +export type { ClusterPageMenuRegistration, ClusterPageMenuComponents } from "../registries/page-menu-registry"; export type { StatusBarRegistration } from "../registries/status-bar-registry"; export type { ProtocolHandlerRegistration, RouteParams as ProtocolRouteParams, RouteHandler as ProtocolRouteHandler } from "../registries/protocol-handler"; diff --git a/src/extensions/extension-loader.ts b/src/extensions/extension-loader.ts index 4073ad1e79..d3349b3c16 100644 --- a/src/extensions/extension-loader.ts +++ b/src/extensions/extension-loader.ts @@ -355,6 +355,10 @@ export class ExtensionLoader extends Singleton { return this.extensions.get(extId); } + getInstanceById(extId: LensExtensionId): E { + return this.instances.get(extId) as E; + } + toJSON(): Map { return toJS(this.extensions); } diff --git a/src/extensions/lens-main-extension.ts b/src/extensions/lens-main-extension.ts index 023a8b92f9..cbdba9e07c 100644 --- a/src/extensions/lens-main-extension.ts +++ b/src/extensions/lens-main-extension.ts @@ -21,7 +21,6 @@ import { LensExtension } from "./lens-extension"; import { WindowManager } from "../main/window-manager"; -import { getExtensionPageUrl } from "./registries/page-registry"; import { catalogEntityRegistry } from "../main/catalog"; import type { CatalogEntity } from "../common/catalog"; import type { IObservableArray } from "mobx"; @@ -30,15 +29,8 @@ import type { MenuRegistration } from "./registries"; export class LensMainExtension extends LensExtension { appMenus: MenuRegistration[] = []; - async navigate

(pageId?: string, params?: P, frameId?: number) { - const windowManager = WindowManager.getInstance(); - const pageUrl = getExtensionPageUrl({ - extensionId: this.name, - pageId, - params: params ?? {}, // compile to url with params - }); - - await windowManager.navigate(pageUrl, frameId); + async navigate(pageId?: string, params?: Record, frameId?: number) { + return WindowManager.getInstance().navigateExtension(this.id, pageId, params, frameId); } addCatalogSource(id: string, source: IObservableArray) { diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 7119b0f7ac..a3026d99a5 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -19,33 +19,26 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import type { - AppPreferenceRegistration, CatalogEntityDetailRegistration, ClusterPageMenuRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, - KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, WelcomeMenuRegistration, WorkloadsOverviewDetailRegistration, -} from "./registries"; +import type * as registries from "./registries"; import type { Cluster } from "../main/cluster"; import { LensExtension } from "./lens-extension"; import { getExtensionPageUrl } from "./registries/page-registry"; -import type { CommandRegistration } from "./registries/command-registry"; -import type { EntitySettingRegistration } from "./registries/entity-setting-registry"; -import type { TopBarRegistration } from "./registries/topbar-registry"; export class LensRendererExtension extends LensExtension { - globalPages: PageRegistration[] = []; - clusterPages: PageRegistration[] = []; - globalPageMenus: PageMenuRegistration[] = []; - clusterPageMenus: ClusterPageMenuRegistration[] = []; - kubeObjectStatusTexts: KubeObjectStatusRegistration[] = []; - appPreferences: AppPreferenceRegistration[] = []; - entitySettings: EntitySettingRegistration[] = []; - statusBarItems: StatusBarRegistration[] = []; - kubeObjectDetailItems: KubeObjectDetailRegistration[] = []; - kubeObjectMenuItems: KubeObjectMenuRegistration[] = []; - kubeWorkloadsOverviewItems: WorkloadsOverviewDetailRegistration[] = []; - commands: CommandRegistration[] = []; - welcomeMenus: WelcomeMenuRegistration[] = []; - catalogEntityDetailItems: CatalogEntityDetailRegistration[] = []; - topBarItems: TopBarRegistration[] = []; + globalPages: registries.PageRegistration[] = []; + clusterPages: registries.PageRegistration[] = []; + clusterPageMenus: registries.ClusterPageMenuRegistration[] = []; + kubeObjectStatusTexts: registries.KubeObjectStatusRegistration[] = []; + appPreferences: registries.AppPreferenceRegistration[] = []; + entitySettings: registries.EntitySettingRegistration[] = []; + statusBarItems: registries.StatusBarRegistration[] = []; + kubeObjectDetailItems: registries.KubeObjectDetailRegistration[] = []; + kubeObjectMenuItems: registries.KubeObjectMenuRegistration[] = []; + kubeWorkloadsOverviewItems: registries.WorkloadsOverviewDetailRegistration[] = []; + commands: registries.CommandRegistration[] = []; + welcomeMenus: registries.WelcomeMenuRegistration[] = []; + catalogEntityDetailItems: registries.CatalogEntityDetailRegistration[] = []; + topBarItems: registries.TopBarRegistration[] = []; async navigate

(pageId?: string, params?: P) { const { navigate } = await import("../renderer/navigation"); diff --git a/src/extensions/registries/page-menu-registry.ts b/src/extensions/registries/page-menu-registry.ts index 73997fb461..17e5f10491 100644 --- a/src/extensions/registries/page-menu-registry.ts +++ b/src/extensions/registries/page-menu-registry.ts @@ -26,23 +26,20 @@ import type { PageTarget, RegisteredPage } from "./page-registry"; import type { LensExtension } from "../lens-extension"; import { BaseRegistry } from "./base-registry"; -export interface PageMenuRegistration { - target?: PageTarget; - title: React.ReactNode; - components: PageMenuComponents; -} - -export interface ClusterPageMenuRegistration extends PageMenuRegistration { +export interface ClusterPageMenuRegistration { id?: string; parentId?: string; + target?: PageTarget; + title: React.ReactNode; + components: ClusterPageMenuComponents; } -export interface PageMenuComponents { +export interface ClusterPageMenuComponents { Icon: React.ComponentType; } -export class PageMenuRegistry extends BaseRegistry { - add(items: T[], ext: LensExtension) { +export class ClusterPageMenuRegistry extends BaseRegistry { + add(items: ClusterPageMenuRegistration[], ext: LensExtension) { const normalizedItems = items.map(menuItem => { menuItem.target = { extensionId: ext.name, @@ -54,9 +51,7 @@ export class PageMenuRegistry extends BaseRegist return super.add(normalizedItems); } -} -export class ClusterPageMenuRegistry extends PageMenuRegistry { getRootItems() { return this.getItems().filter((item) => !item.parentId); } diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index b30d26d519..9a56aaafb8 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -27,7 +27,7 @@ import { appEventBus } from "../common/event-bus"; import { ipcMainOn } from "../common/ipc"; import { initMenu } from "./menu"; import { initTray } from "./tray"; -import { delay, Singleton } from "../common/utils"; +import { delay, iter, Singleton } from "../common/utils"; import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; import logger from "./logger"; @@ -38,6 +38,12 @@ function isHideable(window: BrowserWindow | null): boolean { return Boolean(window && !window.isDestroyed()); } +export interface SendToViewArgs { + channel: string; + frameInfo?: ClusterFrameInfo; + data?: any[]; +} + export class WindowManager extends Singleton { protected mainWindow: BrowserWindow; protected splashWindow: BrowserWindow; @@ -175,7 +181,7 @@ export class WindowManager extends Singleton { return this.mainWindow; } - sendToView({ channel, frameInfo, data = [] }: { channel: string, frameInfo?: ClusterFrameInfo, data?: any[] }) { + private sendToView({ channel, frameInfo, data = [] }: SendToViewArgs) { if (frameInfo) { this.mainWindow.webContents.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...data); } else { @@ -183,10 +189,22 @@ export class WindowManager extends Singleton { } } + async navigateExtension(extId: string, pageId?: string, params?: Record, frameId?: number) { + await this.ensureMainWindow(); + + const frameInfo = iter.find(clusterFrameMap.values(), frameInfo => frameInfo.frameId === frameId); + + this.sendToView({ + channel: "extension:navigate", + frameInfo, + data: [extId, pageId, params], + }); + } + async navigate(url: string, frameId?: number) { await this.ensureMainWindow(); - const frameInfo = Array.from(clusterFrameMap.values()).find((frameInfo) => frameInfo.frameId === frameId); + const frameInfo = iter.find(clusterFrameMap.values(), frameInfo => frameInfo.frameId === frameId); const channel = frameInfo ? IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER : IpcRendererNavigationEvents.NAVIGATE_IN_APP; diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 8c7fe38d4c..3824caa011 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -77,6 +77,7 @@ export async function bootstrap(App: AppComponent) { initializers.intiKubeObjectDetailRegistry(); initializers.initWelcomeMenuRegistry(); initializers.initWorkloadsOverviewDetailRegistry(); + initializers.initIpcRendererListeners(); ExtensionLoader.createInstance().init(); ExtensionDiscovery.createInstance().init(); diff --git a/src/renderer/initializers/index.ts b/src/renderer/initializers/index.ts index a5cbb49257..0bb4eb611e 100644 --- a/src/renderer/initializers/index.ts +++ b/src/renderer/initializers/index.ts @@ -26,3 +26,4 @@ export * from "./kube-object-menu-registry"; export * from "./registries"; export * from "./welcome-menu-registry"; export * from "./workloads-overview-detail-registry"; +export * from "./ipc"; diff --git a/src/renderer/initializers/ipc.ts b/src/renderer/initializers/ipc.ts new file mode 100644 index 0000000000..fedb007d9b --- /dev/null +++ b/src/renderer/initializers/ipc.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { ipcRendererOn } from "../../common/ipc"; +import { ExtensionLoader } from "../../extensions/extension-loader"; +import type { LensRendererExtension } from "../../extensions/lens-renderer-extension"; + +export function initIpcRendererListeners() { + ipcRendererOn("extension:navigate", (event, extId: string, pageId ?: string, params?: Record) => { + ExtensionLoader.getInstance().getInstanceById(extId).navigate(pageId, params); + }); +} diff --git a/src/renderer/initializers/registries.ts b/src/renderer/initializers/registries.ts index 08594d41c5..1d455a0bcc 100644 --- a/src/renderer/initializers/registries.ts +++ b/src/renderer/initializers/registries.ts @@ -32,7 +32,6 @@ export function initRegistries() { registries.KubeObjectDetailRegistry.createInstance(); registries.KubeObjectMenuRegistry.createInstance(); registries.KubeObjectStatusRegistry.createInstance(); - registries.PageMenuRegistry.createInstance(); registries.StatusBarRegistry.createInstance(); registries.WelcomeMenuRegistry.createInstance(); registries.WorkloadsOverviewDetailRegistry.createInstance();