Upgrade to electron 12 and Node 14 (#3572)

Upgrade to electron 12 and Node 14

- Compute cluster ID for tests
- Switch to temporary appData path while testing

Signed-off-by: Sebastian Malton <sebastian@malton.name>

Co-authored-by: Sebastian Malton <smalton@mirantis.com>
Co-authored-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
authored and GitHub committed 2021-08-26 12:13:31 -04:00
1 parent e2812a5bcd
commit 59933507e4
50 files changed
+1000 -1099

No files matched your search

+2 -3
View File
@@ -37,7 +37,6 @@ import { Console } from "console";
import { SemVer } from "semver";
import electron from "electron";
import { stdout, stderr } from "process";
import { beforeEachWrapped } from "../../../integration/helpers/utils";
import { ThemeStore } from "../../renderer/theme.store";
import type { ClusterStoreModel } from "../cluster-store";
@@ -45,7 +44,7 @@ console = new Console(stdout, stderr);
describe("user store tests", () => {
describe("for an empty config", () => {
beforeEachWrapped(() => {
beforeEach(() => {
mockFs({ tmp: { "config.json": "{}", "kube_config": "{}" } });
(UserStore.createInstance() as any).refreshNewContexts = jest.fn(() => Promise.resolve());
@@ -94,7 +93,7 @@ describe("user store tests", () => {
});
describe("migrations", () => {
beforeEachWrapped(() => {
beforeEach(() => {
mockFs({
"tmp": {
"config.json": JSON.stringify({
+3 -2
View File
@@ -22,7 +22,7 @@
import path from "path";
import Config from "conf";
import type { Options as ConfOptions } from "conf/dist/source/types";
import { app, ipcMain, ipcRenderer, remote } from "electron";
import { ipcMain, ipcRenderer } from "electron";
import { IReactionOptions, makeObservable, reaction, runInAction } from "mobx";
import { getAppVersion, Singleton, toJS, Disposer } from "./utils";
import logger from "../main/logger";
@@ -30,6 +30,7 @@ import { broadcastMessage, ipcMainOn, ipcRendererOn } from "./ipc";
import isEqual from "lodash/isEqual";
import { isTestEnv } from "./vars";
import { kebabCase } from "lodash";
import { getPath } from "./utils/getPath";
export interface BaseStoreParams<T> extends ConfOptions<T> {
syncOptions?: IReactionOptions;
@@ -88,7 +89,7 @@ export abstract class BaseStore<T> extends Singleton {
}
protected cwd() {
return (app || remote.app).getPath("userData");
return getPath("userData");
}
protected async saveToFile(model: T) {
+2
View File
@@ -19,6 +19,8 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
export const dialogShowOpenDialogHandler = "dialog:show-open-dialog";
export * from "./ipc";
export * from "./invalid-kubeconfig";
export * from "./update-available.ipc";
+3 -1
View File
@@ -23,12 +23,14 @@
// https://www.electronjs.org/docs/api/ipc-main
// https://www.electronjs.org/docs/api/ipc-renderer
import { ipcMain, ipcRenderer, remote, webContents } from "electron";
import { ipcMain, ipcRenderer, webContents } from "electron";
import { toJS } from "../utils/toJS";
import logger from "../../main/logger";
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
import type { Disposer } from "../utils";
const remote = ipcMain ? null : require("@electron/remote");
const subFramesChannel = "ipc:get-sub-frames";
export async function requestMain(channel: string, ...args: any[]) {
+3 -2
View File
@@ -19,13 +19,14 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { app, ipcMain, remote } from "electron";
import { ipcMain } from "electron";
import winston, { format } from "winston";
import type Transport from "winston-transport";
import { consoleFormat } from "winston-console-format";
import { isDebugging, isTestEnv } from "./vars";
import BrowserConsole from "winston-transport-browserconsole";
import { SentryTransport } from "./logger-transports";
import { getPath } from "./utils/getPath";
const logLevel = process.env.LOG_LEVEL
? process.env.LOG_LEVEL
@@ -71,7 +72,7 @@ if (!isTestEnv) {
handleExceptions: false,
level: logLevel,
filename: "lens.log",
dirname: (app ?? remote?.app)?.getPath("logs"),
dirname: getPath("logs"),
maxsize: 16 * 1024,
maxFiles: 16,
tailable: true,
+7 -6
View File
@@ -19,7 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { app, remote } from "electron";
import { app } from "electron";
import semver from "semver";
import { action, computed, observable, reaction, makeObservable } from "mobx";
import { BaseStore } from "../base-store";
@@ -32,7 +32,8 @@ import { fileNameMigration } from "../../migrations/user-store";
import { ObservableToggleSet, toJS } from "../../renderer/utils";
import { DESCRIPTORS, KubeconfigSyncValue, UserPreferencesModel, EditorConfiguration } from "./preferences-helpers";
import logger from "../../main/logger";
import type {monaco} from "react-monaco-editor";
import type { monaco } from "react-monaco-editor";
import { getPath } from "../utils/getPath";
export interface UserStoreModel {
lastSeenAppVersion: string;
@@ -69,7 +70,7 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
@observable shell?: string;
@observable downloadBinariesPath?: string;
@observable kubectlBinariesPath?: string;
/**
* Download kubectl binaries matching cluster version
*/
@@ -86,7 +87,7 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
* Monaco editor configs
*/
@observable editorConfiguration:EditorConfiguration = {tabSize: null, miniMap: null, lineNumbers: null};
/**
* The set of file/folder paths to be synced
*/
@@ -115,7 +116,7 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
});
}, {
fireImmediately: true,
});
});
}
// Returns monaco editor options for selected editor type (the place, where a particular instance of the editor is mounted)
@@ -244,5 +245,5 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
* @returns string
*/
export function getDefaultKubectlDownloadPath(): string {
return path.join((app || remote.app).getPath("userData"), "binaries");
return path.join(getPath("userData"), "binaries");
}
+37
View File
@@ -0,0 +1,37 @@
/**
* 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.
*/
import { app, ipcMain } from "electron";
const remote = ipcMain ? null : require("@electron/remote");
/**
* calls getPath either on app or on the remote's app
*
* @deprecated Use a different method for accessing the getPath function
*/
export function getPath(name: Parameters<typeof app["getPath"]>[0]): string {
if (app) {
return app.getPath(name);
}
return remote.app.getPath(name);
}
+1
View File
@@ -40,6 +40,7 @@ export * from "./downloadFile";
export * from "./formatDuration";
export * from "./escapeRegExp";
export * from "./extended-map";
export * from "./getPath";
export * from "./getRandId";
export * from "./hash-set";
export * from "./local-kubeconfig";
+2 -2
View File
@@ -19,13 +19,13 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { app, remote } from "electron";
import path from "path";
import * as uuid from "uuid";
import type { ClusterId } from "../cluster-types";
import { getPath } from "./getPath";
export function storedKubeConfigFolder(): string {
return path.resolve((app || remote.app).getPath("userData"), "kubeconfigs");
return path.resolve(getPath("userData"), "kubeconfigs");
}
export function getCustomKubeConfigPath(clusterId: ClusterId = uuid.v4()): string {
+3
View File
@@ -35,6 +35,9 @@ export const isTestEnv = !!process.env.JEST_WORKER_ID;
export const isDevelopment = !isTestEnv && !isProduction;
export const isPublishConfigured = Object.keys(packageInfo.build).includes("publish");
export const integrationTestingArg = "--integration-testing";
export const isIntegrationTesting = process.argv.includes(integrationTestingArg);
export const productName = packageInfo.productName;
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`;
export const publicPath = "/build/" as string;
+3 -3
View File
@@ -19,14 +19,14 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { app, ipcRenderer, remote } from "electron";
import { ipcRenderer } from "electron";
import { EventEmitter } from "events";
import { isEqual } from "lodash";
import { action, computed, makeObservable, observable, observe, reaction, when } from "mobx";
import path from "path";
import { ClusterStore } from "../common/cluster-store";
import { broadcastMessage, ipcMainOn, ipcRendererOn, requestMain, ipcMainHandle } from "../common/ipc";
import { Disposer, getHostedClusterId, Singleton, toJS } from "../common/utils";
import { Disposer, getHostedClusterId, Singleton, toJS, getPath } from "../common/utils";
import logger from "../main/logger";
import type { InstalledExtension } from "./extension-discovery";
import { ExtensionsStore } from "./extensions-store";
@@ -36,7 +36,7 @@ import type { LensRendererExtension } from "./lens-renderer-extension";
import * as registries from "./registries";
export function extensionPackagesRoot() {
return path.join((app || remote.app).getPath("userData"));
return path.join(getPath("userData"));
}
const logModule = "[EXTENSIONS-LOADER]";
+2 -1
View File
@@ -33,7 +33,8 @@ import { VersionDetector } from "./cluster-detectors/version-detector";
import { DetectorRegistry } from "./cluster-detectors/detector-registry";
import plimit from "p-limit";
import { toJS } from "../common/utils";
import { initialNodeShellImage, ClusterState, ClusterMetadataKey, ClusterRefreshOptions, ClusterStatus, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel } from "../common/cluster-types";
import type { ClusterState, ClusterRefreshOptions, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel } from "../common/cluster-types";
import { ClusterMetadataKey, initialNodeShellImage, ClusterStatus } from "../common/cluster-types";
/**
* Cluster
-5
View File
@@ -26,14 +26,9 @@ import { ClusterManager } from "./cluster-manager";
import logger from "./logger";
export function exitApp() {
console.log("before windowManager");
const windowManager = WindowManager.getInstance(false);
console.log("before clusterManager");
const clusterManager = ClusterManager.getInstance(false);
console.log("after clusterManager");
appEventBus.emit({ name: "service", action: "close" });
windowManager?.hide();
clusterManager?.stop();
+2 -2
View File
@@ -21,13 +21,13 @@
import { randomBytes } from "crypto";
import { SHA256 } from "crypto-js";
import { app, remote } from "electron";
import fse from "fs-extra";
import { action, makeObservable, observable } from "mobx";
import path from "path";
import { BaseStore } from "../common/base-store";
import type { LensExtensionId } from "../extensions/lens-extension";
import { toJS } from "../common/utils";
import { getPath } from "../common/utils/getPath";
interface FSProvisionModel {
extensions: Record<string, string>; // extension names to paths
@@ -55,7 +55,7 @@ export class FilesystemProvisionerStore extends BaseStore<FSProvisionModel> {
if (!this.registeredExtensions.has(extensionName)) {
const salt = randomBytes(32).toString("hex");
const hashedName = SHA256(`${extensionName}/${salt}`).toString();
const dirPath = path.resolve((app || remote.app).getPath("userData"), "extension_data", hashedName);
const dirPath = path.resolve(getPath("userData"), "extension_data", hashedName);
this.registeredExtensions.set(extensionName, dirPath);
}
+17 -14
View File
@@ -22,12 +22,12 @@
// Main process
import "../common/system-ca";
import { initialize as initializeRemote } from "@electron/remote/main";
import * as Mobx from "mobx";
import * as LensExtensionsCommonApi from "../extensions/common-api";
import * as LensExtensionsMainApi from "../extensions/main-api";
import { app, autoUpdater, dialog, powerMonitor } from "electron";
import { appName, isMac, productName } from "../common/vars";
import path from "path";
import { appName, isIntegrationTesting, isMac, productName } from "../common/vars";
import { LensProxy } from "./lens-proxy";
import { WindowManager } from "./window-manager";
import { ClusterManager } from "./cluster-manager";
@@ -63,13 +63,13 @@ import { ensureDir } from "fs-extra";
import { Router } from "./router";
import { initMenu } from "./menu";
import { initTray } from "./tray";
import * as path from "path";
import { kubeApiRequest, shellApiRequest } from "./proxy-functions";
const onCloseCleanup = disposer();
const onQuitCleanup = disposer();
SentryInit();
const workingDir = path.join(app.getPath("appData"), appName);
const cleanup = disposer();
app.setName(appName);
logger.info(`📟 Setting ${productName} as protocol client for lens://`);
@@ -80,14 +80,16 @@ if (app.setAsDefaultProtocolClient("lens")) {
logger.info("📟 Protocol client register failed ❗");
}
if (!process.env.CICD) {
app.setPath("userData", workingDir);
if (process.env.CICD) {
app.setPath("appData", process.env.CICD);
app.setPath("userData", path.join(process.env.CICD, appName));
}
if (process.env.LENS_DISABLE_GPU) {
app.disableHardwareAcceleration();
}
initializeRemote();
configurePackages();
mangleProxyEnv();
initializers.initIpcMainHandlers();
@@ -121,7 +123,7 @@ app.on("second-instance", (event, argv) => {
});
app.on("ready", async () => {
logger.info(`🚀 Starting ${productName} from "${workingDir}"`);
logger.info(`🚀 Starting ${productName} from "${app.getPath("exe")}"`);
logger.info("🐚 Syncing shell environment");
await shellSync();
@@ -209,7 +211,7 @@ app.on("ready", async () => {
logger.info("🖥️ Starting WindowManager");
const windowManager = WindowManager.createInstance();
cleanup.push(
onQuitCleanup.push(
initMenu(windowManager),
initTray(windowManager),
);
@@ -221,7 +223,7 @@ app.on("ready", async () => {
}
ipcMainOn(IpcRendererNavigationEvents.LOADED, async () => {
cleanup.push(pushCatalogToRenderer(catalogEntityRegistry));
onCloseCleanup.push(pushCatalogToRenderer(catalogEntityRegistry));
await ensureDir(storedKubeConfigFolder());
KubeconfigSyncManager.getInstance().startSync();
startUpdateChecking();
@@ -269,7 +271,7 @@ app.on("activate", (event, hasVisibleWindows) => {
/**
* This variable should is used so that `autoUpdater.installAndQuit()` works
*/
let blockQuit = true;
let blockQuit = !isIntegrationTesting;
autoUpdater.on("before-quit-for-update", () => blockQuit = false);
@@ -282,7 +284,7 @@ app.on("will-quit", (event) => {
appEventBus.emit({ name: "app", action: "close" });
ClusterManager.getInstance(false)?.stop(); // close cluster connections
KubeconfigSyncManager.getInstance(false)?.stopSync();
cleanup();
onCloseCleanup();
if (lprm) {
// This is set to false here so that LPRM can wait to send future lens://
@@ -298,7 +300,8 @@ app.on("will-quit", (event) => {
return; // skip exit to make tray work, to quit go to app's global menu or tray's menu
}
LensProtocolRouterMain.getInstance(false)?.cleanup();
lprm?.cleanup();
onQuitCleanup();
});
app.on("open-url", (event, rawUrl) => {
+9 -2
View File
@@ -19,20 +19,21 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import type { IpcMainInvokeEvent } from "electron";
import { BrowserWindow, dialog, 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 { ClusterStore } from "../../common/cluster-store";
import type { ClusterId } from "../../common/cluster-types";
import { appEventBus } from "../../common/event-bus";
import { ipcMainHandle } from "../../common/ipc";
import { dialogShowOpenDialogHandler, ipcMainHandle } from "../../common/ipc";
import { catalogEntityRegistry } from "../catalog";
import { ClusterManager } from "../cluster-manager";
import { bundledKubectlPath } from "../kubectl";
import logger from "../logger";
import { promiseExecFile } from "../promise-exec";
import { ResourceApplier } from "../resource-applier";
import { WindowManager } from "../window-manager";
export function initIpcMainHandlers() {
ipcMainHandle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => {
@@ -138,4 +139,10 @@ export function initIpcMainHandlers() {
throw `${clusterId} is not a valid cluster id`;
}
});
ipcMainHandle(dialogShowOpenDialogHandler, async (event, dialogOpts: Electron.OpenDialogOptions) => {
await WindowManager.getInstance().ensureMainWindow();
return dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), dialogOpts);
});
}
+2 -2
View File
@@ -19,7 +19,6 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { app, remote } from "electron";
import path from "path";
import fs from "fs";
import { promiseExec } from "./promise-exec";
@@ -32,6 +31,7 @@ import { customRequest } from "../common/request";
import { getBundledKubectlVersion } from "../common/utils/app-version";
import { isDevelopment, isWindows, isTestEnv } from "../common/vars";
import { SemVer } from "semver";
import { getPath } from "../common/utils/getPath";
const bundledVersion = getBundledKubectlVersion();
const kubectlMap: Map<string, string> = new Map([
@@ -84,7 +84,7 @@ export class Kubectl {
protected dirname: string;
static get kubectlDir() {
return path.join((app || remote.app).getPath("userData"), "binaries", "kubectl");
return path.join(getPath("userData"), "binaries", "kubectl");
}
public static readonly bundledKubectlVersion: string = bundledVersion;
+3 -3
View File
@@ -83,7 +83,7 @@ export function buildMenu(windowManager: WindowManager) {
accelerator: "CmdOrCtrl+,",
click() {
navigate(preferencesURL());
}
},
},
{
label: "Extensions",
@@ -106,7 +106,7 @@ export function buildMenu(windowManager: WindowManager) {
exitApp();
}
}
]
],
};
const fileMenu: MenuItemConstructorOptions = {
label: "File",
@@ -150,7 +150,7 @@ export function buildMenu(windowManager: WindowManager) {
}
}
])
]
],
};
const editMenu: MenuItemConstructorOptions = {
label: "Edit",
+6 -2
View File
@@ -89,7 +89,8 @@ export class WindowManager extends Singleton {
nodeIntegration: true,
nodeIntegrationInSubFrames: true,
enableRemoteModule: true,
webviewTag: true
webviewTag: true,
contextIsolation: false,
},
});
this.windowState.manage(this.mainWindow);
@@ -257,7 +258,10 @@ export class WindowManager extends Singleton {
resizable: false,
show: false,
webPreferences: {
nodeIntegration: true
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false,
nodeIntegrationInSubFrames: true,
}
});
await this.splashWindow.loadURL("static://splash.html");
@@ -20,11 +20,11 @@
*/
import fse from "fs-extra";
import { app, remote } from "electron";
import path from "path";
import { getPath } from "../../common/utils/getPath";
export function fileNameMigration() {
const userDataPath = (app || remote.app).getPath("userData");
const userDataPath = getPath("userData");
const configJsonPath = path.join(userDataPath, "config.json");
const lensUserStoreJsonPath = path.join(userDataPath, "lens-user-store.json");
@@ -96,7 +96,7 @@ export class HelmCharts extends Component<Props> {
searchProps: {
...searchProps,
placeholder: "Search Helm Charts...",
},
}
})}
renderTableHeader={[
{ className: "icon", showWithColumn: columnId.name },
@@ -21,7 +21,7 @@
import "./extensions.scss";
import { remote, shell } from "electron";
import { shell } from "electron";
import fse from "fs-extra";
import _ from "lodash";
import { makeObservable, observable, reaction, when } from "mobx";
@@ -46,6 +46,8 @@ import { InstalledExtensions } from "./installed-extensions";
import { Notice } from "./notice";
import { SettingLayout } from "../layout/setting-layout";
import { docsUrl } from "../../../common/vars";
import { dialog } from "../../remote-helpers";
import { getPath } from "../../../common/utils/getPath";
function getMessageFromError(error: any): string {
if (!error || typeof error !== "object") {
@@ -466,9 +468,8 @@ async function installFromInput(input: string) {
const supportedFormats = ["tar", "tgz"];
async function installFromSelectFileDialog() {
const { dialog, BrowserWindow, app } = remote;
const { canceled, filePaths } = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), {
defaultPath: app.getPath("downloads"),
const { canceled, filePaths } = await dialog.showOpenDialog({
defaultPath: getPath("downloads"),
properties: ["openFile", "multiSelections"],
message: `Select extensions to install (formats: ${supportedFormats.join(", ")}), `,
buttonLabel: "Use configuration",
@@ -22,7 +22,7 @@
import "./add-helm-repo-dialog.scss";
import React from "react";
import { remote, FileFilter } from "electron";
import type { FileFilter } from "electron";
import { observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
@@ -35,6 +35,7 @@ import { SubTitle } from "../layout/sub-title";
import { Icon } from "../icon";
import { Notifications } from "../notifications";
import { HelmRepo, HelmRepoManager } from "../../../main/helm/helm-repo-manager";
import { dialog } from "../../remote-helpers";
interface Props extends Partial<DialogProps> {
onAddRepo: Function
@@ -88,8 +89,7 @@ export class AddHelmRepoDialog extends React.Component<Props> {
}
async selectFileDialog(type: FileType, fileFilter: FileFilter) {
const { dialog, BrowserWindow } = remote;
const { canceled, filePaths } = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), {
const { canceled, filePaths } = await dialog.showOpenDialog({
defaultPath: this.getFilePath(type),
properties: ["openFile", "showHiddenFiles"],
message: `Select file`,
@@ -50,6 +50,7 @@ export async function initView(clusterId: ClusterId) {
const parentElem = document.getElementById("lens-views");
const iframe = document.createElement("iframe");
iframe.id = `cluster-frame-${cluster.id}`;
iframe.name = cluster.contextName;
iframe.setAttribute("src", getClusterFrameUrl(clusterId));
iframe.addEventListener("load", () => {
@@ -0,0 +1,22 @@
/**
* 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.
*/
export * from "./path-picker";
@@ -27,7 +27,7 @@ import { UserStore } from "../../common/user-store";
import { getAllEntries } from "../components/+preferences/kubeconfig-syncs";
import { runInAction } from "mobx";
import { isWindows } from "../../common/vars";
import { PathPicker } from "../components/path-picker/path-picker";
import { PathPicker } from "../components/path-picker";
import { Notifications } from "../components/notifications";
import { Link } from "react-router-dom";
+1 -1
View File
@@ -117,7 +117,7 @@ function HotbarTooManyItemsHandler(): void {
Notifications.error(`Cannot have more than ${defaultHotbarCells} items pinned to a hotbar`);
}
export function registerIpcHandlers() {
export function registerIpcListeners() {
onCorrect({
source: ipcRenderer,
channel: UpdateAvailableChannel,
+88
View File
@@ -0,0 +1,88 @@
/**
* 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.
*/
import { ipcRenderer } from "electron";
import { navigate } from "../navigation";
/**
* The definition of a keyboard shortcut
*/
interface Shortcut {
code?: string;
key?: string;
metaKey?: boolean;
altKey?: boolean;
shiftKey?: boolean;
ctrlKey?: boolean;
action: () => void;
}
const shortcuts: Shortcut[] = [
{
key: "p",
metaKey: true,
shiftKey: true,
action: () => ipcRenderer.emit("command-palette:open"),
},
{
code: "Comma",
metaKey: true,
action: () => navigate("/preferences"),
},
];
function shortcutMatches(shortcut: Shortcut, event: KeyboardEvent): boolean {
if (typeof shortcut.metaKey === "boolean" && shortcut.metaKey !== event.metaKey) {
return false;
}
if (typeof shortcut.altKey === "boolean" && shortcut.altKey !== event.altKey) {
return false;
}
if (typeof shortcut.shiftKey === "boolean" && shortcut.shiftKey !== event.shiftKey) {
return false;
}
if (typeof shortcut.ctrlKey === "boolean" && shortcut.ctrlKey !== event.ctrlKey) {
return false;
}
if (typeof shortcut.code === "string" && shortcut.code !== event.code) {
return false;
}
if (typeof shortcut.key === "string" && shortcut.key !== event.key) {
return false;
}
return true;
}
export function registerKeyboardShortcuts() {
window.addEventListener("keydown", event => {
for (const shortcut of shortcuts) {
if (shortcutMatches(shortcut, event)) {
shortcut.action();
}
}
});
}
+4 -2
View File
@@ -32,10 +32,11 @@ import { ExtensionLoader } from "../extensions/extension-loader";
import { broadcastMessage } from "../common/ipc";
import { CommandContainer } from "./components/command-palette/command-container";
import { bindProtocolAddRouteHandlers, LensProtocolRouterRenderer } from "./protocol-handler";
import { registerIpcHandlers } from "./ipc";
import { registerIpcListeners } from "./ipc";
import { ipcRenderer } from "electron";
import { IpcRendererNavigationEvents } from "./navigation/events";
import { catalogEntityRegistry } from "./api/catalog-entity-registry";
import { registerKeyboardShortcuts } from "./keyboard-shortcuts";
@observer
export class LensApp extends React.Component {
@@ -48,7 +49,8 @@ export class LensApp extends React.Component {
window.addEventListener("offline", () => broadcastMessage("network:offline"));
window.addEventListener("online", () => broadcastMessage("network:online"));
registerIpcHandlers();
registerKeyboardShortcuts();
registerIpcListeners();
}
componentDidMount() {
+26
View File
@@ -0,0 +1,26 @@
/**
* 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.
*/
import { dialogShowOpenDialogHandler, requestMain } from "../../common/ipc";
export async function showOpenDialog(options: Electron.OpenDialogOptions): Promise<Electron.OpenDialogReturnValue> {
return requestMain(dialogShowOpenDialogHandler, options);
}
+24
View File
@@ -0,0 +1,24 @@
/**
* 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.
*/
import * as dialog from "./dialog";
export { dialog };
+2 -3
View File
@@ -22,13 +22,12 @@
// Keeps window.localStorage state in external JSON-files.
// Because app creates random port between restarts => storage session wiped out each time.
import path from "path";
import { app, remote } from "electron";
import { comparer, observable, reaction, toJS, when } from "mobx";
import fse from "fs-extra";
import { StorageHelper } from "./storageHelper";
import { ClusterStore } from "../../common/cluster-store";
import logger from "../../main/logger";
import { getHostedClusterId } from "../../common/utils";
import { getHostedClusterId, getPath } from "../../common/utils";
const storage = observable({
initialized: false,
@@ -47,7 +46,7 @@ export function createStorage<T>(key: string, defaultValue: T) {
export function createAppStorage<T>(key: string, defaultValue: T, clusterId?: string | undefined) {
const { logPrefix } = StorageHelper;
const folder = path.resolve((app || remote.app).getPath("userData"), "lens-local-storage");
const folder = path.resolve(getPath("userData"), "lens-local-storage");
const fileName = `${clusterId ?? "app"}.json`;
const filePath = path.resolve(folder, fileName);