1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

navigation refactoring, move out buildUrl to common/utils so react and react-router not required as package.json dependecies in runtime (main)

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2020-10-16 13:58:20 +03:00
parent 8097ca5af8
commit 457171dd00
40 changed files with 128 additions and 144 deletions

View File

@ -207,7 +207,6 @@
"openid-client": "^3.15.2",
"path-to-regexp": "^6.1.0",
"proper-lockfile": "^4.1.1",
"react-router": "^5.2.0",
"request": "^2.88.2",
"request-promise-native": "^1.0.8",
"semver": "^7.3.2",
@ -313,6 +312,7 @@
"react": "^16.14.0",
"react-beautiful-dnd": "^13.0.0",
"react-dom": "^16.13.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-select": "^3.1.0",
"react-window": "^1.8.5",

View File

@ -0,0 +1,14 @@
import { compile } from "path-to-regexp"
export interface IURLParams<P extends object = {}, Q extends object = {}> {
params?: P;
query?: Q;
}
export function buildURL<P extends object = {}, Q extends object = {}>(path: string | any) {
const pathBuilder = compile(String(path));
return function ({ params, query }: IURLParams<P, Q> = {}) {
const queryParams = query ? new URLSearchParams(Object.entries(query)).toString() : ""
return pathBuilder(params) + (queryParams ? `?${queryParams}` : "")
}
}

View File

