1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

attempt 1 to fix opening bug

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2021-07-27 11:20:08 -04:00
parent 4b660633dd
commit c426eb9899
9 changed files with 161 additions and 39 deletions

View File

@ -20,8 +20,8 @@
*/
import { catalogCategoryRegistry } from "../catalog/catalog-category-registry";
import { CatalogEntity, CatalogEntityActionContext, CatalogEntityAddMenuContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog";
import { clusterActivateHandler, clusterDeleteHandler, clusterDisconnectHandler } from "../cluster-ipc";
import { CatalogEntity, CatalogEntityAddMenuContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog";
import { clusterActivateHandler, clusterDeleteHandler, clusterDisconnectHandler, navigateToClusterHandler } from "../cluster-ipc";
import { ClusterStore } from "../cluster-store";
import { onNewWindowForClusterHandler, requestMain } from "../ipc";
import { CatalogCategory, CatalogCategorySpec } from "../catalog";
@ -90,8 +90,8 @@ export class KubernetesCluster extends CatalogEntity<KubernetesClusterMetadata,
}
}
async onRun(context: CatalogEntityActionContext) {
context.navigate(`/cluster/${this.metadata.uid}`);
async onRun() {
requestMain(navigateToClusterHandler, this.getId());
}
onDetailsOpen(): void {
@ -107,7 +107,7 @@ export class KubernetesCluster extends CatalogEntity<KubernetesClusterMetadata,
{
title: "Open",
icon: "open_in_full",
onClick: () => this.onRun(context),
onClick: () => this.onRun(),
},
{
title: "Open in new window",

View File

@ -19,11 +19,45 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { observable } from "mobx";
import { action, observable } from "mobx";
import type { ClusterId } from "./cluster-store";
import { iter, Singleton } from "./utils";
export interface ClusterFrameInfo {
frameId: number;
processId: number
processId: number;
windowId: number;
}
export const clusterFrameMap = observable.map<string, ClusterFrameInfo>();
export class ClusterFrames extends Singleton {
private mapping = observable.map<ClusterId, ClusterFrameInfo>();
public getAllFrameInfo(): ClusterFrameInfo[] {
return [...this.mapping.values()];
}
public set(clusterId: ClusterId, info: ClusterFrameInfo): void {
this.mapping.set(clusterId, info);
}
public getFrameInfoByClusterId(clusterId: ClusterId): ClusterFrameInfo | undefined {
return this.mapping.get(clusterId);
}
public getFrameInfoByFrameId(frameId: number): ClusterFrameInfo | undefined {
return iter.find(this.mapping.values(), frameInfo => frameInfo.frameId === frameId);
}
public clearInfoForCluster(clusterId: ClusterId): void {
this.mapping.delete(clusterId);
}
@action
public clearInfoForWindow(windowId: number): void {
for (const [clusterId, frameInfo] of this.mapping) {
if (frameInfo.windowId === windowId) {
this.mapping.delete(clusterId);
}
}
}
}

View File

@ -19,6 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
export const navigateToClusterHandler = "navigate:to-cluster";
export const clusterActivateHandler = "cluster:activate";
export const clusterSetFrameIdHandler = "cluster:set-frame-id";
export const clusterVisibilityHandler = "cluster:visibility";

View File

@ -26,7 +26,7 @@
import { ipcMain, ipcRenderer, remote, webContents } from "electron";
import { toJS } from "../utils/toJS";
import logger from "../../main/logger";
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
import { ClusterFrames } from "../cluster-frames";
import type { Disposer } from "../utils";
const subFramesChannel = "ipc:get-sub-frames";
@ -41,10 +41,6 @@ export function ipcMainHandle(channel: string, listener: (event: Electron.IpcMai
});
}
function getSubFrames(): ClusterFrameInfo[] {
return Array.from(clusterFrameMap.values());
}
export function broadcastMessage(channel: string, ...args: any[]) {
const views = (webContents || remote?.webContents)?.getAllWebContents();
@ -56,7 +52,7 @@ export function broadcastMessage(channel: string, ...args: any[]) {
const subFramesP = ipcRenderer
? requestMain(subFramesChannel)
: Promise.resolve(getSubFrames());
: Promise.resolve(ClusterFrames.getInstance().getAllFrameInfo());
subFramesP
.then(subFrames => {
@ -88,7 +84,7 @@ export function ipcRendererOn(channel: string, listener: (event: Electron.IpcRen
}
export function bindBroadcastHandlers() {
ipcMainHandle(subFramesChannel, () => getSubFrames());
ipcMainHandle(subFramesChannel, () => ClusterFrames.getInstance().getAllFrameInfo());
}
/**

View File

@ -55,3 +55,9 @@ export * from "./type-narrowing";
import * as iter from "./iter";
export { iter };
export type NeverPartial<T> = {
[P in keyof T]?: never;
};
export type OnlyOneOf<T> = { [K in keyof T]-?: Required<Pick<T, K>> & NeverPartial<Pick<T, Exclude<keyof T, K>>>; }[keyof T];

View File

@ -62,6 +62,7 @@ import { FilesystemProvisionerStore } from "./extension-filesystem";
import { SentryInit } from "../common/sentry";
import { initMenu } from "./menu";
import { initTray } from "./tray";
import { ClusterFrames } from "../common/cluster-frames";
// This has to be called before start using winton-based logger
// For example, before any logger.log
@ -121,6 +122,8 @@ app.on("second-instance", (event, argv) => {
});
app.on("ready", async () => {
ClusterFrames.createInstance();
logger.info(`🚀 Starting ${productName} from "${workingDir}"`);
logger.info("🐚 Syncing shell environment");
await shellSync();

View File

@ -21,8 +21,8 @@
import type { IpcMainInvokeEvent } from "electron";
import { KubernetesCluster } from "../../common/catalog-entities";
import { clusterFrameMap } from "../../common/cluster-frames";
import { clusterActivateHandler, clusterSetFrameIdHandler, clusterVisibilityHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterDeleteHandler } from "../../common/cluster-ipc";
import { ClusterFrames } from "../../common/cluster-frames";
import { clusterActivateHandler, clusterSetFrameIdHandler, clusterVisibilityHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterDeleteHandler, navigateToClusterHandler } from "../../common/cluster-ipc";
import { ClusterId, ClusterStore } from "../../common/cluster-store";
import { appEventBus } from "../../common/event-bus";
import { ipcMainHandle, onNewWindowForClusterHandler } from "../../common/ipc";
@ -46,11 +46,25 @@ export function initIpcMainHandlers() {
const cluster = ClusterStore.getInstance().getById(clusterId);
if (cluster) {
clusterFrameMap.set(cluster.id, { frameId: event.frameId, processId: event.processId });
ClusterFrames.getInstance().set(cluster.id, {
frameId: event.frameId,
processId: event.processId,
windowId: event.sender.getProcessId(),
});
cluster.pushState();
}
});
ipcMainHandle(navigateToClusterHandler, async (event, clusterId: ClusterId) => {
const cluster = ClusterStore.getInstance().getById(clusterId);
if (!cluster) {
return void logger.warn("[NAVIGATE-TO-CLUSTER]: unknown cluster", { clusterId });
}
await WindowManager.getInstance().navigate(`/cluster/${clusterId}`, { clusterId }, { windowId: event.sender.getProcessId() });
});
ipcMainHandle(clusterVisibilityHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId, visible: boolean) => {
const entity = catalogEntityRegistry.getById(clusterId);
@ -75,7 +89,7 @@ export function initIpcMainHandlers() {
if (cluster) {
cluster.disconnect();
clusterFrameMap.delete(cluster.id);
ClusterFrames.getInstance().clearInfoForCluster(cluster.id);
}
});
@ -89,7 +103,7 @@ export function initIpcMainHandlers() {
ClusterManager.getInstance().deleting.add(clusterId);
cluster.disconnect();
clusterFrameMap.delete(cluster.id);
ClusterFrames.getInstance().clearInfoForCluster(cluster.id);
const kubectlPath = bundledKubectlPath();
const args = ["config", "delete-context", cluster.contextName, "--kubeconfig", cluster.kubeConfigPath];
@ -148,10 +162,8 @@ export function initIpcMainHandlers() {
return void logger.info("Cannot open clutser in new window, unknown cluster Id", { clusterId });
}
const wm = WindowManager.getInstance();
try {
console.log("trying to opening new window", event);
const wm = WindowManager.getInstance();
const window = await wm.openNewWindow();
window.webContents.send(IpcRendererNavigationEvents.NAVIGATE_IN_APP, `/cluster/${clusterId}`);

View File

@ -118,6 +118,13 @@ export function buildMenu(windowManager: WindowManager) {
navigate(addClusterURL());
}
},
{
label: "New Window",
accelerator: "CmdOrCtrl+N",
click() {
windowManager.openNewWindow();
}
},
...ignoreOnMac([
{ type: "separator" },
{

View File

@ -22,8 +22,8 @@
import { app, BrowserWindow, dialog, ipcMain, shell, webContents } from "electron";
import windowStateKeeper from "electron-window-state";
import { appEventBus } from "../common/event-bus";
import { delay, iter, Singleton } from "../common/utils";
import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames";
import { delay, iter, Singleton, toJS } from "../common/utils";
import { ClusterFrameInfo, ClusterFrames } from "../common/cluster-frames";
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
import logger from "./logger";
import { productName } from "../common/vars";
@ -40,6 +40,12 @@ export interface SendToViewArgs {
data?: any[];
}
export interface NavigateFrameInfoSpecifier {
windowId?: number;
clusterId?: number;
frameId?: number;
}
export class WindowManager extends Singleton {
protected splashWindow: BrowserWindow;
protected windows = new Map<number, [BrowserWindow, windowStateKeeper.State]>();
@ -93,7 +99,9 @@ export class WindowManager extends Singleton {
// clean up
windowState.unmanage();
this.windows.delete(windowId);
ClusterFrames.getInstance().clearInfoForWindow(windowId);
this.splashWindow?.close();
this.splashWindow = null;
})
.webContents
@ -117,11 +125,7 @@ export class WindowManager extends Singleton {
const windowId = browserWindow.webContents.getProcessId();
try {
if (!this.hasVisibleWindow()) {
await this.showSplash();
}
console.log(this.windows);
await this.showSplash();
logger.info(`[WINDOW-MANAGER]: Loading window from url: ${this.mainUrl} ...`, { windowId });
@ -139,9 +143,9 @@ export class WindowManager extends Singleton {
await browserWindow.loadURL(this.mainUrl);
await viewHasLoaded;
browserWindow.show();
this.splashWindow?.close();
this.splashWindow = undefined;
browserWindow.show();
setTimeout(() => {
appEventBus.emit({ name: "app", action: "start" });
@ -154,7 +158,7 @@ export class WindowManager extends Singleton {
return browserWindow;
}
async ensureWindow(): Promise<BrowserWindow> {
async ensureWindow(windowId?: number): Promise<BrowserWindow> {
// This needs to be ready to hear the IPC message before the window is loaded
let viewHasLoaded = Promise.resolve();
let browserWindow: BrowserWindow;
@ -174,18 +178,20 @@ export class WindowManager extends Singleton {
logger.error("Loading window failed", { error });
dialog.showErrorBox("ERROR!", error.toString());
}
} else {
browserWindow = iter.first(this.windows.values())[0];
} else if (typeof windowId === "number") {
browserWindow = (this.windows.get(windowId) ?? iter.first(this.windows.values()))[0];
}
browserWindow ??= iter.first(this.windows.values())[0];
try {
await viewHasLoaded;
await delay(50); // wait just a bit longer to let the first round of rendering happen
logger.info("[WINDOW-MANAGER]: Window has reported that it has loaded", { windowId: browserWindow.webContents.getProcessId() });
browserWindow.show();
this.splashWindow?.close();
this.splashWindow = undefined;
browserWindow.show();
setTimeout(() => {
appEventBus.emit({ name: "app", action: "start" });
}, 1000);
@ -217,7 +223,7 @@ export class WindowManager extends Singleton {
async navigateExtension(extId: string, pageId?: string, params?: Record<string, any>, frameId?: number) {
const browserWindow = await this.ensureWindow();
const frameInfo = iter.find(clusterFrameMap.values(), frameInfo => frameInfo.frameId === frameId);
const frameInfo = ClusterFrames.getInstance().getFrameInfoByFrameId(frameId);
this.sendToView(browserWindow, {
channel: "extension:navigate",
@ -226,9 +232,66 @@ export class WindowManager extends Singleton {
});
}
async navigate(url: string, frameId?: number) {
const browserWindow = await this.ensureWindow();
const frameInfo = iter.find(clusterFrameMap.values(), frameInfo => frameInfo.frameId === frameId);
/**
* Get the naviate target
* @param specifics The fallback options for specifying a target
*/
private getNavigateTarget(specifics: NavigateFrameInfoSpecifier[]): [ClusterFrameInfo | undefined, number | undefined] {
function helper(): ClusterFrameInfo | undefined | number {
const clusterFrames = ClusterFrames.getInstance();
for (const fallback of specifics) {
if (typeof fallback.clusterId === "string") {
const res = clusterFrames.getFrameInfoByClusterId(fallback.clusterId);
if (res == null) { // intentional
return res;
} else {
continue;
}
}
if (typeof fallback.frameId === "number") {
const res = clusterFrames.getFrameInfoByFrameId(fallback.frameId);
if (res == null) { // intentional
return res;
} else {
continue;
}
}
if (typeof fallback.windowId === "number") {
return fallback.windowId;
}
}
return undefined;
}
const target = helper();
if (target == null) { // intentional
return [undefined, undefined];
}
if (typeof target === "number") {
return [undefined, target];
}
return [target, target.windowId];
}
/**
* Navigate to `url` on a specific window or frame
* @param url The url to navigate to
* @param specifics Data for specifying a specific window or iframe
*/
async navigate(url: string, ...specifics: NavigateFrameInfoSpecifier[]): Promise<void> {
const [frameInfo, windowId] = this.getNavigateTarget(specifics);
console.log("[WINDOW-MANAGER]: navigate to", url, "with", specifics, toJS(frameInfo), { windowId });
const browserWindow = await this.ensureWindow(windowId);
const channel = frameInfo
? IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER
: IpcRendererNavigationEvents.NAVIGATE_IN_APP;