diff --git a/package.json b/package.json index 2b3fd43f4c..dfc8f59e3b 100644 --- a/package.json +++ b/package.json @@ -201,7 +201,7 @@ "dependencies": { "@hapi/call": "^8.0.1", "@hapi/subtext": "^7.0.3", - "@kubernetes/client-node": "^0.16.1", + "@kubernetes/client-node": "^0.16.3", "@ogre-tools/injectable": "5.1.2", "@ogre-tools/injectable-react": "5.1.2", "@sentry/electron": "^2.5.4", @@ -300,7 +300,7 @@ "@types/js-yaml": "^4.0.5", "@types/jsdom": "^16.2.14", "@types/jsonpath": "^0.2.0", - "@types/lodash": "^4.14.180", + "@types/lodash": "^4.14.181", "@types/marked": "^4.0.3", "@types/md5-file": "^4.0.2", "@types/mini-css-extract-plugin": "^2.4.0", @@ -312,7 +312,7 @@ "@types/randomcolor": "^0.5.6", "@types/react": "^17.0.40", "@types/react-beautiful-dnd": "^13.1.2", - "@types/react-dom": "^17.0.11", + "@types/react-dom": "^17.0.14", "@types/react-router-dom": "^5.3.2", "@types/react-select": "3.1.2", "@types/react-table": "^7.7.9", @@ -355,7 +355,7 @@ "eslint": "^8.11.0", "eslint-plugin-header": "^3.1.1", "eslint-plugin-import": "^2.25.4", - "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react": "^7.29.4", "eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-unused-imports": "^2.0.0", "flex.box": "^3.4.4", diff --git a/src/common/k8s-api/kube-api.ts b/src/common/k8s-api/kube-api.ts index 151e89f03d..54231b3947 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 ba52869e84..d21b179611 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[]; @@ -454,35 +455,51 @@ export abstract class KubeObjectStore extends ItemStore @action protected updateFromEventsBuffer() { + // TODO: Convert items from Array to Map to avoid doing findIndex many times per second (events delay) + // Also, its worth to use Web Worker for such operations to prevent blocking UI + // https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers const items = this.getItems(); - for (const { type, object } of this.eventsBuffer.clear()) { - // TODO: Convert items from Array to Map to avoid doing findIndex many times per second (events delay) - // Also, its worth to use Web Worker for such operations to prevent blocking UI - // https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers - 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/avatar/avatar.tsx b/src/renderer/components/avatar/avatar.tsx index c63df5054e..540b57cc64 100644 --- a/src/renderer/components/avatar/avatar.tsx +++ b/src/renderer/components/avatar/avatar.tsx @@ -57,14 +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 = () => { - if (src) { - return "transparent"; - } - - return background || randomColor({ seed: colorHash, luminosity: "dark" }); - }; + const colorFromHash = randomColor({ seed: colorHash, luminosity: "dark" }); const renderContents = () => { if (src) { @@ -81,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/dock/dock.scss b/src/renderer/components/dock/dock.scss index 27f8d8004b..365d74bad1 100644 --- a/src/renderer/components/dock/dock.scss +++ b/src/renderer/components/dock/dock.scss @@ -69,6 +69,7 @@ flex: 1; overflow: hidden; transition: flex-basis 25ms ease-in; + background: var(--dockInfoBackground); &.terminal { background: var(--terminalBackground); 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 e4b09d14a9..cc6a5c21e4 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/yarn.lock b/yarn.lock index 655e196ef7..40137e87bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -808,10 +808,10 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@kubernetes/client-node@^0.16.1": - version "0.16.1" - resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.16.1.tgz#c78ef667579777c1a532983922807e228dbc9b90" - integrity sha512-/Ah+3gFSjXFeqDMGGTyYBKug44Eu2D2qowKLdiZqxCkHdSNgy+CNk6FU1Vy80WrTvGkF/CZr4az6O5AopAiJEw== +"@kubernetes/client-node@^0.16.3": + version "0.16.3" + resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.16.3.tgz#a26a5abbd6e45603b4f75f0baff00e19853e5be7" + integrity sha512-L7IckuyuPfhd+/Urib8MRas9D6sfKEq8IaITYcaE6LlU+Y8MeD7MTbuW6Yb2WdeRuFN8HPSS47mxPnOUNYBXEg== dependencies: "@types/js-yaml" "^4.0.1" "@types/node" "^10.12.0" @@ -828,9 +828,9 @@ openid-client "^4.1.1" request "^2.88.0" rfc4648 "^1.3.0" - shelljs "^0.8.4" + shelljs "^0.8.5" stream-buffers "^3.0.2" - tar "^6.0.2" + tar "^6.1.11" tmp-promise "^3.0.2" tslib "^1.9.3" underscore "^1.9.1" @@ -1632,10 +1632,10 @@ dependencies: "@types/node" "*" -"@types/lodash@^4.14.180": - version "4.14.180" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" - integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g== +"@types/lodash@^4.14.181": + version "4.14.181" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.181.tgz#d1d3740c379fda17ab175165ba04e2d03389385d" + integrity sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag== "@types/marked@^4.0.3": version "4.0.3" @@ -1770,10 +1770,10 @@ dependencies: "@types/react" "*" -"@types/react-dom@*", "@types/react-dom@^17.0.11": - version "17.0.11" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466" - integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q== +"@types/react-dom@*", "@types/react-dom@^17.0.14": + version "17.0.14" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.14.tgz#c8f917156b652ddf807711f5becbd2ab018dea9f" + integrity sha512-H03xwEP1oXmSfl3iobtmQ/2dHF5aBHr8aUMwyGZya6OW45G+xtdzmq6HkncefiBt5JU8DVyaWl/nWZbjZCnzAQ== dependencies: "@types/react" "*" @@ -5358,22 +5358,22 @@ eslint-plugin-react-hooks@^4.3.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== -eslint-plugin-react@^7.28.0: - version "7.28.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz#8f3ff450677571a659ce76efc6d80b6a525adbdf" - integrity sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw== +eslint-plugin-react@^7.29.4: + version "7.29.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz#4717de5227f55f3801a5fd51a16a4fa22b5914d2" + integrity sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ== dependencies: array-includes "^3.1.4" array.prototype.flatmap "^1.2.5" doctrine "^2.1.0" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.0.4" + minimatch "^3.1.2" object.entries "^1.1.5" object.fromentries "^2.0.5" object.hasown "^1.1.0" object.values "^1.1.5" - prop-types "^15.7.2" + prop-types "^15.8.1" resolve "^2.0.0-next.3" semver "^6.3.0" string.prototype.matchall "^4.0.6" @@ -9107,13 +9107,20 @@ minimalistic-assert@^1.0.0: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: +minimatch@3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -10781,14 +10788,14 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== +prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" object-assign "^4.1.1" - react-is "^16.8.1" + react-is "^16.13.1" proper-lockfile@4.1.2, proper-lockfile@^4.1.2: version "4.1.2" @@ -11002,7 +11009,7 @@ react-input-autosize@^3.0.0: dependencies: prop-types "^15.5.8" -react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -11929,7 +11936,7 @@ shell-env@^3.0.1: execa "^1.0.0" strip-ansi "^5.2.0" -shelljs@^0.8.4: +shelljs@^0.8.5: version "0.8.5" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==