From f87f5f245c7206509e235e7c054210d0783ec630 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Mon, 20 Jun 2022 10:19:20 +0300 Subject: [PATCH] Simplify logic for injecting many instances which can reactively change e.g. based on extension being enabled or disabled (#5662) * Reorganize stuff to prevent circular dependencies Signed-off-by: Janne Savolainen * Install injectable extension to allow injecting reactively many implementations Signed-off-by: Janne Savolainen * Switch to using computedInjectMany over filtering injectMany based on enabled extensions for simplicity Signed-off-by: Janne Savolainen * Remove await for not being needed Signed-off-by: Janne Savolainen * Sort dependencies alphabetically Signed-off-by: Janne Savolainen --- package.json | 9 +-- ...and-tab-navigation-for-extensions.test.tsx | 4 +- ...-characters-in-page-registrations.test.tsx | 2 +- .../navigate-to-extension-page.test.tsx | 2 +- .../navigating-between-routes.test.tsx | 14 ++--- ...to-extension-specific-preferences.test.tsx | 4 +- ...nu-item-originating-from-extension.test.ts | 16 +++-- ...arators-originating-from-extension.test.ts | 6 +- .../extension-loader.injectable.ts | 5 +- .../extension-loader/extension-loader.ts | 16 ++--- .../extension-registrator-injection-token.ts | 3 +- .../extension/extension.injectable.ts | 50 +++++++++++++++ src/main/getDi.ts | 4 +- src/main/getDiForUnitTesting.ts | 3 + .../tray-menu-item-injection-token.ts | 2 - .../tray-menu-item-registrator.injectable.ts | 18 ++---- .../tray-menu-items.injectable.ts | 43 ++++--------- ...-items-for-definition-groups.injectable.ts | 4 +- ...-preference-item-registrator.injectable.ts | 14 ++--- .../extension-preference-items.injectable.ts | 35 ++--------- ...-preference-item-registrator.injectable.ts | 14 ++--- .../telemetry-preference-items.injectable.ts | 38 ++---------- .../components/+preferences/telemetry.tsx | 4 +- ...on-sidebar-item-registrator.injectable.tsx | 14 ++--- .../layout/sidebar-items.injectable.ts | 33 ++-------- .../test-utils/get-application-builder.tsx | 61 ++++++++++--------- src/renderer/getDi.tsx | 3 + src/renderer/getDiForUnitTesting.tsx | 3 + src/renderer/routes/all-routes.injectable.ts | 40 ------------ .../current-path-parameters.injectable.ts | 23 ------- .../current-route-component.injectable.ts | 7 ++- ...extension-route-registrator.injectable.tsx | 12 ++-- .../routes/route-is-active.injectable.ts | 13 +++- src/renderer/routes/routes.injectable.ts | 8 ++- yarn.lock | 51 +++++++++------- 35 files changed, 245 insertions(+), 333 deletions(-) create mode 100644 src/extensions/extension-loader/extension/extension.injectable.ts delete mode 100644 src/renderer/routes/all-routes.injectable.ts delete mode 100644 src/renderer/routes/current-path-parameters.injectable.ts diff --git a/package.json b/package.json index 7c038a4f13..04543ae784 100644 --- a/package.json +++ b/package.json @@ -208,10 +208,11 @@ "@hapi/subtext": "^7.0.3", "@kubernetes/client-node": "^0.16.3", "@material-ui/styles": "^4.11.5", - "@ogre-tools/fp": "7.1.0", - "@ogre-tools/injectable": "7.1.0", - "@ogre-tools/injectable-extension-for-auto-registration": "7.1.0", - "@ogre-tools/injectable-react": "7.1.0", + "@ogre-tools/fp": "8.0.0", + "@ogre-tools/injectable": "8.0.0", + "@ogre-tools/injectable-extension-for-auto-registration": "8.0.0", + "@ogre-tools/injectable-extension-for-mobx": "8.0.0", + "@ogre-tools/injectable-react": "8.0.0", "@sentry/electron": "^3.0.7", "@sentry/integrations": "^6.19.3", "@side/jest-runtime": "^1.0.0", diff --git a/src/behaviours/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx b/src/behaviours/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx index 8ef472f20d..380a64a4e1 100644 --- a/src/behaviours/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx +++ b/src/behaviours/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx @@ -42,11 +42,11 @@ describe("cluster - sidebar and tab navigation for extensions", () => { }); describe("given extension with cluster pages and cluster page menus", () => { - beforeEach(async () => { + beforeEach(() => { const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder); const testExtension = getRendererExtensionFake(extensionStubWithSidebarItems); - await applicationBuilder.extensions.renderer.enable(testExtension); + applicationBuilder.extensions.renderer.enable(testExtension); }); describe("given no state for expanded sidebar items exists, and navigated to child sidebar item, when rendered", () => { diff --git a/src/behaviours/extension-special-characters-in-page-registrations.test.tsx b/src/behaviours/extension-special-characters-in-page-registrations.test.tsx index 20c29d82b5..f30e300582 100644 --- a/src/behaviours/extension-special-characters-in-page-registrations.test.tsx +++ b/src/behaviours/extension-special-characters-in-page-registrations.test.tsx @@ -23,7 +23,7 @@ describe("extension special characters in page registrations", () => { extensionWithPagesHavingSpecialCharacters, ); - await applicationBuilder.extensions.renderer.enable(testExtension); + applicationBuilder.extensions.renderer.enable(testExtension); rendered = await applicationBuilder.render(); }); diff --git a/src/behaviours/navigate-to-extension-page.test.tsx b/src/behaviours/navigate-to-extension-page.test.tsx index 255a8951ba..f78a8493d8 100644 --- a/src/behaviours/navigate-to-extension-page.test.tsx +++ b/src/behaviours/navigate-to-extension-page.test.tsx @@ -27,7 +27,7 @@ describe("navigate to extension page", () => { extensionWithPagesHavingParameters, ); - await applicationBuilder.extensions.renderer.enable(testExtension); + applicationBuilder.extensions.renderer.enable(testExtension); rendered = await applicationBuilder.render(); diff --git a/src/behaviours/navigating-between-routes.test.tsx b/src/behaviours/navigating-between-routes.test.tsx index ab0240b2a4..4758fb4074 100644 --- a/src/behaviours/navigating-between-routes.test.tsx +++ b/src/behaviours/navigating-between-routes.test.tsx @@ -16,8 +16,8 @@ import { getApplicationBuilder } from "../renderer/components/test-utils/get-app import currentRouteInjectable from "../renderer/routes/current-route.injectable"; import currentPathInjectable from "../renderer/routes/current-path.injectable"; import queryParametersInjectable from "../renderer/routes/query-parameters.injectable"; -import currentPathParametersInjectable from "../renderer/routes/current-path-parameters.injectable"; import { navigateToRouteInjectionToken } from "../common/front-end-routing/navigate-to-route-injection-token"; +import routePathParametersInjectable from "../renderer/routes/route-path-parameters.injectable"; describe("navigating between routes", () => { let rendererDi: DiContainer; @@ -73,7 +73,7 @@ describe("navigating between routes", () => { }); it("does not have path parameters", () => { - const pathParameters = rendererDi.inject(currentPathParametersInjectable); + const pathParameters = rendererDi.inject(routePathParametersInjectable, route); expect(pathParameters.get()).toEqual({}); }); @@ -101,7 +101,6 @@ describe("navigating between routes", () => { describe("given route with optional path parameters", () => { beforeEach(async () => { - applicationBuilder.beforeApplicationStart(({ rendererDi }) => { rendererDi.register(routeWithOptionalPathParametersInjectable); rendererDi.register(routeWithOptionalPathParametersComponentInjectable); @@ -146,7 +145,7 @@ describe("navigating between routes", () => { }); it("knows path parameters", () => { - const pathParameters = rendererDi.inject(currentPathParametersInjectable); + const pathParameters = rendererDi.inject(routePathParametersInjectable, route); expect(pathParameters.get()).toEqual({ someParameter: "some-value", @@ -179,7 +178,7 @@ describe("navigating between routes", () => { }); it("knows path parameters", () => { - const pathParameters = rendererDi.inject(currentPathParametersInjectable); + const pathParameters = rendererDi.inject(routePathParametersInjectable, route); expect(pathParameters.get()).toEqual({ someParameter: undefined, @@ -227,10 +226,11 @@ const routeWithOptionalPathParametersComponentInjectable = getInjectable({ id: "some-route-component", instantiate: (di) => { - const pathParameters = di.inject(currentPathParametersInjectable); + const route = di.inject(routeWithOptionalPathParametersInjectable); + const pathParameters = di.inject(routePathParametersInjectable, route); return { - route: di.inject(routeWithOptionalPathParametersInjectable), + route, Component: observer(() => (
{JSON.stringify(pathParameters.get(), null, 2)}
diff --git a/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx b/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx index de97d67bcf..5eb9b2f384 100644 --- a/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx +++ b/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx @@ -44,11 +44,11 @@ describe("preferences - navigation to extension specific preferences", () => { }); describe("when extension with specific preferences is enabled", () => { - beforeEach(async () => { + beforeEach(() => { const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder); const testExtension = getRendererExtensionFake(extensionStubWithExtensionSpecificPreferenceItems); - await applicationBuilder.extensions.renderer.enable(testExtension); + applicationBuilder.extensions.renderer.enable(testExtension); }); it("renders", () => { diff --git a/src/behaviours/tray/clicking-tray-menu-item-originating-from-extension.test.ts b/src/behaviours/tray/clicking-tray-menu-item-originating-from-extension.test.ts index 249673ef83..006760f902 100644 --- a/src/behaviours/tray/clicking-tray-menu-item-originating-from-extension.test.ts +++ b/src/behaviours/tray/clicking-tray-menu-item-originating-from-extension.test.ts @@ -29,7 +29,7 @@ describe("clicking tray menu item originating from extension", () => { let someExtension: SomeTestExtension; let clickMock: jest.Mock; - beforeEach(async () => { + beforeEach(() => { clickMock = jest.fn(); someExtension = new SomeTestExtension({ @@ -37,12 +37,12 @@ describe("clicking tray menu item originating from extension", () => { trayMenus: [{ label: "some-label", click: clickMock }], }); - await applicationBuilder.extensions.main.enable(someExtension); + applicationBuilder.extensions.main.enable(someExtension); }); it("when item is clicked, triggers the click handler", () => { applicationBuilder.tray.click( - "some-label-tray-menu-item-for-extension-some-extension-id-instance-1", + "some-label-tray-menu-item-for-extension-some-extension-id", ); expect(clickMock).toHaveBeenCalled(); @@ -55,7 +55,7 @@ describe("clicking tray menu item originating from extension", () => { }); applicationBuilder.tray.click( - "some-label-tray-menu-item-for-extension-some-extension-id-instance-1", + "some-label-tray-menu-item-for-extension-some-extension-id", ); }); @@ -72,7 +72,7 @@ describe("clicking tray menu item originating from extension", () => { clickMock.mockImplementation(() => Promise.reject("some-rejection")); applicationBuilder.tray.click( - "some-label-tray-menu-item-for-extension-some-extension-id-instance-1", + "some-label-tray-menu-item-for-extension-some-extension-id", ); }); @@ -90,11 +90,9 @@ describe("clicking tray menu item originating from extension", () => { }); it("does not have the tray menu item from extension", () => { - applicationBuilder.extensions.main.disable(someExtension); - expect( applicationBuilder.tray.get( - "some-label-tray-menu-item-for-extension-some-extension-id-instance-1", + "some-label-tray-menu-item-for-extension-some-extension-id", ), ).toBeNull(); }); @@ -105,7 +103,7 @@ describe("clicking tray menu item originating from extension", () => { expect( applicationBuilder.tray.get( - "some-label-tray-menu-item-for-extension-some-extension-id-instance-2", + "some-label-tray-menu-item-for-extension-some-extension-id", ), ).not.toBeNull(); }); diff --git a/src/behaviours/tray/multiple-separators-originating-from-extension.test.ts b/src/behaviours/tray/multiple-separators-originating-from-extension.test.ts index d2e44c24e2..96b9f18198 100644 --- a/src/behaviours/tray/multiple-separators-originating-from-extension.test.ts +++ b/src/behaviours/tray/multiple-separators-originating-from-extension.test.ts @@ -28,9 +28,9 @@ describe("multiple separators originating from extension", () => { trayMenus: [{ type: "separator" }, { type: "separator" } ], }); - return expect( - applicationBuilder.extensions.main.enable(someExtension), - ).resolves.toBeUndefined(); + expect(() => { + applicationBuilder.extensions.main.enable(someExtension); + }).not.toThrow(); }); }); diff --git a/src/extensions/extension-loader/extension-loader.injectable.ts b/src/extensions/extension-loader/extension-loader.injectable.ts index 1686b69f96..a1c592ffdd 100644 --- a/src/extensions/extension-loader/extension-loader.injectable.ts +++ b/src/extensions/extension-loader/extension-loader.injectable.ts @@ -5,10 +5,11 @@ import { getInjectable } from "@ogre-tools/injectable"; import { ExtensionLoader } from "./extension-loader"; import updateExtensionsStateInjectable from "./update-extensions-state/update-extensions-state.injectable"; -import { extensionRegistratorInjectionToken } from "./extension-registrator-injection-token"; import extensionInstallationCounterInjectable from "./extension-installation-counter.injectable"; import { createExtensionInstanceInjectionToken } from "./create-extension-instance.token"; import extensionInstancesInjectable from "./extension-instances.injectable"; +import type { LensExtension } from "../lens-extension"; +import extensionInjectable from "./extension/extension.injectable"; const extensionLoaderInjectable = getInjectable({ id: "extension-loader", @@ -16,9 +17,9 @@ const extensionLoaderInjectable = getInjectable({ instantiate: (di) => new ExtensionLoader({ updateExtensionsState: di.inject(updateExtensionsStateInjectable), createExtensionInstance: di.inject(createExtensionInstanceInjectionToken), - extensionRegistrators: di.injectMany(extensionRegistratorInjectionToken), extensionInstallationCounter: di.inject(extensionInstallationCounterInjectable), extensionInstances: di.inject(extensionInstancesInjectable), + getExtension: (instance: LensExtension) => di.inject(extensionInjectable, instance), }), }); diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts index 3fc89b8547..9ed691002d 100644 --- a/src/extensions/extension-loader/extension-loader.ts +++ b/src/extensions/extension-loader/extension-loader.ts @@ -23,15 +23,16 @@ import { requestExtensionLoaderInitialState } from "../../renderer/ipc"; import assert from "assert"; import { EventEmitter } from "../../common/event-emitter"; import type { CreateExtensionInstance } from "./create-extension-instance.token"; +import type { Extension } from "./extension/extension.injectable"; const logModule = "[EXTENSIONS-LOADER]"; interface Dependencies { updateExtensionsState: (extensionsState: Record) => void; createExtensionInstance: CreateExtensionInstance; - readonly extensionRegistrators: ((extension: LensExtension, extensionInstallationCount: number) => void)[]; readonly extensionInstallationCounter: Map; readonly extensionInstances: ObservableMap; + getExtension: (instance: LensExtension) => Extension; } export interface ExtensionLoading { @@ -169,6 +170,11 @@ export class ExtensionLoader { try { instance.disable(); + + const registeredExtension = this.dependencies.getExtension(instance); + + registeredExtension.deregister(); + this.onRemoveExtensionId.emit(instance.id); this.dependencies.extensionInstances.delete(lensExtensionId); this.nonInstancesByName.delete(instance.name); @@ -354,13 +360,9 @@ export class ExtensionLoader { ); extensions.forEach(({ instance }) => { - const installationCount = (this.dependencies.extensionInstallationCounter.get(instance.sanitizedExtensionId) ?? 0) + 1; + const getExtension = this.dependencies.getExtension(instance); - this.dependencies.extensionInstallationCounter.set(instance.sanitizedExtensionId, installationCount); - - this.dependencies.extensionRegistrators.forEach((register) => - register(instance, installationCount), - ); + getExtension.register(); }); // Return ExtensionLoading[] diff --git a/src/extensions/extension-loader/extension-registrator-injection-token.ts b/src/extensions/extension-loader/extension-registrator-injection-token.ts index 9fe35f5f39..b5527cd00d 100644 --- a/src/extensions/extension-loader/extension-registrator-injection-token.ts +++ b/src/extensions/extension-loader/extension-registrator-injection-token.ts @@ -2,11 +2,12 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import type { Injectable } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable"; import type { LensExtension } from "../lens-extension"; export const extensionRegistratorInjectionToken = getInjectionToken< - (extension: LensExtension, extensionInstallationCount: number) => void + (extension: LensExtension) => Injectable[] >({ id: "extension-registrator-token", }); diff --git a/src/extensions/extension-loader/extension/extension.injectable.ts b/src/extensions/extension-loader/extension/extension.injectable.ts new file mode 100644 index 0000000000..1473c8db12 --- /dev/null +++ b/src/extensions/extension-loader/extension/extension.injectable.ts @@ -0,0 +1,50 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import type { LensExtension } from "../../lens-extension"; +import { extensionRegistratorInjectionToken } from "../extension-registrator-injection-token"; + +export interface Extension { + register: () => void; + deregister: () => void; +} + +const extensionInjectable = getInjectable({ + id: "extension", + + instantiate: (parentDi, instance: LensExtension): Extension => { + const extensionInjectable = getInjectable({ + id: `extension-${instance.sanitizedExtensionId}`, + + instantiate: (childDi) => { + const extensionRegistrators = childDi.injectMany(extensionRegistratorInjectionToken); + + return { + register: () => { + const injectables = extensionRegistrators.flatMap((getInjectablesOfExtension) => + getInjectablesOfExtension(instance), + ); + + childDi.register(...injectables); + }, + + deregister: () => { + parentDi.deregister(extensionInjectable); + }, + }; + }, + }); + + parentDi.register(extensionInjectable); + + return parentDi.inject(extensionInjectable); + }, + + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, extension: LensExtension) => extension, + }), +}); + +export default extensionInjectable; diff --git a/src/main/getDi.ts b/src/main/getDi.ts index 9d9c60bbe8..0d55e7c8d7 100644 --- a/src/main/getDi.ts +++ b/src/main/getDi.ts @@ -2,14 +2,16 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ - import { createContainer } from "@ogre-tools/injectable"; import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; +import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; export const getDi = () => { const di = createContainer(); + registerMobX(di); + autoRegister({ di, requireContexts: [ diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts index a022bf15ba..45fedce511 100644 --- a/src/main/getDiForUnitTesting.ts +++ b/src/main/getDiForUnitTesting.ts @@ -98,6 +98,7 @@ import listHelmReleasesInjectable from "./helm/helm-service/list-helm-releases.i import rollbackHelmReleaseInjectable from "./helm/helm-service/rollback-helm-release.injectable"; import updateHelmReleaseInjectable from "./helm/helm-service/update-helm-release.injectable"; import waitUntilBundledExtensionsAreLoadedInjectable from "./start-main-application/lens-window/application-window/wait-until-bundled-extensions-are-loaded.injectable"; +import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) { const { @@ -106,6 +107,8 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) const di = createContainer(); + registerMobX(di); + setLegacyGlobalDiForExtensionApi(di, Environments.main); for (const filePath of getInjectableFilePaths()) { diff --git a/src/main/tray/tray-menu-item/tray-menu-item-injection-token.ts b/src/main/tray/tray-menu-item/tray-menu-item-injection-token.ts index f8e9d7c6cc..3e0a0b243a 100644 --- a/src/main/tray/tray-menu-item/tray-menu-item-injection-token.ts +++ b/src/main/tray/tray-menu-item/tray-menu-item-injection-token.ts @@ -4,7 +4,6 @@ */ import { getInjectionToken } from "@ogre-tools/injectable"; import type { IComputedValue } from "mobx"; -import type { LensMainExtension } from "../../../extensions/lens-main-extension"; export interface TrayMenuItem { id: string; @@ -17,7 +16,6 @@ export interface TrayMenuItem { click?: () => Promise | void; tooltip?: string; separator?: boolean; - extension?: LensMainExtension; } export const trayMenuItemInjectionToken = getInjectionToken({ diff --git a/src/main/tray/tray-menu-item/tray-menu-item-registrator.injectable.ts b/src/main/tray/tray-menu-item/tray-menu-item-registrator.injectable.ts index fa9914b4b6..152c4ea8d1 100644 --- a/src/main/tray/tray-menu-item/tray-menu-item-registrator.injectable.ts +++ b/src/main/tray/tray-menu-item/tray-menu-item-registrator.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { pipeline } from "@ogre-tools/fp"; -import { flatMap, kebabCase } from "lodash/fp"; +import { kebabCase } from "lodash/fp"; import type { Injectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; @@ -20,17 +20,13 @@ import getRandomIdInjectable from "../../../common/utils/get-random-id.injectabl const trayMenuItemRegistratorInjectable = getInjectable({ id: "tray-menu-item-registrator", - instantiate: (di) => (extension, installationCounter) => { + instantiate: (di) => (extension) => { const mainExtension = extension as LensMainExtension; const withErrorLoggingFor = di.inject(withErrorLoggingInjectable); const getRandomId = di.inject(getRandomIdInjectable); - pipeline( - mainExtension.trayMenus, - - flatMap(toItemInjectablesFor(mainExtension, installationCounter, withErrorLoggingFor, getRandomId)), - - (injectables) => di.register(...injectables), + return mainExtension.trayMenus.flatMap( + toItemInjectablesFor(mainExtension, withErrorLoggingFor, getRandomId), ); }, @@ -39,10 +35,10 @@ const trayMenuItemRegistratorInjectable = getInjectable({ export default trayMenuItemRegistratorInjectable; -const toItemInjectablesFor = (extension: LensMainExtension, installationCounter: number, withErrorLoggingFor: WithErrorLoggingFor, getRandomId: () => string) => { +const toItemInjectablesFor = (extension: LensMainExtension, withErrorLoggingFor: WithErrorLoggingFor, getRandomId: () => string) => { const _toItemInjectables = (parentId: string | null) => (registration: TrayMenuRegistration): Injectable[] => { const trayItemId = registration.id || kebabCase(registration.label || getRandomId()); - const id = `${trayItemId}-tray-menu-item-for-extension-${extension.sanitizedExtensionId}-instance-${installationCounter}`; + const id = `${trayItemId}-tray-menu-item-for-extension-${extension.sanitizedExtensionId}`; const parentInjectable = getInjectable({ id, @@ -76,8 +72,6 @@ const toItemInjectablesFor = (extension: LensMainExtension, installationCounter: enabled: computed(() => registration.enabled ?? true), visible: computed(() => true), - - extension, }), injectionToken: trayMenuItemInjectionToken, diff --git a/src/main/tray/tray-menu-item/tray-menu-items.injectable.ts b/src/main/tray/tray-menu-item/tray-menu-items.injectable.ts index c29482007d..ed7aefb6b5 100644 --- a/src/main/tray/tray-menu-item/tray-menu-items.injectable.ts +++ b/src/main/tray/tray-menu-item/tray-menu-items.injectable.ts @@ -3,47 +3,30 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; import { computed } from "mobx"; - - -import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable"; -import type { TrayMenuItem } from "./tray-menu-item-injection-token"; import { trayMenuItemInjectionToken } from "./tray-menu-item-injection-token"; import { pipeline } from "@ogre-tools/fp"; -import { filter, overSome, sortBy } from "lodash/fp"; -import type { LensMainExtension } from "../../../extensions/lens-main-extension"; +import { filter, sortBy } from "lodash/fp"; const trayMenuItemsInjectable = getInjectable({ id: "tray-menu-items", instantiate: (di) => { - const extensions = di.inject(mainExtensionsInjectable); + const computedInjectMany = di.inject( + computedInjectManyInjectable, + ); - return computed(() => { - const enabledExtensions = extensions.get(); + const reactiveMenuItems = computedInjectMany(trayMenuItemInjectionToken); - return pipeline( - di.injectMany(trayMenuItemInjectionToken), - - filter((item) => - overSome([ - isNonExtensionItem, - isEnabledExtensionItemFor(enabledExtensions), - ])(item), - ), - - filter(item => item.visible.get()), - items => sortBy("orderNumber", items), - ); - }); + return computed(() => + pipeline( + reactiveMenuItems.get(), + filter((item) => item.visible.get()), + (items) => sortBy("orderNumber", items), + ), + ); }, }); -const isNonExtensionItem = (item: TrayMenuItem) => !item.extension; - -const isEnabledExtensionItemFor = - (enabledExtensions: LensMainExtension[]) => (item: TrayMenuItem) => - !!enabledExtensions.find((extension) => extension === item.extension); - - export default trayMenuItemsInjectable; diff --git a/src/renderer/components/+custom-resources/sidebar-items-for-definition-groups.injectable.ts b/src/renderer/components/+custom-resources/sidebar-items-for-definition-groups.injectable.ts index f05e5df43a..895df08dc4 100644 --- a/src/renderer/components/+custom-resources/sidebar-items-for-definition-groups.injectable.ts +++ b/src/renderer/components/+custom-resources/sidebar-items-for-definition-groups.injectable.ts @@ -8,10 +8,10 @@ import crdListRouteInjectable from "../../../common/front-end-routing/routes/clu import customResourceDefinitionsInjectable from "./custom-resources.injectable"; import { groupBy, matches, noop, some, toPairs } from "lodash/fp"; import customResourcesRouteInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable"; -import currentPathParametersInjectable from "../../routes/current-path-parameters.injectable"; import type { SidebarItemRegistration } from "../layout/sidebar-items.injectable"; import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; import navigateToCustomResourcesInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/custom-resources/navigate-to-custom-resources.injectable"; +import routePathParametersInjectable from "../../routes/route-path-parameters.injectable"; const sidebarItemsForDefinitionGroupsInjectable = getInjectable({ id: "sidebar-items-for-definition-groups", @@ -24,7 +24,7 @@ const sidebarItemsForDefinitionGroupsInjectable = getInjectable({ const crdRoute = di.inject(customResourcesRouteInjectable); const crdRouteIsActive = di.inject(routeIsActiveInjectable, crdRoute); const crdListRoute = di.inject(crdListRouteInjectable); - const pathParameters = di.inject(currentPathParametersInjectable); + const pathParameters = di.inject(routePathParametersInjectable, crdRoute); const navigateToCustomResources = di.inject(navigateToCustomResourcesInjectable); return computed((): SidebarItemRegistration[] => { diff --git a/src/renderer/components/+preferences/extension-preference-item-registrator.injectable.ts b/src/renderer/components/+preferences/extension-preference-item-registrator.injectable.ts index d7daebce1f..1129699bbd 100644 --- a/src/renderer/components/+preferences/extension-preference-item-registrator.injectable.ts +++ b/src/renderer/components/+preferences/extension-preference-item-registrator.injectable.ts @@ -13,10 +13,11 @@ const extensionPreferenceItemRegistratorInjectable = getInjectable({ id: "extension-preference-item-registrator", instantiate: - (di) => - (ext, extensionInstallationCount) => { + () => + (ext) => { const extension = ext as LensRendererExtension; - const injectables = pipeline( + + return pipeline( extension.appPreferences, filter( @@ -27,13 +28,12 @@ const extensionPreferenceItemRegistratorInjectable = getInjectable({ const id = `extension-preferences-item-${registration.id}-for-extension-${extension.sanitizedExtensionId}`; return getInjectable({ - id: `${id}-for-instance-${extensionInstallationCount}`, + id, injectionToken: extensionPreferenceItemInjectionToken, instantiate: () => ({ id: registration.id || id, title: registration.title, - extension, components: { Hint: registration.components.Hint, @@ -43,10 +43,6 @@ const extensionPreferenceItemRegistratorInjectable = getInjectable({ }); }), ); - - di.register(...injectables); - - return; }, injectionToken: extensionRegistratorInjectionToken, diff --git a/src/renderer/components/+preferences/extension-preference-items.injectable.ts b/src/renderer/components/+preferences/extension-preference-items.injectable.ts index f4d08304d2..5143ee60c9 100644 --- a/src/renderer/components/+preferences/extension-preference-items.injectable.ts +++ b/src/renderer/components/+preferences/extension-preference-items.injectable.ts @@ -2,19 +2,11 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { pipeline } from "@ogre-tools/fp"; import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; -import { filter, overSome } from "lodash/fp"; -import { computed } from "mobx"; -import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable"; -import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension"; +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration"; -interface ExtensionPreferenceItem extends RegisteredAppPreference { - extension: LensRendererExtension; -} - -export const extensionPreferenceItemInjectionToken = getInjectionToken({ +export const extensionPreferenceItemInjectionToken = getInjectionToken({ id: "extension-preference-item-injection-token", }); @@ -22,29 +14,10 @@ const extensionsPreferenceItemsInjectable = getInjectable({ id: "extension-preference-items", instantiate: (di) => { - const extensions = di.inject(rendererExtensionsInjectable); + const computedInjectMany = di.inject(computedInjectManyInjectable); - return computed(() => { - const enabledExtensions = extensions.get(); - - return pipeline( - di.injectMany(extensionPreferenceItemInjectionToken), - - filter((item) => - overSome([ - isNonExtensionItem, - isEnabledExtensionItemFor(enabledExtensions), - ])(item), - ), - ); - }); + return computedInjectMany(extensionPreferenceItemInjectionToken); }, }); -const isNonExtensionItem = (item: ExtensionPreferenceItem) => !item.extension; - -const isEnabledExtensionItemFor = - (enabledExtensions: LensRendererExtension[]) => (item: ExtensionPreferenceItem) => - !!enabledExtensions.find((extension) => extension === item.extension); - export default extensionsPreferenceItemsInjectable; diff --git a/src/renderer/components/+preferences/extension-telemetry-preference-item-registrator.injectable.ts b/src/renderer/components/+preferences/extension-telemetry-preference-item-registrator.injectable.ts index ff6fa2ecbc..f6a3900675 100644 --- a/src/renderer/components/+preferences/extension-telemetry-preference-item-registrator.injectable.ts +++ b/src/renderer/components/+preferences/extension-telemetry-preference-item-registrator.injectable.ts @@ -13,10 +13,11 @@ const extensionTelemetryPreferenceItemRegistratorInjectable = getInjectable({ id: "extension-telemetry-preference-item-registrator", instantiate: - (di) => - (ext, extensionInstallationCount) => { + () => + (ext) => { const extension = ext as LensRendererExtension; - const injectables = pipeline( + + return pipeline( extension.appPreferences, filter( @@ -27,13 +28,12 @@ const extensionTelemetryPreferenceItemRegistratorInjectable = getInjectable({ const id = `telemetry-preferences-item-${registration.id}-for-extension-${extension.sanitizedExtensionId}`; return getInjectable({ - id: `${id}-for-instance-${extensionInstallationCount}`, + id, injectionToken: telemetryPreferenceItemInjectionToken, instantiate: () => ({ id: registration.id || id, title: registration.title, - extension, components: { Hint: registration.components.Hint, @@ -43,10 +43,6 @@ const extensionTelemetryPreferenceItemRegistratorInjectable = getInjectable({ }); }), ); - - di.register(...injectables); - - return; }, injectionToken: extensionRegistratorInjectionToken, diff --git a/src/renderer/components/+preferences/telemetry-preference-items.injectable.ts b/src/renderer/components/+preferences/telemetry-preference-items.injectable.ts index 421a45b83f..b0b920e7f5 100644 --- a/src/renderer/components/+preferences/telemetry-preference-items.injectable.ts +++ b/src/renderer/components/+preferences/telemetry-preference-items.injectable.ts @@ -3,48 +3,22 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; -import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable"; -import { pipeline } from "@ogre-tools/fp"; -import { filter, overSome } from "lodash/fp"; -import { computed } from "mobx"; -import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension"; +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration"; -export interface ExtensionTelemetryPreferenceRegistration extends AppPreferenceRegistration { - extension?: LensRendererExtension; -} - -export const telemetryPreferenceItemInjectionToken = getInjectionToken({ +export const telemetryPreferenceItemInjectionToken = getInjectionToken({ id: "telemetry-preference-item-injection-token", }); const telemetryPreferenceItemsInjectable = getInjectable({ id: "telemetry-preference-items", + instantiate: (di) => { - const extensions = di.inject(rendererExtensionsInjectable); + const computedInjectMany = di.inject(computedInjectManyInjectable); - return computed(() => { - const enabledExtensions = extensions.get(); - - return pipeline( - di.injectMany(telemetryPreferenceItemInjectionToken), - filter((item) => - overSome([ - isNonExtensionItem, - isEnabledExtensionItemFor(enabledExtensions), - ])(item), - ), - ); - }); + return computedInjectMany(telemetryPreferenceItemInjectionToken); }, }); - -const isNonExtensionItem = (item: ExtensionTelemetryPreferenceRegistration) => !item.extension; - -const isEnabledExtensionItemFor = - (enabledExtensions: LensRendererExtension[]) => (item: ExtensionTelemetryPreferenceRegistration) => - !!enabledExtensions.find((extension) => extension === item.extension); - - export default telemetryPreferenceItemsInjectable; + diff --git a/src/renderer/components/+preferences/telemetry.tsx b/src/renderer/components/+preferences/telemetry.tsx index 825679649e..26809ee2f2 100644 --- a/src/renderer/components/+preferences/telemetry.tsx +++ b/src/renderer/components/+preferences/telemetry.tsx @@ -11,13 +11,13 @@ import { ExtensionSettings } from "./extension-settings"; import type { IComputedValue } from "mobx"; import { withInjectables } from "@ogre-tools/injectable-react"; import { Preferences } from "./preferences"; -import type { ExtensionTelemetryPreferenceRegistration } from "./telemetry-preference-items.injectable"; import telemetryPreferenceItemsInjectable from "./telemetry-preference-items.injectable"; import sentryDnsUrlInjectable from "./sentry-dns-url.injectable"; import userStoreInjectable from "../../../common/user-store/user-store.injectable"; +import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration"; interface Dependencies { - telemetryPreferenceItems: IComputedValue; + telemetryPreferenceItems: IComputedValue; sentryDnsUrl: string; userStore: UserStore; } diff --git a/src/renderer/components/layout/extension-sidebar-item-registrator.injectable.tsx b/src/renderer/components/layout/extension-sidebar-item-registrator.injectable.tsx index 397e087c33..1a1a8515d9 100644 --- a/src/renderer/components/layout/extension-sidebar-item-registrator.injectable.tsx +++ b/src/renderer/components/layout/extension-sidebar-item-registrator.injectable.tsx @@ -18,17 +18,16 @@ import type { LensRendererExtension } from "../../../extensions/lens-renderer-ex const extensionSidebarItemRegistratorInjectable = getInjectable({ id: "extension-sidebar-item-registrator", - instantiate: (di) => (ext, extensionInstallationCount) => { + instantiate: (di) => (ext) => { const extension = ext as LensRendererExtension; + const navigateToRoute = di.inject(navigateToRouteInjectionToken); + const routes = di.inject(routesInjectable); const sidebarItemsForExtensionInjectable = getInjectable({ - id: `sidebar-items-for-extension-${extension.sanitizedExtensionId}-instance-${extensionInstallationCount}`, + id: `sidebar-items-for-extension-${extension.sanitizedExtensionId}`, injectionToken: sidebarItemsInjectionToken, instantiate: (di) => { - const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const routes = di.inject(routesInjectable); - return computed(() => { const extensionRoutes = routes.get().filter(matches({ extension })); @@ -44,7 +43,6 @@ const extensionSidebarItemRegistratorInjectable = getInjectable({ const res: SidebarItemRegistration = { id: `${extension.sanitizedExtensionId}-${registration.id}`, - extension, orderNumber: 9999, parentId: registration.parentId @@ -73,7 +71,9 @@ const extensionSidebarItemRegistratorInjectable = getInjectable({ }, }); - di.register(sidebarItemsForExtensionInjectable); + return [ + sidebarItemsForExtensionInjectable, + ]; }, injectionToken: extensionRegistratorInjectionToken, diff --git a/src/renderer/components/layout/sidebar-items.injectable.ts b/src/renderer/components/layout/sidebar-items.injectable.ts index 96dcc38b8f..e5f067b64d 100644 --- a/src/renderer/components/layout/sidebar-items.injectable.ts +++ b/src/renderer/components/layout/sidebar-items.injectable.ts @@ -6,7 +6,7 @@ import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; import type { IComputedValue } from "mobx"; import { computed } from "mobx"; import { pipeline } from "@ogre-tools/fp"; - +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; import { filter, flatMap, @@ -15,11 +15,8 @@ import { isEmpty, map, orderBy, - overSome, some, } from "lodash/fp"; -import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable"; -import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension"; export interface SidebarItemRegistration { id: string; @@ -30,7 +27,6 @@ export interface SidebarItemRegistration { isActive?: IComputedValue; isVisible?: IComputedValue; orderNumber: number; - extension?: LensRendererExtension; } export const sidebarItemsInjectionToken = getInjectionToken< @@ -47,25 +43,14 @@ const sidebarItemsInjectable = getInjectable({ id: "sidebar-items", instantiate: (di) => { - const extensions = di.inject(rendererExtensionsInjectable); + const computedInjectMany = di.inject(computedInjectManyInjectable); + + const sidebarItemRegistrations = computedInjectMany(sidebarItemsInjectionToken); return computed((): HierarchicalSidebarItem[] => { - const enabledExtensions = extensions.get(); - - const sidebarItemRegistrations = di.injectMany( - sidebarItemsInjectionToken, - ); - const registrations = pipeline( - sidebarItemRegistrations, + sidebarItemRegistrations.get(), flatMap(dereference), - - filter( - overSome([ - isNonExtensionSidebarItem, - isEnabledExtensionSidebarItemFor(enabledExtensions), - ]), - ), ); const getSidebarItemsHierarchy = (registrations: SidebarItemRegistration[]) => { @@ -114,14 +99,6 @@ const sidebarItemsInjectable = getInjectable({ }, }); -const isNonExtensionSidebarItem = (sidebarItem: SidebarItemRegistration) => - !sidebarItem.extension; - -const isEnabledExtensionSidebarItemFor = - (enabledExtensions: LensRendererExtension[]) => - (sidebarItem: SidebarItemRegistration) => - !!enabledExtensions.find((x) => x === sidebarItem.extension); - const dereference = (items: IComputedValue) => items.get(); diff --git a/src/renderer/components/test-utils/get-application-builder.tsx b/src/renderer/components/test-utils/get-application-builder.tsx index 9a1b3ea4a4..32a4bba41f 100644 --- a/src/renderer/components/test-utils/get-application-builder.tsx +++ b/src/renderer/components/test-utils/get-application-builder.tsx @@ -5,7 +5,6 @@ import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension"; import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable"; import currentlyInClusterFrameInjectable from "../../routes/currently-in-cluster-frame.injectable"; -import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token"; import type { IObservableArray, ObservableSet } from "mobx"; import { computed, observable, runInAction } from "mobx"; import { renderFor } from "./renderFor"; @@ -60,9 +59,11 @@ import type { LensMainExtension } from "../../../extensions/lens-main-extension" import trayMenuItemsInjectable from "../../../main/tray/tray-menu-item/tray-menu-items.injectable"; import type { LensExtension } from "../../../extensions/lens-extension"; +import extensionInjectable from "../../../extensions/extension-loader/extension/extension.injectable"; + type Callback = (dis: DiContainers) => void | Promise; -type EnableExtensions = (...extensions: T[]) => Promise; +type EnableExtensions = (...extensions: T[]) => void; type DisableExtensions = (...extensions: T[]) => void; export interface ApplicationBuilder { @@ -222,33 +223,26 @@ export const getApplicationBuilder = () => { extensionState: T, di: DiContainer, ) => { - let index = 0; + const getExtension = (extension: LensExtension) => + di.inject(extensionInjectable, extension); - return async (...extensions: LensExtension[]) => { - const extensionRegistrators = di.injectMany( - extensionRegistratorInjectionToken, - ); + return (...extensionInstances: LensExtension[]) => { + const addAndEnableExtensions = () => { + extensionInstances.forEach(instance => { + const extension = getExtension(instance); - const addAndEnableExtensions = async () => { - index++; - - const registratorPromises = extensions.flatMap((extension) => - extensionRegistrators.map((registrator) => - registrator(extension, index), - ), - ); - - await Promise.all(registratorPromises); + extension.register(); + }); runInAction(() => { - extensions.forEach((extension) => { + extensionInstances.forEach((extension) => { extensionState.add(extension); }); }); }; if (rendered) { - await addAndEnableExtensions(); + addAndEnableExtensions(); } else { builder.beforeRender(addAndEnableExtensions); } @@ -407,12 +401,12 @@ export const getApplicationBuilder = () => { extensions: { renderer: { enable: enableExtensionsFor(rendererExtensionsState, rendererDi), - disable: disableExtensionsFor(rendererExtensionsState), + disable: disableExtensionsFor(rendererExtensionsState, rendererDi), }, main: { enable: enableExtensionsFor(mainExtensionsState, mainDi), - disable: disableExtensionsFor(mainExtensionsState), + disable: disableExtensionsFor(mainExtensionsState, mainDi), }, }, @@ -538,13 +532,22 @@ function toFlatChildren(parentId: string | null | undefined): ToFlatChildren { ]; } -const disableExtensionsFor = - (extensionState: T) => +const disableExtensionsFor = ( + extensionState: T, + di: DiContainer, +) => { + const getExtension = (instance: LensExtension) => + di.inject(extensionInjectable, instance); - (...extensions: LensExtension[]) => { - extensions.forEach((extension) => { - runInAction(() => { - extensionState.delete(extension); - }); + return (...extensionInstances: LensExtension[]) => { + extensionInstances.forEach((instance) => { + const extension = getExtension(instance); + + extension.deregister(); + + runInAction(() => { + extensionState.delete(extension); }); - }; + }); + }; +}; diff --git a/src/renderer/getDi.tsx b/src/renderer/getDi.tsx index 540ba3ae3d..094af6292f 100644 --- a/src/renderer/getDi.tsx +++ b/src/renderer/getDi.tsx @@ -5,11 +5,14 @@ import { createContainer } from "@ogre-tools/injectable"; import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; +import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; export const getDi = () => { const di = createContainer(); + registerMobX(di); + autoRegister({ di, requireContexts: [ diff --git a/src/renderer/getDiForUnitTesting.tsx b/src/renderer/getDiForUnitTesting.tsx index 6deb96df64..78ecf47438 100644 --- a/src/renderer/getDiForUnitTesting.tsx +++ b/src/renderer/getDiForUnitTesting.tsx @@ -51,6 +51,7 @@ import getFilePathsInjectable from "./components/+preferences/kubernetes/helm-ch import callForPublicHelmRepositoriesInjectable from "./components/+preferences/kubernetes/helm-charts/adding-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable"; import platformInjectable from "../common/vars/platform.injectable"; import startTopbarStateSyncInjectable from "./components/layout/top-bar/start-state-sync.injectable"; +import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => { const { @@ -59,6 +60,8 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) const di = createContainer(); + registerMobX(di); + setLegacyGlobalDiForExtensionApi(di, Environments.renderer); for (const filePath of getInjectableFilePaths()) { diff --git a/src/renderer/routes/all-routes.injectable.ts b/src/renderer/routes/all-routes.injectable.ts deleted file mode 100644 index 4ba0eb4d0e..0000000000 --- a/src/renderer/routes/all-routes.injectable.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * 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 { overSome } from "lodash/fp"; -import { computed } from "mobx"; -import rendererExtensionsInjectable from "../../extensions/renderer-extensions.injectable"; -import type { LensRendererExtension } from "../../extensions/lens-renderer-extension"; -import type { Route } from "../../common/front-end-routing/front-end-route-injection-token"; -import { frontEndRouteInjectionToken } from "../../common/front-end-routing/front-end-route-injection-token"; - -const allRoutesInjectable = getInjectable({ - id: "all-routes", - - instantiate: (di) => { - const extensions = di.inject(rendererExtensionsInjectable); - - return computed(() => { - const enabledExtensions = extensions.get(); - - return di - .injectMany(frontEndRouteInjectionToken) - .filter((route) => - overSome([ - isNonExtensionRoute, - isEnabledExtensionRouteFor(enabledExtensions), - ])(route), - ); - }); - }, -}); - -const isNonExtensionRoute = (route: Route) => !route.extension; - -const isEnabledExtensionRouteFor = - (enabledExtensions: LensRendererExtension[]) => (route: Route) => - !!enabledExtensions.find((x) => x === route.extension); - -export default allRoutesInjectable; diff --git a/src/renderer/routes/current-path-parameters.injectable.ts b/src/renderer/routes/current-path-parameters.injectable.ts deleted file mode 100644 index 8aceb17036..0000000000 --- a/src/renderer/routes/current-path-parameters.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 { computed } from "mobx"; -import matchingRouteInjectable from "./matching-route.injectable"; - -const currentPathParametersInjectable = getInjectable({ - id: "current-path-parameters", - - instantiate: (di) => { - const matchingRoute = di.inject(matchingRouteInjectable); - - return computed((): Record => { - const match = matchingRoute.get(); - - return match ? match.pathParameters: {}; - }); - }, -}); - -export default currentPathParametersInjectable; diff --git a/src/renderer/routes/current-route-component.injectable.ts b/src/renderer/routes/current-route-component.injectable.ts index afc309ed5a..56b047c37b 100644 --- a/src/renderer/routes/current-route-component.injectable.ts +++ b/src/renderer/routes/current-route-component.injectable.ts @@ -3,6 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; import { matches } from "lodash/fp"; import { computed } from "mobx"; import currentRouteInjectable from "./current-route.injectable"; @@ -13,6 +14,8 @@ const currentRouteComponentInjectable = getInjectable({ instantiate: (di) => { const route = di.inject(currentRouteInjectable); + const computedInjectMany = di.inject(computedInjectManyInjectable); + const routeComponents = computedInjectMany(routeSpecificComponentInjectionToken); return computed(() => { const currentRoute = route.get(); @@ -21,8 +24,8 @@ const currentRouteComponentInjectable = getInjectable({ return undefined; } - const routeSpecificComponent = di - .injectMany(routeSpecificComponentInjectionToken) + const routeSpecificComponent = routeComponents + .get() .find(matches({ route: currentRoute })); return routeSpecificComponent?.Component; diff --git a/src/renderer/routes/extension-route-registrator.injectable.tsx b/src/renderer/routes/extension-route-registrator.injectable.tsx index de2c4a2838..54eecb3fe1 100644 --- a/src/renderer/routes/extension-route-registrator.injectable.tsx +++ b/src/renderer/routes/extension-route-registrator.injectable.tsx @@ -22,20 +22,17 @@ const extensionRouteRegistratorInjectable = getInjectable({ id: "extension-route-registrator", instantiate: (di) => { - return (ext, extensionInstallationCount) => { + return (ext) => { const extension = ext as LensRendererExtension; const toRouteInjectable = toRouteInjectableFor( di, extension, - extensionInstallationCount, ); - const routeInjectables = [ + return [ ...extension.globalPages.map(toRouteInjectable(false)), ...extension.clusterPages.map(toRouteInjectable(true)), ].flat(); - - di.register(...routeInjectables); }; }, @@ -48,12 +45,11 @@ const toRouteInjectableFor = ( di: DiContainerForInjection, extension: LensRendererExtension, - extensionInstallationCount: number, ) => (clusterFrame: boolean) => (registration: PageRegistration) => { const routeInjectable = getInjectable({ - id: `route-${registration.id}-for-extension-${extension.sanitizedExtensionId}-installation-${extensionInstallationCount}`, + id: `route-${registration.id}-for-extension-${extension.sanitizedExtensionId}`, instantiate: () => ({ path: getExtensionRoutePath(extension, registration.id), @@ -95,7 +91,7 @@ const toRouteInjectableFor = }; const routeSpecificComponentInjectable = getInjectable({ - id: `route-${registration.id}-component-for-extension-${extension.sanitizedExtensionId}-installation-${extensionInstallationCount}`, + id: `route-${registration.id}-component-for-extension-${extension.sanitizedExtensionId}`, instantiate: (di) => ({ route: di.inject(routeInjectable), diff --git a/src/renderer/routes/route-is-active.injectable.ts b/src/renderer/routes/route-is-active.injectable.ts index b5218c0a6a..4234b393ad 100644 --- a/src/renderer/routes/route-is-active.injectable.ts +++ b/src/renderer/routes/route-is-active.injectable.ts @@ -4,16 +4,23 @@ */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import currentRouteInjectable from "./current-route.injectable"; import type { Route } from "../../common/front-end-routing/front-end-route-injection-token"; +import currentPathInjectable from "./current-path.injectable"; +import { matchPath } from "react-router-dom"; const routeIsActiveInjectable = getInjectable({ id: "route-is-active", instantiate: (di, route: Route) => { - const currentRoute = di.inject(currentRouteInjectable); + const currentPath = di.inject(currentPathInjectable); - return computed(() => currentRoute.get() === route); + return computed( + () => + !!matchPath(currentPath.get(), { + path: route.path, + exact: true, + }), + ); }, lifecycle: lifecycleEnum.keyedSingleton({ diff --git a/src/renderer/routes/routes.injectable.ts b/src/renderer/routes/routes.injectable.ts index 2230bad0b2..8f9b6f36eb 100644 --- a/src/renderer/routes/routes.injectable.ts +++ b/src/renderer/routes/routes.injectable.ts @@ -5,18 +5,20 @@ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; import currentlyInClusterFrameInjectable from "./currently-in-cluster-frame.injectable"; -import allRoutesInjectable from "./all-routes.injectable"; import { matches } from "lodash/fp"; +import { frontEndRouteInjectionToken } from "../../common/front-end-routing/front-end-route-injection-token"; +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; const routesInjectable = getInjectable({ id: "routes", instantiate: (di) => { - const allRoutes = di.inject(allRoutesInjectable); + const computedInjectMany = di.inject(computedInjectManyInjectable); const currentlyInClusterFrame = di.inject(currentlyInClusterFrameInjectable); + const routes = computedInjectMany(frontEndRouteInjectionToken); return computed(() => - allRoutes + routes .get() .filter(matches({ clusterFrame: currentlyInClusterFrame })) .filter((route) => route.isEnabled.get()), diff --git a/yarn.lock b/yarn.lock index f7a79194e8..7e4429e9a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1189,37 +1189,46 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@ogre-tools/fp@7.1.0", "@ogre-tools/fp@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@ogre-tools/fp/-/fp-7.1.0.tgz#63bdd23e82d4d0f3cfffbf575017e4dac893f590" - integrity sha512-lhXreCXr1mlyvNf+YXvqBcIvL2ZKswYJXK1hx2ieeQLmgyvDs6xGio6h+6bHf1kJhvM55PfBATAJ3DyyiUfwiA== +"@ogre-tools/fp@8.0.0", "@ogre-tools/fp@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@ogre-tools/fp/-/fp-8.0.0.tgz#dd2319a96ce3d2edd3ee2bc2acca07a94a77bf3b" + integrity sha512-8OpGUbG3avGtx6ASz3XNnK/KCyPW25RPp8oYzzU0zihKU5D4QKHy9qNkQ3npurzeg4d1k4BsgkeX+805nqtZOA== dependencies: lodash "^4.17.21" -"@ogre-tools/injectable-extension-for-auto-registration@7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-auto-registration/-/injectable-extension-for-auto-registration-7.1.0.tgz#eb4aabee04fff1c4e353e4b5e805639e5e8923d5" - integrity sha512-xEnXF2iAxYOCj46HymDIjO85zsunONmMD8CThks46pyUD7kTOaf7c9tClEUFdXQTmCAfU3UFxcAxDjyJynoSTg== +"@ogre-tools/injectable-extension-for-auto-registration@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-auto-registration/-/injectable-extension-for-auto-registration-8.0.0.tgz#3a443f1f1c9b564baa78cca6a3c81ac4102660b0" + integrity sha512-DX1bxn8mDwek+W/SaI5WmDHmkY3B3njs3X4pOvqRtiMis3GaWHzeCZeK3q3Iv5cd14FTW8AsfKtujPmLklNf/A== dependencies: - "@ogre-tools/fp" "^7.1.0" - "@ogre-tools/injectable" "^7.1.0" + "@ogre-tools/fp" "^8.0.0" + "@ogre-tools/injectable" "^8.0.0" lodash "^4.17.21" -"@ogre-tools/injectable-react@7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-7.1.0.tgz#afb60951e7e22b59921eb5ce5d04e3be93fe119e" - integrity sha512-JDTZR+1IhFUAPjlzP7IwMRysFBVjpQke4Y/7Ddy7MhOF+WtfFx8pOnuWFWKQRlU2FS4PsMpCG09Jgy09j+Qa8Q== +"@ogre-tools/injectable-extension-for-mobx@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-mobx/-/injectable-extension-for-mobx-8.0.0.tgz#80ff506011e078050dd8dcb72660d17181db6d34" + integrity sha512-m8gU3cEFHl9IMZLcvvoS7hVxS6p6nG3jdf6fY6MUZE0u9hx4bZuUdWYoylGQizy0FyRFQ2/m5xhTH2VdtXqx8w== dependencies: - "@ogre-tools/fp" "^7.1.0" - "@ogre-tools/injectable" "^7.1.0" + "@ogre-tools/fp" "^8.0.0" + "@ogre-tools/injectable" "^8.0.0" lodash "^4.17.21" -"@ogre-tools/injectable@7.1.0", "@ogre-tools/injectable@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-7.1.0.tgz#4137d630720245e9bc8635a0f62888878f711da5" - integrity sha512-EN82trTh80TuSE0Gr1Gk4oC/Mj3Od//kWH2r+0Z3Np554e4zOgW6lyEOQlxaGIm77taGSvHq9B9cO7lI9hJonQ== +"@ogre-tools/injectable-react@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-8.0.0.tgz#b2d8db4bb697ba2822d71ec73e6cabee30f70f34" + integrity sha512-rZa38fm3UzGa/09qC765Za8xJiSPOYHJGsob8UOt2JQIt/BecTNXPMHexfxy9W+DIdAer+YruUgedChdk9nvdQ== dependencies: - "@ogre-tools/fp" "^7.1.0" + "@ogre-tools/fp" "^8.0.0" + "@ogre-tools/injectable" "^8.0.0" + lodash "^4.17.21" + +"@ogre-tools/injectable@8.0.0", "@ogre-tools/injectable@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-8.0.0.tgz#ea4f98bd2466149add94d4f6a9beb7be03729da7" + integrity sha512-59p+8uGqwVQ5IpGpgfn3RA+wXzn1tjnPdFWO3GLEgjyp5dWBaMKufCpCFRvvb9sP6B68qo19aLfy/uSm4AXduw== + dependencies: + "@ogre-tools/fp" "^8.0.0" lodash "^4.17.21" "@panva/asn1.js@^1.0.0":