mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
fixes, micro-optimizations & switching to webview in both envs (prod/dev)
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
ac7b399c81
commit
1330ebded8
@ -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 { broadcastIpc } from "./ipc";
|
||||
import isEqual from "lodash/isEqual";
|
||||
|
||||
export interface BaseStoreParams<T = any> extends ConfOptions<T> {
|
||||
@ -124,35 +124,14 @@ export class BaseStore<T = any> extends Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// sending store's state to all WebContents (BrowserWindow, webview, etc.)
|
||||
protected async syncToWebViews(model: T) {
|
||||
const msg: IpcBroadcastParams = {
|
||||
broadcastIpc({
|
||||
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<number[]> {
|
||||
const subFrames: number[] = [];
|
||||
const { clusterStore } = await import("./cluster-store");
|
||||
clusterStore.clustersList.forEach(cluster => {
|
||||
if (cluster.frameId) {
|
||||
subFrames.push(cluster.frameId)
|
||||
}
|
||||
});
|
||||
return subFrames;
|
||||
}
|
||||
|
||||
@action
|
||||
protected fromStore(data: T) {
|
||||
this.data = data;
|
||||
|
||||
@ -5,12 +5,8 @@ import { tracker } from "./tracker";
|
||||
export const clusterIpc = {
|
||||
activate: createIpcChannel({
|
||||
channel: "cluster:activate",
|
||||
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
|
||||
return cluster.activate(true);
|
||||
}
|
||||
handle: async (clusterId: ClusterId) => {
|
||||
return clusterStore.getById(clusterId)?.activate({ init: true });
|
||||
},
|
||||
}),
|
||||
|
||||
|
||||
@ -75,10 +75,13 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
});
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.on("cluster:state", (event, model: ClusterState) => {
|
||||
this.applyWithoutSync(() => {
|
||||
logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host}`, model);
|
||||
this.getById(model.id)?.updateModel(model);
|
||||
})
|
||||
const cluster = this.getById(model.id);
|
||||
if (cluster) {
|
||||
this.applyWithoutSync(() => {
|
||||
logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host}`, model);
|
||||
cluster.updateModel(model);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -210,14 +213,14 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
export const clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
export function isClusterView(): boolean {
|
||||
return !!getHostedClusterId(); // note: process.isMainFrame cannot be used here since it's "true" for webview-tags
|
||||
return !!getHostedClusterId(); // note: process.isMainFrame cannot be used with "webview"-tags since they have own renderer process
|
||||
}
|
||||
|
||||
export function getClusterIdFromHost(hostname: string): ClusterId {
|
||||
return hostname.match(/^(.*?)\.localhost/)?.[1]
|
||||
}
|
||||
|
||||
export function getClusterFrameUrl(clusterId: ClusterId) {
|
||||
export function getClusterViewUrl(clusterId: ClusterId) {
|
||||
const { protocol, host } = location
|
||||
return `${protocol}//${clusterId}.${host}`
|
||||
}
|
||||
|
||||
@ -56,7 +56,6 @@ export interface IpcBroadcastParams<A extends any[] = any> {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -41,7 +41,6 @@ export interface ClusterState extends ClusterModel {
|
||||
|
||||
export class Cluster implements ClusterModel {
|
||||
public id: ClusterId;
|
||||
public frameId: number;
|
||||
public kubeCtl: Kubectl
|
||||
public contextHandler: ContextHandler;
|
||||
protected kubeconfigManager: KubeconfigManager;
|
||||
@ -113,7 +112,7 @@ export class Cluster implements ClusterModel {
|
||||
const refreshTimer = setInterval(() => this.online && this.refresh(), 30000); // every 30s
|
||||
|
||||
this.eventDisposers.push(
|
||||
reaction(this.getState, this.pushState),
|
||||
reaction(this.getState, this.pushState, { delay: 150 }),
|
||||
() => clearInterval(refreshTimer),
|
||||
);
|
||||
}
|
||||
@ -124,7 +123,8 @@ export class Cluster implements ClusterModel {
|
||||
this.eventDisposers.length = 0;
|
||||
}
|
||||
|
||||
async activate(init = false) {
|
||||
@action
|
||||
async activate({ init = false } = {}) {
|
||||
logger.info(`[CLUSTER]: activate`, this.getMeta());
|
||||
await this.whenInitialized;
|
||||
if (!this.eventDisposers.length) {
|
||||
@ -140,9 +140,9 @@ export class Cluster implements ClusterModel {
|
||||
this.kubeCtl = new Kubectl(this.version)
|
||||
this.kubeCtl.ensureKubectl() // download kubectl in background, so it's not blocking dashboard
|
||||
}
|
||||
return this.pushState();
|
||||
}
|
||||
|
||||
@action
|
||||
async reconnect() {
|
||||
logger.info(`[CLUSTER]: reconnect`, this.getMeta());
|
||||
this.contextHandler.stopServer();
|
||||
@ -402,7 +402,6 @@ export class Cluster implements ClusterModel {
|
||||
logger.silly(`[CLUSTER]: push-state`, state);
|
||||
broadcastIpc({
|
||||
channel: "cluster:state",
|
||||
frameId: this.frameId,
|
||||
args: [state],
|
||||
});
|
||||
return state;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { app, BrowserWindow, dialog, Menu, MenuItem, MenuItemConstructorOptions, shell, webContents } from "electron"
|
||||
import { app, BrowserWindow, dialog, Menu, MenuItem, MenuItemConstructorOptions, shell } from "electron"
|
||||
import { autorun } from "mobx";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import { appName, isMac, issuesTrackerUrl, isWindows, slackUrl } from "../common/vars";
|
||||
@ -29,12 +29,13 @@ export function buildMenu(windowManager: WindowManager) {
|
||||
return menuItems;
|
||||
}
|
||||
|
||||
function navigate(url: string) {
|
||||
function navigate(url: string, isClusterView = false) {
|
||||
logger.info(`[MENU]: navigating to ${url}`);
|
||||
windowManager.navigate({
|
||||
channel: "menu:navigate",
|
||||
url: url,
|
||||
})
|
||||
if (isClusterView) {
|
||||
windowManager.getClusterView(windowManager.activeClusterId)?.send("menu:navigate", url);
|
||||
} else {
|
||||
windowManager.getMainView()?.send("menu:navigate", url);
|
||||
}
|
||||
}
|
||||
|
||||
function showAbout(browserWindow: BrowserWindow) {
|
||||
@ -148,24 +149,31 @@ export function buildMenu(windowManager: WindowManager) {
|
||||
label: 'Back',
|
||||
accelerator: 'CmdOrCtrl+[',
|
||||
click() {
|
||||
webContents.getFocusedWebContents()?.goBack();
|
||||
windowManager.getActiveView()?.goBack();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Forward',
|
||||
accelerator: 'CmdOrCtrl+]',
|
||||
click() {
|
||||
webContents.getFocusedWebContents()?.goForward()
|
||||
windowManager.getActiveView()?.goForward()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Reload',
|
||||
accelerator: 'CmdOrCtrl+R',
|
||||
click() {
|
||||
windowManager.reload({ channel: "menu:reload" });
|
||||
windowManager.getActiveView()?.send("menu:reload");
|
||||
}
|
||||
},
|
||||
{ role: 'toggleDevTools' },
|
||||
{
|
||||
accelerator: "CmdOrCtrl+Shift+I",
|
||||
label: 'Open Dashboard Devtools',
|
||||
click() {
|
||||
windowManager.getClusterView(windowManager.activeClusterId)?.openDevTools();
|
||||
}
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ role: 'resetZoom' },
|
||||
{ role: 'zoomIn' },
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { ClusterId } from "../common/cluster-store";
|
||||
import { clusterStore } from "../common/cluster-store";
|
||||
import { BrowserWindow, dialog, ipcMain, shell, webContents } from "electron"
|
||||
import { BrowserWindow, dialog, ipcMain, shell, WebContents, webContents } from "electron"
|
||||
import windowStateKeeper from "electron-window-state"
|
||||
import { observable } from "mobx";
|
||||
import { initMenu } from "./menu";
|
||||
@ -29,7 +28,6 @@ export class WindowManager {
|
||||
backgroundColor: "#1e2124",
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInSubFrames: true,
|
||||
enableRemoteModule: true,
|
||||
webviewTag: true,
|
||||
},
|
||||
@ -52,21 +50,21 @@ export class WindowManager {
|
||||
initMenu(this);
|
||||
}
|
||||
|
||||
navigate({ url, channel, frameId }: { url: string, channel: string, frameId?: number }) {
|
||||
if (frameId) {
|
||||
this.mainView.webContents.sendToFrame(frameId, channel, url);
|
||||
} else {
|
||||
this.mainView.webContents.send(channel, url);
|
||||
}
|
||||
getMainView(): WebContents {
|
||||
return this.mainView.webContents;
|
||||
}
|
||||
|
||||
reload({ channel }: { channel: string }) {
|
||||
const frameId = clusterStore.getById(this.activeClusterId)?.frameId;
|
||||
if (frameId) {
|
||||
this.mainView.webContents.sendToFrame(frameId, channel);
|
||||
} else {
|
||||
webContents.getFocusedWebContents()?.reload();
|
||||
getClusterView(clusterId: ClusterId): WebContents {
|
||||
return webContents.getAllWebContents().find(webContent => {
|
||||
return webContent.getURL().includes(`${clusterId}.localhost:${this.proxyPort}`);
|
||||
})
|
||||
}
|
||||
|
||||
getActiveView(): WebContents {
|
||||
if (this.activeClusterId) {
|
||||
return this.getClusterView(this.activeClusterId);
|
||||
}
|
||||
return this.getMainView();
|
||||
}
|
||||
|
||||
async showMain() {
|
||||
|
||||
@ -34,17 +34,15 @@ 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 { MainLayout } from "./layout/main-layout";
|
||||
|
||||
@observer
|
||||
export class App extends React.Component {
|
||||
static async init() {
|
||||
const frameId = webFrame.routingId;
|
||||
const clusterId = getHostedClusterId();
|
||||
logger.info(`[APP]: Init dashboard, clusterId=${clusterId}, frameId=${frameId}`)
|
||||
logger.info(`[APP]: Init dashboard, clusterId=${clusterId}`)
|
||||
await Terminal.preloadFonts()
|
||||
await clusterIpc.activate.invokeFromRenderer(clusterId, frameId);
|
||||
await clusterIpc.activate.invokeFromRenderer(clusterId);
|
||||
await getHostedCluster().whenReady; // cluster.refresh() is done at this point
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ export function getMatchedCluster() {
|
||||
}
|
||||
|
||||
if (ipcRenderer) {
|
||||
if (isClusterView()) {
|
||||
if (!isClusterView()) {
|
||||
// Keep track of active cluster-id for handling IPC/menus/etc.
|
||||
reaction(() => getMatchedClusterId(), clusterId => {
|
||||
ipcRenderer.send("cluster-view:current-id", clusterId);
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
import { WebviewTag } from "electron";
|
||||
import { observable, when } from "mobx";
|
||||
import { ClusterId, clusterStore, getClusterFrameUrl } from "../../../common/cluster-store";
|
||||
import { ClusterId, clusterStore, getClusterViewUrl } from "../../../common/cluster-store";
|
||||
import logger from "../../../main/logger";
|
||||
import { isDebugging, isDevelopment, isProduction } from "../../../common/vars";
|
||||
import { getMatchedCluster } from "./cluster-view.route";
|
||||
|
||||
export type LensViewElem = HTMLIFrameElement | WebviewTag;
|
||||
|
||||
export interface LensView {
|
||||
isLoaded?: boolean
|
||||
clusterId: ClusterId;
|
||||
view: LensViewElem;
|
||||
view: WebviewTag;
|
||||
}
|
||||
|
||||
export const lensViews = observable.map<ClusterId, LensView>();
|
||||
@ -24,31 +21,22 @@ export async function initView(clusterId: ClusterId) {
|
||||
return;
|
||||
}
|
||||
logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`)
|
||||
let view: LensViewElem;
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
const parentElem = document.getElementById("lens-views");
|
||||
const frameUrl = getClusterFrameUrl(clusterId);
|
||||
const onLoad = () => {
|
||||
logger.info(`[LENS-VIEW]: loaded from ${view.src}`)
|
||||
lensViews.get(clusterId).isLoaded = true;
|
||||
};
|
||||
if (isDevelopment || isDebugging) {
|
||||
view = document.createElement("iframe");
|
||||
view.addEventListener("load", onLoad);
|
||||
view.name = cluster.contextName;
|
||||
} else if (isProduction) {
|
||||
view = document.createElement("webview");
|
||||
view.addEventListener("did-frame-finish-load", onLoad);
|
||||
view.setAttribute("nodeintegration", "true");
|
||||
view.setAttribute("enableremotemodule", "true");
|
||||
}
|
||||
view.setAttribute("src", frameUrl);
|
||||
const view = document.createElement("webview");
|
||||
view.addEventListener("did-frame-finish-load", onLoad);
|
||||
view.setAttribute("nodeintegration", "true");
|
||||
view.setAttribute("enableremotemodule", "true");
|
||||
view.setAttribute("src", getClusterViewUrl(clusterId));
|
||||
parentElem.appendChild(view);
|
||||
lensViews.set(clusterId, { clusterId, view });
|
||||
await autoCleanOnRemove(clusterId, view);
|
||||
}
|
||||
|
||||
export async function autoCleanOnRemove(clusterId: ClusterId, view: LensViewElem) {
|
||||
export async function autoCleanOnRemove(clusterId: ClusterId, view: WebviewTag) {
|
||||
await when(() => !clusterStore.getById(clusterId));
|
||||
logger.info(`[LENS-VIEW]: remove dashboard, clusterId=${clusterId}`)
|
||||
lensViews.delete(clusterId);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user