From 36dab02c34d0f2ae835b95544a7aed77adacbd33 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Tue, 15 Jun 2021 13:48:23 +0300 Subject: [PATCH 1/4] Fixing resizing anchor highlight (#3056) Signed-off-by: Alex Andreev --- .../resizing-anchor/resizing-anchor.scss | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/renderer/components/resizing-anchor/resizing-anchor.scss b/src/renderer/components/resizing-anchor/resizing-anchor.scss index 024a83105b..13cebe4b8e 100644 --- a/src/renderer/components/resizing-anchor/resizing-anchor.scss +++ b/src/renderer/components/resizing-anchor/resizing-anchor.scss @@ -38,13 +38,13 @@ body.resizing { height: 100%; margin-left: 50%; background: transparent; - transition: all 0.2s 0s; + transition: background 0.2s 0s; } &:hover { &::after { background: var(--blue); - transition: all 0.2s 0.5s; + transition: background 0.2s 0.5s; } } @@ -58,8 +58,20 @@ body.resizing { cursor: row-resize; height: $dimension; - &.leading { - top: -$dimension / 2; + &::after { + height: 3px; + width: 100%; + margin-left: 0; + } + + .resizing & { + $expandedHeight: 200px; + height: $expandedHeight; + margin-top: -$expandedHeight / 2; + + &::after { + margin-top: $expandedHeight / 2; + } } &.trailing { From ab6b422547dc8530fdc11472886c59fc85e34d1a Mon Sep 17 00:00:00 2001 From: Mario Sarcher Date: Tue, 15 Jun 2021 15:40:17 +0200 Subject: [PATCH 2/4] Adopts landing page according to new catalog features (#3055) Signed-off-by: Mario Sarcher --- src/renderer/components/+welcome/welcome.tsx | 5 +++-- src/renderer/initializers/welcome-menu-registry.ts | 11 +++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/renderer/components/+welcome/welcome.tsx b/src/renderer/components/+welcome/welcome.tsx index ed3d9b5dc4..703520fa17 100644 --- a/src/renderer/components/+welcome/welcome.tsx +++ b/src/renderer/components/+welcome/welcome.tsx @@ -34,10 +34,11 @@ export class Welcome extends React.Component {
-

Welcome to {productName} 5 Beta!

+

Welcome to {productName} 5!

- Here are some steps to help you get started with {productName} 5 Beta. + To get you started we have auto-detected your clusters in your kubeconfig file and added them to the catalog, your centralized view for managing all your cloud-native resources. +

If you have any questions or feedback, please join our Lens Community slack channel.

diff --git a/src/renderer/initializers/welcome-menu-registry.ts b/src/renderer/initializers/welcome-menu-registry.ts index 887735b357..9fce6a8c61 100644 --- a/src/renderer/initializers/welcome-menu-registry.ts +++ b/src/renderer/initializers/welcome-menu-registry.ts @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { catalogURL, preferencesURL } from "../../common/routes"; +import { catalogURL } from "../../common/routes"; import { WelcomeMenuRegistry } from "../../extensions/registries"; import { navigate } from "../navigation"; @@ -27,14 +27,9 @@ export function initWelcomeMenuRegistry() { WelcomeMenuRegistry.getInstance() .add([ { - title: "Browse Your Catalog", + title: "Browse Clusters", icon: "view_list", - click: () => navigate(catalogURL()) - }, - { - title: "Configure Preferences", - icon: "settings", - click: () => navigate(preferencesURL()) + click: () => navigate(catalogURL({ params: { group: "entity.k8slens.dev", kind: "KubernetesCluster" } } )) } ]); } From 42bb2a620abc297fe763a8409682d22feccce492 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 15 Jun 2021 14:14:03 -0400 Subject: [PATCH 3/4] Fix extensions not having the same version as Lens (#3027) Signed-off-by: Sebastian Malton --- build/set_build_version.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/set_build_version.ts b/build/set_build_version.ts index 8315d535bd..80fd385c59 100644 --- a/build/set_build_version.ts +++ b/build/set_build_version.ts @@ -45,7 +45,7 @@ function getBuildChannel(): string { async function writeOutExtensionVersion(manifestPath: string) { const extensionPackageJson = await fse.readJson(manifestPath); - extensionPackageJson.version = `${versionInfo.format()}.${buildNumber}`; + extensionPackageJson.version = appInfo.version; return fse.writeJson(manifestPath, extensionPackageJson, { spaces: 2, From 3abb3bdcce9cce4228b74540268d3ca7f490dd52 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Wed, 16 Jun 2021 07:12:10 +0300 Subject: [PATCH 4/4] Bring back support for custom cluster icons (#3066) * revive custom cluster icons Signed-off-by: Jari Kolehmainen * fix borders Signed-off-by: Jari Kolehmainen --- .../catalog-entities/kubernetes-cluster.ts | 1 + src/main/cluster-manager.ts | 67 ++++++----- .../+catalog/catalog-entity-details.tsx | 1 + src/renderer/components/+catalog/catalog.tsx | 16 +-- .../cluster-settings/cluster-settings.tsx | 4 + .../components/cluster-icon-settings.tsx | 110 ++++++++++++++++++ .../cluster-settings/components/index.ts | 1 + .../components/hotbar/hotbar-entity-icon.tsx | 1 + .../components/hotbar/hotbar-icon.scss | 14 ++- .../components/hotbar/hotbar-icon.tsx | 32 +++-- 10 files changed, 202 insertions(+), 45 deletions(-) create mode 100644 src/renderer/components/cluster-settings/components/cluster-icon-settings.tsx diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index 0ee8c7353f..82582d2bc5 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -42,6 +42,7 @@ export type KubernetesClusterPrometheusMetrics = { export type KubernetesClusterSpec = { kubeconfigPath: string; kubeconfigContext: string; + iconData?: string; metrics?: { source: string; prometheus?: KubernetesClusterPrometheusMetrics; diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index f3859e5fb5..4075c81e36 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -21,7 +21,7 @@ import "../common/cluster-ipc"; import type http from "http"; -import { action, autorun, makeObservable, reaction } from "mobx"; +import { action, autorun, makeObservable, reaction, toJS } from "mobx"; import { ClusterStore, getClusterIdFromHost } from "../common/cluster-store"; import type { Cluster } from "./cluster"; import logger from "./logger"; @@ -45,7 +45,14 @@ export class ClusterManager extends Singleton { reaction( () => this.store.clustersList.map(c => c.getState()), () => this.updateCatalog(this.store.clustersList), - { fireImmediately: true, } + { fireImmediately: false, } + ); + + // reacting to every cluster's preferences change and total amount of items + reaction( + () => this.store.clustersList.map(c => toJS(c.preferences)), + () => this.updateCatalog(this.store.clustersList), + { fireImmediately: false, } ); reaction(() => catalogEntityRegistry.getItemsForApiKind("entity.k8slens.dev/v1alpha1", "KubernetesCluster"), (entities) => { @@ -74,32 +81,40 @@ export class ClusterManager extends Singleton { @action protected updateCatalog(clusters: Cluster[]) { for (const cluster of clusters) { - const index = catalogEntityRegistry.items.findIndex((entity) => entity.metadata.uid === cluster.id); - - if (index !== -1) { - const entity = catalogEntityRegistry.items[index] as KubernetesCluster; - - this.updateEntityStatus(entity, cluster); - - if (cluster.preferences?.clusterName) { - entity.metadata.name = cluster.preferences.clusterName; - } - - entity.spec.metrics ||= { source: "local" }; - - if (entity.spec.metrics.source === "local") { - const prometheus: KubernetesClusterPrometheusMetrics = entity.spec?.metrics?.prometheus || {}; - - prometheus.type = cluster.preferences.prometheusProvider?.type; - prometheus.address = cluster.preferences.prometheus; - entity.spec.metrics.prometheus = prometheus; - } - - catalogEntityRegistry.items.splice(index, 1, entity); - } + this.updateEntityFromCluster(cluster); } } + protected updateEntityFromCluster(cluster: Cluster) { + const index = catalogEntityRegistry.items.findIndex((entity) => entity.metadata.uid === cluster.id); + + if (index === -1) { + return; + } + + const entity = catalogEntityRegistry.items[index] as KubernetesCluster; + + this.updateEntityStatus(entity, cluster); + + if (cluster.preferences?.clusterName) { + entity.metadata.name = cluster.preferences.clusterName; + } + + entity.spec.metrics ||= { source: "local" }; + + if (entity.spec.metrics.source === "local") { + const prometheus: KubernetesClusterPrometheusMetrics = entity.spec?.metrics?.prometheus || {}; + + prometheus.type = cluster.preferences.prometheusProvider?.type; + prometheus.address = cluster.preferences.prometheus; + entity.spec.metrics.prometheus = prometheus; + } + + entity.spec.iconData = cluster.preferences.icon; + + catalogEntityRegistry.items.splice(index, 1, entity); + } + protected updateEntityStatus(entity: KubernetesCluster, cluster: Cluster) { entity.status.phase = cluster.accessible ? "connected" : "disconnected"; } @@ -121,7 +136,7 @@ export class ClusterManager extends Singleton { cluster.kubeConfigPath = entity.spec.kubeconfigPath; cluster.contextName = entity.spec.kubeconfigContext; - this.updateEntityStatus(entity, cluster); + this.updateEntityFromCluster(cluster); } } } diff --git a/src/renderer/components/+catalog/catalog-entity-details.tsx b/src/renderer/components/+catalog/catalog-entity-details.tsx index 73a15c9f48..639d7c4843 100644 --- a/src/renderer/components/+catalog/catalog-entity-details.tsx +++ b/src/renderer/components/+catalog/catalog-entity-details.tsx @@ -79,6 +79,7 @@ export class CatalogEntityDetails extends Component { uid={entity.metadata.uid} title={entity.metadata.name} source={entity.metadata.source} + icon={entity.spec.iconData} onClick={() => this.openEntity()} size={128} />
diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 9d9ac6fd2e..7a34d44c04 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -37,12 +37,12 @@ import { catalogCategoryRegistry } from "../../../common/catalog"; import { CatalogAddButton } from "./catalog-add-button"; import type { RouteComponentProps } from "react-router"; import { Notifications } from "../notifications"; -import { Avatar } from "../avatar/avatar"; import { MainLayout } from "../layout/main-layout"; import { cssNames } from "../../utils"; import { makeCss } from "../../../common/utils/makeCss"; import { CatalogEntityDetails } from "./catalog-entity-details"; import type { CatalogViewRouteParam } from "../../../common/routes"; +import { HotbarIcon } from "../hotbar/hotbar-icon"; enum sortBy { name = "name", @@ -192,13 +192,13 @@ export class Catalog extends React.Component { renderIcon(item: CatalogEntityItem) { return ( - + this.onDetails(item)} + size={24} /> ); } diff --git a/src/renderer/components/cluster-settings/cluster-settings.tsx b/src/renderer/components/cluster-settings/cluster-settings.tsx index 32190df9bc..ed01ce4fb7 100644 --- a/src/renderer/components/cluster-settings/cluster-settings.tsx +++ b/src/renderer/components/cluster-settings/cluster-settings.tsx @@ -20,6 +20,7 @@ */ import React from "react"; +import type { KubernetesCluster } from "../../../common/catalog-entities"; import { ClusterStore } from "../../../common/cluster-store"; import type { EntitySettingViewProps } from "../../../extensions/registries"; import type { CatalogEntity } from "../../api/catalog-entity"; @@ -41,6 +42,9 @@ export function GeneralSettings({ entity }: EntitySettingViewProps) {
+
+ +
diff --git a/src/renderer/components/cluster-settings/components/cluster-icon-settings.tsx b/src/renderer/components/cluster-settings/components/cluster-icon-settings.tsx new file mode 100644 index 0000000000..2800586a11 --- /dev/null +++ b/src/renderer/components/cluster-settings/components/cluster-icon-settings.tsx @@ -0,0 +1,110 @@ +/** + * 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 React from "react"; +import type { Cluster } from "../../../../main/cluster"; +//import { FilePicker, OverSizeLimitStyle } from "../../file-picker"; +import { boundMethod } from "../../../utils"; +import { Button } from "../../button"; +import { observable } from "mobx"; +import { observer } from "mobx-react"; +import { SubTitle } from "../../layout/sub-title"; +import { HotbarIcon } from "../../hotbar/hotbar-icon"; +import type { KubernetesCluster } from "../../../../common/catalog-entities"; +import { FilePicker, OverSizeLimitStyle } from "../../file-picker"; + +enum GeneralInputStatus { + CLEAN = "clean", + ERROR = "error", +} + +interface Props { + cluster: Cluster; + entity: KubernetesCluster +} + +@observer +export class ClusterIconSetting extends React.Component { + @observable status = GeneralInputStatus.CLEAN; + @observable errorText?: string; + + @boundMethod + async onIconPick([file]: File[]) { + const { cluster } = this.props; + + try { + if (file) { + const buf = Buffer.from(await file.arrayBuffer()); + + cluster.preferences.icon = `data:${file.type};base64,${buf.toString("base64")}`; + } else { + // this has to be done as a seperate branch (and not always) because `cluster` + // is observable and triggers an update loop. + cluster.preferences.icon = undefined; + } + } catch (e) { + this.errorText = e.toString(); + this.status = GeneralInputStatus.ERROR; + } + } + + getClearButton() { + if (this.props.cluster.preferences.icon) { + return