@ -1,8 +1,6 @@
import { action, computed, observable, toJS } from "mobx";
import { BaseStore } from "./base-store";
import { clusterStore } from "./cluster-store"
import { landingURL } from "../renderer/components/+landing-page/landing-page.route";
import { navigate } from "../renderer/navigation";
export type WorkspaceId = string;
@ -56,18 +54,13 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
}
@action
setActive(id = WorkspaceStore.defaultId, { redirectToLanding = true, resetActiveCluster = true } = {}) {
setActive(id = WorkspaceStore.defaultId, reset = true) {
if (id === this.currentWorkspaceId) return;
if (!this.getById(id)) {
throw new Error(`workspace ${id} doesn't exist`);
}
this.currentWorkspaceId = id;
if (resetActiveCluster) {
clusterStore.setActive(null)
}
if (redirectToLanding) {
navigate(landingURL())
}
clusterStore.activeClusterId = null; // fixme: handle previously selected cluster from current workspace
}
@action

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const addClusterRoute: RouteProps = {
path: "/add-cluster"

View File

@ -1,6 +1,6 @@
import { RouteProps } from "react-router"
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
import { appsRoute } from "../+apps/apps.route";
import { buildURL } from "../../navigation";
export const helmChartsRoute: RouteProps = {
path: appsRoute.path + "/charts/:repo?/:chartName?"

View File

@ -1,6 +1,6 @@
import { RouteProps } from "react-router"
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
import { appsRoute } from "../+apps/apps.route";
import { buildURL } from "../../navigation";
export const releaseRoute: RouteProps = {
path: appsRoute.path + "/releases/:namespace?/:name?"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const appsRoute: RouteProps = {
path: "/apps",

View File

@ -1,6 +1,6 @@
import type { IClusterViewRouteParams } from "../cluster-manager/cluster-view.route";
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export interface IClusterSettingsRouteParams extends IClusterViewRouteParams {
}

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const clusterRoute: RouteProps = {
path: "/cluster"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const hpaRoute: RouteProps = {
path: "/hpa"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const configMapsRoute: RouteProps = {
path: "/configmaps"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const pdbRoute: RouteProps = {
path: "/poddisruptionbudgets"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const resourceQuotaRoute: RouteProps = {
path: "/resourcequotas"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const secretsRoute: RouteProps = {
path: "/secrets"

View File

@ -1,7 +1,7 @@
import { RouteProps } from "react-router";
import { configMapsURL } from "../+config-maps";
import { Config } from "./config";
import { IURLParams } from "../../navigation";
import { IURLParams } from "../../../common/utils/buildUrl";
import { configMapsURL } from "../+config-maps/config-maps.route";
export const configRoute: RouteProps = {
get path() {

View File

@ -10,8 +10,8 @@ import { resourceQuotaRoute, ResourceQuotas, resourceQuotaURL } from "../+config
import { PodDisruptionBudgets, pdbRoute, pdbURL } from "../+config-pod-disruption-budgets";
import { configURL } from "./config.route";
import { HorizontalPodAutoscalers, hpaRoute, hpaURL } from "../+config-autoscalers";
import { buildURL } from "../../navigation";
import { isAllowedResource } from "../../../common/rbac"
import { buildURL } from "../../../common/utils/buildUrl";
export const certificatesURL = buildURL("/certificates");
export const issuersURL = buildURL("/issuers");

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const crdRoute: RouteProps = {
path: "/crd"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const eventRoute: RouteProps = {
path: "/events"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const landingRoute: RouteProps = {
path: "/landing"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const namespacesRoute: RouteProps = {
path: "/namespaces"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const endpointRoute: RouteProps = {
path: "/endpoints"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const ingressRoute: RouteProps = {
path: "/ingresses"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const networkPoliciesRoute: RouteProps = {
path: "/network-policies"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const servicesRoute: RouteProps = {
path: "/services"

View File

@ -1,7 +1,7 @@
import { RouteProps } from "react-router"
import { Network } from "./network";
import { servicesURL } from "../+network-services";
import { IURLParams } from "../../navigation";
import { IURLParams } from "../../../common/utils/buildUrl";
export const networkRoute: RouteProps = {
get path() {

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const nodesRoute: RouteProps = {
path: "/nodes"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const podSecurityPoliciesRoute: RouteProps = {
path: "/pod-security-policies"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const preferencesRoute: RouteProps = {
path: "/preferences"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const storageClassesRoute: RouteProps = {
path: "/storage-classes"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const volumeClaimsRoute: RouteProps = {
path: "/persistent-volume-claims"

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const volumesRoute: RouteProps = {
path: "/persistent-volumes"

View File

@ -1,7 +1,7 @@
import { RouteProps } from "react-router"
import { volumeClaimsURL } from "../+storage-volume-claims";
import { Storage } from "./storage";
import { IURLParams } from "../../navigation";
import { IURLParams } from "../../../common/utils/buildUrl";
export const storageRoute: RouteProps = {
get path() {

View File

@ -1,6 +1,6 @@
import { RouteProps } from "react-router";
import type { RouteProps } from "react-router";
import { buildURL, IURLParams } from "../../../common/utils/buildUrl";
import { UserManagement } from "./user-management"
import { buildURL, IURLParams } from "../../navigation";
export const usersManagementRoute: RouteProps = {
get path() {
@ -30,9 +30,7 @@ export interface IRolesRouteParams {
}
// URL-builders
export const usersManagementURL = (params?: IURLParams) => serviceAccountsURL(params);
export const serviceAccountsURL = buildURL<IServiceAccountsRouteParams>(serviceAccountsRoute.path)
export const roleBindingsURL = buildURL<IRoleBindingsRouteParams>(roleBindingsRoute.path)
export const rolesURL = buildURL<IRoleBindingsRouteParams>(rolesRoute.path)
export const usersManagementURL = (params?: IURLParams) => {
return serviceAccountsURL(params);
};

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const whatsNewRoute: RouteProps = {
path: "/what-s-new"

View File

@ -1,7 +1,7 @@
import { RouteProps } from "react-router"
import { Workloads } from "./workloads";
import { buildURL, IURLParams } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL, IURLParams } from "../../../common/utils/buildUrl";
import { KubeResource } from "../../../common/rbac";
import { Workloads } from "./workloads";
export const workloadsRoute: RouteProps = {
get path() {

View File

@ -1,5 +1,5 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../navigation";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const workspacesRoute: RouteProps = {
path: "/workspaces"

View File

@ -11,14 +11,16 @@ import { Workspaces, workspacesRoute } from "../+workspaces";
import { AddCluster, addClusterRoute } from "../+add-cluster";
import { ClusterView } from "./cluster-view";
import { ClusterSettings, clusterSettingsRoute } from "../+cluster-settings";
import { clusterViewRoute, clusterViewURL, getMatchedCluster, getMatchedClusterId } from "./cluster-view.route";
import { clusterViewRoute, clusterViewURL } from "./cluster-view.route";
import { clusterStore } from "../../../common/cluster-store";
import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views";
import { isMac } from "../../../common/vars";
import { getMatchedClusterId } from "../../navigation";
@observer
export class ClusterManager extends React.Component {
componentDidMount() {
const getMatchedCluster = () => clusterStore.getById(getMatchedClusterId());
disposeOnUnmount(this, [
reaction(getMatchedClusterId, initView, {
fireImmediately: true
@ -55,7 +57,7 @@ export class ClusterManager extends React.Component {
return (
<div className="ClusterManager">
<main>
<div id="lens-views" />
<div id="lens-views"/>
<Switch>
<Route component={LandingPage} {...landingRoute} />
<Route component={Preferences} {...preferencesRoute} />
@ -63,11 +65,11 @@ export class ClusterManager extends React.Component {
<Route component={AddCluster} {...addClusterRoute} />
<Route component={ClusterView} {...clusterViewRoute} />
<Route component={ClusterSettings} {...clusterSettingsRoute} />
<Redirect exact to={this.startUrl} />
<Redirect exact to={this.startUrl}/>
</Switch>
</main>
<ClustersMenu />
<BottomBar />
<ClustersMenu/>
<BottomBar/>
</div>
)
}

View File

@ -1,8 +1,5 @@
import { reaction } from "mobx";
import { ipcRenderer } from "electron";
import { matchPath, RouteProps } from "react-router";
import { buildURL, navigation } from "../../navigation";
import { clusterStore } from "../../../common/cluster-store";
import type { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export interface IClusterViewRouteParams {
clusterId: string;
@ -14,33 +11,3 @@ export const clusterViewRoute: RouteProps = {
}
export const clusterViewURL = buildURL<IClusterViewRouteParams>(clusterViewRoute.path)
export function getMatchedClusterId(): string {
const matched = matchPath<IClusterViewRouteParams>(navigation.location.pathname, {
exact: true,
path: clusterViewRoute.path
})
if (matched) {
return matched.params.clusterId;
}
}
export function getMatchedCluster() {
return clusterStore.getById(getMatchedClusterId())
}
if (ipcRenderer) {
if (process.isMainFrame) {
// Keep track of active cluster-id for handling IPC/menus/etc.
reaction(() => getMatchedClusterId(), clusterId => {
ipcRenderer.send("cluster-view:current-id", clusterId);
}, {
fireImmediately: true
})
}
// Reload dashboard
ipcRenderer.on("menu:reload", () => {
location.reload();
});
}

View File

@ -1,6 +1,6 @@
import { observable, when } from "mobx";
import { ClusterId, clusterStore, getClusterFrameUrl } from "../../../common/cluster-store";
import { getMatchedCluster } from "./cluster-view.route"
import { getMatchedClusterId } from "../../navigation";
import logger from "../../../main/logger";
export interface LensView {
@ -51,7 +51,7 @@ export async function autoCleanOnRemove(clusterId: ClusterId, iframe: HTMLIFrame
}
export function refreshViews() {
const cluster = getMatchedCluster();
const cluster = clusterStore.getById(getMatchedClusterId());
lensViews.forEach(({ clusterId, view, isLoaded }) => {
const isCurrent = clusterId === cluster?.id;
const isReady = cluster?.available && cluster?.ready;

View File

@ -1,22 +1,16 @@
// Navigation helpers
import { ipcRenderer } from "electron";
import { compile } from "path-to-regexp"
import { createBrowserHistory, createMemoryHistory, LocationDescriptor } from "history";
import { matchPath } from "react-router";
import { reaction } from "mobx";
import { createObservableHistory } from "mobx-observable-history";
import { createBrowserHistory, createMemoryHistory, LocationDescriptor } from "history";
import logger from "../main/logger";
import { clusterViewRoute, IClusterViewRouteParams } from "./components/cluster-manager/cluster-view.route";
export const history = typeof window !== "undefined" ? createBrowserHistory() : createMemoryHistory();
export const navigation = createObservableHistory(history);
// handle navigation from other process (e.g. system menus in main, common->cluster view interactions)
if (ipcRenderer) {
ipcRenderer.on("menu:navigate", (event, location: LocationDescriptor) => {
logger.info(`[IPC]: ${event.type} ${JSON.stringify(location)}`, event);
navigate(location);
})
}
export function navigate(location: LocationDescriptor) {
const currentLocation = navigation.getPath();
navigation.push(location);
@ -25,20 +19,6 @@ export function navigate(location: LocationDescriptor) {
}
}
export interface IURLParams<P = {}, Q = {}> {
params?: P;
query?: IQueryParams & Q;
}
// todo: extract building urls to commons (also used in menu.ts)
// fixme: missing types validation for params & query
export function buildURL<P extends object, Q = object>(path: string | string[]) {
const pathBuilder = compile(path.toString());
return function ({ params, query }: IURLParams<P, Q> = {}) {
return pathBuilder(params) + (query ? getQueryString(query, false) : "")
}
}
// common params for all pages
export interface IQueryParams {
namespaces?: string[]; // selected context namespaces
@ -100,3 +80,33 @@ export function setSearch(text: string) {
export function getSearch() {
return navigation.searchParams.get("search") || "";
}
export function getMatchedClusterId(): string {
const matched = matchPath<IClusterViewRouteParams>(navigation.location.pathname, {
exact: true,
path: clusterViewRoute.path
});
return matched?.params.clusterId;
}
//-- EVENTS
if (process.isMainFrame) {
// Keep track of active cluster-id for handling IPC/menus/etc.
reaction(() => getMatchedClusterId(), clusterId => {
ipcRenderer.send("cluster-view:current-id", clusterId);
}, {
fireImmediately: true
})
}
// Handle navigation via IPC (e.g. from top menu)
ipcRenderer.on("menu:navigate", (event, location: LocationDescriptor) => {
logger.info(`[IPC]: ${event.type} ${JSON.stringify(location)}`, event);
navigate(location);
});
// Reload dashboard window
ipcRenderer.on("menu:reload", () => {
location.reload();
});