mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Refactor watches to use native k8s api (#2095)
* fix lint Signed-off-by: Roman <ixrock@gmail.com> * fixes & refactoring Signed-off-by: Roman <ixrock@gmail.com> * fix lint, micro-refactoring Signed-off-by: Roman <ixrock@gmail.com> * more refactoring, clean up, responding to comments Signed-off-by: Roman <ixrock@gmail.com> * fix: remove extra check for cluster.allowedApi from processing buffered watch-api events Signed-off-by: Roman <ixrock@gmail.com> * refactoring, detaching NamespaceStore from KubeObjectStore Signed-off-by: Roman <ixrock@gmail.com> * fix: wait for contextReady in NamespaceStore Signed-off-by: Roman <ixrock@gmail.com> * refactoring & fixes Signed-off-by: Roman <ixrock@gmail.com> * fix lint Signed-off-by: Roman <ixrock@gmail.com> * fixes: reloading context stores on NamespaceSelect-change Signed-off-by: Roman <ixrock@gmail.com> * optimize loading all resources when "all namespaces" selected -> single request per resource (when have rights) Signed-off-by: Roman <ixrock@gmail.com> * use native k8s api watches Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * retry watch when it makes sense Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * workaround for browser connection limits Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * cleanup Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * cleanup Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * use always random subdomain for getResponse Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * resubscribe stores on contextNamespace change Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * modify watch event before calling callback Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> Co-authored-by: Roman <ixrock@gmail.com>
This commit is contained in:
1 parent
6ded5e73fa
commit
035dd470ef
34 files changed
+414
-599
No files matched your search
@@ -73,8 +73,8 @@ export class ReleaseStore extends ItemStore<HelmRelease> {
|
||||
}
|
||||
}
|
||||
|
||||
async loadSelectedNamespaces(): Promise<void> {
|
||||
return this.loadAll(namespaceStore.getContextNamespaces());
|
||||
async loadFromContextNamespaces(): Promise<void> {
|
||||
return this.loadAll(namespaceStore.contextNamespaces);
|
||||
}
|
||||
|
||||
async loadItems(namespaces: string[]) {
|
||||
@@ -86,7 +86,7 @@ export class ReleaseStore extends ItemStore<HelmRelease> {
|
||||
async create(payload: IReleaseCreatePayload) {
|
||||
const response = await helmReleasesApi.create(payload);
|
||||
|
||||
if (this.isLoaded) this.loadSelectedNamespaces();
|
||||
if (this.isLoaded) this.loadFromContextNamespaces();
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ export class ReleaseStore extends ItemStore<HelmRelease> {
|
||||
async update(name: string, namespace: string, payload: IReleaseUpdatePayload) {
|
||||
const response = await helmReleasesApi.update(name, namespace, payload);
|
||||
|
||||
if (this.isLoaded) this.loadSelectedNamespaces();
|
||||
if (this.isLoaded) this.loadFromContextNamespaces();
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -102,7 +102,7 @@ export class ReleaseStore extends ItemStore<HelmRelease> {
|
||||
async rollback(name: string, namespace: string, revision: number) {
|
||||
const response = await helmReleasesApi.rollback(name, namespace, revision);
|
||||
|
||||
if (this.isLoaded) this.loadSelectedNamespaces();
|
||||
if (this.isLoaded) this.loadFromContextNamespaces();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export class CrdResources extends React.Component<Props> {
|
||||
const { store } = this;
|
||||
|
||||
if (store && !store.isLoading && !store.isLoaded) {
|
||||
store.loadSelectedNamespaces();
|
||||
store.reloadAll();
|
||||
}
|
||||
})
|
||||
]);
|
||||
@@ -97,7 +97,7 @@ export class CrdResources extends React.Component<Props> {
|
||||
...extraColumns.map((column) => {
|
||||
let value = jsonPath.value(crdInstance, parseJsonPath(column.jsonPath.slice(1)));
|
||||
|
||||
if (Array.isArray(value) || typeof value === "object") {
|
||||
if (Array.isArray(value) || typeof value === "object") {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface KubeEventDetailsProps {
|
||||
@observer
|
||||
export class KubeEventDetails extends React.Component<KubeEventDetailsProps> {
|
||||
async componentDidMount() {
|
||||
eventStore.loadSelectedNamespaces();
|
||||
eventStore.reloadAll();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -32,8 +32,8 @@ export class NamespaceDetails extends React.Component<Props> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
resourceQuotaStore.loadSelectedNamespaces();
|
||||
limitRangeStore.loadSelectedNamespaces();
|
||||
resourceQuotaStore.reloadAll();
|
||||
limitRangeStore.reloadAll();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -82,7 +82,7 @@ export class NamespaceSelect extends React.Component<Props> {
|
||||
@observer
|
||||
export class NamespaceSelectFilter extends React.Component {
|
||||
@computed get placeholder(): React.ReactNode {
|
||||
const namespaces = namespaceStore.getContextNamespaces();
|
||||
const namespaces = namespaceStore.contextNamespaces;
|
||||
|
||||
switch (namespaces.length) {
|
||||
case 0:
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { action, comparer, computed, IReactionDisposer, IReactionOptions, observable, reaction, toJS, when } from "mobx";
|
||||
import { action, comparer, computed, IReactionDisposer, IReactionOptions, observable, reaction } from "mobx";
|
||||
import { autobind, createStorage } from "../../utils";
|
||||
import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
|
||||
import { Namespace, namespacesApi } from "../../api/endpoints/namespaces.api";
|
||||
import { createPageParam } from "../../navigation";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { clusterStore, getHostedCluster } from "../../../common/cluster-store";
|
||||
|
||||
const storage = createStorage<string[]>("context_namespaces", []);
|
||||
|
||||
@@ -35,9 +34,6 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
api = namespacesApi;
|
||||
|
||||
@observable private contextNs = observable.set<string>();
|
||||
@observable isReady = false;
|
||||
|
||||
whenReady = when(() => this.isReady);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -45,15 +41,11 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
}
|
||||
|
||||
private async init() {
|
||||
await clusterStore.whenLoaded;
|
||||
if (!getHostedCluster()) return;
|
||||
await getHostedCluster().whenReady; // wait for cluster-state from main
|
||||
await this.contextReady;
|
||||
|
||||
this.setContext(this.initialNamespaces);
|
||||
this.autoLoadAllowedNamespaces();
|
||||
this.autoUpdateUrlAndLocalStorage();
|
||||
|
||||
this.isReady = true;
|
||||
}
|
||||
|
||||
public onContextChange(callback: (contextNamespaces: string[]) => void, opts: IReactionOptions = {}): IReactionDisposer {
|
||||
@@ -73,16 +65,12 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
}
|
||||
|
||||
private autoLoadAllowedNamespaces(): IReactionDisposer {
|
||||
return reaction(() => this.allowedNamespaces, namespaces => this.loadAll(namespaces), {
|
||||
return reaction(() => this.allowedNamespaces, namespaces => this.loadAll({ namespaces }), {
|
||||
fireImmediately: true,
|
||||
equals: comparer.shallow,
|
||||
});
|
||||
}
|
||||
|
||||
@computed get allowedNamespaces(): string[] {
|
||||
return toJS(getHostedCluster().allowedNamespaces);
|
||||
}
|
||||
|
||||
@computed
|
||||
private get initialNamespaces(): string[] {
|
||||
const namespaces = new Set(this.allowedNamespaces);
|
||||
@@ -103,27 +91,26 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
return [];
|
||||
}
|
||||
|
||||
getContextNamespaces(): string[] {
|
||||
@computed get allowedNamespaces(): string[] {
|
||||
return Array.from(new Set([
|
||||
...(this.context?.allNamespaces ?? []), // allowed namespaces from cluster (main), updating every 30s
|
||||
...this.items.map(item => item.getName()), // loaded namespaces from k8s api
|
||||
].flat()));
|
||||
}
|
||||
|
||||
@computed get contextNamespaces(): string[] {
|
||||
const namespaces = Array.from(this.contextNs);
|
||||
|
||||
// show all namespaces when nothing selected
|
||||
if (!namespaces.length) {
|
||||
// return actual namespaces list since "allowedNamespaces" updating every 30s in cluster and thus might be stale
|
||||
if (this.isLoaded) {
|
||||
return this.items.map(namespace => namespace.getName());
|
||||
}
|
||||
|
||||
return this.allowedNamespaces;
|
||||
return this.allowedNamespaces; // show all namespaces when nothing selected
|
||||
}
|
||||
|
||||
return namespaces;
|
||||
}
|
||||
|
||||
getSubscribeApis() {
|
||||
const { accessibleNamespaces } = getHostedCluster();
|
||||
|
||||
// if user has given static list of namespaces let's not start watches because watch adds stuff that's not wanted
|
||||
if (accessibleNamespaces.length > 0) {
|
||||
if (this.context?.cluster.accessibleNamespaces.length > 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export class NodeDetails extends React.Component<Props> {
|
||||
});
|
||||
|
||||
async componentDidMount() {
|
||||
podsStore.loadSelectedNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Dialog, DialogProps } from "../dialog";
|
||||
import { Wizard, WizardStep } from "../wizard";
|
||||
import { Select, SelectOption } from "../select";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { IRoleBindingSubject, RoleBinding, ServiceAccount, Role } from "../../api/endpoints";
|
||||
import { IRoleBindingSubject, Role, RoleBinding, ServiceAccount } from "../../api/endpoints";
|
||||
import { Icon } from "../icon";
|
||||
import { Input } from "../input";
|
||||
import { NamespaceSelect } from "../+namespaces/namespace-select";
|
||||
@@ -19,6 +19,7 @@ import { namespaceStore } from "../+namespaces/namespace.store";
|
||||
import { serviceAccountsStore } from "../+user-management-service-accounts/service-accounts.store";
|
||||
import { roleBindingsStore } from "./role-bindings.store";
|
||||
import { showDetails } from "../kube-object";
|
||||
import { KubeObjectStore } from "../../kube-object.store";
|
||||
|
||||
interface BindingSelectOption extends SelectOption {
|
||||
value: string; // binding name
|
||||
@@ -73,14 +74,14 @@ export class AddRoleBindingDialog extends React.Component<Props> {
|
||||
};
|
||||
|
||||
async loadData() {
|
||||
const stores = [
|
||||
const stores: KubeObjectStore[] = [
|
||||
namespaceStore,
|
||||
rolesStore,
|
||||
serviceAccountsStore,
|
||||
];
|
||||
|
||||
this.isLoading = true;
|
||||
await Promise.all(stores.map(store => store.loadSelectedNamespaces()));
|
||||
await Promise.all(stores.map(store => store.reloadAll()));
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
@@ -136,8 +137,7 @@ export class AddRoleBindingDialog extends React.Component<Props> {
|
||||
roleBinding: this.roleBinding,
|
||||
addSubjects: subjects,
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
const name = useRoleForBindingName ? selectedRole.getName() : bindingName;
|
||||
|
||||
roleBinding = await roleBindingsStore.create({ name, namespace }, {
|
||||
@@ -265,7 +265,7 @@ export class AddRoleBindingDialog extends React.Component<Props> {
|
||||
</h5>
|
||||
);
|
||||
const disableNext = this.isLoading || !selectedRole || !selectedBindings.length;
|
||||
const nextLabel = isEditing ? "Update" : "Create";
|
||||
const nextLabel = isEditing ? "Update" : "Create";
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
||||
@@ -20,7 +20,7 @@ interface Props extends KubeObjectDetailsProps<CronJob> {
|
||||
@observer
|
||||
export class CronJobDetails extends React.Component<Props> {
|
||||
async componentDidMount() {
|
||||
jobStore.loadSelectedNamespaces();
|
||||
jobStore.reloadAll();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -30,7 +30,7 @@ export class DaemonSetDetails extends React.Component<Props> {
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
podsStore.loadSelectedNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -31,7 +31,7 @@ export class DeploymentDetails extends React.Component<Props> {
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
podsStore.loadSelectedNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -25,7 +25,7 @@ interface Props extends KubeObjectDetailsProps<Job> {
|
||||
@observer
|
||||
export class JobDetails extends React.Component<Props> {
|
||||
async componentDidMount() {
|
||||
podsStore.loadSelectedNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -6,7 +6,6 @@ import { OverviewWorkloadStatus } from "./overview-workload-status";
|
||||
import { Link } from "react-router-dom";
|
||||
import { workloadURL, workloadStores } from "../+workloads";
|
||||
import { namespaceStore } from "../+namespaces/namespace.store";
|
||||
import { PageFiltersList } from "../item-object-list/page-filters-list";
|
||||
import { NamespaceSelectFilter } from "../+namespaces/namespace-select";
|
||||
import { isAllowedResource, KubeResource } from "../../../common/rbac";
|
||||
import { ResourceNames } from "../../../renderer/utils/rbac";
|
||||
@@ -27,7 +26,7 @@ export class OverviewStatuses extends React.Component {
|
||||
@autobind()
|
||||
renderWorkload(resource: KubeResource): React.ReactElement {
|
||||
const store = workloadStores[resource];
|
||||
const items = store.getAllByNs(namespaceStore.getContextNamespaces());
|
||||
const items = store.getAllByNs(namespaceStore.contextNamespaces);
|
||||
|
||||
return (
|
||||
<div className="workload" key={resource}>
|
||||
@@ -50,7 +49,6 @@ export class OverviewStatuses extends React.Component {
|
||||
<h5 className="box grow">Overview</h5>
|
||||
<NamespaceSelectFilter />
|
||||
</div>
|
||||
<PageFiltersList />
|
||||
<div className="workloads">
|
||||
{workloads}
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@ import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
||||
import { Events } from "../+events";
|
||||
import { isAllowedResource } from "../../../common/rbac";
|
||||
import { kubeWatchApi } from "../../api/kube-watch-api";
|
||||
import { clusterContext } from "../context";
|
||||
|
||||
interface Props extends RouteComponentProps<IWorkloadsOverviewRouteParams> {
|
||||
}
|
||||
@@ -29,6 +30,7 @@ export class WorkloadsOverview extends React.Component<Props> {
|
||||
jobStore, cronJobStore, eventStore,
|
||||
], {
|
||||
preload: true,
|
||||
namespaces: clusterContext.contextNamespaces,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export class ReplicaSetDetails extends React.Component<Props> {
|
||||
});
|
||||
|
||||
async componentDidMount() {
|
||||
podsStore.loadSelectedNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -30,7 +30,7 @@ export class StatefulSetDetails extends React.Component<Props> {
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
podsStore.loadSelectedNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -43,12 +43,13 @@ import { ClusterPageMenuRegistration, clusterPageMenuRegistry } from "../../exte
|
||||
import { TabLayout, TabLayoutRoute } from "./layout/tab-layout";
|
||||
import { StatefulSetScaleDialog } from "./+workloads-statefulsets/statefulset-scale-dialog";
|
||||
import { eventStore } from "./+events/event.store";
|
||||
import { namespaceStore } from "./+namespaces/namespace.store";
|
||||
import { nodesStore } from "./+nodes/nodes.store";
|
||||
import { podsStore } from "./+workloads-pods/pods.store";
|
||||
import { kubeWatchApi } from "../api/kube-watch-api";
|
||||
import { ReplicaSetScaleDialog } from "./+workloads-replicasets/replicaset-scale-dialog";
|
||||
import { CommandContainer } from "./command-palette/command-container";
|
||||
import { KubeObjectStore } from "../kube-object.store";
|
||||
import { clusterContext } from "./context";
|
||||
|
||||
@observer
|
||||
export class App extends React.Component {
|
||||
@@ -76,11 +77,9 @@ export class App extends React.Component {
|
||||
});
|
||||
whatInput.ask(); // Start to monitor user input device
|
||||
|
||||
await namespaceStore.whenReady;
|
||||
await kubeWatchApi.init({
|
||||
getCluster: getHostedCluster,
|
||||
getNamespaces: namespaceStore.getContextNamespaces,
|
||||
});
|
||||
// Setup hosted cluster context
|
||||
KubeObjectStore.defaultContext = clusterContext;
|
||||
kubeWatchApi.context = clusterContext;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -163,9 +162,9 @@ export class App extends React.Component {
|
||||
const tabRoutes = this.getTabLayoutRoutes(menu);
|
||||
|
||||
if (tabRoutes.length > 0) {
|
||||
const pageComponent = () => <TabLayout tabs={tabRoutes} />;
|
||||
const pageComponent = () => <TabLayout tabs={tabRoutes}/>;
|
||||
|
||||
route = <Route key={`extension-tab-layout-route-${index}`} component={pageComponent} path={tabRoutes.map((tab) => tab.routePath)} />;
|
||||
route = <Route key={`extension-tab-layout-route-${index}`} component={pageComponent} path={tabRoutes.map((tab) => tab.routePath)}/>;
|
||||
this.extensionRoutes.set(menu, route);
|
||||
} else {
|
||||
const page = clusterPageRegistry.getByPageTarget(menu.target);
|
||||
@@ -229,7 +228,7 @@ export class App extends React.Component {
|
||||
<StatefulSetScaleDialog/>
|
||||
<ReplicaSetScaleDialog/>
|
||||
<CronJobTriggerDialog/>
|
||||
<CommandContainer cluster={cluster} />
|
||||
<CommandContainer cluster={cluster}/>
|
||||
</ErrorBoundary>
|
||||
</Router>
|
||||
);
|
||||
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
import type { Cluster } from "../../main/cluster";
|
||||
import { getHostedCluster } from "../../common/cluster-store";
|
||||
import { namespaceStore } from "./+namespaces/namespace.store";
|
||||
|
||||
export interface ClusterContext {
|
||||
cluster?: Cluster;
|
||||
allNamespaces?: string[]; // available / allowed namespaces from cluster.ts
|
||||
contextNamespaces?: string[]; // selected by user (see: namespace-select.tsx)
|
||||
}
|
||||
|
||||
export const clusterContext: ClusterContext = {
|
||||
get cluster(): Cluster | null {
|
||||
return getHostedCluster();
|
||||
},
|
||||
|
||||
get allNamespaces(): string[] {
|
||||
return this.cluster?.allowedNamespaces ?? [];
|
||||
},
|
||||
|
||||
get contextNamespaces(): string[] {
|
||||
return namespaceStore.contextNamespaces ?? [];
|
||||
},
|
||||
};
|
||||
@@ -80,7 +80,7 @@ export class UpgradeChartStore extends DockTabStore<IChartUpgradeData> {
|
||||
const values = this.values.getData(tabId);
|
||||
|
||||
await Promise.all([
|
||||
!releaseStore.isLoaded && releaseStore.loadSelectedNamespaces(),
|
||||
!releaseStore.isLoaded && releaseStore.loadFromContextNamespaces(),
|
||||
!values && this.loadValues(tabId)
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ interface IHeaderPlaceholders {
|
||||
export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
||||
tableId?: string;
|
||||
className: IClassName;
|
||||
items?: T[];
|
||||
store: ItemStore<T>;
|
||||
dependentStores?: ItemStore[];
|
||||
preloadStores?: boolean;
|
||||
@@ -138,7 +139,8 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
const { store, dependentStores } = this.props;
|
||||
const stores = Array.from(new Set([store, ...dependentStores]));
|
||||
|
||||
stores.forEach(store => store.loadAll(namespaceStore.getContextNamespaces()));
|
||||
// load context namespaces by default (see also: `<NamespaceSelectFilter/>`)
|
||||
stores.forEach(store => store.loadAll(namespaceStore.contextNamespaces));
|
||||
}
|
||||
|
||||
private filterCallbacks: { [type: string]: ItemsFilter } = {
|
||||
@@ -179,11 +181,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
|
||||
@computed get filters() {
|
||||
let { activeFilters } = pageFilters;
|
||||
const { isClusterScoped, isSearchable, searchFilters } = this.props;
|
||||
|
||||
if (isClusterScoped) {
|
||||
activeFilters = activeFilters.filter(({ type }) => type !== FilterType.NAMESPACE);
|
||||
}
|
||||
const { isSearchable, searchFilters } = this.props;
|
||||
|
||||
if (!(isSearchable && searchFilters)) {
|
||||
activeFilters = activeFilters.filter(({ type }) => type !== FilterType.SEARCH);
|
||||
@@ -217,7 +215,9 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
}
|
||||
});
|
||||
|
||||
return this.applyFilters(filterItems, allItems);
|
||||
const items = this.props.items ?? allItems;
|
||||
|
||||
return this.applyFilters(filterItems, items);
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@@ -337,8 +337,8 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
}
|
||||
|
||||
renderInfo() {
|
||||
const { allItems, items, isReady, userSettings, filters } = this;
|
||||
const allItemsCount = allItems.length;
|
||||
const { items, isReady, userSettings, filters } = this;
|
||||
const allItemsCount = this.props.store.getTotalCount();
|
||||
const itemsCount = items.length;
|
||||
const isFiltered = isReady && filters.length > 0;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { computed, observable, reaction } from "mobx";
|
||||
import { autobind } from "../../utils";
|
||||
import { namespaceStore } from "../+namespaces/namespace.store";
|
||||
import { searchUrlParam } from "../input/search-input-url";
|
||||
|
||||
export enum FilterType {
|
||||
@@ -24,32 +23,6 @@ export class PageFiltersStore {
|
||||
|
||||
constructor() {
|
||||
this.syncWithGlobalSearch();
|
||||
this.syncWithContextNamespace();
|
||||
}
|
||||
|
||||
protected syncWithContextNamespace() {
|
||||
const disposers = [
|
||||
reaction(() => this.getValues(FilterType.NAMESPACE), filteredNs => {
|
||||
if (filteredNs.length !== namespaceStore.getContextNamespaces().length) {
|
||||
namespaceStore.setContext(filteredNs);
|
||||
}
|
||||
}),
|
||||
namespaceStore.onContextChange(namespaces => {
|
||||
const filteredNs = this.getValues(FilterType.NAMESPACE);
|
||||
const isChanged = namespaces.length !== filteredNs.length;
|
||||
|
||||
if (isChanged) {
|
||||
this.filters.replace([
|
||||
...this.filters.filter(({ type }) => type !== FilterType.NAMESPACE),
|
||||
...namespaces.map(ns => ({ type: FilterType.NAMESPACE, value: ns })),
|
||||
]);
|
||||
}
|
||||
}, {
|
||||
fireImmediately: true
|
||||
})
|
||||
];
|
||||
|
||||
return () => disposers.forEach(dispose => dispose());
|
||||
}
|
||||
|
||||
protected syncWithGlobalSearch() {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { KubeObjectStore } from "../../kube-object.store";
|
||||
import { KubeObjectMenu } from "./kube-object-menu";
|
||||
import { kubeSelectedUrlParam, showDetails } from "./kube-object-details";
|
||||
import { kubeWatchApi } from "../../api/kube-watch-api";
|
||||
import { clusterContext } from "../context";
|
||||
|
||||
export interface KubeObjectListLayoutProps extends ItemListLayoutProps {
|
||||
store: KubeObjectStore;
|
||||
@@ -26,7 +27,8 @@ export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutPr
|
||||
|
||||
disposeOnUnmount(this, [
|
||||
kubeWatchApi.subscribeStores(stores, {
|
||||
preload: true
|
||||
preload: true,
|
||||
namespaces: clusterContext.contextNamespaces,
|
||||
})
|
||||
]);
|
||||
}
|
||||
@@ -40,12 +42,14 @@ export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutPr
|
||||
};
|
||||
|
||||
render() {
|
||||
const items = this.props.store.contextItems;
|
||||
const { className, ...layoutProps } = this.props;
|
||||
|
||||
return (
|
||||
<ItemListLayout
|
||||
{...layoutProps}
|
||||
className={cssNames("KubeObjectListLayout", className)}
|
||||
items={items}
|
||||
preloadStores={false} // loading handled in kubeWatchApi.subscribeStores()
|
||||
detailsItem={this.selectedItem}
|
||||
onDetails={this.onDetails}
|
||||
|
||||
@@ -40,7 +40,7 @@ interface Props {
|
||||
@observer
|
||||
export class Sidebar extends React.Component<Props> {
|
||||
async componentDidMount() {
|
||||
crdStore.loadSelectedNamespaces();
|
||||
crdStore.reloadAll();
|
||||
}
|
||||
|
||||
renderCustomResources() {
|
||||
|
||||
Reference in new issue
Block a user