diff --git a/package.json b/package.json index d987348eb1..4004ef119d 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.4.4", + "version": "5.4.5", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", diff --git a/src/common/k8s-api/kube-api.ts b/src/common/k8s-api/kube-api.ts index b81b72eae4..6c86f1dfe7 100644 --- a/src/common/k8s-api/kube-api.ts +++ b/src/common/k8s-api/kube-api.ts @@ -655,7 +655,7 @@ export class KubeApi { if (event.type === "ERROR" && event.object.kind === "Status") { errorReceived = true; - return callback(null, new KubeStatus(event.object as any)); + return callback(null, new KubeStatus(event.object)); } this.modifyWatchEvent(event); diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts index 373ffb8251..beae1aa64d 100644 --- a/src/common/k8s-api/kube-object.store.ts +++ b/src/common/k8s-api/kube-object.store.ts @@ -19,6 +19,7 @@ import type { RequestInit } from "node-fetch"; // eslint-disable-next-line import/no-named-as-default import AbortController from "abort-controller"; import type { Patch } from "rfc6902"; +import logger from "../logger"; export interface KubeObjectStoreLoadingParams { namespaces: string[]; @@ -456,30 +457,46 @@ export abstract class KubeObjectStore extends ItemStore protected updateFromEventsBuffer() { const items = this.getItems(); - for (const { type, object } of this.eventsBuffer.clear()) { - const index = items.findIndex(item => item.getId() === object.metadata?.uid); - const item = items[index]; + for (const event of this.eventsBuffer.clear()) { + if (event.type === "ERROR") { + continue; + } - switch (type) { - case "ADDED": + try { + const { type, object } = event; - // falls through - case "MODIFIED": { - const newItem = new this.api.objectConstructor(object); - - if (!item) { - items.push(newItem); - } else { - items[index] = newItem; - } - - break; + if (!object.metadata?.uid) { + logger.warn("[KUBE-STORE]: watch event did not have defined .metadata.uid, skipping", { event }); + // Other parts of the code will break if this happens + continue; } - case "DELETED": - if (item) { - items.splice(index, 1); + + const index = items.findIndex(item => item.getId() === object.metadata.uid); + const item = items[index]; + + switch (type) { + case "ADDED": + + // fallthrough + case "MODIFIED": { + const newItem = new this.api.objectConstructor(object); + + if (!item) { + items.push(newItem); + } else { + items[index] = newItem; + } + + break; } - break; + case "DELETED": + if (item) { + items.splice(index, 1); + } + break; + } + } catch (error) { + logger.error("[KUBE-STORE]: failed to handle event from watch buffer", { error, event }); } } diff --git a/src/common/k8s-api/kube-watch-event.ts b/src/common/k8s-api/kube-watch-event.ts index acf57f250b..7332576560 100644 --- a/src/common/k8s-api/kube-watch-event.ts +++ b/src/common/k8s-api/kube-watch-event.ts @@ -4,9 +4,13 @@ */ import type { KubeJsonApiData } from "./kube-json-api"; +import type { KubeStatusData } from "./kube-object"; -export interface IKubeWatchEvent { - type: "ADDED" | "MODIFIED" | "DELETED" | "ERROR"; - object?: T; -} +export type IKubeWatchEvent = { + type: "ADDED" | "MODIFIED" | "DELETED"; + object: T; +} | { + type: "ERROR"; + object: KubeStatusData; +}; diff --git a/src/renderer/components/+catalog/internal-category-columns.tsx b/src/renderer/components/+catalog/internal-category-columns.tsx index 6aee0acc1d..f820d40837 100644 --- a/src/renderer/components/+catalog/internal-category-columns.tsx +++ b/src/renderer/components/+catalog/internal-category-columns.tsx @@ -117,5 +117,6 @@ export const defaultCategoryColumns: RegisteredAdditionalCategoryColumn[] = [ sortBy: "status", }, searchFilter: entity => entity.status.phase, + sortCallback: entity => entity.status.phase, }, ]; diff --git a/src/renderer/components/avatar/avatar.tsx b/src/renderer/components/avatar/avatar.tsx index c504d773f1..540b57cc64 100644 --- a/src/renderer/components/avatar/avatar.tsx +++ b/src/renderer/components/avatar/avatar.tsx @@ -57,10 +57,7 @@ function getLabelFromTitle(title: string) { export function Avatar(props: AvatarProps) { const { title, variant = "rounded", size = 32, colorHash, children, background, imgProps, src, className, disabled, ...rest } = props; - - const getBackgroundColor = () => { - return background || randomColor({ seed: colorHash, luminosity: "dark" }); - }; + const colorFromHash = randomColor({ seed: colorHash, luminosity: "dark" }); const renderContents = () => { if (src) { @@ -77,7 +74,11 @@ export function Avatar(props: AvatarProps) { [styles.rounded]: variant == "rounded", [styles.disabled]: disabled, }, className)} - style={{ width: `${size}px`, height: `${size}px`, backgroundColor: getBackgroundColor() }} + style={{ + width: `${size}px`, + height: `${size}px`, + background: background || (src ? "transparent" : colorFromHash), + }} {...rest} > {renderContents()} diff --git a/src/renderer/components/cluster-manager/cluster-status.tsx b/src/renderer/components/cluster-manager/cluster-status.tsx index 408f45b1bb..97dc3cb06c 100644 --- a/src/renderer/components/cluster-manager/cluster-status.tsx +++ b/src/renderer/components/cluster-manager/cluster-status.tsx @@ -55,6 +55,13 @@ export class ClusterStatus extends React.Component { ]); } + componentDidUpdate(prevProps: Readonly): void { + if (prevProps.cluster.id !== this.props.cluster.id) { + this.isReconnecting = false; + this.authOutput = []; + } + } + reconnect = async () => { this.authOutput = []; this.isReconnecting = true; diff --git a/src/renderer/components/dock/dock.scss b/src/renderer/components/dock/dock.scss index 7a543ce934..85e0cb9e50 100644 --- a/src/renderer/components/dock/dock.scss +++ b/src/renderer/components/dock/dock.scss @@ -63,6 +63,7 @@ flex: 1; overflow: hidden; transition: flex-basis 25ms ease-in; + background: var(--dockInfoBackground); &.terminal { background: var(--terminalBackground); diff --git a/src/renderer/components/dock/edit-resource/view.tsx b/src/renderer/components/dock/edit-resource/view.tsx index aa23af9cea..5a9aaf9abc 100644 --- a/src/renderer/components/dock/edit-resource/view.tsx +++ b/src/renderer/components/dock/edit-resource/view.tsx @@ -46,7 +46,7 @@ class NonInjectedEditResource extends React.Component { const obj = this.resource; if (!obj) { - if (store.isLoaded) { + if (store?.isLoaded) { // auto-close tab when resource removed from store this.props.closeTab(this.props.tab.id); } else if (!store.isLoading) { diff --git a/src/renderer/components/hotbar/hotbar-icon.module.scss b/src/renderer/components/hotbar/hotbar-icon.module.scss index de89c5a08f..38df5c3d5f 100644 --- a/src/renderer/components/hotbar/hotbar-icon.module.scss +++ b/src/renderer/components/hotbar/hotbar-icon.module.scss @@ -4,7 +4,8 @@ */ .HotbarIcon { - --iconActiveShadow: 0 0 0px 3px var(--clusterMenuBackground), 0 0 0px 6px var(--textColorAccent); + --corner: 0px 0px 0px 1px var(--clusterMenuBackground); + --iconActiveShadow: var(--corner), var(--corner), 0 0 0px 3px var(--clusterMenuBackground), 0 0 0px 6px var(--textColorAccent); --iconHoverShadow: 0 0 0px 3px var(--clusterMenuBackground), 0 0 0px 6px #ffffff50; display: flex; @@ -24,6 +25,10 @@ .avatar { border-radius: 6px; + + &.hasImage { + background-color: var(--clusterMenuCellBackground); + } } .active { diff --git a/src/renderer/components/hotbar/hotbar-icon.tsx b/src/renderer/components/hotbar/hotbar-icon.tsx index f22b30c66e..bf6f1acced 100644 --- a/src/renderer/components/hotbar/hotbar-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-icon.tsx @@ -61,7 +61,7 @@ export const HotbarIcon = observer(({ menuItems = [], size = 40, tooltip, ...pro id={id} title={title} colorHash={`${title}-${source}`} - className={cssNames(styles.avatar, { [styles.active]: active })} + className={cssNames(styles.avatar, { [styles.active]: active, [styles.hasImage]: !!src })} disabled={disabled} size={size} src={src} diff --git a/src/renderer/components/layout/sidebar-cluster.tsx b/src/renderer/components/layout/sidebar-cluster.tsx index 58cae55c99..34d9b733fc 100644 --- a/src/renderer/components/layout/sidebar-cluster.tsx +++ b/src/renderer/components/layout/sidebar-cluster.tsx @@ -16,6 +16,7 @@ import { navigate } from "../../navigation"; import { Menu, MenuItem } from "../menu"; import { ConfirmDialog } from "../confirm-dialog"; import { Tooltip } from "../tooltip"; +import { observer } from "mobx-react"; const contextMenu: CatalogEntityContextMenuContext = observable({ menuItems: [], @@ -59,7 +60,7 @@ function renderLoadingSidebarCluster() { ); } -export function SidebarCluster({ clusterEntity }: { clusterEntity: CatalogEntity }) { +export const SidebarCluster = observer(({ clusterEntity }: { clusterEntity: CatalogEntity }) => { const [opened, setOpened] = useState(false); if (!clusterEntity) { @@ -138,4 +139,4 @@ export function SidebarCluster({ clusterEntity }: { clusterEntity: CatalogEntity ); -} +});