mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into consitent-tooltips
This commit is contained in:
commit
79f57c9826
@ -3,7 +3,7 @@
|
||||
"productName": "OpenLens",
|
||||
"description": "OpenLens - Open Source IDE for Kubernetes",
|
||||
"homepage": "https://github.com/lensapp/lens",
|
||||
"version": "5.0.0-beta.12",
|
||||
"version": "5.0.0-beta.13",
|
||||
"main": "static/build/main.js",
|
||||
"copyright": "© 2021 OpenLens Authors",
|
||||
"license": "MIT",
|
||||
|
||||
@ -72,7 +72,7 @@ export interface ClusterModel {
|
||||
workspace?: string;
|
||||
|
||||
/** User context in kubeconfig */
|
||||
contextName?: string;
|
||||
contextName: string;
|
||||
|
||||
/** Preferences */
|
||||
preferences?: ClusterPreferences;
|
||||
|
||||
@ -192,6 +192,10 @@ export class ExtensionLoader extends Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
setIsEnabled(lensExtensionId: LensExtensionId, isEnabled: boolean) {
|
||||
this.extensions.get(lensExtensionId).isEnabled = isEnabled;
|
||||
}
|
||||
|
||||
protected async initMain() {
|
||||
this.isLoaded = true;
|
||||
this.loadOnMain();
|
||||
|
||||
120
src/migrations/cluster-store/5.0.0-beta.13.ts
Normal file
120
src/migrations/cluster-store/5.0.0-beta.13.ts
Normal file
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* 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 type { ClusterModel, ClusterPreferences, ClusterPrometheusPreferences } from "../../common/cluster-store";
|
||||
import { MigrationDeclaration, migrationLog } from "../helpers";
|
||||
import { generateNewIdFor } from "../utils";
|
||||
import path from "path";
|
||||
import { app } from "electron";
|
||||
import { moveSync, removeSync } from "fs-extra";
|
||||
|
||||
function mergePrometheusPreferences(left: ClusterPrometheusPreferences, right: ClusterPrometheusPreferences): ClusterPrometheusPreferences {
|
||||
if (left.prometheus && left.prometheusProvider) {
|
||||
return {
|
||||
prometheus: left.prometheus,
|
||||
prometheusProvider: left.prometheusProvider,
|
||||
};
|
||||
}
|
||||
|
||||
if (right.prometheus && right.prometheusProvider) {
|
||||
return {
|
||||
prometheus: right.prometheus,
|
||||
prometheusProvider: right.prometheusProvider,
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
function mergePreferences(left: ClusterPreferences, right: ClusterPreferences): ClusterPreferences {
|
||||
return {
|
||||
terminalCWD: left.terminalCWD || right.terminalCWD || undefined,
|
||||
clusterName: left.clusterName || right.clusterName || undefined,
|
||||
iconOrder: left.iconOrder || right.iconOrder || undefined,
|
||||
icon: left.icon || right.icon || undefined,
|
||||
httpsProxy: left.httpsProxy || right.httpsProxy || undefined,
|
||||
hiddenMetrics: mergeSet(left.hiddenMetrics ?? [], right.hiddenMetrics ?? []),
|
||||
...mergePrometheusPreferences(left, right),
|
||||
};
|
||||
}
|
||||
|
||||
function mergeLabels(left: Record<string, string>, right: Record<string, string>): Record<string, string> {
|
||||
return {
|
||||
...right,
|
||||
...left
|
||||
};
|
||||
}
|
||||
|
||||
function mergeSet(left: Iterable<string>, right: Iterable<string>): string[] {
|
||||
return [...new Set([...left, ...right])];
|
||||
}
|
||||
|
||||
function mergeClusterModel(prev: ClusterModel, right: Omit<ClusterModel, "id">): ClusterModel {
|
||||
return {
|
||||
id: prev.id,
|
||||
kubeConfigPath: prev.id,
|
||||
contextName: prev.contextName,
|
||||
preferences: mergePreferences(prev.preferences ?? {}, right.preferences ?? {}),
|
||||
metadata: prev.metadata,
|
||||
labels: mergeLabels(prev.labels ?? {}, right.labels ?? {}),
|
||||
accessibleNamespaces: mergeSet(prev.accessibleNamespaces ?? [], right.accessibleNamespaces ?? []),
|
||||
};
|
||||
}
|
||||
|
||||
function moveStorageFolder({ folder, newId, oldId }: { folder: string, newId: string, oldId: string }): void {
|
||||
const oldPath = path.resolve(folder, `${oldId}.json`);
|
||||
const newPath = path.resolve(folder, `${newId}.json`);
|
||||
|
||||
try {
|
||||
moveSync(oldPath, newPath);
|
||||
} catch (error) {
|
||||
if (String(error).includes("dest already exists")) {
|
||||
migrationLog(`Multiple old lens-local-storage files for newId=${newId}. Removing ${oldId}.json`);
|
||||
removeSync(oldPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
version: "5.0.0-beta.13",
|
||||
run(store) {
|
||||
const folder = path.resolve(app.getPath("userData"), "lens-local-storage");
|
||||
|
||||
const oldClusters: ClusterModel[] = store.get("clusters");
|
||||
const clusters = new Map<string, ClusterModel>();
|
||||
|
||||
for (const { id: oldId, ...cluster } of oldClusters) {
|
||||
const newId = generateNewIdFor(cluster);
|
||||
|
||||
if (clusters.has(newId)) {
|
||||
clusters.set(newId, mergeClusterModel(clusters.get(newId), cluster));
|
||||
} else {
|
||||
clusters.set(newId, {
|
||||
...cluster,
|
||||
id: newId,
|
||||
});
|
||||
moveStorageFolder({ folder, newId, oldId });
|
||||
}
|
||||
}
|
||||
|
||||
store.set("clusters", [...clusters.values()]);
|
||||
}
|
||||
} as MigrationDeclaration;
|
||||
@ -31,6 +31,7 @@ import version270Beta0 from "./2.7.0-beta.0";
|
||||
import version270Beta1 from "./2.7.0-beta.1";
|
||||
import version360Beta1 from "./3.6.0-beta.1";
|
||||
import version500Beta10 from "./5.0.0-beta.10";
|
||||
import version500Beta13 from "./5.0.0-beta.13";
|
||||
import snap from "./snap";
|
||||
|
||||
export default joinMigrations(
|
||||
@ -42,5 +43,6 @@ export default joinMigrations(
|
||||
version270Beta1,
|
||||
version360Beta1,
|
||||
version500Beta10,
|
||||
version500Beta13,
|
||||
snap,
|
||||
);
|
||||
|
||||
@ -19,16 +19,16 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { createHash } from "crypto";
|
||||
import { app } from "electron";
|
||||
import fse from "fs-extra";
|
||||
import { isNull } from "lodash";
|
||||
import { isNull, uniqBy } from "lodash";
|
||||
import path from "path";
|
||||
import * as uuid from "uuid";
|
||||
import type { ClusterStoreModel } from "../../common/cluster-store";
|
||||
import { defaultHotbarCells, Hotbar, HotbarStore } from "../../common/hotbar-store";
|
||||
import { catalogEntity } from "../../main/catalog-sources/general";
|
||||
import type { MigrationDeclaration } from "../helpers";
|
||||
import { generateNewIdFor } from "../utils";
|
||||
|
||||
interface Pre500WorkspaceStoreModel {
|
||||
workspaces: {
|
||||
@ -47,6 +47,7 @@ export default {
|
||||
const workspaceStoreData: Pre500WorkspaceStoreModel = fse.readJsonSync(path.join(userDataPath, "lens-workspace-store.json"));
|
||||
const { clusters }: ClusterStoreModel = fse.readJSONSync(path.join(userDataPath, "lens-cluster-store.json"));
|
||||
const workspaceHotbars = new Map<string, Hotbar>(); // mapping from WorkspaceId to HotBar
|
||||
const uniqueClusters = uniqBy(clusters, "metadata.id"); // Filtering out duplicated clusters
|
||||
|
||||
for (const { id, name } of workspaceStoreData.workspaces) {
|
||||
workspaceHotbars.set(id, {
|
||||
@ -68,13 +69,13 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
for (const cluster of clusters) {
|
||||
for (const cluster of uniqueClusters) {
|
||||
const workspaceHotbar = workspaceHotbars.get(cluster.workspace);
|
||||
|
||||
if (workspaceHotbar?.items.length < defaultHotbarCells) {
|
||||
workspaceHotbar.items.push({
|
||||
entity: {
|
||||
uid: createHash("md5").update(`${cluster.kubeConfigPath}:${cluster.contextName}`).digest("hex"),
|
||||
uid: generateNewIdFor(cluster),
|
||||
name: cluster.preferences.clusterName || cluster.contextName,
|
||||
}
|
||||
});
|
||||
|
||||
26
src/migrations/utils.ts
Normal file
26
src/migrations/utils.ts
Normal 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 { createHash } from "crypto";
|
||||
|
||||
export function generateNewIdFor(cluster: { kubeConfigPath: string, contextName: string }): string {
|
||||
return createHash("md5").update(`${cluster.kubeConfigPath}:${cluster.contextName}`).digest("hex");
|
||||
}
|
||||
@ -278,7 +278,7 @@ async function unpackExtension(request: InstallRequestValidated, disposeDownload
|
||||
await when(() => ExtensionLoader.getInstance().userExtensions.has(id));
|
||||
|
||||
// Enable installed extensions by default.
|
||||
ExtensionLoader.getInstance().userExtensions.get(id).isEnabled = true;
|
||||
ExtensionLoader.getInstance().setIsEnabled(id, true);
|
||||
|
||||
Notifications.ok(
|
||||
<p>Extension <b>{displayName}</b> successfully installed!</p>
|
||||
|
||||
@ -49,6 +49,10 @@
|
||||
cursor: default;
|
||||
filter: grayscale(0.7);
|
||||
|
||||
&.contextMenuAvailable {
|
||||
cursor: context-menu;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:not(.active) {
|
||||
box-shadow: none;
|
||||
|
||||
@ -97,7 +97,7 @@ export const HotbarIcon = observer(({menuItems = [], size = 40, interactive = tr
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cssNames("HotbarIcon flex", className, { disabled })}>
|
||||
<div className={cssNames("HotbarIcon flex", className, { disabled, contextMenuAvailable: menuItems.length > 0 })}>
|
||||
{tooltip && <Tooltip targetId={id}>{tooltip}</Tooltip>}
|
||||
<div id={id}>
|
||||
{renderIcon()}
|
||||
@ -111,19 +111,17 @@ export const HotbarIcon = observer(({menuItems = [], size = 40, interactive = tr
|
||||
toggleEvent="contextmenu"
|
||||
position={{right: true, bottom: true }} // FIXME: position does not work
|
||||
open={() => {
|
||||
if (!disabled) {
|
||||
onMenuOpen?.();
|
||||
toggleMenu();
|
||||
}
|
||||
onMenuOpen?.();
|
||||
toggleMenu();
|
||||
}}
|
||||
close={() => toggleMenu()}>
|
||||
{ menuItems.map((menuItem) => {
|
||||
return (
|
||||
<MenuItem key={menuItem.title} onClick={() => onMenuItemClick(menuItem) }>
|
||||
{
|
||||
menuItems.map((menuItem) => (
|
||||
<MenuItem key={menuItem.title} onClick={() => onMenuItemClick(menuItem)}>
|
||||
{menuItem.title}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
))
|
||||
}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -27,7 +27,7 @@ import { HotbarEntityIcon } from "./hotbar-entity-icon";
|
||||
import { cssNames, IClassName } from "../../utils";
|
||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
import { defaultHotbarCells, HotbarItem, HotbarStore } from "../../../common/hotbar-store";
|
||||
import { CatalogEntity, CatalogEntityContextMenu, catalogEntityRunContext } from "../../api/catalog-entity";
|
||||
import { CatalogEntity, catalogEntityRunContext } from "../../api/catalog-entity";
|
||||
import { DragDropContext, Draggable, Droppable, DropResult } from "react-beautiful-dnd";
|
||||
import { HotbarSelector } from "./hotbar-selector";
|
||||
import { HotbarCell } from "./hotbar-cell";
|
||||
@ -110,12 +110,6 @@ export class HotbarMenu extends React.Component<Props> {
|
||||
renderGrid() {
|
||||
return this.items.map((item, index) => {
|
||||
const entity = this.getEntity(item);
|
||||
const disabledMenuItems: CatalogEntityContextMenu[] = [
|
||||
{
|
||||
title: "Unpin from Hotbar",
|
||||
onClick: () => this.removeItem(item.entity.uid)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<Droppable droppableId={`${index}`} key={index}>
|
||||
@ -163,8 +157,13 @@ export class HotbarMenu extends React.Component<Props> {
|
||||
uid={`hotbar-icon-${item.entity.uid}`}
|
||||
title={item.entity.name}
|
||||
source={item.entity.source}
|
||||
menuItems={disabledMenuItems}
|
||||
tooltip={`${item.entity.name} (${item.entity.source})`}
|
||||
menuItems={[
|
||||
{
|
||||
title: "Unpin from Hotbar",
|
||||
onClick: () => this.removeItem(item.entity.uid)
|
||||
}
|
||||
]}
|
||||
disabled
|
||||
size={40}
|
||||
/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user