diff --git a/packages/core/src/renderer/cluster-frame-context/cluster-frame-context.ts b/packages/core/src/renderer/cluster-frame-context/cluster-frame-context.ts
index e23bac5fd4..740be14e8e 100755
--- a/packages/core/src/renderer/cluster-frame-context/cluster-frame-context.ts
+++ b/packages/core/src/renderer/cluster-frame-context/cluster-frame-context.ts
@@ -7,10 +7,25 @@
* This type is used for KubeObjectStores
*/
export interface ClusterContext {
- readonly allNamespaces: string[]; // available / allowed namespaces from cluster.ts
- readonly contextNamespaces: string[]; // selected by user (see: namespace-select.tsx)
+ /**
+ * A computed getter for all the namespaces that this cluster knows about
+ */
+ readonly allNamespaces: string[];
+
+ /**
+ * The computed getter of namespaces that are currently selected
+ */
+ readonly contextNamespaces: string[];
+
readonly hasSelectedAll: boolean;
isLoadingAll(namespaces: string[]): boolean;
isGlobalWatchEnabled(): boolean;
}
+
+export interface NamespaceScopedClusterContext extends ClusterContext {
+ selectAllNamespaces(): void;
+ toggleNamespace(namespace: string): void;
+ selectNamespace(namespace: string): void;
+ deselectNamespace(namespace: string): void;
+}
diff --git a/packages/core/src/renderer/cluster-frame-context/for-namespaced-resources.injectable.ts b/packages/core/src/renderer/cluster-frame-context/for-namespaced-resources.injectable.ts
index 1eb85b3a43..bf8ff3bfc9 100644
--- a/packages/core/src/renderer/cluster-frame-context/for-namespaced-resources.injectable.ts
+++ b/packages/core/src/renderer/cluster-frame-context/for-namespaced-resources.injectable.ts
@@ -3,18 +3,21 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import type { ClusterContext } from "./cluster-frame-context";
+import type { NamespaceScopedClusterContext } from "./cluster-frame-context";
import namespaceStoreInjectable from "../components/+namespaces/store.injectable";
import hostedClusterInjectable from "./hosted-cluster.injectable";
import assert from "assert";
import { computed } from "mobx";
+import selectedNamespaceStorageInjectable from "../components/+namespaces/namespace-storage.injectable";
+import { toggle } from "../utils";
const clusterFrameContextForNamespacedResourcesInjectable = getInjectable({
id: "cluster-frame-context-for-namespaced-resources",
- instantiate: (di): ClusterContext => {
+ instantiate: (di): NamespaceScopedClusterContext => {
const cluster = di.inject(hostedClusterInjectable);
const namespaceStore = di.inject(namespaceStoreInjectable);
+ const selectedNamespaceStorage = di.inject(selectedNamespaceStorageInjectable);
assert(cluster, "This can only be injected within a cluster frame");
@@ -25,20 +28,37 @@ const clusterFrameContextForNamespacedResourcesInjectable = getInjectable({
}
if (namespaceStore.items.length > 0) {
- // namespaces from kubernetes api
+ // namespaces from kubernetes api
return namespaceStore.items.map((namespace) => namespace.getName());
}
// fallback to cluster resolved namespaces because we could not load list
return cluster.allowedNamespaces.slice();
});
- const contextNamespaces = computed(() => namespaceStore.contextNamespaces);
+ const contextNamespaces = computed(() => {
+ const storedState = selectedNamespaceStorage.get();
+
+ if (!storedState || storedState.length === 0) {
+ return allNamespaces.get();
+ }
+
+ const state = new Set(storedState);
+ const currentlyKnownNamespaces = new Set(allNamespaces.get());
+
+ for (const namespace of storedState) {
+ if (!currentlyKnownNamespaces.has(namespace)) {
+ state.delete(namespace);
+ }
+ }
+
+ return [...state];
+ });
const hasSelectedAll = computed(() => {
const namespaces = new Set(contextNamespaces.get());
return allNamespaces.get().length > 1
- && cluster.accessibleNamespaces.length === 0
- && allNamespaces.get().every(ns => namespaces.has(ns));
+ && cluster.accessibleNamespaces.length === 0
+ && allNamespaces.get().every(ns => namespaces.has(ns));
});
return {
@@ -48,6 +68,24 @@ const clusterFrameContextForNamespacedResourcesInjectable = getInjectable({
&& allNamespaces.get().every(ns => namespaces.includes(ns))
),
isGlobalWatchEnabled: () => cluster.isGlobalWatchEnabled,
+ selectAllNamespaces: () => {
+ selectedNamespaceStorage.set([]);
+ },
+ selectNamespace: (namespace) => {
+ selectedNamespaceStorage.set([namespace]);
+ },
+ toggleNamespace: (namespace) => {
+ const nextState = new Set(contextNamespaces.get());
+
+ toggle(nextState, namespace);
+ selectedNamespaceStorage.set([...nextState]);
+ },
+ deselectNamespace: (namespace) => {
+ const nextState = new Set(contextNamespaces.get());
+
+ nextState.delete(namespace);
+ selectedNamespaceStorage.set([...nextState]);
+ },
get allNamespaces() {
return allNamespaces.get();
},
diff --git a/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/filter-by-namespace.injectable.ts b/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/filter-by-namespace.injectable.ts
index fff1c37202..f099b109af 100644
--- a/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/filter-by-namespace.injectable.ts
+++ b/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/filter-by-namespace.injectable.ts
@@ -3,16 +3,16 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
-import namespaceStoreInjectable from "../store.injectable";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
export type FilterByNamespace = (namespace: string) => void;
const filterByNamespaceInjectable = getInjectable({
id: "filter-by-namespace",
instantiate: (di): FilterByNamespace => {
- const namespaceStore = di.inject(namespaceStoreInjectable);
+ const context = di.inject(clusterFrameContextForNamespacedResourcesInjectable);
- return (namespace) => namespaceStore.selectSingle(namespace);
+ return (namespace) => context.selectNamespace(namespace);
},
});
diff --git a/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/namespace-select-filter-model.injectable.ts b/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/namespace-select-filter-model.injectable.ts
index d84292410b..61bdcf0118 100644
--- a/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/namespace-select-filter-model.injectable.ts
+++ b/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/namespace-select-filter-model.injectable.ts
@@ -4,7 +4,6 @@
*/
import { namespaceSelectFilterModelFor } from "./namespace-select-filter-model";
import { getInjectable } from "@ogre-tools/injectable";
-import namespaceStoreInjectable from "../store.injectable";
import isMultiSelectionKeyInjectable from "./is-selection-key.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
@@ -12,7 +11,6 @@ const namespaceSelectFilterModelInjectable = getInjectable({
id: "namespace-select-filter-model",
instantiate: (di) => namespaceSelectFilterModelFor({
- namespaceStore: di.inject(namespaceStoreInjectable),
isMultiSelectionKey: di.inject(isMultiSelectionKeyInjectable),
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
}),
diff --git a/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/namespace-select-filter-model.tsx b/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/namespace-select-filter-model.tsx
index b9396f56a0..6819fc1361 100644
--- a/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/namespace-select-filter-model.tsx
+++ b/packages/core/src/renderer/components/+namespaces/namespace-select-filter-model/namespace-select-filter-model.tsx
@@ -5,16 +5,15 @@
import React from "react";
import type { IComputedValue } from "mobx";
import { observable, action, computed, comparer } from "mobx";
-import type { NamespaceStore } from "../store";
import type { ActionMeta, MultiValue } from "react-select";
import { Icon } from "../../icon";
import type { SelectOption } from "../../select";
import { observableCrate } from "../../../utils";
import type { IsMultiSelectionKey } from "./is-selection-key.injectable";
+import type { NamespaceScopedClusterContext } from "../../../cluster-frame-context/cluster-frame-context";
interface Dependencies {
context: NamespaceScopedClusterContext;
- namespaceStore: NamespaceStore;
isMultiSelectionKey: IsMultiSelectionKey;
}
@@ -45,7 +44,7 @@ enum SelectMenuState {
}
export function namespaceSelectFilterModelFor(dependencies: Dependencies): NamespaceSelectFilterModel {
- const { isMultiSelectionKey, namespaceStore, context } = dependencies;
+ const { isMultiSelectionKey, context } = dependencies;
let didToggle = false;
let isMultiSelection = false;
@@ -111,7 +110,7 @@ export function namespaceSelectFilterModelFor(dependencies: Dependencies): Names
onChange: (_, action) => {
switch (action.action) {
case "clear":
- namespaceStore.selectAll();
+ context.selectAllNamespaces();
break;
case "deselect-option":
case "select-option":
@@ -119,11 +118,11 @@ export function namespaceSelectFilterModelFor(dependencies: Dependencies): Names
didToggle = true;
if (action.option.value === selectAllNamespaces) {
- namespaceStore.selectAll();
+ context.selectAllNamespaces();
} else if (isMultiSelection) {
- namespaceStore.toggleSingle(action.option.value);
+ context.toggleNamespace(action.option.value);
} else {
- namespaceStore.selectSingle(action.option.value);
+ context.selectNamespace(action.option.value);
}
}
break;
diff --git a/packages/core/src/renderer/components/+namespaces/namespace-select-filter.test.tsx b/packages/core/src/renderer/components/+namespaces/namespace-select-filter.test.tsx
index 14751c13dd..064c6b6590 100644
--- a/packages/core/src/renderer/components/+namespaces/namespace-select-filter.test.tsx
+++ b/packages/core/src/renderer/components/+namespaces/namespace-select-filter.test.tsx
@@ -15,6 +15,8 @@ import type { Fetch } from "../../../common/fetch/fetch.injectable";
import fetchInjectable from "../../../common/fetch/fetch.injectable";
import { Namespace } from "../../../common/k8s-api/endpoints";
import { createMockResponseFromString } from "../../../test-utils/mock-responses";
+import type { NamespaceScopedClusterContext } from "../../cluster-frame-context/cluster-frame-context";
+import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
import createClusterInjectable from "../../cluster/create-cluster.injectable";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
@@ -43,6 +45,7 @@ function createNamespace(name: string): Namespace {
describe("", () => {
let di: DiContainer;
let namespaceStore: NamespaceStore;
+ let context: NamespaceScopedClusterContext;
let fetchMock: AsyncFnMock;
let result: RenderResult;
let cleanup: Disposer;
@@ -69,6 +72,7 @@ describe("", () => {
}));
namespaceStore = di.inject(namespaceStoreInjectable);
+ context = di.inject(clusterFrameContextForNamespacedResourcesInjectable);
const subscribeStores = di.inject(subscribeStoresInjectable);
@@ -138,7 +142,7 @@ describe("", () => {
});
it("has only 'test-2' is selected in the store", () => {
- expect(namespaceStore.contextNamespaces).toEqual(["test-2"]);
+ expect(context.contextNamespaces).toEqual(["test-2"]);
});
it("closes menu", () => {
@@ -172,7 +176,7 @@ describe("", () => {
});
it("has only 'test-1' is selected in the store", () => {
- expect(namespaceStore.contextNamespaces).toEqual(["test-1"]);
+ expect(context.contextNamespaces).toEqual(["test-1"]);
});
it("closes menu", () => {
@@ -197,7 +201,7 @@ describe("", () => {
});
it("has both 'test-1' and 'test-3' as selected in the store", () => {
- expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3"]));
+ expect(new Set(context.contextNamespaces)).toEqual(new Set(["test-1", "test-3"]));
});
it("keeps menu open", () => {
@@ -214,7 +218,7 @@ describe("", () => {
});
it("has all of 'test-1', 'test-3', and 'test-13' selected in the store", () => {
- expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3", "test-13"]));
+ expect(new Set(context.contextNamespaces)).toEqual(new Set(["test-1", "test-3", "test-13"]));
});
it("'test-13' is not sorted to the top of the list", () => {