diff --git a/src/common/k8s-api/endpoints/pods.api.ts b/src/common/k8s-api/endpoints/pods.api.ts index 2c1bfdc6e8..a5ecf97a06 100644 --- a/src/common/k8s-api/endpoints/pods.api.ts +++ b/src/common/k8s-api/endpoints/pods.api.ts @@ -27,11 +27,11 @@ import type { KubeJsonApiData } from "../kube-json-api"; import { isClusterPageContext } from "../../utils/cluster-id-url-parsing"; export class PodsApi extends KubeApi { - async getLogs(params: { namespace: string; name: string }, query?: IPodLogsQuery): Promise { + getLogs = async (params: { namespace: string; name: string }, query?: IPodLogsQuery): Promise => { const path = `${this.getUrl(params)}/log`; return this.request.get(path, { query }); - } + }; } export function getMetricsForPods(pods: Pod[], namespace: string, selector = "pod, namespace"): Promise { diff --git a/src/renderer/components/dock/__test__/log-resource-selector.test.tsx b/src/renderer/components/dock/__test__/log-resource-selector.test.tsx index d856768a2b..462442c270 100644 --- a/src/renderer/components/dock/__test__/log-resource-selector.test.tsx +++ b/src/renderer/components/dock/__test__/log-resource-selector.test.tsx @@ -58,7 +58,6 @@ const getComponent = (tabData: LogTabData) => { tabId="tabId" tabData={tabData} save={jest.fn()} - reload={jest.fn()} /> ); }; diff --git a/src/renderer/components/dock/dock-store/dock-storage/dock-storage.injectable.ts b/src/renderer/components/dock/dock-store/dock-storage/dock-storage.injectable.ts new file mode 100644 index 0000000000..59eb50a99d --- /dev/null +++ b/src/renderer/components/dock/dock-store/dock-storage/dock-storage.injectable.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import createStorageInjectable from "../../../../utils/create-storage/create-storage.injectable"; +import { DockStorageState, TabKind } from "../dock.store"; + +const dockStorageInjectable = getInjectable({ + instantiate: (di) => { + const createStorage = di.inject(createStorageInjectable); + + return createStorage("dock", { + height: 300, + tabs: [ + { + id: "terminal", + kind: TabKind.TERMINAL, + title: "Terminal", + pinned: false, + }, + ], + }); + }, + + lifecycle: lifecycleEnum.singleton, +}); + +export default dockStorageInjectable; diff --git a/src/renderer/components/dock/dock-store/dock-store.injectable.ts b/src/renderer/components/dock/dock-store/dock-store.injectable.ts index 58bf854485..d9468083b2 100644 --- a/src/renderer/components/dock/dock-store/dock-store.injectable.ts +++ b/src/renderer/components/dock/dock-store/dock-store.injectable.ts @@ -19,29 +19,14 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { DockStorageState, DockStore, TabKind } from "./dock.store"; -import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; +import { DockStore } from "./dock.store"; +import dockStorageInjectable from "./dock-storage/dock-storage.injectable"; const dockStoreInjectable = getInjectable({ - instantiate: (di) => { - const createStorage = di.inject(createStorageInjectable); - - const storage = createStorage("dock", { - height: 300, - tabs: [ - { - id: "terminal", - kind: TabKind.TERMINAL, - title: "Terminal", - pinned: false, - }, - ], - }); - - return new DockStore({ - storage, - }); - }, + instantiate: (di) => + new DockStore({ + storage: di.inject(dockStorageInjectable), + }), lifecycle: lifecycleEnum.singleton, }); diff --git a/src/renderer/components/dock/dock-store/dock.store.ts b/src/renderer/components/dock/dock-store/dock.store.ts index e3cc16acb1..025ece7499 100644 --- a/src/renderer/components/dock/dock-store/dock.store.ts +++ b/src/renderer/components/dock/dock-store/dock.store.ts @@ -131,16 +131,18 @@ export class DockStore implements DockStorageState { return this.dependencies.storage.whenReady; } + @computed get isOpen(): boolean { - return this.dependencies.storage.get().isOpen; + return this.dependencies.storage.value.isOpen; } set isOpen(isOpen: boolean) { this.dependencies.storage.merge({ isOpen }); } + @computed get height(): number { - return this.dependencies.storage.get().height; + return this.dependencies.storage.value.height; } set height(height: number) { @@ -149,21 +151,23 @@ export class DockStore implements DockStorageState { }); } + @computed get tabs(): DockTab[] { - return this.dependencies.storage.get().tabs; + return this.dependencies.storage.value.tabs; } set tabs(tabs: DockTab[]) { this.dependencies.storage.merge({ tabs }); } + @computed get selectedTabId(): TabId | undefined { - return this.dependencies.storage.get().selectedTabId - || ( - this.tabs.length > 0 - ? this.tabs[0]?.id - : undefined - ); + const storageData = this.dependencies.storage.value; + + return ( + storageData.selectedTabId || + (this.tabs.length > 0 ? this.tabs[0]?.id : undefined) + ); } set selectedTabId(tabId: TabId) { 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 61a037f3fd..38f1897f0c 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 @@ -58,8 +58,9 @@ export class DockTabStore { // auto-save to local-storage if (storageKey) { this.storage = this.dependencies.createStorage(storageKey, {}); + this.storage.whenReady.then(() => { - this.data.replace(this.storage.get()); + this.data.replace(this.storage.value); reaction(() => this.toJSON(), data => this.storage.set(data)); }); } diff --git a/src/renderer/components/dock/dock.tsx b/src/renderer/components/dock/dock.tsx index c636352b30..29dfc130a2 100644 --- a/src/renderer/components/dock/dock.tsx +++ b/src/renderer/components/dock/dock.tsx @@ -101,7 +101,7 @@ class NonInjectedDock extends React.Component { case TabKind.UPGRADE_CHART: return ; case TabKind.POD_LOGS: - return ; + return ; case TabKind.TERMINAL: return ; } diff --git a/src/renderer/components/dock/log-controls.tsx b/src/renderer/components/dock/log-controls.tsx index c8904be314..27f360bd66 100644 --- a/src/renderer/components/dock/log-controls.tsx +++ b/src/renderer/components/dock/log-controls.tsx @@ -37,7 +37,6 @@ interface Props { tabData?: LogTabData logs: string[] save: (data: Partial) => void - reload: () => void } interface Dependencies { @@ -45,7 +44,7 @@ interface Dependencies { } const NonInjectedLogControls = observer((props: Props & Dependencies) => { - const { tabData, save, reload, logs, logStore } = props; + const { tabData, save, logs, logStore } = props; if (!tabData) { return null; @@ -61,7 +60,7 @@ const NonInjectedLogControls = observer((props: Props & Dependencies) => { const togglePrevious = () => { save({ previous: !previous }); - reload(); + logStore.reload(); }; const downloadLogs = () => { diff --git a/src/renderer/components/dock/log-list.tsx b/src/renderer/components/dock/log-list.tsx index 85b04e3531..485c996cf7 100644 --- a/src/renderer/components/dock/log-list.tsx +++ b/src/renderer/components/dock/log-list.tsx @@ -32,7 +32,6 @@ import type { Align, ListOnScrollProps } from "react-window"; import { SearchStore } from "../../search-store/search-store"; import { UserStore } from "../../../common/user-store"; import { array, boundMethod, cssNames } from "../../utils"; -import { Spinner } from "../spinner"; import { VirtualList } from "../virtual-list"; import type { LogStore } from "./log-store/log.store"; import type { LogTabStore } from "./log-tab-store/log-tab.store"; @@ -44,8 +43,6 @@ import searchStoreInjectable from "../../search-store/search-store.injectable"; interface Props { logs: string[] - isLoading: boolean - load: () => void id: string } @@ -58,7 +55,7 @@ interface Dependencies { } @observer -class NonInjectedLogList extends React.Component { +export class NonInjectedLogList extends React.Component { @observable isJumpButtonVisible = false; @observable isLastLineVisible = true; @@ -165,7 +162,7 @@ class NonInjectedLogList extends React.Component { const { scrollOffset } = props; if (scrollOffset === 0) { - this.props.load(); + this.props.logStore.load(); } }; @@ -241,18 +238,8 @@ class NonInjectedLogList extends React.Component { }; render() { - const { isLoading } = this.props; - const isInitLoading = isLoading && !this.logs.length; const rowHeights = array.filled(this.logs.length, this.lineHeight); - if (isInitLoading) { - return ( -
- -
- ); - } - if (!this.logs.length) { return (
@@ -262,7 +249,7 @@ class NonInjectedLogList extends React.Component { } return ( -
+
) => void - reload: () => void } interface Dependencies { logTabStore: LogTabStore + reloadLogs: () => Promise } const NonInjectedLogResourceSelector = observer((props: Props & Dependencies) => { - const { tabData, save, reload, tabId, logTabStore } = props; + const { tabData, save, tabId, logTabStore, reloadLogs } = props; const { selectedPod, selectedContainer, pods } = tabData; const pod = new Pod(selectedPod); const containers = pod.getContainers(); @@ -57,7 +58,8 @@ const NonInjectedLogResourceSelector = observer((props: Props & Dependencies) => .concat(initContainers) .find(container => container.name === option.value), }); - reload(); + + reloadLogs(); }; const onPodChange = (option: SelectOption) => { @@ -96,7 +98,7 @@ const NonInjectedLogResourceSelector = observer((props: Props & Dependencies) => ]; useEffect(() => { - reload(); + reloadLogs(); }, [selectedPod]); return ( @@ -128,6 +130,7 @@ export const LogResourceSelector = withInjectables( { getProps: (di, props) => ({ logTabStore: di.inject(logTabStoreInjectable), + reloadLogs: di.inject(logStoreInjectable).reload, ...props, }), }, diff --git a/src/renderer/components/dock/log-store/call-for-logs/call-for-logs.injectable.ts b/src/renderer/components/dock/log-store/call-for-logs/call-for-logs.injectable.ts new file mode 100644 index 0000000000..d26263dd74 --- /dev/null +++ b/src/renderer/components/dock/log-store/call-for-logs/call-for-logs.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { podsApi } from "../../../../../common/k8s-api/endpoints"; + +const callForLogsInjectable = getInjectable({ + instantiate: () => podsApi.getLogs, + lifecycle: lifecycleEnum.singleton, +}); + +export default callForLogsInjectable; diff --git a/src/renderer/components/dock/log-store/log-store.injectable.ts b/src/renderer/components/dock/log-store/log-store.injectable.ts index a0c17acb57..a493c8e5e7 100644 --- a/src/renderer/components/dock/log-store/log-store.injectable.ts +++ b/src/renderer/components/dock/log-store/log-store.injectable.ts @@ -22,11 +22,13 @@ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; import { LogStore } from "./log.store"; import logTabStoreInjectable from "../log-tab-store/log-tab-store.injectable"; import dockStoreInjectable from "../dock-store/dock-store.injectable"; +import callForLogsInjectable from "./call-for-logs/call-for-logs.injectable"; const logStoreInjectable = getInjectable({ instantiate: (di) => new LogStore({ logTabStore: di.inject(logTabStoreInjectable), dockStore: di.inject(dockStoreInjectable), + callForLogs: di.inject(callForLogsInjectable), }), lifecycle: lifecycleEnum.singleton, diff --git a/src/renderer/components/dock/log-store/log.store.ts b/src/renderer/components/dock/log-store/log.store.ts index 9f2a26d2af..9c1d46bb57 100644 --- a/src/renderer/components/dock/log-store/log.store.ts +++ b/src/renderer/components/dock/log-store/log.store.ts @@ -21,7 +21,7 @@ import { autorun, computed, observable, makeObservable } from "mobx"; -import { IPodLogsQuery, Pod, podsApi } from "../../../../common/k8s-api/endpoints"; +import { IPodLogsQuery, Pod } from "../../../../common/k8s-api/endpoints"; import { autoBind, interval } from "../../../utils"; import { DockStore, TabId, TabKind } from "../dock-store/dock.store"; import type { LogTabStore } from "../log-tab-store/log-tab.store"; @@ -33,6 +33,7 @@ const logLinesToLoad = 500; interface Dependencies { logTabStore: LogTabStore dockStore: DockStore + callForLogs: ({ namespace, name }: { namespace: string, name: string }, query: IPodLogsQuery) => Promise } export class LogStore { @@ -79,9 +80,10 @@ export class LogStore { * Each time it increasing it's number, caused to fetch more logs. * Also, it handles loading errors, rewriting whole logs with error * messages - * @param tabId */ - load = async (tabId: TabId) => { + load = async () => { + const tabId = this.dependencies.dockStore.selectedTabId; + try { const logs = await this.loadLogs(tabId, { tailLines: this.lines + logLinesToLoad, @@ -127,12 +129,13 @@ export class LogStore { */ async loadLogs(tabId: TabId, params: Partial): Promise { const data = this.dependencies.logTabStore.getData(tabId); + const { selectedContainer, previous } = data; const pod = new Pod(data.selectedPod); const namespace = pod.getNs(); const name = pod.getName(); - const result = await podsApi.getLogs({ namespace, name }, { + const result = await this.dependencies.callForLogs({ namespace, name }, { ...params, timestamps: true, // Always setting timestamp to separate old logs from new ones container: selectedContainer.name, @@ -205,4 +208,10 @@ export class LogStore { clearLogs(tabId: TabId) { this.podLogs.delete(tabId); } + + reload = async () => { + this.clearLogs(this.dependencies.dockStore.selectedTabId); + + await this.load(); + }; } diff --git a/src/renderer/components/dock/log-store/reloaded-log-store.injectable.ts b/src/renderer/components/dock/log-store/reloaded-log-store.injectable.ts new file mode 100644 index 0000000000..159c4a415c --- /dev/null +++ b/src/renderer/components/dock/log-store/reloaded-log-store.injectable.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import logStoreInjectable from "./log-store.injectable"; + +const reloadedLogStoreInjectable = getInjectable({ + instantiate: async (di) => { + const nonReloadedStore = di.inject(logStoreInjectable); + + await nonReloadedStore.reload(); + + return nonReloadedStore; + }, + + lifecycle: lifecycleEnum.transient, +}); + +export default reloadedLogStoreInjectable; diff --git a/src/renderer/components/dock/log-tab-store/log-tab.store.ts b/src/renderer/components/dock/log-tab-store/log-tab.store.ts index 7357bf6166..6f3f55426a 100644 --- a/src/renderer/components/dock/log-tab-store/log-tab.store.ts +++ b/src/renderer/components/dock/log-tab-store/log-tab.store.ts @@ -20,7 +20,7 @@ */ import uniqueId from "lodash/uniqueId"; -import { reaction } from "mobx"; +import { computed, makeObservable, reaction } from "mobx"; import { podsStore } from "../../+workloads-pods/pods.store"; import { IPodContainer, Pod } from "../../../../common/k8s-api/endpoints"; @@ -59,6 +59,14 @@ export class LogTabStore extends DockTabStore { }); reaction(() => podsStore.items.length, () => this.updateTabsData()); + + makeObservable(this, { + tabs: computed, + }); + } + + get tabs() { + return this.data.get(this.dependencies.dockStore.selectedTabId); } createPodTab({ selectedPod, selectedContainer }: PodLogsTabData): string { diff --git a/src/renderer/components/dock/logs.tsx b/src/renderer/components/dock/logs.tsx index ea982aa527..4991826867 100644 --- a/src/renderer/components/dock/logs.tsx +++ b/src/renderer/components/dock/logs.tsx @@ -20,87 +20,46 @@ */ import React from "react"; -import { observable, reaction, makeObservable } from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; - +import { observer } from "mobx-react"; import { boundMethod } from "../../utils"; -import type { DockTab } from "./dock-store/dock.store"; import { InfoPanel } from "./info-panel"; import { LogResourceSelector } from "./log-resource-selector"; -import { LogList } from "./log-list"; -import type { LogStore } from "./log-store/log.store"; +import { LogList, NonInjectedLogList } from "./log-list"; import { LogSearch } from "./log-search"; import { LogControls } from "./log-controls"; -import type { LogTabData, LogTabStore } from "./log-tab-store/log-tab.store"; import { withInjectables } from "@ogre-tools/injectable-react"; -import logTabStoreInjectable from "./log-tab-store/log-tab-store.injectable"; -import logStoreInjectable from "./log-store/log-store.injectable"; import type { SearchStore } from "../../search-store/search-store"; import searchStoreInjectable from "../../search-store/search-store.injectable"; +import { Spinner } from "../spinner"; +import logsViewModelInjectable from "./logs/logs-view-model/logs-view-model.injectable"; +import type { LogsViewModel } from "./logs/logs-view-model/logs-view-model"; interface Props { - className?: string - tab: DockTab + className?: string; } interface Dependencies { - logTabStore: LogTabStore - logStore: LogStore searchStore: SearchStore + model: LogsViewModel } @observer class NonInjectedLogs extends React.Component { - @observable isLoading = true; + private logListElement = React.createRef(); // A reference for VirtualList component - private logListElement = React.createRef(); // A reference for VirtualList component - - constructor(props: Props & Dependencies) { - super(props); - makeObservable(this); - } - - componentDidMount() { - disposeOnUnmount(this, - reaction(() => this.props.tab.id, this.reload, { fireImmediately: true }), - ); - } - - get tabId() { - return this.props.tab.id; - } - - load = async () => { - this.isLoading = true; - await this.props.logStore.load(this.tabId); - this.isLoading = false; - }; - - reload = async () => { - this.props.logStore.clearLogs(this.tabId); - await this.load(); - }; - - /** - * A function for various actions after search is happened - * @param query {string} A text from search field - */ - @boundMethod - onSearch() { - this.toOverlay(); + get model() { + return this.props.model; } /** * Scrolling to active overlay (search word highlight) */ @boundMethod - toOverlay() { + scrollToOverlay() { const { activeOverlayLine } = this.props.searchStore; if (!this.logListElement.current || activeOverlayLine === undefined) return; // Scroll vertically - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore this.logListElement.current.scrollToItem(activeOverlayLine, "center"); // Scroll horizontally in timeout since virtual list need some time to prepare its contents setTimeout(() => { @@ -111,33 +70,35 @@ class NonInjectedLogs extends React.Component { }, 100); } - renderResourceSelector(data?: LogTabData) { - if (!data) { + renderResourceSelector() { + const { tabs, logs, logsWithoutTimestamps, saveTab, tabId } = this.model; + + if (!tabs) { return null; } - const logs = this.props.logStore.logs; - const searchLogs = data.showTimestamps ? logs : this.props.logStore.logsWithoutTimestamps; + const searchLogs = tabs.showTimestamps ? logs : logsWithoutTimestamps; + const controls = (
this.props.logTabStore.setData(this.tabId, { ...data, ...newData })} - reload={this.reload} + tabId={tabId} + tabData={tabs} + save={saveTab} /> +
); return ( { } render() { - const logs = this.props.logStore.logs; - const data = this.props.logTabStore.getData(this.tabId); - - if (!data) { - this.reload(); - } + const { logs, tabs, tabId, saveTab } = this.model; return (
- {this.renderResourceSelector(data)} + {this.renderResourceSelector()} + + this.props.logTabStore.setData(this.tabId, { ...data, ...newData })} - reload={this.reload} + tabData={tabs} + save={saveTab} />
); } } + + export const Logs = withInjectables( NonInjectedLogs, { - getProps: (di, props) => ({ - logTabStore: di.inject(logTabStoreInjectable), - logStore: di.inject(logStoreInjectable), + + getPlaceholder: () => ( +
+ +
+ ), + + getProps: async (di, props) => ({ searchStore: di.inject(searchStoreInjectable), + model: await di.inject(logsViewModelInjectable), ...props, }), }, diff --git a/src/renderer/components/dock/logs/logs-view-model/logs-view-model.injectable.ts b/src/renderer/components/dock/logs/logs-view-model/logs-view-model.injectable.ts new file mode 100644 index 0000000000..30c4265fc0 --- /dev/null +++ b/src/renderer/components/dock/logs/logs-view-model/logs-view-model.injectable.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import dockStoreInjectable from "../../dock-store/dock-store.injectable"; +import logTabStoreInjectable from "../../log-tab-store/log-tab-store.injectable"; +import reloadedLogStoreInjectable from "../../log-store/reloaded-log-store.injectable"; +import { LogsViewModel } from "./logs-view-model"; + +const logsViewModelInjectable = getInjectable({ + instantiate: async (di) => new LogsViewModel({ + dockStore: di.inject(dockStoreInjectable), + logTabStore: di.inject(logTabStoreInjectable), + logStore: await di.inject(reloadedLogStoreInjectable), + }), + + lifecycle: lifecycleEnum.singleton, +}); + +export default logsViewModelInjectable; diff --git a/src/renderer/components/dock/logs/logs-view-model/logs-view-model.ts b/src/renderer/components/dock/logs/logs-view-model/logs-view-model.ts new file mode 100644 index 0000000000..7f51e0426a --- /dev/null +++ b/src/renderer/components/dock/logs/logs-view-model/logs-view-model.ts @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import type { LogTabData, LogTabStore } from "../../log-tab-store/log-tab.store"; +import type { LogStore } from "../../log-store/log.store"; +import { computed } from "mobx"; +import { makeObservable } from "mobx"; + +interface Dependencies { + dockStore: { selectedTabId: string }, + logTabStore: LogTabStore + logStore: LogStore +} + +export class LogsViewModel { + constructor(private dependencies: Dependencies) { + makeObservable(this, { + logs: computed, + logsWithoutTimestamps: computed, + tabs: computed, + tabId: computed, + }); + } + + get logs() { + return this.dependencies.logStore.logs; + } + + get logsWithoutTimestamps() { + return this.dependencies.logStore.logsWithoutTimestamps; + } + + get tabs() { + return this.dependencies.logTabStore.tabs; + } + + get tabId() { + return this.dependencies.dockStore.selectedTabId; + } + + saveTab = (newTabs: LogTabData) => { + this.dependencies.logTabStore.setData(this.tabId, { ...this.tabs, ...newTabs }); + }; +} diff --git a/src/renderer/components/item-object-list/item-list-layout.tsx b/src/renderer/components/item-object-list/item-list-layout.tsx index 814aecd652..c3aacfa17e 100644 --- a/src/renderer/components/item-object-list/item-list-layout.tsx +++ b/src/renderer/components/item-object-list/item-list-layout.tsx @@ -546,7 +546,10 @@ class NonInjectedItemListLayout extends React.Component( props: ItemListLayoutProps, ) { - return withInjectables>( + const InjectedItemListLayout = withInjectables< + Dependencies, + ItemListLayoutProps + >( NonInjectedItemListLayout, { @@ -556,5 +559,7 @@ export function ItemListLayout( ...props, }), }, - )(props); + ); + + return ; } diff --git a/src/renderer/components/kube-object-list-layout/kube-object-list-layout.tsx b/src/renderer/components/kube-object-list-layout/kube-object-list-layout.tsx index 129fc8cee2..93c5085d95 100644 --- a/src/renderer/components/kube-object-list-layout/kube-object-list-layout.tsx +++ b/src/renderer/components/kube-object-list-layout/kube-object-list-layout.tsx @@ -157,7 +157,10 @@ class NonInjectedKubeObjectListLayout extends React.Compon export function KubeObjectListLayout( props: KubeObjectListLayoutProps, ) { - return withInjectables>( + const InjectedKubeObjectListLayout = withInjectables< + Dependencies, + KubeObjectListLayoutProps + >( NonInjectedKubeObjectListLayout, { @@ -167,5 +170,7 @@ export function KubeObjectListLayout( ...props, }), }, - )(props); + ); + + return ; } diff --git a/src/renderer/components/kube-object-menu/kube-object-menu.tsx b/src/renderer/components/kube-object-menu/kube-object-menu.tsx index 33bde59816..eff4748a72 100644 --- a/src/renderer/components/kube-object-menu/kube-object-menu.tsx +++ b/src/renderer/components/kube-object-menu/kube-object-menu.tsx @@ -127,7 +127,7 @@ class NonInjectedKubeObjectMenu extends React.Co export function KubeObjectMenu( props: KubeObjectMenuProps, ) { - return withInjectables>( + const InjectedKubeObjectMenu = withInjectables>( NonInjectedKubeObjectMenu, { getProps: (di, props) => ({ @@ -142,5 +142,7 @@ export function KubeObjectMenu( ...props, }), }, - )(props); + ); + + return ; } diff --git a/src/renderer/components/table/table.tsx b/src/renderer/components/table/table.tsx index 3f6aefe21a..d068ac31c7 100644 --- a/src/renderer/components/table/table.tsx +++ b/src/renderer/components/table/table.tsx @@ -263,7 +263,7 @@ class NonInjectedTable extends React.Component & Dependen } export function Table(props: TableProps) { - return withInjectables>( + const InjectedTable = withInjectables>( NonInjectedTable, { @@ -272,6 +272,8 @@ export function Table(props: TableProps) { ...props, }), }, - )(props); + ); + + return ; } diff --git a/src/renderer/utils/storageHelper.ts b/src/renderer/utils/storageHelper.ts index 88022d054c..bd240cea39 100755 --- a/src/renderer/utils/storageHelper.ts +++ b/src/renderer/utils/storageHelper.ts @@ -20,7 +20,15 @@ */ // Helper for working with storages (e.g. window.localStorage, NodeJS/file-system, etc.) -import { action, comparer, makeObservable, observable, toJS, when } from "mobx"; +import { + action, + comparer, + computed, + makeObservable, + observable, + toJS, + when, +} from "mobx"; import produce, { Draft, isDraft } from "immer"; import { isEqual, isPlainObject } from "lodash"; import logger from "../../main/logger"; @@ -66,10 +74,6 @@ export class StorageHelper { this.storage = storage; - this.data.observe_(({ newValue, oldValue }) => { - this.onChange(newValue as T, oldValue as T); - }); - if (autoInit) { this.init(); } @@ -130,16 +134,25 @@ export class StorageHelper { } get(): T { + return this.value; + } + + @computed + get value(): T { return this.data.get() ?? this.defaultValue; } @action - set(value: T) { - if (this.isDefaultValue(value)) { + set(newValue: T) { + const oldValue = this.value; + + if (this.isDefaultValue(newValue)) { this.reset(); } else { - this.data.set(value); + this.data.set(newValue); } + + this.onChange(newValue, oldValue); } @action