1
0
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:
Alex Andreev 2021-03-04 15:12:28 +03:00 committed by GitHub
parent bcaef79386
commit 852aa1147f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 70 additions and 96 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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";

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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 {
} }

View File

@ -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>
); );

View File

@ -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) {

View File

@ -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>
); );
} });