From 595fb90a2a0786007f63aed2d7e12135a7462849 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 25 Jan 2022 17:23:58 -0500 Subject: [PATCH] Full dependency inversion of and all current tab kinds - Fixes pod logs not working on initial load - Removes DockStore as a dependency of all *TabStore - Removes all the reactions about loading new data from the stores - Removes dependencies on selectedTabId from all *TabStore's Signed-off-by: Sebastian Malton --- ...ct-for-extension-api-with-modifications.ts | 42 +++++ ...gacy-global-singleton-for-extension-api.ts | 43 ----- src/extensions/renderer-api/components.ts | 34 +++- .../+apps-helm-charts/helm-chart-details.tsx | 2 +- .../release-details/release-details.tsx | 2 +- .../+apps-releases/release-menu.tsx | 2 +- .../internal-commands.injectable.tsx | 5 +- .../dock/__test__/dock-tabs.test.tsx | 4 +- .../create-install-chart-tab.injectable.ts | 19 --- .../create-install-chart-tab.ts | 44 ----- .../create-resource-tab.injectable.ts | 17 -- .../create-resource-tab.ts | 18 --- .../components/dock/create-resource.scss | 7 - ...ear-create-resource-tab-data.injectable.ts | 26 +++ .../create-resource-tab.injectable.ts | 30 ++++ .../store.injectable.ts} | 10 +- .../store.ts} | 6 +- .../view.tsx} | 46 +++--- .../create-terminal-tab.injectable.ts | 17 -- .../create-terminal-tab.ts | 22 --- .../create-upgrade-chart-tab.injectable.ts | 19 --- .../create-upgrade-chart-tab.ts | 41 ----- .../dock/dock-store/dock-store.injectable.ts | 18 --- .../create-dock-tab-store.injectable.ts | 6 +- .../dock/dock-tab-store/dock-tab.store.ts | 43 ++--- src/renderer/components/dock/dock-tab.tsx | 4 +- src/renderer/components/dock/dock-tabs.tsx | 6 +- src/renderer/components/dock/dock.tsx | 20 +-- .../dock/dock/close-dock-tab.injectable.ts | 25 +++ .../dock/dock/create-dock-tab.injectable.ts | 25 +++ .../dock-storage.injectable.ts | 4 +- .../dock/dock/rename-tab.injectable.ts | 25 +++ .../dock/dock/select-dock-tab.injectable.ts | 25 +++ .../components/dock/dock/store.injectable.ts | 35 ++++ .../dock.store.ts => dock/store.ts} | 18 ++- .../edit-resource-store.injectable.ts | 20 --- .../edit-resource.store.ts | 109 ------------- .../edit-resource-tab.injectable.ts | 19 --- .../edit-resource-tab/edit-resource-tab.ts | 41 ----- .../components/dock/edit-resource.scss | 7 - ...clear-edit-resource-tab-data.injectable.ts | 26 +++ .../edit-resource-tab.injectable.ts | 57 +++++++ .../dock/edit-resource/store.injectable.ts | 17 ++ .../components/dock/edit-resource/store.ts | 57 +++++++ .../view.tsx} | 64 +++++--- src/renderer/components/dock/editor-panel.tsx | 4 +- src/renderer/components/dock/info-panel.tsx | 4 +- ...clear-install-chart-tab-data.injectable.ts | 26 +++ .../create-install-chart-tab.injectable.ts | 51 ++++++ .../{ => install-chart}/install-chart.scss | 0 .../store.injectable.ts} | 10 +- .../store.ts} | 28 +--- .../view.tsx} | 51 +++--- .../__test__/log-resource-selector.test.tsx | 50 ++++-- .../dock/logs/__test__/log-tab.store.test.ts | 23 +-- .../logs/clear-log-tab-data.injectable.ts | 26 +++ .../components/dock/logs/controls.tsx | 8 +- .../dock/logs/create-logs-tab.injectable.ts | 48 ++++++ .../logs/create-pod-logs-tab.injectable.ts | 38 +++++ .../create-workload-logs-tab.injectable.ts | 44 +++++ .../components/dock/logs/dock-tab.tsx | 101 ------------ .../dock/logs/get-log-tab-data.injectable.ts | 14 +- .../get-logs-without-timestamps.injectable.ts | 14 +- .../dock/logs/get-logs.injectable.ts | 14 +- .../get-timestamp-split-logs.injectable.ts | 14 +- .../logs/is-logs-tab-data-valid.injectable.ts | 26 +++ .../dock/logs/load-logs.injectable.ts | 16 +- .../dock/logs/log-tab-data.validator.ts | 26 +++ .../dock/logs/logs-view-model.injectable.ts | 9 +- .../components/dock/logs/logs-view-model.ts | 33 +++- .../dock/logs/reload-logs.injectable.ts | 20 ++- .../dock/logs/resource-selector.tsx | 28 ++-- .../dock/logs/set-log-tab-data.injectable.ts | 14 +- .../dock/logs/stop-loading-logs.injectable.ts | 14 +- .../components/dock/logs/store.injectable.ts | 2 + src/renderer/components/dock/logs/store.ts | 80 +++++---- .../dock/logs/tab-store.injectable.ts | 4 +- .../components/dock/logs/tab-store.ts | 66 ++++++++ .../components/dock/logs/tab.store.ts | 139 ---------------- .../dock/logs/update-tab-name.injectable.ts | 17 +- src/renderer/components/dock/logs/view.tsx | 105 ++++++++++++ .../dock/terminal-store/terminal.store.ts | 152 ------------------ .../components/dock/terminal-tab.scss | 8 - .../components/dock/terminal-window.tsx | 74 --------- .../clear-terminal-tab-data.injectable.ts | 26 +++ .../create-terminal-tab.injectable.ts | 30 ++++ .../terminal/create-terminal.injectable.ts | 13 +- .../dock-tab.tsx} | 34 ++-- .../terminal/get-terminal-api.injectable.ts | 27 ++++ .../dock/terminal/send-command.injectable.ts | 92 +++++++++++ .../store.injectable.ts} | 8 +- .../components/dock/terminal/store.ts | 87 ++++++++++ .../dock/{ => terminal}/terminal-window.scss | 0 .../components/dock/terminal/terminal.ts | 18 +-- .../components/dock/terminal/view.tsx | 74 +++++++++ .../upgrade-chart.store.ts | 128 --------------- ...clear-upgrade-chart-tab-data.injectable.ts | 27 ++++ .../create-upgrade-chart-tab.injectable.ts | 57 +++++++ .../store.injectable.ts} | 16 +- .../components/dock/upgrade-chart/store.ts | 51 ++++++ .../{ => upgrade-chart}/upgrade-chart.scss | 0 .../view.tsx} | 95 ++++++----- .../kube-object-menu.test.tsx | 10 +- .../kube-object-menu/kube-object-menu.tsx | 8 +- .../subscribe-stores.injectable.ts | 13 ++ 105 files changed, 1887 insertions(+), 1455 deletions(-) create mode 100644 src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.ts delete mode 100644 src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-for-extension-api.ts delete mode 100644 src/renderer/components/dock/create-install-chart-tab/create-install-chart-tab.injectable.ts delete mode 100644 src/renderer/components/dock/create-install-chart-tab/create-install-chart-tab.ts delete mode 100644 src/renderer/components/dock/create-resource-tab/create-resource-tab.injectable.ts delete mode 100644 src/renderer/components/dock/create-resource-tab/create-resource-tab.ts delete mode 100644 src/renderer/components/dock/create-resource.scss create mode 100644 src/renderer/components/dock/create-resource/clear-create-resource-tab-data.injectable.ts create mode 100644 src/renderer/components/dock/create-resource/create-resource-tab.injectable.ts rename src/renderer/components/dock/{create-resource-store/create-resource-store.injectable.ts => create-resource/store.injectable.ts} (55%) rename src/renderer/components/dock/{create-resource-store/create-resource.store.ts => create-resource/store.ts} (91%) rename src/renderer/components/dock/{create-resource.tsx => create-resource/view.tsx} (73%) delete mode 100644 src/renderer/components/dock/create-terminal-tab/create-terminal-tab.injectable.ts delete mode 100644 src/renderer/components/dock/create-terminal-tab/create-terminal-tab.ts delete mode 100644 src/renderer/components/dock/create-upgrade-chart-tab/create-upgrade-chart-tab.injectable.ts delete mode 100644 src/renderer/components/dock/create-upgrade-chart-tab/create-upgrade-chart-tab.ts delete mode 100644 src/renderer/components/dock/dock-store/dock-store.injectable.ts create mode 100644 src/renderer/components/dock/dock/close-dock-tab.injectable.ts create mode 100644 src/renderer/components/dock/dock/create-dock-tab.injectable.ts rename src/renderer/components/dock/{dock-store/dock-storage => dock}/dock-storage.injectable.ts (81%) create mode 100644 src/renderer/components/dock/dock/rename-tab.injectable.ts create mode 100644 src/renderer/components/dock/dock/select-dock-tab.injectable.ts create mode 100644 src/renderer/components/dock/dock/store.injectable.ts rename src/renderer/components/dock/{dock-store/dock.store.ts => dock/store.ts} (93%) delete mode 100644 src/renderer/components/dock/edit-resource-store/edit-resource-store.injectable.ts delete mode 100644 src/renderer/components/dock/edit-resource-store/edit-resource.store.ts delete mode 100644 src/renderer/components/dock/edit-resource-tab/edit-resource-tab.injectable.ts delete mode 100644 src/renderer/components/dock/edit-resource-tab/edit-resource-tab.ts delete mode 100644 src/renderer/components/dock/edit-resource.scss create mode 100644 src/renderer/components/dock/edit-resource/clear-edit-resource-tab-data.injectable.ts create mode 100644 src/renderer/components/dock/edit-resource/edit-resource-tab.injectable.ts create mode 100644 src/renderer/components/dock/edit-resource/store.injectable.ts create mode 100644 src/renderer/components/dock/edit-resource/store.ts rename src/renderer/components/dock/{edit-resource.tsx => edit-resource/view.tsx} (67%) create mode 100644 src/renderer/components/dock/install-chart/clear-install-chart-tab-data.injectable.ts create mode 100644 src/renderer/components/dock/install-chart/create-install-chart-tab.injectable.ts rename src/renderer/components/dock/{ => install-chart}/install-chart.scss (100%) rename src/renderer/components/dock/{install-chart-store/install-chart-store.injectable.ts => install-chart/store.injectable.ts} (73%) rename src/renderer/components/dock/{install-chart-store/install-chart.store.ts => install-chart/store.ts} (72%) rename src/renderer/components/dock/{install-chart.tsx => install-chart/view.tsx} (79%) create mode 100644 src/renderer/components/dock/logs/clear-log-tab-data.injectable.ts create mode 100644 src/renderer/components/dock/logs/create-logs-tab.injectable.ts create mode 100644 src/renderer/components/dock/logs/create-pod-logs-tab.injectable.ts create mode 100644 src/renderer/components/dock/logs/create-workload-logs-tab.injectable.ts delete mode 100644 src/renderer/components/dock/logs/dock-tab.tsx create mode 100644 src/renderer/components/dock/logs/is-logs-tab-data-valid.injectable.ts create mode 100644 src/renderer/components/dock/logs/log-tab-data.validator.ts create mode 100644 src/renderer/components/dock/logs/tab-store.ts delete mode 100644 src/renderer/components/dock/logs/tab.store.ts create mode 100644 src/renderer/components/dock/logs/view.tsx delete mode 100644 src/renderer/components/dock/terminal-store/terminal.store.ts delete mode 100644 src/renderer/components/dock/terminal-tab.scss delete mode 100644 src/renderer/components/dock/terminal-window.tsx create mode 100644 src/renderer/components/dock/terminal/clear-terminal-tab-data.injectable.ts create mode 100644 src/renderer/components/dock/terminal/create-terminal-tab.injectable.ts rename src/renderer/components/dock/{terminal-tab.tsx => terminal/dock-tab.tsx} (70%) create mode 100644 src/renderer/components/dock/terminal/get-terminal-api.injectable.ts create mode 100644 src/renderer/components/dock/terminal/send-command.injectable.ts rename src/renderer/components/dock/{terminal-store/terminal-store.injectable.ts => terminal/store.injectable.ts} (52%) create mode 100644 src/renderer/components/dock/terminal/store.ts rename src/renderer/components/dock/{ => terminal}/terminal-window.scss (100%) create mode 100644 src/renderer/components/dock/terminal/view.tsx delete mode 100644 src/renderer/components/dock/upgrade-chart-store/upgrade-chart.store.ts create mode 100644 src/renderer/components/dock/upgrade-chart/clear-upgrade-chart-tab-data.injectable.ts create mode 100644 src/renderer/components/dock/upgrade-chart/create-upgrade-chart-tab.injectable.ts rename src/renderer/components/dock/{upgrade-chart-store/upgrade-chart-store.injectable.ts => upgrade-chart/store.injectable.ts} (55%) create mode 100644 src/renderer/components/dock/upgrade-chart/store.ts rename src/renderer/components/dock/{ => upgrade-chart}/upgrade-chart.scss (100%) rename src/renderer/components/dock/{upgrade-chart.tsx => upgrade-chart/view.tsx} (64%) create mode 100644 src/renderer/kube-watch-api/subscribe-stores.injectable.ts diff --git a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.ts b/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.ts new file mode 100644 index 0000000000..4d79cb9b42 --- /dev/null +++ b/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { Injectable } from "@ogre-tools/injectable"; +import { getLegacyGlobalDiForExtensionApi } from "./legacy-global-di-for-extension-api"; + +type TentativeTuple = T extends object ? [T] : [undefined?]; + +export const asLegacyGlobalObjectForExtensionApiWithModifications = < + TInjectable extends Injectable, + TInstantiationParameter, +>( + injectableKey: TInjectable, + otherFields: Record any>, + ...instantiationParameter: TentativeTuple + ) => + new Proxy( + {}, + { + get(target, propertyName) { + if (propertyName === "$$typeof") { + return undefined; + } + + const instance: any = getLegacyGlobalDiForExtensionApi().inject( + injectableKey, + ...instantiationParameter, + ); + + const propertyValue = instance[propertyName] ?? otherFields[propertyName]?.(); + + if (typeof propertyValue === "function") { + return function (...args: any[]) { + return propertyValue.apply(instance, args); + }; + } + + return propertyValue; + }, + }, + ) as ReturnType; diff --git a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-for-extension-api.ts b/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-for-extension-api.ts deleted file mode 100644 index 4e77961ca8..0000000000 --- a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-for-extension-api.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { Injectable } from "@ogre-tools/injectable"; -import { getLegacyGlobalDiForExtensionApi } from "./legacy-global-di-for-extension-api"; - -type TentativeTuple = T extends object ? [T] : [undefined?]; - -export const asLegacyGlobalSingletonForExtensionApi = < - TClass extends abstract new (...args: any[]) => any, - TInjectable extends Injectable, - TInstantiationParameter, ->( - Class: TClass, - injectableKey: TInjectable, - ...instantiationParameter: TentativeTuple - ) => - new Proxy(Class, { - construct: () => { - throw new Error("A legacy singleton class must be created by createInstance()"); - }, - - get: (target: any, propertyName) => { - if (propertyName === "getInstance" || propertyName === "createInstance") { - return () => - getLegacyGlobalDiForExtensionApi().inject( - injectableKey, - ...instantiationParameter, - ); - } - - if (propertyName === "resetInstance") { - return () => getLegacyGlobalDiForExtensionApi().purge(injectableKey); - } - - return target[propertyName]; - }, - }) as InstanceType & { - getInstance: () => InstanceType; - createInstance: () => InstanceType; - resetInstance: () => void; - }; diff --git a/src/extensions/renderer-api/components.ts b/src/extensions/renderer-api/components.ts index 54a0f2d2d1..dfabddd7ec 100644 --- a/src/extensions/renderer-api/components.ts +++ b/src/extensions/renderer-api/components.ts @@ -3,14 +3,17 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; -import createTerminalTabInjectable from "../../renderer/components/dock/create-terminal-tab/create-terminal-tab.injectable"; -import terminalStoreInjectable from "../../renderer/components/dock/terminal-store/terminal-store.injectable"; +import createTerminalTabInjectable from "../../renderer/components/dock/terminal/create-terminal-tab.injectable"; +import terminalStoreInjectable from "../../renderer/components/dock/terminal/store.injectable"; import { asLegacyGlobalObjectForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; import logTabStoreInjectable from "../../renderer/components/dock/logs/tab-store.injectable"; -import { asLegacyGlobalSingletonForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-singleton-for-extension-api"; -import { TerminalStore as TerminalStoreClass } from "../../renderer/components/dock/terminal-store/terminal.store"; import commandOverlayInjectable from "../../renderer/components/command-palette/command-overlay.injectable"; +import { asLegacyGlobalObjectForExtensionApiWithModifications } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications"; +import createPodLogsTabInjectable from "../../renderer/components/dock/logs/create-pod-logs-tab.injectable"; +import createWorkloadLogsTabInjectable from "../../renderer/components/dock/logs/create-workload-logs-tab.injectable"; +import updateTabNameInjectable from "../../renderer/components/dock/logs/update-tab-name.injectable"; +import sendCommandInjectable from "../../renderer/components/dock/terminal/send-command.injectable"; // layouts export * from "../../renderer/components/layout/main-layout"; @@ -71,7 +74,24 @@ export * from "../../renderer/components/+events/kube-event-details"; export * from "../../renderer/components/status-brick"; export const createTerminalTab = asLegacyGlobalFunctionForExtensionApi(createTerminalTabInjectable); -export const TerminalStore = asLegacyGlobalSingletonForExtensionApi(TerminalStoreClass, terminalStoreInjectable); -export const terminalStore = asLegacyGlobalObjectForExtensionApi(terminalStoreInjectable); -export const logTabStore = asLegacyGlobalObjectForExtensionApi(logTabStoreInjectable); +export const terminalStore = asLegacyGlobalObjectForExtensionApiWithModifications(terminalStoreInjectable, { + sendCommand: () => asLegacyGlobalFunctionForExtensionApi(sendCommandInjectable), +}); +export const logTabStore = asLegacyGlobalObjectForExtensionApiWithModifications(logTabStoreInjectable, { + createPodTab: () => asLegacyGlobalFunctionForExtensionApi(createPodLogsTabInjectable), + createWorkloadTab: () => asLegacyGlobalFunctionForExtensionApi(createWorkloadLogsTabInjectable), + renameTab: () => asLegacyGlobalFunctionForExtensionApi(updateTabNameInjectable), + tabs: () => undefined, +}); +export class TerminalStore { + static getInstance() { + return terminalStore; + } + static createInstance() { + return terminalStore; + } + static resetInstance() { + console.warn("TerminalStore.resetInstance() does nothing"); + } +} diff --git a/src/renderer/components/+apps-helm-charts/helm-chart-details.tsx b/src/renderer/components/+apps-helm-charts/helm-chart-details.tsx index 936859dbd0..ff7d929e8f 100644 --- a/src/renderer/components/+apps-helm-charts/helm-chart-details.tsx +++ b/src/renderer/components/+apps-helm-charts/helm-chart-details.tsx @@ -18,7 +18,7 @@ import { Select, SelectOption } from "../select"; import { Badge } from "../badge"; import { Tooltip, withStyles } from "@material-ui/core"; import { withInjectables } from "@ogre-tools/injectable-react"; -import createInstallChartTabInjectable from "../dock/create-install-chart-tab/create-install-chart-tab.injectable"; +import createInstallChartTabInjectable from "../dock/install-chart/create-install-chart-tab.injectable"; interface Props { chart: HelmChart; diff --git a/src/renderer/components/+apps-releases/release-details/release-details.tsx b/src/renderer/components/+apps-releases/release-details/release-details.tsx index 77f6ab2d87..30d34e1857 100644 --- a/src/renderer/components/+apps-releases/release-details/release-details.tsx +++ b/src/renderer/components/+apps-releases/release-details/release-details.tsx @@ -27,7 +27,7 @@ import { getDetailsUrl } from "../../kube-detail-params"; import { Checkbox } from "../../checkbox"; import { MonacoEditor } from "../../monaco-editor"; import { IAsyncComputed, withInjectables } from "@ogre-tools/injectable-react"; -import createUpgradeChartTabInjectable from "../../dock/create-upgrade-chart-tab/create-upgrade-chart-tab.injectable"; +import createUpgradeChartTabInjectable from "../../dock/upgrade-chart/create-upgrade-chart-tab.injectable"; import updateReleaseInjectable from "../update-release/update-release.injectable"; import releaseInjectable from "./release.injectable"; import releaseDetailsInjectable from "./release-details.injectable"; diff --git a/src/renderer/components/+apps-releases/release-menu.tsx b/src/renderer/components/+apps-releases/release-menu.tsx index a1dc263806..30824dba15 100644 --- a/src/renderer/components/+apps-releases/release-menu.tsx +++ b/src/renderer/components/+apps-releases/release-menu.tsx @@ -10,7 +10,7 @@ import { MenuActions, MenuActionsProps } from "../menu/menu-actions"; import { MenuItem } from "../menu"; import { Icon } from "../icon"; import { withInjectables } from "@ogre-tools/injectable-react"; -import createUpgradeChartTabInjectable from "../dock/create-upgrade-chart-tab/create-upgrade-chart-tab.injectable"; +import createUpgradeChartTabInjectable from "../dock/upgrade-chart/create-upgrade-chart-tab.injectable"; import releaseRollbackDialogModelInjectable from "./release-rollback-dialog-model/release-rollback-dialog-model.injectable"; import deleteReleaseInjectable from "./delete-release/delete-release.injectable"; diff --git a/src/renderer/components/command-palette/registered-commands/internal-commands.injectable.tsx b/src/renderer/components/command-palette/registered-commands/internal-commands.injectable.tsx index e1a3d7c46d..76e8ed7743 100644 --- a/src/renderer/components/command-palette/registered-commands/internal-commands.injectable.tsx +++ b/src/renderer/components/command-palette/registered-commands/internal-commands.injectable.tsx @@ -14,9 +14,8 @@ import { ActivateEntityCommand } from "../../activate-entity-command"; import type { CommandContext, CommandRegistration } from "./commands"; import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; import commandOverlayInjectable from "../command-overlay.injectable"; -import createTerminalTabInjectable - from "../../dock/create-terminal-tab/create-terminal-tab.injectable"; -import type { DockTabCreate } from "../../dock/dock-store/dock.store"; +import createTerminalTabInjectable from "../../dock/terminal/create-terminal-tab.injectable"; +import type { DockTabCreate } from "../../dock/dock/store"; export function isKubernetesClusterActive(context: CommandContext): boolean { return context.entity?.kind === "KubernetesCluster"; diff --git a/src/renderer/components/dock/__test__/dock-tabs.test.tsx b/src/renderer/components/dock/__test__/dock-tabs.test.tsx index 705680317f..4be169bf7f 100644 --- a/src/renderer/components/dock/__test__/dock-tabs.test.tsx +++ b/src/renderer/components/dock/__test__/dock-tabs.test.tsx @@ -8,12 +8,12 @@ import { fireEvent } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; import fse from "fs-extra"; import { DockTabs } from "../dock-tabs"; -import { DockStore, DockTab, TabKind } from "../dock-store/dock.store"; +import { DockStore, DockTab, TabKind } from "../dock/store"; import { noop } from "../../../utils"; import { ThemeStore } from "../../../theme.store"; import { UserStore } from "../../../../common/user-store"; import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import dockStoreInjectable from "../dock-store/dock-store.injectable"; +import dockStoreInjectable from "../dock/store.injectable"; import type { DiRender } from "../../test-utils/renderFor"; import { renderFor } from "../../test-utils/renderFor"; import directoryForUserDataInjectable diff --git a/src/renderer/components/dock/create-install-chart-tab/create-install-chart-tab.injectable.ts b/src/renderer/components/dock/create-install-chart-tab/create-install-chart-tab.injectable.ts deleted file mode 100644 index 8544cff719..0000000000 --- a/src/renderer/components/dock/create-install-chart-tab/create-install-chart-tab.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { createInstallChartTab } from "./create-install-chart-tab"; -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import installChartStoreInjectable from "../install-chart-store/install-chart-store.injectable"; -import dockStoreInjectable from "../dock-store/dock-store.injectable"; - -const createInstallChartTabInjectable = getInjectable({ - instantiate: (di) => createInstallChartTab({ - installChartStore: di.inject(installChartStoreInjectable), - createDockTab: di.inject(dockStoreInjectable).createTab, - }), - - lifecycle: lifecycleEnum.singleton, -}); - -export default createInstallChartTabInjectable; diff --git a/src/renderer/components/dock/create-install-chart-tab/create-install-chart-tab.ts b/src/renderer/components/dock/create-install-chart-tab/create-install-chart-tab.ts deleted file mode 100644 index d4f6aba833..0000000000 --- a/src/renderer/components/dock/create-install-chart-tab/create-install-chart-tab.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { HelmChart } from "../../../../common/k8s-api/endpoints/helm-charts.api"; -import { - DockTab, - DockTabCreate, - DockTabCreateSpecific, - TabKind, -} from "../dock-store/dock.store"; - -import type { InstallChartStore } from "../install-chart-store/install-chart.store"; - -interface Dependencies { - createDockTab: (rawTab: DockTabCreate, addNumber: boolean) => DockTab; - installChartStore: InstallChartStore; -} - -export const createInstallChartTab = - ({ createDockTab, installChartStore }: Dependencies) => - (chart: HelmChart, tabParams: DockTabCreateSpecific = {}) => { - const { name, repo, version } = chart; - - const tab = createDockTab( - { - title: `Helm Install: ${repo}/${name}`, - ...tabParams, - kind: TabKind.INSTALL_CHART, - }, - false, - ); - - installChartStore.setData(tab.id, { - name, - repo, - version, - namespace: "default", - releaseName: "", - description: "", - }); - - return tab; - }; diff --git a/src/renderer/components/dock/create-resource-tab/create-resource-tab.injectable.ts b/src/renderer/components/dock/create-resource-tab/create-resource-tab.injectable.ts deleted file mode 100644 index daacd34c33..0000000000 --- a/src/renderer/components/dock/create-resource-tab/create-resource-tab.injectable.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { createResourceTab } from "./create-resource-tab"; -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import dockStoreInjectable from "../dock-store/dock-store.injectable"; - -const createResourceTabInjectable = getInjectable({ - instantiate: (di) => createResourceTab({ - dockStore: di.inject(dockStoreInjectable), - }), - - lifecycle: lifecycleEnum.singleton, -}); - -export default createResourceTabInjectable; diff --git a/src/renderer/components/dock/create-resource-tab/create-resource-tab.ts b/src/renderer/components/dock/create-resource-tab/create-resource-tab.ts deleted file mode 100644 index bea9dc05aa..0000000000 --- a/src/renderer/components/dock/create-resource-tab/create-resource-tab.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { DockStore, DockTabCreateSpecific, TabKind } from "../dock-store/dock.store"; - -interface Dependencies { - dockStore: DockStore -} - -export const createResourceTab = - ({ dockStore }: Dependencies) => - (tabParams: DockTabCreateSpecific = {}) => - dockStore.createTab({ - title: "Create resource", - ...tabParams, - kind: TabKind.CREATE_RESOURCE, - }); diff --git a/src/renderer/components/dock/create-resource.scss b/src/renderer/components/dock/create-resource.scss deleted file mode 100644 index 7191de3134..0000000000 --- a/src/renderer/components/dock/create-resource.scss +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.CreateResource { -} diff --git a/src/renderer/components/dock/create-resource/clear-create-resource-tab-data.injectable.ts b/src/renderer/components/dock/create-resource/clear-create-resource-tab-data.injectable.ts new file mode 100644 index 0000000000..f0936ff270 --- /dev/null +++ b/src/renderer/components/dock/create-resource/clear-create-resource-tab-data.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { TabId } from "../dock/store"; +import type { CreateResourceTabStore } from "./store"; +import createResourceTabStoreInjectable from "./store.injectable"; + +interface Dependencies { + createResourceTabStore: CreateResourceTabStore; +} + +function clearCreateResourceTabData({ createResourceTabStore }: Dependencies, tabId: TabId): void { + createResourceTabStore.clearData(tabId); +} + +const clearCreateResourceTabDataInjectable = getInjectable({ + instantiate: (di) => bind(clearCreateResourceTabData, null, { + createResourceTabStore: di.inject(createResourceTabStoreInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default clearCreateResourceTabDataInjectable; diff --git a/src/renderer/components/dock/create-resource/create-resource-tab.injectable.ts b/src/renderer/components/dock/create-resource/create-resource-tab.injectable.ts new file mode 100644 index 0000000000..540c1739c0 --- /dev/null +++ b/src/renderer/components/dock/create-resource/create-resource-tab.injectable.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import dockStoreInjectable from "../dock/store.injectable"; +import { DockStore, DockTabCreateSpecific, TabKind } from "../dock/store"; + +interface Dependencies { + dockStore: DockStore +} + +function createResourceTab({ dockStore }: Dependencies, tabParams: DockTabCreateSpecific = {}) { + return dockStore.createTab({ + title: "Create resource", + ...tabParams, + kind: TabKind.CREATE_RESOURCE, + }); +} + +const createResourceTabInjectable = getInjectable({ + instantiate: (di) => bind(createResourceTab, null, { + dockStore: di.inject(dockStoreInjectable), + }), + + lifecycle: lifecycleEnum.singleton, +}); + +export default createResourceTabInjectable; diff --git a/src/renderer/components/dock/create-resource-store/create-resource-store.injectable.ts b/src/renderer/components/dock/create-resource/store.injectable.ts similarity index 55% rename from src/renderer/components/dock/create-resource-store/create-resource-store.injectable.ts rename to src/renderer/components/dock/create-resource/store.injectable.ts index 258b1469e7..0b005f96f9 100644 --- a/src/renderer/components/dock/create-resource-store/create-resource-store.injectable.ts +++ b/src/renderer/components/dock/create-resource/store.injectable.ts @@ -3,17 +3,15 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import dockStoreInjectable from "../dock-store/dock-store.injectable"; -import { CreateResourceStore } from "./create-resource.store"; +import { CreateResourceTabStore } from "./store"; import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; -const createResourceStoreInjectable = getInjectable({ - instantiate: (di) => new CreateResourceStore({ - dockStore: di.inject(dockStoreInjectable), +const createResourceTabStoreInjectable = getInjectable({ + instantiate: (di) => new CreateResourceTabStore({ createStorage: di.inject(createStorageInjectable), }), lifecycle: lifecycleEnum.singleton, }); -export default createResourceStoreInjectable; +export default createResourceTabStoreInjectable; diff --git a/src/renderer/components/dock/create-resource-store/create-resource.store.ts b/src/renderer/components/dock/create-resource/store.ts similarity index 91% rename from src/renderer/components/dock/create-resource-store/create-resource.store.ts rename to src/renderer/components/dock/create-resource/store.ts index 7c0c62f30b..1a3663301d 100644 --- a/src/renderer/components/dock/create-resource-store/create-resource.store.ts +++ b/src/renderer/components/dock/create-resource/store.ts @@ -11,14 +11,12 @@ import filehound from "filehound"; import { watch } from "chokidar"; import { autoBind, StorageHelper } from "../../../utils"; import { DockTabStorageState, DockTabStore } from "../dock-tab-store/dock-tab.store"; -import type { DockStore } from "../dock-store/dock.store"; interface Dependencies { - dockStore: DockStore, - createStorage: (storageKey: string, options: DockTabStorageState) => StorageHelper> + createStorage: (storageKey: string, options: DockTabStorageState) => StorageHelper>; } -export class CreateResourceStore extends DockTabStore { +export class CreateResourceTabStore extends DockTabStore { constructor(protected dependencies: Dependencies) { super(dependencies, { storageKey: "create_resource", diff --git a/src/renderer/components/dock/create-resource.tsx b/src/renderer/components/dock/create-resource/view.tsx similarity index 73% rename from src/renderer/components/dock/create-resource.tsx rename to src/renderer/components/dock/create-resource/view.tsx index 2b6728ede6..4c6b3035b6 100644 --- a/src/renderer/components/dock/create-resource.tsx +++ b/src/renderer/components/dock/create-resource/view.tsx @@ -3,37 +3,35 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import "./create-resource.scss"; - import React from "react"; import path from "path"; import fs from "fs-extra"; -import { GroupSelectOption, Select, SelectOption } from "../select"; +import { GroupSelectOption, Select, SelectOption } from "../../select"; import yaml from "js-yaml"; import { makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; -import type { CreateResourceStore } from "./create-resource-store/create-resource.store"; -import type { DockTab } from "./dock-store/dock.store"; -import { EditorPanel } from "./editor-panel"; -import { InfoPanel } from "./info-panel"; -import * as resourceApplierApi from "../../../common/k8s-api/endpoints/resource-applier.api"; -import { Notifications } from "../notifications"; -import logger from "../../../common/logger"; -import type { KubeJsonApiData } from "../../../common/k8s-api/kube-json-api"; -import { getDetailsUrl } from "../kube-detail-params"; -import { apiManager } from "../../../common/k8s-api/api-manager"; -import { prevDefault } from "../../utils"; -import { navigate } from "../../navigation"; +import type { CreateResourceTabStore } from "./store"; +import type { DockTab } from "../dock/store"; +import { EditorPanel } from "../editor-panel"; +import { InfoPanel } from "../info-panel"; +import * as resourceApplierApi from "../../../../common/k8s-api/endpoints/resource-applier.api"; +import { Notifications } from "../../notifications"; +import logger from "../../../../common/logger"; +import type { KubeJsonApiData } from "../../../../common/k8s-api/kube-json-api"; +import { getDetailsUrl } from "../../kube-detail-params"; +import { apiManager } from "../../../../common/k8s-api/api-manager"; +import { prevDefault } from "../../../utils"; +import { navigate } from "../../../navigation"; import { withInjectables } from "@ogre-tools/injectable-react"; -import createResourceStoreInjectable - from "./create-resource-store/create-resource-store.injectable"; +import createResourceTabStoreInjectable + from "./store.injectable"; interface Props { tab: DockTab; } interface Dependencies { - createResourceStore: CreateResourceStore + createResourceTabStore: CreateResourceTabStore; } @observer @@ -48,8 +46,8 @@ class NonInjectedCreateResource extends React.Component { } componentDidMount() { - this.props.createResourceStore.getMergedTemplates().then(v => this.updateGroupSelectOptions(v)); - this.props.createResourceStore.watchUserTemplates(() => this.props.createResourceStore.getMergedTemplates().then(v => this.updateGroupSelectOptions(v))); + this.props.createResourceTabStore.getMergedTemplates().then(v => this.updateGroupSelectOptions(v)); + this.props.createResourceTabStore.watchUserTemplates(() => this.props.createResourceTabStore.getMergedTemplates().then(v => this.updateGroupSelectOptions(v))); } updateGroupSelectOptions(templates: Record) { @@ -68,7 +66,7 @@ class NonInjectedCreateResource extends React.Component { } get data() { - return this.props.createResourceStore.getData(this.tabId); + return this.props.createResourceTabStore.getData(this.tabId); } get currentTemplate() { @@ -77,7 +75,7 @@ class NonInjectedCreateResource extends React.Component { onChange = (value: string) => { this.error = ""; // reset first, validation goes later - this.props.createResourceStore.setData(this.tabId, value); + this.props.createResourceTabStore.setData(this.tabId, value); }; onError = (error: Error | string) => { @@ -87,7 +85,7 @@ class NonInjectedCreateResource extends React.Component { onSelectTemplate = (item: SelectOption) => { this.currentTemplates.set(this.tabId, item); fs.readFile(item.value, "utf8").then(v => { - this.props.createResourceStore.setData(this.tabId, v); + this.props.createResourceTabStore.setData(this.tabId, v); }); }; @@ -177,7 +175,7 @@ export const CreateResource = withInjectables( { getProps: (di, props) => ({ - createResourceStore: di.inject(createResourceStoreInjectable), + createResourceTabStore: di.inject(createResourceTabStoreInjectable), ...props, }), }, diff --git a/src/renderer/components/dock/create-terminal-tab/create-terminal-tab.injectable.ts b/src/renderer/components/dock/create-terminal-tab/create-terminal-tab.injectable.ts deleted file mode 100644 index e22ceaf273..0000000000 --- a/src/renderer/components/dock/create-terminal-tab/create-terminal-tab.injectable.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { createTerminalTab } from "./create-terminal-tab"; -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import dockStoreInjectable from "../dock-store/dock-store.injectable"; - -const createTerminalTabInjectable = getInjectable({ - instantiate: (di) => createTerminalTab({ - dockStore: di.inject(dockStoreInjectable), - }), - - lifecycle: lifecycleEnum.singleton, -}); - -export default createTerminalTabInjectable; diff --git a/src/renderer/components/dock/create-terminal-tab/create-terminal-tab.ts b/src/renderer/components/dock/create-terminal-tab/create-terminal-tab.ts deleted file mode 100644 index cd3c501158..0000000000 --- a/src/renderer/components/dock/create-terminal-tab/create-terminal-tab.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { - DockStore, - DockTabCreateSpecific, - TabKind, -} from "../dock-store/dock.store"; - -interface Dependencies { - dockStore: DockStore; -} - -export const createTerminalTab = - ({ dockStore }: Dependencies) => - (tabParams: DockTabCreateSpecific = {}) => - dockStore.createTab({ - title: `Terminal`, - ...tabParams, - kind: TabKind.TERMINAL, - }); diff --git a/src/renderer/components/dock/create-upgrade-chart-tab/create-upgrade-chart-tab.injectable.ts b/src/renderer/components/dock/create-upgrade-chart-tab/create-upgrade-chart-tab.injectable.ts deleted file mode 100644 index 0ba73c3abe..0000000000 --- a/src/renderer/components/dock/create-upgrade-chart-tab/create-upgrade-chart-tab.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { createUpgradeChartTab } from "./create-upgrade-chart-tab"; -import upgradeChartStoreInjectable from "../upgrade-chart-store/upgrade-chart-store.injectable"; -import dockStoreInjectable from "../dock-store/dock-store.injectable"; - -const createUpgradeChartTabInjectable = getInjectable({ - instantiate: (di) => createUpgradeChartTab({ - upgradeChartStore: di.inject(upgradeChartStoreInjectable), - dockStore: di.inject(dockStoreInjectable), - }), - - lifecycle: lifecycleEnum.singleton, -}); - -export default createUpgradeChartTabInjectable; diff --git a/src/renderer/components/dock/create-upgrade-chart-tab/create-upgrade-chart-tab.ts b/src/renderer/components/dock/create-upgrade-chart-tab/create-upgrade-chart-tab.ts deleted file mode 100644 index 17c94c6741..0000000000 --- a/src/renderer/components/dock/create-upgrade-chart-tab/create-upgrade-chart-tab.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api"; -import { DockStore, DockTabCreateSpecific, TabKind } from "../dock-store/dock.store"; -import type { UpgradeChartStore } from "../upgrade-chart-store/upgrade-chart.store"; - -interface Dependencies { - upgradeChartStore: UpgradeChartStore; - dockStore: DockStore -} - -export const createUpgradeChartTab = - ({ upgradeChartStore, dockStore }: Dependencies) => - (release: HelmRelease, tabParams: DockTabCreateSpecific = {}) => { - let tab = upgradeChartStore.getTabByRelease(release.getName()); - - if (tab) { - dockStore.open(); - dockStore.selectTab(tab.id); - } - - if (!tab) { - tab = dockStore.createTab( - { - title: `Helm Upgrade: ${release.getName()}`, - ...tabParams, - kind: TabKind.UPGRADE_CHART, - }, - false, - ); - - upgradeChartStore.setData(tab.id, { - releaseName: release.getName(), - releaseNamespace: release.getNs(), - }); - } - - return tab; - }; diff --git a/src/renderer/components/dock/dock-store/dock-store.injectable.ts b/src/renderer/components/dock/dock-store/dock-store.injectable.ts deleted file mode 100644 index 19a47dead6..0000000000 --- a/src/renderer/components/dock/dock-store/dock-store.injectable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { DockStore } from "./dock.store"; -import dockStorageInjectable from "./dock-storage/dock-storage.injectable"; - -const dockStoreInjectable = getInjectable({ - instantiate: (di) => - new DockStore({ - storage: di.inject(dockStorageInjectable), - }), - - lifecycle: lifecycleEnum.singleton, -}); - -export default dockStoreInjectable; diff --git a/src/renderer/components/dock/dock-tab-store/create-dock-tab-store.injectable.ts b/src/renderer/components/dock/dock-tab-store/create-dock-tab-store.injectable.ts index 19b5af4084..d91fe65df0 100644 --- a/src/renderer/components/dock/dock-tab-store/create-dock-tab-store.injectable.ts +++ b/src/renderer/components/dock/dock-tab-store/create-dock-tab-store.injectable.ts @@ -3,14 +3,12 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { DockTabStore, DockTabStoreOptions } from "./dock-tab.store"; -import dockStoreInjectable from "../dock-store/dock-store.injectable"; +import { DockTabStore, DockTabStoreDependencies, DockTabStoreOptions } from "./dock-tab.store"; import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; const createDockTabStoreInjectable = getInjectable({ instantiate: (di) => { - const dependencies = { - dockStore: di.inject(dockStoreInjectable), + const dependencies: DockTabStoreDependencies = { createStorage: di.inject(createStorageInjectable), }; diff --git a/src/renderer/components/dock/dock-tab-store/dock-tab.store.ts b/src/renderer/components/dock/dock-tab-store/dock-tab.store.ts index 5bfcdd504c..3364ebe936 100644 --- a/src/renderer/components/dock/dock-tab-store/dock-tab.store.ts +++ b/src/renderer/components/dock/dock-tab-store/dock-tab.store.ts @@ -3,9 +3,9 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { autorun, observable, reaction } from "mobx"; +import { action, observable, reaction } from "mobx"; import { autoBind, StorageHelper, toJS } from "../../../utils"; -import type { DockStore, TabId } from "../dock-store/dock.store"; +import type { TabId } from "../dock/store"; export interface DockTabStoreOptions { autoInit?: boolean; // load data from storage when `storageKey` is provided and bind events, default: true @@ -14,16 +14,15 @@ export interface DockTabStoreOptions { export type DockTabStorageState = Record; -interface Dependencies { - dockStore: DockStore +export interface DockTabStoreDependencies { createStorage: (storageKey: string, options: DockTabStorageState) => StorageHelper> } export class DockTabStore { protected storage?: StorageHelper>; - protected data = observable.map(); + private data = observable.map(); - constructor(protected dependencies: Dependencies, protected options: DockTabStoreOptions) { + constructor(protected dependencies: DockTabStoreDependencies, protected options: DockTabStoreOptions) { autoBind(this); this.options = { @@ -48,17 +47,6 @@ export class DockTabStore { reaction(() => this.toJSON(), data => this.storage.set(data)); }); } - - // clear data for closed tabs - autorun(() => { - const currentTabs = this.dependencies.dockStore.tabs.map(tab => tab.id); - - Array.from(this.data.keys()).forEach(tabId => { - if (!currentTabs.includes(tabId)) { - this.clearData(tabId); - } - }); - }); } protected finalizeDataForSave(data: T): T { @@ -75,8 +63,22 @@ export class DockTabStore { return Object.fromEntries(deepCopy); } + protected getAllData() { + return this.data.toJSON(); + } + + findTabIdFromData(inspecter: (val: T) => any): TabId | undefined { + for (const [tabId, data] of this.data) { + if (inspecter(data)) { + return tabId; + } + } + + return undefined; + } + isReady(tabId: TabId): boolean { - return Boolean(this.getData(tabId) !== undefined); + return this.getData(tabId) !== undefined; } getData(tabId: TabId) { @@ -91,8 +93,11 @@ export class DockTabStore { this.data.delete(tabId); } + @action reset() { - this.data.clear(); + for (const tabId of this.data.keys()) { + this.clearData(tabId); + } this.storage?.reset(); } } diff --git a/src/renderer/components/dock/dock-tab.tsx b/src/renderer/components/dock/dock-tab.tsx index f5d7be54c2..4b91c609ba 100644 --- a/src/renderer/components/dock/dock-tab.tsx +++ b/src/renderer/components/dock/dock-tab.tsx @@ -8,14 +8,14 @@ import "./dock-tab.scss"; import React from "react"; import { observer } from "mobx-react"; import { boundMethod, cssNames, prevDefault, isMiddleClick } from "../../utils"; -import type { DockStore, DockTab as DockTabModel } from "./dock-store/dock.store"; +import type { DockStore, DockTab as DockTabModel } from "./dock/store"; import { Tab, TabProps } from "../tabs"; import { Icon } from "../icon"; import { Menu, MenuItem } from "../menu"; import { observable, makeObservable } from "mobx"; import { isMac } from "../../../common/vars"; import { withInjectables } from "@ogre-tools/injectable-react"; -import dockStoreInjectable from "./dock-store/dock-store.injectable"; +import dockStoreInjectable from "./dock/store.injectable"; export interface DockTabProps extends TabProps { moreActions?: React.ReactNode; diff --git a/src/renderer/components/dock/dock-tabs.tsx b/src/renderer/components/dock/dock-tabs.tsx index 8902fe439a..d4443cf6c0 100644 --- a/src/renderer/components/dock/dock-tabs.tsx +++ b/src/renderer/components/dock/dock-tabs.tsx @@ -8,9 +8,9 @@ import React, { Fragment } from "react"; import { Icon } from "../icon"; import { Tabs } from "../tabs/tabs"; import { DockTab } from "./dock-tab"; -import type { DockTab as DockTabModel } from "./dock-store/dock.store"; -import { TabKind } from "./dock-store/dock.store"; -import { TerminalTab } from "./terminal-tab"; +import type { DockTab as DockTabModel } from "./dock/store"; +import { TabKind } from "./dock/store"; +import { TerminalTab } from "./terminal/dock-tab"; interface Props { tabs: DockTabModel[] diff --git a/src/renderer/components/dock/dock.tsx b/src/renderer/components/dock/dock.tsx index dcc655c60d..c8b481b859 100644 --- a/src/renderer/components/dock/dock.tsx +++ b/src/renderer/components/dock/dock.tsx @@ -13,18 +13,18 @@ import { Icon } from "../icon"; import { MenuItem } from "../menu"; import { MenuActions } from "../menu/menu-actions"; import { ResizeDirection, ResizingAnchor } from "../resizing-anchor"; -import { CreateResource } from "./create-resource"; +import { CreateResource } from "./create-resource/view"; import { DockTabs } from "./dock-tabs"; -import { DockStore, DockTab, TabKind } from "./dock-store/dock.store"; -import { EditResource } from "./edit-resource"; -import { InstallChart } from "./install-chart"; -import { LogsDockTab } from "./logs/dock-tab"; -import { TerminalWindow } from "./terminal-window"; -import { UpgradeChart } from "./upgrade-chart"; +import { DockStore, DockTab, TabKind } from "./dock/store"; +import { EditResource } from "./edit-resource/view"; +import { InstallChart } from "./install-chart/view"; +import { LogsDockTab } from "./logs/view"; +import { TerminalWindow } from "./terminal/view"; +import { UpgradeChart } from "./upgrade-chart/view"; import { withInjectables } from "@ogre-tools/injectable-react"; -import createResourceTabInjectable from "./create-resource-tab/create-resource-tab.injectable"; -import dockStoreInjectable from "./dock-store/dock-store.injectable"; -import createTerminalTabInjectable from "./create-terminal-tab/create-terminal-tab.injectable"; +import createResourceTabInjectable from "./create-resource/create-resource-tab.injectable"; +import dockStoreInjectable from "./dock/store.injectable"; +import createTerminalTabInjectable from "./terminal/create-terminal-tab.injectable"; interface Props { className?: string; diff --git a/src/renderer/components/dock/dock/close-dock-tab.injectable.ts b/src/renderer/components/dock/dock/close-dock-tab.injectable.ts new file mode 100644 index 0000000000..6325a2a58e --- /dev/null +++ b/src/renderer/components/dock/dock/close-dock-tab.injectable.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { DockStore, TabId } from "./store"; +import dockStoreInjectable from "./store.injectable"; + +interface Dependencies { + dockStore: DockStore; +} + +function closeDockTab({ dockStore }: Dependencies, tabId: TabId): void { + dockStore.closeTab(tabId); +} + +const closeDockTabInjectable = getInjectable({ + instantiate: (di) => bind(closeDockTab, null, { + dockStore: di.inject(dockStoreInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default closeDockTabInjectable; diff --git a/src/renderer/components/dock/dock/create-dock-tab.injectable.ts b/src/renderer/components/dock/dock/create-dock-tab.injectable.ts new file mode 100644 index 0000000000..9557e90df2 --- /dev/null +++ b/src/renderer/components/dock/dock/create-dock-tab.injectable.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import dockStoreInjectable from "./store.injectable"; +import type { DockStore, DockTab, DockTabCreate } from "./store"; + +interface Dependencies { + dockStore: DockStore; +} + +function createDockTab({ dockStore }: Dependencies, rawTabDesc: DockTabCreate, addNumber?: boolean): DockTab { + return dockStore.createTab(rawTabDesc, addNumber); +} + +const createDockTabInjectable = getInjectable({ + instantiate: (di) => bind(createDockTab, null, { + dockStore: di.inject(dockStoreInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default createDockTabInjectable; diff --git a/src/renderer/components/dock/dock-store/dock-storage/dock-storage.injectable.ts b/src/renderer/components/dock/dock/dock-storage.injectable.ts similarity index 81% rename from src/renderer/components/dock/dock-store/dock-storage/dock-storage.injectable.ts rename to src/renderer/components/dock/dock/dock-storage.injectable.ts index 56e6edd37e..e58127761f 100644 --- a/src/renderer/components/dock/dock-store/dock-storage/dock-storage.injectable.ts +++ b/src/renderer/components/dock/dock/dock-storage.injectable.ts @@ -3,8 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import createStorageInjectable from "../../../../utils/create-storage/create-storage.injectable"; -import { DockStorageState, TabKind } from "../dock.store"; +import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; +import { DockStorageState, TabKind } from "./store"; const dockStorageInjectable = getInjectable({ instantiate: (di) => { diff --git a/src/renderer/components/dock/dock/rename-tab.injectable.ts b/src/renderer/components/dock/dock/rename-tab.injectable.ts new file mode 100644 index 0000000000..a312bb9533 --- /dev/null +++ b/src/renderer/components/dock/dock/rename-tab.injectable.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import dockStoreInjectable from "./store.injectable"; +import type { DockStore, TabId } from "./store"; + +interface Dependencies { + dockStore: DockStore; +} + +function renameTab({ dockStore }: Dependencies, tabId: TabId, title: string): void { + dockStore.renameTab(tabId, title); +} + +const renameTabInjectable = getInjectable({ + instantiate: (di) => bind(renameTab, null, { + dockStore: di.inject(dockStoreInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default renameTabInjectable; diff --git a/src/renderer/components/dock/dock/select-dock-tab.injectable.ts b/src/renderer/components/dock/dock/select-dock-tab.injectable.ts new file mode 100644 index 0000000000..9f14f27e81 --- /dev/null +++ b/src/renderer/components/dock/dock/select-dock-tab.injectable.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { DockStore, TabId } from "./store"; +import dockStoreInjectable from "./store.injectable"; + +interface Dependencies { + dockStore: DockStore; +} + +function selectDockTab({ dockStore }: Dependencies, tabId: TabId): void { + dockStore.selectTab(tabId); +} + +const selectDockTabInjectable = getInjectable({ + instantiate: (di) => bind(selectDockTab, null, { + dockStore: di.inject(dockStoreInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default selectDockTabInjectable; diff --git a/src/renderer/components/dock/dock/store.injectable.ts b/src/renderer/components/dock/dock/store.injectable.ts new file mode 100644 index 0000000000..b61450ea54 --- /dev/null +++ b/src/renderer/components/dock/dock/store.injectable.ts @@ -0,0 +1,35 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { DockStore, TabKind } from "./store"; +import dockStorageInjectable from "./dock-storage.injectable"; +import clearLogTabDataInjectable from "../logs/clear-log-tab-data.injectable"; +import clearUpgradeChartTabDataInjectable from "../upgrade-chart/clear-upgrade-chart-tab-data.injectable"; +import clearCreateResourceTabDataInjectable from "../create-resource/clear-create-resource-tab-data.injectable"; +import clearEditResourceTabDataInjectable from "../edit-resource/clear-edit-resource-tab-data.injectable"; +import clearTerminalTabDataInjectable from "../terminal/clear-terminal-tab-data.injectable"; +import clearInstallChartTabDataInjectable from "../install-chart/clear-install-chart-tab-data.injectable"; +import isLogsTabDataValidInjectable from "../logs/is-logs-tab-data-valid.injectable"; + +const dockStoreInjectable = getInjectable({ + instantiate: (di) => new DockStore({ + storage: di.inject(dockStorageInjectable), + tabDataClearers: { + [TabKind.POD_LOGS]: di.inject(clearLogTabDataInjectable), + [TabKind.UPGRADE_CHART]: di.inject(clearUpgradeChartTabDataInjectable), + [TabKind.CREATE_RESOURCE]: di.inject(clearCreateResourceTabDataInjectable), + [TabKind.EDIT_RESOURCE]: di.inject(clearEditResourceTabDataInjectable), + [TabKind.INSTALL_CHART]: di.inject(clearInstallChartTabDataInjectable), + [TabKind.TERMINAL]: di.inject(clearTerminalTabDataInjectable), + }, + tabDataValidator: { + [TabKind.POD_LOGS]: di.inject(isLogsTabDataValidInjectable), + }, + }), + + lifecycle: lifecycleEnum.singleton, +}); + +export default dockStoreInjectable; diff --git a/src/renderer/components/dock/dock-store/dock.store.ts b/src/renderer/components/dock/dock/store.ts similarity index 93% rename from src/renderer/components/dock/dock-store/dock.store.ts rename to src/renderer/components/dock/dock/store.ts index 6cca0e4d06..3cbe3c03aa 100644 --- a/src/renderer/components/dock/dock-store/dock.store.ts +++ b/src/renderer/components/dock/dock/store.ts @@ -98,11 +98,13 @@ export interface DockTabCloseEvent { } interface Dependencies { - storage: StorageHelper + readonly storage: StorageHelper + readonly tabDataClearers: Record void>; + readonly tabDataValidator: Partial boolean>>; } export class DockStore implements DockStorageState { - constructor(private dependencies: Dependencies) { + constructor(private readonly dependencies: Dependencies) { makeObservable(this); autoBind(this); this.init(); @@ -167,6 +169,16 @@ export class DockStore implements DockStorageState { private init() { // adjust terminal height if window size changes window.addEventListener("resize", throttle(this.adjustHeight, 250)); + + this.whenReady.then(action(() => { + for (const tab of this.tabs) { + const validator = this.dependencies.tabDataValidator[tab.kind]; + + if (validator && !validator(tab.id)) { + this.closeTab(tab.id); + } + } + })); } get maxHeight() { @@ -317,6 +329,7 @@ export class DockStore implements DockStorageState { } this.tabs = this.tabs.filter(tab => tab.id !== tabId); + this.dependencies.tabDataClearers[tab.kind](tab.id); if (this.selectedTabId === tab.id) { if (this.tabs.length) { @@ -330,6 +343,7 @@ export class DockStore implements DockStorageState { } } + @action closeTabs(tabs: DockTab[]) { tabs.forEach(tab => this.closeTab(tab.id)); } diff --git a/src/renderer/components/dock/edit-resource-store/edit-resource-store.injectable.ts b/src/renderer/components/dock/edit-resource-store/edit-resource-store.injectable.ts deleted file mode 100644 index 393bfa560b..0000000000 --- a/src/renderer/components/dock/edit-resource-store/edit-resource-store.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import dockStoreInjectable from "../dock-store/dock-store.injectable"; -import { EditResourceStore } from "./edit-resource.store"; -import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; - -const editResourceStoreInjectable = getInjectable({ - instantiate: (di) => - new EditResourceStore({ - dockStore: di.inject(dockStoreInjectable), - createStorage: di.inject(createStorageInjectable), - }), - - lifecycle: lifecycleEnum.singleton, -}); - -export default editResourceStoreInjectable; diff --git a/src/renderer/components/dock/edit-resource-store/edit-resource.store.ts b/src/renderer/components/dock/edit-resource-store/edit-resource.store.ts deleted file mode 100644 index 92982cb52d..0000000000 --- a/src/renderer/components/dock/edit-resource-store/edit-resource.store.ts +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { autoBind, noop, StorageHelper } from "../../../utils"; -import { DockTabStorageState, DockTabStore } from "../dock-tab-store/dock-tab.store"; -import { autorun, IReactionDisposer } from "mobx"; -import type { DockStore, DockTab, TabId } from "../dock-store/dock.store"; -import type { KubeObject } from "../../../../common/k8s-api/kube-object"; -import { apiManager } from "../../../../common/k8s-api/api-manager"; -import type { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; - -export interface EditingResource { - resource: string; // resource path, e.g. /api/v1/namespaces/default - draft?: string; // edited draft in yaml - firstDraft?: string; -} - -interface Dependencies { - dockStore: DockStore - createStorage: (storageKey: string, options: DockTabStorageState) => StorageHelper> -} - -export class EditResourceStore extends DockTabStore { - private watchers = new Map(); - - constructor(protected dependencies: Dependencies) { - super(dependencies, { - storageKey: "edit_resource_store", - }); - - autoBind(this); - } - - protected async init() { - super.init(); - await this.storage.whenReady; - - autorun(() => { - Array.from(this.data).forEach(([tabId, { resource }]) => { - if (this.watchers.get(tabId)) { - return; - } - this.watchers.set(tabId, autorun(() => { - const store = apiManager.getStore(resource); - - if (store) { - const isActiveTab = this.dependencies.dockStore.isOpen && this.dependencies.dockStore.selectedTabId === tabId; - const obj = store.getByPath(resource); - - // preload resource for editing - if (!obj && !store.isLoaded && !store.isLoading && isActiveTab) { - store.loadFromPath(resource).catch(noop); - } - // auto-close tab when resource removed from store - else if (!obj && store.isLoaded) { - this.dependencies.dockStore.closeTab(tabId); - } - } - }, { - delay: 100, // make sure all kube-object stores are initialized - })); - }); - }); - } - - protected finalizeDataForSave({ draft, ...data }: EditingResource): EditingResource { - return data; // skip saving draft to local-storage - } - - isReady(tabId: TabId) { - const tabDataReady = super.isReady(tabId); - - return Boolean(tabDataReady && this.getResource(tabId)); // ready to edit resource - } - - getStore(tabId: TabId): KubeObjectStore | undefined { - return apiManager.getStore(this.getResourcePath(tabId)); - } - - getResource(tabId: TabId): KubeObject | undefined { - return this.getStore(tabId)?.getByPath(this.getResourcePath(tabId)); - } - - getResourcePath(tabId: TabId): string | undefined { - return this.getData(tabId)?.resource; - } - - getTabByResource(object: KubeObject): DockTab { - const [tabId] = Array.from(this.data).find(([, { resource }]) => { - return object.selfLink === resource; - }) || []; - - return this.dependencies.dockStore.getTabById(tabId); - } - - clearInitialDraft(tabId: TabId): void { - delete this.getData(tabId)?.firstDraft; - } - - reset() { - super.reset(); - Array.from(this.watchers).forEach(([tabId, dispose]) => { - this.watchers.delete(tabId); - dispose(); - }); - } -} diff --git a/src/renderer/components/dock/edit-resource-tab/edit-resource-tab.injectable.ts b/src/renderer/components/dock/edit-resource-tab/edit-resource-tab.injectable.ts deleted file mode 100644 index ba94e51376..0000000000 --- a/src/renderer/components/dock/edit-resource-tab/edit-resource-tab.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { editResourceTab } from "./edit-resource-tab"; -import editResourceStoreInjectable from "../edit-resource-store/edit-resource-store.injectable"; -import dockStoreInjectable from "../dock-store/dock-store.injectable"; - -const editResourceTabInjectable = getInjectable({ - instantiate: (di) => editResourceTab({ - dockStore: di.inject(dockStoreInjectable), - editResourceStore: di.inject(editResourceStoreInjectable), - }), - - lifecycle: lifecycleEnum.singleton, -}); - -export default editResourceTabInjectable; diff --git a/src/renderer/components/dock/edit-resource-tab/edit-resource-tab.ts b/src/renderer/components/dock/edit-resource-tab/edit-resource-tab.ts deleted file mode 100644 index e42004ef69..0000000000 --- a/src/renderer/components/dock/edit-resource-tab/edit-resource-tab.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { KubeObject } from "../../../../common/k8s-api/kube-object"; -import { DockStore, DockTabCreateSpecific, TabKind } from "../dock-store/dock.store"; -import type { EditResourceStore } from "../edit-resource-store/edit-resource.store"; - -interface Dependencies { - dockStore: DockStore; - editResourceStore: EditResourceStore; -} - -export const editResourceTab = - ({ dockStore, editResourceStore }: Dependencies) => - (object: KubeObject, tabParams: DockTabCreateSpecific = {}) => { - // use existing tab if already opened - let tab = editResourceStore.getTabByResource(object); - - if (tab) { - dockStore.open(); - dockStore.selectTab(tab.id); - } - - // or create new tab - if (!tab) { - tab = dockStore.createTab( - { - title: `${object.kind}: ${object.getName()}`, - ...tabParams, - kind: TabKind.EDIT_RESOURCE, - }, - false, - ); - editResourceStore.setData(tab.id, { - resource: object.selfLink, - }); - } - - return tab; - }; diff --git a/src/renderer/components/dock/edit-resource.scss b/src/renderer/components/dock/edit-resource.scss deleted file mode 100644 index 2ae07cdd07..0000000000 --- a/src/renderer/components/dock/edit-resource.scss +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -.EditResource { -} diff --git a/src/renderer/components/dock/edit-resource/clear-edit-resource-tab-data.injectable.ts b/src/renderer/components/dock/edit-resource/clear-edit-resource-tab-data.injectable.ts new file mode 100644 index 0000000000..cc541ed398 --- /dev/null +++ b/src/renderer/components/dock/edit-resource/clear-edit-resource-tab-data.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { TabId } from "../dock/store"; +import type { EditResourceTabStore } from "./store"; +import editResourceTabStoreInjectable from "./store.injectable"; + +interface Dependencies { + editResourceTabStore: EditResourceTabStore; +} + +function clearEditResourceTabData({ editResourceTabStore }: Dependencies, tabId: TabId) { + editResourceTabStore.clearData(tabId); +} + +const clearEditResourceTabDataInjectable = getInjectable({ + instantiate: (di) => bind(clearEditResourceTabData, null, { + editResourceTabStore: di.inject(editResourceTabStoreInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default clearEditResourceTabDataInjectable; diff --git a/src/renderer/components/dock/edit-resource/edit-resource-tab.injectable.ts b/src/renderer/components/dock/edit-resource/edit-resource-tab.injectable.ts new file mode 100644 index 0000000000..a8c6e2501d --- /dev/null +++ b/src/renderer/components/dock/edit-resource/edit-resource-tab.injectable.ts @@ -0,0 +1,57 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import editResourceTabStoreInjectable from "./store.injectable"; +import dockStoreInjectable from "../dock/store.injectable"; +import type { KubeObject } from "../../../../common/k8s-api/kube-object"; +import { DockStore, DockTabCreateSpecific, TabId, TabKind } from "../dock/store"; +import type { EditResourceTabStore } from "./store"; +import { bind } from "../../../utils"; +import { runInAction } from "mobx"; + +interface Dependencies { + dockStore: DockStore; + editResourceStore: EditResourceTabStore; +} + +function createEditResourceTab({ dockStore, editResourceStore }: Dependencies, object: KubeObject, tabParams: DockTabCreateSpecific = {}): TabId { + // use existing tab if already opened + const tabId = editResourceStore.getTabIdByResource(object); + + if (tabId) { + dockStore.open(); + dockStore.selectTab(tabId); + + return tabId; + } + + return runInAction(() => { + const tab = dockStore.createTab( + { + title: `${object.kind}: ${object.getName()}`, + ...tabParams, + kind: TabKind.EDIT_RESOURCE, + }, + false, + ); + + editResourceStore.setData(tab.id, { + resource: object.selfLink, + }); + + return tab.id; + }); +} + +const createEditResourceTabInjectable = getInjectable({ + instantiate: (di) => bind(createEditResourceTab, null, { + dockStore: di.inject(dockStoreInjectable), + editResourceStore: di.inject(editResourceTabStoreInjectable), + }), + + lifecycle: lifecycleEnum.singleton, +}); + +export default createEditResourceTabInjectable; diff --git a/src/renderer/components/dock/edit-resource/store.injectable.ts b/src/renderer/components/dock/edit-resource/store.injectable.ts new file mode 100644 index 0000000000..ae2f462262 --- /dev/null +++ b/src/renderer/components/dock/edit-resource/store.injectable.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { EditResourceTabStore } from "./store"; +import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; + +const editResourceTabStoreInjectable = getInjectable({ + instantiate: (di) => new EditResourceTabStore({ + createStorage: di.inject(createStorageInjectable), + }), + + lifecycle: lifecycleEnum.singleton, +}); + +export default editResourceTabStoreInjectable; diff --git a/src/renderer/components/dock/edit-resource/store.ts b/src/renderer/components/dock/edit-resource/store.ts new file mode 100644 index 0000000000..5b8794d758 --- /dev/null +++ b/src/renderer/components/dock/edit-resource/store.ts @@ -0,0 +1,57 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { StorageHelper } from "../../../utils"; +import { DockTabStorageState, DockTabStore } from "../dock-tab-store/dock-tab.store"; +import type { TabId } from "../dock/store"; +import type { KubeObject } from "../../../../common/k8s-api/kube-object"; +import { apiManager } from "../../../../common/k8s-api/api-manager"; +import type { KubeObjectStore } from "../../../../common/k8s-api/kube-object.store"; + +export interface EditingResource { + resource: string; // resource path, e.g. /api/v1/namespaces/default + draft?: string; // edited draft in yaml + firstDraft?: string; +} + +interface Dependencies { + createStorage: (storageKey: string, options: DockTabStorageState) => StorageHelper> +} + +export class EditResourceTabStore extends DockTabStore { + constructor(protected dependencies: Dependencies) { + super(dependencies, { + storageKey: "edit_resource_store", + }); + } + + protected finalizeDataForSave({ draft, ...data }: EditingResource): EditingResource { + return data; // skip saving draft to local-storage + } + + isReady(tabId: TabId) { + return super.isReady(tabId) && Boolean(this.getResource(tabId)); // ready to edit resource + } + + getStore(tabId: TabId): KubeObjectStore | undefined { + return apiManager.getStore(this.getResourcePath(tabId)); + } + + getResource(tabId: TabId): KubeObject | undefined { + return this.getStore(tabId)?.getByPath(this.getResourcePath(tabId)); + } + + getResourcePath(tabId: TabId): string | undefined { + return this.getData(tabId)?.resource; + } + + getTabIdByResource(object: KubeObject): TabId { + return this.findTabIdFromData(({ resource }) => object.selfLink === resource); + } + + clearInitialDraft(tabId: TabId): void { + delete this.getData(tabId)?.firstDraft; + } +} diff --git a/src/renderer/components/dock/edit-resource.tsx b/src/renderer/components/dock/edit-resource/view.tsx similarity index 67% rename from src/renderer/components/dock/edit-resource.tsx rename to src/renderer/components/dock/edit-resource/view.tsx index ec15e27ab5..aa23af9cea 100644 --- a/src/renderer/components/dock/edit-resource.tsx +++ b/src/renderer/components/dock/edit-resource/view.tsx @@ -3,29 +3,30 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import "./edit-resource.scss"; - import React from "react"; -import { computed, makeObservable, observable } from "mobx"; -import { observer } from "mobx-react"; +import { autorun, computed, makeObservable, observable } from "mobx"; +import { disposeOnUnmount, observer } from "mobx-react"; import yaml from "js-yaml"; -import type { DockTab } from "./dock-store/dock.store"; -import type { EditResourceStore } from "./edit-resource-store/edit-resource.store"; -import { InfoPanel } from "./info-panel"; -import { Badge } from "../badge"; -import { EditorPanel } from "./editor-panel"; -import { Spinner } from "../spinner"; -import type { KubeObject } from "../../../common/k8s-api/kube-object"; +import type { DockTab, TabId } from "../dock/store"; +import type { EditResourceTabStore } from "./store"; +import { InfoPanel } from "../info-panel"; +import { Badge } from "../../badge"; +import { EditorPanel } from "../editor-panel"; +import { Spinner } from "../../spinner"; +import type { KubeObject } from "../../../../common/k8s-api/kube-object"; import { createPatch } from "rfc6902"; import { withInjectables } from "@ogre-tools/injectable-react"; -import editResourceStoreInjectable from "./edit-resource-store/edit-resource-store.injectable"; +import editResourceTabStoreInjectable from "./store.injectable"; +import { noop } from "../../../utils"; +import closeDockTabInjectable from "../dock/close-dock-tab.injectable"; interface Props { tab: DockTab; } interface Dependencies { - editResourceStore: EditResourceStore + editResourceStore: EditResourceTabStore; + closeTab: (tabId: TabId) => void; } @observer @@ -37,6 +38,26 @@ class NonInjectedEditResource extends React.Component { makeObservable(this); } + componentDidMount(): void { + disposeOnUnmount(this, [ + autorun(() => { + const store = this.props.editResourceStore.getStore(this.props.tab.id); + const tabData = this.props.editResourceStore.getData(this.props.tab.id); + const obj = this.resource; + + if (!obj) { + if (store.isLoaded) { + // auto-close tab when resource removed from store + this.props.closeTab(this.props.tab.id); + } else if (!store.isLoading) { + // preload resource for editing + store.loadFromPath(tabData.resource).catch(noop); + } + } + }), + ]); + } + get tabId() { return this.props.tab.id; } @@ -132,13 +153,10 @@ class NonInjectedEditResource extends React.Component { } } -export const EditResource = withInjectables( - NonInjectedEditResource, - - { - getProps: (di, props) => ({ - editResourceStore: di.inject(editResourceStoreInjectable), - ...props, - }), - }, -); +export const EditResource = withInjectables(NonInjectedEditResource, { + getProps: (di, props) => ({ + editResourceStore: di.inject(editResourceTabStoreInjectable), + closeTab: di.inject(closeDockTabInjectable), + ...props, + }), +}); diff --git a/src/renderer/components/dock/editor-panel.tsx b/src/renderer/components/dock/editor-panel.tsx index e2500a7513..90e47b1c7f 100644 --- a/src/renderer/components/dock/editor-panel.tsx +++ b/src/renderer/components/dock/editor-panel.tsx @@ -8,11 +8,11 @@ import throttle from "lodash/throttle"; import React from "react"; import { makeObservable, observable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; -import type { DockStore, TabId } from "./dock-store/dock.store"; +import type { DockStore, TabId } from "./dock/store"; import { cssNames } from "../../utils"; import { MonacoEditor, MonacoEditorProps } from "../monaco-editor"; import { withInjectables } from "@ogre-tools/injectable-react"; -import dockStoreInjectable from "./dock-store/dock-store.injectable"; +import dockStoreInjectable from "./dock/store.injectable"; export interface EditorPanelProps { tabId: TabId; diff --git a/src/renderer/components/dock/info-panel.tsx b/src/renderer/components/dock/info-panel.tsx index 0d534b3a6f..27358dc90c 100644 --- a/src/renderer/components/dock/info-panel.tsx +++ b/src/renderer/components/dock/info-panel.tsx @@ -12,10 +12,10 @@ import { cssNames } from "../../utils"; import { Button } from "../button"; import { Icon } from "../icon"; import { Spinner } from "../spinner"; -import type { DockStore, TabId } from "./dock-store/dock.store"; +import type { DockStore, TabId } from "./dock/store"; import { Notifications } from "../notifications"; import { withInjectables } from "@ogre-tools/injectable-react"; -import dockStoreInjectable from "./dock-store/dock-store.injectable"; +import dockStoreInjectable from "./dock/store.injectable"; interface Props extends OptionalProps { tabId: TabId; diff --git a/src/renderer/components/dock/install-chart/clear-install-chart-tab-data.injectable.ts b/src/renderer/components/dock/install-chart/clear-install-chart-tab-data.injectable.ts new file mode 100644 index 0000000000..7ac46182d6 --- /dev/null +++ b/src/renderer/components/dock/install-chart/clear-install-chart-tab-data.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { TabId } from "../dock/store"; +import type { InstallChartTabStore } from "./store"; +import installChartTabStoreInjectable from "./store.injectable"; + +interface Dependencies { + installChartTabStore: InstallChartTabStore; +} + +function clearInstallChartTabData({ installChartTabStore }: Dependencies, tabId: TabId) { + installChartTabStore.clearData(tabId); +} + +const clearInstallChartTabDataInjectable = getInjectable({ + instantiate: (di) => bind(clearInstallChartTabData, null, { + installChartTabStore: di.inject(installChartTabStoreInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default clearInstallChartTabDataInjectable; diff --git a/src/renderer/components/dock/install-chart/create-install-chart-tab.injectable.ts b/src/renderer/components/dock/install-chart/create-install-chart-tab.injectable.ts new file mode 100644 index 0000000000..4178627602 --- /dev/null +++ b/src/renderer/components/dock/install-chart/create-install-chart-tab.injectable.ts @@ -0,0 +1,51 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import installChartTabStoreInjectable from "./store.injectable"; +import type { HelmChart } from "../../../../common/k8s-api/endpoints/helm-charts.api"; +import { DockTab, DockTabCreate, DockTabCreateSpecific, TabKind } from "../dock/store"; +import type { InstallChartTabStore } from "./store"; +import { bind } from "../../../utils"; +import createDockTabInjectable from "../dock/create-dock-tab.injectable"; + +interface Dependencies { + createDockTab: (rawTab: DockTabCreate, addNumber: boolean) => DockTab; + installChartStore: InstallChartTabStore; +} + +function createInstallChartTab({ createDockTab, installChartStore }: Dependencies, chart: HelmChart, tabParams: DockTabCreateSpecific = {}) { + const { name, repo, version } = chart; + + const tab = createDockTab( + { + title: `Helm Install: ${repo}/${name}`, + ...tabParams, + kind: TabKind.INSTALL_CHART, + }, + false, + ); + + installChartStore.setData(tab.id, { + name, + repo, + version, + namespace: "default", + releaseName: "", + description: "", + }); + + return tab; +} + +const createInstallChartTabInjectable = getInjectable({ + instantiate: (di) => bind(createInstallChartTab, null, { + installChartStore: di.inject(installChartTabStoreInjectable), + createDockTab: di.inject(createDockTabInjectable), + }), + + lifecycle: lifecycleEnum.singleton, +}); + +export default createInstallChartTabInjectable; diff --git a/src/renderer/components/dock/install-chart.scss b/src/renderer/components/dock/install-chart/install-chart.scss similarity index 100% rename from src/renderer/components/dock/install-chart.scss rename to src/renderer/components/dock/install-chart/install-chart.scss diff --git a/src/renderer/components/dock/install-chart-store/install-chart-store.injectable.ts b/src/renderer/components/dock/install-chart/store.injectable.ts similarity index 73% rename from src/renderer/components/dock/install-chart-store/install-chart-store.injectable.ts rename to src/renderer/components/dock/install-chart/store.injectable.ts index d0fdb731a0..f1d38d744a 100644 --- a/src/renderer/components/dock/install-chart-store/install-chart-store.injectable.ts +++ b/src/renderer/components/dock/install-chart/store.injectable.ts @@ -3,18 +3,16 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { InstallChartStore } from "./install-chart.store"; -import dockStoreInjectable from "../dock-store/dock-store.injectable"; +import { InstallChartTabStore } from "./store"; import createDockTabStoreInjectable from "../dock-tab-store/create-dock-tab-store.injectable"; import type { IReleaseUpdateDetails } from "../../../../common/k8s-api/endpoints/helm-releases.api"; import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; -const installChartStoreInjectable = getInjectable({ +const installChartTabStoreInjectable = getInjectable({ instantiate: (di) => { const createDockTabStore = di.inject(createDockTabStoreInjectable); - return new InstallChartStore({ - dockStore: di.inject(dockStoreInjectable), + return new InstallChartTabStore({ createStorage: di.inject(createStorageInjectable), versionsStore: createDockTabStore(), detailsStore: createDockTabStore(), @@ -23,4 +21,4 @@ const installChartStoreInjectable = getInjectable({ lifecycle: lifecycleEnum.singleton, }); -export default installChartStoreInjectable; +export default installChartTabStoreInjectable; diff --git a/src/renderer/components/dock/install-chart-store/install-chart.store.ts b/src/renderer/components/dock/install-chart/store.ts similarity index 72% rename from src/renderer/components/dock/install-chart-store/install-chart.store.ts rename to src/renderer/components/dock/install-chart/store.ts index 9e8a9d4c32..6417a8fab5 100644 --- a/src/renderer/components/dock/install-chart-store/install-chart.store.ts +++ b/src/renderer/components/dock/install-chart/store.ts @@ -3,12 +3,11 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { action, autorun, makeObservable } from "mobx"; -import { DockStore, TabId, TabKind } from "../dock-store/dock.store"; +import { action, makeObservable } from "mobx"; +import type { TabId } from "../dock/store"; import { DockTabStorageState, DockTabStore } from "../dock-tab-store/dock-tab.store"; import { getChartDetails, getChartValues } from "../../../../common/k8s-api/endpoints/helm-charts.api"; import type { IReleaseUpdateDetails } from "../../../../common/k8s-api/endpoints/helm-releases.api"; -import { Notifications } from "../../notifications"; import type { StorageHelper } from "../../../utils"; export interface IChartInstallData { @@ -23,29 +22,18 @@ export interface IChartInstallData { } interface Dependencies { - dockStore: DockStore, - createStorage: (storageKey: string, options: DockTabStorageState) => StorageHelper> - - versionsStore: DockTabStore, - detailsStore: DockTabStore + createStorage: (storageKey: string, options: DockTabStorageState) => StorageHelper>; + versionsStore: DockTabStore; + detailsStore: DockTabStore; } -export class InstallChartStore extends DockTabStore { +export class InstallChartTabStore extends DockTabStore { constructor(protected dependencies: Dependencies) { super( dependencies, { storageKey: "install_charts" }, ); - makeObservable(this); - autorun(() => { - const { selectedTab, isOpen } = dependencies.dockStore; - - if (selectedTab?.kind === TabKind.INSTALL_CHART && isOpen) { - this.loadData(selectedTab.id) - .catch(err => Notifications.error(String(err))); - } - }, { delay: 250 }); } get versions() { @@ -94,8 +82,4 @@ export class InstallChartStore extends DockTabStore { return this.loadValues(tabId, attempt + 1); } } - - setData(tabId: TabId, data: IChartInstallData){ - super.setData(tabId, data); - } } diff --git a/src/renderer/components/dock/install-chart.tsx b/src/renderer/components/dock/install-chart/view.tsx similarity index 79% rename from src/renderer/components/dock/install-chart.tsx rename to src/renderer/components/dock/install-chart/view.tsx index a240b50979..f162ae13a3 100644 --- a/src/renderer/components/dock/install-chart.tsx +++ b/src/renderer/components/dock/install-chart/view.tsx @@ -8,29 +8,27 @@ import "./install-chart.scss"; import React, { Component } from "react"; import { action, makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; -import type { DockStore, DockTab } from "./dock-store/dock.store"; -import { InfoPanel } from "./info-panel"; -import { Badge } from "../badge"; -import { NamespaceSelect } from "../+namespaces/namespace-select"; -import { prevDefault } from "../../utils"; -import type { IChartInstallData, InstallChartStore } from "./install-chart-store/install-chart.store"; -import { Spinner } from "../spinner"; -import { Icon } from "../icon"; -import { Button } from "../button"; -import { LogsDialog } from "../dialog/logs-dialog"; -import { Select, SelectOption } from "../select"; -import { Input } from "../input"; -import { EditorPanel } from "./editor-panel"; -import { navigate } from "../../navigation"; -import { releaseURL } from "../../../common/routes"; -import type { - IReleaseCreatePayload, - IReleaseUpdateDetails, -} from "../../../common/k8s-api/endpoints/helm-releases.api"; +import type { DockStore, DockTab } from "../dock/store"; +import { InfoPanel } from "../info-panel"; +import { Badge } from "../../badge"; +import { NamespaceSelect } from "../../+namespaces/namespace-select"; +import { prevDefault } from "../../../utils"; +import type { IChartInstallData, InstallChartTabStore } from "./store"; +import { Spinner } from "../../spinner"; +import { Icon } from "../../icon"; +import { Button } from "../../button"; +import { LogsDialog } from "../../dialog/logs-dialog"; +import { Select, SelectOption } from "../../select"; +import { Input } from "../../input"; +import { EditorPanel } from "../editor-panel"; +import { navigate } from "../../../navigation"; +import { releaseURL } from "../../../../common/routes"; +import type { IReleaseCreatePayload, IReleaseUpdateDetails } from "../../../../common/k8s-api/endpoints/helm-releases.api"; import { withInjectables } from "@ogre-tools/injectable-react"; -import installChartStoreInjectable from "./install-chart-store/install-chart-store.injectable"; -import dockStoreInjectable from "./dock-store/dock-store.injectable"; -import createReleaseInjectable from "../+apps-releases/create-release/create-release.injectable"; +import installChartTabStoreInjectable from "./store.injectable"; +import dockStoreInjectable from "../dock/store.injectable"; +import createReleaseInjectable from "../../+apps-releases/create-release/create-release.injectable"; +import { Notifications } from "../../notifications"; interface Props { tab: DockTab; @@ -38,7 +36,7 @@ interface Props { interface Dependencies { createRelease: (payload: IReleaseCreatePayload) => Promise - installChartStore: InstallChartStore + installChartStore: InstallChartTabStore dockStore: DockStore } @@ -52,6 +50,11 @@ class NonInjectedInstallChart extends Component { makeObservable(this); } + componentDidMount(): void { + this.props.installChartStore.loadData(this.tabId) + .catch(err => Notifications.error(String(err))); + } + get chartData() { return this.props.installChartStore.getData(this.tabId); } @@ -221,7 +224,7 @@ export const InstallChart = withInjectables( { getProps: (di, props) => ({ createRelease: di.inject(createReleaseInjectable), - installChartStore: di.inject(installChartStoreInjectable), + installChartStore: di.inject(installChartTabStoreInjectable), dockStore: di.inject(dockStoreInjectable), ...props, }), diff --git a/src/renderer/components/dock/logs/__test__/log-resource-selector.test.tsx b/src/renderer/components/dock/logs/__test__/log-resource-selector.test.tsx index 9858827f86..b4ca553a81 100644 --- a/src/renderer/components/dock/logs/__test__/log-resource-selector.test.tsx +++ b/src/renderer/components/dock/logs/__test__/log-resource-selector.test.tsx @@ -8,7 +8,7 @@ import "@testing-library/jest-dom/extend-expect"; import * as selectEvent from "react-select-event"; import { Pod } from "../../../../../common/k8s-api/endpoints"; import { LogResourceSelector } from "../resource-selector"; -import { dockerPod, deploymentPod1 } from "./pod.mock"; +import { dockerPod, deploymentPod1, deploymentPod2 } from "./pod.mock"; import { ThemeStore } from "../../../../theme.store"; import { UserStore } from "../../../../../common/user-store"; import mockFs from "mock-fs"; @@ -18,7 +18,7 @@ import { renderFor } from "../../../test-utils/renderFor"; import directoryForUserDataInjectable from "../../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import callForLogsInjectable from "../call-for-logs.injectable"; import { LogTabViewModel, LogTabViewModelDependencies } from "../logs-view-model"; -import type { TabId } from "../../dock-store/dock.store"; +import type { TabId } from "../../dock/store"; jest.mock("electron", () => ({ app: { @@ -51,6 +51,8 @@ function mockLogTabViewModel(tabId: TabId, deps: Partial { return mockLogTabViewModel(tabId, { getLogTabData: () => ({ - pods: [selectedPod], - selectedPod, - selectedContainer: selectedPod.getContainers()[0], + selectedPodId: selectedPod.getId(), + selectedContainer: selectedPod.getContainers()[0].name, + namespace: selectedPod.getNs(), + showPrevious: false, + showTimestamps: false, }), + getPodById: (id) => { + if (id === selectedPod.getId()) { + return selectedPod; + } + + return undefined; + }, }); }; const getFewPodsTabData = (tabId: TabId): LogTabViewModel => { const selectedPod = new Pod(deploymentPod1); - const anotherPod = new Pod(dockerPod); + const anotherPod = new Pod(deploymentPod2); return mockLogTabViewModel(tabId, { getLogTabData: () => ({ - pods: [selectedPod, anotherPod], - selectedPod, - selectedContainer: selectedPod.getContainers()[0], + ownerId: "uuid", + selectedPodId: selectedPod.getId(), + selectedContainer: selectedPod.getContainers()[0].name, + namespace: selectedPod.getNs(), + showPrevious: false, + showTimestamps: false, }), + getPodById: (id) => { + if (id === selectedPod.getId()) { + return selectedPod; + } + + if (id === anotherPod.getId()) { + return anotherPod; + } + + return undefined; + }, + getPodsByOwnerId: (id) => { + if (id === "uuid") { + return [selectedPod, anotherPod]; + } + + return []; + }, }); }; diff --git a/src/renderer/components/dock/logs/__test__/log-tab.store.test.ts b/src/renderer/components/dock/logs/__test__/log-tab.store.test.ts index b98b46daf9..d493e667a2 100644 --- a/src/renderer/components/dock/logs/__test__/log-tab.store.test.ts +++ b/src/renderer/components/dock/logs/__test__/log-tab.store.test.ts @@ -11,12 +11,13 @@ import { deploymentPod1, deploymentPod2, deploymentPod3, dockerPod } from "./pod import { mockWindow } from "../../../../../../__mocks__/windowMock"; import { getDiForUnitTesting } from "../../../../getDiForUnitTesting"; import logTabStoreInjectable from "../tab-store.injectable"; -import type { LogTabStore } from "../tab.store"; -import dockStoreInjectable from "../../dock-store/dock-store.injectable"; -import type { DockStore } from "../../dock-store/dock.store"; -import directoryForUserDataInjectable - from "../../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import type { LogTabStore } from "../tab-store"; +import dockStoreInjectable from "../../dock/store.injectable"; +import type { DockStore, TabId } from "../../dock/store"; +import directoryForUserDataInjectable from "../../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import mockFs from "mock-fs"; +import type { PodLogsTabData } from "../create-pod-logs-tab.injectable"; +import createPodLogsTabInjectable from "../create-pod-logs-tab.injectable"; mockWindow(); @@ -27,6 +28,7 @@ podsStore.items.push(new Pod(deploymentPod2)); describe("log tab store", () => { let logTabStore: LogTabStore; let dockStore: DockStore; + let createPodTab: ({ selectedPod, selectedContainer }: PodLogsTabData) => TabId; beforeEach(async () => { const di = getDiForUnitTesting({ doGeneralOverrides: true }); @@ -39,6 +41,7 @@ describe("log tab store", () => { dockStore = di.inject(dockStoreInjectable); logTabStore = di.inject(logTabStoreInjectable); + createPodTab = di.inject(createPodLogsTabInjectable); UserStore.createInstance(); ThemeStore.createInstance(); @@ -54,7 +57,7 @@ describe("log tab store", () => { const selectedPod = new Pod(dockerPod); const selectedContainer = selectedPod.getAllContainers()[0]; - logTabStore.createPodTab({ + createPodTab({ selectedPod, selectedContainer, }); @@ -73,7 +76,7 @@ describe("log tab store", () => { const siblingPod = new Pod(deploymentPod2); const selectedContainer = selectedPod.getInitContainers()[0]; - logTabStore.createPodTab({ + createPodTab({ selectedPod, selectedContainer, }); @@ -91,7 +94,7 @@ describe("log tab store", () => { const selectedPod = new Pod(deploymentPod1); const selectedContainer = selectedPod.getInitContainers()[0]; - logTabStore.createPodTab({ + createPodTab({ selectedPod, selectedContainer, }); @@ -111,7 +114,7 @@ describe("log tab store", () => { const selectedPod = new Pod(deploymentPod1); const selectedContainer = selectedPod.getInitContainers()[0]; - logTabStore.createPodTab({ + createPodTab({ selectedPod, selectedContainer, }); @@ -132,7 +135,7 @@ describe("log tab store", () => { const selectedPod = new Pod(deploymentPod1); const selectedContainer = selectedPod.getInitContainers()[0]; - const id = logTabStore.createPodTab({ + const id = createPodTab({ selectedPod, selectedContainer, }); diff --git a/src/renderer/components/dock/logs/clear-log-tab-data.injectable.ts b/src/renderer/components/dock/logs/clear-log-tab-data.injectable.ts new file mode 100644 index 0000000000..23e35272b1 --- /dev/null +++ b/src/renderer/components/dock/logs/clear-log-tab-data.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { TabId } from "../dock/store"; +import type { LogTabStore } from "./tab-store"; +import logTabStoreInjectable from "./tab-store.injectable"; + +interface Dependencies { + logTabStore: LogTabStore; +} + +function clearLogTabData({ logTabStore }: Dependencies, tabId: TabId): void { + logTabStore.clearData(tabId); +} + +const clearLogTabDataInjectable = getInjectable({ + instantiate: (di) => bind(clearLogTabData, null, { + logTabStore: di.inject(logTabStoreInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default clearLogTabDataInjectable; diff --git a/src/renderer/components/dock/logs/controls.tsx b/src/renderer/components/dock/logs/controls.tsx index 954ccb8478..e3348cdb27 100644 --- a/src/renderer/components/dock/logs/controls.tsx +++ b/src/renderer/components/dock/logs/controls.tsx @@ -8,7 +8,6 @@ import "./controls.scss"; import React from "react"; import { observer } from "mobx-react"; -import { Pod } from "../../../../common/k8s-api/endpoints"; import { cssNames, saveFileDialog } from "../../../utils"; import { Checkbox } from "../../checkbox"; import { Icon } from "../../icon"; @@ -26,21 +25,20 @@ export const LogControls = observer(({ model }: LogControlsProps) => { } const logs = model.timestampSplitLogs.get(); - const { showTimestamps, previous } = tabData; + const { showTimestamps, showPrevious: previous } = tabData; const since = logs.length ? logs[0][0] : null; - const pod = new Pod(tabData.selectedPod); const toggleTimestamps = () => { model.updateLogTabData({ showTimestamps: !showTimestamps }); }; const togglePrevious = () => { - model.updateLogTabData({ previous: !previous }); + model.updateLogTabData({ showPrevious: !previous }); model.reloadLogs(); }; const downloadLogs = () => { - const fileName = pod.getName(); + const fileName = model.pod.get().getName(); const logsToDownload: string[] = showTimestamps ? model.logs.get() : model.logsWithoutTimestamps.get(); diff --git a/src/renderer/components/dock/logs/create-logs-tab.injectable.ts b/src/renderer/components/dock/logs/create-logs-tab.injectable.ts new file mode 100644 index 0000000000..c1092375d7 --- /dev/null +++ b/src/renderer/components/dock/logs/create-logs-tab.injectable.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import { DockTabCreate, DockTab, TabKind, TabId } from "../dock/store"; +import type { LogTabData } from "./tab-store"; +import * as uuid from "uuid"; +import { runInAction } from "mobx"; +import createDockTabInjectable from "../dock/create-dock-tab.injectable"; +import setLogTabDataInjectable from "./set-log-tab-data.injectable"; + +export type CreateLogsTabData = Pick & Omit, "ownerId" | "selectedPodId" | "selectedContainer" | "namespace">; + +interface Dependencies { + createDockTab: (rawTabDesc: DockTabCreate, addNumber?: boolean) => DockTab; + setLogTabData: (tabId: string, data: LogTabData) => void; +} + +function createLogsTab({ createDockTab, setLogTabData }: Dependencies, title: string, data: CreateLogsTabData): TabId { + const id = `log-tab-${uuid.v4()}`; + + runInAction(() => { + createDockTab({ + id, + title, + kind: TabKind.POD_LOGS, + }, false); + setLogTabData(id, { + showTimestamps: false, + showPrevious: false, + ...data, + }); + }); + + return id; +} + +const createLogsTabInjectable = getInjectable({ + instantiate: (di) => bind(createLogsTab, null, { + createDockTab: di.inject(createDockTabInjectable), + setLogTabData: di.inject(setLogTabDataInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default createLogsTabInjectable; diff --git a/src/renderer/components/dock/logs/create-pod-logs-tab.injectable.ts b/src/renderer/components/dock/logs/create-pod-logs-tab.injectable.ts new file mode 100644 index 0000000000..78beaddd47 --- /dev/null +++ b/src/renderer/components/dock/logs/create-pod-logs-tab.injectable.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import type { Pod, IPodContainer } from "../../../../common/k8s-api/endpoints"; +import { bind } from "../../../utils"; +import type { TabId } from "../dock/store"; +import createLogsTabInjectable, { CreateLogsTabData } from "./create-logs-tab.injectable"; + +export interface PodLogsTabData { + selectedPod: Pod; + selectedContainer: IPodContainer; +} + +interface Dependencies { + createLogsTab: (title: string, data: CreateLogsTabData) => TabId; +} + +function createPodLogsTab({ createLogsTab }: Dependencies, { selectedPod, selectedContainer }: PodLogsTabData): TabId { + const podOwner = selectedPod.getOwnerRefs()[0]; + + return createLogsTab(`Pod ${selectedPod.getName()}`, { + ownerId: podOwner?.uid, + namespace: selectedPod.getNs(), + selectedContainer: selectedContainer.name, + selectedPodId: selectedPod.getId(), + }); +} + +const createPodLogsTabInjectable = getInjectable({ + instantiate: (di) => bind(createPodLogsTab, null, { + createLogsTab: di.inject(createLogsTabInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default createPodLogsTabInjectable; diff --git a/src/renderer/components/dock/logs/create-workload-logs-tab.injectable.ts b/src/renderer/components/dock/logs/create-workload-logs-tab.injectable.ts new file mode 100644 index 0000000000..a131a26706 --- /dev/null +++ b/src/renderer/components/dock/logs/create-workload-logs-tab.injectable.ts @@ -0,0 +1,44 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { podsStore } from "../../+workloads-pods/pods.store"; +import type { WorkloadKubeObject } from "../../../../common/k8s-api/workload-kube-object"; +import { bind } from "../../../utils"; +import type { TabId } from "../dock/store"; +import createLogsTabInjectable, { CreateLogsTabData } from "./create-logs-tab.injectable"; + +export interface WorkloadLogsTabData { + workload: WorkloadKubeObject +} + +interface Dependencies { + createLogsTab: (title: string, data: CreateLogsTabData) => TabId; +} + +function createWorkloadLogsTab({ createLogsTab }: Dependencies, { workload }: WorkloadLogsTabData): TabId | undefined { + const pods = podsStore.getPodsByOwnerId(workload.getId()); + + if (pods.length === 0) { + return undefined; + } + + const selectedPod = pods[0]; + + return createLogsTab(`${workload.kind} ${selectedPod.getName()}`, { + selectedContainer: selectedPod.getAllContainers()[0].name, + selectedPodId: selectedPod.getId(), + namespace: selectedPod.getNs(), + ownerId: workload.getId(), + }); +} + +const createWorkloadLogsTabInjectable = getInjectable({ + instantiate: (di) => bind(createWorkloadLogsTab, null, { + createLogsTab: di.inject(createLogsTabInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default createWorkloadLogsTabInjectable; diff --git a/src/renderer/components/dock/logs/dock-tab.tsx b/src/renderer/components/dock/logs/dock-tab.tsx deleted file mode 100644 index 1ae62b4fe6..0000000000 --- a/src/renderer/components/dock/logs/dock-tab.tsx +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { observer } from "mobx-react"; -import { boundMethod } from "../../../utils"; -import { InfoPanel } from "../info-panel"; -import { LogResourceSelector } from "./resource-selector"; -import { LogList } from "./list"; -import { LogSearch } from "./search"; -import { LogControls } from "./controls"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import logsViewModelInjectable from "./logs-view-model.injectable"; -import type { LogTabViewModel } from "./logs-view-model"; -import type { DockTab } from "../dock-store/dock.store"; - -export interface LogsDockTabProps { - className?: string; - tab: DockTab; -} - -interface Dependencies { - model: LogTabViewModel; -} - -@observer -class NonInjectedLogsDockTab extends React.Component { - private logListElement = React.createRef(); // A reference for VirtualList component - - componentDidMount(): void { - this.props.model.reloadLogs(); - } - - componentWillUnmount(): void { - this.props.model.stopLoadingLogs(); - } - - /** - * Scrolling to active overlay (search word highlight) - */ - @boundMethod - scrollToOverlay() { - const { activeOverlayLine } = this.props.model.searchStore; - - if (!this.logListElement.current || activeOverlayLine === undefined) return; - // Scroll vertically - this.logListElement.current.scrollToItem(activeOverlayLine, "center"); - // Scroll horizontally in timeout since virtual list need some time to prepare its contents - setTimeout(() => { - const overlay = document.querySelector(".PodLogs .list span.active"); - - if (!overlay) return; - overlay.scrollIntoViewIfNeeded(); - }, 100); - } - - render() { - const { model, tab } = this.props; - const { logTabData } = model; - const data = logTabData.get(); - - if (!data) { - return null; - } - - return ( -
- - - -
- )} - showSubmitClose={false} - showButtons={false} - showStatusPanel={false} - /> - - - - ); - } -} - -export const LogsDockTab = withInjectables(NonInjectedLogsDockTab, { - getProps: (di, props) => ({ - model: di.inject(logsViewModelInjectable, { - tabId: props.tab.id, - }), - ...props, - }), -}); diff --git a/src/renderer/components/dock/logs/get-log-tab-data.injectable.ts b/src/renderer/components/dock/logs/get-log-tab-data.injectable.ts index e706fdaafb..8a8dde1368 100644 --- a/src/renderer/components/dock/logs/get-log-tab-data.injectable.ts +++ b/src/renderer/components/dock/logs/get-log-tab-data.injectable.ts @@ -3,10 +3,22 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { LogTabData, LogTabStore } from "./tab-store"; import logTabStoreInjectable from "./tab-store.injectable"; +interface Dependencies { + logTabStore: LogTabStore; +} + +function getLogTabData({ logTabStore }: Dependencies, tabId: string): LogTabData { + return logTabStore.getData(tabId); +} + const getLogTabDataInjectable = getInjectable({ - instantiate: (di) => di.inject(logTabStoreInjectable).getData, + instantiate: (di) => bind(getLogTabData, null, { + logTabStore: di.inject(logTabStoreInjectable), + }), lifecycle: lifecycleEnum.singleton, }); diff --git a/src/renderer/components/dock/logs/get-logs-without-timestamps.injectable.ts b/src/renderer/components/dock/logs/get-logs-without-timestamps.injectable.ts index 1be01ff443..cbe0596aec 100644 --- a/src/renderer/components/dock/logs/get-logs-without-timestamps.injectable.ts +++ b/src/renderer/components/dock/logs/get-logs-without-timestamps.injectable.ts @@ -3,10 +3,22 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { LogStore } from "./store"; import logStoreInjectable from "./store.injectable"; +interface Dependencies { + logStore: LogStore; +} + +function getLogsWithoutTimestamps({ logStore }: Dependencies, tabId: string): string[] { + return logStore.getLogsWithoutTimestamps(tabId); +} + const getLogsWithoutTimestampsInjectable = getInjectable({ - instantiate: (di) => di.inject(logStoreInjectable).getLogsWithoutTimestampsByTabId, + instantiate: (di) => bind(getLogsWithoutTimestamps, null, { + logStore: di.inject(logStoreInjectable), + }), lifecycle: lifecycleEnum.singleton, }); diff --git a/src/renderer/components/dock/logs/get-logs.injectable.ts b/src/renderer/components/dock/logs/get-logs.injectable.ts index 659a87c46f..9337a465a1 100644 --- a/src/renderer/components/dock/logs/get-logs.injectable.ts +++ b/src/renderer/components/dock/logs/get-logs.injectable.ts @@ -3,10 +3,22 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { LogStore } from "./store"; import logStoreInjectable from "./store.injectable"; +interface Dependencies { + logStore: LogStore; +} + +function getLogs({ logStore }: Dependencies, tabId: string): string[] { + return logStore.getLogs(tabId); +} + const getLogsInjectable = getInjectable({ - instantiate: (di) => di.inject(logStoreInjectable).getLogsByTabId, + instantiate: (di) => bind(getLogs, null, { + logStore: di.inject(logStoreInjectable), + }), lifecycle: lifecycleEnum.singleton, }); diff --git a/src/renderer/components/dock/logs/get-timestamp-split-logs.injectable.ts b/src/renderer/components/dock/logs/get-timestamp-split-logs.injectable.ts index 7cdac68327..bc28d3ebf2 100644 --- a/src/renderer/components/dock/logs/get-timestamp-split-logs.injectable.ts +++ b/src/renderer/components/dock/logs/get-timestamp-split-logs.injectable.ts @@ -3,10 +3,22 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { LogStore } from "./store"; import logStoreInjectable from "./store.injectable"; +interface Dependencies { + logStore: LogStore; +} + +function getTimestampSplitLogs({ logStore }: Dependencies, tabId: string): [string, string][] { + return logStore.getTimestampSplitLogs(tabId); +} + const getTimestampSplitLogsInjectable = getInjectable({ - instantiate: (di) => di.inject(logStoreInjectable).getTimestampSplitLogsByTabId, + instantiate: (di) => bind(getTimestampSplitLogs, null, { + logStore: di.inject(logStoreInjectable), + }), lifecycle: lifecycleEnum.singleton, }); diff --git a/src/renderer/components/dock/logs/is-logs-tab-data-valid.injectable.ts b/src/renderer/components/dock/logs/is-logs-tab-data-valid.injectable.ts new file mode 100644 index 0000000000..d0b0335f76 --- /dev/null +++ b/src/renderer/components/dock/logs/is-logs-tab-data-valid.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { bind } from "../../../utils"; +import type { TabId } from "../dock/store"; +import type { LogTabStore } from "./tab-store"; +import logTabStoreInjectable from "./tab-store.injectable"; + +interface Dependencies { + logTabStore: LogTabStore; +} + +function isLogsTabDataValid({ logTabStore }: Dependencies, tabId: TabId) { + return logTabStore.isDataValid(tabId); +} + +const isLogsTabDataValidInjectable = getInjectable({ + instantiate: (di) => bind(isLogsTabDataValid, null, { + logTabStore: di.inject(logTabStoreInjectable), + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default isLogsTabDataValidInjectable; diff --git a/src/renderer/components/dock/logs/load-logs.injectable.ts b/src/renderer/components/dock/logs/load-logs.injectable.ts index de92239454..8b7f2e4eae 100644 --- a/src/renderer/components/dock/logs/load-logs.injectable.ts +++ b/src/renderer/components/dock/logs/load-logs.injectable.ts @@ -3,10 +3,24 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import type { IComputedValue } from "mobx"; +import { bind } from "../../../utils"; +import type { LogStore } from "./store"; import logStoreInjectable from "./store.injectable"; +import type { LogTabData } from "./tab-store"; + +interface Dependencies { + logStore: LogStore; +} + +function loadLogs({ logStore }: Dependencies, tabId: string, logTabData: IComputedValue): Promise { + return logStore.load(tabId, logTabData); +} const loadLogsInjectable = getInjectable({ - instantiate: (di) => di.inject(logStoreInjectable).load, + instantiate: (di) => bind(loadLogs, null, { + logStore: di.inject(logStoreInjectable), + }), lifecycle: lifecycleEnum.singleton, }); diff --git a/src/renderer/components/dock/logs/log-tab-data.validator.ts b/src/renderer/components/dock/logs/log-tab-data.validator.ts new file mode 100644 index 0000000000..2b8db255e0 --- /dev/null +++ b/src/renderer/components/dock/logs/log-tab-data.validator.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import Joi from "joi"; + +export const logTabDataValidator = Joi.object({ + ownerId: Joi + .string() + .optional(), + selectedPodId: Joi + .string() + .required(), + namespace: Joi + .string() + .required(), + selectedContainer: Joi + .string() + .optional(), + showTimestamps: Joi + .boolean() + .required(), + previous: Joi + .boolean() + .required(), +}); diff --git a/src/renderer/components/dock/logs/logs-view-model.injectable.ts b/src/renderer/components/dock/logs/logs-view-model.injectable.ts index 7516e47124..93e2008678 100644 --- a/src/renderer/components/dock/logs/logs-view-model.injectable.ts +++ b/src/renderer/components/dock/logs/logs-view-model.injectable.ts @@ -4,16 +4,17 @@ */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; import { LogTabViewModel } from "./logs-view-model"; -import type { TabId } from "../dock-store/dock.store"; +import type { TabId } from "../dock/store"; import getLogsInjectable from "./get-logs.injectable"; import getLogsWithoutTimestampsInjectable from "./get-logs-without-timestamps.injectable"; import getTimestampSplitLogsInjectable from "./get-timestamp-split-logs.injectable"; -import reloadLoadsInjectable from "./reload-logs.injectable"; +import reloadLogsInjectable from "./reload-logs.injectable"; import getLogTabDataInjectable from "./get-log-tab-data.injectable"; import loadLogsInjectable from "./load-logs.injectable"; import setLogTabDataInjectable from "./set-log-tab-data.injectable"; import updateTabNameInjectable from "./update-tab-name.injectable"; import stopLoadingLogsInjectable from "./stop-loading-logs.injectable"; +import { podsStore } from "../../+workloads-pods/pods.store"; export interface InstantiateArgs { tabId: TabId; @@ -24,12 +25,14 @@ const logsViewModelInjectable = getInjectable({ getLogs: di.inject(getLogsInjectable), getLogsWithoutTimestamps: di.inject(getLogsWithoutTimestampsInjectable), getTimestampSplitLogs: di.inject(getTimestampSplitLogsInjectable), - reloadLogs: di.inject(reloadLoadsInjectable), + reloadLogs: di.inject(reloadLogsInjectable), getLogTabData: di.inject(getLogTabDataInjectable), setLogTabData: di.inject(setLogTabDataInjectable), loadLogs: di.inject(loadLogsInjectable), updateTabName: di.inject(updateTabNameInjectable), stopLoadingLogs: di.inject(stopLoadingLogsInjectable), + getPodById: id => podsStore.getById(id), + getPodsByOwnerId: id => podsStore.getPodsByOwnerId(id), }), lifecycle: lifecycleEnum.transient, }); diff --git a/src/renderer/components/dock/logs/logs-view-model.ts b/src/renderer/components/dock/logs/logs-view-model.ts index 6c2f182355..02185c7152 100644 --- a/src/renderer/components/dock/logs/logs-view-model.ts +++ b/src/renderer/components/dock/logs/logs-view-model.ts @@ -2,10 +2,11 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { LogTabData } from "./tab.store"; +import type { LogTabData } from "./tab-store"; import { computed, IComputedValue } from "mobx"; -import type { TabId } from "../dock-store/dock.store"; +import type { TabId } from "../dock/store"; import { SearchStore } from "../../../search-store/search-store"; +import type { Pod } from "../../../../common/k8s-api/endpoints"; export interface LogTabViewModelDependencies { getLogs: (tabId: TabId) => string[]; @@ -15,8 +16,10 @@ export interface LogTabViewModelDependencies { setLogTabData: (tabId: TabId, data: LogTabData) => void; loadLogs: (tabId: TabId, logTabData: IComputedValue) => Promise; reloadLogs: (tabId: TabId, logTabData: IComputedValue) => Promise; - updateTabName: (tabId: TabId) => void; + updateTabName: (tabId: TabId, pod: Pod) => void; stopLoadingLogs: (tabId: TabId) => void; + getPodById: (id: string) => Pod | undefined; + getPodsByOwnerId: (id: string) => Pod[]; } export class LogTabViewModel { @@ -26,6 +29,28 @@ export class LogTabViewModel { readonly logsWithoutTimestamps = computed(() => this.dependencies.getLogsWithoutTimestamps(this.tabId)); readonly timestampSplitLogs = computed(() => this.dependencies.getTimestampSplitLogs(this.tabId)); readonly logTabData = computed(() => this.dependencies.getLogTabData(this.tabId)); + readonly pods = computed(() => { + const data = this.logTabData.get(); + + if (!data) { + return []; + } + + if (typeof data.ownerId === "string") { + return this.dependencies.getPodsByOwnerId(data.ownerId); + } + + return [this.dependencies.getPodById(data.selectedPodId)]; + }); + readonly pod = computed(() => { + const data = this.logTabData.get(); + + if (!data) { + return undefined; + } + + return this.dependencies.getPodById(data.selectedPodId); + }); readonly searchStore = new SearchStore(); updateLogTabData = (partialData: Partial) => { @@ -34,6 +59,6 @@ export class LogTabViewModel { loadLogs = () => this.dependencies.loadLogs(this.tabId, this.logTabData); reloadLogs = () => this.dependencies.reloadLogs(this.tabId, this.logTabData); - updateTabName = () => this.dependencies.updateTabName(this.tabId); + updateTabName = () => this.dependencies.updateTabName(this.tabId, this.pod.get()); stopLoadingLogs = () => this.dependencies.stopLoadingLogs(this.tabId); } diff --git a/src/renderer/components/dock/logs/reload-logs.injectable.ts b/src/renderer/components/dock/logs/reload-logs.injectable.ts index 9fa917bf4b..4ed8874e59 100644 --- a/src/renderer/components/dock/logs/reload-logs.injectable.ts +++ b/src/renderer/components/dock/logs/reload-logs.injectable.ts @@ -3,11 +3,25 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import type { IComputedValue } from "mobx"; +import { bind } from "../../../utils"; +import type { LogStore } from "./store"; import logStoreInjectable from "./store.injectable"; +import type { LogTabData } from "./tab-store"; -const reloadLoadsInjectable = getInjectable({ - instantiate: (di) => di.inject(logStoreInjectable).reload, +interface Dependencies { + logStore: LogStore; +} + +function reloadLogs({ logStore }: Dependencies, tabId: string, logTabData: IComputedValue): Promise { + return logStore.reload(tabId, logTabData); +} + +const reloadLogsInjectable = getInjectable({ + instantiate: (di) => bind(reloadLogs, null, { + logStore: di.inject(logStoreInjectable), + }), lifecycle: lifecycleEnum.singleton, }); -export default reloadLoadsInjectable; +export default reloadLogsInjectable; diff --git a/src/renderer/components/dock/logs/resource-selector.tsx b/src/renderer/components/dock/logs/resource-selector.tsx index bca2c30f45..f664c42862 100644 --- a/src/renderer/components/dock/logs/resource-selector.tsx +++ b/src/renderer/components/dock/logs/resource-selector.tsx @@ -8,11 +8,10 @@ import "./resource-selector.scss"; import React, { useEffect } from "react"; import { observer } from "mobx-react"; -import { Pod } from "../../../../common/k8s-api/endpoints"; import { Badge } from "../../badge"; import { Select, SelectOption } from "../../select"; -import { podsStore } from "../../+workloads-pods/pods.store"; import type { LogTabViewModel } from "./logs-view-model"; +import { action } from "mobx"; export interface LogResourceSelectorProps { model: LogTabViewModel; @@ -25,27 +24,26 @@ export const LogResourceSelector = observer(({ model }: LogResourceSelectorProps return null; } - const { selectedPod, selectedContainer, pods } = tabData; - const pod = new Pod(selectedPod); + const { selectedContainer } = tabData; + const pods = model.pods.get(); + const pod = model.pod.get(); const containers = pod.getContainers(); const initContainers = pod.getInitContainers(); - const onContainerChange = (option: SelectOption) => { + const onContainerChange = (option: SelectOption) => { model.updateLogTabData({ - selectedContainer: containers - .concat(initContainers) - .find(container => container.name === option.value), + selectedContainer: option.value, }); model.reloadLogs(); }; - const onPodChange = (option: SelectOption) => { - const selectedPod = podsStore.getByName(option.value, pod.getNs()); - - model.updateLogTabData({ selectedPod }); + const onPodChange = action((option: SelectOption) => { + model.updateLogTabData({ + selectedPodId: option.value, + }); model.updateTabName(); - }; + }); const getSelectOptions = (items: string[]) => { return items.map(item => { @@ -76,7 +74,7 @@ export const LogResourceSelector = observer(({ model }: LogResourceSelectorProps useEffect(() => { model.reloadLogs(); - }, [selectedPod]); + }, [pod.getId()]); return (
@@ -93,7 +91,7 @@ export const LogResourceSelector = observer(({ model }: LogResourceSelectorProps Container