mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Convert all of kube-details-params/ and navigate/ to injectable
- This fixes a runtime error that was encountered during testing Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
b3cf868078
commit
fd86ea0d97
@ -134,7 +134,7 @@ export class KubernetesCluster<
|
||||
}
|
||||
}
|
||||
|
||||
class KubernetesClusterCategory extends CatalogCategory {
|
||||
export class KubernetesClusterCategory extends CatalogCategory {
|
||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "CatalogCategory";
|
||||
public metadata = {
|
||||
@ -151,5 +151,3 @@ class KubernetesClusterCategory extends CatalogCategory {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const kubernetesClusterCategory = new KubernetesClusterCategory();
|
||||
|
||||
15
src/common/catalog/categories/general.injectable.ts
Normal file
15
src/common/catalog/categories/general.injectable.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { GeneralCategory } from "../../catalog-entities";
|
||||
import { builtInCategoryInjectionToken } from "../category-registry.injectable";
|
||||
|
||||
const generalCategoryInjectable = getInjectable({
|
||||
id: "general-category",
|
||||
instantiate: () => new GeneralCategory(),
|
||||
injectionToken: builtInCategoryInjectionToken,
|
||||
});
|
||||
|
||||
export default generalCategoryInjectable;
|
||||
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { KubernetesClusterCategory } from "../../catalog-entities/kubernetes-cluster";
|
||||
import { builtInCategoryInjectionToken } from "../category-registry.injectable";
|
||||
|
||||
const kubernetesClusterCategoryInjectable = getInjectable({
|
||||
id: "kubernetes-cluster-category",
|
||||
instantiate: () => new KubernetesClusterCategory(),
|
||||
injectionToken: builtInCategoryInjectionToken,
|
||||
});
|
||||
|
||||
export default kubernetesClusterCategoryInjectable;
|
||||
15
src/common/catalog/categories/weblink.injectable.ts
Normal file
15
src/common/catalog/categories/weblink.injectable.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { WebLinkCategory } from "../../catalog-entities";
|
||||
import { builtInCategoryInjectionToken } from "../category-registry.injectable";
|
||||
|
||||
const weblinkCategoryInjectable = getInjectable({
|
||||
id: "weblink-category",
|
||||
instantiate: () => new WebLinkCategory(),
|
||||
injectionToken: builtInCategoryInjectionToken,
|
||||
});
|
||||
|
||||
export default weblinkCategoryInjectable;
|
||||
@ -2,18 +2,25 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { GeneralCategory, kubernetesClusterCategory, WebLinkCategory } from "../catalog-entities";
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import { WebLinkCategory } from "../catalog-entities";
|
||||
import type { CatalogCategory } from "./catalog-entity";
|
||||
import { CatalogCategoryRegistry } from "./category-registry";
|
||||
|
||||
export const builtInCategoryInjectionToken = getInjectionToken<CatalogCategory>({
|
||||
id: "built-in-category-token",
|
||||
});
|
||||
|
||||
const catalogCategoryRegistryInjectable = getInjectable({
|
||||
id: "catalog-category-registry",
|
||||
instantiate: () => {
|
||||
instantiate: (di) => {
|
||||
const registry = new CatalogCategoryRegistry();
|
||||
const categories = di.injectMany(builtInCategoryInjectionToken);
|
||||
|
||||
for (const category of categories) {
|
||||
registry.add(category);
|
||||
}
|
||||
|
||||
// TODO: move to different place
|
||||
registry.add(new GeneralCategory());
|
||||
registry.add(kubernetesClusterCategory);
|
||||
registry.add(new WebLinkCategory());
|
||||
|
||||
return registry;
|
||||
|
||||
@ -3,13 +3,17 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import kubernetesClusterCategoryInjectable from "../../common/catalog/categories/kubernetes-cluster.injectable";
|
||||
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||
|
||||
export {
|
||||
KubernetesCluster,
|
||||
kubernetesClusterCategory,
|
||||
GeneralEntity,
|
||||
WebLink,
|
||||
} from "../../common/catalog-entities";
|
||||
|
||||
export const kubernetesClusterCategory = asLegacyGlobalForExtensionApi(kubernetesClusterCategoryInjectable);
|
||||
|
||||
export type {
|
||||
KubernetesClusterPrometheusMetrics,
|
||||
KubernetesClusterSpec,
|
||||
|
||||
@ -40,6 +40,6 @@ export interface RegisteredPage {
|
||||
id: string;
|
||||
extensionId: string;
|
||||
url: string; // registered extension's page URL (without page params)
|
||||
params: PageParams<PageParam>; // normalized params
|
||||
params: PageParams<PageParam<any>>; // normalized params
|
||||
components: PageComponents; // normalized components
|
||||
}
|
||||
|
||||
@ -3,14 +3,20 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { PageParamInit } from "../../renderer/navigation";
|
||||
import { navigation, PageParam } from "../../renderer/navigation";
|
||||
import getDetailsUrlInjectable from "../../renderer/components/kube-detail-params/get-details-url.injectable";
|
||||
import hideDetailsInjectable from "../../renderer/components/kube-detail-params/hide-details.injectable";
|
||||
import showDetailsInjectable from "../../renderer/components/kube-detail-params/show-details.injectable";
|
||||
import createPageParamInjectable from "../../renderer/navigation/create-page-param.injectable";
|
||||
import isActiveRouteInjectable from "../../renderer/navigation/is-route-active.injectable";
|
||||
import navigateInjectable from "../../renderer/navigation/navigate.injectable";
|
||||
import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||
|
||||
export type { PageParamInit, PageParam } from "../../renderer/navigation/page-param";
|
||||
export { navigate, isActiveRoute } from "../../renderer/navigation/helpers";
|
||||
export { hideDetails, showDetails, getDetailsUrl } from "../../renderer/components/kube-detail-params";
|
||||
export type { PageParamInit, PageParam } from "../../renderer/navigation";
|
||||
export type { URLParams } from "../../common/utils/buildUrl";
|
||||
|
||||
export function createPageParam<V>(init: PageParamInit<V>) {
|
||||
return new PageParam<V>(init, navigation);
|
||||
}
|
||||
export const getDetailsUrl = asLegacyGlobalFunctionForExtensionApi(getDetailsUrlInjectable);
|
||||
export const showDetails = asLegacyGlobalFunctionForExtensionApi(showDetailsInjectable);
|
||||
export const hideDetails = asLegacyGlobalFunctionForExtensionApi(hideDetailsInjectable);
|
||||
export const createPageParam = asLegacyGlobalFunctionForExtensionApi(createPageParamInjectable);
|
||||
export const isActiveRoute = asLegacyGlobalFunctionForExtensionApi(isActiveRouteInjectable);
|
||||
export const navigate = asLegacyGlobalFunctionForExtensionApi(navigateInjectable);
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { when } from "mobx";
|
||||
import { catalogCategoryRegistry } from "../../../common/catalog";
|
||||
import { catalogEntityRegistry } from "../catalog-entity-registry";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import assert from "assert";
|
||||
|
||||
export async function setEntityOnRouteMatch() {
|
||||
await when(() => catalogEntityRegistry.entities.size > 0);
|
||||
|
||||
const generalCategory = catalogCategoryRegistry.getByName("General");
|
||||
|
||||
assert(generalCategory);
|
||||
const entities = catalogEntityRegistry.getItemsForCategory(generalCategory);
|
||||
const activeEntity = entities.find(entity => isActiveRoute(entity.spec.path));
|
||||
|
||||
if (activeEntity) {
|
||||
catalogEntityRegistry.activeEntity = activeEntity;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { reaction, when } from "mobx";
|
||||
import generalCategoryInjectable from "../../../common/catalog/categories/general.injectable";
|
||||
import isActiveRouteInjectable from "../../navigation/is-route-active.injectable";
|
||||
import observableHistoryInjectable from "../../navigation/observable-history.injectable";
|
||||
import type { Disposer } from "../../utils";
|
||||
import { disposer } from "../../utils";
|
||||
import catalogEntityRegistryInjectable from "../catalog-entity-registry/catalog-entity-registry.injectable";
|
||||
|
||||
export type WatchForGeneralEntityNavigation = () => Disposer;
|
||||
|
||||
const watchForGeneralEntityNavigationInjectable = getInjectable({
|
||||
id: "watch-for-general-entity-navigation",
|
||||
instantiate: (di): WatchForGeneralEntityNavigation => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
const isActiveRoute = di.inject(isActiveRouteInjectable);
|
||||
const entityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
const generalCategory = di.inject(generalCategoryInjectable);
|
||||
|
||||
return () => {
|
||||
const dispose = disposer();
|
||||
|
||||
dispose.push(when(
|
||||
() => entityRegistry.entities.size > 0,
|
||||
() => {
|
||||
dispose.push(reaction(
|
||||
() => observableHistory.location,
|
||||
() => {
|
||||
const entities = entityRegistry.getItemsForCategory(generalCategory);
|
||||
const activeEntity = entities.find(entity => isActiveRoute(entity.spec.path));
|
||||
|
||||
if (activeEntity) {
|
||||
entityRegistry.activeEntity = activeEntity;
|
||||
}
|
||||
},
|
||||
{
|
||||
fireImmediately: true,
|
||||
},
|
||||
));
|
||||
},
|
||||
));
|
||||
|
||||
return dispose;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default watchForGeneralEntityNavigationInjectable;
|
||||
@ -45,6 +45,7 @@ import { bindEvents } from "./navigation/events";
|
||||
import assert from "assert";
|
||||
import openDeleteClusterDialogInjectable from "./components/delete-cluster-dialog/open.injectable";
|
||||
import { init } from "@sentry/electron/renderer";
|
||||
import kubernetesClusterCategoryInjectable from "../common/catalog/categories/kubernetes-cluster.injectable";
|
||||
|
||||
configurePackages(); // global packages
|
||||
registerCustomThemes(); // monaco editor themes
|
||||
@ -95,13 +96,11 @@ export async function bootstrap(di: DiContainer) {
|
||||
logger.info(`${logPrefix} initializing CatalogEntityDetailRegistry`);
|
||||
initializers.initCatalogEntityDetailRegistry();
|
||||
|
||||
const navigateToAddCluster = di.inject(navigateToAddClusterInjectable);
|
||||
const addSyncEntries = di.inject(addSyncEntriesInjectable);
|
||||
|
||||
logger.info(`${logPrefix} initializing CatalogCategoryRegistryEntries`);
|
||||
initializers.initCatalogCategoryRegistryEntries({
|
||||
navigateToAddCluster,
|
||||
addSyncEntries,
|
||||
navigateToAddCluster: di.inject(navigateToAddClusterInjectable),
|
||||
addSyncEntries: di.inject(addSyncEntriesInjectable),
|
||||
kubernetesClusterCategory: di.inject(kubernetesClusterCategoryInjectable),
|
||||
});
|
||||
|
||||
logger.info(`${logPrefix} initializing Catalog`);
|
||||
|
||||
@ -15,7 +15,6 @@ import { cssNames, prevDefault } from "../../utils";
|
||||
import type { ItemObject } from "../../../common/item.store";
|
||||
import { Spinner } from "../spinner";
|
||||
import type { ThemeStore } from "../../themes/store";
|
||||
import { kubeSelectedUrlParam, toggleDetails } from "../kube-detail-params";
|
||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
@ -25,6 +24,10 @@ import type { EventStore } from "../+events/store";
|
||||
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||
import eventStoreInjectable from "../+events/store.injectable";
|
||||
import nodeStoreInjectable from "../+nodes/store.injectable";
|
||||
import type { PageParam } from "../../navigation";
|
||||
import type { ToggleDetails } from "../kube-detail-params/toggle-details.injectable";
|
||||
import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable";
|
||||
import toggleDetailsInjectable from "../kube-detail-params/toggle-details.injectable";
|
||||
|
||||
export interface ClusterIssuesProps {
|
||||
className?: string;
|
||||
@ -49,6 +52,8 @@ interface Dependencies {
|
||||
nodeStore: NodeStore;
|
||||
eventStore: EventStore;
|
||||
apiManager: ApiManager;
|
||||
kubeSelectedUrlParam: PageParam<string>;
|
||||
toggleDetails: ToggleDetails;
|
||||
}
|
||||
|
||||
@observer
|
||||
@ -86,6 +91,7 @@ class NonInjectedClusterIssues extends React.Component<ClusterIssuesProps & Depe
|
||||
|
||||
getTableRow = (uid: string) => {
|
||||
const { warnings } = this;
|
||||
const { kubeSelectedUrlParam, toggleDetails } = this.props;
|
||||
const warning = warnings.find(warn => warn.getId() == uid);
|
||||
|
||||
if (!warning) {
|
||||
@ -189,5 +195,7 @@ export const ClusterIssues = withInjectables<Dependencies, ClusterIssuesProps>(N
|
||||
apiManager: di.inject(apiManagerInjectable),
|
||||
eventStore: di.inject(eventStoreInjectable),
|
||||
nodeStore: di.inject(nodeStoreInjectable),
|
||||
kubeSelectedUrlParam: di.inject(kubeSelectedUrlParamInjectable),
|
||||
toggleDetails: di.inject(toggleDetailsInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -14,13 +14,11 @@ const closePreferencesInjectable = getInjectable({
|
||||
const navigateToFrontPage = di.inject(navigateToFrontPageInjectable);
|
||||
|
||||
return () => {
|
||||
if (observableHistory.length === 1) {
|
||||
if (observableHistory.length <= 1) {
|
||||
navigateToFrontPage();
|
||||
|
||||
return;
|
||||
} else {
|
||||
observableHistory.goBack();
|
||||
}
|
||||
|
||||
observableHistory.goBack();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@ -15,25 +15,25 @@ import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { TopBar } from "../layout/top-bar/top-bar";
|
||||
import catalogPreviousActiveTabStorageInjectable from "../+catalog/catalog-previous-active-tab-storage/catalog-previous-active-tab-storage.injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { reaction } from "mobx";
|
||||
import currentRouteComponentInjectable from "../../routes/current-route-component.injectable";
|
||||
import { setEntityOnRouteMatch } from "../../api/helpers/general-active-sync";
|
||||
import { navigation } from "../../navigation";
|
||||
import welcomeRouteInjectable from "../../../common/front-end-routing/routes/welcome/welcome-route.injectable";
|
||||
import { buildURL } from "../../../common/utils/buildUrl";
|
||||
import type { StorageLayer } from "../../utils";
|
||||
import type { WatchForGeneralEntityNavigation } from "../../api/helpers/watch-for-general-entity-navigation.injectable";
|
||||
import watchForGeneralEntityNavigationInjectable from "../../api/helpers/watch-for-general-entity-navigation.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
catalogPreviousActiveTabStorage: StorageLayer<string | null>;
|
||||
currentRouteComponent: IComputedValue<React.ElementType | undefined>;
|
||||
welcomeUrl: string;
|
||||
watchForGeneralEntityNavigation: WatchForGeneralEntityNavigation;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedClusterManager extends React.Component<Dependencies> {
|
||||
componentDidMount() {
|
||||
disposeOnUnmount(this, [
|
||||
reaction(() => navigation.location, () => setEntityOnRouteMatch(), { fireImmediately: true }),
|
||||
this.props.watchForGeneralEntityNavigation(),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -64,5 +64,6 @@ export const ClusterManager = withInjectables<Dependencies>(NonInjectedClusterMa
|
||||
catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable),
|
||||
currentRouteComponent: di.inject(currentRouteComponentInjectable),
|
||||
welcomeUrl: buildURL(di.inject(welcomeRouteInjectable).path),
|
||||
watchForGeneralEntityNavigation: di.inject(watchForGeneralEntityNavigationInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -18,7 +18,7 @@ import drawerStorageInjectable, {
|
||||
defaultDrawerWidth,
|
||||
} from "./drawer-storage/drawer-storage.injectable";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import historyInjectable from "../../navigation/history/history.injectable";
|
||||
import historyInjectable from "../../navigation/history.injectable";
|
||||
import type { History } from "history";
|
||||
|
||||
export type DrawerPosition = "top" | "left" | "right" | "bottom";
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import observableHistoryInjectable from "../../navigation/observable-history.injectable";
|
||||
import kubeDetailsUrlParamInjectable from "./kube-details-url.injectable";
|
||||
import kubeSelectedUrlParamInjectable from "./kube-selected-url.injectable";
|
||||
|
||||
export type GetDetailsUrl = (selfLink: string, resetSelected?: boolean, mergeGlobals?: boolean) => string;
|
||||
|
||||
const getDetailsUrlInjectable = getInjectable({
|
||||
id: "get-details-url",
|
||||
instantiate: (di): GetDetailsUrl => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
const kubeDetailsUrlParam = di.inject(kubeDetailsUrlParamInjectable);
|
||||
const kubeSelectedUrlParam = di.inject(kubeSelectedUrlParamInjectable);
|
||||
|
||||
return (selfLink, resetSelected = false, mergeGlobals = true) => {
|
||||
const params = new URLSearchParams(mergeGlobals ? observableHistory.searchParams : "");
|
||||
|
||||
params.set(kubeDetailsUrlParam.name, selfLink);
|
||||
|
||||
if (resetSelected) {
|
||||
params.delete(kubeSelectedUrlParam.name);
|
||||
} else {
|
||||
params.set(kubeSelectedUrlParam.name, kubeSelectedUrlParam.get());
|
||||
}
|
||||
|
||||
return `?${params}`;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default getDetailsUrlInjectable;
|
||||
@ -2,12 +2,18 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { hideDetails } from "../../kube-detail-params";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import showDetailsInjectable from "./show-details.injectable";
|
||||
|
||||
export type HideDetails = () => void;
|
||||
|
||||
const hideDetailsInjectable = getInjectable({
|
||||
id: "hide-details",
|
||||
instantiate: () => hideDetails,
|
||||
instantiate: (di): HideDetails => {
|
||||
const showDetails = di.inject(showDetailsInjectable);
|
||||
|
||||
return () => showDetails("");
|
||||
},
|
||||
});
|
||||
|
||||
export default hideDetailsInjectable;
|
||||
@ -3,4 +3,16 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./params";
|
||||
import { asLegacyGlobalFunctionForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||
import getDetailsUrlInjectable from "./get-details-url.injectable";
|
||||
import showDetailsInjectable from "./show-details.injectable";
|
||||
|
||||
/**
|
||||
* @deprecated use `di.inject(getDetailsUrlInjectable)` instead
|
||||
*/
|
||||
export const getDetailsUrl = asLegacyGlobalFunctionForExtensionApi(getDetailsUrlInjectable);
|
||||
|
||||
/**
|
||||
* @deprecated use `di.inject(showDetailsInjectable)` instead
|
||||
*/
|
||||
export const showDetails = asLegacyGlobalFunctionForExtensionApi(showDetailsInjectable);
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import createPageParamInjectable from "../../navigation/create-page-param.injectable";
|
||||
|
||||
/**
|
||||
* Used to store `object.selfLink` to show more info about resource in the details panel.
|
||||
*/
|
||||
const kubeDetailsUrlParamInjectable = getInjectable({
|
||||
id: "kube-details-url-param",
|
||||
instantiate: (di) => {
|
||||
const createPageParam = di.inject(createPageParamInjectable);
|
||||
|
||||
return createPageParam({
|
||||
name: "kube-details",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default kubeDetailsUrlParamInjectable;
|
||||
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import createPageParamInjectable from "../../navigation/create-page-param.injectable";
|
||||
import kubeDetailsUrlParamInjectable from "./kube-details-url.injectable";
|
||||
|
||||
/**
|
||||
* Used to highlight last active/selected table row with the resource.
|
||||
*
|
||||
* @example
|
||||
* If we go to "Nodes (page) -> Node (details) -> Pod (details)",
|
||||
* last clicked Node should be "active" while Pod details are shown).
|
||||
*/
|
||||
const kubeSelectedUrlParamInjectable = getInjectable({
|
||||
id: "kube-selected-url-param",
|
||||
instantiate: (di) => {
|
||||
const createPageParam = di.inject(createPageParamInjectable);
|
||||
const kubeDetailsUrlParam = di.inject(kubeDetailsUrlParamInjectable);
|
||||
|
||||
return createPageParam({
|
||||
name: "kube-selected",
|
||||
get defaultValue() {
|
||||
return kubeDetailsUrlParam.get();
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default kubeSelectedUrlParamInjectable;
|
||||
@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { createPageParam, navigation } from "../../navigation";
|
||||
|
||||
/**
|
||||
* Used to store `object.selfLink` to show more info about resource in the details panel.
|
||||
*/
|
||||
export const kubeDetailsUrlParam = createPageParam({
|
||||
name: "kube-details",
|
||||
});
|
||||
|
||||
/**
|
||||
* Used to highlight last active/selected table row with the resource.
|
||||
*
|
||||
* @example
|
||||
* If we go to "Nodes (page) -> Node (details) -> Pod (details)",
|
||||
* last clicked Node should be "active" while Pod details are shown).
|
||||
*/
|
||||
export const kubeSelectedUrlParam = createPageParam({
|
||||
name: "kube-selected",
|
||||
get defaultValue() {
|
||||
return kubeDetailsUrlParam.get();
|
||||
},
|
||||
});
|
||||
|
||||
export function toggleDetails(selfLink: string | undefined, resetSelected = true) {
|
||||
const current = kubeSelectedUrlParam.get() === selfLink;
|
||||
|
||||
if (current) {
|
||||
hideDetails();
|
||||
} else {
|
||||
showDetails(selfLink, resetSelected);
|
||||
}
|
||||
}
|
||||
|
||||
export function showDetails(selfLink = "", resetSelected = true) {
|
||||
const detailsUrl = getDetailsUrl(selfLink, resetSelected);
|
||||
|
||||
navigation.merge({ search: detailsUrl });
|
||||
}
|
||||
|
||||
export function hideDetails() {
|
||||
showDetails();
|
||||
}
|
||||
|
||||
export function getDetailsUrl(selfLink: string, resetSelected = false, mergeGlobals = true) {
|
||||
const params = new URLSearchParams(mergeGlobals ? navigation.searchParams : "");
|
||||
|
||||
params.set(kubeDetailsUrlParam.name, selfLink);
|
||||
|
||||
if (resetSelected) {
|
||||
params.delete(kubeSelectedUrlParam.name);
|
||||
} else {
|
||||
params.set(kubeSelectedUrlParam.name, kubeSelectedUrlParam.get());
|
||||
}
|
||||
|
||||
return `?${params}`;
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import observableHistoryInjectable from "../../navigation/observable-history.injectable";
|
||||
import getDetailsUrlInjectable from "./get-details-url.injectable";
|
||||
|
||||
/**
|
||||
* @param selfLink The Kube selflink to show details for
|
||||
* @param resetSelected If true then will reset the selected kube object (which object is highlighted generally)
|
||||
* @default resetSelected true
|
||||
*/
|
||||
export type ShowDetails = (selfLink: string, resetSelected?: boolean) => void;
|
||||
|
||||
const showDetailsInjectable = getInjectable({
|
||||
id: "show-details",
|
||||
instantiate: (di): ShowDetails => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
const getDetailsUrl = di.inject(getDetailsUrlInjectable);
|
||||
|
||||
return (selfLink, resetSelected = true) => {
|
||||
observableHistory.merge({
|
||||
search: getDetailsUrl(selfLink, resetSelected),
|
||||
});
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default showDetailsInjectable;
|
||||
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import hideDetailsInjectable from "./hide-details.injectable";
|
||||
import kubeSelectedUrlParamInjectable from "./kube-selected-url.injectable";
|
||||
import showDetailsInjectable from "./show-details.injectable";
|
||||
|
||||
export interface ToggleDetails {
|
||||
/**
|
||||
* @param selfLink The selfLink of the kube object to toggle the details panal of
|
||||
* @param resetSelected If true then the selected kube object (for the current list view) will be reset
|
||||
* @default resetSelected true
|
||||
*/
|
||||
(selfLink: string, resetSelected?: boolean): void;
|
||||
/**
|
||||
* @deprecated Future versions will not support passing in `selfLink` that is `undefined`
|
||||
*/
|
||||
(selfLink: string | undefined, resetSelected?: boolean): void;
|
||||
}
|
||||
|
||||
const toggleDetailsInjectable = getInjectable({
|
||||
id: "toggle-details",
|
||||
instantiate: (di): ToggleDetails => {
|
||||
const showDetails = di.inject(showDetailsInjectable);
|
||||
const hideDetails = di.inject(hideDetailsInjectable);
|
||||
const kubeSelectedUrlParam = di.inject(kubeSelectedUrlParamInjectable);
|
||||
|
||||
return (selfLink, resetSelected = true) => {
|
||||
const current = kubeSelectedUrlParam.get() === selfLink;
|
||||
|
||||
if (current || !selfLink) {
|
||||
hideDetails();
|
||||
} else {
|
||||
showDetails(selfLink, resetSelected);
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default toggleDetailsInjectable;
|
||||
@ -11,37 +11,50 @@ import { computed, observable, reaction, makeObservable } from "mobx";
|
||||
import { Drawer } from "../drawer";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import { Spinner } from "../spinner";
|
||||
import { apiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { customResourceDefinitionStore } from "../+custom-resources/legacy-store";
|
||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { KubeObjectMenu } from "../kube-object-menu";
|
||||
import { KubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
|
||||
import { CustomResourceDetails } from "../+custom-resources";
|
||||
import { KubeObjectMeta } from "../kube-object-meta";
|
||||
import { hideDetails, kubeDetailsUrlParam } from "../kube-detail-params";
|
||||
import type { PageParam } from "../../navigation";
|
||||
import type { HideDetails } from "../kube-detail-params/hide-details.injectable";
|
||||
import type { CustomResourceDefinitionStore } from "../+custom-resources/definition.store";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||
import customResourceDefinitionStoreInjectable from "../+custom-resources/definition.store.injectable";
|
||||
import hideDetailsInjectable from "../kube-detail-params/hide-details.injectable";
|
||||
import kubeDetailsUrlParamInjectable from "../kube-detail-params/kube-details-url.injectable";
|
||||
|
||||
|
||||
export interface KubeObjectDetailsProps<T extends KubeObject = KubeObject> {
|
||||
export interface KubeObjectDetailsProps<Kube extends KubeObject = KubeObject> {
|
||||
className?: string;
|
||||
object: T;
|
||||
object: Kube;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
kubeDetailsUrlParam: PageParam<string>;
|
||||
apiManager: ApiManager;
|
||||
hideDetails: HideDetails;
|
||||
customResourceDefinitionStore: CustomResourceDefinitionStore;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class KubeObjectDetails extends React.Component {
|
||||
class NonInjectedKubeObjectDetails extends React.Component<Dependencies> {
|
||||
@observable isLoading = false;
|
||||
@observable.ref loadingError: React.ReactNode;
|
||||
|
||||
constructor(props: {}) {
|
||||
constructor(props: Dependencies) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@computed get path() {
|
||||
return kubeDetailsUrlParam.get();
|
||||
return this.props.kubeDetailsUrlParam.get();
|
||||
}
|
||||
|
||||
@computed get object() {
|
||||
try {
|
||||
return apiManager
|
||||
return this.props.apiManager
|
||||
.getStore(this.path)
|
||||
?.getByPath(this.path);
|
||||
} catch (error) {
|
||||
@ -56,13 +69,13 @@ export class KubeObjectDetails extends React.Component {
|
||||
reaction(() => [
|
||||
this.path,
|
||||
this.object, // resource might be updated via watch-event or from already opened details
|
||||
customResourceDefinitionStore.items.length, // crd stores initialized after loading
|
||||
this.props.customResourceDefinitionStore.items.length, // crd stores initialized after loading
|
||||
], async () => {
|
||||
this.loadingError = "";
|
||||
const { path, object } = this;
|
||||
|
||||
if (!object) {
|
||||
const store = apiManager.getStore(path);
|
||||
const store = this.props.apiManager.getStore(path);
|
||||
|
||||
if (store) {
|
||||
this.isLoading = true;
|
||||
@ -103,7 +116,7 @@ export class KubeObjectDetails extends React.Component {
|
||||
));
|
||||
|
||||
if (details.length === 0) {
|
||||
const crd = customResourceDefinitionStore.getByObject(object);
|
||||
const crd = this.props.customResourceDefinitionStore.getByObject(object);
|
||||
|
||||
/**
|
||||
* This is a fallback so that if a custom resource object doesn't have
|
||||
@ -141,10 +154,20 @@ export class KubeObjectDetails extends React.Component {
|
||||
open={!!(object || isLoading || loadingError)}
|
||||
title={this.renderTitle(object)}
|
||||
toolbar={object && <KubeObjectMenu object={object} toolbar={true}/>}
|
||||
onClose={hideDetails}
|
||||
onClose={this.props.hideDetails}
|
||||
>
|
||||
{object && this.renderContents(object)}
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const KubeObjectDetails = withInjectables<Dependencies>(NonInjectedKubeObjectDetails, {
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
apiManager: di.inject(apiManagerInjectable),
|
||||
customResourceDefinitionStore: di.inject(customResourceDefinitionStoreInjectable),
|
||||
hideDetails: di.inject(hideDetailsInjectable),
|
||||
kubeDetailsUrlParam: di.inject(kubeDetailsUrlParamInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -17,7 +17,6 @@ import type { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"
|
||||
import { KubeObjectMenu } from "../kube-object-menu";
|
||||
import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter";
|
||||
import { ResourceKindMap, ResourceNames } from "../../utils/rbac";
|
||||
import { kubeSelectedUrlParam, toggleDetails } from "../kube-detail-params";
|
||||
import { Icon } from "../icon";
|
||||
import { TooltipPosition } from "../tooltip";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
@ -26,6 +25,10 @@ import clusterFrameContextInjectable from "../../cluster-frame-context/cluster-f
|
||||
import type { SubscribableStore, SubscribeStores } from "../../kube-watch-api/kube-watch-api";
|
||||
import type { KubeApi } from "../../../common/k8s-api/kube-api";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import type { PageParam } from "../../navigation";
|
||||
import type { ToggleDetails } from "../kube-detail-params/toggle-details.injectable";
|
||||
import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable";
|
||||
import toggleDetailsInjectable from "../kube-detail-params/toggle-details.injectable";
|
||||
|
||||
export interface KubeObjectListLayoutProps<
|
||||
K extends KubeObject,
|
||||
@ -42,6 +45,8 @@ export interface KubeObjectListLayoutProps<
|
||||
interface Dependencies {
|
||||
clusterFrameContext: ClusterFrameContext;
|
||||
subscribeToStores: SubscribeStores;
|
||||
kubeSelectedUrlParam: PageParam<string>;
|
||||
toggleDetails: ToggleDetails;
|
||||
}
|
||||
|
||||
@observer
|
||||
@ -51,14 +56,13 @@ class NonInjectedKubeObjectListLayout<
|
||||
D extends KubeJsonApiDataFor<K>,
|
||||
> extends React.Component<KubeObjectListLayoutProps<K, A, D> & Dependencies> {
|
||||
static defaultProps = {
|
||||
onDetails: (item: KubeObject) => toggleDetails(item.selfLink),
|
||||
subscribeStores: true,
|
||||
};
|
||||
|
||||
private loadErrors = observable.array<string>();
|
||||
|
||||
@computed get selectedItem() {
|
||||
return this.props.store.getByPath(kubeSelectedUrlParam.get());
|
||||
return this.props.store.getByPath(this.props.kubeSelectedUrlParam.get());
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -104,7 +108,16 @@ class NonInjectedKubeObjectListLayout<
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, customizeHeader, store, items, dependentStores, ...layoutProps } = this.props;
|
||||
const {
|
||||
className,
|
||||
customizeHeader,
|
||||
store,
|
||||
items,
|
||||
dependentStores,
|
||||
toggleDetails,
|
||||
onDetails,
|
||||
...layoutProps
|
||||
} = this.props;
|
||||
const placeholderString = ResourceNames[ResourceKindMap[store.api.kind]] || store.api.kind;
|
||||
|
||||
return (
|
||||
@ -137,13 +150,14 @@ class NonInjectedKubeObjectListLayout<
|
||||
...[customizeHeader].filter(isDefined).flat(),
|
||||
]}
|
||||
renderItemMenu={item => <KubeObjectMenu object={item} />}
|
||||
onDetails={onDetails ?? ((item) => toggleDetails(item.selfLink))}
|
||||
{...layoutProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const InjectedKubeObjectListLayout = withInjectables<
|
||||
export const KubeObjectListLayout = withInjectables<
|
||||
Dependencies,
|
||||
KubeObjectListLayoutProps<KubeObject, KubeApi<KubeObject, KubeJsonApiDataFor<KubeObject>>, KubeJsonApiDataFor<KubeObject>>
|
||||
>(NonInjectedKubeObjectListLayout, {
|
||||
@ -151,13 +165,11 @@ const InjectedKubeObjectListLayout = withInjectables<
|
||||
...props,
|
||||
clusterFrameContext: di.inject(clusterFrameContextInjectable),
|
||||
subscribeToStores: di.inject(subscribeStoresInjectable),
|
||||
kubeSelectedUrlParam: di.inject(kubeSelectedUrlParamInjectable),
|
||||
toggleDetails: di.inject(toggleDetailsInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
export function KubeObjectListLayout<
|
||||
}) as <
|
||||
K extends KubeObject,
|
||||
A extends KubeApi<K, D>,
|
||||
D extends KubeJsonApiDataFor<K> = KubeJsonApiDataFor<K>,
|
||||
>(props: KubeObjectListLayoutProps<K, A, D>) {
|
||||
return <InjectedKubeObjectListLayout {...(props as never)} />;
|
||||
}
|
||||
>(props: KubeObjectListLayoutProps<K, A, D>) => React.ReactElement;
|
||||
|
||||
@ -13,9 +13,10 @@ import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import clusterNameInjectable from "./dependencies/cluster-name.injectable";
|
||||
import createEditResourceTabInjectable from "../dock/edit-resource/edit-resource-tab.injectable";
|
||||
import hideDetailsInjectable from "./dependencies/hide-details.injectable";
|
||||
import kubeObjectMenuItemsInjectable from "./dependencies/kube-object-menu-items/kube-object-menu-items.injectable";
|
||||
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||
import type { HideDetails } from "../kube-detail-params/hide-details.injectable";
|
||||
import hideDetailsInjectable from "../kube-detail-params/hide-details.injectable";
|
||||
|
||||
export interface KubeObjectMenuProps<TKubeObject extends KubeObject> extends MenuActionsProps {
|
||||
object: TKubeObject;
|
||||
@ -27,7 +28,7 @@ interface Dependencies {
|
||||
apiManager: ApiManager;
|
||||
kubeObjectMenuItems: React.ElementType[];
|
||||
clusterName: string | undefined;
|
||||
hideDetails: () => void;
|
||||
hideDetails: HideDetails;
|
||||
createEditResourceTab: (kubeObject: KubeObject) => void;
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ import { cssNames } from "../../utils";
|
||||
import { CloseButton } from "./close-button";
|
||||
import { getLegacyGlobalDiForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
||||
import observableHistoryInjectable from "../../navigation/history/observable.injectable";
|
||||
import observableHistoryInjectable from "../../navigation/observable-history.injectable";
|
||||
|
||||
export interface SettingLayoutProps extends React.DOMAttributes<any> {
|
||||
className?: IClassName;
|
||||
|
||||
@ -37,7 +37,7 @@ import { observable, toJS } from "mobx";
|
||||
import type { Draft } from "immer";
|
||||
import { produce, isDraft } from "immer";
|
||||
import type { GetDiForUnitTestingOptions } from "../test-utils/get-dis-for-unit-testing";
|
||||
import historyInjectable from "./navigation/history/history.injectable";
|
||||
import historyInjectable from "./navigation/history.injectable";
|
||||
|
||||
export interface GetRendererDiForUnitTestingOptions extends GetDiForUnitTestingOptions {
|
||||
overrideCreateStorage?: boolean;
|
||||
|
||||
@ -3,16 +3,21 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { kubernetesClusterCategory } from "../../common/catalog-entities";
|
||||
import type { KubernetesClusterCategory } from "../../common/catalog-entities";
|
||||
import { isLinux, isWindows } from "../../common/vars";
|
||||
import { PathPicker } from "../components/path-picker";
|
||||
|
||||
interface Dependencies {
|
||||
navigateToAddCluster: () => void;
|
||||
addSyncEntries: (filePaths: string[]) => void;
|
||||
kubernetesClusterCategory: KubernetesClusterCategory;
|
||||
}
|
||||
|
||||
export function initCatalogCategoryRegistryEntries({ navigateToAddCluster, addSyncEntries } : Dependencies) {
|
||||
export function initCatalogCategoryRegistryEntries({
|
||||
navigateToAddCluster,
|
||||
addSyncEntries,
|
||||
kubernetesClusterCategory,
|
||||
} : Dependencies) {
|
||||
kubernetesClusterCategory.on("catalogAddMenu", ctx => {
|
||||
ctx.menuItems.push(
|
||||
{
|
||||
|
||||
23
src/renderer/navigation/create-page-param.injectable.ts
Normal file
23
src/renderer/navigation/create-page-param.injectable.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import observableHistoryInjectable from "./observable-history.injectable";
|
||||
import type { PageParamDependencies, PageParamInit } from "./page-param";
|
||||
import { PageParam } from "./page-param";
|
||||
|
||||
export type CreatePageParam = <Value = string>(init: PageParamInit<Value>) => PageParam<Value>;
|
||||
|
||||
const createPageParamInjectable = getInjectable({
|
||||
id: "create-page-param",
|
||||
instantiate: (di): CreatePageParam => {
|
||||
const deps: PageParamDependencies = {
|
||||
history: di.inject(observableHistoryInjectable),
|
||||
};
|
||||
|
||||
return (init) => new PageParam(deps, init);
|
||||
},
|
||||
});
|
||||
|
||||
export default createPageParamInjectable;
|
||||
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LocationDescriptor } from "history";
|
||||
import { createPath } from "history";
|
||||
import type { RouteProps } from "react-router";
|
||||
import { matchPath } from "react-router";
|
||||
import { navigation } from "./history";
|
||||
import type { PageParamInit } from "./page-param";
|
||||
import { PageParam } from "./page-param";
|
||||
|
||||
export function navigate(location: LocationDescriptor) {
|
||||
const currentLocation = createPath(navigation.location);
|
||||
|
||||
navigation.push(location);
|
||||
|
||||
const newLocation = createPath(navigation.location);
|
||||
|
||||
if (currentLocation === newLocation) {
|
||||
navigation.goBack(); // prevent sequences of same url in history
|
||||
}
|
||||
}
|
||||
|
||||
export function createPageParam<V = string>(init: PageParamInit<V>) {
|
||||
return new PageParam<V>(init, navigation);
|
||||
}
|
||||
|
||||
export function matchRoute<P>(route: string | string[] | RouteProps) {
|
||||
return matchPath<P>(navigation.location.pathname, route);
|
||||
}
|
||||
|
||||
export function isActiveRoute(route: string | string[] | RouteProps): boolean {
|
||||
return !!matchRoute(route);
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||
import observableHistoryInjectable from "./history/observable.injectable";
|
||||
|
||||
export { searchParamsOptions } from "./history/search-params";
|
||||
|
||||
/**
|
||||
* @deprecated: Switch to using di.inject(observableHistoryInjectable)
|
||||
*/
|
||||
export const navigation = asLegacyGlobalForExtensionApi(observableHistoryInjectable);
|
||||
@ -3,8 +3,33 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Navigation (renderer)
|
||||
import { asLegacyGlobalFunctionForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||
import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||
import createPageParamInjectable from "./create-page-param.injectable";
|
||||
import matchRouteInjectable from "./match-route.injectable";
|
||||
import navigateInjectable from "./navigate.injectable";
|
||||
import observableHistoryInjectable from "./observable-history.injectable";
|
||||
|
||||
export { searchParamsOptions } from "./search-params";
|
||||
|
||||
/**
|
||||
* @deprecated use `di.inject(observableHistoryInjectable)` instead
|
||||
*/
|
||||
export const navigation = asLegacyGlobalForExtensionApi(observableHistoryInjectable);
|
||||
|
||||
/**
|
||||
* @deprecated use `di.inject(navigateInjectable)` instead
|
||||
*/
|
||||
export const navigate = asLegacyGlobalFunctionForExtensionApi(navigateInjectable);
|
||||
|
||||
/**
|
||||
* @deprecated use `di.inject(createPageParamInjectable)` instead
|
||||
*/
|
||||
export const createPageParam = asLegacyGlobalFunctionForExtensionApi(createPageParamInjectable);
|
||||
|
||||
/**
|
||||
* @deprecated use `di.inject(matchRouteInjectable)` instead
|
||||
*/
|
||||
export const matchRoute = asLegacyGlobalFunctionForExtensionApi(matchRouteInjectable);
|
||||
|
||||
export * from "./history";
|
||||
export * from "./helpers";
|
||||
export * from "./page-param";
|
||||
|
||||
20
src/renderer/navigation/is-route-active.injectable.ts
Normal file
20
src/renderer/navigation/is-route-active.injectable.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { RouteProps } from "react-router";
|
||||
import matchRouteInjectable from "./match-route.injectable";
|
||||
|
||||
export type IsRouteActive = (route: string | string[] | RouteProps) => boolean;
|
||||
|
||||
const isActiveRouteInjectable = getInjectable({
|
||||
id: "is-active-route",
|
||||
instantiate: (di): IsRouteActive => {
|
||||
const matchRoute = di.inject(matchRouteInjectable);
|
||||
|
||||
return (route) => Boolean(matchRoute(route));
|
||||
},
|
||||
});
|
||||
|
||||
export default isActiveRouteInjectable;
|
||||
21
src/renderer/navigation/match-route.injectable.ts
Normal file
21
src/renderer/navigation/match-route.injectable.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { match, RouteProps } from "react-router";
|
||||
import { matchPath } from "react-router";
|
||||
import observableHistoryInjectable from "./observable-history.injectable";
|
||||
|
||||
export type MatchRoute = <Params>(route: string | string[] | RouteProps) => match<Params> | null;
|
||||
|
||||
const matchRouteInjectable = getInjectable({
|
||||
id: "match-route",
|
||||
instantiate: (di): MatchRoute => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
|
||||
return (route) => matchPath(observableHistory.location.pathname, route);
|
||||
},
|
||||
});
|
||||
|
||||
export default matchRouteInjectable;
|
||||
32
src/renderer/navigation/navigate.injectable.ts
Normal file
32
src/renderer/navigation/navigate.injectable.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { LocationDescriptor } from "history";
|
||||
import { action } from "mobx";
|
||||
import observableHistoryInjectable from "./observable-history.injectable";
|
||||
import { createPath } from "history";
|
||||
|
||||
export type Navigate = (location: LocationDescriptor) => void;
|
||||
|
||||
const navigateInjectable = getInjectable({
|
||||
id: "navigate",
|
||||
instantiate: (di): Navigate => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
|
||||
return action((location) => {
|
||||
const currentLocation = createPath(observableHistory.location);
|
||||
|
||||
observableHistory.push(location);
|
||||
|
||||
const newLocation = createPath(observableHistory.location);
|
||||
|
||||
if (currentLocation === newLocation) {
|
||||
observableHistory.goBack(); // prevent sequences of same url in history
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default navigateInjectable;
|
||||
@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { createObservableHistory } from "mobx-observable-history";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import { searchParamsOptions } from "./search-params";
|
||||
import historyInjectable from "./history.injectable";
|
||||
|
||||
@ -7,19 +7,23 @@
|
||||
import { action, makeObservable } from "mobx";
|
||||
import type { ObservableHistory } from "mobx-observable-history";
|
||||
|
||||
export interface PageParamInit<V = any> {
|
||||
export interface PageParamInit<Value = any> {
|
||||
name: string;
|
||||
defaultValue?: V; // multi-values param must be defined with array-value, e.g. []
|
||||
parse?(value: string | string[]): V; // from URL
|
||||
stringify?(value: V): string | string[]; // to URL
|
||||
defaultValue?: Value; // multi-values param must be defined with array-value, e.g. []
|
||||
parse?(value: string | string[]): Value; // from URL
|
||||
stringify?(value: Value): string | string[]; // to URL
|
||||
}
|
||||
|
||||
export interface PageParamDependencies {
|
||||
readonly history: ObservableHistory<unknown>;
|
||||
}
|
||||
|
||||
// TODO: write tests
|
||||
export class PageParam<V = any> {
|
||||
export class PageParam<Value = any> {
|
||||
readonly name: string;
|
||||
readonly isMulti: boolean;
|
||||
|
||||
constructor(private init: PageParamInit<V>, private history: ObservableHistory<unknown>) {
|
||||
constructor(protected readonly dependencies: PageParamDependencies, private init: PageParamInit<Value>) {
|
||||
makeObservable(this);
|
||||
const { name, defaultValue } = init;
|
||||
|
||||
@ -28,11 +32,11 @@ export class PageParam<V = any> {
|
||||
}
|
||||
|
||||
// should be a getter since `init.defaultValue` could be a getter too
|
||||
get defaultValue(): V | undefined {
|
||||
get defaultValue(): Value | undefined {
|
||||
return this.init.defaultValue;
|
||||
}
|
||||
|
||||
parse(values: string | string[]): V {
|
||||
parse(values: string | string[]): Value {
|
||||
const { parse } = this.init;
|
||||
|
||||
if (parse) {
|
||||
@ -40,10 +44,10 @@ export class PageParam<V = any> {
|
||||
}
|
||||
|
||||
// return as-is ("string"-value based params)
|
||||
return values as any as V;
|
||||
return values as unknown as Value;
|
||||
}
|
||||
|
||||
stringify(value: V = this.get()): string[] {
|
||||
stringify(value: Value = this.get()): string[] {
|
||||
const { stringify } = this.init;
|
||||
|
||||
if (stringify) {
|
||||
@ -53,17 +57,17 @@ export class PageParam<V = any> {
|
||||
return [value].flat().map(String);
|
||||
}
|
||||
|
||||
get(): V {
|
||||
get(): Value {
|
||||
// TODO: cleanup
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return this.parse(this.getRaw()) ?? this.defaultValue!;
|
||||
}
|
||||
|
||||
@action
|
||||
set(value: V, { mergeGlobals = true, replaceHistory = false } = {}): void {
|
||||
set(value: Value, { mergeGlobals = true, replaceHistory = false } = {}): void {
|
||||
const search = this.toString({ mergeGlobals, value });
|
||||
|
||||
this.history.merge({ search }, replaceHistory);
|
||||
this.dependencies.history.merge({ search }, replaceHistory);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,10 +81,10 @@ export class PageParam<V = any> {
|
||||
if (this.isMulti) {
|
||||
this.clear();
|
||||
values.forEach(value => {
|
||||
this.history.searchParams.append(this.name, value);
|
||||
this.dependencies.history.searchParams.append(this.name, value);
|
||||
});
|
||||
} else {
|
||||
this.history.searchParams.set(this.name, values[0]);
|
||||
this.dependencies.history.searchParams.set(this.name, values[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,21 +92,21 @@ export class PageParam<V = any> {
|
||||
* Get stringified raw value(s) from `document.location.search`
|
||||
*/
|
||||
getRaw(): string | string[] {
|
||||
const values: string[] = this.history.searchParams.getAll(this.name);
|
||||
const values: string[] = this.dependencies.history.searchParams.getAll(this.name);
|
||||
|
||||
return this.isMulti ? values : values[0];
|
||||
}
|
||||
|
||||
@action
|
||||
clear() {
|
||||
this.history.searchParams.delete(this.name);
|
||||
this.dependencies.history.searchParams.delete(this.name);
|
||||
}
|
||||
|
||||
toString({ mergeGlobals = true, value = this.get() } = {}): string {
|
||||
let searchParams = new URLSearchParams();
|
||||
|
||||
if (mergeGlobals) {
|
||||
searchParams = new URLSearchParams(this.history.searchParams);
|
||||
searchParams = new URLSearchParams(this.dependencies.history.searchParams);
|
||||
searchParams.delete(this.name);
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import observableHistoryInjectable from "../navigation/history/observable.injectable";
|
||||
import observableHistoryInjectable from "../navigation/observable-history.injectable";
|
||||
|
||||
const currentPathInjectable = getInjectable({
|
||||
id: "current-path",
|
||||
|
||||
@ -5,11 +5,10 @@
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import type { PageParamInit } from "../navigation";
|
||||
import { PageParam } from "../navigation";
|
||||
import observableHistoryInjectable from "../navigation/history/observable.injectable";
|
||||
import type { LensRendererExtension } from "../../extensions/lens-renderer-extension";
|
||||
import type { PageRegistration } from "../../extensions/registries";
|
||||
import { fromPairs, map, toPairs } from "lodash/fp";
|
||||
import { fromPairs, map } from "lodash/fp";
|
||||
import createPageParamInjectable from "../navigation/create-page-param.injectable";
|
||||
|
||||
interface InstantiationParameter {
|
||||
extension: LensRendererExtension;
|
||||
@ -20,18 +19,18 @@ const extensionPageParametersInjectable = getInjectable({
|
||||
id: "extension-page-parameters",
|
||||
|
||||
instantiate: (di, { registration }: InstantiationParameter) => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
const createPageParam = di.inject(createPageParamInjectable);
|
||||
|
||||
return pipeline(
|
||||
registration.params ?? {},
|
||||
toPairs,
|
||||
map(([key, value]): [string, PageParamInit] => [
|
||||
Object.entries,
|
||||
map(([key, value]): [string, PageParamInit<unknown>] => [
|
||||
key,
|
||||
typeof value === "string"
|
||||
? convertStringToPageParamInit(key, value)
|
||||
: convertPartialPageParamInitToFull(key, value as PageParamInit),
|
||||
: convertPartialPageParamInitToFull(key, value),
|
||||
]),
|
||||
map(([key, value]) => [key, new PageParam(value, observableHistory)]),
|
||||
map(([key, value]) => [key, createPageParam(value)]),
|
||||
fromPairs,
|
||||
);
|
||||
},
|
||||
@ -44,20 +43,20 @@ const extensionPageParametersInjectable = getInjectable({
|
||||
}),
|
||||
});
|
||||
|
||||
const convertPartialPageParamInitToFull = (
|
||||
const convertPartialPageParamInitToFull = <V>(
|
||||
key: string,
|
||||
value: PageParamInit,
|
||||
): PageParamInit => ({
|
||||
name: key,
|
||||
defaultValue: value.defaultValue,
|
||||
stringify: value.stringify,
|
||||
parse: value.parse,
|
||||
});
|
||||
value: PageParamInit<V>,
|
||||
): PageParamInit<V> => ({
|
||||
name: key,
|
||||
defaultValue: value.defaultValue,
|
||||
stringify: value.stringify,
|
||||
parse: value.parse,
|
||||
});
|
||||
|
||||
const convertStringToPageParamInit = (
|
||||
key: string,
|
||||
value: string,
|
||||
): PageParamInit => ({
|
||||
): PageParamInit<string> => ({
|
||||
name: key,
|
||||
defaultValue: value,
|
||||
});
|
||||
|
||||
@ -10,8 +10,6 @@ import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { isEmpty, matches } from "lodash/fp";
|
||||
import type { PageRegistration } from "../../extensions/registries";
|
||||
import observableHistoryInjectable from "../navigation/observable-history.injectable";
|
||||
import type { ObservableHistory } from "mobx-observable-history";
|
||||
import { extensionRegistratorInjectionToken } from "../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import { SiblingsInTabLayout } from "../components/layout/siblings-in-tab-layout";
|
||||
import extensionPageParametersInjectable from "./extension-page-parameters.injectable";
|
||||
@ -24,14 +22,11 @@ const extensionRouteRegistratorInjectable = getInjectable({
|
||||
id: "extension-route-registrator",
|
||||
|
||||
instantiate: (di) => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
|
||||
return (ext, extensionInstallationCount) => {
|
||||
const extension = ext as LensRendererExtension;
|
||||
const toRouteInjectable = toRouteInjectableFor(
|
||||
di,
|
||||
extension,
|
||||
observableHistory,
|
||||
extensionInstallationCount,
|
||||
);
|
||||
|
||||
@ -53,7 +48,6 @@ const toRouteInjectableFor =
|
||||
(
|
||||
di: DiContainerForInstantiate,
|
||||
extension: LensRendererExtension,
|
||||
observableHistory: ObservableHistory<unknown>,
|
||||
extensionInstallationCount: number,
|
||||
) =>
|
||||
(clusterFrame: boolean) =>
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import observableHistoryInjectable from "../navigation/observable-history.injectable";
|
||||
import { runInAction } from "mobx";
|
||||
import type { NavigateToUrl } from "../../common/front-end-routing/navigate-to-url-injection-token";
|
||||
import { navigateToUrlInjectionToken } from "../../common/front-end-routing/navigate-to-url-injection-token";
|
||||
import { broadcastMessage } from "../../common/ipc";
|
||||
import { IpcRendererNavigationEvents } from "../navigation/events";
|
||||
@ -12,24 +13,20 @@ import { IpcRendererNavigationEvents } from "../navigation/events";
|
||||
const navigateToUrlInjectable = getInjectable({
|
||||
id: "navigate-to-url",
|
||||
|
||||
instantiate: (di) => {
|
||||
instantiate: (di): NavigateToUrl => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
|
||||
return (url, options = {}) => {
|
||||
return (url, options = {}): void => {
|
||||
if (options.forceRootFrame) {
|
||||
broadcastMessage(IpcRendererNavigationEvents.NAVIGATE_IN_APP, url);
|
||||
|
||||
return;
|
||||
return void broadcastMessage(IpcRendererNavigationEvents.NAVIGATE_IN_APP, url);
|
||||
}
|
||||
|
||||
runInAction(() => {
|
||||
if (options.withoutAffectingBackButton) {
|
||||
observableHistory.replace(url);
|
||||
|
||||
return;
|
||||
} else {
|
||||
observableHistory.push(url);
|
||||
}
|
||||
|
||||
observableHistory.push(url);
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
@ -13,11 +13,7 @@ const queryParametersInjectable = getInjectable({
|
||||
instantiate: (di) => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
|
||||
return computed(() => {
|
||||
const queryString = observableHistory.location.search;
|
||||
|
||||
return parseQueryString(queryString);
|
||||
});
|
||||
return computed(() => parseQueryString(observableHistory.location.search));
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user