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:
parent
4b660633dd
commit
c426eb9899
@ -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",
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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}`);
|
||||
|
||||
@ -118,6 +118,13 @@ export function buildMenu(windowManager: WindowManager) {
|
||||
navigate(addClusterURL());
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "New Window",
|
||||
accelerator: "CmdOrCtrl+N",
|
||||
click() {
|
||||
windowManager.openNewWindow();
|
||||
}
|
||||
},
|
||||
...ignoreOnMac([
|
||||
{ type: "separator" },
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user