diff --git a/src/extensions/lens-main-extension.ts b/src/extensions/lens-main-extension.ts index accc87989c..6c901db4ed 100644 --- a/src/extensions/lens-main-extension.ts +++ b/src/extensions/lens-main-extension.ts @@ -6,7 +6,7 @@ import { LensExtension, lensExtensionDependencies } from "./lens-extension"; import type { CatalogEntity } from "../common/catalog"; import type { IObservableArray } from "mobx"; -import type { MenuRegistration } from "../main/menu/menu-registration"; +import type { MenuRegistration } from "../features/application-menu/main/menu-registration"; import type { TrayMenuRegistration } from "../main/tray/tray-menu-registration"; import type { ShellEnvModifier } from "../main/shell-session/shell-env-modifier/shell-env-modifier-registration"; import type { LensMainExtensionDependencies } from "./lens-extension-set-dependencies"; diff --git a/src/features/application-menu/__snapshots__/application-menu.test.ts.snap b/src/features/application-menu/__snapshots__/application-menu.test.ts.snap new file mode 100644 index 0000000000..ee688702a1 --- /dev/null +++ b/src/features/application-menu/__snapshots__/application-menu.test.ts.snap @@ -0,0 +1,399 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`application-menu given enough time passes populates application menu janne 1`] = ` +Array [ + Array [ + Array [ + Object { + "id": "root", + "label": "some-product-name", + "submenu": Array [ + Object { + "id": "primary", + "label": "some-product-name", + "parentId": null, + }, + Object { + "click": [Function], + "id": "about", + "label": "About some-product-name", + "parentId": "primary", + }, + Object { + "type": "separator", + }, + Object { + "accelerator": "CmdOrCtrl+,", + "click": [Function], + "id": "preferences", + "label": "Preferences", + }, + Object { + "accelerator": "CmdOrCtrl+Shift+E", + "click": [Function], + "id": "extensions", + "label": "Extensions", + }, + Object { + "type": "separator", + }, + Object { + "role": "services", + }, + Object { + "type": "separator", + }, + Object { + "role": "hide", + }, + Object { + "role": "hideOthers", + }, + Object { + "role": "unhide", + }, + Object { + "type": "separator", + }, + Object { + "accelerator": "Cmd+Q", + "click": [Function], + "id": "quit", + "label": "Quit", + }, + ], + }, + Object { + "id": "file", + "label": "File", + "submenu": Array [ + Object { + "accelerator": "CmdOrCtrl+Shift+A", + "click": [Function], + "id": "add-cluster", + "label": "Add Cluster", + }, + Object { + "type": "separator", + }, + Object { + "accelerator": "Shift+Cmd+W", + "label": "Close Window", + "role": "close", + }, + ], + }, + Object { + "id": "edit", + "label": "Edit", + "submenu": Array [ + Object { + "role": "undo", + }, + Object { + "role": "redo", + }, + Object { + "type": "separator", + }, + Object { + "role": "cut", + }, + Object { + "role": "copy", + }, + Object { + "role": "paste", + }, + Object { + "role": "delete", + }, + Object { + "type": "separator", + }, + Object { + "role": "selectAll", + }, + ], + }, + Object { + "id": "view", + "label": "View", + "submenu": Array [ + Object { + "accelerator": "Shift+CmdOrCtrl+C", + "click": [Function], + "id": "catalog", + "label": "Catalog", + }, + Object { + "accelerator": "Shift+CmdOrCtrl+P", + "click": [Function], + "id": "command-palette", + "label": "Command Palette...", + }, + Object { + "type": "separator", + }, + Object { + "accelerator": "CmdOrCtrl+[", + "click": [Function], + "id": "go-back", + "label": "Back", + }, + Object { + "accelerator": "CmdOrCtrl+]", + "click": [Function], + "id": "go-forward", + "label": "Forward", + }, + Object { + "accelerator": "CmdOrCtrl+R", + "click": [Function], + "id": "reload", + "label": "Reload", + }, + Object { + "role": "toggleDevTools", + }, + Object { + "type": "separator", + }, + Object { + "role": "resetZoom", + }, + Object { + "role": "zoomIn", + }, + Object { + "role": "zoomOut", + }, + Object { + "type": "separator", + }, + Object { + "role": "togglefullscreen", + }, + ], + }, + Object { + "id": "help", + "role": "help", + "submenu": Array [ + Object { + "click": [Function], + "id": "welcome", + "label": "Welcome", + }, + Object { + "click": [Function], + "id": "documentation", + "label": "Documentation", + }, + Object { + "click": [Function], + "id": "support", + "label": "Support", + }, + ], + }, + ], + ], +] +`; + +exports[`application-menu given enough time passes populates application menu lol 1`] = ` +Array [ + Array [ + Array [ + Object { + "id": "root", + "label": "some-product-name", + "submenu": Array [ + Object { + "click": [Function], + "id": "about", + "label": "About some-product-name", + }, + Object { + "type": "separator", + }, + Object { + "accelerator": "CmdOrCtrl+,", + "click": [Function], + "id": "preferences", + "label": "Preferences", + }, + Object { + "accelerator": "CmdOrCtrl+Shift+E", + "click": [Function], + "id": "extensions", + "label": "Extensions", + }, + Object { + "type": "separator", + }, + Object { + "role": "services", + }, + Object { + "type": "separator", + }, + Object { + "role": "hide", + }, + Object { + "role": "hideOthers", + }, + Object { + "role": "unhide", + }, + Object { + "type": "separator", + }, + Object { + "accelerator": "Cmd+Q", + "click": [Function], + "id": "quit", + "label": "Quit", + }, + ], + }, + Object { + "id": "file", + "label": "File", + "submenu": Array [ + Object { + "accelerator": "CmdOrCtrl+Shift+A", + "click": [Function], + "id": "add-cluster", + "label": "Add Cluster", + }, + Object { + "type": "separator", + }, + Object { + "accelerator": "Shift+Cmd+W", + "label": "Close Window", + "role": "close", + }, + ], + }, + Object { + "id": "edit", + "label": "Edit", + "submenu": Array [ + Object { + "role": "undo", + }, + Object { + "role": "redo", + }, + Object { + "type": "separator", + }, + Object { + "role": "cut", + }, + Object { + "role": "copy", + }, + Object { + "role": "paste", + }, + Object { + "role": "delete", + }, + Object { + "type": "separator", + }, + Object { + "role": "selectAll", + }, + ], + }, + Object { + "id": "view", + "label": "View", + "submenu": Array [ + Object { + "accelerator": "Shift+CmdOrCtrl+C", + "click": [Function], + "id": "catalog", + "label": "Catalog", + }, + Object { + "accelerator": "Shift+CmdOrCtrl+P", + "click": [Function], + "id": "command-palette", + "label": "Command Palette...", + }, + Object { + "type": "separator", + }, + Object { + "accelerator": "CmdOrCtrl+[", + "click": [Function], + "id": "go-back", + "label": "Back", + }, + Object { + "accelerator": "CmdOrCtrl+]", + "click": [Function], + "id": "go-forward", + "label": "Forward", + }, + Object { + "accelerator": "CmdOrCtrl+R", + "click": [Function], + "id": "reload", + "label": "Reload", + }, + Object { + "role": "toggleDevTools", + }, + Object { + "type": "separator", + }, + Object { + "role": "resetZoom", + }, + Object { + "role": "zoomIn", + }, + Object { + "role": "zoomOut", + }, + Object { + "type": "separator", + }, + Object { + "role": "togglefullscreen", + }, + ], + }, + Object { + "id": "help", + "role": "help", + "submenu": Array [ + Object { + "click": [Function], + "id": "welcome", + "label": "Welcome", + }, + Object { + "click": [Function], + "id": "documentation", + "label": "Documentation", + }, + Object { + "click": [Function], + "id": "support", + "label": "Support", + }, + ], + }, + ], + ], +] +`; diff --git a/src/features/application-menu/application-menu.test.ts b/src/features/application-menu/application-menu.test.ts new file mode 100644 index 0000000000..d37a2ae451 --- /dev/null +++ b/src/features/application-menu/application-menu.test.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { DiContainer } from "@ogre-tools/injectable"; +import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import populateApplicationMenuInjectable from "./main/populate-application-menu.injectable"; +import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; + +describe("application-menu", () => { + let builder: ApplicationBuilder; + let mainDi: DiContainer; + let populateApplicationMenuMock: jest.Mock; + + beforeEach(async () => { + useFakeTime(); + + populateApplicationMenuMock = jest.fn(); + + builder = getApplicationBuilder(); + + builder.beforeApplicationStart((mainDi) => { + mainDi.override(populateApplicationMenuInjectable, () => populateApplicationMenuMock); + }); + + // await builder.render(); + await builder.startHidden(); + + mainDi = builder.mainDi; + }); + + it("when insufficient time passes, does not populate menu items yet", () => { + advanceFakeTime(99); + + expect(populateApplicationMenuMock).not.toHaveBeenCalled(); + }); + + describe("given enough time passes", () => { + beforeEach(() => { + advanceFakeTime(100); + }); + + it("populates application menu", () => { + expect(populateApplicationMenuMock).toHaveBeenCalledWith(expect.any(Array)); + }); + + it("populates application menu lol", () => { + expect(populateApplicationMenuMock.mock.calls).toMatchSnapshot(); + }); + }); +}); diff --git a/src/main/menu/application-menu-items.injectable.ts b/src/features/application-menu/main/application-menu-items.injectable.ts similarity index 73% rename from src/main/menu/application-menu-items.injectable.ts rename to src/features/application-menu/main/application-menu-items.injectable.ts index 8bd7566a36..ea157e6150 100644 --- a/src/main/menu/application-menu-items.injectable.ts +++ b/src/features/application-menu/main/application-menu-items.injectable.ts @@ -3,28 +3,31 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { docsUrl, supportUrl } from "../../common/vars"; -import { broadcastMessage } from "../../common/ipc"; +import { docsUrl, supportUrl } from "../../../common/vars"; +import { broadcastMessage } from "../../../common/ipc"; import type { MenuItemConstructorOptions } from "electron"; import { webContents } from "electron"; -import loggerInjectable from "../../common/logger.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import electronMenuItemsInjectable from "./electron-menu-items.injectable"; -import updatingIsEnabledInjectable from "../../features/application-update/main/updating-is-enabled/updating-is-enabled.injectable"; -import navigateToPreferencesInjectable from "../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable"; -import navigateToExtensionsInjectable from "../../common/front-end-routing/routes/extensions/navigate-to-extensions.injectable"; -import navigateToCatalogInjectable from "../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; -import navigateToWelcomeInjectable from "../../common/front-end-routing/routes/welcome/navigate-to-welcome.injectable"; -import navigateToAddClusterInjectable from "../../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable"; -import stopServicesAndExitAppInjectable from "../stop-services-and-exit-app.injectable"; -import isMacInjectable from "../../common/vars/is-mac.injectable"; +import updatingIsEnabledInjectable from "../../application-update/main/updating-is-enabled/updating-is-enabled.injectable"; +import navigateToPreferencesInjectable from "../../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable"; +import navigateToExtensionsInjectable from "../../../common/front-end-routing/routes/extensions/navigate-to-extensions.injectable"; +import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; +import navigateToWelcomeInjectable from "../../../common/front-end-routing/routes/welcome/navigate-to-welcome.injectable"; +import navigateToAddClusterInjectable from "../../../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable"; +import stopServicesAndExitAppInjectable from "../../../main/stop-services-and-exit-app.injectable"; +import isMacInjectable from "../../../common/vars/is-mac.injectable"; import { computed } from "mobx"; -import showAboutInjectable from "./show-about.injectable"; -import reloadCurrentApplicationWindowInjectable from "../start-main-application/lens-window/reload-current-application-window.injectable"; -import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable"; -import processCheckingForUpdatesInjectable from "../../features/application-update/main/process-checking-for-updates.injectable"; -import openLinkInBrowserInjectable from "../../common/utils/open-link-in-browser.injectable"; -import appNameInjectable from "../../common/vars/app-name.injectable"; -import productNameInjectable from "../../common/vars/product-name.injectable"; +import showAboutInjectable from "./menu-items/primary-for-mac/show-about-application/show-about.injectable"; +import reloadCurrentApplicationWindowInjectable from "../../../main/start-main-application/lens-window/reload-current-application-window.injectable"; +import showApplicationWindowInjectable from "../../../main/start-main-application/lens-window/show-application-window.injectable"; +import processCheckingForUpdatesInjectable from "../../application-update/main/process-checking-for-updates.injectable"; +import openLinkInBrowserInjectable from "../../../common/utils/open-link-in-browser.injectable"; +import appNameInjectable from "../../../common/vars/app-name.injectable"; +import productNameInjectable from "../../../common/vars/product-name.injectable"; + + +import applicationMenuItemInjectionToken from "./menu-items/application-menu-item-injection-token"; function ignoreIf(check: boolean, menuItems: MenuItemOpts[]) { return check ? [] : menuItems; @@ -46,57 +49,35 @@ const applicationMenuItemsInjectable = getInjectable({ const electronMenuItems = di.inject(electronMenuItemsInjectable); const showAbout = di.inject(showAboutInjectable); const showApplicationWindow = di.inject(showApplicationWindowInjectable); - const reloadApplicationWindow = di.inject(reloadCurrentApplicationWindowInjectable); + + const reloadApplicationWindow = di.inject( + reloadCurrentApplicationWindowInjectable, + ); + const navigateToPreferences = di.inject(navigateToPreferencesInjectable); const navigateToExtensions = di.inject(navigateToExtensionsInjectable); const navigateToCatalog = di.inject(navigateToCatalogInjectable); const navigateToWelcome = di.inject(navigateToWelcomeInjectable); const navigateToAddCluster = di.inject(navigateToAddClusterInjectable); const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable); - const processCheckingForUpdates = di.inject(processCheckingForUpdatesInjectable); + + const processCheckingForUpdates = di.inject( + processCheckingForUpdatesInjectable, + ); + const openLinkInBrowser = di.inject(openLinkInBrowserInjectable); logger.info(`[MENU]: autoUpdateEnabled=${updatingIsEnabled}`); + const menuItems = di + .injectMany(applicationMenuItemInjectionToken) + .filter(x => x.isShown !== false); + return computed((): MenuItemOpts[] => { const macAppMenu: MenuItemOpts = { label: appName, id: "root", submenu: [ - { - label: `About ${productName}`, - id: "about", - click() { - showAbout(); - }, - }, - ...ignoreIf(!updatingIsEnabled, [ - { - id: "check-for-updates", - label: "Check for updates", - click() { - processCheckingForUpdates("application-menu").then(() => showApplicationWindow()); - }, - }, - ]), - { type: "separator" }, - { - label: "Preferences", - accelerator: "CmdOrCtrl+,", - id: "preferences", - click() { - navigateToPreferences(); - }, - }, - { - label: "Extensions", - accelerator: "CmdOrCtrl+Shift+E", - id: "extensions", - click() { - navigateToExtensions(); - }, - }, - { type: "separator" }, { role: "services" }, { type: "separator" }, { role: "hide" }, @@ -113,6 +94,7 @@ const applicationMenuItemsInjectable = getInjectable({ }, ], }; + const fileMenu: MenuItemOpts = { label: "File", id: "file", @@ -325,9 +307,10 @@ const applicationMenuItemsInjectable = getInjectable({ appMenu.delete("mac"); } - return [...appMenu.values()]; + return [...menuItems, ...appMenu.values()]; }); }, }); export default applicationMenuItemsInjectable; + diff --git a/src/features/application-menu/main/application-menu-reactivity.injectable.ts b/src/features/application-menu/main/application-menu-reactivity.injectable.ts new file mode 100644 index 0000000000..f1f52d89e6 --- /dev/null +++ b/src/features/application-menu/main/application-menu-reactivity.injectable.ts @@ -0,0 +1,26 @@ +/** + * 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 { autorun } from "mobx"; +import applicationMenuItemsInjectable from "./application-menu-items.injectable"; +import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable"; +import populateApplicationMenuInjectable from "./populate-application-menu.injectable"; + +const applicationMenuReactivityInjectable = getInjectable({ + id: "application-menu-reactivity", + + instantiate: (di) => { + const applicationMenuItems = di.inject(applicationMenuItemsInjectable); + const populateApplicationMenu = di.inject(populateApplicationMenuInjectable); + + return getStartableStoppable("application-menu-reactivity", () => + autorun(() => populateApplicationMenu(applicationMenuItems.get()), { + delay: 100, + }), + ); + }, +}); + +export default applicationMenuReactivityInjectable; diff --git a/src/main/menu/electron-menu-items.injectable.ts b/src/features/application-menu/main/electron-menu-items.injectable.ts similarity index 86% rename from src/main/menu/electron-menu-items.injectable.ts rename to src/features/application-menu/main/electron-menu-items.injectable.ts index ff0206b734..024c80be3e 100644 --- a/src/main/menu/electron-menu-items.injectable.ts +++ b/src/features/application-menu/main/electron-menu-items.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import mainExtensionsInjectable from "../../extensions/main-extensions.injectable"; +import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable"; const electronMenuItemsInjectable = getInjectable({ id: "electron-menu-items", diff --git a/src/main/menu/electron-menu-items.test.ts b/src/features/application-menu/main/electron-menu-items.test.ts similarity index 93% rename from src/main/menu/electron-menu-items.test.ts rename to src/features/application-menu/main/electron-menu-items.test.ts index cc47994d82..eb6641d6cd 100644 --- a/src/main/menu/electron-menu-items.test.ts +++ b/src/features/application-menu/main/electron-menu-items.test.ts @@ -3,13 +3,13 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import type { DiContainer } from "@ogre-tools/injectable"; -import { LensMainExtension } from "../../extensions/lens-main-extension"; +import { LensMainExtension } from "../../../extensions/lens-main-extension"; import electronMenuItemsInjectable from "./electron-menu-items.injectable"; import type { IComputedValue } from "mobx"; import { computed, ObservableMap, runInAction } from "mobx"; import type { MenuRegistration } from "./menu-registration"; -import { getDiForUnitTesting } from "../getDiForUnitTesting"; -import mainExtensionsInjectable from "../../extensions/main-extensions.injectable"; +import { getDiForUnitTesting } from "../../../main/getDiForUnitTesting"; +import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable"; describe("electron-menu-items", () => { let di: DiContainer; diff --git a/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts b/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts new file mode 100644 index 0000000000..116c0b5d58 --- /dev/null +++ b/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectionToken } from "@ogre-tools/injectable"; + +interface Shared { + parentId: string | null; + orderNumber: number; + isShown?: boolean; +} + +export interface ApplicationMenuItem extends Shared { + id: string; + label: string; + click?: () => void; + accelerator?: string; +} + +export interface Separator extends Shared { + type: "separator"; +} + +const applicationMenuItemInjectionToken = getInjectionToken({ + id: "application-menu-item-injection-token", +}); + +export default applicationMenuItemInjectionToken; diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/check-for-updates/check-for-updates-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/check-for-updates/check-for-updates-menu-item.injectable.ts new file mode 100644 index 0000000000..0a86605ad9 --- /dev/null +++ b/src/features/application-menu/main/menu-items/primary-for-mac/check-for-updates/check-for-updates-menu-item.injectable.ts @@ -0,0 +1,43 @@ +/** + * 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 applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import processCheckingForUpdatesInjectable from "../../../../../application-update/main/process-checking-for-updates.injectable"; +import showApplicationWindowInjectable from "../../../../../../main/start-main-application/lens-window/show-application-window.injectable"; +import updatingIsEnabledInjectable from "../../../../../application-update/main/updating-is-enabled/updating-is-enabled.injectable"; + +const checkForUpdatesMenuItemInjectable = getInjectable({ + id: "check-for-updates-menu-item", + + instantiate: (di) => { + const processCheckingForUpdates = di.inject( + processCheckingForUpdatesInjectable, + ); + + const showApplicationWindow = di.inject(showApplicationWindowInjectable); + + const updatingIsEnabled = di.inject(updatingIsEnabledInjectable); + + return { + parentId: "primary-for-mac", + id: "check-for-updates", + orderNumber: 20, + label: "Check for updates", + isShown: updatingIsEnabled, + + click: () => { + // Todo: implement using async/await + processCheckingForUpdates("application-menu").then(() => + showApplicationWindow(), + ); + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default checkForUpdatesMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-extensions/navigate-to-extensions-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-extensions/navigate-to-extensions-menu-item.injectable.ts new file mode 100644 index 0000000000..4908b3c216 --- /dev/null +++ b/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-extensions/navigate-to-extensions-menu-item.injectable.ts @@ -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 applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import navigateToExtensionsInjectable from "../../../../../../common/front-end-routing/routes/extensions/navigate-to-extensions.injectable"; + +const navigateToExtensionsMenuItem = getInjectable({ + id: "navigate-to-extensions-menu-item", + + instantiate: (di) => { + const navigateToExtensions = di.inject(navigateToExtensionsInjectable); + + return { + parentId: "primary-for-mac", + id: "navigate-to-extensions", + orderNumber: 50, + label: "Extensions", + accelerator: "CmdOrCtrl+Shift+E", + + click: () => { + navigateToExtensions(); + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default navigateToExtensionsMenuItem; diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-preferences/navigate-to-preferences-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-preferences/navigate-to-preferences-menu-item.injectable.ts new file mode 100644 index 0000000000..098d831d54 --- /dev/null +++ b/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-preferences/navigate-to-preferences-menu-item.injectable.ts @@ -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 applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import navigateToPreferencesInjectable from "../../../../../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable"; + +const navigateToPreferencesMenuItem = getInjectable({ + id: "navigate-to-preferences-menu-item", + + instantiate: (di) => { + const navigateToPreferences = di.inject(navigateToPreferencesInjectable); + + return { + parentId: "primary-for-mac", + id: "navigate-to-preferences", + orderNumber: 40, + label: "Preferences", + accelerator: "CmdOrCtrl+,", + + click: () => { + navigateToPreferences(); + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default navigateToPreferencesMenuItem; diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/primary-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/primary-menu-item.injectable.ts new file mode 100644 index 0000000000..71798e3393 --- /dev/null +++ b/src/features/application-menu/main/menu-items/primary-for-mac/primary-menu-item.injectable.ts @@ -0,0 +1,29 @@ +/** + * 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 applicationMenuItemInjectionToken from "../application-menu-item-injection-token"; +import appNameInjectable from "../../../../../common/vars/app-name.injectable"; +import isMacInjectable from "../../../../../common/vars/is-mac.injectable"; + +const primaryMenuItemInjectable = getInjectable({ + id: "primary-application-menu-item", + + instantiate: (di) => { + const appName = di.inject(appNameInjectable); + const isMac = di.inject(isMacInjectable); + + return { + parentId: null, + id: "primary-for-mac", + orderNumber: 10, + label: appName, + isShown: isMac, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default primaryMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/separator-after-check-for-updates/separator-after-check-for-updates.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/separator-after-check-for-updates/separator-after-check-for-updates.injectable.ts new file mode 100644 index 0000000000..3a8319f7a3 --- /dev/null +++ b/src/features/application-menu/main/menu-items/primary-for-mac/separator-after-check-for-updates/separator-after-check-for-updates.injectable.ts @@ -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 applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; + +const separatorAfterCheckForUpdatesInjectable = getInjectable({ + id: "separator-after-check-for-updates", + + instantiate: () => ({ + id: "separator-after-check-for-updates", + parentId: "primary-for-mac", + type: "separator" as const, + orderNumber: 30, + }), + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default separatorAfterCheckForUpdatesInjectable; diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/separator-after-extensions/separator-after-extensions.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/separator-after-extensions/separator-after-extensions.injectable.ts new file mode 100644 index 0000000000..17a28c4927 --- /dev/null +++ b/src/features/application-menu/main/menu-items/primary-for-mac/separator-after-extensions/separator-after-extensions.injectable.ts @@ -0,0 +1,24 @@ +/** + * 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 applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; + +const id = "separator-after-extensions"; + +const separatorAfterExtensionsInjectable = getInjectable({ + id, + + instantiate: () => ({ + id, + parentId: "primary-for-mac", + type: "separator" as const, + orderNumber: 70, + }), + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default separatorAfterExtensionsInjectable; diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/show-about-application/about-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/show-about-application/about-menu-item.injectable.ts new file mode 100644 index 0000000000..5511b0208d --- /dev/null +++ b/src/features/application-menu/main/menu-items/primary-for-mac/show-about-application/about-menu-item.injectable.ts @@ -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 productNameInjectable from "../../../../../../common/vars/product-name.injectable"; +import showAboutInjectable from "./show-about.injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; + +const aboutMenuItemInjectable = getInjectable({ + id: "about-menu-item", + + instantiate: (di) => { + const productName = di.inject(productNameInjectable); + const showAbout = di.inject(showAboutInjectable); + + return { + parentId: "primary-for-mac", + id: "about", + orderNumber: 10, + label: `About ${productName}`, + + click() { + showAbout(); + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default aboutMenuItemInjectable; diff --git a/src/main/menu/show-about.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/show-about-application/show-about.injectable.ts similarity index 63% rename from src/main/menu/show-about.injectable.ts rename to src/features/application-menu/main/menu-items/primary-for-mac/show-about-application/show-about.injectable.ts index e19c2b0bfa..a91e2af337 100644 --- a/src/main/menu/show-about.injectable.ts +++ b/src/features/application-menu/main/menu-items/primary-for-mac/show-about-application/show-about.injectable.ts @@ -3,13 +3,13 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import showMessagePopupInjectable from "../electron-app/features/show-message-popup.injectable"; -import isWindowsInjectable from "../../common/vars/is-windows.injectable"; -import appNameInjectable from "../../common/vars/app-name.injectable"; -import productNameInjectable from "../../common/vars/product-name.injectable"; -import buildVersionInjectable from "../vars/build-version/build-version.injectable"; -import extensionApiVersionInjectable from "../../common/vars/extension-api-version.injectable"; -import applicationCopyrightInjectable from "../../common/vars/application-copyright.injectable"; +import showMessagePopupInjectable from "../../../../../../main/electron-app/features/show-message-popup.injectable"; +import isWindowsInjectable from "../../../../../../common/vars/is-windows.injectable"; +import appNameInjectable from "../../../../../../common/vars/app-name.injectable"; +import productNameInjectable from "../../../../../../common/vars/product-name.injectable"; +import buildVersionInjectable from "../../../../../../main/vars/build-version/build-version.injectable"; +import extensionApiVersionInjectable from "../../../../../../common/vars/extension-api-version.injectable"; +import applicationCopyrightInjectable from "../../../../../../common/vars/application-copyright.injectable"; const showAboutInjectable = getInjectable({ id: "show-about", diff --git a/src/main/menu/menu-registration.ts b/src/features/application-menu/main/menu-registration.ts similarity index 100% rename from src/main/menu/menu-registration.ts rename to src/features/application-menu/main/menu-registration.ts diff --git a/src/main/menu/menu.ts b/src/features/application-menu/main/menu.ts similarity index 100% rename from src/main/menu/menu.ts rename to src/features/application-menu/main/menu.ts diff --git a/src/features/application-menu/main/populate-application-menu.global-override-for-injectable.ts b/src/features/application-menu/main/populate-application-menu.global-override-for-injectable.ts new file mode 100644 index 0000000000..7f6903209a --- /dev/null +++ b/src/features/application-menu/main/populate-application-menu.global-override-for-injectable.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getGlobalOverrideForFunction } from "../../../common/test-utils/get-global-override-for-function"; +import populateApplicationMenuInjectable from "./populate-application-menu.injectable"; + +export default getGlobalOverrideForFunction(populateApplicationMenuInjectable); diff --git a/src/features/application-menu/main/populate-application-menu.injectable.ts b/src/features/application-menu/main/populate-application-menu.injectable.ts new file mode 100644 index 0000000000..bc6676ae66 --- /dev/null +++ b/src/features/application-menu/main/populate-application-menu.injectable.ts @@ -0,0 +1,19 @@ +/** + * 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 { Menu } from "electron"; +import type { MenuItemOpts } from "./application-menu-items.injectable"; + +const populateApplicationMenuInjectable = getInjectable({ + id: "populate-application-menu", + + instantiate: () => (applicationMenuItems: MenuItemOpts[]) => { + Menu.setApplicationMenu(Menu.buildFromTemplate(applicationMenuItems)); + }, + + causesSideEffects: true, +}); + +export default populateApplicationMenuInjectable; diff --git a/src/main/menu/start-application-menu.injectable.ts b/src/features/application-menu/main/start-application-menu.injectable.ts similarity index 68% rename from src/main/menu/start-application-menu.injectable.ts rename to src/features/application-menu/main/start-application-menu.injectable.ts index 3365223c86..77fb31cb2a 100644 --- a/src/main/menu/start-application-menu.injectable.ts +++ b/src/features/application-menu/main/start-application-menu.injectable.ts @@ -3,15 +3,15 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import applicationMenuInjectable from "./application-menu.injectable"; -import { onLoadOfApplicationInjectionToken } from "../start-main-application/runnable-tokens/on-load-of-application-injection-token"; +import applicationMenuReactivityInjectable from "./application-menu-reactivity.injectable"; +import { onLoadOfApplicationInjectionToken } from "../../../main/start-main-application/runnable-tokens/on-load-of-application-injection-token"; const startApplicationMenuInjectable = getInjectable({ id: "start-application-menu", instantiate: (di) => { const applicationMenu = di.inject( - applicationMenuInjectable, + applicationMenuReactivityInjectable, ); return { diff --git a/src/main/menu/stop-application-menu.injectable.ts b/src/features/application-menu/main/stop-application-menu.injectable.ts similarity index 68% rename from src/main/menu/stop-application-menu.injectable.ts rename to src/features/application-menu/main/stop-application-menu.injectable.ts index 73ed462242..63abccadf9 100644 --- a/src/main/menu/stop-application-menu.injectable.ts +++ b/src/features/application-menu/main/stop-application-menu.injectable.ts @@ -3,15 +3,15 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import applicationMenuInjectable from "./application-menu.injectable"; -import { beforeQuitOfBackEndInjectionToken } from "../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token"; +import applicationMenuReactivityInjectable from "./application-menu-reactivity.injectable"; +import { beforeQuitOfBackEndInjectionToken } from "../../../main/start-main-application/runnable-tokens/before-quit-of-back-end-injection-token"; const stopApplicationMenuInjectable = getInjectable({ id: "stop-application-menu", instantiate: (di) => { const applicationMenu = di.inject( - applicationMenuInjectable, + applicationMenuReactivityInjectable, ); return { diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts index dc71c1fd57..6630dd9067 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts @@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { setupIpcMainHandlers } from "./setup-ipc-main-handlers"; import loggerInjectable from "../../../../common/logger.injectable"; import clusterManagerInjectable from "../../../cluster/manager.injectable"; -import applicationMenuItemsInjectable from "../../../menu/application-menu-items.injectable"; +import applicationMenuItemsInjectable from "../../../../features/application-menu/main/application-menu-items.injectable"; import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import { onLoadOfApplicationInjectionToken } from "../../../start-main-application/runnable-tokens/on-load-of-application-injection-token"; import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable"; diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts index 8be12213fe..630313d154 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts @@ -15,7 +15,7 @@ import { pushCatalogToRenderer } from "../../../catalog-pusher"; import type { ClusterManager } from "../../../cluster/manager"; import { ResourceApplier } from "../../../resource-applier"; import type { IComputedValue } from "mobx"; -import type { MenuItemOpts } from "../../../menu/application-menu-items.injectable"; +import type { MenuItemOpts } from "../../../../features/application-menu/main/application-menu-items.injectable"; import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window"; import { handleWindowAction, onLocationChange } from "../../../ipc/window"; import { openFilePickingDialogChannel } from "../../../../common/ipc/dialog"; diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts index 8efbe83820..035f81adcd 100644 --- a/src/main/getDiForUnitTesting.ts +++ b/src/main/getDiForUnitTesting.ts @@ -31,7 +31,6 @@ import setupLensProxyInjectable from "./start-main-application/runnables/setup-l import setupShellInjectable from "./start-main-application/runnables/setup-shell.injectable"; import setupSyncingOfWeblinksInjectable from "./start-main-application/runnables/setup-syncing-of-weblinks.injectable"; import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable"; -import applicationMenuInjectable from "./menu/application-menu.injectable"; import isDevelopmentInjectable from "../common/vars/is-development.injectable"; import setupSystemCaInjectable from "./start-main-application/runnables/setup-system-ca.injectable"; import setupDeepLinkingInjectable from "./electron-app/runnables/setup-deep-linking.injectable"; @@ -148,8 +147,6 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) di.override(stopServicesAndExitAppInjectable, () => () => {}); di.override(lensResourcesDirInjectable, () => "/irrelevant"); - di.override(applicationMenuInjectable, () => ({ start: () => {}, stop: () => {} })); - overrideFunctionalInjectables(di, [ getHelmChartVersionsInjectable, getHelmChartValuesInjectable, diff --git a/src/main/menu/application-menu.injectable.ts b/src/main/menu/application-menu.injectable.ts deleted file mode 100644 index 633ccedbc4..0000000000 --- a/src/main/menu/application-menu.injectable.ts +++ /dev/null @@ -1,25 +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 { autorun } from "mobx"; -import { buildMenu } from "./menu"; -import applicationMenuItemsInjectable from "./application-menu-items.injectable"; -import { getStartableStoppable } from "../../common/utils/get-startable-stoppable"; - -const applicationMenuInjectable = getInjectable({ - id: "application-menu", - - instantiate: (di) => { - const applicationMenuItems = di.inject(applicationMenuItemsInjectable); - - return getStartableStoppable("build-of-application-menu", () => - autorun(() => buildMenu(applicationMenuItems.get()), { - delay: 100, - }), - ); - }, -}); - -export default applicationMenuInjectable; diff --git a/src/main/tray/tray-menu-item/implementations/about-app-tray-item.injectable.ts b/src/main/tray/tray-menu-item/implementations/about-app-tray-item.injectable.ts index 5f2432966d..2f47a08620 100644 --- a/src/main/tray/tray-menu-item/implementations/about-app-tray-item.injectable.ts +++ b/src/main/tray/tray-menu-item/implementations/about-app-tray-item.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import showApplicationWindowInjectable from "../../../start-main-application/lens-window/show-application-window.injectable"; -import showAboutInjectable from "../../../menu/show-about.injectable"; +import showAboutInjectable from "../../../../features/application-menu/main/menu-items/primary-for-mac/show-about-application/show-about.injectable"; import { trayMenuItemInjectionToken } from "../tray-menu-item-injection-token"; import { computed } from "mobx"; import withErrorLoggingInjectable from "../../../../common/utils/with-error-logging/with-error-logging.injectable"; diff --git a/src/renderer/components/test-utils/get-application-builder.tsx b/src/renderer/components/test-utils/get-application-builder.tsx index 45f33bdc9e..e3b51d5ed9 100644 --- a/src/renderer/components/test-utils/get-application-builder.tsx +++ b/src/renderer/components/test-utils/get-application-builder.tsx @@ -20,8 +20,8 @@ import { pipeline } from "@ogre-tools/fp"; import { compact, filter, first, flatMap, get, join, last, map, matches } from "lodash/fp"; import preferenceNavigationItemsInjectable from "../+preferences/preferences-navigation/preference-navigation-items.injectable"; import navigateToPreferencesInjectable from "../../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable"; -import type { MenuItemOpts } from "../../../main/menu/application-menu-items.injectable"; -import applicationMenuItemsInjectable from "../../../main/menu/application-menu-items.injectable"; +import type { MenuItemOpts } from "../../../features/application-menu/main/application-menu-items.injectable"; +import applicationMenuItemsInjectable from "../../../features/application-menu/main/application-menu-items.injectable"; import type { MenuItem, MenuItemConstructorOptions } from "electron"; import type { NavigateToHelmCharts } from "../../../common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable"; import navigateToHelmChartsInjectable from "../../../common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable";