mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix: preventing <App/> to render on cluster refresh (#2253)
* Removing @observer decorator from <App/> Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Add observer wrapper to <MainLayoutHeader/> Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fix eslint claim Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Moving extension route renderers to components Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Clean up Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Removing external observables out from App render() Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fetching hosted cluster inside Command Palette Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Setting route lists explicitly To avoid using observable data within tabRoutes arrays Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Review fixes Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
parent
4c7c2d1084
commit
c1a2c1e849
@ -1,12 +1,21 @@
|
|||||||
import { RouteProps } from "react-router";
|
import { RouteProps } from "react-router";
|
||||||
import { Config } from "./config";
|
|
||||||
import { IURLParams } from "../../../common/utils/buildUrl";
|
import { IURLParams } from "../../../common/utils/buildUrl";
|
||||||
import { configMapsURL } from "../+config-maps/config-maps.route";
|
import { configMapsRoute, configMapsURL } from "../+config-maps/config-maps.route";
|
||||||
|
import { hpaRoute } from "../+config-autoscalers";
|
||||||
|
import { limitRangesRoute } from "../+config-limit-ranges";
|
||||||
|
import { pdbRoute } from "../+config-pod-disruption-budgets";
|
||||||
|
import { resourceQuotaRoute } from "../+config-resource-quotas";
|
||||||
|
import { secretsRoute } from "../+config-secrets";
|
||||||
|
|
||||||
export const configRoute: RouteProps = {
|
export const configRoute: RouteProps = {
|
||||||
get path() {
|
path: [
|
||||||
return Config.tabRoutes.map(({ routePath }) => routePath).flat();
|
configMapsRoute,
|
||||||
}
|
secretsRoute,
|
||||||
|
resourceQuotaRoute,
|
||||||
|
limitRangesRoute,
|
||||||
|
hpaRoute,
|
||||||
|
pdbRoute
|
||||||
|
].map(route => route.path.toString())
|
||||||
};
|
};
|
||||||
|
|
||||||
export const configURL = (params?: IURLParams) => configMapsURL(params);
|
export const configURL = (params?: IURLParams) => configMapsURL(params);
|
||||||
|
|||||||
@ -1,12 +1,17 @@
|
|||||||
import { RouteProps } from "react-router";
|
import { RouteProps } from "react-router";
|
||||||
import { Network } from "./network";
|
import { endpointRoute } from "../+network-endpoints";
|
||||||
import { servicesURL } from "../+network-services";
|
import { ingressRoute } from "../+network-ingresses";
|
||||||
|
import { networkPoliciesRoute } from "../+network-policies";
|
||||||
|
import { servicesRoute, servicesURL } from "../+network-services";
|
||||||
import { IURLParams } from "../../../common/utils/buildUrl";
|
import { IURLParams } from "../../../common/utils/buildUrl";
|
||||||
|
|
||||||
export const networkRoute: RouteProps = {
|
export const networkRoute: RouteProps = {
|
||||||
get path() {
|
path: [
|
||||||
return Network.tabRoutes.map(({ routePath }) => routePath).flat();
|
servicesRoute,
|
||||||
}
|
endpointRoute,
|
||||||
|
ingressRoute,
|
||||||
|
networkPoliciesRoute
|
||||||
|
].map(route => route.path.toString())
|
||||||
};
|
};
|
||||||
|
|
||||||
export const networkURL = (params?: IURLParams) => servicesURL(params);
|
export const networkURL = (params?: IURLParams) => servicesURL(params);
|
||||||
|
|||||||
@ -1,3 +1,2 @@
|
|||||||
export * from "./pod-security-policies.route";
|
|
||||||
export * from "./pod-security-policies";
|
export * from "./pod-security-policies";
|
||||||
export * from "./pod-security-policy-details";
|
export * from "./pod-security-policy-details";
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
import type { RouteProps } from "react-router";
|
|
||||||
import { buildURL } from "../../../common/utils/buildUrl";
|
|
||||||
|
|
||||||
export const podSecurityPoliciesRoute: RouteProps = {
|
|
||||||
path: "/pod-security-policies"
|
|
||||||
};
|
|
||||||
|
|
||||||
export const podSecurityPoliciesURL = buildURL(podSecurityPoliciesRoute.path);
|
|
||||||
@ -1,12 +1,15 @@
|
|||||||
import { RouteProps } from "react-router";
|
import { RouteProps } from "react-router";
|
||||||
import { volumeClaimsURL } from "../+storage-volume-claims";
|
import { storageClassesRoute } from "../+storage-classes";
|
||||||
import { Storage } from "./storage";
|
import { volumeClaimsRoute, volumeClaimsURL } from "../+storage-volume-claims";
|
||||||
|
import { volumesRoute } from "../+storage-volumes";
|
||||||
import { IURLParams } from "../../../common/utils/buildUrl";
|
import { IURLParams } from "../../../common/utils/buildUrl";
|
||||||
|
|
||||||
export const storageRoute: RouteProps = {
|
export const storageRoute: RouteProps = {
|
||||||
get path() {
|
path: [
|
||||||
return Storage.tabRoutes.map(({ routePath }) => routePath).flat();
|
volumeClaimsRoute,
|
||||||
}
|
volumesRoute,
|
||||||
|
storageClassesRoute
|
||||||
|
].map(route => route.path.toString())
|
||||||
};
|
};
|
||||||
|
|
||||||
export const storageURL = (params?: IURLParams) => volumeClaimsURL(params);
|
export const storageURL = (params?: IURLParams) => volumeClaimsURL(params);
|
||||||
|
|||||||
@ -1,12 +1,5 @@
|
|||||||
import type { RouteProps } from "react-router";
|
import type { RouteProps } from "react-router";
|
||||||
import { buildURL, IURLParams } from "../../../common/utils/buildUrl";
|
import { buildURL, IURLParams } from "../../../common/utils/buildUrl";
|
||||||
import { UserManagement } from "./user-management";
|
|
||||||
|
|
||||||
export const usersManagementRoute: RouteProps = {
|
|
||||||
get path() {
|
|
||||||
return UserManagement.tabRoutes.map(({ routePath }) => routePath).flat();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
export const serviceAccountsRoute: RouteProps = {
|
export const serviceAccountsRoute: RouteProps = {
|
||||||
@ -18,6 +11,18 @@ export const rolesRoute: RouteProps = {
|
|||||||
export const roleBindingsRoute: RouteProps = {
|
export const roleBindingsRoute: RouteProps = {
|
||||||
path: "/role-bindings"
|
path: "/role-bindings"
|
||||||
};
|
};
|
||||||
|
export const podSecurityPoliciesRoute: RouteProps = {
|
||||||
|
path: "/pod-security-policies"
|
||||||
|
};
|
||||||
|
|
||||||
|
export const usersManagementRoute: RouteProps = {
|
||||||
|
path: [
|
||||||
|
serviceAccountsRoute,
|
||||||
|
roleBindingsRoute,
|
||||||
|
rolesRoute,
|
||||||
|
podSecurityPoliciesRoute
|
||||||
|
].map(route => route.path.toString())
|
||||||
|
};
|
||||||
|
|
||||||
// Route params
|
// Route params
|
||||||
export interface IServiceAccountsRouteParams {
|
export interface IServiceAccountsRouteParams {
|
||||||
@ -34,3 +39,4 @@ export const usersManagementURL = (params?: IURLParams) => serviceAccountsURL(pa
|
|||||||
export const serviceAccountsURL = buildURL<IServiceAccountsRouteParams>(serviceAccountsRoute.path);
|
export const serviceAccountsURL = buildURL<IServiceAccountsRouteParams>(serviceAccountsRoute.path);
|
||||||
export const roleBindingsURL = buildURL<IRoleBindingsRouteParams>(roleBindingsRoute.path);
|
export const roleBindingsURL = buildURL<IRoleBindingsRouteParams>(roleBindingsRoute.path);
|
||||||
export const rolesURL = buildURL<IRoleBindingsRouteParams>(rolesRoute.path);
|
export const rolesURL = buildURL<IRoleBindingsRouteParams>(rolesRoute.path);
|
||||||
|
export const podSecurityPoliciesURL = buildURL(podSecurityPoliciesRoute.path);
|
||||||
|
|||||||
@ -5,9 +5,9 @@ import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
|||||||
import { Roles } from "../+user-management-roles";
|
import { Roles } from "../+user-management-roles";
|
||||||
import { RoleBindings } from "../+user-management-roles-bindings";
|
import { RoleBindings } from "../+user-management-roles-bindings";
|
||||||
import { ServiceAccounts } from "../+user-management-service-accounts";
|
import { ServiceAccounts } from "../+user-management-service-accounts";
|
||||||
import { roleBindingsRoute, roleBindingsURL, rolesRoute, rolesURL, serviceAccountsRoute, serviceAccountsURL } from "./user-management.route";
|
import { podSecurityPoliciesRoute, podSecurityPoliciesURL, roleBindingsRoute, roleBindingsURL, rolesRoute, rolesURL, serviceAccountsRoute, serviceAccountsURL } from "./user-management.route";
|
||||||
import { namespaceUrlParam } from "../+namespaces/namespace.store";
|
import { namespaceUrlParam } from "../+namespaces/namespace.store";
|
||||||
import { PodSecurityPolicies, podSecurityPoliciesRoute, podSecurityPoliciesURL } from "../+pod-security-policies";
|
import { PodSecurityPolicies } from "../+pod-security-policies";
|
||||||
import { isAllowedResource } from "../../../common/rbac";
|
import { isAllowedResource } from "../../../common/rbac";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
|
|||||||
@ -1,13 +1,6 @@
|
|||||||
import type { RouteProps } from "react-router";
|
import type { RouteProps } from "react-router";
|
||||||
import { buildURL, IURLParams } from "../../../common/utils/buildUrl";
|
import { buildURL, IURLParams } from "../../../common/utils/buildUrl";
|
||||||
import { KubeResource } from "../../../common/rbac";
|
import { KubeResource } from "../../../common/rbac";
|
||||||
import { Workloads } from "./workloads";
|
|
||||||
|
|
||||||
export const workloadsRoute: RouteProps = {
|
|
||||||
get path() {
|
|
||||||
return Workloads.tabRoutes.map(({ routePath }) => routePath).flat();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
export const overviewRoute: RouteProps = {
|
export const overviewRoute: RouteProps = {
|
||||||
@ -35,6 +28,19 @@ export const cronJobsRoute: RouteProps = {
|
|||||||
path: "/cronjobs"
|
path: "/cronjobs"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const workloadsRoute: RouteProps = {
|
||||||
|
path: [
|
||||||
|
overviewRoute,
|
||||||
|
podsRoute,
|
||||||
|
deploymentsRoute,
|
||||||
|
daemonSetsRoute,
|
||||||
|
statefulSetsRoute,
|
||||||
|
replicaSetsRoute,
|
||||||
|
jobsRoute,
|
||||||
|
cronJobsRoute
|
||||||
|
].map(route => route.path.toString())
|
||||||
|
};
|
||||||
|
|
||||||
// Route params
|
// Route params
|
||||||
export interface IWorkloadsOverviewRouteParams {
|
export interface IWorkloadsOverviewRouteParams {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,27 +91,15 @@ export class App extends React.Component {
|
|||||||
reaction(() => this.warningsTotal, (count: number) => {
|
reaction(() => this.warningsTotal, (count: number) => {
|
||||||
broadcastMessage(`cluster-warning-event-count:${getHostedCluster().id}`, count);
|
broadcastMessage(`cluster-warning-event-count:${getHostedCluster().id}`, count);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
reaction(() => clusterPageMenuRegistry.getRootItems(), (rootItems) => {
|
|
||||||
this.generateExtensionTabLayoutRoutes(rootItems);
|
|
||||||
}, {
|
|
||||||
fireImmediately: true
|
|
||||||
})
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@observable startUrl = isAllowedResource(["events", "nodes", "pods"]) ? clusterURL() : workloadsURL();
|
||||||
|
|
||||||
@computed get warningsTotal(): number {
|
@computed get warningsTotal(): number {
|
||||||
return nodesStore.getWarningsCount() + eventStore.getWarningsCount();
|
return nodesStore.getWarningsCount() + eventStore.getWarningsCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
get startURL() {
|
|
||||||
if (isAllowedResource(["events", "nodes", "pods"])) {
|
|
||||||
return clusterURL();
|
|
||||||
}
|
|
||||||
|
|
||||||
return workloadsURL();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTabLayoutRoutes(menuItem: ClusterPageMenuRegistration) {
|
getTabLayoutRoutes(menuItem: ClusterPageMenuRegistration) {
|
||||||
const routes: TabLayoutRoute[] = [];
|
const routes: TabLayoutRoute[] = [];
|
||||||
|
|
||||||
@ -152,38 +140,6 @@ export class App extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@observable extensionRoutes: Map<ClusterPageMenuRegistration, React.ReactNode> = new Map();
|
|
||||||
|
|
||||||
generateExtensionTabLayoutRoutes(rootItems: ClusterPageMenuRegistration[]) {
|
|
||||||
rootItems.forEach((menu, index) => {
|
|
||||||
let route = this.extensionRoutes.get(menu);
|
|
||||||
|
|
||||||
if (!route) {
|
|
||||||
const tabRoutes = this.getTabLayoutRoutes(menu);
|
|
||||||
|
|
||||||
if (tabRoutes.length > 0) {
|
|
||||||
const pageComponent = () => <TabLayout tabs={tabRoutes}/>;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (page) {
|
|
||||||
route = <Route key={`extension-tab-layout-route-${index}`} path={page.url} component={page.components.Page}/>;
|
|
||||||
this.extensionRoutes.set(menu, route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const menu of this.extensionRoutes.keys()) {
|
|
||||||
if (!rootItems.includes(menu)) {
|
|
||||||
this.extensionRoutes.delete(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderExtensionRoutes() {
|
renderExtensionRoutes() {
|
||||||
return clusterPageRegistry.getItems().map((page, index) => {
|
return clusterPageRegistry.getItems().map((page, index) => {
|
||||||
const menu = clusterPageMenuRegistry.getByPage(page);
|
const menu = clusterPageMenuRegistry.getByPage(page);
|
||||||
@ -195,8 +151,6 @@ export class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const cluster = getHostedCluster();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
@ -215,7 +169,7 @@ export class App extends React.Component {
|
|||||||
<Route component={Apps} {...appsRoute}/>
|
<Route component={Apps} {...appsRoute}/>
|
||||||
{this.renderExtensionTabLayoutRoutes()}
|
{this.renderExtensionTabLayoutRoutes()}
|
||||||
{this.renderExtensionRoutes()}
|
{this.renderExtensionRoutes()}
|
||||||
<Redirect exact from="/" to={this.startURL}/>
|
<Redirect exact from="/" to={this.startUrl}/>
|
||||||
<Route component={NotFound}/>
|
<Route component={NotFound}/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
@ -228,7 +182,7 @@ export class App extends React.Component {
|
|||||||
<StatefulSetScaleDialog/>
|
<StatefulSetScaleDialog/>
|
||||||
<ReplicaSetScaleDialog/>
|
<ReplicaSetScaleDialog/>
|
||||||
<CronJobTriggerDialog/>
|
<CronJobTriggerDialog/>
|
||||||
<CommandContainer cluster={cluster}/>
|
<CommandContainer clusterId={getHostedCluster()?.id}/>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { CommandDialog } from "./command-dialog";
|
|||||||
import { CommandRegistration, commandRegistry } from "../../../extensions/registries/command-registry";
|
import { CommandRegistration, commandRegistry } from "../../../extensions/registries/command-registry";
|
||||||
import { clusterStore } from "../../../common/cluster-store";
|
import { clusterStore } from "../../../common/cluster-store";
|
||||||
import { workspaceStore } from "../../../common/workspace-store";
|
import { workspaceStore } from "../../../common/workspace-store";
|
||||||
import { Cluster } from "../../../main/cluster";
|
|
||||||
|
|
||||||
export type CommandDialogEvent = {
|
export type CommandDialogEvent = {
|
||||||
component: React.ReactElement
|
component: React.ReactElement
|
||||||
@ -29,7 +28,7 @@ export class CommandOverlay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class CommandContainer extends React.Component<{cluster?: Cluster}> {
|
export class CommandContainer extends React.Component<{ clusterId?: string }> {
|
||||||
@observable.ref commandComponent: React.ReactElement;
|
@observable.ref commandComponent: React.ReactElement;
|
||||||
|
|
||||||
private escHandler(event: KeyboardEvent) {
|
private escHandler(event: KeyboardEvent) {
|
||||||
@ -56,8 +55,8 @@ export class CommandContainer extends React.Component<{cluster?: Cluster}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (this.props.cluster) {
|
if (this.props.clusterId) {
|
||||||
subscribeToBroadcast(`command-palette:run-action:${this.props.cluster.id}`, (event, commandId: string) => {
|
subscribeToBroadcast(`command-palette:run-action:${this.props.clusterId}`, (event, commandId: string) => {
|
||||||
const command = this.findCommandById(commandId);
|
const command = this.findCommandById(commandId);
|
||||||
|
|
||||||
if (command) {
|
if (command) {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { observer } from "mobx-react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { clusterSettingsURL } from "../+cluster-settings";
|
import { clusterSettingsURL } from "../+cluster-settings";
|
||||||
@ -11,7 +12,7 @@ interface Props {
|
|||||||
className?: string
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MainLayoutHeader({ cluster, className }: Props) {
|
export const MainLayoutHeader = observer(({ cluster, className }: Props) => {
|
||||||
return (
|
return (
|
||||||
<header className={cssNames("flex gaps align-center justify-space-between", className)}>
|
<header className={cssNames("flex gaps align-center justify-space-between", className)}>
|
||||||
<span className="cluster">{cluster.name}</span>
|
<span className="cluster">{cluster.name}</span>
|
||||||
@ -29,4 +30,4 @@ export function MainLayoutHeader({ cluster, className }: Props) {
|
|||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user