diff --git a/src/extensions/extension-loader/extension/extension.injectable.ts b/src/extensions/extension-loader/extension/extension.injectable.ts index dcbe9d72dc..6b9424cea4 100644 --- a/src/extensions/extension-loader/extension/extension.injectable.ts +++ b/src/extensions/extension-loader/extension/extension.injectable.ts @@ -2,7 +2,9 @@ * 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 { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { difference, find, map } from "lodash"; import { reaction, runInAction } from "mobx"; import { disposer } from "../../../common/utils/disposer"; import type { LensExtension } from "../../lens-extension"; @@ -13,6 +15,16 @@ export interface Extension { deregister: () => void; } +const idsToInjectables = (ids: string[], injectables: Injectable[]) => ids.map(id => { + const injectable = find(injectables, { id }); + + if (!injectable) { + throw new Error(`Injectable ${id} not found`); + } + + return injectable; +}); + const extensionInjectable = getInjectable({ id: "extension", @@ -34,13 +46,20 @@ const extensionInjectable = getInjectable({ // we need to update the registered injectables with a reaction every time they change reaction( () => Array.isArray(injectables) ? injectables : injectables.get(), - (currentInjectables, previousInjectables) => { - // On the second reaction remove the previously registered injectables to avoid duplicate injectables - if (previousInjectables) { - childDi.deregister(...previousInjectables); + (currentInjectables, previousInjectables = []) => { + // Register new injectables and deregister removed injectables by id + const currentIds = map(currentInjectables, "id"); + const previousIds = map(previousInjectables, "id"); + const idsToAdd = difference(currentIds, previousIds); + const idsToRemove = previousIds.filter(previousId => !currentIds.includes(previousId)); + + if (idsToRemove.length > 0) { + childDi.deregister(...idsToInjectables(idsToRemove, previousInjectables)); } - childDi.register(...currentInjectables); + if (idsToAdd.length > 0) { + childDi.register(...idsToInjectables(idsToAdd, currentInjectables)); + } }, { fireImmediately: true, }, diff --git a/src/features/tray/extension-adding-tray-items.test.tsx b/src/features/tray/extension-adding-tray-items.test.tsx index 67e0a3a210..2b640be07c 100644 --- a/src/features/tray/extension-adding-tray-items.test.tsx +++ b/src/features/tray/extension-adding-tray-items.test.tsx @@ -258,5 +258,21 @@ describe("preferences: extension adding tray items", () => { ), ).toBeNull(); }); + + it("given items are removed and one added, it's shown", async () => { + menuItems.replace([]); + menuItems.push({ + id: "some-added", + label: "some-added", + click: () => {}, + visible: computed(() => true), + }); + + expect( + builder.tray.get( + "some-added-tray-menu-item-for-extension-some-extension", + ), + ).not.toBeNull(); + }); }); });