From 3254f49f018a71da857c358e72727b83aaaa7f8a Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 24 Jan 2022 12:07:45 -0500 Subject: [PATCH] Replace all uses of requestMain with typed functions Signed-off-by: Sebastian Malton --- .../catalog-entities/kubernetes-cluster.ts | 10 +-- src/common/cluster-ipc.ts | 1 + src/common/cluster-store/cluster-store.ts | 18 ++--- src/common/ipc/dialog.ts | 24 +------ src/common/ipc/extension-handling.ts | 9 +++ src/common/ipc/extension-loader.ipc.ts | 5 -- src/common/ipc/ipc.ts | 6 +- src/common/ipc/window-actions.ts | 51 ++++++++----- src/common/k8s/resource-stack.ts | 11 ++- .../extension-discovery.ts | 20 ++---- .../extension-loader/extension-loader.ts | 22 +++--- .../init-ipc-main-handlers.ts | 10 +-- src/main/ipc/dialog.ts | 22 +----- src/main/ipc/window.ts | 40 ++++------- src/main/window-manager.ts | 5 +- .../cluster-manager/cluster-status.tsx | 6 +- .../cluster-manager/cluster-view.tsx | 5 +- .../delete-cluster-dialog.tsx | 9 ++- .../components/layout/top-bar/top-bar.tsx | 20 +++--- .../components/path-picker/path-picker.tsx | 5 +- .../frames/cluster-frame/cluster-frame.tsx | 1 - .../init-cluster-frame/init-cluster-frame.ts | 5 +- .../init-root-frame/init-root-frame.ts | 7 +- src/renderer/ipc/index.ts | 71 +++++++++++++++++++ .../ipc/{index.tsx => register-listeners.tsx} | 0 src/renderer/remote-helpers/dialog.ts | 10 --- .../remote-helpers/history-updater.ts | 5 +- src/renderer/remote-helpers/index.ts | 8 --- 28 files changed, 206 insertions(+), 200 deletions(-) create mode 100644 src/common/ipc/extension-handling.ts delete mode 100644 src/common/ipc/extension-loader.ipc.ts create mode 100644 src/renderer/ipc/index.ts rename src/renderer/ipc/{index.tsx => register-listeners.tsx} (100%) delete mode 100644 src/renderer/remote-helpers/dialog.ts delete mode 100644 src/renderer/remote-helpers/index.ts diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index 813d5d3866..dcc732a2ca 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -5,12 +5,12 @@ import { catalogCategoryRegistry } from "../catalog/catalog-category-registry"; import { CatalogEntity, CatalogEntityActionContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus, CatalogCategory, CatalogCategorySpec } from "../catalog"; -import { clusterActivateHandler, clusterDisconnectHandler } from "../cluster-ipc"; import { ClusterStore } from "../cluster-store/cluster-store"; -import { broadcastMessage, requestMain } from "../ipc"; +import { broadcastMessage } from "../ipc"; import { app } from "electron"; import type { CatalogEntitySpec } from "../catalog/catalog-entity"; import { IpcRendererNavigationEvents } from "../../renderer/navigation/events"; +import { requestClusterActivation, requestClusterDisconnection } from "../../renderer/ipc"; export interface KubernetesClusterPrometheusMetrics { address?: { @@ -69,7 +69,7 @@ export class KubernetesCluster extends CatalogEntity requestMain(clusterDisconnectHandler, this.metadata.uid), + onClick: () => requestClusterDisconnection(this.metadata.uid), }); break; case LensKubernetesClusterStatus.DISCONNECTED: diff --git a/src/common/cluster-ipc.ts b/src/common/cluster-ipc.ts index 373bafcfad..6ed8182cba 100644 --- a/src/common/cluster-ipc.ts +++ b/src/common/cluster-ipc.ts @@ -13,3 +13,4 @@ export const clusterSetDeletingHandler = "cluster:deleting:set"; export const clusterClearDeletingHandler = "cluster:deleting:clear"; export const clusterKubectlApplyAllHandler = "cluster:kubectl-apply-all"; export const clusterKubectlDeleteAllHandler = "cluster:kubectl-delete-all"; +export const clusterStates = "cluster:states"; diff --git a/src/common/cluster-store/cluster-store.ts b/src/common/cluster-store/cluster-store.ts index 41617e11a8..343d793d59 100644 --- a/src/common/cluster-store/cluster-store.ts +++ b/src/common/cluster-store/cluster-store.ts @@ -2,7 +2,7 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ - + import { ipcMain, ipcRenderer, webFrame } from "electron"; import { action, comparer, computed, makeObservable, observable, reaction } from "mobx"; @@ -11,16 +11,16 @@ import { Cluster } from "../cluster/cluster"; import migrations from "../../migrations/cluster-store"; import logger from "../../main/logger"; import { appEventBus } from "../app-event-bus/event-bus"; -import { ipcMainHandle, requestMain } from "../ipc"; +import { ipcMainHandle } from "../ipc"; import { disposer, toJS } from "../utils"; import type { ClusterModel, ClusterId, ClusterState } from "../cluster-types"; +import { requestInitialClusterStates } from "../../renderer/ipc"; +import { clusterStates } from "../cluster-ipc"; export interface ClusterStoreModel { clusters?: ClusterModel[]; } -const initialStates = "cluster:states"; - interface Dependencies { createCluster: (model: ClusterModel) => Cluster } @@ -49,18 +49,18 @@ export class ClusterStore extends BaseStore { async loadInitialOnRenderer() { logger.info("[CLUSTER-STORE] requesting initial state sync"); - for (const { id, state } of await requestMain(initialStates)) { + for (const { id, state } of await requestInitialClusterStates()) { this.getById(id)?.setState(state); } } provideInitialFromMain() { - ipcMainHandle(initialStates, () => { - return this.clustersList.map(cluster => ({ + ipcMainHandle(clusterStates, () => ( + this.clustersList.map(cluster => ({ id: cluster.id, state: cluster.getState(), - })); - }); + })) + )); } protected pushStateToViewsAutomatically() { diff --git a/src/common/ipc/dialog.ts b/src/common/ipc/dialog.ts index 416714f041..eab621a280 100644 --- a/src/common/ipc/dialog.ts +++ b/src/common/ipc/dialog.ts @@ -1,24 +1,6 @@ /** - * 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. + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. */ -export const enum IpcMainDialogEvents { - SHOW_OPEN = "dialog:show-open", -} +export const openFilePickingDialogChannel = "dialog:open:file-picking"; diff --git a/src/common/ipc/extension-handling.ts b/src/common/ipc/extension-handling.ts new file mode 100644 index 0000000000..1474a6d18d --- /dev/null +++ b/src/common/ipc/extension-handling.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +export const extensionDiscoveryStateChannel = "extension-discovery:state"; +export const bundledExtensionsLoaded = "extension-loader:bundled-extensions-loaded"; +export const extensionLoaderFromMainChannel = "extension-loader:main:state"; +export const extensionLoaderFromRendererChannel = "extension-loader:renderer:state"; diff --git a/src/common/ipc/extension-loader.ipc.ts b/src/common/ipc/extension-loader.ipc.ts deleted file mode 100644 index 48bfedd970..0000000000 --- a/src/common/ipc/extension-loader.ipc.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -export const BundledExtensionsLoaded = "extension-loader:bundled-extensions-loaded"; diff --git a/src/common/ipc/ipc.ts b/src/common/ipc/ipc.ts index 1df105d831..b875a7d30d 100644 --- a/src/common/ipc/ipc.ts +++ b/src/common/ipc/ipc.ts @@ -15,10 +15,6 @@ import type { Disposer } from "../utils"; export const broadcastMainChannel = "ipc:broadcast-main"; -export async function requestMain(channel: string, ...args: any[]) { - return ipcRenderer.invoke(channel, ...args.map(sanitizePayload)); -} - export function ipcMainHandle(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) { ipcMain.handle(channel, async (event, ...args) => { return sanitizePayload(await listener(event, ...args)); @@ -31,7 +27,7 @@ function getSubFrames(): ClusterFrameInfo[] { export async function broadcastMessage(channel: string, ...args: any[]): Promise { if (ipcRenderer) { - return requestMain(broadcastMainChannel, channel, ...args); + return ipcRenderer.invoke(broadcastMainChannel, channel, ...args.map(sanitizePayload)); } if (!webContents) { diff --git a/src/common/ipc/window-actions.ts b/src/common/ipc/window-actions.ts index af01e9f49e..58523ec08f 100644 --- a/src/common/ipc/window-actions.ts +++ b/src/common/ipc/window-actions.ts @@ -1,20 +1,35 @@ /** - * 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. + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. */ + +/** + * The supported actions on the current window + */ +export enum WindowAction { + /** + * Request that the current window goes back one step of browser history + */ + GO_BACK = "back", + + /** + * Request that the current window goes forward one step of browser history + */ + GO_FORWARD = "forward", + + /** + * Request that the current window is minimized + */ + MINIMIZE = "minimize", + + /** + * Request that the current window is maximized if it isn't, or unmaximized + * if it is + */ + TOGGLE_MAXIMIZE = "toggle-maximize", + + /** + * Request that the current window is closed + */ + CLOSE = "close", +} diff --git a/src/common/k8s/resource-stack.ts b/src/common/k8s/resource-stack.ts index 08aae15d0b..29ade4b84a 100644 --- a/src/common/k8s/resource-stack.ts +++ b/src/common/k8s/resource-stack.ts @@ -9,11 +9,10 @@ import { ResourceApplier } from "../../main/resource-applier"; import type { KubernetesCluster } from "../catalog-entities"; import logger from "../../main/logger"; import { app } from "electron"; -import { requestMain } from "../ipc"; -import { clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler } from "../cluster-ipc"; import { ClusterStore } from "../cluster-store/cluster-store"; import yaml from "js-yaml"; import { productName } from "../vars"; +import { requestKubectlApplyAll, requestKubectlDeleteAll } from "../../renderer/ipc"; export class ResourceStack { constructor(protected cluster: KubernetesCluster, protected name: string) {} @@ -41,7 +40,7 @@ export class ResourceStack { } protected async applyResources(resources: string[], extraArgs?: string[]): Promise { - const clusterModel = ClusterStore.getInstance().getById(this.cluster.metadata.uid); + const clusterModel = ClusterStore.getInstance().getById(this.cluster.getId()); if (!clusterModel) { throw new Error(`cluster not found`); @@ -54,7 +53,7 @@ export class ResourceStack { if (app) { return await new ResourceApplier(clusterModel).kubectlApplyAll(resources, kubectlArgs); } else { - const response = await requestMain(clusterKubectlApplyAllHandler, this.cluster.metadata.uid, resources, kubectlArgs); + const response = await requestKubectlApplyAll(this.cluster.getId(), resources, kubectlArgs); if (response.stderr) { throw new Error(response.stderr); @@ -65,7 +64,7 @@ export class ResourceStack { } protected async deleteResources(resources: string[], extraArgs?: string[]): Promise { - const clusterModel = ClusterStore.getInstance().getById(this.cluster.metadata.uid); + const clusterModel = ClusterStore.getInstance().getById(this.cluster.getId()); if (!clusterModel) { throw new Error(`cluster not found`); @@ -78,7 +77,7 @@ export class ResourceStack { if (app) { return await new ResourceApplier(clusterModel).kubectlDeleteAll(resources, kubectlArgs); } else { - const response = await requestMain(clusterKubectlDeleteAllHandler, this.cluster.metadata.uid, resources, kubectlArgs); + const response = await requestKubectlDeleteAll(this.cluster.getId(), resources, kubectlArgs); if (response.stderr) { throw new Error(response.stderr); diff --git a/src/extensions/extension-discovery/extension-discovery.ts b/src/extensions/extension-discovery/extension-discovery.ts index d563fcbdb5..eb957a4f0c 100644 --- a/src/extensions/extension-discovery/extension-discovery.ts +++ b/src/extensions/extension-discovery/extension-discovery.ts @@ -10,12 +10,7 @@ import fse from "fs-extra"; import { makeObservable, observable, reaction, when } from "mobx"; import os from "os"; import path from "path"; -import { - broadcastMessage, - ipcMainHandle, - ipcRendererOn, - requestMain, -} from "../../common/ipc"; +import { broadcastMessage, ipcMainHandle, ipcRendererOn } from "../../common/ipc"; import { toJS } from "../../common/utils"; import logger from "../../main/logger"; import type { ExtensionsStore } from "../extensions-store/extensions-store"; @@ -24,6 +19,8 @@ import type { LensExtensionId, LensExtensionManifest } from "../lens-extension"; import { isProduction } from "../../common/vars"; import type { ExtensionInstallationStateStore } from "../extension-installation-state-store/extension-installation-state-store"; import type { PackageJson } from "type-fest"; +import { extensionDiscoveryStateChannel } from "../../common/ipc/extension-handling"; +import { requestInitialExtensionDiscovery } from "../../renderer/ipc"; interface Dependencies { extensionLoader: ExtensionLoader; @@ -96,9 +93,6 @@ export class ExtensionDiscovery { return when(() => this.isLoaded); } - // IPC channel to broadcast changes to extension-discovery from main - protected static readonly extensionDiscoveryChannel = "extension-discovery:main"; - public events = new EventEmitter(); constructor(protected dependencies : Dependencies) { @@ -141,14 +135,14 @@ export class ExtensionDiscovery { this.isLoaded = isLoaded; }; - requestMain(ExtensionDiscovery.extensionDiscoveryChannel).then(onMessage); - ipcRendererOn(ExtensionDiscovery.extensionDiscoveryChannel, (_event, message: ExtensionDiscoveryChannelMessage) => { + requestInitialExtensionDiscovery().then(onMessage); + ipcRendererOn(extensionDiscoveryStateChannel, (_event, message: ExtensionDiscoveryChannelMessage) => { onMessage(message); }); } async initMain(): Promise { - ipcMainHandle(ExtensionDiscovery.extensionDiscoveryChannel, () => this.toJSON()); + ipcMainHandle(extensionDiscoveryStateChannel, () => this.toJSON()); reaction(() => this.toJSON(), () => { this.broadcast(); @@ -492,6 +486,6 @@ export class ExtensionDiscovery { } broadcast(): void { - broadcastMessage(ExtensionDiscovery.extensionDiscoveryChannel, this.toJSON()); + broadcastMessage(extensionDiscoveryStateChannel, this.toJSON()); } } diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts index afe09cce19..04b552884c 100644 --- a/src/extensions/extension-loader/extension-loader.ts +++ b/src/extensions/extension-loader/extension-loader.ts @@ -8,7 +8,7 @@ import { EventEmitter } from "events"; import { isEqual } from "lodash"; import { action, computed, makeObservable, observable, observe, reaction, when } from "mobx"; import path from "path"; -import { broadcastMessage, ipcMainOn, ipcRendererOn, requestMain, ipcMainHandle } from "../../common/ipc"; +import { broadcastMessage, ipcMainOn, ipcRendererOn, ipcMainHandle } from "../../common/ipc"; import { Disposer, toJS } from "../../common/utils"; import logger from "../../main/logger"; import type { KubernetesCluster } from "../common-api/catalog"; @@ -17,6 +17,8 @@ import type { LensExtension, LensExtensionConstructor, LensExtensionId } from ". import type { LensRendererExtension } from "../lens-renderer-extension"; import * as registries from "../registries"; import type { LensExtensionState } from "../extensions-store/extensions-store"; +import { extensionLoaderFromMainChannel, extensionLoaderFromRendererChannel } from "../../common/ipc/extension-handling"; +import { requestExtensionLoaderInitialState } from "../../renderer/ipc"; const logModule = "[EXTENSIONS-LOADER]"; @@ -49,12 +51,6 @@ export class ExtensionLoader { */ protected instancesByName = observable.map(); - // IPC channel to broadcast changes to extensions from main - protected static readonly extensionsMainChannel = "extensions:main"; - - // IPC channel to broadcast changes to extensions from renderer - protected static readonly extensionsRendererChannel = "extensions:renderer"; - // emits event "remove" of type LensExtension when the extension is removed private events = new EventEmitter(); @@ -196,11 +192,11 @@ export class ExtensionLoader { this.isLoaded = true; this.loadOnMain(); - ipcMainHandle(ExtensionLoader.extensionsMainChannel, () => { + ipcMainHandle(extensionLoaderFromMainChannel, () => { return Array.from(this.toJSON()); }); - ipcMainOn(ExtensionLoader.extensionsRendererChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => { + ipcMainOn(extensionLoaderFromRendererChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => { this.syncExtensions(extensions); }); } @@ -220,16 +216,16 @@ export class ExtensionLoader { }); }; - requestMain(ExtensionLoader.extensionsMainChannel).then(extensionListHandler); - ipcRendererOn(ExtensionLoader.extensionsMainChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => { + requestExtensionLoaderInitialState().then(extensionListHandler); + ipcRendererOn(extensionLoaderFromMainChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => { extensionListHandler(extensions); }); } broadcastExtensions() { const channel = ipcRenderer - ? ExtensionLoader.extensionsRendererChannel - : ExtensionLoader.extensionsMainChannel; + ? extensionLoaderFromRendererChannel + : extensionLoaderFromMainChannel; broadcastMessage(channel, Array.from(this.extensions)); } diff --git a/src/main/initializers/init-ipc-main-handlers/init-ipc-main-handlers.ts b/src/main/initializers/init-ipc-main-handlers/init-ipc-main-handlers.ts index 1dbc4a01be..28402cdccb 100644 --- a/src/main/initializers/init-ipc-main-handlers/init-ipc-main-handlers.ts +++ b/src/main/initializers/init-ipc-main-handlers/init-ipc-main-handlers.ts @@ -20,8 +20,8 @@ import { remove } from "fs-extra"; import { getAppMenu } from "../../menu/menu"; import type { MenuRegistration } from "../../menu/menu-registration"; import type { IComputedValue } from "mobx"; -import { onLocationChange, windowAction } from "../../ipc/window"; -import { IpcMainDialogEvents } from "../../../common/ipc/dialog"; +import { onLocationChange, handleWindowAction } from "../../ipc/window"; +import { openFilePickingDialogChannel } from "../../../common/ipc/dialog"; import { showOpenDialog } from "../../ipc/dialog"; interface Dependencies { @@ -145,11 +145,11 @@ export const initIpcMainHandlers = ({ electronMenuItems, directoryForLensLocalSt return dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), dialogOpts); }); - ipcMainHandle(IpcMainWindowEvents.WINDOW_ACTION, (event, action) => windowAction(action)); + ipcMainHandle(IpcMainWindowEvents.WINDOW_ACTION, (event, action) => handleWindowAction(action)); - ipcMainHandle(IpcMainWindowEvents.LOCATION_CHANGED, () => onLocationChange()); + ipcMainOn(IpcMainWindowEvents.LOCATION_CHANGED, () => onLocationChange()); - ipcMainHandle(IpcMainDialogEvents.SHOW_OPEN, (event, opts) => showOpenDialog(opts)); + ipcMainHandle(openFilePickingDialogChannel, (event, opts) => showOpenDialog(opts)); ipcMainHandle(broadcastMainChannel, (event, channel, ...args) => broadcastMessage(channel, ...args)); diff --git a/src/main/ipc/dialog.ts b/src/main/ipc/dialog.ts index fe58bcf677..d192869ebd 100644 --- a/src/main/ipc/dialog.ts +++ b/src/main/ipc/dialog.ts @@ -1,27 +1,11 @@ /** - * 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. + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. */ import { BrowserWindow, dialog, OpenDialogOptions } from "electron"; -export async function showOpenDialog(dialogOptions: OpenDialogOptions) { +export async function showOpenDialog(dialogOptions: OpenDialogOptions): Promise<{ canceled: boolean; filePaths: string[]; }> { const { canceled, filePaths } = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), dialogOptions); return { canceled, filePaths }; diff --git a/src/main/ipc/window.ts b/src/main/ipc/window.ts index 29d273fe1d..5938f8ad02 100644 --- a/src/main/ipc/window.ts +++ b/src/main/ipc/window.ts @@ -1,51 +1,34 @@ /** - * 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. + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. */ import { BrowserWindow, webContents } from "electron"; import { broadcastMessage } from "../../common/ipc"; +import { WindowAction } from "../../common/ipc/window-actions"; -type WindowAction = "back" | "forward" | "minimize" | "toggleMaximize" | "close"; - -export function windowAction(action: WindowAction) { +export function handleWindowAction(action: WindowAction) { const window = BrowserWindow.getFocusedWindow(); if (!window) return; switch (action) { - case "back": { + case WindowAction.GO_BACK: { window.webContents.goBack(); break; } - case "forward": { + case WindowAction.GO_FORWARD: { window.webContents.goForward(); break; } - case "minimize": { + case WindowAction.MINIMIZE: { window.minimize(); break; } - case "toggleMaximize": { + case WindowAction.TOGGLE_MAXIMIZE: { if (window.isMaximized()) { window.unmaximize(); } else { @@ -54,14 +37,17 @@ export function windowAction(action: WindowAction) { break; } - case "close": { + case WindowAction.CLOSE: { window.close(); break; } + + default: + throw new Error(`Attemped window action ${action} is unknown`); } } -export function onLocationChange() { +export function onLocationChange(): void { const getAllWebContents = webContents.getAllWebContents(); const canGoBack = getAllWebContents.some((webContent) => { diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index ab6025d0a3..0880f392dc 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -8,13 +8,14 @@ import { makeObservable, observable } from "mobx"; import { app, BrowserWindow, dialog, ipcMain, shell, webContents } from "electron"; import windowStateKeeper from "electron-window-state"; import { appEventBus } from "../common/app-event-bus/event-bus"; -import { BundledExtensionsLoaded, ipcMainOn } from "../common/ipc"; +import { ipcMainOn } from "../common/ipc"; import { delay, iter, Singleton } from "../common/utils"; import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; import logger from "./logger"; import { isMac, productName } from "../common/vars"; import { LensProxy } from "./lens-proxy"; +import { bundledExtensionsLoaded } from "../common/ipc/extension-handling"; function isHideable(window: BrowserWindow | null): boolean { return Boolean(window && !window.isDestroyed()); @@ -160,7 +161,7 @@ export class WindowManager extends Singleton { if (!this.mainWindow) { viewHasLoaded = new Promise(resolve => { - ipcMain.once(BundledExtensionsLoaded, () => resolve()); + ipcMain.once(bundledExtensionsLoaded, () => resolve()); }); await this.initMainWindow(showSplash); } diff --git a/src/renderer/components/cluster-manager/cluster-status.tsx b/src/renderer/components/cluster-manager/cluster-status.tsx index 11a16f2689..408f45b1bb 100644 --- a/src/renderer/components/cluster-manager/cluster-status.tsx +++ b/src/renderer/components/cluster-manager/cluster-status.tsx @@ -8,8 +8,7 @@ import styles from "./cluster-status.module.scss"; import { computed, observable, makeObservable } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import React from "react"; -import { clusterActivateHandler } from "../../../common/cluster-ipc"; -import { ipcRendererOn, requestMain } from "../../../common/ipc"; +import { ipcRendererOn } from "../../../common/ipc"; import type { Cluster } from "../../../common/cluster/cluster"; import { cssNames, IClassName } from "../../utils"; import { Button } from "../button"; @@ -19,6 +18,7 @@ import { navigate } from "../../navigation"; import { entitySettingsURL } from "../../../common/routes"; import type { KubeAuthUpdate } from "../../../common/cluster-types"; import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; +import { requestClusterActivation } from "../../ipc"; interface Props { className?: IClassName; @@ -60,7 +60,7 @@ export class ClusterStatus extends React.Component { this.isReconnecting = true; try { - await requestMain(clusterActivateHandler, this.cluster.id, true); + await requestClusterActivation(this.cluster.id, true); } catch (error) { this.authOutput.push({ message: error.toString(), diff --git a/src/renderer/components/cluster-manager/cluster-view.tsx b/src/renderer/components/cluster-manager/cluster-view.tsx index 29b0f4c739..5187866ef3 100644 --- a/src/renderer/components/cluster-manager/cluster-view.tsx +++ b/src/renderer/components/cluster-manager/cluster-view.tsx @@ -12,11 +12,10 @@ import { ClusterStatus } from "./cluster-status"; import { ClusterFrameHandler } from "./lens-views"; import type { Cluster } from "../../../common/cluster/cluster"; import { ClusterStore } from "../../../common/cluster-store/cluster-store"; -import { requestMain } from "../../../common/ipc"; -import { clusterActivateHandler } from "../../../common/cluster-ipc"; import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; import { navigate } from "../../navigation"; import { catalogURL, ClusterViewRouteParams } from "../../../common/routes"; +import { requestClusterActivation } from "../../ipc"; interface Props extends RouteComponentProps { } @@ -58,7 +57,7 @@ export class ClusterView extends React.Component { reaction(() => this.clusterId, async (clusterId) => { ClusterFrameHandler.getInstance().setVisibleCluster(clusterId); ClusterFrameHandler.getInstance().initView(clusterId); - requestMain(clusterActivateHandler, clusterId, false); // activate and fetch cluster's state from main + requestClusterActivation(clusterId, false); // activate and fetch cluster's state from main catalogEntityRegistry.activeEntity = clusterId; }, { fireImmediately: true, diff --git a/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx b/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx index 9b30e686fe..f28222a2a2 100644 --- a/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx +++ b/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx @@ -12,8 +12,6 @@ import { Button } from "../button"; import type { KubeConfig } from "@kubernetes/client-node"; import type { Cluster } from "../../../common/cluster/cluster"; import { saveKubeconfig } from "./save-config"; -import { requestMain } from "../../../common/ipc"; -import { clusterClearDeletingHandler, clusterDeleteHandler, clusterSetDeletingHandler } from "../../../common/cluster-ipc"; import { Notifications } from "../notifications"; import { HotbarStore } from "../../../common/hotbar-store"; import { boundMethod } from "autobind-decorator"; @@ -21,6 +19,7 @@ import { Dialog } from "../dialog"; import { Icon } from "../icon"; import { Select } from "../select"; import { Checkbox } from "../checkbox"; +import { requestClearClusterAsDeleting, requestDeleteCluster, requestSetClusterAsDeleting } from "../../ipc"; type DialogState = { isOpen: boolean, @@ -87,18 +86,18 @@ export class DeleteClusterDialog extends React.Component { async onDelete() { const { cluster, config } = dialogState; - await requestMain(clusterSetDeletingHandler, cluster.id); + await requestSetClusterAsDeleting(cluster.id); this.removeContext(); this.changeCurrentContext(); try { await saveKubeconfig(config, cluster.kubeConfigPath); HotbarStore.getInstance().removeAllHotbarItems(cluster.id); - await requestMain(clusterDeleteHandler, cluster.id); + await requestDeleteCluster(cluster.id); } catch(error) { Notifications.error(`Cannot remove cluster, failed to process config file. ${error}`); } finally { - await requestMain(clusterClearDeletingHandler, cluster.id); + await requestClearClusterAsDeleting(cluster.id); } this.onClose(); diff --git a/src/renderer/components/layout/top-bar/top-bar.tsx b/src/renderer/components/layout/top-bar/top-bar.tsx index 9f354167ff..21d99ad645 100644 --- a/src/renderer/components/layout/top-bar/top-bar.tsx +++ b/src/renderer/components/layout/top-bar/top-bar.tsx @@ -9,7 +9,7 @@ import { observer } from "mobx-react"; import type { IComputedValue } from "mobx"; import { Icon } from "../../icon"; import { observable } from "mobx"; -import { broadcastMessage, IpcMainWindowEvents, ipcRendererOn, requestMain } from "../../../../common/ipc"; +import { broadcastMessage, IpcMainWindowEvents, ipcRendererOn } from "../../../../common/ipc"; import { watchHistoryState } from "../../../remote-helpers/history-updater"; import { isActiveRoute, navigate } from "../../../navigation"; import { catalogRoute, catalogURL } from "../../../../common/routes"; @@ -18,6 +18,8 @@ import { cssNames } from "../../../utils"; import topBarItemsInjectable from "./top-bar-items/top-bar-items.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { TopBarRegistration } from "./top-bar-registration"; +import { requestWindowAction } from "../../../ipc"; +import { WindowAction } from "../../../../common/ipc/window-actions"; interface Props extends React.HTMLAttributes {} @@ -48,11 +50,11 @@ const NonInjectedTopBar = (({ items, children, ...rest }: Props & Dependencies) }; const goBack = () => { - requestMain(IpcMainWindowEvents.WINDOW_ACTION, "back"); + requestWindowAction(WindowAction.GO_BACK); }; const goForward = () => { - requestMain(IpcMainWindowEvents.WINDOW_ACTION, "forward"); + requestWindowAction(WindowAction.GO_FORWARD); }; const windowSizeToggle = (evt: React.MouseEvent) => { @@ -65,22 +67,18 @@ const NonInjectedTopBar = (({ items, children, ...rest }: Props & Dependencies) }; const minimizeWindow = () => { - requestMain(IpcMainWindowEvents.WINDOW_ACTION, "minimize"); + requestWindowAction(WindowAction.MINIMIZE); }; const toggleMaximize = () => { - requestMain(IpcMainWindowEvents.WINDOW_ACTION, "toggleMaximize"); + requestWindowAction(WindowAction.TOGGLE_MAXIMIZE); }; const closeWindow = () => { - requestMain(IpcMainWindowEvents.WINDOW_ACTION, "close"); + requestWindowAction(WindowAction.CLOSE); }; - useEffect(() => { - const disposer = watchHistoryState(); - - return () => disposer(); - }, []); + useEffect(() => watchHistoryState(), []); return (
diff --git a/src/renderer/components/path-picker/path-picker.tsx b/src/renderer/components/path-picker/path-picker.tsx index c9be7cfed6..416bd3fd42 100644 --- a/src/renderer/components/path-picker/path-picker.tsx +++ b/src/renderer/components/path-picker/path-picker.tsx @@ -8,8 +8,7 @@ import { observer } from "mobx-react"; import React from "react"; import { cssNames } from "../../utils"; import { Button } from "../button"; -import { requestMain } from "../../../common/ipc"; -import { IpcMainDialogEvents } from "../../../common/ipc/dialog"; +import { requestOpenFilePickingDialog } from "../../ipc"; export interface PathPickOpts { label: string; @@ -32,7 +31,7 @@ export class PathPicker extends React.Component { static async pick(opts: PathPickOpts) { const { onPick, onCancel, label, ...dialogOptions } = opts; - const { canceled, filePaths } = await requestMain(IpcMainDialogEvents.SHOW_OPEN, { + const { canceled, filePaths } = await requestOpenFilePickingDialog({ message: label, ...dialogOptions, }); diff --git a/src/renderer/frames/cluster-frame/cluster-frame.tsx b/src/renderer/frames/cluster-frame/cluster-frame.tsx index cad742f485..9ef25ab844 100755 --- a/src/renderer/frames/cluster-frame/cluster-frame.tsx +++ b/src/renderer/frames/cluster-frame/cluster-frame.tsx @@ -71,7 +71,6 @@ class NonInjectedClusterFrame extends React.Component { this.props.subscribeStores([ this.props.namespaceStore, ]), - watchHistoryState(), ]); } diff --git a/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts b/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts index 22acc43be5..71d71cf8be 100644 --- a/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts +++ b/src/renderer/frames/cluster-frame/init-cluster-frame/init-cluster-frame.ts @@ -6,8 +6,6 @@ import type { Cluster } from "../../../../common/cluster/cluster"; import type { CatalogEntityRegistry } from "../../../api/catalog-entity-registry"; import logger from "../../../../main/logger"; import { Terminal } from "../../../components/dock/terminal/terminal"; -import { requestMain } from "../../../../common/ipc"; -import { clusterSetFrameIdHandler } from "../../../../common/cluster-ipc"; import type { KubernetesCluster } from "../../../../common/catalog-entities"; import { Notifications } from "../../../components/notifications"; import type { AppEvent } from "../../../../common/app-event-bus/event-bus"; @@ -16,6 +14,7 @@ import { when } from "mobx"; import { unmountComponentAtNode } from "react-dom"; import type { ClusterFrameContext } from "../../../cluster-frame-context/cluster-frame-context"; import { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; +import { requestSetClusterFrameId } from "../../../ipc"; interface Dependencies { hostedCluster: Cluster; @@ -42,7 +41,7 @@ export const initClusterFrame = ); await Terminal.preloadFonts(); - await requestMain(clusterSetFrameIdHandler, hostedCluster.id); + await requestSetClusterFrameId(hostedCluster.id); await hostedCluster.whenReady; // cluster.activate() is done at this point catalogEntityRegistry.activeEntity = hostedCluster.id; diff --git a/src/renderer/frames/root-frame/init-root-frame/init-root-frame.ts b/src/renderer/frames/root-frame/init-root-frame/init-root-frame.ts index c166847e2c..511c0baab0 100644 --- a/src/renderer/frames/root-frame/init-root-frame/init-root-frame.ts +++ b/src/renderer/frames/root-frame/init-root-frame/init-root-frame.ts @@ -3,12 +3,13 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { delay } from "../../../../common/utils"; -import { broadcastMessage, BundledExtensionsLoaded } from "../../../../common/ipc"; -import { registerIpcListeners } from "../../../ipc"; +import { broadcastMessage } from "../../../../common/ipc"; +import { registerIpcListeners } from "../../../ipc/register-listeners"; import logger from "../../../../common/logger"; import { unmountComponentAtNode } from "react-dom"; import type { ExtensionLoading } from "../../../../extensions/extension-loader"; import type { CatalogEntityRegistry } from "../../../api/catalog-entity-registry"; +import { bundledExtensionsLoaded } from "../../../../common/ipc/extension-handling"; interface Dependencies { loadExtensions: () => Promise; @@ -50,7 +51,7 @@ export const initRootFrame = await Promise.race([bundledExtensionsFinished, timeout]); } finally { - ipcRenderer.send(BundledExtensionsLoaded); + ipcRenderer.send(bundledExtensionsLoaded); } lensProtocolRouterRenderer.init(); diff --git a/src/renderer/ipc/index.ts b/src/renderer/ipc/index.ts new file mode 100644 index 0000000000..48e1ef6e7a --- /dev/null +++ b/src/renderer/ipc/index.ts @@ -0,0 +1,71 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { ipcRenderer, OpenDialogOptions } from "electron"; +import { clusterActivateHandler, clusterClearDeletingHandler, clusterDeleteHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterSetDeletingHandler, clusterSetFrameIdHandler, clusterStates } from "../../common/cluster-ipc"; +import type { ClusterId, ClusterState } from "../../common/cluster-types"; +import { IpcMainWindowEvents } from "../../common/ipc"; +import { openFilePickingDialogChannel } from "../../common/ipc/dialog"; +import { extensionDiscoveryStateChannel, extensionLoaderFromMainChannel } from "../../common/ipc/extension-handling"; +import type { WindowAction } from "../../common/ipc/window-actions"; +import type { InstalledExtension } from "../../extensions/extension-discovery/extension-discovery"; +import type { LensExtensionId } from "../../extensions/lens-extension"; +import { toJS } from "../utils"; + +function requestMain(channel: string, ...args: any[]) { + return ipcRenderer.invoke(channel, ...args.map(toJS)); +} + +export function requestWindowAction(type: WindowAction): Promise { + return requestMain(IpcMainWindowEvents.WINDOW_ACTION, type); +} + +export function requestOpenFilePickingDialog(opts: OpenDialogOptions): Promise<{ canceled: boolean; filePaths: string[]; }> { + return requestMain(openFilePickingDialogChannel, opts); +} + +export function requestSetClusterFrameId(clusterId: ClusterId): Promise { + return requestMain(clusterSetFrameIdHandler, clusterId); +} + +export function requestClusterActivation(clusterId: ClusterId, force?: boolean): Promise { + return requestMain(clusterActivateHandler, clusterId, force); +} + +export function requestClusterDisconnection(clusterId: ClusterId, force?: boolean): Promise { + return requestMain(clusterDisconnectHandler, clusterId, force); +} + +export function requestSetClusterAsDeleting(clusterId: ClusterId): Promise { + return requestMain(clusterSetDeletingHandler, clusterId); +} + +export function requestClearClusterAsDeleting(clusterId: ClusterId): Promise { + return requestMain(clusterClearDeletingHandler, clusterId); +} + +export function requestDeleteCluster(clusterId: ClusterId): Promise { + return requestMain(clusterDeleteHandler, clusterId); +} + +export function requestInitialClusterStates(): Promise<{ id: string, state: ClusterState }[]> { + return requestMain(clusterStates); +} + +export function requestKubectlApplyAll(clusterId: ClusterId, resources: string[], kubectlArgs: string[]): Promise<{ stderr?: string; stdout?: string }> { + return requestMain(clusterKubectlApplyAllHandler, clusterId, resources, kubectlArgs); +} + +export function requestKubectlDeleteAll(clusterId: ClusterId, resources: string[], kubectlArgs: string[]): Promise<{ stderr?: string; stdout?: string }> { + return requestMain(clusterKubectlDeleteAllHandler, clusterId, resources, kubectlArgs); +} + +export function requestInitialExtensionDiscovery(): Promise<{ isLoaded: boolean }> { + return requestMain(extensionDiscoveryStateChannel); +} + +export function requestExtensionLoaderInitialState(): Promise<[LensExtensionId, InstalledExtension][]> { + return requestMain(extensionLoaderFromMainChannel); +} diff --git a/src/renderer/ipc/index.tsx b/src/renderer/ipc/register-listeners.tsx similarity index 100% rename from src/renderer/ipc/index.tsx rename to src/renderer/ipc/register-listeners.tsx diff --git a/src/renderer/remote-helpers/dialog.ts b/src/renderer/remote-helpers/dialog.ts deleted file mode 100644 index 038409eb39..0000000000 --- a/src/renderer/remote-helpers/dialog.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { dialogShowOpenDialogHandler, requestMain } from "../../common/ipc"; - -export async function showOpenDialog(options: Electron.OpenDialogOptions): Promise { - return requestMain(dialogShowOpenDialogHandler, options); -} diff --git a/src/renderer/remote-helpers/history-updater.ts b/src/renderer/remote-helpers/history-updater.ts index f066e5df3c..3380a4aeac 100644 --- a/src/renderer/remote-helpers/history-updater.ts +++ b/src/renderer/remote-helpers/history-updater.ts @@ -3,12 +3,13 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { ipcRenderer } from "electron"; import { reaction } from "mobx"; -import { IpcMainWindowEvents, requestMain } from "../../common/ipc"; +import { IpcMainWindowEvents } from "../../common/ipc"; import { navigation } from "../navigation"; export function watchHistoryState() { return reaction(() => navigation.location, (location) => { - requestMain(IpcMainWindowEvents.LOCATION_CHANGED, location); + ipcRenderer.send(IpcMainWindowEvents.LOCATION_CHANGED, location); }); } diff --git a/src/renderer/remote-helpers/index.ts b/src/renderer/remote-helpers/index.ts deleted file mode 100644 index b15edbcc83..0000000000 --- a/src/renderer/remote-helpers/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import * as dialog from "./dialog"; - -export { dialog };