From cae880ad856895d5dd426c64c380bf69fa747a07 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Wed, 4 Nov 2020 18:43:23 +0200 Subject: [PATCH] refactor ipc module Signed-off-by: Jari Kolehmainen --- src/common/base-store.ts | 58 +++------ src/common/cluster-ipc.ts | 90 ++++++-------- src/common/cluster-store.ts | 20 +-- src/common/ipc.ts | 117 ++++++++---------- src/common/workspace-store.ts | 7 +- src/extensions/cluster-feature.ts | 4 +- src/extensions/extension-loader.ts | 27 ++-- src/main/cluster.ts | 8 +- src/main/kube-auth-proxy.ts | 4 +- src/main/window-manager.ts | 10 +- .../+cluster-settings/cluster-settings.tsx | 6 +- .../components/remove-cluster-button.tsx | 1 - src/renderer/components/app.tsx | 4 +- .../cluster-manager/cluster-status.tsx | 6 +- .../cluster-manager/clusters-menu.tsx | 4 +- src/renderer/components/layout/sidebar.tsx | 8 +- src/renderer/lens-app.tsx | 5 +- src/renderer/navigation.ts | 11 +- 18 files changed, 176 insertions(+), 214 deletions(-) diff --git a/src/common/base-store.ts b/src/common/base-store.ts index 5261c604d6..e057f17f4c 100644 --- a/src/common/base-store.ts +++ b/src/common/base-store.ts @@ -6,7 +6,7 @@ import { action, observable, reaction, runInAction, toJS, when } from "mobx"; import Singleton from "./utils/singleton"; import { getAppVersion } from "./utils/app-version"; import logger from "../main/logger"; -import { broadcastIpc, IpcBroadcastParams } from "./ipc"; +import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "./ipc"; import isEqual from "lodash/isEqual"; export interface BaseStoreParams extends ConfOptions { @@ -36,8 +36,12 @@ export class BaseStore extends Singleton { return path.basename(this.storeConfig.path); } - get syncChannel() { - return `store-sync:${this.name}` + get syncRendererChannel() { + return `store-sync-renderer:${this.name}` + } + + get syncMainChannel() { + return `store-sync-main:${this.name}` } protected async init() { @@ -84,16 +88,16 @@ export class BaseStore extends Singleton { logger.silly(`[STORE]: SYNC ${this.name} from renderer`, { model }); this.onSync(model); }; - ipcMain.on(this.syncChannel, callback); - this.syncDisposers.push(() => ipcMain.off(this.syncChannel, callback)); + subscribeToBroadcast(this.syncMainChannel, callback) + this.syncDisposers.push(() => unsubscribeFromBroadcast(this.syncMainChannel, callback)); } if (ipcRenderer) { const callback = (event: IpcRendererEvent, model: T) => { logger.silly(`[STORE]: SYNC ${this.name} from main`, { model }); this.onSyncFromMain(model); }; - ipcRenderer.on(this.syncChannel, callback); - this.syncDisposers.push(() => ipcRenderer.off(this.syncChannel, callback)); + subscribeToBroadcast(this.syncRendererChannel, callback) + this.syncDisposers.push(() => unsubscribeFromBroadcast(this.syncRendererChannel, callback)); } } @@ -104,7 +108,8 @@ export class BaseStore extends Singleton { } unregisterIpcListener() { - ipcRenderer.removeAllListeners(this.syncChannel) + ipcRenderer.removeAllListeners(this.syncMainChannel) + ipcRenderer.removeAllListeners(this.syncRendererChannel) } disableSync() { @@ -130,41 +135,10 @@ export class BaseStore extends Singleton { protected async onModelChange(model: T) { if (ipcMain) { this.saveToFile(model); // save config file - this.syncToWebViews(model); // send update to renderer views + broadcastMessage(this.syncRendererChannel, model) + } else { + broadcastMessage(this.syncMainChannel, model) } - // send "update-request" to main-process - if (ipcRenderer) { - ipcRenderer.send(this.syncChannel, model); - } - } - - protected async syncToWebViews(model: T) { - const msg: IpcBroadcastParams = { - channel: this.syncChannel, - args: [model], - } - broadcastIpc(msg); // send to all windows (BrowserWindow, webContents) - const frames = await this.getSubFrames(); - frames.forEach(frameId => { - // send to all sub-frames (e.g. cluster-view managed in iframe) - broadcastIpc({ - ...msg, - frameId: frameId, - frameOnly: true, - }); - }); - } - - // todo: refactor? - protected async getSubFrames(): Promise { - const subFrames: number[] = []; - const { clusterStore } = await import("./cluster-store"); - clusterStore.clustersList.forEach(cluster => { - if (cluster.frameId) { - subFrames.push(cluster.frameId) - } - }); - return subFrames; } @action diff --git a/src/common/cluster-ipc.ts b/src/common/cluster-ipc.ts index 69a3ebf80f..38a25e8a75 100644 --- a/src/common/cluster-ipc.ts +++ b/src/common/cluster-ipc.ts @@ -1,59 +1,43 @@ -import { createIpcChannel } from "./ipc"; +import { handleRequest } from "./ipc"; import { ClusterId, clusterStore } from "./cluster-store"; -import { extensionLoader } from "../extensions/extension-loader" import { appEventBus } from "./event-bus" import { ResourceApplier } from "../main/resource-applier"; +import { ipcMain } from "electron"; -export const clusterIpc = { - activate: createIpcChannel({ - channel: "cluster:activate", - handle: (clusterId: ClusterId, force = false) => { - const cluster = clusterStore.getById(clusterId); - if (cluster) { - return cluster.activate(force); - } - }, - }), - - setFrameId: createIpcChannel({ - channel: "cluster:set-frame-id", - handle: (clusterId: ClusterId, frameId?: number) => { - const cluster = clusterStore.getById(clusterId); - if (cluster) { - if (frameId) cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates - extensionLoader.broadcastExtensions(frameId) - return cluster.pushState(); - } - }, - }), - - refresh: createIpcChannel({ - channel: "cluster:refresh", - handle: (clusterId: ClusterId) => { - const cluster = clusterStore.getById(clusterId); - if (cluster) return cluster.refresh({ refreshMetadata: true }) - }, - }), - - disconnect: createIpcChannel({ - channel: "cluster:disconnect", - handle: (clusterId: ClusterId) => { - appEventBus.emit({name: "cluster", action: "stop"}); - return clusterStore.getById(clusterId)?.disconnect(); - }, - }), - - kubectlApplyAll: createIpcChannel({ - channel: "cluster:kubectl-apply-all", - handle: (clusterId: ClusterId, resources: string[]) => { - appEventBus.emit({name: "cluster", action: "kubectl-apply-all"}) - const cluster = clusterStore.getById(clusterId); - if (cluster) { - const applier = new ResourceApplier(cluster) - applier.kubectlApplyAll(resources) - } else { - throw `${clusterId} is not a valid cluster id`; - } +if (ipcMain) { + handleRequest("cluster:activate", (event, clusterId: ClusterId, force = false) => { + const cluster = clusterStore.getById(clusterId); + if (cluster) { + return cluster.activate(force); } - }), + }) + + handleRequest("cluster:set-frame-id", (event, clusterId: ClusterId, frameId?: number) => { + const cluster = clusterStore.getById(clusterId); + if (cluster) { + if (frameId) cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates + return cluster.pushState(); + } + }) + + handleRequest("cluster:refresh", (event, clusterId: ClusterId) => { + const cluster = clusterStore.getById(clusterId); + if (cluster) return cluster.refresh({ refreshMetadata: true }) + }) + + handleRequest("cluster:disconnect", (event, clusterId: ClusterId) => { + appEventBus.emit({name: "cluster", action: "stop"}); + return clusterStore.getById(clusterId)?.disconnect(); + }) + + handleRequest("cluster:kubectl-apply-all", (event, clusterId: ClusterId, resources: string[]) => { + appEventBus.emit({name: "cluster", action: "kubectl-apply-all"}) + const cluster = clusterStore.getById(clusterId); + if (cluster) { + const applier = new ResourceApplier(cluster) + applier.kubectlApplyAll(resources) + } else { + throw `${clusterId} is not a valid cluster id`; + } + }) } diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts index 838e6cc119..b376598bc7 100644 --- a/src/common/cluster-store.ts +++ b/src/common/cluster-store.ts @@ -2,7 +2,7 @@ import type { WorkspaceId } from "./workspace-store"; import path from "path"; import { app, ipcRenderer, remote, webFrame } from "electron"; import { unlink } from "fs-extra"; -import { action, computed, observable, toJS } from "mobx"; +import { action, computed, observable, reaction, toJS } from "mobx"; import { BaseStore } from "./base-store"; import { Cluster, ClusterState } from "../main/cluster"; import migrations from "../migrations/cluster-store" @@ -13,6 +13,7 @@ import { saveToAppFiles } from "./utils/saveToAppFiles"; import { KubeConfig } from "@kubernetes/client-node"; import _ from "lodash"; import move from "array-move"; +import { subscribeToBroadcast, unsubscribeAllFromBroadcast } from "./ipc"; export interface ClusterIconUpload { clusterId: string; @@ -84,21 +85,20 @@ export class ClusterStore extends BaseStore { migrations: migrations, }); - this.pushStateToViewsPeriodically() + this.pushStateToViewsAutomatically() } - protected pushStateToViewsPeriodically() { + protected pushStateToViewsAutomatically() { if (!ipcRenderer) { - // This is a bit of a hack, we need to do this because we might loose messages that are sent before a view is ready - setInterval(() => { + reaction(() => this.connectedClustersList, () => { this.pushState() - }, 5000) + }) } } registerIpcListener() { logger.info(`[CLUSTER-STORE] start to listen (${webFrame.routingId})`) - ipcRenderer.on("cluster:state", (event, clusterId: string, state: ClusterState) => { + subscribeToBroadcast("cluster:state", (event, clusterId: string, state: ClusterState) => { logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host} (${webFrame.routingId})`, clusterId, state); this.getById(clusterId)?.setState(state) }) @@ -106,7 +106,7 @@ export class ClusterStore extends BaseStore { unregisterIpcListener() { super.unregisterIpcListener() - ipcRenderer.removeAllListeners("cluster:state") + unsubscribeAllFromBroadcast("cluster:state") } pushState() { @@ -131,6 +131,10 @@ export class ClusterStore extends BaseStore { return this.getById(this.activeCluster); } + @computed get connectedClustersList(): Cluster[] { + return this.clustersList.filter((c) => !c.disconnected) + } + isActive(id: ClusterId) { return this.activeCluster === id; } diff --git a/src/common/ipc.ts b/src/common/ipc.ts index 97c4dd05cd..6cff18f16b 100644 --- a/src/common/ipc.ts +++ b/src/common/ipc.ts @@ -1,79 +1,70 @@ -// Inter-protocol communications (main <-> renderer) +// Inter-process communications (main <-> renderer) // https://www.electronjs.org/docs/api/ipc-main // https://www.electronjs.org/docs/api/ipc-renderer -import { ipcMain, ipcRenderer, WebContents, webContents } from "electron" +import { ipcMain, ipcRenderer, webContents, remote } from "electron" import logger from "../main/logger"; -export type IpcChannel = string; - -export interface IpcChannelOptions { - channel: IpcChannel; // main <-> renderer communication channel name - handle?: (...args: any[]) => Promise | any; // message handler - autoBind?: boolean; // auto-bind message handler in main-process, default: true - timeout?: number; // timeout for waiting response from the sender - once?: boolean; // one-time event +export function handleRequest(channel: string, listener: (...args: any[]) => any) { + ipcMain.handle(channel, listener) } -export function createIpcChannel({ autoBind = true, once, timeout = 0, handle, channel }: IpcChannelOptions) { - const ipcChannel = { - channel: channel, - handleInMain: () => { - logger.info(`[IPC]: setup channel "${channel}"`); - const ipcHandler = once ? ipcMain.handleOnce : ipcMain.handle; - ipcHandler(channel, async (event, ...args) => { - let timerId: any; - try { - if (timeout > 0) { - timerId = setTimeout(() => { - throw new Error(`[IPC]: response timeout in ${timeout}ms`) - }, timeout); - } - return await handle(...args); // todo: maybe exec in separate thread/worker - } catch (error) { - throw error - } finally { - clearTimeout(timerId); - } - }) - }, - removeHandler() { - ipcMain.removeHandler(channel); - }, - invokeFromRenderer: async (...args: any[]): Promise => { - return ipcRenderer.invoke(channel, ...args); - }, - } - if (autoBind && ipcMain) { - ipcChannel.handleInMain(); - } - return ipcChannel; +export async function requestMain(channel: string, ...args: any[]) { + return await ipcRenderer.invoke(channel, ...args) } -export interface IpcBroadcastParams { - channel: IpcChannel - webContentId?: number; // send to single webContents view - frameId?: number; // send to inner frame of webContents - frameOnly?: boolean; // send message only to view with provided `frameId` - filter?: (webContent: WebContents) => boolean - timeout?: number; // todo: add support - args?: A; +async function getSubFrames(): Promise { + const subFrames: number[] = []; + const { clusterStore } = await import("./cluster-store"); + clusterStore.clustersList.forEach(cluster => { + if (cluster.frameId) { + subFrames.push(cluster.frameId) + } + }); + return subFrames; } -export function broadcastIpc({ channel, frameId, frameOnly, webContentId, filter, args = [] }: IpcBroadcastParams) { - const singleView = webContentId ? webContents.fromId(webContentId) : null; - let views = singleView ? [singleView] : webContents.getAllWebContents(); - if (filter) { - views = views.filter(filter); - } +export function broadcastMessage(channel: string, ...args: any[]) { + const views = (webContents || remote.webContents).getAllWebContents(); views.forEach(webContent => { const type = webContent.getType(); logger.silly(`[IPC]: broadcasting "${channel}" to ${type}=${webContent.id}`, { args }); - if (!frameOnly) { - webContent.send(channel, ...args); - } - if (frameId) { - webContent.sendToFrame(frameId, channel, ...args) - } + webContent.send(channel, ...args); + getSubFrames().then((frames) => { + frames.map((frameId) => { + webContent.sendToFrame(frameId, channel, ...args) + }) + }).catch((e) => e) }) + if (ipcRenderer) { + ipcRenderer.send(channel, ...args) + } else { + ipcMain.emit(channel, ...args) + } +} + +export function subscribeToBroadcast(channel: string, listener: (...args: any[]) => any) { + if (ipcRenderer) { + ipcRenderer.on(channel, listener) + } else { + ipcMain.on(channel, listener) + } + + return listener +} + +export function unsubscribeFromBroadcast(channel: string, listener: (...args: any[]) => any) { + if (ipcRenderer) { + ipcRenderer.off(channel, listener) + } else { + ipcMain.off(channel, listener) + } +} + +export function unsubscribeAllFromBroadcast(channel: string) { + if (ipcRenderer) { + ipcRenderer.removeAllListeners(channel) + } else { + ipcMain.removeAllListeners(channel) + } } diff --git a/src/common/workspace-store.ts b/src/common/workspace-store.ts index 97611a01d3..6b0c6350f4 100644 --- a/src/common/workspace-store.ts +++ b/src/common/workspace-store.ts @@ -3,7 +3,7 @@ import { action, computed, observable, toJS, reaction } from "mobx"; import { BaseStore } from "./base-store"; import { clusterStore } from "./cluster-store" import { appEventBus } from "./event-bus"; -import { broadcastIpc } from "../common/ipc"; +import { broadcastMessage } from "../common/ipc"; import logger from "../main/logger"; export type WorkspaceId = string; @@ -53,10 +53,7 @@ export class Workspace implements WorkspaceModel, WorkspaceState { pushState(state = this.getState()) { logger.silly("[WORKSPACE] pushing state", {...state, id: this.id}) - broadcastIpc({ - channel: "workspace:state", - args: [this.id, toJS(state)], - }); + broadcastMessage("workspace:state", this.id, toJS(state)) } @action diff --git a/src/extensions/cluster-feature.ts b/src/extensions/cluster-feature.ts index 08904750e2..efb26aba43 100644 --- a/src/extensions/cluster-feature.ts +++ b/src/extensions/cluster-feature.ts @@ -6,7 +6,7 @@ import { ResourceApplier } from "../main/resource-applier" import { Cluster } from "../main/cluster"; import logger from "../main/logger"; import { app } from "electron" -import { clusterIpc } from "../common/cluster-ipc" +import { requestMain } from "../common/ipc"; export interface ClusterFeatureStatus { currentVersion: string; @@ -39,7 +39,7 @@ export abstract class ClusterFeature { if (app) { await new ResourceApplier(cluster).kubectlApplyAll(resources) } else { - await clusterIpc.kubectlApplyAll.invokeFromRenderer(cluster.id, resources) + await requestMain("cluster:kubectl-apply-all", cluster.id, resources) } } diff --git a/src/extensions/extension-loader.ts b/src/extensions/extension-loader.ts index 5495438555..1ffafc9646 100644 --- a/src/extensions/extension-loader.ts +++ b/src/extensions/extension-loader.ts @@ -2,10 +2,12 @@ import type { ExtensionId, ExtensionManifest, ExtensionModel, LensExtension } fr import type { LensMainExtension } from "./lens-main-extension" import type { LensRendererExtension } from "./lens-renderer-extension" import path from "path" -import { broadcastIpc } from "../common/ipc" +import { broadcastMessage, subscribeToBroadcast } from "../common/ipc" import { observable, reaction, toJS, } from "mobx" import logger from "../main/logger" import { app, ipcRenderer, remote } from "electron" +import { appEventBus } from "./core-api/event-bus" +import { clusterStore } from "./core-api/stores" import { appPreferenceRegistry, clusterFeatureRegistry, clusterPageRegistry, globalPageRegistry, kubeObjectDetailRegistry, kubeObjectMenuRegistry, menuRegistry, statusBarRegistry @@ -27,13 +29,25 @@ export class ExtensionLoader { constructor() { if (ipcRenderer) { - ipcRenderer.on("extensions:loaded", (event, extensions: InstalledExtension[]) => { + subscribeToBroadcast("extensions:loaded", (event, extensions: InstalledExtension[]) => { extensions.forEach((ext) => { if (!this.getById(ext.manifestPath)) { this.extensions.set(ext.manifestPath, ext) } }) }) + } else { + reaction(() => this.extensions.toJS(), () => { + this.broadcastExtensions() + }) + appEventBus.addListener((ev) => { + if (ev.name === "app" && ev.action === "dom-ready") { + this.broadcastExtensions() + } + }) + reaction(() => clusterStore.connectedClustersList, () => { + this.broadcastExtensions() + }) } } @@ -120,13 +134,8 @@ export class ExtensionLoader { } } - broadcastExtensions(frameId?: number) { - broadcastIpc({ - channel: "extensions:loaded", - frameId: frameId, - frameOnly: !!frameId, - args: [this.toJSON().extensions], - }) + broadcastExtensions() { + broadcastMessage("extensions:loaded", this.toJSON().extensions) } toJSON() { diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 656ee67cdb..759004ce14 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -4,7 +4,7 @@ import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api"; import type { WorkspaceId } from "../common/workspace-store"; import { action, computed, observable, reaction, toJS, when } from "mobx"; import { apiKubePrefix } from "../common/vars"; -import { broadcastIpc } from "../common/ipc"; +import { broadcastMessage } from "../common/ipc"; import { ContextHandler } from "./context-handler" import { AuthorizationV1Api, CoreV1Api, KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node" import { Kubectl } from "./kubectl"; @@ -405,11 +405,7 @@ export class Cluster implements ClusterModel, ClusterState { pushState(state = this.getState()) { logger.silly(`[CLUSTER]: push-state`, state); - broadcastIpc({ - channel: "cluster:state", - frameId: this.frameId, - args: [this.id, state], - }) + broadcastMessage("cluster:state", this.id, state) } // get cluster system meta, e.g. use in "logger" diff --git a/src/main/kube-auth-proxy.ts b/src/main/kube-auth-proxy.ts index 7192425466..4d64e77495 100644 --- a/src/main/kube-auth-proxy.ts +++ b/src/main/kube-auth-proxy.ts @@ -1,6 +1,6 @@ import { ChildProcess, spawn } from "child_process" import { waitUntilUsed } from "tcp-port-used"; -import { broadcastIpc } from "../common/ipc"; +import { broadcastMessage } from "../common/ipc"; import type { Cluster } from "./cluster" import { Kubectl } from "./kubectl" import logger from "./logger" @@ -88,7 +88,7 @@ export class KubeAuthProxy { protected async sendIpcLogMessage(res: KubeAuthProxyLog) { const channel = `kube-auth:${this.cluster.id}` logger.info(`[KUBE-AUTH]: out-channel "${channel}"`, { ...res, meta: this.cluster.getMeta() }); - broadcastIpc({ channel: channel, args: [res] }); + broadcastMessage(channel, res) } public exit() { diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index 46b008a0d4..fbeccb7443 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -1,10 +1,10 @@ import type { ClusterId } from "../common/cluster-store"; import { clusterStore } from "../common/cluster-store"; import { observable } from "mobx"; -import { app, BrowserWindow, dialog, ipcMain, shell, webContents } from "electron" +import { app, BrowserWindow, dialog, shell, webContents } from "electron" import windowStateKeeper from "electron-window-state" -import { extensionLoader } from "../extensions/extension-loader"; import { appEventBus } from "../common/event-bus" +import { subscribeToBroadcast } from "../common/ipc" import { initMenu } from "./menu"; import { initTray } from "./tray"; @@ -61,7 +61,7 @@ export class WindowManager { shell.openExternal(url); }); this.mainWindow.webContents.on("dom-ready", () => { - extensionLoader.broadcastExtensions() + appEventBus.emit({name: "app", action: "dom-ready"}) }) this.mainWindow.on("focus", () => { appEventBus.emit({name: "app", action: "focus"}) @@ -98,9 +98,9 @@ export class WindowManager { protected bindEvents() { // track visible cluster from ui - ipcMain.on("cluster-view:current-id", (event, clusterId: ClusterId) => { + subscribeToBroadcast("cluster-view:current-id", (event, clusterId: ClusterId) => { this.activeClusterId = clusterId; - }); + }) } async ensureMainWindow(): Promise { diff --git a/src/renderer/components/+cluster-settings/cluster-settings.tsx b/src/renderer/components/+cluster-settings/cluster-settings.tsx index fc6cca3cce..ecf5b982b0 100644 --- a/src/renderer/components/+cluster-settings/cluster-settings.tsx +++ b/src/renderer/components/+cluster-settings/cluster-settings.tsx @@ -12,8 +12,8 @@ import { Cluster } from "../../../main/cluster"; import { ClusterIcon } from "../cluster-icon"; import { IClusterSettingsRouteParams } from "./cluster-settings.route"; import { clusterStore } from "../../../common/cluster-store"; -import { clusterIpc } from "../../../common/cluster-ipc"; import { PageLayout } from "../layout/page-layout"; +import { requestMain } from "../../../common/ipc"; interface Props extends RouteComponentProps { } @@ -41,8 +41,8 @@ export class ClusterSettings extends React.Component { refreshCluster = async () => { if (this.cluster) { - await clusterIpc.activate.invokeFromRenderer(this.cluster.id); - await clusterIpc.refresh.invokeFromRenderer(this.cluster.id); + await requestMain("cluster:activate", this.cluster.id) + await requestMain("cluster:refresh", this.cluster.id) } } diff --git a/src/renderer/components/+cluster-settings/components/remove-cluster-button.tsx b/src/renderer/components/+cluster-settings/components/remove-cluster-button.tsx index dda3e7c137..f407ee383a 100644 --- a/src/renderer/components/+cluster-settings/components/remove-cluster-button.tsx +++ b/src/renderer/components/+cluster-settings/components/remove-cluster-button.tsx @@ -1,7 +1,6 @@ import React from "react"; import { Trans } from "@lingui/macro"; import { observer } from "mobx-react"; -import { clusterIpc } from "../../../../common/cluster-ipc"; import { clusterStore } from "../../../../common/cluster-store"; import { Cluster } from "../../../../main/cluster"; import { autobind } from "../../../utils"; diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx index 5ad908a528..67daab1175 100755 --- a/src/renderer/components/app.tsx +++ b/src/renderer/components/app.tsx @@ -33,12 +33,12 @@ import { ErrorBoundary } from "./error-boundary"; import { Terminal } from "./dock/terminal"; import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store"; import logger from "../../main/logger"; -import { clusterIpc } from "../../common/cluster-ipc"; import { webFrame } from "electron"; import { clusterPageRegistry } from "../../extensions/registries/page-registry"; import { DynamicPage } from "../../extensions/dynamic-page"; import { extensionLoader } from "../../extensions/extension-loader"; import { appEventBus } from "../../common/event-bus" +import { requestMain } from "../../common/ipc"; @observer export class App extends React.Component { @@ -48,7 +48,7 @@ export class App extends React.Component { logger.info(`[APP]: Init dashboard, clusterId=${clusterId}, frameId=${frameId}`) await Terminal.preloadFonts() - await clusterIpc.setFrameId.invokeFromRenderer(clusterId, frameId); + await requestMain("cluster:set-frame-id", clusterId, frameId) await getHostedCluster().whenReady; // cluster.activate() is done at this point extensionLoader.loadOnClusterRenderer(); appEventBus.emit({name: "cluster", action: "open", params: { diff --git a/src/renderer/components/cluster-manager/cluster-status.tsx b/src/renderer/components/cluster-manager/cluster-status.tsx index 5a488c4405..c64c8f9f84 100644 --- a/src/renderer/components/cluster-manager/cluster-status.tsx +++ b/src/renderer/components/cluster-manager/cluster-status.tsx @@ -5,7 +5,7 @@ import React from "react"; import { observer } from "mobx-react"; import { ipcRenderer } from "electron"; import { computed, observable } from "mobx"; -import { clusterIpc } from "../../../common/cluster-ipc"; +import { requestMain, subscribeToBroadcast } from "../../../common/ipc"; import { Icon } from "../icon"; import { Button } from "../button"; import { cssNames, IClassName } from "../../utils"; @@ -32,7 +32,7 @@ export class ClusterStatus extends React.Component { } async componentDidMount() { - ipcRenderer.on(`kube-auth:${this.cluster.id}`, (evt, res: KubeAuthProxyLog) => { + subscribeToBroadcast(`kube-auth:${this.cluster.id}`, (evt, res: KubeAuthProxyLog) => { this.authOutput.push({ data: res.data.trimRight(), error: res.error, @@ -48,7 +48,7 @@ export class ClusterStatus extends React.Component { } activateCluster = async (force = false) => { - await clusterIpc.activate.invokeFromRenderer(this.props.clusterId, force); + await requestMain("cluster:activate", this.props.clusterId, force) } reconnect = async () => { diff --git a/src/renderer/components/cluster-manager/clusters-menu.tsx b/src/renderer/components/cluster-manager/clusters-menu.tsx index 3d1e6debaf..9608ee3d51 100644 --- a/src/renderer/components/cluster-manager/clusters-menu.tsx +++ b/src/renderer/components/cluster-manager/clusters-menu.tsx @@ -2,6 +2,7 @@ import "./clusters-menu.scss" import React from "react"; import { remote } from "electron" +import { requestMain } from "../../../common/ipc"; import type { Cluster } from "../../../main/cluster"; import { DragDropContext, Draggable, DraggableProvided, Droppable, DroppableProvided, DropResult } from "react-beautiful-dnd"; import { observer } from "mobx-react"; @@ -20,7 +21,6 @@ import { clusterSettingsURL } from "../+cluster-settings"; import { landingURL } from "../+landing-page"; import { Tooltip } from "../tooltip"; import { ConfirmDialog } from "../confirm-dialog"; -import { clusterIpc } from "../../../common/cluster-ipc"; import { clusterViewURL } from "./cluster-view.route"; import { globalPageRegistry } from "../../../extensions/registries/page-registry"; @@ -60,7 +60,7 @@ export class ClustersMenu extends React.Component { navigate(landingURL()); clusterStore.setActive(null); } - await clusterIpc.disconnect.invokeFromRenderer(cluster.id); + await requestMain("cluster:disconnect", cluster.id) } })) } diff --git a/src/renderer/components/layout/sidebar.tsx b/src/renderer/components/layout/sidebar.tsx index 4619e48de7..f0b7352ecb 100644 --- a/src/renderer/components/layout/sidebar.tsx +++ b/src/renderer/components/layout/sidebar.tsx @@ -72,9 +72,15 @@ export class Sidebar extends React.Component { }); } + @computed + get pageRegistryItems() { + return clusterPageRegistry.getItems() + } + render() { const { toggle, isPinned, className } = this.props; const query = namespaceStore.getContextParams(); + const registryItems = this.pageRegistryItems return (
@@ -184,7 +190,7 @@ export class Sidebar extends React.Component { > {this.renderCustomResources()} - {clusterPageRegistry.getItems().map(({ path, title, url = String(path), hideInMenu, components: { MenuIcon } }) => { + {registryItems.map(({ path, title, url = String(path), hideInMenu, components: { MenuIcon } }) => { if (!MenuIcon || hideInMenu) { return; } diff --git a/src/renderer/lens-app.tsx b/src/renderer/lens-app.tsx index 5c1b27b13a..4ed92f7ff8 100644 --- a/src/renderer/lens-app.tsx +++ b/src/renderer/lens-app.tsx @@ -13,16 +13,17 @@ import { WhatsNew, whatsNewRoute } from "./components/+whats-new"; import { Notifications } from "./components/notifications"; import { ConfirmDialog } from "./components/confirm-dialog"; import { extensionLoader } from "../extensions/extension-loader"; +import { broadcastMessage } from "../common/ipc"; @observer export class LensApp extends React.Component { static async init() { extensionLoader.loadOnClusterManagerRenderer(); window.addEventListener("offline", () => { - ipcRenderer.send("network:offline") + broadcastMessage("network:offline") }) window.addEventListener("online", () => { - ipcRenderer.send("network:online") + broadcastMessage("network:online") }) } diff --git a/src/renderer/navigation.ts b/src/renderer/navigation.ts index b4e0f383b1..7a223dab0c 100644 --- a/src/renderer/navigation.ts +++ b/src/renderer/navigation.ts @@ -7,6 +7,7 @@ import { createObservableHistory } from "mobx-observable-history"; import { createBrowserHistory, createMemoryHistory, LocationDescriptor } from "history"; import logger from "../main/logger"; import { clusterViewRoute, IClusterViewRouteParams } from "./components/cluster-manager/cluster-view.route"; +import { broadcastMessage, subscribeToBroadcast } from "../common/ipc"; export const history = typeof window !== "undefined" ? createBrowserHistory() : createMemoryHistory(); export const navigation = createObservableHistory(history); @@ -94,19 +95,19 @@ export function getMatchedClusterId(): string { if (process.isMainFrame) { // Keep track of active cluster-id for handling IPC/menus/etc. reaction(() => getMatchedClusterId(), clusterId => { - ipcRenderer.send("cluster-view:current-id", clusterId); + broadcastMessage("cluster-view:current-id", clusterId) }, { fireImmediately: true }) } // Handle navigation via IPC (e.g. from top menu) -ipcRenderer.on("menu:navigate", (event, location: LocationDescriptor) => { +subscribeToBroadcast("menu:navigate", (event, location: LocationDescriptor) => { logger.info(`[IPC]: ${event.type} ${JSON.stringify(location)}`, event); navigate(location); -}); +}) // Reload dashboard window -ipcRenderer.on("menu:reload", () => { +subscribeToBroadcast("menu:reload", () => { location.reload(); -}); +})